Hvað er stöðugildi (idempotency)? Af hverju bókhalds- og greiðslaforritaskil þurfa það
Hvernig endurtakin beiðni býr til tvítekið reikning, hvað stöðugildislykill (idempotency key) tryggir, hvaða villutilfelli hann verður að ráða við og hvernig á að útfæra hann rétt.
Aðgerð telst vera stöðugild (idempotent) ef framkvæmd hennar tvisvar sinnum hefur sömu áhrif og að framkvæma hana einu sinni. Það er eðlislægt að lestur á færslu sé stöðugildur. Að búa til reikning er það hins vegar ekki - kallaðu á það tvisvar og þú ert með tvo reikninga, tvær viðskiptakröfur og viðskiptavin sem spyr hvers vegna hann hafi verið rukkaður tvisvar.
Þessi munur skiptir máli vegna þess að netkerfi bregðast yfirleitt sérstaklega illa við ákveðnum aðstæðum: beiðnin skilar sér en svarið týnist. Biðlarinn (client) sjá tímamörk (timeout) renna út og getur ekki greint á milli þess hvort beiðnin hafi „aldrei borist“ eða „borist, unnist og svarið týnst“. Eina leiðin sem lítur út fyrir að vera örugg er að endurtaka beiðnina, og það er endurtekningin sem býr til tvítekninguna.
Af hverju bókhald gerir þetta verra
Í flestum kerfum er tvítekning smávægilegur óþægindi. Í fjárhagsbókhaldi er það vaxandi vandamál:
- Tvítekinn reikningur eyðir númeri úr billausri röð (gapless sequence), þannig að það er ekki í boði að eyða honum - óbreytanleiki þýðir að til að leiðrétta hann þarf kreditreikning, sem er skjal sem viðskiptavinur þinn sér.
- Tvítekningin bókar færslur á bókhaldið, þannig að tekjur, virðisaukaskattur og viðskiptakröfur eru öll ofmetin þar til einhver tekur eftir því.
- Hann fer inn í virðisaukaskattsskýrsluna og, í Litháen, i.SAF skrárnar.
- Ef hann kveikti á greiðslu, þá færðust peningar í raun til.
„Athugaðu bara með tvítekningar“ virkar heldur ekki, því tvö sannarlega aðskilin reikningar til sama viðskiptavinar fyrir sömu upphæð á sama degi eru fullkomlega eðlilegir. Kerfið getur ekki ráðið í tilganginn út frá innihaldinu - biðlarinn verður að tilgreina hann.
Hvernig stöðugildislyklar virka
Biðlarinn býr til einkvæman lykil fyrir hverja rökfræðilega aðgerð og sendir hann með beiðninni:
POST /v1/sales/invoices/create
Idempotency-Key: 8f14e45f-ea1b-4c2b-9f3a-2d7c1e0b5a91
Þá ábyrgist netþjónninn eftirfarandi:
- Fyrsta beiðni með þessum lykli - unnin eins og venjulega, niðurstaðan vistuð tengd lyklinum.
- Endurtekning með sama lykli og sama hlassi (payload) - ekki endurvinna; skila því svari sem þegar var vistað.
- Sami lykill, annað hlass - hafnað. Lykillinn er fullyrðing um að um sömu aðgerð sé að ræða; annar texti þýðir að svo er ekki, og að skila hljóðlaust gömlu niðurstöðunni væri verra en villa.
- Endurtekning á meðan upphaflega beiðnin er enn í vinnslu - hafnað með villu sem hægt er að endurtaka í stað þess að keyra samhliða.
Fjórða atriðið er það sem einfaldar útfærslur láta oft hjá líða. Biðlarinn sem fær tímamörk eftir 5 sekúndur og reynir aftur eftir 6 sekúndur mun rekast á sína eigin beiðni sem er þegar í vinnslu; án samvirkniwverndar (concurrency guard) halda báðar áfram og þú færð tvítekninguna sem kerfinu var ætlað að koma í veg fyrir.
Að búa til lykla á réttan hátt
Lykillinn verður að kenna við aðgerðina, en ekki tilraunina:
- Rétt: UUID sem er búið til einu sinni þegar notandinn smellir á „gefa út reikning“, endurnotað fyrir hverja endurtekningu á þeim smelli.
- Rétt: ákvörðunarlykill (deterministic key) dreginn af þínu eigin léni -
order-1041-invoice. - Rangt: nýtt UUID í hverri HTTP tilraun. Hver endurtekning fær nýjan lykil, þannig að hver endurtekning er ný aðgerð og þú hefur ekkert útfært.
- Rangt: kjörið (hash) af hlassinu. Tvær sannarlega eins aðgerðir rekast þá á og sú síðari er gleypt þegjandi og hljóðalaust.
Lyklar þurfa geymslutíma (retention period) - nógu lengi til að dekka raunhæfa endurtekningarglugga og truflanir hjá biðlara, en ekki að eilífu.
Hvað þetta leysir ekki
Stöðugildi verndar gegn tvítekinni framkvæmd á sömu beiðni. Það verndar ekki gegn:
- Tveimur mismunandi notendum sem búa til sama reikning úr mismunandi lotum (sessions) - það er vandamál sem tengist viðskiptareglum, leyst með einkvæmisþvingunum (uniqueness constraints) eins og einum reikningi á pöntun.
- Rökfræðilegum tvítekningum með mismunandi lykla.
- Óvissu (non-determinism) innan aðgerðarinnar - ef meðhöndlarinn býr til tímastimpil eða númer, þá geymir vistaða svarið þann fyrsta, sem er æskileg hegðun en gott að skilja.
Þetta styður við, í stað þess að leysa af hólmi, gagnagrunnsþvinganir. Fjárhagsbókhald Nordlet notar hvort tveggja: stöðugildislyklar stöðva endurteknar beiðnir, en einkvæmir vísar (unique indexes) stöðva tvíteknar uppgjörslotur á útgreiðsluskilríki, tvíteknar innkaupareikninga á birgi og skjalanúmer, og tvíteknar afskriftalotur á eign og tímabil.
Hvernig Nordlet útfærir þetta
Sendu Idempotency-Key haus á hvaða beiðni sem breytir stöðu - ekki tómur strengur sem er að hámarki 255 stafir.
Fyrsta beiðnin með gefnum lykli er keyrð og niðurstaða hennar vistuð. Endurtekning með sama lykli og sama hlassi skilar vistuðu svari án þess að endurkeyra, þannig að reikningurinn er búinn til nákvæmlega einu sinni óháð því hversu oft kallið er endurtekið.
Tvö villutilfelli eru skýr í stað þess að vera hljóðlát:
- Sami lykill, annað hlass → hafnað: "Idempotency-Key was already used with a different request payload." Þetta grípur hið raunverulega hættulega tilfell þar coð biðlari endurnotar lykil á meðan upphæðinni er breytt.
- Sami lykill, upphaflega beiðnin enn í vinnslu → hafnað sem villu sem hægt er að endurtaka: "A request with this Idempotency-Key is still being processed, retry shortly." Engin tvíverknaður í samvinnu.
Lyklar eru afmarkaðir per fyrirtæki, þannig að tveir leigutakar (tenants) sem búa til sama UUID rekast aldrei á.
Viðbótartryggingin keyrir í hina áttina. Vefboðsending (webhook delivery) er minnst-einu-sinni (at-least-once), sem þýðir að endapunkturinn þinn mun öðru hverju fá sama atburðinn tvisvar - eftir tímamörk þar sem afhendingin tókst í raun, eða við endursendingu. Hver atburður ber með sér auðkenni og meðhöndlarinn þinn ætti að skrá unnin auðkenni og hunsa endurtekningar. Stöðugildi er eiginleiki sem báðir aðilar samþættingar þurfa; leiðbeiningar um forritaskilakonvension fara ítarlega yfir beiðnihliðina.
FAQ
Hvað er stöðugildislykill?
Auðkenni sem biðlari býr til og sendir með beiðni sem gerir netþjóninum kleift að þekkja endurtekningu á sömu rökfræðilegu aðgerð. Fyrsta beiðnin er keyrð; endurtekningar með sama lykli skila vistuðu niðurstöðunni í stað þess að keyra aftur.
Af hverju þurfa bókhaldsforritaskil stöðugildi?
Vegna þess að tvítekið skrifbókarverk býr til raunverulegt skjal með raunverulegum afleiðingum - annan reikning sem notar raðnúmer, bókar í fjárhagsbókhald og fer inn í virðisaukaskattsskýrslu. Ólíkt tvítekinni lestri er ekki hægt að eyða því einfaldlega; það verður að leiðrétta með kreditreikningi.
Á ég að búa til nýjan lykil fyrir hverja endurtekningu?
Nei - það eyðileggur kerfið alveg. Búðu til einan lykil fyrir hverja rökfræðilega aðgerð og endurnotaðu hann fyrir hverja endurtekningu á þeirri aðgerð. Nýr lykill per tilraun þýðir að hver tilraun er meðhöndluð sem ný aðgerð.
Hvað gerist ef ég endurnota lykil með öðru hlassi?
Beiðninni er hafnað. Lyklar fullyrða að um sömu aðgerð sé að ræða, þannig að breyttur líkami þýðir að eitthvað er að - líklega villa við endurnotkun lykils hjá biðlara. Að skila fyrri niðurstöðunni hljóðlaust væri verra.
Kyrrir stöðugildi allar tvítekningar?
Nei. Það kemur í veg fyrir tvíteknar framkvæmdir á sömu beiðni. Tveir mismunandi notendur sem búa sjálfstætt til sama reikning hafa gert tvær mismunandi beiðnir, sem er vandamál sem tengist viðskiptareglum fyrir einkvæmisþvinganir, ekki eitthvað sem stöðugildislykill getur séð.