Pregunta de entrevista y alcance
Diseñe un rastreador web a gran escala que suministre HTML a un índice de búsqueda. Realiza el seguimiento de 10 mil millones de URLs conocidas y puede emitir como máximo 1 mil millones de recuperaciones (fetches) por día. Diseñe la frontera de URLs, la cortesía a nivel de host, la deduplicación, el rerastreo, la recuperación ante fallas y el plan de validación, incluyendo estimaciones de capacidad.
Esta es una pregunta de diseño de sistemas para roles senior de backend, infraestructura, búsqueda y plataformas de datos. La salida contiene HTML sin procesar comprimido, metadatos de recuperación y enlaces recién descubiertos. La indexación de texto completo, el ranking de búsqueda, las páginas autenticadas, imágenes y videos, y el renderizado predeterminado de JavaScript están fuera del alcance. La cobertura es de mejor esfuerzo; el sistema no promete recorrer la web entera.
Los siguientes números son suposiciones de entrevista, no mediciones de producción: el cuerpo promedio de una respuesta exitosa es de 200 KB; el límite diario de recuperaciones incluye éxitos, respuestas 304, fallas y reintentos; la carga promedio utiliza el límite diario completo, y el pico planificado es de 25.000 recuperaciones por segundo. Una URL recién descubierta es duradera dentro de los 60 segundos. Cuando hay capacidad disponible, el 99% de las URLs de alta prioridad vencidas reciben una concesión (lease) en menos de 10 minutos. La cortesía hacia el host es una restricción estricta y no se puede relajar para recuperar rendimiento (throughput).
Un informe público de entrevista describe una ronda de diseño de 25 minutos en la que el código del rastreador ya existía y el candidato debía diseñar una arquitectura escalable. Material público de diseño de sistemas de 2026 también trata un rastreador distribuido como un ejercicio independiente. Un solo informe no puede establecer el banco de preguntas fijo de una empresa ni su frecuencia, por lo que este artículo lo trata como un problema representativo de diseño de sistemas y no hace ninguna atribución de empresa.
Qué evalúa el entrevistador
La primera señal es el alcance y la estimación. Un candidato sólido define el objetivo de rastreo, los objetivos de frescura, el consumidor posterior y la semántica de fallas antes de calcular el rendimiento y el almacenamiento. Dibujar inmediatamente una cola, workers de rastreo y una base de datos no explica por qué se necesitan esos componentes.
La segunda señal es el invariante de programación de la frontera de URLs. Un FIFO global único puede distribuir trabajo, pero no puede aplicar concurrencia, retraso y retroceso (backoff) entre todos los consumidores para el mismo host. Un buen diseño separa "¿qué host está listo?" de "¿qué URL debería recuperar este host a continuación?" y otorga a cada host_key un único propietario lógico para el estado de sus tokens.
La tercera señal es distinguir tres tipos de duplicados: la misma URL normalizada, diferentes URLs con contenido idéntico byte por byte y páginas con pequeñas diferencias de contenido. Requieren, respectivamente, el estado exacto de la URL, un hash de contenido y una huella digital para casi duplicados. Un único filtro de Bloom no puede cumplir las tres funciones.
La señal final es el modelo de fallas. La recuperación externa encuentra inevitablemente tiempos de espera agotados, 429, 5xx, errores de DNS, bucles de redirección, respuestas gigantescas y páginas maliciosas. Una buena respuesta acepta la ejecución de al menos una vez (at-least-once), limita los efectos secundarios duplicados con concesiones, escrituras condicionales por versión y finalización idempotente, y propone pruebas y métricas que podrían falsar el diseño.
Preguntas para aclarar antes de responder
- ¿Qué consume la salida? Un índice de búsqueda necesita HTML, marca de tiempo de recuperación, estado y una URL canonizada. Un archivo histórico también necesita versiones inmutables. Un corpus de entrenamiento enfatizaría filtros de calidad y licencias. Este problema envía la salida únicamente a un índice de búsqueda.
- ¿Qué contenido está dentro del alcance? Este diseño recupera HTML público por HTTP/HTTPS. PDFs, medios, sesiones autenticadas o renderizado de JavaScript cambiarían el recuperador, el analizador, el modelo de costos y el aislamiento de seguridad.
- ¿Cómo balancear la cobertura frente a la frescura? De los 10 mil millones de URLs conocidas, este problema actualiza 100 millones de URLs de alto valor diariamente y apunta a un intervalo de 30 días para los otros 9,9 mil millones. Actualizar cada página todos los días es matemáticamente incompatible con un presupuesto diario de 1 mil millones de recuperaciones.
- ¿En qué límite se aplica la cortesía? El diseño forma una
host_keya partir descheme + authorityy centraliza la política de robots, la concurrencia, el retraso mínimo y el backoff dirigido por el servidor para esa clave. Una asignación negociada solo cambia la política de ese host, no el invariante global. - ¿Qué tan estricto es "sin duplicados"? El descubrimiento de URLs no debe perder silenciosamente una URL debido a un falso positivo de un filtro de Bloom, por lo que una clave única duradera es la fuente de verdad. Las recuperaciones de red pueden repetirse; el almacenamiento y los eventos posteriores deben ser idempotentes.
- ¿Cuánto tiempo permanecen las páginas eliminadas y fallidas? Un
404, un410, fallas repetidas y un5xxtemporal necesitan diferentes intervalos de revisita. Este diseño retiene una lápida (tombstone) y el estado más reciente para que el redescubrimiento no cree una nueva URL.
La respuesta de 30 segundos
"Mil millones de recuperaciones por día son unas 11.600 por segundo en promedio, por lo que planificaría para un pico de 25.000 por segundo y dividiría la frescura en 100 millones de URLs actualizadas a diario y 9,9 mil millones actualizadas cada 30 días. El descubrimiento realiza una normalización conservadora y deduplicación exacta por clave única. La frontera se particiona por host: una partición primero elige un host cuyo next_allowed_at ha llegado, y luego toma la URL de mayor prioridad de la cola de ese host. Eso le da a la política de robots, la concurrencia y el backoff un único propietario. Los recuperadores usan concesiones y solicitudes condicionales, escriben HTML en almacenamiento de objetos y lo envían a analizadores que devuelven los enlaces al descubrimiento. La ejecución es de al menos una vez; las versiones de URL y la finalización idempotente absorben los duplicados. Centraría la validación en límites de tasa por host, expiración de concesiones, 429/503, archivos robots inalcanzables, bucles de redirección y trampas de rastreadores."
Análisis detallado paso a paso
Paso 1: Demostrar que los objetivos se ajustan al presupuesto
Mil millones de recuperaciones divididas por 86.400 segundos representan aproximadamente 11.574 recuperaciones por segundo en promedio. Permitiendo variaciones de tráfico y trabajo de puesta al día, redondee el pico planificado a 25.000 por segundo. Si cada respuesta devolviera un cuerpo de 200 KB, el tráfico de entrada (ingress) sería a lo sumo de unos 200 TB por día, o 2,31 GB por segundo en promedio. Un 304 Not Modified no tiene contenido de respuesta, por lo que el ingress real debería ser inferior a este límite conservador y debe calibrarse con pruebas de carga y distribuciones observadas.
El plan diario de revisitas requiere:
100,000,000 + 9,900,000,000 / 30 = 430,000,000 fetches
Eso deja aproximadamente 570 millones de recuperaciones para páginas recién descubiertas, reintentos y páginas de cambio rápido. Asumiendo 200 bytes de estado lógico bruto por URL, 10 mil millones de registros de URL requieren alrededor de 2 TB. Se excluyen la replicación, los índices, la amplificación LSM y el almacenamiento de objetos. Este orden de magnitud exige metadatos particionados horizontalmente y almacenamiento de objetos separado para el HTML; los cuerpos de las páginas no pertenecen a la frontera.
Paso 2: Construir un flujo de datos por etapas
La ruta completa es: semillas y Sitemaps → descubrimiento y normalización de URLs → estado visto exacto → metadatos de URL → programador de la frontera → verificación de robots y cortesía de host → recuperador DNS/HTTP → almacenamiento de objetos HTML → analizador (parser) → enlaces descubiertos de vuelta al descubrimiento. La salida analizada y los eventos de finalización de recuperación van luego al índice de búsqueda y al calculador de revisitas.
Un Sitemap complementa a las semillas; no garantiza la cobertura. Un archivo Sitemap puede contener como máximo 50.000 URLs y tener a lo sumo 50 MB sin comprimir. Los sitios grandes los dividen bajo un índice de Sitemaps. El descubrimiento de enlaces, los Sitemaps y las semillas provistas por operadores utilizan el mismo punto de entrada de deduplicación para que tres máquinas de estado no puedan discrepar.
Separar la recuperación del análisis tiene dos beneficios directos. La E/S externa lenta no ocupa la CPU del analizador, y una falla del analizador puede reproducir el HTML almacenado sin contactar al sitio nuevamente. Cada etapa necesita una cola acotada y contrapresión (backpressure) para que una tasa de descarga temporal superior a la capacidad de análisis o almacenamiento no agote la memoria.
Paso 3: Hacer de la cortesía hacia el host la primitiva de programación de la frontera
La frontera utiliza dos niveles de cola. El nivel superior almacena el next_allowed_at y la prioridad de cada host y selecciona solo hosts que estén listos y no en backoff. El nivel inferior es una cola de prioridad de URLs para ese host, ordenada por señales como valor del negocio, tiempo de vencimiento, profundidad del enlace y tasa de cambio histórica. Conceder una URL actualiza atómicamente el conteo de in_flight del host y el siguiente tiempo elegible.
Calcular el hash de host_key asigna un host a una partición del programador. Incluso si un host tiene un millón de URLs pendientes, un propietario lógico otorga sus tokens mientras el trabajo de recuperación puede ejecutarse en muchas máquinas. Un host muy activo puede tener múltiples conexiones concurrentes, pero el mismo estado de host sigue controlando su margen permitido. Agregar workers aumenta el paralelismo entre hosts; no puede legítimamente exceder el margen de un único host.
robots.txt se recupera desde el /robots.txt de nivel superior del servicio. Una recuperación exitosa requiere que el rastreador siga las reglas analizables. Cuando el archivo no está disponible con 400–499, el protocolo permite el acceso; cuando errores de red o 500–599 lo hacen inalcanzable, el rastreador asume una prohibición total. Una copia en caché normalmente no debería usarse por más de 24 horas a menos que el archivo sea inalcanzable. "Una solicitud concurrente y un segundo de retraso por host" es solo el valor predeterminado configurable de esta entrevista; el protocolo no define una tasa universal. Ante un 429 o 503, respete Retry-After; si está ausente, aplique backoff exponencial con fluctuación (jitter) y reduzca el margen de ese host.
Paso 4: Separar la deduplicación de URLs de la deduplicación de contenido
La normalización realiza solo transformaciones que preservan la semántica: resolver referencias relativas, eliminar el fragmento, normalizar mayúsculas/minúsculas de esquema y hostname, manejar puertos predeterminados y resolver segmentos de punto en la ruta. No elimine globalmente ni reordene parámetros de consulta; algunos sitios asignan significado al orden y a las claves repetidas. Una URL canónica declarada por la página puede influir en la puntuación y agrupación, pero no debe sobrescribir la URL observada como un hecho.
Almacene canonical_url, o una clave única a prueba de colisiones para esta, en el almacén de metadatos particionado. Un filtro de Bloom es solo un acelerador negativo: ante "definitivamente ausente", intente la inserción directamente; ante "posiblemente presente", verifique aún la clave única duradera. Por lo tanto, un falso positivo agrega una lectura en lugar de descartar una página. Resuelva colisiones de hash comparando la URL completa o una segunda huella digital.
Calcule un hash de contenido solo después de la recuperación. Contenido idéntico byte por byte puede reutilizar un único objeto mientras preserva los metadatos de cada URL. Las páginas casi duplicadas pueden agruparse con una huella digital como SimHash. La investigación ha demostrado esta clase de huella a escala de miles de millones de páginas, pero la señal de casi duplicado es más segura como entrada para almacenamiento, indexación o prioridad de revisita. Descartar una página por completo también puede descartar enlaces que sean únicos de esa página.
Paso 5: Recuperar con estado versionado y concesiones (leases)
Mantenga compactos los registros principales:
UrlState( urlid, canonicalurl, hostkey, stateversion, lastfetchat, nextfetchat, priority, etag, lastmodified, contenthash, failure_count )
HostState( hostkey, robotspolicy, robotsexpiresat, nextallowedat, inflight, backoffuntil, policy_version )
FetchLease(leaseid, urlid, urlversion, expiresat, attempt)
El programador emite una concesión acotada que lleva url_version. Un recuperador puede fallar después de escribir el HTML pero antes de confirmar la tarea, por lo que la expiración de la concesión puede causar otra recuperación. La finalización utiliza una escritura condicional sobre (url_id, url_version). Una concesión antigua o una confirmación duplicada devuelve el resultado existente y no publica otro evento de índice. Una nueva revisita incrementa primero la versión, de modo que la clave de idempotencia de la ronda anterior no pueda suprimir trabajo nuevo legítimo.
Cuando un ETag está disponible, envíe If-None-Match; de lo contrario, un Last-Modified almacenado puede dirigir If-Modified-Since. Un 304 actualiza el tiempo de recuperación y la siguiente programación sin escribir un cuerpo vacío. Errores de DNS por tiempo agotado, fallas de conexión y 5xx entran en una política de reintentos con tope. Un 404/410 duradero crea una lápida y un intervalo de revisita mucho más largo. Las redirecciones tienen un límite de saltos y detección de bucles.
Paso 6: Poner revisitas, protección contra trampas y seguridad bajo un solo presupuesto
La prioridad de revisita combina el valor de la página, el intervalo de cambio reciente, el estado y el margen permitido del sitio. Un cambio de contenido acorta el intervalo; resultados repetidos sin cambios lo alargan, acotado entre uno y 30 días. Esto traslada presupuesto de páginas estables a páginas cambiantes mientras retiene una garantía mínima de frescura.
Una profundidad máxima de enlaces por sí sola no detiene calendarios, navegación facetada o combinaciones infinitas de parámetros de consulta. Añada un presupuesto diario por host, límites de crecimiento de plantillas de URL, conteo de parámetros de consulta, detección de rutas repetidas, límites de tamaño para cuerpo de respuesta y tamaño descomprimido, límites de tiempo de análisis y límites de saltos de redirección. Cuando un patrón consume su presupuesto, pause ese patrón y conserve una muestra sin bloquear hosts no relacionados.
Los recuperadores procesan entradas no confiables. Rechace direcciones de loopback, privadas, de enlace local (link-local) y de metadatos de nube provenientes de resultados de DNS y vuelva a verificarlas inmediatamente antes de conectar para reducir el riesgo de SSRF y DNS rebinding. Ejecute los analizadores con límites de memoria y CPU y aísle bombas de compresión y HTML mal formado. Las reglas de robots expresan preferencias de rastreo; no son autorización de acceso.
Paso 7: Validar invariantes con inyección de fallas
Comience con una simulación de programación determinista. Dé a tres hosts diferentes tasas, reglas de robots y valores de Retry-After; avance un reloj virtual; asegure que ninguna ventana de tiempo exceda una asignación y que una ruta no permitida nunca reciba una concesión. Luego inyecte "el proceso falla después del éxito HTTP", "se pierde la confirmación de la concesión", "expira la caché de robots", "el DNS se resuelve a una dirección privada" y "la cola del analizador se detiene". Las tareas deben recuperarse, los eventos del índice deben permanecer únicos y la etapa de recuperación debe aplicar contrapresión.
Las pruebas de capacidad deben cubrir 25.000 otorgamientos de concesiones por segundo, sesgo de partición a lo largo de un espacio de claves de 10 mil millones de URLs y un host activo con un millón de URLs pendientes. Las métricas clave incluyen el retraso de elegibilidad, tasas de recuperaciones y bytes, proporciones de 2xx/304/429/5xx, violaciones de políticas de host, reintentos de concesiones, tasas de duplicados de URLs y contenido, antigüedad de la caché de robots, acumulación del analizador y tasa de activación de presupuestos. Las violaciones de políticas de host deben permanecer en cero; alcanzar el rendimiento promedio violando la cortesía es una prueba fallida.
Alternativas y sus límites
A un nivel de unos pocos millones de recuperaciones por día contra sitios propios, una base de datos relacional puede indexar next_fetch_at, usar SKIP LOCKED para reclamar tareas y actualizar un token de host en la misma transacción. Es más simple de desplegar y depurar. Con mil millones de recuperaciones por día, los escaneos de índices globales, las actualizaciones frecuentes y la limpieza se convierten en cuellos de botella, haciendo que la frontera particionada de dos niveles sea una opción mucho mejor.
Usar un filtro de Bloom como el conjunto de vistos ahorra lecturas, pero los falsos positivos reducen permanentemente la cobertura. Este diseño lo degrada a una caché y mantiene la clave única duradera como la fuente de verdad. Omitir la lectura de confirmación solo es razonable cuando el producto acepta explícitamente un presupuesto cuantificado de falsos positivos.
Ejemplo de una respuesta sólida
"Comenzaría con dos invariantes: presupuesto y cortesía. Mil millones de recuperaciones por día representan unas 11.600 por segundo en promedio y un pico de 25.000 por segundo. Actualizar 100 millones de URLs diariamente más 9,9 mil millones cada 30 días programa unos 430 millones de recuperaciones por día, dejando espacio para descubrimiento, reintentos y actualizaciones impulsadas por cambios. A 200 KB por respuesta, 200 TB por día es un límite superior de red conservador; las respuestas 304 reducen el tráfico real.
Al entrar, una URL recibe solo una normalización segura y se compara contra una clave única duradera. Un filtro de Bloom solo evita lecturas para URLs obviamente no vistas. La frontera se particiona por scheme + authority; cada partición mantiene la preparación del host más una cola de prioridad de URLs por host. Reclamar trabajo consume atómicamente un token del host, por lo que múltiples recuperadores no pueden sobrecargar colectivamente un sitio. Un archivo robots inalcanzable pausa el host, y un 429/503 activa Retry-After o un backoff con fluctuación.
Un recuperador recibe una concesión acotada, realiza un GET condicional, escribe el HTML en almacenamiento de objetos y entrega el análisis a la siguiente etapa. Los enlaces analizados regresan a través del mismo punto de entrada de descubrimiento. La expiración de la concesión puede repetir una solicitud, pero la finalización versionada por URL evita que una concesión antigua sobrescriba el estado o emita un segundo evento de índice. Un hash de contenido reutiliza objetos duplicados exactos, mientras que SimHash afecta la prioridad de casi duplicados en lugar de descartar enlaces potencialmente únicos.
Probaría el intervalo por host con un reloj virtual e inyectaría fallas tras la escritura, confirmaciones perdidas, estado de robots expirado, DNS rebinding y contrapresión del analizador. La aceptación requiere tanto el pico de 25.000 por segundo como cero violaciones a las políticas de los hosts."
Errores comunes
- Error: usar una cola de mensajes global única. Por qué falla: consumidores independientes no pueden hacer cumplir de forma conjunta el siguiente tiempo elegible de un host, por lo que un mayor rendimiento aumenta el riesgo de violar la cortesía. Solución: asigne la propiedad de la programación por
host_keyy use una cola de preparación de hosts junto con colas de URLs por host. - Error: usar un filtro de Bloom como el único conjunto de vistos. Por qué falla: un falso positivo descarta permanentemente una URL no vista, y el filtro no puede almacenar estado, versión o tiempo de revisita. Solución: úselo solo como una caché y conserve el estado con clave única duradera.
- Error: tratar la deduplicación de URLs como deduplicación de contenido. Por qué falla: diferentes URLs pueden devolver el mismo contenido, y una misma URL puede cambiar con el tiempo. Solución: deduplique URLs exactas durante el descubrimiento, y luego calcule un hash de contenido y una huella de casi duplicados después de la recuperación.
- Error: prometer rastreo exactamente una vez (exactly-once). Por qué falla: el éxito HTTP externo y la confirmación interna no pueden formar una única transacción atómica, dejando una ventana para fallas. Solución: acepte la ejecución de al menos una vez y controle los efectos secundarios internos con concesiones, escrituras condicionales por versión y eventos idempotentes.
- Error: continuar ante cualquier error de robots. Por qué falla: el protocolo distingue no disponible de inalcanzable; un error de red o
5xxrequiere una prohibición total. Solución: implemente una máquina de estados explícita y pruebe por separado la antigüedad de la caché, redirecciones y clases de falla. - Error: depender únicamente de la profundidad máxima para evitar trampas de rastreadores. Por qué falla: una sola profundidad puede contener combinaciones infinitas de navegación facetada, calendarios y parámetros de consulta. Solución: combine presupuestos por host, límites de crecimiento de patrones de URL, conteo de parámetros, tamaño de respuesta y límites de tiempo de análisis.
Preguntas de seguimiento
¿Qué pasa si el 50% de las URLs pendientes pertenecen a un solo host?
Primero establezca la concurrencia y la tasa que el sitio permite. Con una asignación fija, más workers no pueden aumentar el rendimiento legítimo de ese host; solo mejoran el paralelismo entre otros hosts. La cola de URLs del host puede particionarse para reducir un punto caliente de almacenamiento, pero cada partición sigue solicitando capacidad a un servicio de tokens lógico único. Si el negocio necesita más velocidad, negocie una fuente (feed) dedicada o un margen mayor con el sitio y versione la nueva política.
¿Por qué no garantizar exactamente una vez (exactly-once)?
Un recuperador puede fallar después de recibir la respuesta HTTP y antes de confirmar su concesión, y el sitio externo no participa en una transacción interna. Una transacción distribuida no puede deshacer el GET que ya ocurrió. El contrato alcanzable es reclamo de al menos una vez, posible recuperación duplicada y finalización interna idempotente. Mida la tasa de duplicados y reduzca su costo con solicitudes condicionales.
¿Qué pasa si la página necesita JavaScript para exponer su contenido?
Mantenga la recuperación HTTP normal como primer nivel. Solo cuando el análisis resulta vacío, la política del sitio lo permite y el valor de la página supera un umbral, la URL debe ingresar a una cola de renderizado separada. Los renderizadores tienen menor concurrencia y presupuestos más estrictos de CPU, memoria y tiempo, y comparten el token del host original. De lo contrario, un renderizado costoso eludiría la cortesía y consumiría el presupuesto global.
¿Cómo puede ejecutarse el rastreador en múltiples regiones sin consultar a un host dos veces?
Asigne a cada host_key una región de origen y permita que solo esa región otorgue tokens de host; otras regiones pueden analizar y almacenar. Ante una falla regional, transfiera la propiedad con una concesión que lleve un token de aislamiento (fencing token). La región antigua recuperada debe poseer la nueva época antes de otorgar trabajo. La programación activa para el mismo host en múltiples regiones violaría el invariante de cortesía.
El costo de almacenamiento supera repentinamente el presupuesto. ¿Qué debería reducirse primero?
Primero mejore los aciertos de solicitudes condicionales, la compresión y la reutilización de objetos duplicados exactos. Luego reduzca la retención de HTML sin procesar según el valor del contenido. No descarte metadatos de URL ni registros de auditoría de recuperación junto con él; las revisitas, la deduplicación y las investigaciones de cumplimiento dependen de ese estado. La detección de casi duplicados puede reducir la prioridad o elegir un nivel de almacenamiento, pero no debe activar eliminaciones masivas sin validar.