Distribuerade system — grunderna
Kunna resonera om konsistens, fel och köer i system med flera maskiner.
Förkunskaper
- EAsynkron programmeringkrävs
- ENätverk — grundernakrävs
Intuition
Så fort systemet består av fler än en maskin gäller andra regler. Tre som du märker direkt:
1. Nätverket är opålitligt. Ett anrop som inte svarar kan ha misslyckats — eller lyckats med ett svar som gick förlorat. Du kan inte veta vilket. Därför måste operationer vara idempotenta: samma anrop två gånger ska ge samma resultat som en gång.
2. CAP: vid en nätverkspartition måste du välja mellan konsistens (vägra svara med möjligt gammal data) och tillgänglighet (svara med det du har). Du kan inte få båda. Ett betalsystem väljer C; ett rekommendationsflöde väljer A.
3. Köer kopplar isär. Lägg långsamma eller opålitliga steg (LLM-anrop, träning, e-post) bakom en kö. Då kan mottagaren vara nere utan att avsändaren märker det.
Formellt
Leveransgarantier — ingen är gratis:
| Garanti | Innebörd | Kostnad |
|---|---|---|
| At-most-once | skickas en gång, kan tappas | enklast |
| At-least-once | levereras, kan dubbleras | kräver idempotent mottagare |
| Exactly-once | levereras precis en gång | kräver transaktioner eller dedup med id; dyrt och ofta en illusion |
I praktiken bygger man at-least-once + idempotens, vilket ger exactly-once-beteende till rimlig kostnad.
Mönster som löser det mesta:
- Idempotensnyckel: klienten skickar ett id; servern minns resultatet per id.
- Outbox: skriv händelsen i samma databastransaktion som datan, publicera sedan asynkront. Löser «sparade i DB men kraschade före publicering».
- Circuit breaker: sluta anropa ett trasigt beroende och svara degraderat direkt.
- Exponentiell backoff med jitter: annars återförsöker alla klienter samtidigt och slår ut tjänsten igen.
- Dead letter queue: meddelanden som failat n gånger läggs åt sidan för granskning i stället för att blockera kön.
AI-grafens labbkörningar använder just detta: en körning har ett id, resultatet skrivs en gång, och en omkörning med samma id skapar inte en andra post.
Kod
import hashlib, time, random
# Idempotent skrivning: samma nyckel → samma resultat, inget dubbelarbete
async def skapa_korning(db, user_id: str, lab: str, filer: dict, idem_nyckel: str | None = None):
nyckel = idem_nyckel or hashlib.sha256(
f"{user_id}:{lab}:{sorted(filer.items())}".encode()).hexdigest()
if befintlig := await db.fetch_one("SELECT id FROM labs.lab_run WHERE idem_key=%s", (nyckel,)):
return befintlig["id"] # returnera samma resultat
return await db.fetch_one(
"INSERT INTO labs.lab_run (user_id, lab, idem_key) VALUES (%s,%s,%s) "
"ON CONFLICT (idem_key) DO UPDATE SET idem_key=EXCLUDED.idem_key RETURNING id",
(user_id, lab, nyckel))["id"]
# Backoff med jitter — utan jitter återkommer alla klienter samtidigt
async def anrop_med_backoff(fn, forsok=5, bas=0.5, tak=30):
for i in range(forsok):
try:
return await fn()
except TransientError:
if i == forsok - 1:
raise
vanta = min(tak, bas * 2 ** i) * (0.5 + random.random())
await asyncio.sleep(vanta)
Behärskning innebär
- Resonerar om konsistens och tillgänglighet vid partition
- Designar för idempotens och återförsök
- Använder köer för att koppla isär tjänster
Logga in för att göra övningarna och bygga upp din behärskning.
Källor
- Wikipedia — CAP theorem (CC BY-SA 4.0) — CC BY-SA 4.0
- AWS — Exponential backoff and jitter — fri läsning