Spiegazioni · due
Uno scaricamento che va liscio è il caso facile, e ogni client lo gestisce con competenza. Le differenze interessanti compaiono quando manca una parte del post, cosa che accade molto più spesso di quanto il caso pulito lasci pensare. Ecco cosa succede davvero, e perché costa così tanto tempo.
La forma del problema
Un post Usenet non è un file solo. Sono migliaia di piccoli articoli, ciascuno conservato in modo indipendente sui server di ogni provider, e l'NZB che scarichi è l'elenco dei loro nomi. Un rilascio da 6.5 GB ne conta all'incirca 9,000.
Gli articoli spariscono per motivi ordinari. I provider li tengono per un periodo di conservazione fisso e poi li cancellano. Alcuni provider un certo articolo non l'hanno mai ricevuto in partenza, perché la propagazione fra server è imperfetta. Ogni tanto un caricamento era incompleto al momento della pubblicazione, e una sua parte non è mai esistita da nessuna parte. Qualunque sia la causa, l'effetto è lo stesso: il tuo client chiede un articolo, e la risposta è un rifiuto invece che dei dati.
È talmente normale che chi pubblica su Usenet lo mette in conto. Quasi ogni rilascio viene accompagnato da dati di parità, di solito file PAR2, cioè informazione ridondante in più calcolata a partire dall'originale. Se manca una parte del contenuto, la parità può ricostruirla, purché ce ne sia abbastanza. Un post tipico porta con sé circa 10% di parità, quindi può perdere una discreta frazione di sé e restare perfettamente recuperabile.
Un post danneggiato di solito non è dunque uno scaricamento rotto. È uno scaricamento che richiede un po' di aritmetica prima di essere completo. L'unica domanda è quanto ci mette un client a capirlo, ed è lì che differiscono di un fattore due o più.
La parte costosa
Quando un server non ha un articolo restituisce un rifiuto, e il client prova allora il server successivo nel tuo elenco. È la cosa giusta da fare, perché il secondo provider molto spesso ce l'ha. Il costo compare quando non ce l'ha nessuno.
In quel caso il client percorre tutto il tuo elenco di provider, un server alla volta, e viene rifiutato da ognuno di loro prima di poter concludere che l'articolo è davvero sparito. Il percorso è sequenziale, e i rifiuti sono più lenti di quanto si creda. Li abbiamo misurati direttamente, su una linea a riposo, senza alcun contenuto in movimento:
| provider | tempo per rispondere a una richiesta normale | tempo per rifiutare un articolo mancante |
|---|---|---|
| Provider A | 77.5 ms | 78.8 ms |
| Provider B | 10.5 ms | 454 ms |
| Provider C | 10.8 ms | 871 ms |
| Provider D | 9.9 ms | 1,227 ms |
| Provider E | 10.6 ms | 2,239 ms |
Due cose saltano all'occhio. Primo, la forbice è enorme: un rifiuto costa fra 79 ms e 2.2 secondi a seconda del provider che risponde, una differenza di quasi trenta volte. Secondo, un provider rifiuta in sostanza nel tempo che serve a chiedere, mentre un altro impiega più di duecento volte il proprio tempo di risposta normale per dire di no. Non è latenza di rete, è lavoro che avviene dalla loro parte, e chiedere in meno andate e ritorni non serve quasi a niente.
Fai la somma: un percorso completo su cinque provider, per un solo articolo che non esiste più, costa circa 5 secondi. Ora considera che un post danneggiato ha molti articoli così, e che un client che li tratta in sequenza paga quel costo ripetutamente mentre la tua connessione resta ferma. Lo scaricamento non è lento perché i dati arrivano piano. Hanno smesso del tutto di arrivare, e il client sta aspettando che gli si dica ciò che ha già abbastanza informazioni per dedurre.
La soluzione
L'intuizione non è rendere i rifiuti più veloci, perché i provider non li controlliamo noi. È accorgersi di quando la risposta non conta più.
In ogni momento di uno scaricamento il client sa due cose: quanti pezzi mancano ancora all'appello e quanti dati di parità ha già in mano. Se la parità che possiede basta a ricostruire tutto ciò che resta in sospeso, allora la risposta a «qualcuno ha questo articolo?» non può più cambiare l'esito. Che sia un sì lento o un no lento, il file viene ricostruito comunque. Continuare il percorso non compra nulla se non ritardo.
Quindi la build attuale smette di chiedere a quel punto e passa dritta alla riparazione. La ricostruzione in sé è rapida e lo è sempre stata: circa 2.3 secondi su un post da 6.5 GB, e quella cifra è la stessa in ogni versione che abbiamo misurato. Il tempo risparmiato è interamente l'attesa che non avviene più.
| post da 6.5 GB, 60 articoli mancanti | tempo fino a un file verificato |
|---|---|
| aspettare il rifiuto di ogni provider | 23 s |
| fermarsi appena la parità copre il buco | 13 s |
| di cui l'aritmetica di riparazione vera e propria | 2.3 s |
Lo stesso cambiamento non fa proprio nulla su un post integro, il che è la prova più chiara che sta facendo quello che pensiamo: senza articoli mancanti non c'è alcun percorso da accorciare, e le due build finiscono un lavoro pulito da 6.5 GB in 7 secondi identici.
È uno scambio, non una vittoria gratis. Riparare presto significa tenere i dati di parità in memoria invece di scartarli e procurarsene altri dopo, quindi la memoria di picco (RSS) su un post gravemente danneggiato sale da circa 0.8 GB a circa 1.3 GB, e tiriamo giù all'incirca 400 MB in più dai provider. Su un post integro la memoria è invariata a 0.24 GB. Pensiamo sia lo scambio giusto per la maggior parte delle persone, dato che la memoria si spende solo nel caso in cui ti fa guadagnare dieci secondi, ma è un costo reale e c'è un'impostazione apposita.
I nomi dei provider nella tabella dei rifiuti sono taciuti di proposito. Quelle cifre sono la proprietà di una misura, una sera, da un solo luogo, e un provider che indicizza in modo diverso, o che abbiamo raggiunto per caso in una serataccia, non dovrebbe essere etichettato come lento sulla base di una sola tornata. Ciò che conta per il ragionamento è la forma: i rifiuti variano enormemente, e il totale è abbastanza grande da dominare uno scaricamento danneggiato. Le cifre per singolo provider e l'output grezzo delle sonde sono nei nostri archivi interni, e i numeri da estremo a estremo che spiegano sono nella pagina dei benchmark.