Adem
EN
Menu
← Torna agli articoli
Sistemi distribuiti · Infrastruttura dati

Lezioni apprese sviluppando sistemi distribuiti per carichi analitici di oltre 600 TB

Lug 2026 · 24 min di lettura · sistemi distribuiti, dask, kubernetes, analisi

Ricordo ancora la settimana in cui una pipeline di report passò da «lenta» a «inutilizzabile». Non ci fu un’interruzione clamorosa, ma qualcosa di peggio: risultati troppo tardivi per essere affidabili. Quella settimana smettemmo di ragionare sull’ottimizzazione dei singoli job e iniziammo a trattare l’intera piattaforma analitica come un problema di sistemi distribuiti, con conseguenze operative concrete.

1) Quando la scala smise di essere teorica

Sulla carta l’architettura funzionava: servizi Python, calcolo distribuito, orchestrazione Kubernetes, comunicazione Kafka e un insieme consolidato di sistemi di archiviazione. Nella pratica, quando i carichi superarono ampiamente la soglia dei 600 TB di analisi, iniziarono a emergere tutte le dipendenze nascoste.

Lo schema si ripeteva: i job risultavano completati, ma gli utenti non potevano agire perché i dati non erano arrivati in tempo. Questa discrepanza ha cambiato il mio modo di valutare lo stato dei sistemi. Se i dati arrivano tardi, il «successo» non significa nulla.

Figura 1 — Il tempo di esecuzione sembrava crescere linearmente, poi cambiò

100 TB 300 TB 500 TB 700 TB Tempo di esecuzione Dimensione del dataset

Oltre circa 450 TB, i tempi di completamento aumentarono più del previsto perché i costi di coordinamento e lo sbilanciamento dei dati si amplificavano a vicenda.

2) Il vero problema da risolvere

La difficoltà non era solo il calcolo. Dovevamo bilanciare tre vincoli contemporaneamente:

Avevamo carichi pianificati pesanti e analisi ad hoc imprevedibili. Non potevamo quindi ottimizzare per un unico profilo di carico statico.

3) Architettura iniziale: punti di forza e lacune

Avevamo già componenti solidi: Dask per il calcolo distribuito, Kubernetes per l’orchestrazione, Kafka per la comunicazione asincrona, e un insieme concreto di sistemi di persistenza: PostgreSQL, Redis ed Elasticsearch.

Non mancava un’altra tecnologia. Mancavano regole rigorose per l’esecuzione: strategia di partizionamento, limiti di memoria e semantiche esplicite di tentativo e gestione degli errori per ogni fase.

Figura 2 — Architettura iniziale della piattaforma

flowchart LR sources[Fonti dati] --> kafka[Topic Kafka] kafka --> ingest[Servizi di acquisizione Python] ingest --> daskScheduler[Scheduler Dask] daskScheduler --> daskWorkers[Worker Dask] daskWorkers --> grpc[Microservizi gRPC] grpc --> postgres[PostgreSQL] grpc --> redis[Redis] grpc --> elastic[Elasticsearch] daskWorkers --> dashboards[Dashboard analitiche]

I componenti erano moderni, ma il comportamento in esecuzione e gli accordi operativi erano definiti in modo insufficiente per questa scala.

4) Perché i normali approcci alla scalabilità fallirono

All’inizio provammo le soluzioni abituali: nodi più grandi, più repliche e parallelismo aggressivo. Aiutarono per poco, poi generarono problemi indiretti.

La sorpresa fu che, a questa scala, il costo del coordinamento può pesare quanto i limiti della capacità di calcolo.

5) Un approccio al calcolo distribuito più efficace

Il cambiamento principale fu considerare la progettazione dei grafi di task Dask una scelta architetturale. Passammo dall’inviare job enormi a definire accordi per ogni fase, con confini chiari:

Da quel momento il comportamento dei job divenne molto più prevedibile. Non eliminammo i guasti, ma li rendemmo più semplici da contenere e comprendere.

Figura 3 — Flusso di elaborazione distribuita

flowchart TD ingest[Acquisizione partizioni] --> mapStage[Trasformazione map] mapStage --> shuffleStage[Shuffle e ripartizionamento] shuffleStage --> reduceStage[Aggregazione reduce] reduceStage --> validate[Verifica tempestività e qualità] validate --> publish[Pubblicazione risultati] validate --> retryPath[Nuovo tentativo per classe di errore] retryPath --> mapStage

Definire gli accordi per fase e i percorsi di nuovo tentativo ridusse l’imprevedibilità durante l’esecuzione.

6) Lezioni su Kubernetes imparate sul campo

Con oltre 300 nodi, la qualità delle politiche Kubernetes diventa una funzionalità essenziale del sistema. Tre lezioni pratiche:

Iniziammo a subordinare i rilasci al comportamento dei servizi e allo stato delle code, oltre che alla disponibilità dei pod.

7) Le sfide dei dati che dominavano i tempi di esecuzione

Il movimento dei dati era spesso un costo nascosto. Ottenemmo miglioramenti concreti riducendo i trasferimenti inutili e scegliendo con più attenzione archiviazione e partizioni.

Figura 4 — Trasferimento dei dati e pressione dello spill

Trasferimento tra nodi al picco
+42% prima dell’ottimizzazione
Rapporto di spill nei job critici
0.33 → 0.14
Tempo di completamento p95
3 h 40 min → 2 h 05 min
Violazioni delle finestre di aggiornamento
riduzione di circa il 38%

Per noi furono miglioramenti operativamente significativi, ottenuti ottimizzando partizionamento ed esecuzione.

8) Osservabilità: ciò che ha cambiato il nostro lavoro

L’osservabilità divenne più preziosa di ogni singola ottimizzazione del codice perché migliorava la rapidità decisionale. Passammo da dashboard rumorose a una telemetria orientata alle decisioni:

La dashboard più utile rispondeva a questa domanda: Possiamo fidarci di questo risultato adesso?

9) Lezioni sull’affidabilità

A questa scala, affidabilità significava contenere l’impatto dei guasti. Rafforzammo idempotenza, classificazione dei nuovi tentativi e criteri di rollback.

Figura 5 — Ciclo di controllo dell’affidabilità

flowchart LR detect[Rilevazione problema] --> classify[Classificazione errore] classify --> retry[Tentativi limitati] classify --> isolate[Isolamento del guasto] retry --> verify[Verifica correttezza e tempestività] isolate --> verify verify --> restore[Ripristino della fiducia nel servizio] restore --> improve[Aggiornamento procedure operative]

Rendemmo gli incidenti meno dolorosi attraverso una gestione degli errori esplicita e ripetibile.

10) Errori che oggi eviterei

11) Consigli per chi sviluppa sistemi simili

Se stai affrontando questa scala, investi presto in questi aspetti fondamentali:

12) Conclusioni

Sviluppare analisi distribuite per carichi di oltre 600 TB mi ha insegnato che la scala richiede soprattutto disciplina operativa e architetturale. Gli strumenti contano, ma accordi rigorosi, visibilità e gestione dei guasti contano ancora di più.

Se dovessi riassumere il percorso in una frase: il sistema che puoi osservare e ripristinare è il sistema che puoi far crescere.

Se il tuo carico raddoppiasse nel prossimo trimestre, quale ipotesi del tuo modello di esecuzione verrebbe meno per prima?