Saltar al contenido
AutoValue
17 de octubre de 2026 · Data Quality

La semana en que casi perdimos 84.000 anuncios por un error que le echamos la culpa a los proxies

AutoValue Editorial
La semana en que casi perdimos 84.000 anuncios por un error que le echamos la culpa a los proxies

14 de septiembre

Llevábamos semanas esperando este día. Nuestro proveedor de proxies residenciales se había esfumado por un fallo de facturación allá en julio, y durante dos meses una porción cada vez mayor de nuestras 1.375 fuentes rastreaba de forma degradada o directamente no rastreaba nada: fetches que se agotaban, IPs bloqueadas a mitad de sesión, el tipo de sangrado lento que no se manifiesta como una caída sino como un número que se va achicando hasta que nadie lo nota hasta que resulta vergonzoso. Renovamos. Pool nuevo, 20.000 líneas de sesión residenciales rotativas, cada salida probada antes de dejarla acercarse a producción. El 100% de las pruebas pasó. AE, DE, JP: todas activas, todas limpias.

Así que lanzamos un crawl supervisado, del tipo en que alguien de verdad mira el panel en vez de confiar en el cron. Esperaba todo lo contrario a un desastre.

Lo que obtuvimos fue una base de datos que había perdido 84.401 filas en una sola pasada, con 102 fuentes caídas más de un 25% respecto a su snapshot anterior. Peor que los dos meses de inanición de proxies que acabábamos de resolver.

La teoría equivocada, en pocas palabras

Mi primera intuición —y esta la asumo sin rodeos— fue que el pool nuevo era de alguna manera peor de lo que sugerían las pruebas. Quizá las IPs de sesión se estaban topando con límites de velocidad a mitad del crawl, de un modo que una prueba puntual no detectaría. Es una teoría razonable. También es incorrecta, y creérmela durante medio día nos costó un tiempo que no teníamos, porque el cron del crawl queda en pausa todo el tiempo que estés depurando una teoría así: no quieres que una pasada mala sobrescriba un snapshot bueno mientras todavía estás adivinando.

La pista que desmontó la teoría fue dubizzle. Una de nuestras fuentes de mayor volumen en los EAU, había obtenido efectivamente 32.595 anuncios durante esa corrida —un número perfectamente saludable, los proxies claramente funcionando— y luego descartó casi todos. No falló al obtenerlos. Los obtuvo, y después los tiró.

16 de septiembre: encontrando el error real

Ahí fue cuando dejó de ser una historia de proxies y se convirtió en una historia de reemplazo de filas. Nuestra capa de persistencia guarda un snapshot de cada fuente como una sobrescritura completa de <site>_cars_for_sale.csv en cada pasada: solo los anuncios vigentes, se reemplaza toda la tabla. Ese es el comportamiento correcto cuando un crawl termina. Es catastrófico cuando un crawl se agota a mitad de camino y el scraper escribe el lote parcial que logró reunir como si fuera el snapshot completo. Una fuente que normalmente mantiene 9.000 anuncios activos se sobrescribe con los 1.200 que logró scrapear antes de que se acabara el tiempo, y los otros 7.800 desaparecen; no porque los autos se hayan vendido, sino porque nuestro propio código decidió que un fetch incompleto era uno completo.

Los proxies no nos estaban mintiendo. La lógica de reintentos era el problema, y llevaba mucho tiempo siendo silenciosamente defectuosa, solo que un pool más saludable solía terminar los fetches antes de que el barrido importara, y eso lo enmascaraba.

Esto es lo que ese barrido costó en realidad, una vez hecha la aritmética:

Barrido de reintentos antiguoBarrido corregido
Reintentos por proxy31
Demora por intento1s—
Techo efectivo en un pool de 50 proxies~56 minutos por URLPresupuesto total de 120 segundos
Comportamiento ante un timeoutescribe el lote parcial como snapshot completose omite la fuente, se conserva el snapshot anterior

Cincuenta y seis minutos para rendirse con una sola URL. Multiplica eso en un crawl que toca más de mil fuentes y obtienes exactamente el tipo de fallo silencioso y distribuido de forma desigual que vimos: algunas fuentes bien, 102 destripadas, nada en los registros que gritara "caída de proxies" porque, técnicamente, no la hubo.

25 de septiembre: la solución, y la parte que te importa a ti

El parche que se lanzó nueve días después hizo dos cosas poco glamorosas. Limitó la aritmética de reintentos para que un fetch atascado falle rápido en vez de consumirse casi una hora, y —esta es la que de verdad evita la pérdida de datos— un fetch parcial ya no reemplaza el conjunto completo de filas. Si una fuente solo logra completar 1.200 de sus 9.000 anuncios habituales, conservamos el snapshot completo anterior en vez de sobrescribirlo con una fracción. El mismo día se incorporó un segundo cambio que escribe un indicador visible CRAWL_PAUSED que nuestro monitoreo revisa antes de avisarle a alguien sobre una caída, de modo que una pausa deliberada se lea como una pausa, no como una alerta.

¿Por qué debería importarle todo esto a un lector que busca comprar un Hyundai usado? Porque las marcas de "por debajo del mercado" que publicamos —12.823 de ellas activas en el sitio esta semana— solo son honestas si el grupo comparativo contra el que medimos un anuncio es el mercado regional real y completo, y no un fragmento truncado por un timeout. Una etiqueta de oferta construida sobre un snapshot que perdió tres cuartos de su conjunto de comparación no es una etiqueta de oferta, es ruido disfrazado con una etiqueta verde. Preferimos pausar un crawl durante nueve días antes que publicar eso.

El conjunto de datos hoy se sitúa en 1.108.535 anuncios en 73 regiones y 1.375 fuentes: más grande que antes de que empezara todo esto, con los proxies renovados y el error de reemplazo de filas resuelto. Pero el número al que sigo volviendo no es el total. Es 84.401: la brecha entre un crawl que parecía saludable en la capa de proxies y uno que realmente lo era.

Data QualityWEB ScrapingCrawl ReliabilityBelow Market Deals
Compartir

Artículos relacionados