Arhitectura datelor și a sistemelor, de la zero Lecția 9 / 80

De ce sistemele distribuite sunt grele: cele 8 fallacies

Lista lui Peter Deutsch, restated pentru 2026. The network is reliable, latency is zero, bandwidth is infinite și cum fiecare fallacy strică săptămâna cuiva.

Bun venit la Modulul 2. Modulul 1 a fost despre arhitectura pe care o construiești înainte să fii nevoit să te gândești la sisteme distribuite: o mașină, un proces, o bază de date, o buclă rapidă și disciplina de a o ține așa cât de mult îți permite încărcarea. Stripe, Shopify și Basecamp au apărut în lecția 8 ca dovadă că „cât timp îți permite încărcarea” e de obicei mai lung decât cred oamenii.

Dar mai devreme sau mai târziu, pe majoritatea sistemelor ambițioase, singura mașină nu mai e de ajuns. Adaugi a doua. Și în momentul în care o faci, ai un sistem distribuit, cu toate noile moduri de eșec care vin cu el. Restul cursului ăsta e, într-un fel, o socoteală cu acel singur salt de la o cutie la două.

Înainte să ajungem la tipare specifice, trebuie să confruntăm minciunile. Sistemele distribuite sunt grele nu pentru că algoritmii sunt exotici, ci pentru că începătorii (și un număr îngrijorător de oameni care ar trebui să știe mai bine) raționează despre o rețea ca și cum ar fi un apel de funcție. Nu e. Diferențele sunt mari, scumpe și sursa a aproximativ fiecărui incident de producție din istoria software-ului distribuit.

Cea mai utilă enumerare a acelor minciuni e o listă de opt afirmații scrise de Peter Deutsch la Sun Microsystems în 1994, cu încă două adăugate de James Gosling în 1997. Lista se numește cele opt fallacies ale calculului distribuit. Fiecare e ceva care sună adevărat prima dată când îl auzi, e de fapt fals și te va face să livrezi cod care se rupe în moduri surprinzătoare în momentul în care mediul tău devine neprietenos.

Lista, în ordinea canonică:

  1. The network is reliable.
  2. Latency is zero.
  3. Bandwidth is infinite.
  4. The network is secure.
  5. Topology doesn’t change.
  6. There is one administrator.
  7. Transport cost is zero.
  8. The network is homogeneous.

Vom trece prin fiecare și ne vom uita la modul de eșec pe care-l produce. Teza acestei lecții e simplă: bug-urile din sistemele distribuite vin din a crede una dintre acestea. Fiecare tipar arhitectural din restul cursului există, în parte, ca să facă eșecul recuperabil în loc de catastrofic.

1. The network is reliable

Nu e. Rețelele pierd pachete, router-ele se restartează, link-urile fluctuează, se taie fibră, configurațiile BGP greșite trimit regiuni întregi în black hole, furnizorii de cloud au outage-uri aproximativ o dată pe trimestru și load balancer-ul din fața serviciului tău se blochează exact în momentul în care inginerul de on-call e ieșit la cină. Oricare dintre astea transformă „un apel sincron către alt serviciu” în „o cerere care s-ar putea să fi avut loc, s-ar putea să nu fi avut loc sau s-ar putea să fi avut loc de două ori”.

De ce e tentantă această fallacy? Pentru că în development apelezi alte servicii pe localhost, unde interfața de loopback chiar e fiabilă. Prima dată când codul tău rulează într-un mediu real cu o rețea reală între două mașini reale, presupunerea se rupe.

Modul concret de eșec: cererea ta POST /charge dă timeout. S-a întâmplat încasarea? Nu știi. Logica de retry intervine și trimite cererea din nou. Dacă originalul s-a încheiat cu succes, acum ai încasat clientul de două ori. Dacă n-ai scris idempotency keys în protocolul tău, ai un bug financiar pe care nimeni nu-l poate desface fără intervenție manuală.

Răspunsul arhitectural e un set mic de tipare pe care le vom acoperi în lecții ulterioare: idempotency keys, retry-uri cu exponential backoff și jitter, timeout-uri la fiecare hop, circuit breakers și presupunerea că orice apel de rețea ar putea avea nevoie să fie reluat fără pagube.

2. Latency is zero

Nu e. Un round trip pe un LAN rapid e de ordinul a jumătate de milisecundă. Între availability zones din interiorul unei regiuni de cloud, una până la două milisecunde. Între regiuni, douăzeci până la două sute. Între continente, o sută până la trei sute. Lumina are o limită de viteză și la fel ai și tu.

Fallacy-ul e tentant pentru că apelurile de funcții in-process sunt practic gratis la scara asta. IDE-ul tău arată aceeași sintaxă pentru un apel de metodă local și unul remote (mai ales în sistemele care generează stub-uri ca gRPC), așa că e ușor să uiți că unul costă nanosecunde și celălalt costă de zece milioane de ori mai mult.

Modul clasic de eșec e API-ul „chatty”. Endpoint-ul tău „list orders” returnează o listă de ID-uri de comenzi. Frontend-ul face apoi câte un apel pentru fiecare ID ca să obțină detaliile comenzii. Pe o singură mașină, ok. Printr-o rețea cu round-trip de douăzeci de milisecunde, aducerea a o sută de comenzi durează două secunde înainte ca vreuna dintre datele de comandă să fi sosit. E problema N+1 pe care s-ar putea s-o fi întâlnit în baze de date și devine dramatic mai rea pe rețea.

Răspunsul arhitectural e să proiectezi API-uri care fac batch, paginare și returnează suficiente date per cerere astfel încât numărul de round-trip-uri să rămână mic. Această regulă va reveni când discutăm design de API, gRPC versus REST și diferența dintre protocoalele request-response și cele de streaming.

3. Bandwidth is infinite

Nu e. Un link de 10 gigabit pe secundă sună ca mult până încerci să copiezi un snapshot de o sută de gigabytes pe el în timpul zilei de lucru, moment în care cererea fiecăruia altcuiva încetinește. Taxele de egress de la furnizorul tău de cloud sună triviale până livrezi un feature care copiază blob-uri mari între regiuni și descoperi la sfârșitul lunii că ai cheltuit mai mult pe bandwidth decât pe compute.

Acest fallacy e tentant pentru că în development datele sunt mici, link-ul e local și bandwidth-ul pare gratuit. În producție datele sunt mari, link-ul e partajat și factura e lunară.

Modurile de eșec sunt încărcări lente de pagină din cauza over-fetching, link-uri congestionate în timpul backup-urilor și taxe de egress surpriză. Vom petrece o lecție ulterioară pe data locality (ține compute-ul aproape de date, nu invers) și pe tiparele pentru mutarea de seturi mari de date fără să saturezi calea de producție.

4. The network is secure

Nu e. Trafic necriptat între servicii din același data centre a fost default-ul ani de zile, pe teoria că perimetrul data centre-ului era granița de securitate. Apoi cineva a intrat în perimetru (în fiecare an, de mai multe ori, la companii care ar trebui să știe mai bine) și a descoperit că centrul moale și mestecabil n-avea autentificare pe RPC-urile interne.

Fallacy-ul e tentant pentru că să scrii configurație TLS e enervant și interiorul VPC-ului tău se simte ca un loc privat. Nu e privat. Conține codul tău, dependințele tale, codul colegilor tăi, uneori un agent third-party care a fost compromis upstream și trafic de la o mie de mașini pe care nu le cunoști personal.

Cea mai bună practică actuală e zero-trust networking: fiecare serviciu se autentifică cu fiecare alt serviciu prin mutual TLS, fiecare apel poartă un token cu durată scurtă și „interiorul perimetrului” oferă exact zero încredere implicită. Asta e ceea ce unelte ca SPIFFE, Istio și Linkerd există ca să facă tractabil. Vom reveni la asta în lecțiile de securitate și operabilitate.

5. Topology doesn’t change

Se schimbă. Serviciile pornesc, cad, sunt redeployate, sunt reprogramate de Kubernetes pe alt nod, schimbă IP, schimbă port, își pierd înregistrarea DNS pentru treizeci de secunde în timpul unui rollout, sunt scalate de la trei la zece instanțe și înapoi, sunt deployate blue-green cu două seturi paralele de pod-uri. Topologia pe care ai schițat-o pe whiteboard e un instantaneu al unui singur moment.

Fallacy-ul e tentant pentru că în development adresa bazei de date e într-un fișier de config și nu se schimbă niciodată. În producție adresa e „oriunde a pus-o orchestrator-ul în minutul ăsta” și fișierul de config e o minciună învechită.

Modul de eșec e adresele IP hardcodate, TTL-urile DNS care mint, conexiunile la instanțe care nu mai există și întreaga categorie de bug în care un client cachează un endpoint de serviciu și continuă să încerce să-l atingă mult după ce s-a mutat. Răspunsul arhitectural e service discovery (Consul, etcd, servicii Kubernetes), connection pooling care se reîmprospătează și disciplina de a trata orice adresă ca efemeră.

6. There is one administrator

Nu e. Sistemul tău rulează pe infrastructură administrată de echipa ta de platform, furnizorul tău de cloud, furnizorul tău DNS, CDN-ul tău, autoritatea ta de certificate TLS, vendorul tău de observabilitate și probabil un API third-party sau trei. Fiecare e o organizație separată cu priorități separate, ferestre de mentenanță separate și idei separate despre ce înseamnă „graceful degradation”.

Fallacy-ul e tentant într-un startup de cinci oameni unde singurul inginer care știe totul e în cameră. Încetează să fie tentant prima dată când AWS are un outage în us-east-1 și descoperi că serviciul tău „intern” depinde de o funcționalitate exclusiv us-east-1 a unui SaaS third-party pe care uitaseși că-l foloseai.

Consecința arhitecturală e maparea dependințelor, analiza riscului furnizorilor, principiul proiectării pentru graceful degradation când un upstream e jos și înțelegerea că fiabilitatea sistemului tău e produsul fiabilității fiecărei componente, nu doar al părților pe care le-ai scris tu.

7. Transport cost is zero

Nu e. Traficul cross-AZ în AWS costă aproximativ un cent per gigabyte fiecare direcție începând cu 2026. Cross-region e de mai multe ori mai mult. Taxele NAT Gateway adaugă încă o taxă per gigabyte procesat. Egress-ul către internet e cel mai scump dintre toate. O arhitectură de servicii „chatty” care traversează o graniță AZ la fiecare cerere poate produce o factură care eclipsează costul de compute.

Acest fallacy e tentant pentru că rețeaua nu apare în factura ta orară de compute. Apare într-un alt rând pe care nimeni nu se uită până CFO-ul întreabă despre el.

Modul de eșec e factura surpriză. Vom revizita asta când acoperim tiparele multi-AZ și multi-region și compromisul dintre disponibilitate (mai multe zone) și cost (fiecare traversare de zonă are un preț).

8. The network is homogeneous

Nu e. Traficul tău traversează mașini Linux, mașini Windows, mașini ARM și mașini x86. Trece prin router-e de la trei vendori. Atinge load balancere care interpretează HTTP ușor diferit unul de celălalt. Biblioteca TLS de pe client se comportă ușor diferit de biblioteca TLS de pe server. Unele hop-uri suportă HTTP/3, unele doar HTTP/1.1. Payload-ul tău gRPC e ok până traversează un proxy care face buffering într-un mod la care clientul nu se aștepta.

Fallacy-ul e tentant pentru că în development totul e aceeași imagine Docker. În producție totul nu e.

Modul de eșec e bug-ul de protocol-mismatch, care e unul dintre cele mai demoralizante feluri: sistemul funcționează, în mare parte, până când exact o combinație de versiune de client și versiune de proxy interacționează prost, iar rezultatul e o rată sporadică de erori de cinci la sută pe care nimeni nu o poate reproduce în staging. Răspunsul arhitectural e disciplina de interoperabilitate: rămâi la protocoale standard, versionează-ți API-urile, testează împotriva proxy-urilor reale în spatele cărora vei rula și presupune că „omogen” e o ficțiune de mediu de development.

Ce poate merge prost într-un singur apel de rețea

Opt fallacies sunt multe de ținut în cap, așa că ajută să le vezi ca opt moduri în care o singură cerere poate eșua.

flowchart TD
    A[Client makes one network call] --> B{What can go wrong?}
    B --> F1[Packet lost or duplicated]
    B --> F2[Round trip slower than expected]
    B --> F3[Payload too big for the link]
    B --> F4[Traffic intercepted or replayed]
    B --> F5[Destination has moved]
    B --> F6[Upstream dependency unavailable]
    B --> F7[Crosses a paid boundary]
    B --> F8[Proxy or library mismatch]
    F1 --> R[Architectural responses]
    F2 --> R
    F3 --> R
    F4 --> R
    F5 --> R
    F6 --> R
    F7 --> R
    F8 --> R
    R --> P1[Idempotency, retries, timeouts]
    R --> P2[Batching, locality, caching]
    R --> P3[mTLS, zero trust, tokens]
    R --> P4[Service discovery, healthchecks]
    R --> P5[Cost-aware topology]
    R --> P6[Versioning, contract tests]

Fiecare tipar arhitectural din restul acestui curs va ateriza undeva pe partea dreaptă a acelei diagrame. Circuit breakers sunt un răspuns la fallacy 1. Straturile de cache sunt un răspuns la fallacies 2 și 3. Service mesh-urile sunt un răspuns la fallacies 4, 5 și 8. Designul multi-region e un pariu calibrat împotriva fallacy 6 (administratori diferiți, raze de impact diferite) și un management atent al fallacy 7 (cost). Când citești despre un tipar și te întrebi de ce s-a deranjat cineva, răspunsul e aproape mereu: pentru că cineva a crezut una dintre cele opt fallacies și s-a ars.

Cum să folosești lista

Nu trebuie să memorezi lista ca atare. Trebuie să-ți internalizezi obiceiul de a întreba, înainte să scrii orice cod care traversează o graniță de proces: pe care dintre acestea sunt pe cale să o presupun?

Un exercițiu util, mai ales în design reviews, e să iei graful de apeluri propus și să mergi prin el apel cu apel. Pentru fiecare: cum se comportă când răspunsul e lent? Când răspunsul nu vine niciodată? Când răspunsul vine de două ori? Când destinația s-a mutat? Când link-ul e saturat? Când un proxy în mijloc are un bug? Când vine factura? Dacă răspunsul la oricare e „nu ne-am gândit la asta”, apelul ăla e un incident viitor.

Cea mai mare parte a restului cursului e despre a răspunde corect la acele întrebări: cu timeout-uri, idempotency, observabilitate, service discovery, traffic shaping, contract testing și disciplina plictisitoare, dar esențială, de a trata rețeaua ca pe un mediu ostil, cu pierderi, probabilist, în loc de un apel de funcție fiabil.

Ce urmează

Fallacies-ele îți spun ce e greu. Nu-ți spun ce să faci în legătură cu asta. Următoarele două lecții introduc compromisul central care organizează restul teoriei sistemelor distribuite.

Lecția 10 e teorema CAP în practică: în prezența unei partiții de rețea (fallacy 1, dusă la extrem), trebuie să alegi între consistență și disponibilitate pentru fiecare operație. Vom trece prin ce spune CAP de fapt, cele mai comune interpretări greșite și alegerile reale pe care le fac sisteme reale (DNS, registre bancare, Cassandra).

Lecția 11 extinde asta la PACELC, care adaugă cazul de zi cu zi despre care CAP tace: chiar și când nu se întâmplă nicio partiție, faci compromis între latență și consistență la fiecare scriere. Odată ce vezi cadranul PACELC, spațiul de design al bazelor de date distribuite încetează să arate ca o grădină zoologică și începe să arate ca un set de alegeri explicite, denumite.

Suntem acum ferm în teritoriul în care lecțiile Modulului 1 (începe simplu, amână complexitatea) se întâlnesc cu realitatea că unele sisteme chiar au nevoie să scaleze dincolo de o singură mașină. Cunoașterea fallacies-elor e ce-ți permite să arhitecturizezi a doua mașină fără să regreți imediat.

Citări și lecturi suplimentare

  • Peter Deutsch, „The Eight Fallacies of Distributed Computing” (1994), rezumat la https://en.wikipedia.org/wiki/Fallacies_of_distributed_computing (accesat 2026-05-01). Memo-ul original Sun Microsystems e reprodus în mai multe colecții arhivate; articolul Wikipedia leagă la PDF-urile canonice.
  • Arnon Rotem-Gal-Oz, „Fallacies of Distributed Computing Explained” (2006), cea mai citată extindere a listei cu exemple concrete (accesat 2026-05-01).
  • Pagini de prețuri AWS pentru trafic cross-AZ, NAT Gateway și inter-region, pentru numerele folosite în fallacy 7 (accesat 2026-05-01).
  • Pentru fallacy-ul de securitate: lucrările BeyondCorp de la Google (2014 încoace) și specificația SPIFFE (https://spiffe.io/) pentru forma modernă a zero-trust networking în interiorul data centre-urilor.
Caută