Cómo otros comprobadores de enlaces implementan la recursividad

Fuentes: How Other Link Checkers Do Recursion
Imagen generada por IA con el prompt: Editorial illustration contrasting a linear DAG pipeline on the left and a circular crawler cycle on the right, with worker pool nodes, frontier queue boxes and connecting arrows. Dark navy background, minimal line art w
Imagen generada con IA

Matthias Endler, autor de lychee, un comprobador de enlaces escrito en Rust, analiza en este artículo cómo otras herramientas similares implementan la recursividad para rastrear sitios web completos, y por qué añadir esa capacidad a lychee le costó cinco años y cuatro intentos.

El autor estudió el código fuente de cuatro comprobadores recursivos: muffet (Go), LinkChecker (Python), linkinator (TypeScript) y broken-link-checker (JavaScript). La conclusión principal es que ninguno aplica un truco ingenioso que se le hubiera escapado: todos nacieron como rastreadores desde su primer commit, mientras que lychee se diseñó como un flujo unidireccional de una sola pasada (entradas → extracción → verificación → resultados), es decir, un grafo acíclico dirigido (DAG) en lugar de un ciclo.

Los tres componentes que comparten estos rastreadores son: una cola de trabajo mutable (la "frontera"), un conjunto de URLs visitadas que se actualiza en el momento de encolar, antes de la petición de red, para evitar carreras de deduplicación, y un primitivo de terminación que indica cuándo no queda trabajo pendiente.

En muffet, la deduplicación se hace con un conjunto de cadenas protegido por un mutex, y la terminación se apoya en sync.WaitGroup de la biblioteca estándar de Go. La concurrencia se limita con un semáforo (canal con búfer) y un limitador por host, en lugar de acoplar ambos mecanismos como intentaba lychee. LinkChecker, escrito en Python, emplea una cola sin tamaño máximo basada en queue.Queue, decisión que sus autores justifican para evitar bloqueos cuando los hilos trabajadores llaman a put.

El artículo detalla además los compromisos de cada diseño: los goroutines ilimitados de muffet ocultan costes que en Rust se pagan de forma explícita; ninguno de los proyectos ofrece una frontera respaldada en disco, por lo que rastreos muy extensos quedan limitados por la RAM; y la separación entre cola de pendientes y limitador de concurrencia resulta clave, algo que lychee no respetaba en sus intentos previos. Endler concluye que el patrón de terminación basado en WaitGroup es al que lychee ha terminado convergiendo tras cinco años.