Digitālās darba plūsmas un integrācijas

Idempotence integrācijās: kā nepieļaut dubultus rēķinus un pasūtījumus

Praktiska metode idempotences ieviešanai integrācijās: atslēgas, datubāzes ierobežojumi, atkārtojumi, notikumi un pārbaudes pret dublikātiem.

Idempotence integrācijās: kā nepieļaut dubultus rēķinus un pasūtījumus

Lai integrācija neveidotu dubultus rēķinus, pasūtījumus vai maksājumus, katrai biznesa operācijai vajag stabilu identitāti, kuru saglabā visos atkārtotajos mēģinājumos. Saņēmējam šī identitāte atomiski jāreģistrē, jābloķē otrreizēja izpilde un atkārtota pieprasījuma gadījumā jāatgriež jau iegūtais rezultāts. Ar vienu idempotences atslēgu API galvenē nepietiek: aizsardzībai jāaptver datubāze, paralēli pieprasījumi, ziņojumu rindas un ārējie pakalpojumi. Praktiski drošs risinājums apvieno idempotences atslēgu, unikālu datubāzes ierobežojumu, saglabātu operācijas statusu un regulāru neskaidro darījumu saskaņošanu.

Kas ir idempotence integrācijā

Idempotenta operācija ir tāda, kurai viena un tā paša pieprasījuma atkārtošana nemaina paredzēto rezultātu pēc pirmās veiksmīgās izpildes. Piemēram, ja pasūtījuma izveides pieprasījums tīkla pārtraukuma dēļ tiek nosūtīts trīs reizes, sistēmā tik un tā jāpaliek vienam pasūtījumam. Atkārtotie mēģinājumi var saņemt iepriekš izveidotā pasūtījuma identifikatoru, nevis radīt jaunus ierakstus.

HTTP metodes semantika viena pati šo rezultātu negarantē. RFC 9110 noteiktas metodes raksturo kā idempotentas, taču konkrētās sistēmas biznesa blakusefekti joprojām jāprojektē korekti. Savukārt POST pēc noklusējuma nav idempotents, bet konkrētu POST galapunktu var padarīt droši atkārtojamu ar idempotences atslēgu un servera puses kontroli.

Svarīgi atšķirt tehnisku pieprasījumu no biznesa operācijas. Ja lietotājs apzināti veido divus vienādus pasūtījumus, tās ir divas operācijas. Ja klients atkārto vienu pieprasījumu pēc taimauta, tā ir viena operācija ar vairākiem piegādes mēģinājumiem. Idempotences mehānismam šie gadījumi jāspēj nošķirt.

Kāpēc rodas dublikāti

Dublikāts parasti nerodas tāpēc, ka sistēma apzināti izpilda komandu divreiz. Biežāks cēlonis ir nenoteikts iznākums. Klients nosūta pieprasījumu, serveris izveido rēķinu, bet atbilde pazūd. Klients redz taimautu un mēģina vēlreiz, nezinot, ka pirmā darbība jau pabeigta.

Līdzīgu situāciju var izraisīt:

  •      automātisks HTTP klienta vai starpniekservera atkārtojums;
  •      ziņojuma atkārtota piegāde pēc patērētāja kļūdas;
  •      divi paralēli darbinieki vai fona uzdevumi;
  •      lietotāja atkārtots klikšķis;
  •      webhook notikuma atkārtota saņemšana;
  •      procesa atsākšana pēc servera avārijas;
  •      manuāla iestrēguša uzdevuma palaišana bez iepriekšējā stāvokļa pārbaudes.

Tāpēc aizliegta atkārtošana nav droša stratēģija. Izplatītā sistēmā atkārtojumi bieži ir vajadzīgi, lai pārvarētu īslaicīgas kļūmes. Jāpanāk, lai atkārtojums būtu kontrolēts un nemainītu biznesa rezultātu.

Idempotences ieviešana sešos slāņos

1. Piešķir identitāti biznesa operācijas sākumā

Idempotences atslēga jāizveido sistēmā, kurā rodas nodoms veikt operāciju. Tā var būt nejauši ģenerēta vērtība vai stabils biznesa identifikators, ja tā unikālums ir skaidri definēts. Būtiskā prasība: visi vienas operācijas atkārtojumi izmanto to pašu atslēgu.

Ja katrā atkārtojumā ģenerē jaunu atslēgu, saņēmējs redz jaunus pieprasījumus un nevar atpazīt dublikātu. Atslēgas darbības joma arī jānosaka precīzi. Drošāk to vērtēt kopā ar klienta vai nomnieka identifikatoru un operācijas tipu, piemēram, uzņēmums + izveidot-rēķinu + atslēga. Tas neļauj viena klienta atslēgai nejauši bloķēt cita klienta darbību.

2. Piesaisti atslēgu nemainīgam pieprasījuma saturam

Serverim jāsaglabā ne tikai atslēga, bet arī nozīmīgo ievades datu pirkstu nospiedums vai kanoniski salīdzināmi lauki. Ja tā pati atslēga atkārtoti saņemta ar citu summu, valūtu, pasūtījuma sastāvu vai saņēmēju, pieprasījumu nedrīkst klusi uzskatīt par veiksmīgu atkārtojumu.

Šādā situācijā jāatgriež konflikts un jāreģistrē diagnostikas informācija. Pretējā gadījumā klients var domāt, ka izpildīta jaunā versija, lai gan serveris atgriezis pirmās operācijas rezultātu. Jādefinē arī datu normalizācija, jo semantiski vienādi JSON dokumenti var atšķirties ar lauku secību vai nebūtiskām formāta detaļām.

3. Atomiski rezervē operāciju datubāzē

Pirms biznesa blakusefekta izpildes serveris mēģina izveidot idempotences ierakstu ar unikālu atslēgu. Unikālam datubāzes ierobežojumam jābūt galvenajai aizsardzībai pret sacensības situāciju, nevis tikai iepriekšējam SELECT vaicājumam.

Divi paralēli procesi var vienlaikus pārbaudīt, ka ieraksta nav, un abi turpināt darbu. Unikāls indekss vai ierobežojums ļauj tikai vienam iegūt tiesības sākt operāciju. PostgreSQL INSERT ... ON CONFLICT ir viens no tehniskajiem instrumentiem šādai rezervācijai, taču pēc konflikta tik un tā jāielasa esošais ieraksts, jāsalīdzina saturs un jāizvērtē tā statuss.

4. Saglabā statusu un atkārtojamam klientam atdod rezultātu

Idempotences ierakstā noder vismaz operācijas atslēga, darbības joma, ievades pirkstu nospiedums, statuss, izveides laiks un iznākuma atsauce. Tipiski statusi var būt processing, succeeded, failed un unknown, taču to nozīme konkrētajā sistēmā jādefinē nepārprotami.

Ja operācija jau pabeigta, atkārtotam pieprasījumam atdod saglabāto rezultātu vai atsauci uz izveidoto objektu. Ja tā vēl tiek apstrādāta, serveris var norādīt, ka rezultāts nav gatavs, vai piedāvāt statusa pārbaudes galapunktu. Tas ir drošāk nekā paralēli sākt otru izpildi.

Ne katra kļūda jāglabā kā galīga. Validācijas kļūda pirms rezervācijas atšķiras no kļūdas pēc ārēja maksājuma izpildes. Statusu modelim jāparāda, vai operāciju drīkst atkārtot, vai jānoskaidro tās faktiskais iznākums.

5. Aizsargā pašu biznesa objektu

Idempotences tabula nav vienīgais drošības slānis. Arī biznesa tabulā vajadzīgs unikālums, kas atbilst procesa nozīmei. Rēķinam tas var būt avota pasūtījuma un rēķina veida salikums, bet pasūtījumam — ārējās sistēmas pasūtījuma identifikators kopā ar avotu. Precīzais modelis ir atkarīgs no tā, vai vienam pasūtījumam drīkst būt vairāki daļēji rēķini, labojumi vai atsevišķas piegādes.

Šis ierobežojums pasargā arī tad, ja kļūdas dēļ tiek apiets API idempotences slānis. Tomēr pārāk plašs unikālums var bloķēt leģitīmu atkārtotu pirkumu. Tādēļ unikālā biznesa atslēga jābalsta procesa noteikumos, nevis pieņēmumā, ka vienāda summa un klients nozīmē dublikātu.

6. Pārnes to pašu identitāti uz ārējiem pakalpojumiem

Ja vietējā sistēma izsauc maksājumu, rēķinu vai piegādes pakalpojumu, idempotence jāturpina arī šajā robežā. Ja pakalpojums atbalsta idempotences atslēgas, atkārtotajā izsaukumā jālieto tā pati atslēga. Stripe API dokumentācija ir viens no publiski aprakstītiem šādas pieejas piemēriem POST pieprasījumiem.

Ja ārējais pakalpojums neatbalsta idempotenci, jāizmanto tā biznesa identifikatori, statusa pārbaude un saskaņošanas process. Pēc taimauta nedrīkst automātiski pieņemt, ka darbība nav notikusi. Vispirms jāpārbauda ārējās sistēmas statuss; ja to nav iespējams noteikt, vietējai operācijai jāpaliek neskaidrā stāvoklī, nevis uzreiz jārada nākamais maksājums vai rēķins.

Praktisks rēķina izveides piemērs

Pieņemsim, ka CRM nosūta ERP komandai izveidot rēķinu pasūtījumam A-1048. CRM pirms pirmā mēģinājuma izveido operācijas atslēgu un saglabā to pie uzdevuma. ERP saņem pieprasījumu un vienā datubāzes transakcijā rezervē idempotences ierakstu un izveido rēķinu vai drošu norādi tā izveidei.

Ja atbilde pazūd, CRM atkārto pieprasījumu ar to pašu atslēgu. ERP atrod pabeigto ierakstu un atdod jau izveidotā rēķina identifikatoru. Ja divi pieprasījumi pienāk vienlaikus, unikālais ierobežojums ļauj tikai vienam sākt izpildi.

Papildu aizsardzība ir unikāla saite starp avota pasūtījumu un konkrēto rēķina lomu. Tā jāmodelē pietiekami precīzi, lai netraucētu avansa, gala vai korekcijas dokumentiem, ja process tos paredz. Idempotence nosaka atkārtotas tehniskas komandas uzvedību, bet pati nenosaka uzņēmuma rēķinu numerācijas vai labošanas kārtību.

Ziņojumu rindas, outbox un inbox

Ziņojumu rindā patērētājs var pabeigt biznesa darbību, bet avarēt pirms saņemšanas apstiprinājuma. Tad starpnieks ziņojumu piegādā atkārtoti. RabbitMQ dokumentācija par patērētāju apstiprinājumiem skaidro mehānismu, kura dēļ lietotnei jābūt gatavai atkārtotai piegādei.

Outbox pieeja ļauj vienā vietējā datubāzes transakcijā saglabāt gan biznesa izmaiņu, gan nosūtāmo notikumu. Atsevišķs process notikumu publicē un drīkst mēģināt atkārtoti. Saņēmēja inbox vai deduplikācijas tabula reģistrē apstrādāto ziņojumu identifikatorus un neizpilda to pašu darbību vēlreiz.

Outbox neatrisina visu automātiski. Publicēšana un saņemšana joprojām var atkārtoties, bet atkārtojums kļūst drošs. Turklāt viena notikuma identifikators ne vienmēr aizsargā biznesa darbību: divi dažādi notikumi var mēģināt izveidot vienu rēķinu. Tādēļ vajadzīga gan ziņojuma deduplikācija, gan biznesa līmeņa unikālums.

Ko pārbaudīt pirms ieviešanas

Idempotenci nevar apstiprināt tikai ar vienu veiksmīgu API testu. Pārbaudēm jāatdarina nenoteiktība un paralēla izpilde:

  1. Nosūti vienu pieprasījumu vairākas reizes pēc kārtas ar vienu atslēgu un pārbaudi, ka izveidots viens objekts.
  2. Nosūti vairākus vienlaicīgus pieprasījumus ar vienu atslēgu un pārbaudi datubāzes unikālo ierobežojumu.
  3. Atkārto atslēgu ar mainītu summu vai saņēmēju un sagaidi skaidru konfliktu.
  4. Pārtrauc savienojumu pirms atbildes saņemšanas, bet pēc servera izpildes, un tad atkārto pieprasījumu.
  5. Avarē patērētāju pēc datubāzes transakcijas, bet pirms ziņojuma apstiprināšanas.
  6. Imitē ārējā pakalpojuma taimautu un pārbaudi, vai sistēma vispirms noskaidro iepriekšējā mēģinājuma statusu.
  7. Pārbaudi operāciju pēc deduplikācijas ieraksta glabāšanas termiņa beigām.
  8. Palaid manuālo rezerves procesu un pārliecinies, ka tas izmanto tos pašus unikāluma noteikumus.

Glabāšanas termiņu nedrīkst izvēlēties tikai pēc datubāzes izmaksām. Tam jāaptver iespējamais atkārtojumu, aizkavētas piegādes un saskaņošanas periods. Ja ierakstu izdzēš pārāk agri, vecs atkārtojums atkal izskatās pēc jaunas operācijas. Vienlaikus ilgāka glabāšana palielina datu apjomu un var prasīt sensitīvo lauku minimizēšanu.

Novērošana un saskaņošana

Uzraudzībā noder atsevišķi skaitīt veiksmīgus atkārtotus pieprasījumus, atslēgas un satura konfliktus, ilgi iestrēgušus processing ierakstus, operācijas ar unknown statusu un neatbilstības starp vietējo un ārējo sistēmu. Šie rādītāji palīdz atšķirt normālu atkārtojumu no integrācijas defekta vai klienta kļūdainas uzvedības.

Naudas un dokumentu procesos vajadzīgs saskaņošanas uzdevums, kas salīdzina abu sistēmu ierakstus pēc stabilas ārējās atsauces. Idempotence samazina dublikātu iespēju, bet nevar pierādīt, ka attālinātā darbība notikusi, ja ārējās sistēmas atbilde un statusa vaicājums nav pieejams.

Ierobežojumi un riski

Idempotence nepadara nepareizu komandu pareizu. Ja avota sistēma kļūdaini izveido divas atšķirīgas operācijas ar dažādām atslēgām, tehniskais mehānisms abas var izpildīt. Šādu problēmu novērš biznesa validācija, apstiprināšanas noteikumi un atbilstoša unikālā atslēga.

Idempotences atslēga neaizstāj autentifikāciju un autorizāciju. Serverim joprojām jāpārbauda, vai pieprasītājam ir tiesības veikt darbību un piekļūt iepriekš saglabātajam rezultātam. Atslēgu nedrīkst izmantot kā slepenu piekļuves apliecinājumu.

Jāuzmanās arī no pārāk šauras vai pārāk plašas darbības jomas. Viena atslēga visam klientam bloķētu leģitīvas darbības, savukārt atslēga tikai viena procesa atmiņā nepasargātu pēc servera restartēšanas. Deduplikācijas stāvoklim jāatrodas noturīgā glabātuvē, kas pieejama visiem konkrētā galapunkta izpildītājiem.

Visgrūtākās ir blakusparādības ārpus vietējās transakcijas: e-pasts var būt nosūtīts, maksājums pieņemts vai dokuments izveidots tieši pirms avārijas. Šeit vajag ārējā pakalpojuma idempotenci, statusa pārbaudi vai saskaņošanu. Solījums par absolūtu vienreizēju izpildi nav pamatots, ja visās iesaistītajās sistēmās nav kopīgas atomiskas robežas.

Ieteicamās iekšējās saites

Lasītāja nākamajiem soļiem atbilstoši saistītie materiāli ir procesa robežu dokumentēšana rakstā biznesa-procesa-kartesana-pirms-automatizacijas, integrācijas veida izvēle rakstā api-integracija-vai-vienkarsa-datu-apmaina un manuālā rezerves procesa projektēšana rakstā automatizacijas-kludu-apstrade-manualais-rezerves-process.

Secinājums

Droša idempotence sākas nevis ar atkārtojumu aizliegšanu, bet ar vienas biznesa operācijas atpazīšanu visā integrācijas ķēdē. Stabilai atslēgai jāsasniedz datubāze, rinda un ārējais pakalpojums, bet unikālajiem ierobežojumiem un saglabātajam statusam jāaptur paralēla izpilde. Ja ārējais rezultāts nav nosakāms, sistēmai tas jāatzīst un jāvirza uz statusa pārbaudi vai saskaņošanu, nevis akli jārada nākamā darbība.