Architettura di dati e sistemi, dalle fondamenta Lezione 42 / 80

Kafka: il log dominante

Perché Kafka è diventato la spina dorsale di integrazione dell'architettura moderna. Topic, partizioni, consumer group, offset e la garanzia at-least-once.

La lezione 41 ha compiuto il salto concettuale da dati limitati a dati illimitati. La forma di processing naturale per i dati illimitati è lo streaming, e lo streaming ha bisogno di un substrato: un posto dove i producer scrivono eventi e i consumer li leggono, abbastanza durevole da sopravvivere ai guasti, abbastanza veloce da stare al passo con il flusso, e replayable in modo che i consumer possano riavvolgere quando sbagliano qualcosa. La risposta dominante nel 2026, di gran lunga, è Apache Kafka. Questa lezione copre cos’è Kafka, cosa garantisce, e perché ha finito per essere ovunque.

Il TLDR sta in una frase. Kafka è un log distribuito, durevole, partizionato e append-only su cui i producer scrivono e da cui i consumer leggono al proprio ritmo.

Ognuna di queste parole sta facendo lavoro vero, e il resto della lezione le smonta.

Il modello mentale

Un cluster Kafka ha sei concetti. Inquadra bene questi e il resto è dettaglio.

Topic. Un log con un nome. Concettualmente come una tabella di database, salvo che i record vengono accodati invece che aggiornati, e i consumer leggono da una posizione invece di eseguire query. Un deployment Kafka ha di solito da decine a migliaia di topic, uno per stream logico di eventi: orders.placed, payments.completed, users.signed-up, inventory.updated, e così via.

Partition. Un sub-log dentro a un topic. Un topic con dodici partition sono dodici log append-only separati che condividono un nome. Ogni partition è strettamente ordinata: i record accodati alla partition 3 verranno letti nello stesso ordine in cui sono stati scritti. Tra partition diverse non c’è alcuna garanzia d’ordine. Le partition sono l’unità di parallelismo: più partition, più consumer paralleli, più writer paralleli.

Producer. Un client che scrive record in un topic. Il producer o sceglie esplicitamente la partition, o fa l’hash di una key per sceglierla in modo deterministico (così tutti gli eventi per user-42 finiscono nella stessa partition e restano in ordine), o lascia che Kafka faccia round-robin sulle partition quando l’ordine per key non conta.

Consumer. Un client che legge record da una o più partition. Il consumer tiene traccia della propria posizione in ogni partition tramite un offset: un intero monotonicamente crescente che identifica un record specifico. “Ho letto fino all’offset 1.847.392 della partition 5 del topic orders.placed” è una descrizione completa dello stato di un consumer. Gli offset sono memorizzati da Kafka stesso in un topic speciale __consumer_offsets, non dal consumer.

Consumer group. Un insieme coordinato di consumer che si dividono il carico di lettura. Ogni partition di un topic sottoscritto è consumata esattamente da un membro del gruppo alla volta. Se hai dodici partition e quattro consumer nel gruppo, ogni consumer legge tre partition. Se scali il gruppo a dodici consumer, ognuno ne legge una. Se scali a venti, otto stanno fermi (non puoi avere più consumer che partition in un gruppo). Quando un consumer muore, il cluster fa rebalance e un altro consumer del gruppo prende le sue partition.

Broker. Un server Kafka. Un cluster ha molti broker (un deployment piccolo è da tre, uno grande è da centinaia). Ogni partition ha più replica distribuite sui broker, con una replica designata leader (gestisce scritture e letture) e le altre follower (replicano dal leader). Se un broker muore, il cluster elegge un nuovo leader per le partition i cui leader sono morti, e producer e consumer fanno failover senza intervento manuale.

flowchart LR
    P1[Producer 1] --> T
    P2[Producer 2] --> T
    subgraph T[Topic: orders.placed]
      direction TB
      PA[Partition 0]
      PB[Partition 1]
      PC[Partition 2]
    end
    subgraph CG1[Consumer group: billing]
      C1[Consumer 1]
      C2[Consumer 2]
    end
    subgraph CG2[Consumer group: analytics]
      C3[Consumer 3]
    end
    PA --> C1
    PB --> C2
    PC --> C2
    PA --> C3
    PB --> C3
    PC --> C3

Diagramma da creare: una versione rifinita che mostra i producer a sinistra che scrivono su un topic con tre partition al centro, i broker che replicano le partition implicitamente, e due consumer group a destra che leggono lo stesso topic in modo indipendente. Il punto visivo chiave è che le partition sono l’unità di parallelismo dentro a un gruppo, ma ogni consumer group legge il topic intero in modo indipendente.

Il diagramma cattura la proprietà più importante del modello: due consumer group che leggono lo stesso topic sono indipendenti. Il gruppo billing legge la propria copia al proprio ritmo; il gruppo analytics legge la propria copia al proprio ritmo. Condividono i dati; non condividono la posizione. È questo che vuol dire in pratica “disaccoppia producer e consumer”.

Le garanzie

Kafka offre un insieme piccolo e specifico di garanzie, e parecchia confusione mentale sparisce nel momento in cui sai esattamente quali sono.

Dentro a una partition, ordine stretto. Se il producer A scrive il record X e poi il record Y nella stessa partition, ogni consumer legge X prima di Y. Questa è la garanzia d’ordine più forte che Kafka fornisce, ed è il motivo per cui il partitioning per key conta: se vuoi che gli eventi per user-42 siano processati in ordine, fai key per user ID così che finiscano nella stessa partition.

Tra partition diverse, nessuna garanzia d’ordine. Se il producer A scrive il record X sulla partition 0 e il record Y sulla partition 1, i consumer possono leggerli in qualunque ordine. Non è un bug; è il prezzo del parallelismo. Se ti serve ordine globale, hai una sola partition, il che vuol dire un solo consumer, il che vuol dire niente parallelismo. La maggior parte dei team sceglie con cura le partition key e convive con l’ordinamento per key.

Durabilità tramite replication. Un record è durevole una volta che è stato replicato su N nodi, dove N è il livello acks configurato. Con acks=all e replication factor 3, un record viene confermato al producer solo dopo che tutte e tre le replica lo hanno su disco. Se un broker muore dopo l’ack, il record sopravvive sulle replica rimanenti. Se muoiono tutte e tre le replica, il record è perso; è raro ma possibile, ed è il motivo per cui Kafka viene configurato con cura nei settori regolamentati.

At-least-once delivery di default. Un record viene consegnato almeno una volta, possibilmente di più. Il caso “più di una volta” succede quando un consumer processa un record, muore prima di committare l’offset, e un sostituto legge dall’ultimo offset committato (precedente al record). Il record viene processato due volte. I consumer idempotenti (la lezione 38 ha coperto il pattern in batch; la versione streaming è identica) sono il modo in cui si convive con l’at-least-once.

Exactly-once dentro Kafka tramite transazioni. Da Kafka 0.11 (2017), le transazioni permettono a un producer di scrivere in modo atomico su più partition e a un consumer di leggere record solo dentro a transazioni committate. Combinata con il producer idempotente, questa cosa dà semantica exactly-once per workflow di tipo read-from-Kafka, process, write-to-Kafka. L’exactly-once che attraversa il confine fuori da Kafka è più difficile, e la lezione 45 lo copre.

Il riassunto che sta su un post-it: dentro a una partition, ordine stretto; tra partition, nessuno; at-least-once a meno che tu non scelga le transazioni; durabilità regolabile tramite acks e replication factor.

Perché Kafka è diventato dominante

Kafka è nato in LinkedIn nel 2010, è stato reso open-source nel 2011, è diventato un progetto top-level Apache nel 2012, e nel 2018 era il substrato di streaming di default in gran parte delle aziende che ne avevano uno. Le ragioni sono strutturali, non di marketing.

Producer e consumer sono disaccoppiati. Un producer scrive; il record sta nel log; i consumer leggono quando ci arrivano. Il producer non ha bisogno di sapere chi sta leggendo; il consumer non ha bisogno di essere online quando il producer scrive. Confronta con RPC, dove caller e callee devono essere online simultaneamente, e capisci perché le architetture event-driven (Modulo 7) viaggiano su Kafka invece che su chiamate dirette service-to-service.

I replay sono economici. Kafka conserva i record per un periodo configurabile (spesso sette giorni, a volte settimane, a volte per sempre per i compacted topic). Un consumer può riavvolgere a qualunque offset e riprocessare; un nuovo consumer può partire dall’offset zero e leggere l’intera storia. È questa la proprietà che fa funzionare l’architettura Kappa (lezione 46).

Lo scaling per partition è meccanico. Un topic con dodici partition può essere consumato da fino a dodici consumer paralleli in un gruppo. Serve più throughput? Aggiungi partition, aggiungi consumer. Confronta con lo scaling di un database relazionale, dove aggiungere capacità richiede lavoro architetturale vero.

È la spina dorsale di integrazione. Un sistema a microservizi ha un order service, un payment service, un inventory service, un notification service, un analytics service. Senza Kafka, ogni coppia di servizi che ha bisogno di comunicare deve definire una API: un problema N-per-M. Con Kafka, ogni servizio scrive eventi sui topic e legge i topic che gli interessano. La forma dell’integrazione collassa da una mesh di chiamate HTTP a una stella attorno a Kafka. Questa è la singola ragione più grande per cui Kafka si è diffuso da “tool di data engineering” a “infrastruttura generale” tra il 2015 e il 2020.

Ecosistema client forte. Producer e consumer esistono per ogni linguaggio che un’azienda vera usa: Java, Python (confluent-kafka, kafka-python), Go, Rust, Node.js, .NET, Scala. Gli schema registry si integrano in modo pulito. Connect, il framework Kafka-verso-altri-sistemi, ha centinaia di connettori. L’ecosistema è di per sé un fossato.

Le alternative del 2026

Kafka è dominante; non è solo. Quattro contendenti contano abbastanza da nominarli.

Redpanda. Una riscrittura in C++ del protocollo Kafka, abbastanza stabile per la produzione dal 2022. Parla il wire protocol di Kafka così i client esistenti funzionano, viene rilasciato come singolo binario senza JVM e senza ZooKeeper. Operativamente più semplice, latenza di coda più bassa, meno parti in movimento. Il trade-off è un ecosistema più piccolo e un singolo vendor dietro la distribuzione open-source.

Apache Pulsar. Nato in Yahoo, ora un progetto Apache. Separa lo storage (BookKeeper) dal serving, dando un’elasticità migliore e tiered storage out of the box. L’adozione è reale ma più piccola di Kafka.

AWS Kinesis, Google Pub/Sub, Azure Event Hubs. Le alternative cloud-native managed. Ognuna operativamente banale rispetto a girare Kafka in casa, ognuna legata al proprio cloud. Molti team ne scelgono uno e non si voltano più indietro. Molti altri girano Kafka managed (Confluent Cloud, AWS MSK, Aiven) proprio perché vogliono la semantica di Kafka con operations cloud.

La lettura onesta. Se stai partendo da zero e sei impegnato su un solo cloud, l’opzione nativa del cloud va benissimo e probabilmente è più facile. Se vuoi portabilità e leva sull’ecosistema, Kafka (managed o self-hosted, oppure Redpanda come drop-in) è ancora il default.

Quando NON usare Kafka

Kafka è uno strumento potente. È anche pesante, e non ogni workload che coinvolge “eventi” ne ha bisogno.

Code a basso volume. Se hai qualche migliaio di job asincroni al giorno (mandare un’email, generare un PDF, lanciare una ricomputazione), non ti serve Kafka. Una tabella Postgres con una colonna pending/done e un worker che la fa polling funziona bene fino a volumi sorprendentemente alti. SQS, RabbitMQ e le code basate su Redis (Sidekiq, Bull, Celery) sono più semplici operativamente e più adatte.

Loop di controllo davvero real-time. La latenza di Kafka è nell’ordine dei millisecondi. Alcuni workload hanno bisogno di latenza al microsecondo: high-frequency trading, controllo industriale, certa robotica. Kafka non è lo strumento giusto per quelli.

Dataset piccoli dove non ti serve un log. Se il tuo “stream” è qualche centinaia di eventi al giorno da un singolo producer a un singolo consumer, un webhook più una tabella di database batte un cluster Kafka.

La regola spannometrica: quando hai più servizi che producono eventi, più che ne consumano, un bisogno di replay della storia, o throughput che eccede le decine di migliaia di eventi al secondo, Kafka inizia a ripagarsi. Sotto quella soglia, vincono gli strumenti più semplici.

Dove porta tutto questo

Kafka è il substrato. La lezione 43 copre gli engine che leggono da lui e ci scrivono dentro: Apache Flink, Kafka Streams, Spark Structured Streaming. La scelta dell’engine è la prossima decisione architetturale, e i trade-off sono diversi da quelli della scelta Kafka-contro-alternative. La lezione 44 copre l’event time e i watermark, dove gli engine iniziano ad apparire diversi l’uno dall’altro. La lezione 47 copre il Change Data Capture, il pattern che trasforma i database relazionali in producer Kafka e permette ai sistemi di streaming di integrarsi in modo pulito con il mondo OLTP.

Il tema ricorrente lungo il resto del Modulo 6 è che Kafka è la parte facile. La parte difficile è cosa ci fai con i record una volta che stanno scorrendo.

Citazioni e letture di approfondimento

  • Documentazione di Apache Kafka, https://kafka.apache.org/documentation/ (consultata 2026-05-01). Il riferimento canonico. La sezione “Design” è breve e vale la pena leggerla da cima a fondo; spiega il modello di partitioning, replication e consumer-group con le parole degli autori originali.
  • Jay Kreps, Neha Narkhede, Jun Rao, “Kafka: a Distributed Messaging System for Log Processing”, NetDB workshop, 2011. Il paper originale del team di LinkedIn. Breve e chiaro; il design non è cambiato nei suoi tratti essenziali.
  • Jay Kreps, “The Log: What every software engineer should know about real-time data’s unifying abstraction”, LinkedIn Engineering, 2013, https://engineering.linkedin.com/distributed-systems/log-what-every-software-engineer-should-know-about-real-time-datas-unifying (consultato 2026-05-01). Il saggio che ha spiegato perché “il log” è l’astrazione giusta per i dati distribuiti, e perché Kafka si è plasmato attorno a quella idea.
  • “Kafka: The Definitive Guide” (Gwen Shapira, Todd Palino, Rajini Sivaram, Krit Petty, 2nd edition, O’Reilly, 2021). Il libro di riferimento standard.
  • Documentazione di Redpanda, https://docs.redpanda.com/ (consultata 2026-05-01). Per l’alternativa Kafka-compatibile in C++.
  • “Designing Data-Intensive Applications” (Martin Kleppmann, O’Reilly, 2017), capitolo 11. L’inquadramento di sistema del messaging basato su log, contro cui Kafka è l’esempio canonico.
Cerca