Nordlet

← Dokumentácia / Slovník

Čo je idempotencia? Prečo ju účtovné a platobné API potrebujú

Ako opakovaná požiadavka vytvorí duplicitnú faktúru, čo zaručuje idempotentný kľúč, s akými chybovými stavmi sa musí vyrovnať a ako ju správne implementovať.

Operácia je vtedy idempotentná, ak má jej dvojnásobné vykonanie rovnaký účinok ako jednorazové vykonanie. Čítanie záznamu je prirodzene idempotentné. Vytvorenie faktúry však nie je - zavolajte ju dvakrát a máte dve faktúry, dve pohľadávky a zákazníka, ktorý sa pýta, prečo mu bola účtovaná sumárna platba dvakrát.

Tento nedostatok je dôležitý, pretože siete zlyhávajú špecifickým a nepríjemným spôsobom: požiadavka prebehne úspešne, ale odpoveď sa stratí. Klient zaznamená časový limit (timeout) a nedokáže rozlíšiť, či požiadavka „nikdy nedorazila“ alebo „dorazila, spracovala sa, no odpoveď sa stratila“. Jeho jedinou bezpečnou možnosťou je opakovanie pokusu, pričom práve toto opakovanie vytvorí duplicitu.

Prečo to účtovníctvo zhoršuje

Vo väčšine systémov je duplicita len nepríjemnosťou. V hlavnej knihe (ledger) ide o kumulujúci sa problém:

  • Duplicitná faktúra spotrebuje číslo zo súvislej číselnej rady bez medzier (gapless sequence), takže jej vymazanie neprichádza do úvahy - nemennénosť (immutability) znamená, že na jej opravu je potrebný dobropis (credit note), čo je dokument, ktorý váš zákazník uvidí.
  • Duplicita zaúčtuje zápisy do denníka, takže výnosy, DPH a pohľadávky sú nadhodnotené, kým si to niekto nevšimne.
  • Dostane sa do priznania k DPH a v Litve do registrov i.SAF.
  • Ak iniciovala platbu, peniaze sa reálne presunuli.

"Len skontrolovať duplicity" taktiež nefunguje, pretože dve skutočne samostatné faktúry pre toho istého zákazníka na rovnakú sumu v ten istý deň sú úplne bežné. Systém nedokáže odvodiť úmeru z obsahu - klient ju musí výslovne uviesť.

Ako fungujú idempotentné kľúče

Klient vygeneruje pre každú logickú operáciu unikátny kľúč a odošle ho spolu s požiadavkou:

POST /v1/sales/invoices/create
Idempotency-Key: 8f14e45f-ea1b-4c2b-9f3a-2d7c1e0b5a91

Server následne zaručí:

  1. Prvá požiadavka s týmto kľúčom - spracuje sa štandardne a výsledok sa uloží k danému kľúču.
  2. Opakovaný pokus s rovnakým kľúčom a rovnakým telom požiadavky (payload) - nevykoná sa znova, ale vráti sa uložená odpoveď.
  3. Rovnaký kľúč, odlišné telo požiadavky - zamietne sa. Kľúč predstavuje vyhlásenie, že ide o tú istú operáciu; odlišné telo znamená, že to tak nie je, a tiché vrátenie starého výsledku by bolo horšie ako chyba.
  4. Opakovaný pokus, zatiaľ čo pôvodná požiadavka stále prebieha - zamietne sa s chybou vhodnou na opakovanie namiesto súbežného vykonávania.

Štvrtý bod je presne to, čo naivné implementácie prehliadajú. Klient, ktorému vyprší časový limit po 5 sekundách a pokus zopakuje po 6 sekundách, narazí na svoju vlastnú prebiehajúcu požiadavku; bez ochrany pred súbehom (concurrency guard) prebehnú obe a vznikne duplicita, ktorej mal mechanizmus zabrániť.

Správne generovanie kľúčov

Kľúč musí identifikovať operáciu, nie samotný pokus:

  • Správne: UUID vygenerované raz, keď používateľ klikne na tlačidlo "vystaviť faktúru", ktoré sa opätovne použije pri každom opakovaní tohto kliknutia.
  • Správne: deterministický kľúč odvodený z vašej vlastnej domény - order-1041-invoice.
  • Nesprávne: nové UUID pre každý HTTP pokus. Každý opakovaný pokus dostane nový kľúč, takže každý pokus je novou operáciou a neimplementovali ste nič.
  • Nesprávne: hash tela požiadavky. Dve legálne identické operácie by potom kolidovali a druhá by sa potichu ignorovala.

Kľúče potrebujú dobu uchovávania (retention period) - dostatočne dlhú na pokrytie realistických okien pre opakovanie a výpadkov klienta, nie však navždy.

Čo to nevyrieši

Idempotencia chráni pred duplicitným vykonaním tej istej požiadavky. Nechráni pred:

  • Dvoma rôznymi používateľmi vytvárajúcimi rovnakú faktúru z rôznych relácií - to je problém obchodných pravidiel, ktorý sa rieši obmedzeniami jedinečnosti (uniqueness constraints), ako napríklad jedna faktúra na objednávku.
  • Logickými duplicitami s rôznymi kľúčmi.
  • Nedeterminizmom vnútri operácie - ak obslužný program (handler) vygeneruje časovú pečiatku alebo číslo, uložená odpoveď zachytí to prvé, čo je požadované správanie, ale oplatí sa tomu rozumieť.

Dopĺňa databázové obmedzenia, nenahrádza ich. Účtovná kniha (ledger) Nordlet využíva oboje: idempotentné kľúče zastavujú opakovozané požiadavky, zatiaľ čo unikátne indexy zastavujú duplicitné vyrovnávacie dávky (settlement batches) na ID výplaty, duplicitné prijaté faktúry podľa dodávateľa a čísla dokladu, a duplicitné odpisy podľa majetku a obdobia.

Ako to implementuje Nordlet

Odošlite hlavičku Idempotency-Key pri akejkoľvek požiadavke meniacej stav - neprázdny reťazec s maximálnou dĺžkou 255 znakov.

Prvá požiadavka s daným kľúčom sa vykoná a jej výsledok sa uloží. Opakovaný pokus s rovnakým kľúčom a rovnakým telom požiadavky vráti uloženú odpoveď bez opätovného vykonania, takže faktúra sa vytvorí presne raz bez ohľadu na to, koľkokrát sa volanie zopakuje.

Dva chybové stavy sú explicitné namiesto tichého zlyhania:

  • Rovnaký kľúč, odlišné telo požiadavky → zamietnuté: "Idempotency-Key was already used with a different request payload." Toto zachytáva skutočne nebezpečný prípad, keď klient opätovne použije kľúč a zároveň zmení sumu.
  • Rovnaký kľúč, pôvodná požiadavka stále prebieha → zamietnuté ako opakovateľné: "A request with this Idempotency-Key is still being processed, retry shortly." Žiadne súbežné dvojité vykonanie.

Kľúče sú vymedzené pre každú spoločnosť zvlášť (scoped per company), takže dva tenanty generujúce rovnaké UUID nikdy nekolidujú.

Komplementárna záruka funguje opačným smerom. Doručenie webových háčikov (webhook delivery) funguje v režime aspoň raz (at-least-once), čo znamená, že váš koncový bod občas prijme tú istú udalosť dvakrát - po vypršaní časového limitu, kedy doručenie v skutočnosti prebehlo úspešne, alebo pri opätovnom doručení. Každá udalosť nesie ID a váš obslužný program by mal zaznamenávať spracované ID a ignorovať opakovania. Idempotencia je vlastnosť, ktorú potrebujú obe strany integrácie; príručka konvencií API podrobne pokrýva stranu požiadaviek.

FAQ

Čo je to idempotentný kľúč?

Identifikátor vygenerovaný klientom, ktorý sa odosiela spolu s požiadavkou a umožňuje serveru rozpoznat opakovanie tej istej logickej operácie. Prvá požiadavka sa vykoná; opakované pokusy s rovnakým kľúčom vrátedia uložený výsledok namiesto opätovného vykonania.

Prečo účtovné API potrebujú idempotenciu?

Pretože duplicitný zápis vytvorí reálny dokument s reálnymi následkami - druhú faktúru, ktorá spotrebuje sériové číslo, zaúčtuje sa do hlavnej knihy a vstúpi do priznania k DPH. Na rozdiel od duplicitného čítania sa nedá jednoducho vymazať; je nutné ju opraviť dobropisom.

Mám pre každý opakovaný pokus vygenerovať nový kľúč?

Nie - to úplne popiera podstatu mechanizmu. Vygenerujte jeden kľúč pre jednu logickú operáciu a použite ho opätovne pri každom opakovaní tejto operácie. Nový kľúč pre každý pokus znamená, že sa s každým pokusom zaobchádza ako s novou operáciou.

Čo sa stane, ak opätovne použijem kľúč s iným telom požiadavky?

Požiadavka bude zamietnutá. Kľúč deklaruje, že ide o tú istú operáciu, takže zmenené telo znamená, že niečo nie je v poriadku - s najväčšou pravdepodobnosťou ide o chybu opätovného použitia kľúča na strane klienta. Tiché vrátenie skoršieho výsledku by bolo horšie.

Zabraňuje idempotencia všetkým duplicitám?

Nie. Zabraňuje duplicitnému vykonaniu tej istej požiadavky. Dvaja rôzni používatelia nezávisle vytvoria tú istú faktúru prostredníctvom dvoch rôznych požiadaviek, čo je problém obchodných pravidiel pre obmedzenia jedinečnosti, a nie niečo, čo by idempotentný kľúč dokázal identifikovať.