I cinque livelli di consistenza
Azure Cosmos DB offre cinque livelli di consistenza che si dispongono su un continuum che bilancia tre grandezze in tensione tra loro: coerenza (freschezza e ordine delle letture), latenza e disponibilità/throughput. La regola mentale è: più forte è la garanzia, più alta è la latenza di scrittura in configurazioni multi-region e maggiore è il consumo di Request Unit (RU) per le letture. Il livello si imposta a livello di account, ma il client SDK può indebolirlo per singola richiesta (mai rafforzarlo oltre il default dell’account).
- Strong: garantisce linearizzabilità: ogni lettura vede l’ultima scrittura committata. In un account multi-region obbliga il commit su un quorum globale, quindi introduce latenza e non è compatibile con più region di scrittura. Ideale per scenari di sola-lettura critica come inventari finanziari o leaderboard con valore univoco.
- Bounded Staleness: le letture possono essere arretrate ma entro un limite definito da K versioni o da un intervallo di tempo T. Garantisce ordine globale (consistent prefix) fuori dalla finestra. Adatto quando serve quasi-freschezza con SLA di latenza migliori di Strong, tipicamente in scenari cross-region.
- Session (default): garanzia per sessione legata a un session token. Lo stesso client legge le proprie scritture (read-your-writes), con monotonic reads/writes. Copre la stragrande maggioranza delle app web/mobile a costo e latenza contenuti.
- Consistent Prefix: le letture non vedono mai scritture fuori ordine; possono essere arretrate ma l’ordine è preservato.
- Eventual: nessuna garanzia d’ordine, massima disponibilità e latenza minima. Va bene per like, conteggi, telemetria dove la convergenza finale basta.
Un dettaglio d’esame: solo Strong e Bounded Staleness offrono garanzie legate agli SLA di consistenza e influenzano la scelta tra single/multi-region write.
Il change feed
Il change feed è un log persistente e ordinato delle modifiche (insert e update) applicate ai documenti di un container. Punti chiave:
- L’ordine è garantito all’interno di ogni partizione logica (per partition key), non globalmente.
- Registra la versione più recente di ogni documento nel modo latest version (default); non espone di per sé i delete — per intercettarli si usa il soft-delete con un flag TTL, oppure la modalità all versions and deletes dove disponibile.
- È pull-based nella sua essenza, ma quasi sempre lo si consuma in modalità push tramite componenti gestiti.
Come consumarlo
- Change Feed Processor (libreria .NET SDK): il pattern raccomandato per servizi long-running. Distribuisce le partizioni tra più istanze e mantiene il checkpoint dell’avanzamento in un lease container dedicato, garantendo bilanciamento e ripresa dopo un crash.
- Azure Functions con Cosmos DB trigger: sotto il cofano usa lo stesso processor, ma serverless. È la scelta più rapida per architetture event-driven; richiede comunque un lease container.
var processor = container
.GetChangeFeedProcessorBuilder<Doc>("materializer", HandleChangesAsync)
.WithInstanceName("host-1")
.WithLeaseContainer(leaseContainer)
.Build();
Casi d’uso tipici: materialized view e denormalizzazioni pre-calcolate per query costose, replica verso un secondo container/servizio (es. sync su Azure AI Search o Data Lake), invalidazione cache, ed event sourcing che pubblica eventi su Event Hubs / Service Bus.
Trappole tipiche d’esame
- Scenario: app globale multi-region che richiede “sempre l’ultimo dato” → la risposta d’istinto è Strong, ma se sono richieste multi-region write o bassa latenza globale, Strong è incompatibile/troppo costoso: preferire Bounded Staleness o Session. Strong non è quasi mai la scelta corretta in scenari multi-region.
- Scenario: l’utente deve rileggere subito ciò che ha appena scritto sulla stessa web app → basta Session (default), non serve Strong; ricorda di propagare il session token tra le richieste dello stesso client.
- Scenario: serve elaborare le modifiche in modo affidabile e scalabile con ripresa dopo failure → Change Feed Processor (o Functions trigger) con lease container per il checkpoint; non implementare polling manuale della query.
- Scenario: serve intercettare le cancellazioni con il change feed → il modo latest version non le espone: usare soft-delete + TTL oppure la modalità all versions and deletes.
- Scenario: costruire una vista aggregata o replicare dati verso un altro store → change feed verso materialized view, non trigger stored procedure sincroni che gravano sulle RU di scrittura.