Skip to content
Language:hu Author:Laborci Gergely
resilience

Időkeret, ismétlés és idempotencia

Célok

A lecke végére el tudod magyarázni a timeout és a deadline különbségét, meg tudod mondani, mikor biztonságos ismételni egy hívást, és hogyan teszi biztonságossá az idempotencia az ismétlést.

Miért kell?

A távoli hívás kimenete lehet ismeretlen (6.1). Hibatűrés szempontjából az a kérdés, mennyit érdemes várni, mikor kell ismételni, és hogyan őrizhető meg az üzleti állapot helyességét bizonytalan eredmény mellett. Az ismétlés a leggyakoribb védekezés és egyben a leggyakoribb hibaforrás.

Timeout és deadline

A timeout egy várakozás időtartama. A deadline időpont: ameddig a hívó a választ még megvárja. A gRPC dokumentációja szerint a timeout a hívás maximális időtartama, és úgy lesz belőle deadline, hogy hozzáadják a hívás indulási idejéhez. A gRPC alapértelmezetten nem állít be deadline-t, ezért a kliensben mindig explicit, reális határidőt kell megadni.

Láncolt hívásoknál a deadline-t tovább kell adni: ha a kliens időkerete véges, egy belső hívás nem kaphat újra a teljes keretet. A gRPC a hátralevő időt továbbítja, nem az abszolút időpontot, így nincs óraeltérési probléma. A lejárat után a szerver oldalán a hívás megszakadhat, de a már elindított munkát az alkalmazásnak kell leállítania; a már végrehajtott változást a megszakítás nem vonja vissza.

Mely hibát kell ismételni?

Átmeneti hálózati hiba vagy túlterhelés később javulhat. Hibás bemenet, hiányzó jogosultság és üzleti elutasítás ismétléssel nem javul. Az automatikus ismétlés csak a megállapodott hibaosztályra, korlátozott számban és időkereten belül történjen.

Az RFC 9110 szerint egy metódus idempotens, ha több azonos kérés szándékolt hatása ugyanaz, mint egyé; a szabványban a PUT, a DELETE és a biztonságos metódusok ilyenek. Az idempotens kérést automatikusan meg lehet ismételni, ha a kapcsolati hiba a válasz elolvasása előtt történik. Egy nem idempotens metódusú kérést a kliens nem ismételhet automatikusan, hacsak nem tudja, hogy a kérés szemantikája mégis idempotens, vagy nem tudja kideríteni, hogy az eredeti kérés nem hajtódott végre.

Idempotenciakulcs

Módosító kérésnél a kliens ugyanahhoz a logikai művelethez azonos idempotenciakulcsot küldhet. A szerver tartósan tárolja a kulcsot, a kérés jelentését és az eredményt; ismétléskor nem hajtja végre újra, hanem a korábbi eredményt adja. Az AWS leírása is hangsúlyozza: mellékhatással járó API-t csak akkor biztonságos ismételni, ha idempotens.

flowchart TD
  A["Módosító kérés + kulcs"] --> B{"A kulcs már ismert?"}
  B -->|"Nem"| C["Atomi felvétel és végrehajtás"]
  C --> D["Eredmény tartós rögzítése"]
  B -->|"Igen"| F{"Ugyanaz a kérés?"}
  F -->|"Nem"| G["Kulcsütközés: elutasítás"]
  F -->|"Igen"| H["Korábbi eredmény visszaadása"]

A kulcs hatóköre számít (felhasználóhoz és művelettípushoz köthető). Azonos kulccsal eltérő tartalmat el kell utasítani. A párhuzamos első kéréseket atomi egyediségi kulcs védi, nem egy előzetes olvasás. Az „éppen fut” állapotot is meg kell tervezni, és a kulcs lejárata utáni ismétlésre már nincs védelem, ezért a megőrzési idő a szerződés része. Az IETF Idempotency-Key fejléc-szabványa jelenleg vázlat (draft), nem kiadott szabvány.

Ismétlés késleltetéssel: backoff és jitter

Azonnali, korlátlan ismétlés ront a helyzeten. Exponenciális backoff esetén az ismétlések közti várakozás minden próbálkozással nő, jitter véletlenszerűen szórja a várakozást, hogy a kliensek ne egyszerre próbálkozzanak újra. Az AWS leírása szerint érdemes korlátot szabni a várakozásnak és a próbálkozások számának. Egy lehetséges szemléltető szabály: véletlen várakozás 0 és min(maximum, alap × 2^próbálkozás) között; a paramétereket nem általános receptből, hanem a függőség és az igény alapján kell választani.

Retry storm és a felelős réteg

Ha a kliens, az átjáró és a szolgáltatás mind ismétel, a terhelés megsokszorozódik. Az AWS példája szerint rétegenként három próbálkozás öt rétegen át a mélyen levő adatbázis terhelését akár 3⁵ = 243-szorosára növelheti. Ezért az AWS szerint alacsony költségű műveleteknél egyetlen ponton érdemes ismételni a hívási láncban, és csak akkor, ha a függőség egészségesnek látszik. Ha a kiszolgáló túlterhelés miatt 503-at ad, a Retry-After fejléc javasolhat várakozási időt.

Gyakori tévhitek

Állítás Pontosítás
„A retry mindig növeli a megbízhatóságot.” Túlterhelésnél a terhelést növeli; rétegenként szorzódik.
„A timeout után a kérés nem történt meg.” Lehet, hogy végrehajtódott.
„Az idempotens művelet ugyanazt a választ adja.” A hatás ugyanaz, a válasz eltérhet (például első 204, később 404 törlésnél).
„A kérésazonosító egyben idempotenciakulcs.” Csak ha a szerver ilyen szabályt implementál.

Röviden

  • A deadline időpont, a timeout időtartam; láncban a hátralevő időt kell továbbadni.
  • Automatikusan csak átmeneti hibát és idempotens műveletet szabad ismételni.
  • Az idempotenciakulcs tartós, atomi, hatókörhöz kötött, lejáratú.
  • Backoff és jitter szórja az ismétléseket; egyetlen réteg felelős az ismétlésért.

Ellenőrző kérdések

  1. Miért nem ismételhető automatikusan egy nem idempotens kérés?
  2. Mi történjen azonos kulccsal eltérő bemenetnél?
  3. Hogyan szorzódik a terhelés több réteg független ismétlésénél?

Források

Prerequisites (2)

  • távoli hívás
  • kommunikációs szerződés

Teaches (3)

  • timeout és deadline
  • retry
  • idempotencia