Explicaciones · dos
Una descarga que sale perfecta es el caso fácil, y todos los clientes lo manejan con soltura. Las diferencias interesantes aparecen cuando falta parte del post, algo que ocurre mucho más a menudo de lo que el caso limpio sugeriría. Esto es lo que pasa en realidad, y por qué cuesta tanto tiempo.
La forma del problema
Un post de Usenet no es un archivo. Son miles de artículos pequeños, cada uno guardado de forma independiente en los servidores de cada proveedor, y el NZB que descargas es la lista de sus nombres. Un lanzamiento de 6.5 GB son aproximadamente 9,000 de ellos.
Los artículos se pierden por razones corrientes. Los proveedores los conservan durante un periodo de retención fijo y luego los borran. Algunos proveedores nunca recibieron un artículo concreto de entrada, porque la propagación entre servidores es imperfecta. De vez en cuando una subida estaba incompleta al publicarse, y parte de ella nunca existió en ninguna parte. Sea cual sea la causa, el efecto es el mismo: tu cliente pide un artículo, y la respuesta es un rechazo en vez de datos.
Es lo bastante normal como para que quienes publican en Usenet cuenten con ello. Casi todo lanzamiento viene acompañado de datos de paridad, normalmente archivos PAR2, es decir, información redundante adicional calculada a partir del original. Si falta parte del contenido, la paridad puede reconstruirlo, siempre que tengas suficiente. Un post típico lleva alrededor de un 10% de paridad, así que puede perder una fracción considerable de sí mismo y seguir siendo perfectamente recuperable.
Así que un post dañado no suele ser una descarga rota. Es una descarga que requiere algo de aritmética antes de estar completa. La única pregunta es cuánto tarda un cliente en darse cuenta, y ahí es donde difieren en un factor de dos o más.
La parte cara
Cuando un servidor no tiene un artículo devuelve un rechazo, y el cliente prueba entonces con el siguiente servidor de tu lista. Es lo correcto, porque el segundo proveedor muy a menudo sí lo tiene. El coste aparece cuando no lo tiene nadie.
En ese caso el cliente recorre toda tu lista de proveedores, un servidor cada vez, y recibe un rechazo de cada uno antes de poder concluir que el artículo se ha ido de verdad. El recorrido es secuencial, y los rechazos son más lentos de lo que la gente supone. Los medimos directamente, en una línea en reposo, sin ningún contenido en movimiento:
| proveedor | tiempo en responder a una petición normal | tiempo en rechazar un artículo que falta |
|---|---|---|
| Proveedor A | 77.5 ms | 78.8 ms |
| Proveedor B | 10.5 ms | 454 ms |
| Proveedor C | 10.8 ms | 871 ms |
| Proveedor D | 9.9 ms | 1,227 ms |
| Proveedor E | 10.6 ms | 2,239 ms |
Dos cosas destacan. Primero, la horquilla es enorme: un rechazo cuesta entre 79 ms y 2.2 segundos según qué proveedor responda, una diferencia de casi treinta veces. Segundo, un proveedor rechaza prácticamente en lo que se tarda en preguntar, mientras que otro tarda más de doscientas veces su propio tiempo de respuesta normal en decir que no. Eso no es latencia de red, es trabajo que ocurre en su lado, y preguntar en menos idas y venidas apenas ayuda.
Súmalo: un recorrido completo de cinco proveedores, por un solo artículo que ya no existe, cuesta unos 5 segundos. Ahora piensa que un post dañado tiene muchos artículos así, y que un cliente que los procesa en secuencia paga ese coste una y otra vez mientras tu conexión está parada. La descarga no es lenta porque los datos lleguen despacio. Han dejado de llegar por completo, y el cliente está esperando a que le digan lo que ya tiene información suficiente para deducir.
La solución
La idea no es hacer los rechazos más rápidos, porque no controlamos a los proveedores. Es darse cuenta de cuándo la respuesta deja de importar.
En cualquier momento de una descarga el cliente sabe dos cosas: cuántas piezas siguen sin aparecer y cuántos datos de paridad tiene ya. Si la paridad en la mano basta para reconstruir todo lo que queda pendiente, entonces la respuesta a «¿tiene alguien este artículo?» ya no puede cambiar el resultado. Sea un sí lento o un no lento, el archivo se reconstruye igual. Seguir el recorrido no compra nada salvo retraso.
Así que la compilación actual deja de preguntar en ese punto y va directa a la reparación. La reconstrucción en sí es rápida y siempre lo fue: unos 2.3 segundos en un post de 6.5 GB, y esa cifra es la misma en todas las versiones que hemos medido. El tiempo ahorrado es enteramente la espera que ya no ocurre.
| post de 6.5 GB, 60 artículos que faltan | tiempo hasta un archivo verificado |
|---|---|
| esperar a que cada proveedor rechace | 23 s |
| parar en cuanto la paridad cubre el hueco | 13 s |
| de lo cual, la aritmética de reparación en sí | 2.3 s |
El mismo cambio no hace absolutamente nada en un post intacto, que es la prueba más clara de que hace lo que creemos: sin artículos que falten no hay recorrido que acortar, y las dos compilaciones terminan un trabajo limpio de 6.5 GB en unos idénticos 7 segundos.
Es un intercambio, no una ganancia gratis. Reparar pronto significa mantener los datos de paridad en memoria en vez de descartarlos y buscar más después, así que la memoria máxima (RSS) en un post muy dañado sube de unos 0.8 GB a unos 1.3 GB, y tiramos alrededor de 400 MB más de los proveedores. En un post intacto la memoria no cambia, 0.24 GB. Creemos que es el intercambio correcto para la mayoría, ya que la memoria solo se gasta en el caso en que te ahorra diez segundos, pero es un coste real y hay un ajuste para ello.
Los nombres de los proveedores se omiten en la tabla de rechazos a propósito. Las cifras son propiedad de una medición, una tarde, desde un solo sitio, y a un proveedor que indexe de otra forma, o al que diéramos por casualidad en una mala noche, no habría que etiquetarlo de lento por una sola tanda. Lo que importa para el argumento es la forma: los rechazos varían enormemente, y el total es lo bastante grande como para dominar una descarga dañada. Las cifras por proveedor y la salida en bruto de las sondas están en nuestro registro interno, y los números de extremo a extremo que explican están en la página de benchmarks.