Nordlet

← Dokumentācija / Glosārijs

Kas ir idempotence? Kāpēc grāmatvedības un maksājumu API tā ir nepieciešama

Kā atkārtots pieprasījums rada dubultu rēķinu, ko garantē idempotences atslēga, kādas kļūdu situācijas tai ir jāatrisina un kā to pareizi ieviest.

Operācija ir idempotenta, ja tās izpilde divas reizes dod tādu pašu rezultātu kā izpilde vienu reizi. Datu nolasīšana pēc dabas ir idempotenta. Rēķina izveide tāda nav – izsauciet to divas reizes, un jūs saņemsiet divus rēķinus, divus debitoru parādus un klientu, kurš jautā, kāpēc viņam ir piestādīts dubultā rēķins.

Šī problēma pastāv tāpēc, ka tīkli darbojas specifiskā, nepatīkamā veidā: pieprasījums ir izdevies, bet atbilde ir pazudusi. Klients redz noildzi (timeout) un nespēj atšķirt, vai ziņojums "nekad nav pienācis" vai "ir pienācis, nostrādājis, bet atbilde ir pazudusi". Vienīgā drošā iespēja šķiet atkārtot pieprasījumu, un tieši šī atkārtošana rada dublikātu.

Kāpēc grāmatvedībā situācija ir vēl sarežģītāka

Vairumā sistēmu dublikāts ir vienkārši apgrūtinājums. Virsgrāmatā tā ir kumulatīva problēma:

  • Dubultais rēķins izlieto numuru no nepārtrauktas secības (gapless sequence), tāpēc tā dzēšana nav iespējama – nemainīgums (immutability) nozīmē, ka labošanai ir nepieciešams kredītrēķins, kas ir dokuments, ko redz jūsu klients.
  • Dublikāts grāmato žurnāla ierakstus, tāpēc ieņēmumi, PVN un debitoru parādi ir uzpūsti, līdz kāds to pamana.
  • Tas nonāk PVN deklarācijā un i.SAF reģistros.
  • Ja tas aktivizēja maksājumu, nauda reāli ir aizplūduši.

Pieeja "vienkārši pārbaudīt dublikātus" arī nedarbojas, jo divi patiesi neatkarīgi rēķini vienam un tam pašam klientam par vienādu summu tajā pašā dienā ir pilnīgi normāla parādība. Sistēma nevar nolasīt nolūku no satura – klientam tas ir jānorāda pašam.

Kā darbojas idempotences atslēgas

Klienta lietotne ģenerē unikālu atslēgu katrai loģiskajai operācijai un nosūta to kopā ar pieprasījumu:

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

Pēc tam serveris garantē:

  1. Pirmais pieprasījums ar šo atslēgu – tiek apstrādāts parastajā kārtībā, un rezultāts tiek saglabāts, piesaistot to atslēgai.
  2. Atkārtots pieprasījums ar to pašu atslēgu un to pašu datu pakotni (payload) – netiek izpildīts atkārtoti; tiek atgriezta saglabātā atbilde.
  3. Tā pati atslēga, bet cita datu pakotne – tiek noraidīta. Atslēga apliecina, ka tā ir tā pati operācija; atšķirīgs pamatteksts nozīmē, ka tā nav, un klusa vecā rezultāta atgriešana būtu sliktāka par kļūdu.
  4. Atkārtots pieprasījums, kamēr sākotnējais joprojām tiek izpildīts – tiek noraidīts ar kļūdu, kuru var mēģināt atkārtot, nevis izpildīts vienlaicīgi.

Ceturto punktu virkne vienkāršu ieviešanu to palaiž garām. Klients, kurš saņem noildzi pēc 5 sekundēm un mēģina vēlreiz pēc 6 sekundēm, sadursies ar savu pašu notiekošo pieprasījumu; bez vienlaicības kontroles (concurrency guard) abi tiks izpildīti, un jūs saņemsiet dublikātu, kura novēršanai šis mehānisms tika radīts.

Pareiza atslēgu ģenerēšana

Atslēgai ir jāidentificē operācija, nevis mēģinājums:

  • Pareizi: UUID, kas ģenerēts vienu reizi, kad lietotājs noklikšķina uz "izrakstīt rēķinu", un tiek izmantots atkārtoti katrā šī klikšķa mēģinājumā.
  • Pareizi: deterministiska atslēga, kas izriet no jūsu pašu domēna – order-1041-invoice.
  • Nepareizi: jauns UUID katram HTTP mēģinājumam. Katrs mēģinājums saņem jaunu atslēgu, tāpēc katrs mēģinājums ir jauna operācija, un jūs neesat ieviesis neko.
  • Nepareizi: datu pakotnes jaucējkods (hash). Divas likumīgi identiskas operācijas tādā gadījumā sadursies, un otrā tiks klusējot norīta.

Atslēgām ir nepieciešams saglabāšanas periods – pietiekami ilgs, lai aptvertu reālistiskus atkārtotas izpildes logus un klientu darbības traucējumus, bet ne mūžīgi.

Ko tas neatrisina

Idempotence aizsargā pret tā paša pieprasījuma dubultu izpildi. Tā neaizsargā pret:

  • Diviem dažādiem lietotājiem, kuri izveido to pašu rēķinu no dažādām sesijām – tā ir biznesa noteikumu problēma, ko atrisina unikalitātes ierobežojumi, piemēram, viens rēķins vienam pasūtījumam.
  • Loģiskiem dublikātiem ar dažādām atslēgām.
  • Nedeterministisku rīcību operācijas iekšienē – ja apstrādātājs ģenerē laika zīmogu vai numuru, saglabātā atbilde fiksē pirmo no tiem, kas ir vēlamā rīcība, taču to ir vērts saprast.

Tas papildina datu bāzes ierobežojumus, nevis aizstāj tos. Nordlet virsgrāmata izmanto abus: idempotences atslēgas aptur atkārtotus pieprasījumus, savukārt unikālie indeksi aptur dubultas norēķinu paketes vienam izmaksas ID, dubultus pirkuma rēķinus vienam piegādātājam un dokumenta numuram, kā arī dubultus nolietojuma aprēķinus vienam aktīvam un periodam.

Kā Nordlet to īsteno

Nosūtiet galveni Idempotency-Key jebkurā pieprasījumā, kas maina stāvokli – tā ir netukša virkne, kurā ir ne vairāk kā 255 rakstzīmes.

Pirmais pieprasījums ar norādīto atslēgu tiek izpildīts, un tā rezultāts tiek saglabāts. Atkārtots mēģinājums ar to pašu atslēgu un to pašu datu pakotni atgriež saglabāto atbildi bez atkārtotas izpildes, tāpēc rēķins tiek izveidots precīzi vienu reizi neatkarīgi no tā, cik reizes izsaukums tiek atkārtots.

Divas kļūdu situācijas tiek norādītas nepārprotami, nevis klusējot:

  • Tā pati atslēga, bet cita datu pakotne → noraidīts: "Idempotency-Key was already used with a different request payload." Tas novērš patiesi bīstamu gadījumu, kad klients atkārtoti izmanto atslēgu, vienlaikus mainot summu.
  • Tā pati atslēga, sākotnējais pieprasījums joprojām tiek izpildīts → noraidīts kā atkārtojams: "A request with this Idempotency-Key is still being processed, retry shortly." Nenotiek vienlaicīga dubultā izpilde.

Atslēgas tiek grupētas uzņēmuma ietvaros (scoped per company), tāpēc divi nomnieki (tenants), kas ģenerē vienu un to pašu UUID, nekad nesadalās.

Papildu garantija darbojas pretējā virzienā. Tīmekļa āķu (webhook) piegāde notiek vismaz vienu reizi (at-least-once), kas nozīmē, ka jūsu galapunkts (endpoint) laiku pa laikam saņems to pašu notikumu divas reizes – pēc noildzes, kad piegāde patiesībā bija izdevusies, vai atkārtotas piegādes gadījumā. Katrs notikums satur ID, un jūsu apstrādātājam ir jāreģistrē apstrādātie ID un jāignorē atkārtojumi. Idempotence ir īpašība, kas nepieciešama abām integrācijas pusēm; API konvenciju ceļvedī pieprasījumu puse ir apskatīta detalizēti.

FAQ

Kas ir idempotences atslēga?

Klienta ģenerēts identifikators, kas tiek nosūtīts kopā ar pieprasījumu un ļauj serverim atpazīt tās pašas loģiskās operācijas atkārtotu mēģinājumu. Pirmais pieprasījums tiek izpildīts; atkārtoti pieprasījumi ar to pašu atslēgu atgriež saglabāto rezultātu, nevis izpildās no jauna.

Kāpēc grāmatvedības API ir nepieciešama idempotence?

Tāpēc, ka dubultots ieraksts rada reālu dokumentu ar reālām sekām – otru rēķinu, kas patērē secīgu numuru, tiek iegrāmatots virsgrāmatā un nonāk PVN deklarācijā. Atšķirībā no dubultas nolasīšanas, to nevar vienkārši dzēst; tas ir jālabo ar kredītrēķinu.

Vai man vajadzētu ģenerēt jaunu atslēgu katram atkārtotam mēģinājumam?

Nē – tas pilnībā iznīcina šo mehānismu. Vienai loģiskajai operācijai ģenerējiet vienu atslēgu un izmantojiet to atkārtoti katrā šīs operācijas mēģinājumā. Jauna atslēga katram mēģinājumam nozīmē, ka katrs mēģinājums tiek uztverts kā jauna operācija.

Kas notiek, ja es atkārtoti izmantoju atslēgu ar citu datu pakotni?

Pieprasījums tiek noraidīts. Atslēga apliecina, ka tā ir tā pati operācija, tāpēc mainīts pamatteksts nozīmē, ka kaut kas nav kārtībā – visdrīzāk tā ir klienta kļūda, kas saistīta ar atslēgu atkārtotu izmantošanu. Agrāka rezultāta klusa atgriešana būtu sliktāka rīcība.

Vai idempotence novērš visus dublikātus?

Nē. Tas novērš tā paša pieprasījuma dubultu izpildi. Divi dažādi lietotāji, kuri neatkarīgi viens no otra izveido to pašu rēķinu, ir veikuši divus dažādus pieprasījumus, un tā ir biznesa noteikumu problēma un risināma ar unikalitātes ierobežojumiem, nevis kaut kas tāds, ko idempotences atslēga var pamanīt.