Il pensiero CAP di Eric Brewer: perché i sistemi distribuiti fanno compromessi
Scopri il teorema CAP di Eric Brewer come modello mentale pratico: come consistenza, disponibilità e partizioni influenzano le decisioni nei sistemi distribuiti.

Perché il CAP è diventato il modello mentale di riferimento
Quando memorizzi gli stessi dati su più macchine, ottieni velocità e tolleranza ai guasti—ma generi anche un nuovo problema: disaccordo. Due server possono ricevere aggiornamenti diversi, i messaggi possono arrivare in ritardo o non arrivare affatto, e gli utenti possono leggere risposte diverse a seconda della replica a cui si collegano. CAP è diventato popolare perché offre agli ingegneri un modo preciso per parlare di quella realtà confusa senza rinunciare ai dettagli.
Eric Brewer, scienziato informatico e cofondatore di Inktomi, introdusse l'idea centrale nel 2000 come una dichiarazione pratica sui sistemi replicati sotto guasto. Si diffuse rapidamente perché rispecchiava ciò che i team stavano già vivendo in produzione: i sistemi distribuiti non falliscono solo spegnendosi; falliscono dividendo.
CAP è una lente sui guasti, non una checklist di funzionalità
CAP è più utile quando le cose vanno male—soprattutto quando la rete non si comporta. In una giornata normale, molti sistemi possono apparire abbastanza consistenti e disponibili. La vera prova è quando le macchine non riescono a comunicare in modo affidabile e devi decidere cosa fare con letture e scritture mentre il sistema è diviso.
Questa inquadratura è il motivo per cui CAP è diventato un modello mentale diffuso: non discute sulle migliori pratiche; pone una domanda concreta—cosa siamo disposti a sacrificare durante una partizione?
Cosa saprai decidere alla fine
Alla fine di questo articolo dovresti essere in grado di:
- Riconoscere quando ti trovi in un vero scenario CAP (replicazione + possibili interruzioni di comunicazione).
- Scegliere, intenzionalmente, se il tuo sistema dovrebbe favorire consistenza (tutti vedono la stessa verità) o disponibilità (il sistema continua a rispondere) quando le repliche non riescono a mettersi d'accordo.
- Collegare quella scelta all'impatto sul prodotto: cosa sperimenta l'utente, quali errori mostri e quali correzioni saranno necessarie dopo che la partizione si sarà risolta.
CAP perdura perché trasforma il vago “distribuito è difficile” in una decisione che puoi prendere—e giustificare.
L'impostazione: replicazione e il problema del disaccordo
Un sistema distribuito è, in parole semplici, molte macchine che cercano di comportarsi come una sola. Potresti avere diversi server in rack, regioni o zone cloud differenti, ma per l'utente è “l'app” o “il database”.
Perché replicare i dati
Per far funzionare quel sistema condiviso su scala reale, solitamente replichiamo: manteniamo più copie degli stessi dati su macchine diverse.
La replicazione è diffusa per tre ragioni pratiche:
- Scalabilità: più macchine gestiscono più traffico.
- Prestazioni: gli utenti possono essere serviti da una copia vicina, riducendo la latenza.
- Affidabilità: se una macchina si guasta, un'altra copia può mantenere il servizio attivo.
Fin qui la replicazione sembra un vantaggio netto. Il problema è che la replicazione crea un nuovo compito: mantenere tutte le copie d'accordo.
La tensione centrale: le copie possono non essere d'accordo
Se ogni replica potesse sempre parlare istantaneamente con le altre, potrebbero coordinare gli aggiornamenti e rimanere allineate. Ma le reti reali non sono perfette. I messaggi possono essere ritardati, persi o instradati intorno ai guasti.
Quando la comunicazione è sana, le repliche di solito scambiano aggiornamenti e convergono sullo stesso stato. Ma quando la comunicazione si rompe (anche temporaneamente), puoi ritrovarti con due versioni valide della “verità”.
Per esempio, un utente cambia l'indirizzo di spedizione. La replica A riceve l'aggiornamento, la replica B no. Ora il sistema deve rispondere a una domanda apparentemente semplice: qual è l'indirizzo corrente?
Operazione normale vs. operazione in guasto
Questa è la differenza tra:
- Operazione normale: le repliche possono coordinarsi; il disaccordo è per lo più un problema di sincronizzazione temporale.
- Operazione in guasto: alcune repliche non possono comunicare; il disaccordo diventa inevitabile.
Il pensiero CAP parte esattamente da qui: una volta che esiste la replicazione, il disaccordo sotto guasto di comunicazione non è un caso limite—è il problema progettuale centrale.
CAP in parole semplici: C, A e P
CAP è un modello mentale per ciò che gli utenti effettivamente percepiscono quando un sistema è distribuito su più macchine (spesso in luoghi diversi). Non descrive sistemi “buoni” o “cattivi”—solo la tensione che devi gestire.
Consistenza (C): vedo l'ultima scrittura?
La consistenza riguarda l'accordo. Se modifichi qualcosa, la lettura successiva (da qualsiasi replica) mostrerà quell'aggiornamento?
Dal punto di vista dell'utente, è la differenza tra “l'ho appena cambiato e tutti vedono lo stesso valore nuovo” e “alcuni vedono ancora il valore vecchio per un po'”.
Disponibilità (A): ottengo una risposta?
La disponibilità significa che il sistema risponde alle richieste—letture e scritture—con un risultato di successo. Non necessariamente “il più veloce possibile”, ma “non ti rifiuta il servizio”.
Durante i problemi (server giù, un piccolo problema di rete), un sistema disponibile continua ad accettare richieste, anche se deve rispondere con dati leggermente obsoleti.
Tolleranza alle partizioni (P): cosa succede quando i nodi non si parlano?
Una partizione è quando la rete si divide: le macchine sono in esecuzione, ma i messaggi tra alcune di esse non passano (o arrivano troppo tardi per essere utili). Nei sistemi distribuiti non puoi trattare questo come impossibile—devi definire il comportamento quando succede.
Una storia semplice: due negozi, un inventario
Immagina due negozi che vendono lo stesso prodotto e condividono “1 unità di inventario”. Un cliente compra l'ultimo articolo nel Negozio A, quindi Negozio A scrive inventario = 0. Allo stesso tempo, una partizione di rete impedisce a Negozio B di saperlo.
Se Negozio B rimane disponibile, può vendere un articolo che non ha più (accettando la vendita durante la partizione). Se Negozio B impone consistenza, può rifiutare la vendita finché non può confermare l'inventario più recente (negando il servizio durante la partizione).
Cosa sono realmente le partizioni (e perché non puoi ignorarle)
Una “partizione” non è solo “internet è giù”. È qualsiasi situazione in cui parti del sistema non possono parlare tra loro in modo affidabile—anche se ciascuna parte è ancora in esecuzione. In un sistema replicato, i nodi scambiano costantemente messaggi: scritture, acknowledgement, heartbeat, elezioni del leader, richieste di lettura. Una partizione si verifica quando quei messaggi smettono di arrivare (o arrivano troppo tardi), creando disaccordo sulla realtà: “La scrittura è avvenuta?” “Chi è il leader?” “Il nodo B è vivo?”
Le partizioni sono guasti di comunicazione
La comunicazione può fallire in modi disordinati e parziali:
- Perdita di pacchetti che provoca retry e timeout
- Problemi di routing dove il traffico fa un lungo giro o sparisce
- Link sovraccarichi (o NIC saturi) che causano ritardi prolungati
- Firewall / security group mal configurati che bloccano solo porte o direzioni specifiche
- Problemi di DNS o service discovery che impediscono ai nodi di trovarsi
Il punto importante: le partizioni sono spesso degradazione, non un guasto netto on/off. Dal punto di vista dell'applicazione, “abbastanza lento” può essere indistinguibile da “non raggiungibile”.
Perché le partizioni sono inevitabili a scala
Man mano che aggiungi più macchine, reti, regioni e parti mobili, ci sono semplicemente più opportunità perché la comunicazione si rompa temporaneamente. Anche se i singoli componenti sono affidabili, il sistema complessivo subisce guasti perché ha più dipendenze e più coordinazione cross-node.
Non serve assumere una precisa frequenza di guasti per accettare la realtà: se il tuo sistema gira a lungo e copre abbastanza infrastruttura, le partizioni accadranno.
Cosa significa in pratica “tollerare le partizioni”
Tollerare le partizioni significa che il tuo sistema è progettato per continuare a funzionare durante una divisione—anche quando i nodi non possono accordarsi o confermare cosa ha visto l'altra parte. Questo forza una scelta: continuare a servire richieste (rischiando incoerenza) o bloccare/rifiutare alcune richieste (preservando la consistenza).
Il momento chiave: scegliere consistenza o disponibilità durante una partizione
Una volta che hai replicazione, una partizione è semplicemente una rottura di comunicazione: due parti del sistema non possono parlarsi affidabilmente per un po'. Le repliche sono ancora in esecuzione, gli utenti cliccano ancora i pulsanti e il servizio riceve richieste—ma le repliche non riescono a mettersi d'accordo sulla verità più recente.
Questa è la tensione CAP in una frase: durante una partizione devi scegliere se dare priorità a Consistenza (C) o Disponibilità (A). Non puoi avere entrambe contemporaneamente.
Se scegli Consistenza (C)
Stai dicendo: “Preferisco essere corretto piuttosto che reattivo.” Quando il sistema non può confermare che una richiesta manterrà tutte le repliche sincronizzate, deve fallire o attendere.
Effetto pratico: alcuni utenti vedranno errori, timeout o messaggi “riprovare”—soprattutto per operazioni che modificano i dati. Questo è comune quando preferisci rifiutare un pagamento piuttosto che rischiare addebiti doppi, o bloccare la prenotazione di un posto piuttosto che sovravendere.
Se scegli Disponibilità (A)
Stai dicendo: “Preferisco rispondere piuttosto che bloccare.” Ogni lato della partizione continuerà ad accettare richieste, anche se non può coordinarsi.
Effetto pratico: gli utenti ottengono risposte di successo, ma i dati che leggono possono essere obsoleti e gli aggiornamenti concorrenti possono confliggere. In seguito fai affidamento sulla riconciliazione (regole di merge, last-write-wins, revisione manuale, ecc.).
La scelta può variare per operazione
Non è sempre un'impostazione globale. Molti prodotti mescolano strategie:
- Letture vs scritture: mantieni le letture disponibili, ma rendi le scritture più rigorose.
- Azioni critiche vs non critiche: applica consistenza a denaro, identità e inventario; permetti disponibilità per feed, analytics, “like” o profili in cache.
Il momento chiave è decidere—per ogni operazione—cosa è peggio: bloccare un utente ora o risolvere una verità conflittuale in seguito.
Concetti sbagliati comuni: oltre lo slogan “scegli due”
Lo slogan “scegli due” è facile da ricordare, ma spesso induce in errore facendo pensare che CAP sia un menu di tre caratteristiche dove puoi tenerne solo due per sempre. CAP riguarda cosa succede quando la rete smette di collaborare: durante una partizione (o qualsiasi cosa che le somigli), un sistema distribuito deve scegliere tra restituire risposte consistenti o rimanere disponibile per ogni richiesta.
Falso mito 1: “Scelgo C e A e posso evitare le partizioni”
Nella realtà dei sistemi distribuiti, le partizioni non sono una opzione che puoi disabilitare. Se il tuo sistema copre macchine, rack, zone o regioni, i messaggi possono essere ritardati, persi, riordinati o instradati in modo strano. Questo è una partizione dal punto di vista del software: i nodi non possono accordarsi su cosa sta succedendo.
Anche se la rete fisica è OK, guasti altrove producono lo stesso effetto—nodi sovraccarichi, pause del GC, neighbor rumorosi, problemi DNS, load balancer instabili. Il risultato è lo stesso: alcune parti del sistema non riescono a parlare tra loro abbastanza bene da coordinarsi.
Falso mito 2: “Le partizioni sono casi rari”
Le applicazioni non vivono la “partizione” come un evento netto e binario. Vivono picchi di latenza e timeout. Se una richiesta fa timeout dopo 200 ms, non importa se il pacchetto è arrivato al 201 ms o non è mai arrivato: l'app deve decidere cosa fare dopo. Dal punto di vista dell'app, la comunicazione lenta è spesso indistinguibile da quella interrotta.
Falso mito 3: “I sistemi sono o CP o AP”
Molti sistemi reali sono per lo più consistenti o per lo più disponibili, a seconda di configurazione e condizioni operative. Timeout, politiche di retry, dimensione dei quorum e opzioni “read your writes” possono spostare il comportamento.
In condizioni normali un database può sembrare fortemente consistente; sotto stress o problemi cross-region può iniziare a rifiutare richieste (favorendo la consistenza) o a restituire dati più vecchi (favorendo la disponibilità).
CAP serve meno a etichettare prodotti e più a capire il compromesso che stai facendo quando nasce un disaccordo—soprattutto se quel disaccordo è causato dalla semplice lentezza.
Opzioni di consistenza che puoi effettivamente scegliere
Le discussioni su CAP spesso descrivono la consistenza come binaria: o “perfetta” o “qualsiasi cosa”. I sistemi reali offrono un menu di garanzie, ognuna con un'esperienza utente diversa quando le repliche non sono d'accordo o un link di rete si rompe.
Consistenza forte (e il suo prezzo durante il guasto)
Consistenza forte (spesso “linearizzabile”) significa che una volta che una scrittura è confermata, ogni lettura successiva—qualsiasi replica—restituisce quella scrittura.
Il costo: durante una partizione o quando una minoranza di repliche è irraggiungibile, il sistema può ritardare o rifiutare letture/scritture per evitare stati in conflitto. Gli utenti lo notano come timeout, “riprovare” o modalità temporanea sola lettura.
Consistenza eventuale (e cosa possono notare gli utenti)
Consistenza eventuale promette che se non arrivano nuovi aggiornamenti, tutte le repliche convergeranno. Non promette che due utenti che leggono nello stesso istante vedranno la stessa cosa.
Cosa possono notare gli utenti: una foto profilo appena aggiornata che “torna indietro”, contatori in ritardo, o un messaggio appena inviato che non è visibile su un altro dispositivo per qualche secondo.
Garanzie intermedie utili
Spesso puoi ottenere una migliore esperienza senza richiedere la consistenza forte completa:
- Read-your-writes: dopo aver aggiornato qualcosa, non leggerai una versione più vecchia dei tuoi dati.
- Monotonic reads: una volta che hai visto la versione N, non vedrai poi la N-1.
- Causal consistency: se l'evento B dipende da A (risposta dopo aver letto un messaggio), tutti vedranno A prima di B.
Queste garanzie si mappano bene su come le persone pensano (“non farmi sparire le mie modifiche”) e possono essere più facili da mantenere durante guasti parziali.
Scegliere il livello di consistenza in base alle aspettative
Inizia dalle promesse agli utenti, non dalla terminologia tecnica:
- Se letture errate causano danni irreversibili (movimenti di denaro, prenotazione di inventario, cambi di permessi), orientati verso consistenza più forte e accetta una temporanea indisponibilità.
- Se la funzione tollera un disaccordo breve (like, conteggi visualizzazioni, ranking dei feed), la consistenza eventuale o causale solitamente va bene.
- Se il dolore principale è confusione personale (“l'ho salvato—perché non lo vedo?”), dai priorità a read-your-writes e monotonic reads.
La consistenza è una scelta di prodotto: definisci cosa significa “sbagliato” per l'utente, poi scegli la garanzia più debole che impedisce quello sbaglio.
La disponibilità come decisione di prodotto, non solo un numero di uptime
La disponibilità in CAP non è una medaglia da esibire (“cinque nove”)—è una promessa agli utenti su cosa succede quando il sistema non può essere certo.
Successo veloce vs successo accurato
Quando le repliche non riescono ad accordarsi, spesso scegli tra:
- Successo veloce: ritorna qualcosa rapidamente (anche se può essere obsoleto).
- Successo accurato: rispondi solo quando puoi provare che la risposta è aggiornata.
Gli utenti percepiscono questo come “l'app funziona” vs “l'app è corretta”. Nessuna delle due è universalmente migliore; la scelta dipende da cosa significa essere “sbagliati” nel tuo prodotto. Un feed sociale leggermente obsoleto è fastidioso; un saldo conto obsoleto può essere dannoso.
“Fail closed” vs “fail open”
Due comportamenti comuni durante l'incertezza:
- Fail closed: rifiuta la richiesta (errori, timeout, modalità sola lettura). Proteggi la correttezza, ma potresti bloccare gli utenti.
- Fail open: servi una risposta best-effort (dati in cache, replica locale, scrittura in coda). Proteggi il flusso, ma potresti mostrare risultati incoerenti.
Non è una scelta puramente tecnica; è una decisione di policy. Il prodotto deve definire cosa è accettabile mostrale e cosa non lo è mai.
La disponibilità parziale è comunque disponibilità
La disponibilità raramente è tutto o niente. Durante una divisione potresti vedere disponibilità parziale: alcune regioni, reti o gruppi di utenti hanno successo mentre altri falliscono. Questo può essere una scelta deliberata (servire dove la replica locale è sana) o accidentale (squilibri di routing, raggiungibilità di quorum non uniforme).
Modalità degradata: mantieni il nucleo, limita il rischio
Un compromesso pratico è la modalità degradata: continua a servire azioni sicure limitando quelle rischiose. Ad esempio, permetti la navigazione e la ricerca, ma disabilita temporaneamente “trasferisci fondi”, “cambia password” o altre operazioni in cui la correttezza e l'unicità contano.
Esempi concreti: abbinare le scelte CAP ai casi d'uso
CAP diventa concreto quando lo mappi a ciò che gli utenti sperimentano durante una partizione di rete: preferisci che il sistema continui a rispondere, o che si fermi per evitare di accettare dati in conflitto?
Inventario e ordini: rischio di sovravendita vs outage al checkout
Immagina due data center che accettano ordini mentre non possono parlarsi.
Se mantieni il flusso di checkout disponibile, ciascuna parte può vendere l’“ultimo articolo” e finirai per sovravendere. Questo può essere accettabile per beni a basso valore (si gestisce con backorder o scuse), ma doloroso per vendite a disponibilità limitata.
Se scegli consistenza-first, potresti bloccare nuovi ordini quando non puoi confermare lo stock globale. Gli utenti vedranno “riprovare più tardi”, ma eviterai di vendere qualcosa che non puoi evadere.
Pagamenti e saldi: pattern correttezza-centrica (e perché)
Il denaro è il classico dominio in cui sbagliare è costoso. Se due repliche accettano prelievi indipendentemente durante una partizione, un conto può diventare negativo.
I sistemi spesso preferiscono consistenza per le scritture critiche: rifiutare o ritardare azioni se non possono confermare il saldo più recente. Scambierai un po' di disponibilità per correttezza, tracciabilità e fiducia.
Chat, feed, analytics: disponibile con dati leggermente obsoleti va bene
In chat e feed sociali, gli utenti tollerano piccole incoerenze: un messaggio arriva con qualche secondo di ritardo, un conteggio like è sbagliato, una metrica si aggiorna dopo.
Qui progettare per la disponibilità può essere una scelta sensata, purché sia chiaro cosa è “eventualmente corretto” e si possa unire gli aggiornamenti pulitamente.
Il punto: il tuo compromesso è una decisione di business
La scelta giusta dipende dal costo dell'errore: rimborsi, esposizione legale, perdita di fiducia, o caos operativo. Decidi dove puoi accettare l'obsolescenza temporanea e dove devi fallire chiuso.
Pattern di design che implementano il tuo compromesso
Una volta presa una decisione su cosa fare durante una partizione, servono meccanismi che rendano quella decisione effettiva. Questi pattern compaiono in database, sistemi di messaggistica e API—anche se il prodotto non cita mai “CAP”.
Quorum: accordo di maggioranza
Un quorum è semplicemente “la maggior parte delle repliche è d'accordo”. Se hai 5 copie, la maggioranza è 3.
Richiedendo che letture e/o scritture contattino una maggioranza, riduci la probabilità di restituire dati obsoleti o in conflitto. Per esempio, se una scrittura deve essere riconosciuta da 3 repliche, è più difficile che due gruppi isolati accettino verità discordanti.
Il compromesso è velocità e raggiungibilità: se non raggiungi la maggioranza (per partizione o guasti), il sistema può rifiutare l'operazione—preferendo consistenza alla disponibilità.
Timeouts, retry e backoff modellano la disponibilità percepita
Molti problemi di “disponibilità” non sono guasti netti ma risposte lente. Impostare un timeout breve rende il sistema reattivo, ma aumenta la probabilità di considerare una risposta lenta come fallita.
I retry possono recuperare da blip transitori, ma retry troppo aggressivi possono sovraccaricare un servizio già in difficoltà. Backoff (aumentare i tempi tra i retry) e jitter (randomizzazione) aiutano a evitare che i retry diventino un picco di traffico.
L'importante è allineare queste impostazioni con la tua promessa: “rispondere sempre” solitamente significa più retry e fallback; “non mentire mai” significa limiti più stretti e messaggi di errore chiari.
Gestione dei conflitti quando permetti divergenza
Se scegli di restare disponibile durante le partizioni, le repliche possono accettare aggiornamenti diversi e dovrai riconciliarli dopo. Approcci comuni:
- Last-write-wins (LWW): scegli l'aggiornamento con timestamp più recente. Semplice, ma può eliminare cambi validi se gli orologi non sono allineati.
- Version vectors (a livello alto): allega una piccola “storia” che aiuta a rilevare se gli aggiornamenti sono concorrenti o se uno ne sovrascrive un altro.
- Regole di merge: definisci come combinare i cambiamenti (es. unione degli articoli del carrello; contatori che si sommano; profili che preferiscono campi non vuoti). Funziona meglio se progettato nel modello dei dati.
Idempotenza: rendere i retry sicuri
I retry possono generare duplicati: addebiti doppi o ordini inviati più volte. L'idempotenza evita questo.
Un pattern comune è la chiave di idempotenza (request ID) inviata con ogni richiesta. Il server memorizza il primo risultato e restituisce lo stesso risultato per ripetizioni—così i retry migliorano la disponibilità senza corrompere i dati.
Come validare le assunzioni CAP nella pratica
La maggior parte dei team “sceglie” una posizione CAP su una lavagna—poi scopre in produzione che il sistema si comporta diversamente sotto stress. Validare significa creare intenzionalmente le condizioni in cui i tradeoff CAP diventano visibili e verificare che il sistema reagisca come progettato.
Testa le partizioni di proposito (in sicurezza)
Non serve un vero taglio di cavo per imparare qualcosa. Usa fault injection controllata in staging (e con cautela in produzione) per simulare partizioni:
- Blackhole del traffico tra servizi o nodi specifici (scarta pacchetti senza chiudere le connessioni) per imitare una divisione silente.
- Blocca i collegamenti modificando regole di porta o security group tra repliche/regioni.
- Aggiungi latenza estrema e perdita pacchetti così che timeout e retry si comportino come in una partizione.
- Isola un leader (es. rendi primario irraggiungibile da un quorum) per vedere se fallisci “consistente” o “disponibile”.
L'obiettivo è rispondere a domande concrete: le scritture vengono rifiutate o accettate? le letture mostrano dati obsoleti? il sistema si riconcilia automaticamente, e quanto tempo serve?
Se vuoi convalidare questi comportamenti presto (prima di investire settimane a collegare servizi), può aiutare creare un prototipo realistico rapidamente. Per esempio, i team che usano Koder.ai spesso iniziano generando un piccolo servizio (comunemente un backend Go con PostgreSQL e una UI React) e iterano su comportamenti come retry, chiavi di idempotenza e flussi in “modalità degradata” in un ambiente sandbox.
Monitora segnali che rivelano dolore CAP
I controlli di uptime tradizionali non catturano il comportamento “disponibile ma sbagliato”. Monitora:
- Tassi di errore per tipo di operazione (lettura vs scrittura vs aggiornamento condizionale).
- Indicatori di letture obsolete (violazioni read-your-writes, mismatch di version/ETag, metriche di lag).
- Divergenza delle repliche (lag di replicazione, conteggi di apply falliti, tassi di conflitto).
- Timeout e retry (spesso il primo segno di una partizione emergente).
Runbook e comunicazione agli utenti
Gli operatori devono avere azioni predefinite quando si verifica una partizione: quando congelare le scritture, quando eseguire failover, quando degradare funzionalità, e come verificare la sicurezza della riconciliazione.
Pianifica anche il comportamento visibile all'utente. Se scegli consistenza, il messaggio potrebbe essere “Non possiamo confermare la modifica adesso—per favore riprova.” Se scegli disponibilità, sii esplicito: “La tua modifica potrebbe impiegare qualche minuto per apparire ovunque.” Una comunicazione chiara riduce il carico sul supporto e preserva la fiducia.
Una checklist CAP pratica per decisioni quotidiane sui sistemi
Quando prendi una decisione di sistema, CAP è più utile come veloce audit “cosa si rompe durante una partizione?”—non come dibattito teorico. Usa questa checklist prima di scegliere una funzionalità di database, una strategia di caching o una modalità di replicazione.
1) Una breve checklist CAP
Chiediti in ordine:
- Cosa deve essere corretto? (es. “un saldo bancario non deve mai andare negativo”, “non si può sovravendere l'inventario”, “i permessi devono essere accurati”)
- Cosa deve restare disponibile? (es. endpoint checkout, login, catalogo in sola lettura)
- Cosa può degradare temporaneamente? (es. analytics, raccomandazioni, avatar profilo, “ultimo accesso”)
Se succede una partizione, stai decidendo cosa proteggere per primo.
2) Decidi per tipo di dato e per endpoint
Evita un'impostazione globale unica come “siamo un sistema AP”. Invece decidi per:
- Tipo di dato: denaro vs like vs log
- Endpoint: “effettua ordine” vs “visualizza ordine” vs “traccia spedizione”
Esempio: durante una partizione potresti bloccare scritture su payments (preferendo consistenza) ma mantenere letture per product_catalog servite da cache.
3) Definisci l'“incoerenza accettabile” in termini concreti
Scrivi cosa puoi tollerare, con esempi:
- Vincolo temporale: “i contatori possono essere indietro di 5–10 minuti”
- Entità: “l'inventario può essere sbagliato di ±1 per articoli a bassa domanda”
- A livello di campo: “la ETA di spedizione può essere obsoleta; il totale dell'ordine no”
- Testo utente visibile: “mostra ‘in sospeso’ invece di uno stato definitivo”
Se non riesci a descrivere l'incoerenza con esempi semplici, sarà difficile testarla e spiegare gli incidenti.
4) Conclusioni + cosa leggere dopo
- Le partizioni trasformano le garanzie “nice-to-have” in scelte obbligatorie.
- Rendi esplicite quelle scelte per endpoint e documenta l'incoerenza accettabile.
Argomenti successivi utili: consenso, modelli di consistenza e SLO e budget di errore.
Domande frequenti
Quale problema aiuta a ragionare il modello CAP?
CAP è un modello mentale per sistemi replicati sotto guasti di comunicazione. È più utile quando la rete è lenta, perde pacchetti o si spezza, perché è in quei momenti che i replica non possono accordarsi e si è costretti a scegliere tra:
- Consistenza: tutti vedono lo stesso valore più recente
- Disponibilità: il sistema continua a rispondere con esiti positivi
Aiuta a trasformare il concetto vago “i sistemi distribuiti sono difficili” in una decisione concreta di prodotto e ingegneria.
Quando mi trovo davvero in una situazione CAP?
Uno scenario CAP vero richiede entrambi:
- Replicazione (più di un nodo può servire/accettare operazioni per gli stessi dati)
- Una concreta possibilità di guasto di comunicazione (partizioni, timeout, latenze prolungate)
Se il tuo sistema è su un singolo nodo o non replichi lo stato, i tradeoff CAP non sono il problema centrale.
Cosa conta come partizione di rete nei sistemi reali?
Una partizione è qualsiasi situazione in cui parti del sistema non possono comunicare in modo affidabile o entro limiti di tempo richiesti—anche se ogni macchina è ancora in esecuzione.
Praticamente, “partizione” spesso si manifesta come:
- picchi di latenza che causano timeout
- pacchetti persi o blackhole
- firewall o problemi di routing
- nodi sovraccarichi che non rispondono in tempo
Dal punto di vista dell’applicazione, “troppo lento” può essere equivalente a “non raggiungibile”.
Qual è la differenza tra consistenza e disponibilità in termini utente?
Consistenza (C) significa che le letture riflettono la scrittura più recente riconosciuta, da qualsiasi replica. Gli utenti lo percepiscono come “l’ho modificato e tutti vedono la modifica”.
Disponibilità (A) significa che ogni richiesta riceve una risposta positiva (non necessariamente il dato più recente). Gli utenti lo percepiscono come “l’app continua a funzionare”, anche se i dati possono essere obsoleti.
Durante una partizione, normalmente non puoi garantire entrambe le cose per tutte le operazioni.
Perché non posso semplicemente scegliere consistenza e disponibilità e ignorare le partizioni?
Perché le partizioni non sono opzionali nei sistemi distribuiti che si estendono su più macchine, rack, zone o regioni. Se replichi, devi definire come comportarti quando i nodi non possono coordinarsi.
Quindi “tollerare le partizioni” significa che, quando la comunicazione si rompe, il sistema ha ancora un comportamento definito: o rifiuta/mette in pausa alcune azioni (favorendo la consistenza) o serve risultati alla meglio (favorendo la disponibilità).
Come si comporta una scelta ‘consistenza-centrica’ (CP) durante una partizione?
Se preferisci consistenza, di solito:
- rifiuti o ritardi le operazioni quando non puoi confermare l’accordo
- richiedi maggioranze/quorum per letture/scritture
- mostri errori come timeout, “riprovare” o modalità sola lettura
Questo è comune per trasferimenti di denaro, riserve di inventario e cambi di permessi—dove sbagliare è peggio che essere temporaneamente non disponibili.
Come si comporta una scelta ‘disponibilità-centrica’ (AP) durante una partizione?
Se preferisci disponibilità, di solito:
- continui ad accettare letture/scritture da entrambe le parti della partizione
- permetti che le repliche divergano temporaneamente
- riconcili il dato in seguito (regole di merge, risoluzione dei conflitti, revisione manuale)
Gli utenti vedranno meno errori netti, ma dati potenzialmente obsoleti, effetti duplicati senza idempotenza o aggiornamenti in conflitto da risolvere dopo.
Posso mixare scelte di consistenza e disponibilità a seconda dell’operazione?
Sì: puoi scegliere in modo diverso per endpoint o tipo di dato. Strategie comuni includono:
- Letture disponibili, scritture più rigorose (la navigazione funziona; gli aggiornamenti rischiosi possono fallire)
- Azioni critiche che falliscono chiuse (pagamenti, inventario, autenticazione) mentre funzionalità a basso rischio falliscono aperte (feed, analytics)
- Modalità degradata: mantieni le operazioni sicure e disabilita temporaneamente quelle rischiose
Questo evita un’unica etichetta globale “siamo AP/CP” che raramente rispecchia le esigenze reali del prodotto.
Quali garanzie di consistenza posso scegliere oltre a “forte” vs “eventuale”?
Opzioni utili includono:
- Consistenza forte (linearizzabile): massima correttezza; può bloccare o fallire durante le partizioni
- Consistenza eventuale: continua a servire; gli utenti possono vedere dati obsoleti finché le repliche non convergono
- Garanzie intermedie:
- read-your-writes (dopo aver aggiornato, non leggerai versioni precedenti dei tuoi dati)
- monotonic reads (una volta vista la versione N, non vedrai la N-1)
- causal consistency (se B dipende da A, tutti vedono A prima di B)
Scegli la garanzia più debole che impedisce lo “sbagliato” visibile all’utente che non sei disposto a tollerare.
Come testo e monitoro concretamente il comportamento CAP del mio sistema?
Convalida creando condizioni in cui il disaccordo diventa visibile:
- Simula partizioni/latenza in staging (e con cautela in produzione): blackhole del traffico, blocca porte, aggiungi ritardo/perdita
- Verifica il comportamento: le scritture vengono rifiutate o accettate? le letture diventano obsolete? come avviene il recupero/riconciliazione?
- Monitora segnali oltre i controlli di uptime:
- tassi di errore per tipo di operazione (lettura vs scrittura)
- indicatori di letture obsolete (violazioni read-your-writes, mismatch di version/ETag, lag)
- divergenza delle repliche (lag di replicazione, conteggi di apply falliti, tassi di conflitto)
- timeout e retry
Prepara anche runbook e messaggi agli utenti coerenti con il comportamento scelto (fail closed vs fail open).