Cele opt erori ți-au spus de ce sistemele distribuite sunt grele. Teorema CAP îți spune despre compromisul specific și inevitabil care iese din una dintre acele erori (rețeaua e fiabilă, eroarea 1) atunci când o iei în serios. CAP este cel mai citat și cel mai prost înțeles rezultat din sistemele distribuite. E pe orice listă de întrebări la interviuri de arhitectură, pe pagina de marketing a oricărui producător de baze de date și în spatele oricărui „suntem eventually consistent” fluturat din mână într-o ședință de design.
Lecția asta face două lucruri. Întâi, formulează ce spune cu adevărat teorema CAP, ceea ce e o afirmație mai îngustă și mai atentă decât interpretarea uzuală. În al doilea rând, parcurge alegerile reale pe care le fac sistemele reale, cu exemple, ca data viitoare când cineva îți spune că baza lui de date e „AP” să știi ce întrebare să pui mai departe.
Ce spune CAP de fapt
Eric Brewer a ținut un keynote la conferința PODC în 2000 în care a conjecturat că un sistem distribuit cu date partajate poate oferi cel mult două din trei garanții: consistency, availability și partition tolerance. Seth Gilbert și Nancy Lynch au transformat conjectura într-o teoremă formală în 2002.
Cele trei garanții, în sensul precis în care le folosește teorema:
- Consistency (mai exact, linearizability): fiecare read vede cea mai recentă scriere, ca și cum sistemul ar avea o singură copie actualizată.
- Availability: fiecare cerere către un nod care nu e în pană returnează un răspuns într-un timp finit. Nu neapărat un răspuns rapid, dar un răspuns care nu e „refuz”.
- Partition tolerance: sistemul continuă să funcționeze chiar și atunci când mesaje arbitrare între noduri sunt pierdute sau întârziate la nesfârșit.
Teorema spune că nu le poți avea pe toate trei simultan. Interpretarea inutilă e „alege două din trei”, care a produs mai multă confuzie decât orice altă frază din domeniu.
Motivul pentru care „alege două” e greșit e că partition tolerance nu e cu adevărat o alegere în niciun sistem distribuit netrivial. Dacă sistemul tău rulează pe mai mult de o mașină conectate printr-o rețea, partițiile se pot întâmpla. Erorile o garantează. Nu ai opțiunea de a renunța la partition tolerance hotărând că ai prefera CA. Ai opțiunea de a alege ce face sistemul tău atunci când, inevitabil, apare o partiție.
Așadar, modul precis de a citi CAP este acesta: în prezența unei partiții de rețea, trebuie să alegi între consistency și availability pentru fiecare operație. În afara unei partiții, compromisul nu se aplică și le poți avea pe ambele. CAP nu spune nimic despre cazul de zi cu zi. Vom rezolva asta cu PACELC în lecția următoare.
Această citire precisă are două consecințe importante. Întâi, face ca „sistem AP” să fie doar jumătate de propoziție. AP pentru care operații? În ce condiții? Multe sisteme sunt AP pentru unele operații și CP pentru altele, iar operatorul alege per apel. În al doilea rând, face ca întrebarea despre ce face sistemul tău în timpul unei partiții să fie mult mai concretă decât versiunea de manual. Munca de design interesantă e în comportamentul în timpul partiției.
Cele trei categorii, în practică
Sistemele reale stau într-unul din trei locuri.
CP: refuză mai degrabă decât să servească date învechite
Un sistem CP, când detectează o partiție, preferă să eșueze închis. Un nod care nu poate ajunge la un quorum de colegi va refuza scrieri noi (și posibil read-uri noi) mai degrabă decât să servească o valoare pe care nu o poate dovedi că e curentă. Utilizatorul primește o eroare. Datele rămân corecte.
Sistemele arhetipice CP sunt serviciile de coordonare: ZooKeeper, etcd și Consul (în modul său strongly-consistent). Astea sunt sistemele pe care le folosim pentru leader election, pentru a stoca configurări și pentru a servi ca sursă de adevăr pentru „cine deține lock-ul ăsta” într-un sistem distribuit mai mare. Sunt CP pentru că, dacă le folosești pentru coordonare, cel mai rău lucru pe care îl pot face e să dea două lock-uri către două noduri care amândouă cred că au lock-ul. În cazul partiției, refuzul lock-ului e corect; pretinderea că îl acordă de două ori e catastrofală.
Arhetipul registrului bancar e aceeași logică. Dacă două regiuni ale registrului unei bănci nu sunt de acord dacă contul are 100 de dolari și partiția dintre ele împiedică reconcilierea, comportamentul corect al băncii e să refuze retragerea mai degrabă decât să riște ca ambele părți să o autorizeze. Mai bine un client supărat decât două retrageri din aceiași o sută de dolari.
Sistemele CP sacrifică availability în timpul unei partiții pentru garanția că nu vei citi niciodată date învechite și nu vei vedea niciodată o scriere care apoi e pierdută în tăcere. Pentru date unde corectitudinea e primordială și availability poate fi sacrificată temporar, asta e alegerea corectă.
AP: servește orice ai
Un sistem AP, în timpul unei partiții, preferă să eșueze deschis. Fiecare nod continuă să răspundă la cereri folosind cele mai recente date pe care le are. Când partiția se vindecă, versiunile divergente trebuie reconciliate, fie automat (last-write-wins, vector clocks, CRDTs), fie prin logică la nivel de aplicație.
DNS e cel mai familiar sistem AP din lume. Resolverele DNS din laptopul tău, din ISP-ul tău și din diversele cache-uri dintre tine și serverul autoritativ pot ține toate versiuni ușor diferite ale unei înregistrări. Nu există o singură copie actualizată. Sistemul e „eventually consistent” în sensul că, dat fiind suficient timp și suficiente expirări de TTL, cache-urile converg către cea mai recentă valoare. Dar într-un moment dat, doi clienți pot rezolva același nume către două adrese diferite. Sistemul rămâne în picioare. Costul e că uneori nimerești răspunsul de ieri.
Cassandra, în modul său tunable implicit, e o bază de date AP. Acceptă scrieri pe orice replică, le propagă în fundal și folosește last-write-wins (cu timestamp-uri) pentru a rezolva conflictele când două scrieri pentru aceeași cheie ajung pe părți diferite ale unei partiții. S3 a fost faimos eventually consistent timp de mulți ani și a adăugat strong read-after-write consistency abia în 2020.
AP e alegerea corectă atunci când datele învechite sunt tolerabile, iar indisponibilitatea nu. Un coș de cumpărături e exemplul canonic: dacă două dispozitive adaugă același SKU în coș în timpul unei partiții, ai prefera să unești coșurile mai târziu decât să refuzi operația acum. În cel mai rău caz, utilizatorul șterge duplicatul. În cel mai bun caz, nici nu observă.
Tunable: alege per operație
Cele mai utile sisteme în producție sunt cele care îți permit să alegi, per operație, cât de puternică trebuie să fie consistency. Cassandra e exemplul de manual. Fiecare read și fiecare write specifică un consistency level: ONE, QUORUM, ALL, LOCAL_QUORUM și așa mai departe. Un read la QUORUM combinat cu un write la QUORUM îți dă comportament linearizable atâta timp cât majoritatea replicilor pot fi atinse. Un read la consistency level ONE returnează primul răspuns pe care ți-l dă orice replică, care poate fi învechit, dar e rapid și rămâne disponibil chiar și când majoritatea cluster-ului e izolată de partiție.
DynamoDB are o versiune mai simplă a aceleiași idei: fiecare read poate fi fie strongly consistent (care costă mai mult și refuză să servească în timpul unei partiții care afectează partition key relevantă), fie eventually consistent (mai ieftin, mai rapid, poate returna date învechite).
Sistemele tunable sunt felul în care aplicațiile reale obțin ce e mai bun din ambele lumi. Fluxul de login e citit la QUORUM: credențialele învechite ar fi un bug real. Feed-ul de „activitate recentă” e citit la ONE: un feed învechit cu treizeci de secunde e bine.
De unde vin de fapt partițiile
Teorema CAP e uneori acuzată că e academică pe motiv că „rețelele reale rar partiționează”. Asta nu e adevărat, mai ales în mediile cloud. Cauzele reale ale comportamentului de partiție, pe un sistem real în producție în 2026, includ:
- O întreagă availability zone care își pierde conectivitatea pentru zeci de secunde în timpul unei schimbări de routing.
- Un security group prost configurat care blochează traficul intra-cluster pe un anumit port pentru un singur rolling deploy.
- Un nod coordonator supraîncărcat care pierde heartbeat-uri și e marcat incorect ca partiționat de restul cluster-ului.
- Un link cross-region care se degradează scurt în timpul unei reparații de fibră.
- Un bug într-un service mesh care face ca handshake-urile mutual TLS să eșueze pentru o anumită combinație de versiuni.
Lecția e că partițiile în practică sunt rareori scenariul dramatic „centrul de date cade în mare”. Sunt mici sclipiri de zece secunde care se întâmplă suficient de des încât orice cluster netrivial va vedea una în majoritatea săptămânilor. CAP e cadrul care îți spune ce ar trebui să facă sistemul tău în acele zece secunde.
O diagramă de decizie
Întrebarea cheie, la fiecare operație, e ce să faci în timpul unei partiții: să refuzi sau să servești date posibil învechite.
flowchart TD
A[Client makes a request] --> B{Network partition between this node and the rest?}
B -- No --> C[Serve normally: consistent and available]
B -- Yes --> D{What did the operator choose for this operation?}
D -- CP path --> E[Refuse the request, return error]
D -- AP path --> F[Serve from local replica, may be stale]
F --> G[Reconcile after the partition heals]
E --> H[Caller decides whether to retry or fail]
Ramura CP e ce fac ZooKeeper, etcd și un read strongly-consistent pe Spanner. Ramura AP e ce fac DNS, un read implicit Cassandra și un read eventually-consistent pe DynamoDB. Sistemele reale au ambele ramuri disponibile și lasă aplicația să aleagă.
Interpretări greșite frecvente
Câteva afirmații despre CAP pe care le vei vedea în sălbăticie și care merită semnalate:
- „Baza noastră de date e CA.” Nu există așa ceva într-un sistem distribuit. Bazele de date pe o singură mașină (un Postgres nereplicat) sunt tehnic CA în sensul trivial că nu există partiții de tolerat, dar de îndată ce le replici pe mai multe mașini trebuie să alegi un comportament de partiție.
- „Suntem AP, deci suntem mereu disponibili.” Nu e adevărat. Ești disponibil în timpul unei partiții. În afara uneia, ești supus aceluiași set de eșecuri ca oricine altcineva. „Available” în CAP are și un sens precis: fiecare nod care nu e în pană răspunde. Un sistem poate fi AP și totuși să fie căzut pentru că toate nodurile lui au eșuat.
- „Eventual consistency e suficient de bună.” Uneori e, alteori nu. Întrebarea e ce face aplicația când observă date învechite. Dacă răspunsul e „utilizatorul nu observă și sistemul reconciliază”, e bine. Dacă răspunsul e „taxăm dublu un client”, nu e bine.
- „Am ales AP pentru performanță.” Asta combină două compromisuri diferite. AP versus CP e despre comportamentul în timpul partiției. Latency versus consistency e compromisul de zi cu zi și e subiectul PACELC. De cele mai multe ori, când cineva spune că a ales AP pentru performanță, vrea să spună că a ales EL (eventual consistency în afara partițiilor pentru low latency) și încă nu s-a gândit cu atenție la ce se întâmplă în timpul unei partiții.
Ultimul punct e ceea ce motivează lecția următoare.
Ce ratează CAP
CAP e un răspuns util de 90% și unul înșelător de 100%. Punctul lui orb e că descrie sistemul doar în timpul unei partiții, iar sistemul tău își petrece aproape tot timpul neaflat într-o partiție. În cazul de zi cu zi, încă faci un compromis cu ceva. Teorema CAP pur și simplu nu spune asta.
În 2010, Daniel Abadi a subliniat asta într-un articol și o postare pe blog și a propus o extensie numită PACELC. Acronimul e stângaci, iar ideea e simplă: „dacă Partition, alege Availability sau Consistency; Else, alege Latency sau Consistency.” Prima jumătate e CAP. A doua jumătate e partea pe care CAP a uitat-o.
PACELC transformă spațiul de design dintr-o decizie pe o axă într-una pe două axe, iar a doua axă e cea care contează cel mai mult în majoritatea zilelor, pentru că în majoritatea zilelor nu există partiție. Vom petrece lecția următoare pe ea, inclusiv o clasificare în patru cadrane a principalelor baze de date distribuite (Cassandra, DynamoDB, Spanner, Riak, FaunaDB), ca să vezi dintr-o privire ce compromisuri face fiecare.
Pentru moment, concluzia acestei lecții e că teorema CAP e un cadru util pentru o întrebare specifică (ce face sistemul tău în timpul unei partiții de rețea) și că răspunsul e per operație, nu per sistem. Ori de câte ori auzi „suntem un sistem AP”, întrebarea ta de continuare ar trebui să fie „pentru care operații, cu ce strategie de reconciliere și ce face restul sistemului?” Dacă răspunsul e vag, tocmai ai identificat o decizie arhitecturală care încă nu a fost luată.
Citate și lecturi suplimentare
- Eric Brewer, “Towards Robust Distributed Systems”, PODC keynote, 2000. Slides and discussion archived at
https://www.cs.berkeley.edu/~brewer/cs262b-2004/PODC-keynote.pdf(retrieved 2026-05-01). - Seth Gilbert and Nancy Lynch, “Brewer’s conjecture and the feasibility of consistent, available, partition-tolerant web services”, ACM SIGACT News, 2002. The formal proof.
- Eric Brewer, “CAP Twelve Years Later: How the Rules Have Changed”, IEEE Computer, 2012. Brewer’s own retrospective on what the theorem was and was not.
- Martin Kleppmann, “A Critique of the CAP Theorem” (2015), available at
https://arxiv.org/abs/1509.05393. The most thorough technical critique of the standard interpretation. - For the operational behaviour of CP systems: the etcd documentation on quorum and split-brain, and the ZooKeeper “ZAB” protocol description.
- For tunable consistency: Apache Cassandra documentation on consistency levels, and the DynamoDB developer guide section on read consistency. Both retrieved 2026-05-01.