Executive summary
L'elaborazione di dati in tempo reale, la capacità di analizzare, trasformare e reagire a informazioni nel momento stesso in cui vengono generate, è diventata un requisito operativo per applicazioni che richiedono decisioni immediate, dal rilevamento di anomalie alla personalizzazione delle esperienze utente. Questo articolo analizza i sistemi progettati per elaborare flussi continui di dati, esaminando come garantiscano risultati corretti anche quando le informazioni arrivano in ritardo o fuori ordine, come mantengano la memoria delle elaborazioni precedenti senza perdere dati in caso di guasto, e come si integrino con sistemi di previsione basati su modelli statistici per produrre risultati in tempo reale. L'analisi mostra che, sebbene i sistemi attuali abbiano raggiunto una maturità significativa, le sfide principali si sono spostate dalla pura velocità di elaborazione alla gestione efficiente della memoria di stato e alla convergenza con i sistemi di analisi tradizionali in un'unica architettura.
Background
Il processing di dati in batch, raccogliere, accumulare, elaborare a intervalli, è stato il paradigma dominante per decenni, ma presuppone che i dati siano limitati e che la latenza di ore o minuti sia accettabile. Per un numero crescente di applicazioni, rilevamento di frodi, personalizzazione in tempo reale, monitoraggio di sistemi industriali, questa assunzione è inadeguata: i dati arrivano come flussi continui e illimitati, e il valore dell'elaborazione degrada rapidamente con il ritardo. Il Dataflow Model [1], formalizzato da Google nel 2015, ha definito il framework concettuale che unifica batch e streaming sotto un'unica astrazione, articolata intorno a quattro domande: cosa si calcola (trasformazioni), dove nell'event time (finestre), quando nel processing time (trigger), e come si raffinano i risultati precedenti (accumulazione).
L'evoluzione dei sistemi di stream processing riflette una tensione fondamentale tra latenza, throughput, correttezza e complessità operativa. MillWheel [2], il sistema interno di Google, ha dimostrato nel 2013 che è possibile ottenere exactly-once semantics con latenza sub-secondo a scala internet, al costo di una significativa complessità infrastrutturale. Discretized Streams (D-Streams) [3] ha proposto un compromesso alternativo: discretizzare il flusso in micro-batch deterministici, sacrificando la latenza sub-secondo per ottenere fault tolerance parallela e la possibilità di combinare batch, interattivo e streaming in un unico sistema. Apache Flink [4] ha risolto questa tensione trattando il batch come caso speciale dello streaming, implementando un motore pipelined nativo con supporto per event time, sessioni e iterazioni. Una survey sistematica pubblicata su The VLDB Journal nel 2024 [5] documenta questa evoluzione attraverso oltre 50 sistemi, identificando nella gestione dello stato disaggregato, nell'integrazione con pipeline ML e nella migrazione live le principali sfide aperte.
Il modello Dataflow: event time, finestre e watermark
Il contributo centrale del Dataflow Model [1] è la separazione tra event time (il momento in cui l'evento si è verificato) e processing time (il momento in cui il sistema lo elabora). Questa distinzione, apparentemente semplice, ha implicazioni architetturali profonde: in un sistema distribuito, gli eventi arrivano fuori ordine, in ritardo, e potenzialmente duplicati. Un sistema che ragiona solo in processing time produce risultati dipendenti dalla latenza di rete e dal carico del cluster, risultati non riproducibili e non deterministici.
MillWheel [2] ha introdotto il concetto di low watermark come meccanismo per ragionare sulla completezza temporale: il watermark è una stima del progresso nell'event time, che indica al sistema che, con alta probabilità, tutti gli eventi con timestamp precedente sono già stati osservati. Akidau et al. [6] hanno successivamente formalizzato rigorosamente la semantica dei watermark, confrontando le implementazioni di Apache Flink e Google Cloud Dataflow e identificando divergenze comportamentali nella gestione di late data, sorgenti idle e propagazione attraverso operatori. La formalizzazione ha rivelato che sistemi che dichiarano di "supportare watermark" possono implementare semantiche incompatibili, un problema critico per la portabilità delle pipeline.
Le finestre (window) partizionano un flusso illimitato in segmenti finiti su cui calcolare aggregazioni. La tassonomia è più ricca di quanto molti sviluppatori assumano: Verwiebe et al. [7] hanno identificato 12 categorie distinte di finestre, tumbling, sliding, session, landmark, count-based e varianti, analizzando come ciascuna interagisce con l'out-of-order delivery [7]. La loro analisi rivela che la maggior parte dei sistemi supporta solo un sottoinsieme dello spazio di design, e che le policy per la gestione degli eventi in ritardo sono ragionevolmente standardizzate per le finestre tumbling, ma per finestre session e sliding il comportamento varia significativamente tra le implementazioni [7], una fonte di bug sottili in pipeline di produzione.
Il trigger determina quando i risultati di una finestra vengono emessi: alla chiusura del watermark, a intervalli di processing time, o su ogni nuovo elemento. La scelta del trigger bilancia latenza e costo computazionale: un trigger aggressivo (per-element) produce risultati freschi ma moltiplica il carico downstream; un trigger conservativo (al watermark) minimizza il carico ma introduce latenza proporzionale al ritardo massimo degli eventi. L'accumulazione governa come i risultati vengono aggiornati: in modalità discarding (ogni emissione è indipendente), accumulating (ogni emissione include il risultato completo aggiornato), o accumulating-and-retracting (il sistema emette sia il nuovo risultato sia la retrazione del precedente, consentendo correzioni downstream).
Gestione dello stato e tolleranza ai guasti
Lo stream processing stateful, aggregazioni, join, pattern matching, richiede che il sistema mantenga stato mutabile tra gli eventi. In un sistema distribuito, questo stato deve essere partizionato, replicato e recuperabile in caso di failure senza violare le garanzie di exactly-once. Il problema è fondamentalmente diverso dal fault tolerance in sistemi batch: non è possibile semplicemente rieseguire l'intero job, perché il flusso è illimitato.
L'Asynchronous Barrier Snapshotting (ABS) [8] è ispirato all'algoritmo di Chandy-Lamport per gli snapshot distribuiti, ma si differenzia nella struttura: anziché utilizzare canali di controllo separati e registrare i messaggi in transito, ABS inietta marker di barriera direttamente nel flusso dati. Quando un operatore ha ricevuto la barriera da tutti i canali di input, proprietà garantita dalla topologia DAG delle pipeline, effettua lo snapshot del proprio stato locale e propaga la barriera downstream. Questa progettazione elimina completamente il logging dei messaggi in transito su grafi aciclici, riducendo l'overhead a meno dell'1% del throughput in workload standard [8]. Il costo del checkpointing è tuttavia distinto dal costo end-to-end dell'exactly-once semantics, che richiede il coordinamento transazionale con il sink (ad esempio tramite two-phase commit con un produttore Kafka); Vogel et al. [18] documentano che, in Kafka Streams, l'abilitazione dell'exactly-once può degradare significativamente la latenza di recovery in funzione del data rate e della topologia. In caso di failure, il sistema ripristina l'ultimo snapshot completo e riprocessa gli eventi dal checkpoint.
Carbone et al. [9] hanno descritto in dettaglio l'architettura di state management di Flink: keyed state partizionato per chiave logica, operator state per metadati non partizionabili, e i savepoint come meccanismo di snapshot gestito per migrazione, rescaling e versionamento. Il RocksDB state backend, raccomandato per workload in produzione con stato di grandi dimensioni, serializza lo stato come byte array su disco locale, supportando stato che eccede la memoria heap della JVM. L'incremental checkpointing scrive solo i delta rispetto al checkpoint precedente, riducendo il volume di I/O verso lo storage durevole.
Il disaggregated state di Flink 2.0 [10], presentato a VLDB 2025, rappresenta il cambiamento architetturale più significativo nel campo dalla formalizzazione di ABS. L'architettura disaccoppia lo stato dell'operatore dal TaskManager locale, offloadandolo su uno storage remoto a livelli (object store con cache SSD locale). Questo consente di scalare compute e stato indipendentemente, eliminando il vincolo di memoria che limita il parallelismo degli operatori stateful. I risultati sperimentali mostrano un throughput superiore del 75-120% rispetto al backend RocksDB locale per query con I/O intensivo su stato di 1.2-4.8 GB, e una riduzione fino a 10x della dimensione dei checkpoint [10]. La latenza di rescaling passa da minuti a secondi, un avanzamento critico per il deployment su Kubernetes con autoscaling.
L'approccio di Apache Kafka Streams alla gestione dello stato segue un paradigma architetturale diverso. Invece di snapshot globali, Kafka Streams mantiene lo stato in store locali (RocksDB o in-memory) backed da changelog topic su Kafka: ogni modifica allo stato viene scritta su un topic compattato, e in caso di failure un nuovo consumer ricostruisce lo stato locale consumando il changelog dalla posizione più recente. Questo design elimina la necessità di coordinamento globale tra operatori durante il checkpointing, ma introduce un trade-off: il recovery time è proporzionale alla dimensione dello stato da ricostruire dal changelog, e la strategia di partition rebalancing, necessaria quando il consumer group cambia composizione, può generare periodi prolungati di indisponibilità. Il modello si rivela efficace per applicazioni con stato contenuto e topologie semplici, ma scala meno favorevolmente rispetto ad ABS su pipeline complesse con stato nell'ordine dei gigabyte [18].
Complex Event Processing
Il Complex Event Processing (CEP) si distingue dallo stream processing generico per un focus specifico: rilevare pattern temporali composti all'interno di flussi di eventi. Dove lo stream processing calcola aggregazioni, join e trasformazioni su finestre, il CEP identifica sequenze, combinazioni e assenze di eventi che soddisfano vincoli temporali e logici, ad esempio, "tre tentativi di login falliti seguiti da un accesso riuscito da IP diverso entro 5 minuti".
La sfida architetturale del CEP è la complessità computazionale del pattern matching su flussi ad alto throughput. Gli engine CEP tradizionali, come Esper e la libreria CEP di Apache Flink, eseguono un singolo automa per query, creando un collo di bottiglia su hardware multi-core. DecoPa [11], pubblicato a SIGMOD 2024, affronta questo limite decomponendo le query CEP in sotto-query eseguibili in parallelo, guidate da un modello di costo che tiene conto dell'overhead di comunicazione e riassemblaggio. I risultati mostrano miglioramenti di throughput fino a un ordine di grandezza rispetto all'esecuzione monolitica, con il modello di costo che predice accuratamente il grado di decomposizione ottimale.
Dal punto di vista computazionale, il pattern matching nei sistemi CEP è implementato tramite automi a stati finiti non deterministici (NFA). La scelta della semantica di contiguità, se gli eventi nel pattern devono essere immediatamente consecutivi (strict), separabili da eventi intermedi (relaxed), o se il sistema deve esplorare tutti i possibili match sovrapposti (non-deterministic relaxed), ha implicazioni di complessità significative. La modalità non-deterministic relaxed, in particolare, genera uno spazio di match potenzialmente esponenziale nella lunghezza del pattern, perché ogni evento intermedio può essere incluso o escluso da match alternativi. Le skip strategy, che governano come il sistema gestisce i match sovrapposti, aggiungono un secondo asse di variabilità: la stessa sequenza di eventi, con la stessa pattern expression, produce risultati radicalmente diversi in funzione della strategia scelta. In pratica, la combinazione di contiguità e skip strategy è una delle fonti più insidiose di bug semantici nelle pipeline di produzione, perché il comportamento scorretto non si manifesta come errore di esecuzione ma come risultati silenziosamente errati, un problema che né DecoPa [11] né i framework attuali affrontano con meccanismi di validazione automatica.
Stream processing per pipeline ML in produzione
L'integrazione tra stream processing e machine learning si articola su tre livelli: il calcolo di feature in tempo reale, il serving di modelli su flussi continui, e l'addestramento online con aggiornamento incrementale.
Crayfish [12], presentato a EDBT 2024, fornisce un confronto sistematico tra pattern architetturali di ML inference all'interno di sistemi di stream processing (Flink, Kafka Streams, Hazelcast). Lo studio confronta due pattern architetturali: embedding del modello direttamente nell'operatore di streaming, e invocazione di un server di inferenza esterno. L'embedding produce la latenza più bassa ma limita l'agilità di aggiornamento del modello, il deploy di una nuova versione richiede il restart dell'operatore. Il server esterno offre isolamento e aggiornamento indipendente, ma con una latenza per-evento 2-3x superiore. Il risultato chiave è che non esiste un pattern dominante: la scelta ottimale dipende dalla dimensione del modello, dalla frequenza di aggiornamento e dai requisiti di SLA.
StreamMLOps [13], pubblicato a ICDE 2023, affronta il livello successivo: il ciclo completo MLOps per modelli addestrati online su flussi live. L'architettura, costruita su Kafka e la libreria River per online learning, dimostra che l'addestramento online può essere scalato orizzontalmente in produzione per raggiungere il throughput delle pipeline di feature streaming. Le strategie per l'upgrade zero-downtime dei modelli e il retraining automatico su concept drift sono formalizzate, con una latenza di swap del modello inferiore a 50ms e rollback automatico al rilevamento di degradazione dell'accuratezza.
RALF [14], pubblicato a VLDB 2024, chiude il loop tra freshness delle feature e accuratezza predittiva. Nei sistemi tradizionali, le feature vengono ricalcolate a frequenza fissa, ma il costo computazionale di ricalcolo (in particolare per embedding vettoriali) è elevato, e non tutte le feature hanno lo stesso impatto sull'accuratezza del modello a valle. RALF sostituisce le policy di refresh one-size-fits-all con uno scheduling guidato dal feedback di errore downstream, che prioritizza il ricalcolo delle feature con maggiore impatto sull'accuratezza. È il primo sistema a formalizzare il trade-off tra costo di computazione e staleness delle feature nei feature store in produzione.
Convergenza batch-stream e architetture lakehouse
La separazione storica tra sistemi batch e streaming, l'architettura Lambda con due pipeline parallele, è stata a lungo il compromesso accettato dall'industria: il layer batch garantisce correttezza, il layer streaming bassa latenza, ma il costo operativo di mantenere due codebase duplicati è significativo. L'architettura Kappa, proposta come alternativa, elimina il layer batch riprocessando lo storico dallo stesso flusso, ma richiede una retention del broker proporzionale alla finestra di riprocessamento e un sistema di streaming con throughput comparabile alla pipeline batch [5].
DBSP [15], vincitore del Best Paper Award a VLDB 2023, fornisce una delle basi teoriche per l'incremental view maintenance: un'algebra che supporta una classe ampia di query relazionali, incluse aggregazioni e ricorsione monotona e non monotona. Il principio è "calcolare solo il delta": dato un cambiamento incrementale nell'input, DBSP calcola il cambiamento incrementale nell'output senza ricalcolare l'intero risultato. Sistemi come Materialize e RisingWave adottano questo framework direttamente; l'approccio di Apache Flink alle Materialized Table segue percorsi architetturali indipendenti, basati sull'integrazione nativa con Apache Paimon.
Flo [16], pubblicato a POPL 2025, propone una semantica formale unificata, una delle lacune storicamente aperte nel campo, che cattura sia il dataflow stile Flink, sia la computazione incrementale stile DBSP, sia la computazione lattice-based, definendo due proprietà formali, streaming progress e eager execution, che garantiscono determinismo e freshness dell'output. Il contributo pratico è un sistema di tipi leggero che distingue flussi limitati da illimitati, consentendo al compilatore di verificare staticamente proprietà di correttezza che oggi sono affidate a test e code review.
Sul piano dei benchmark, ShuffleBench [17] misura specificamente operazioni di repartizionamento ad alto shuffle in ambienti cloud-native (Kubernetes), uno scenario che accentua il vantaggio del modello pipelined rispetto al micro-batch. Su tale workload, Flink raggiunge il throughput più alto (~950.000 record/secondo) con P95 latency di 88ms; Kafka Streams bilancia throughput (~680.000 rec/s) e latenza (183ms P95); Spark Structured Streaming mostra throughput inferiore (~280.000 rec/s) con latenza P95 superiore a 10 secondi [17]. Su workload aggregation-heavy con stato ridotto, il divario tra Flink e Spark Structured Streaming si riduce significativamente. Questi numeri quantificano tuttavia il ceiling operativo strutturale di ciascuna architettura sullo shuffle, l'operazione più costosa in molte pipeline reali.
Un approccio architetturale alternativo è rappresentato dagli streaming database come RisingWave e Materialize, che espongono la computazione su stream interamente tramite interfaccia SQL (PostgreSQL-compatible). In questi sistemi, una materialized view è la primitiva fondamentale: definita come una query SQL su stream o tabelle di input, viene mantenuta incrementalmente aggiornata dal motore con garanzie exactly-once. Lo stato è persistito su object storage con compute e storage disaccoppiati. Il vantaggio operativo è significativo, gli sviluppatori utilizzano competenze SQL esistenti senza apprendere API specializzate, ma il modello introduce vincoli: le computazioni esprimibili sono limitate a ciò che il framework di incremental view maintenance supporta, e la latenza minima è tipicamente più alta rispetto ai framework general-purpose come Flink per pattern che richiedono logica procedurale custom.
L'evoluzione verso architetture lakehouse streaming, dove i dati fluiscono direttamente dal broker in tabelle aperte (Parquet/Iceberg/Delta) su object storage, elimina il tradizionale strato di connettori tra streaming e storage analitico. Apache Flink 2.0 introduce le Materialized Table, dove una singola definizione SQL governa sia l'esecuzione streaming (aggiornamento continuo) sia batch (computazione storica), con integrazione nativa con Apache Paimon come formato di storage. Questo segna la concretizzazione in produzione della convergenza batch-stream: non più due pipeline, ma un'unica definizione dichiarativa con doppia modalità di esecuzione.
Limiti e problemi aperti
La tolleranza ai guasti rimane un punto critico nelle implementazioni reali, al di là delle garanzie formali. Vogel et al. [18] hanno condotto il primo benchmarking sistematico del recovery da failure, confrontando Flink, Kafka Streams e Spark Structured Streaming. I risultati rivelano che Kafka Streams mostra un recovery volatile e subottimale a causa della strategia di partition rebalancing, con latenza che degrada severamente quando exactly-once è abilitato, in alcuni scenari eccedendo le soglie di misurazione. Flink fornisce il recovery più rapido e stabile, ma il costo cresce con la dimensione dei checkpoint. Il dato più significativo è che la "reliability", definita come correttezza dell'output sotto failure, è sensibile alla configurazione: un sistema affidabile con una configurazione può non esserlo con un diverso data rate, numero di partizioni o grado di parallelismo.
Il transactional stream processing, l'integrazione di garanzie ACID con la semantica continua e a bassa latenza dello streaming, è un'area emergente documentata da Zhang et al. [19], che hanno classificato oltre 15 sistemi in base al meccanismo di consistenza (concorrenza ottimistica, ordinamento deterministico, MVCC su flussi). L'ordinamento deterministico dell'esecuzione, come implementato in Calvin e Flink, emerge come il percorso più scalabile verso semantiche transazionali exactly-once, ma impone vincoli sulla topologia delle pipeline che limitano la flessibilità compositiva.
La scalabilità dello stato distribuito, nonostante i progressi del disaggregated state [10], resta un problema aperto per workload con stato nell'ordine dei terabyte. L'edge computing introduce un ulteriore livello di complessità: sistemi progettati per il continuum cloud-edge gestiscono connettività intermittente e dispositivi eterogenei, ma la loro maturità produttiva è significativamente inferiore rispetto ai framework cloud-first. L'integrazione con pipeline ML aggiunge la sfida della coerenza tra feature store, modello e flusso di predizione, un problema che RALF [14] ha iniziato ad affrontare formalmente ma che rimane largamente irrisolto in scenari con molteplici modelli concorrenti e feature condivise.
Riferimenti
[1] T. Akidau et al., "The Dataflow Model: A Practical Approach to Balancing Correctness, Latency, and Cost in Massive-Scale, Unbounded, Out-of-Order Data Processing," in PVLDB, vol. 8, no. 12, 2015. https://dl.acm.org/doi/10.14778/2824032.2824076
[2] T. Akidau et al., "MillWheel: Fault-Tolerant Stream Processing at Internet Scale," in PVLDB, vol. 6, no. 11, 2013. https://dl.acm.org/doi/10.14778/2536222.2536229
[3] M. Zaharia et al., "Discretized Streams: Fault-Tolerant Streaming Computation at Scale," in Proc. ACM SOSP, 2013. https://dl.acm.org/doi/10.1145/2517349.2522737
[4] P. Carbone et al., "Apache Flink: Stream and Batch Processing in a Single Engine," in IEEE Data Engineering Bulletin, vol. 38, no. 4, 2015. https://asterios.katsifodimos.com/assets/publications/flink-deb.pdf
[5] M. Fragkoulis et al., "A Survey on the Evolution of Stream Processing Systems," in The VLDB Journal, vol. 33, 2024. https://doi.org/10.1007/s00778-023-00819-8
[6] T. Akidau et al., "Watermarks in Stream Processing Systems: Semantics and Comparative Analysis of Apache Flink and Google Cloud Dataflow," in PVLDB, vol. 14, no. 12, 2021. https://dl.acm.org/doi/10.14778/3476311.3476389
[7] J. Verwiebe et al., "Survey of Window Types for Aggregation in Stream Processing Systems," in The VLDB Journal, vol. 32, 2023. https://doi.org/10.1007/s00778-022-00778-6
[8] P. Carbone et al., "Lightweight Asynchronous Snapshots for Distributed Dataflows," arXiv:1506.08603, 2015. https://arxiv.org/abs/1506.08603 (base formale per il checkpointing di Apache Flink)
[9] P. Carbone et al., "State Management in Apache Flink: Consistent Stateful Distributed Stream Processing," in PVLDB, vol. 10, no. 12, 2017. https://dl.acm.org/doi/10.14778/3137765.3137777
[10] Y. Mei et al., "Disaggregated State Management in Apache Flink 2.0," in PVLDB, vol. 18, no. 12, 2025. https://dl.acm.org/doi/10.14778/3750601.3750609
[11] S. Akili et al., "DecoPa: Query Decomposition for Parallel Complex Event Processing," in Proc. ACM SIGMOD, 2024. https://dl.acm.org/doi/10.1145/3654935
[12] S. Horchidan et al., "Crayfish: Navigating the Labyrinth of Machine Learning Inference in Stream Processing Systems," in Proc. EDBT, 2024. https://openproceedings.org/2024/conf/edbt/paper-156.pdf
[13] M. Barry et al., "StreamMLOps: Operationalizing Online Learning for Big Data Streaming and Real-Time Applications," in Proc. IEEE ICDE, 2023. https://doi.org/10.1109/ICDE55515.2023.00272
[14] S. Wooders et al., "RALF: Accuracy-Aware Scheduling for Feature Store Maintenance," in PVLDB, vol. 17, no. 3, 2024. https://dl.acm.org/doi/abs/10.14778/3632093.3632116
[15] M. Budiu et al., "DBSP: Automatic Incremental View Maintenance for Rich Query Languages," in PVLDB, vol. 16, 2023. https://doi.org/10.14778/3583140.3583152
[16] S. Laddad et al., "Flo: A Semantic Foundation for Progressive Stream Processing," in Proc. ACM POPL, 2025. https://dl.acm.org/doi/10.1145/3704845
[17] S. Henning et al., "ShuffleBench: A Benchmark for Large-Scale Data Shuffling Operations with Distributed Stream Processing Frameworks," in Proc. ACM/SPEC ICPE, 2024. https://arxiv.org/abs/2403.04570
[18] A. Vogel et al., "A Comprehensive Benchmarking Analysis of Fault Recovery in Stream Processing Frameworks," in Proc. ACM DEBS, 2024. https://arxiv.org/abs/2404.06203
[19] S. Zhang et al., "A Survey on Transactional Stream Processing," in The VLDB Journal, vol. 33, 2024. https://doi.org/10.1007/s00778-023-00814-z