Mtumiaji anabonyeza kitufe. Ombi linachelewa. Sekunde kumi za ukimya. Anabonyeza kitufe cha mbadala (fallback). Sasa kazi mbili zinaendelea kwa nia moja. Unajikuta na matokeo ya ziada (duplicate side effects), malipo mara mbili, na vurugu za data zinazokuharibia mchana wako.

Hili siyo hitilafu ya frontend. Kitufe kilichozimwa au "debounce timer" katika React hakutasaidia. Ombi la kwanza lilikuwa tayari linaendelea. Mtandao tu ndio ulimeza majibu (response). Ikiwa backend yako inachukulia kila ombi linaloingia kama maelekezo mapya kabisa, majaribio ya tena (retries) yanakuwa hatari. Unahitaji kurekebisha hili katika usanifu wako wa API na muundo wako wa kanzi data (database schema).

Suluhisho huanza na mgawanyo rahisi wa kimuundo.

Gawanya Kazi (Jobs) kutoka kwa Majaribio (Attempts)

Fikiria kazi (job) kama rekodi ya kudumu ya kile mtumiaji anachotaka. Inahifadhi mmiliki, vigezo (parameters), mtoa huduma lengwa, na nia halisi. Jaribio (attempt) ni jaribio mahususi la kutimiza nia hiyo.

Wazia duka la uchapishaji. Unamkabidhi mtu faili na anakupa tiketi namba #45. Tiketi hiyo ndiyo kazi (job). Duka linajaribu printa ya inkjet. Inajikwangua (jams). Hilo ndilo jaribio la kwanza. Wanahamishia faili kwenye printa ya laser. Hilo ndilo jaribio la pili. Katika mchakato mzima, tiketi #45 haibadiliki. Ikiwa duka lingetoa tiketi mpya kwa kila printa waliojaribu, ungelipia mara tatu na kupokea nakala tatu usizozitaka.

Kanzi data yako inapaswa kuiga mfumo huu. Jedwali moja linahifadhi kazi (jobs). Jedwali lingine linahifadhi majaribio (attempts). Safu ya kazi (job row) inabaki vilevile huku majaribio yakijikusanya chini yake.

Mgawanyo huu unakupa udhibiti. Pia unakupa sehemu ya kuambatanisha funguo ya idempotency (idempotency key) inayoweza kuhimili hitilafu za mtandao.

Hitaji Funguo ya Idempotency kwenye Kila Kazi

Kila ombi la POST linalounda kazi lazima liwe na funguo ya kipekee ya idempotency. Funguo hii ni ya mtumiaji, siyo ya kikao (session). Unganisha ID ya mmiliki na funguo hiyo, kisha simamia kizuizi cha kipekee cha kanzi data (unique database constraint) kwenye safu hizo mbili.

Kwa nini kizuizi cha kanzi data? Kwa sababu kuangalia uwepo wa kitu kwenye kodi ya programu kabla ya kuingiza ni hatari ya mashindano (race condition) inayoweza kutokea wakati wowote. Maombi mawili yanayofanana yanaweza kupenya katika pengo la microsecond moja. Acha kanzi data iwe msimamizi. Ikiwa mtumiaji atatuma ID ya mmiliki na funguo ileile mara mbili, ombi la pili litakutana na ukiukaji wa kipekee na wewe utarudisha kazi iliyopo. Maombi yote mawili yatapata ID ileile ya kazi. Hakuna kazi ya ziada itakayoanza.

Kuwa mkali kuhusu upeo (scope). Ikiwa mtu atatumia tena funguo hiyo lakini akabadilisha maudhui ya data (input payload), rudisha hitilafu ya mgongano (conflict). Funguo ya idempotency lazima iambatane na nia mahususi, siyo tu mtumiaji. Funguo ileile ikiwa na ingizo tofauti inamaanisha mteja amechanganyikiwa, na mfumo wako unapaswa kuikataa badala ya kukisia.

Linda Mabadiliko ya Hali (State Transitions)

Jaribio ni mabadiliko ya hali, siyo kazi mpya. API yako lazima ikatae kuanzisha jaribio jipya ikiwa jaribio lililopita bado linasubiri katika hali ya kuanza au hali isiyojulikana.

Muda uliopitishwa (timeouts) ndio sababu. Wakati ombi la mtoa huduma linapozidi muda, mteja huona kufeli, lakini mchakato wa upande wa seva unaweza kuwa bado unaendelea. Kundi la GPU (GPU cluster) linaweza kuwa bado linafanya kazi ya ombi lako la ufuatiliaji (inference request). Kibunzi (container) kinaweza kuwa bado kinaandika kwenye hifadhi ya blob (blob storage). Ikiwa utaweka alama ya jaribio lililozidi muda kama limefeli na kuanzisha jaribio la pili mara moja, unacheza kamari na matokeo ya ziada.

Chukulia muda uliopitishwa kama hali isiyojulikana, siyo hali ya kufeli. Zuia majaribio mapya hadi yale ya awali yafikie hali ya mwisho (terminal state) au yakafutwe waziwazi na mchakato mwingine. Kusubiri huku kunaweza kukatisha tamaa. Inamlazimu mtumiaji kusubiri. Pia inazuia vurugu za wafanyakazi wawili kubadilisha rasilimali zilezile za chini.

Tatua Mashindano kwa Kutumia Compare-and-Swap

Matatizo magumu zaidi hutokea majaribio mengi yanapomalizika. Labda mfumo wako ulituma jaribio la kwanza kwa mtoa huduma mkuu. Baada ya sekunde kumi za ukimya, ulituma jaribio la pili kwa mbadala. Sasa majaribio yote mawili yamekamilika. Huwezi kuruhusu yote mawili yaandike matokeo yake kwenye safu ileile ya kazi.

Tumia mantiki ya compare-and-swap. Ongeza namba ya toleo (version number) kwenye safu ya kazi. Jaribio linapomalizika, linatekeleza mchakato wa kusasisha (update) wenye masharti:

  • Toleo la sasa lazima lifanane na lile ambalo jaribio lilisoma mwanzoni.
  • Hakuna jaribio lingine linalopaswa kuwa tayari limechukua nafasi ya matokeo.
  • Ikiwa yote mawili yamepita, andika matokeo na uongeze toleo.

Katika istilahi za SQL, hiyo inaonekana kama kauli ya kusasisha (update statement) yenye WHERE id = $1 AND version = $2 AND completed_by IS NULL. Ikiwa mchakato wa kusasisha unarudisha sufuri ya safu, jaribio lingine tayari limeshinda. Ombi la kuchelewa lazima lipuuzwe. Linda matokeo yake. Usichanganye. Usiongeze. Tupa kazi hiyo. Matokeo ya kuchelewa yanayofuta mshindi wa awali ni uharibifu wa data, na hatua pekee salama ni kuyaondoa.

Hii inashughulikia kumalizika kwa mpangilio wa kinyume kwa ufasaha. Jaribio A linaondoka kwanza lakini linarudi baada ya sekunde thalathini. Jaribio B linaondoka la pili lakini linarudi baada ya sekunde tano. Jaribio B linashinda mchakato wa compare-and-swap. Sasisho la Jaribio A haligusi mstari wowote (zero rows). Mfumo wako unarekodi mgongano huo (race), unapuuzia data iliyopitwa na wakati (stale payload), na kuendelea mbele.

Jaribu Maeneo ya Kuvunjika (Breakpoints)

Hutayapata hitilafu (bugs) hizi katika upimaji wa njia ya kawaida (happy-path testing). Seti yako ya majaribio inahitaji kulenga mapengo.

  • Simulia mbofyo mara mbili (double-click). Maombi mawili ya POST ya wakati mmoja yenye funguo sawa ya idempotency lazima yarudishe ID za kazi zinazofanana.
  • Tuma funguo hiyo hiyo ukitumia ingizo tofauti. Tarajia majibu ya mgongano (conflict response). Mfumo haupaswi kurudisha kazi iliyopo kimyakya ikiwa vigezo (parameters) ni tofauti.
  • Chochea muda uliopitiliza (timeout). Hakikisha kazi inaingia katika hali isiyojulikana, si hali ya kushindwa, na kwamba mfumo unazuia majaribio mengine hadi utata huo utakapotatuliwa.
  • Lazimisha majaribio mawili yakamilike kwa mpangilio wa kinyume. Thibitisha kwamba lile la pili kurudi linashindwa, hata kama lile la kwanza kuondoka lilikuwa mtoa huduma mkuu rasmi.

Majaribio haya si anasa za hali nadra (edge-case luxuries). Ni mkataba ambao API yako inafanya na sehemu nyingine za mfumo.

Thibitisha Nia ya Mtoa Huduma Kabla ya Kufanya Failover

Ikiwa unatumia mpangilio wa watoa huduma wengi (multi-provider setup), unaweza kushawishika kuchukulia mifano tofauti ya AI kama nafasi zinazoweza kubadilishana. Zinashiriki njia ile ile ya kodi (code path), mteja (client) ule ule wa HTTP, na muundo ule ule wa JSON (JSON schema). Hiyo haimaanishi kwamba zinafanya kazi kwa namna inayofanana.

Model moja inaweza kuleta dhana potofu (hallucinate) kuhusu funguo ya ngazi ya juu (top-level key). Nyingine inaweza kupuuza mpangilio wa maelekezo ya mfumo (system prompt formatting). Uhakiki wa muundo (schema validation) unakamata makosa ya sintaksia, lakini utaruhusu jibu ambalo mantiki ya biashara yako (business logic) haiwezi kutafsiri. Mtoa huduma anaweza kurudisha JSON halali ambayo inafanya jambo lisilo sahihi na kiolezo chako cha maelekezo (prompt template).

Fanya majaribio mahususi kwa mtoa huduma kabla ya kuruhusu ubadilishaji wa modeli wa kiotomatiki. Thibitisha kwamba modeli mbadala (fallback model) inaheshimu muundo wako wa matokeo katika hali ya joto la chini (low temperature). Hakikisha kuwa maelekezo yako (prompt) yanatokea kwa usahihi kupitia tokenizer ya mtoa huduma huyo. Jaribu mzunguko mzima (round trip) ukitumia ingizo halisi. Failover ya kiotomatiki ni salama tu pale unapothibitisha kuwa mbadala huo unashiriki mkataba ule ule wa kiutendaji.

Weka Kazi Moja kwa Kila Nia (Intent)

Njia mbadala (fallback paths) ni nzuri. Kuzidisha njia mbadala bila udhibiti ni hitilafu (bug). Kila tabaka la mfumo wako (stack) linahitaji kutathmini ikiwa tayari limeona kazi hiyo hiyo mahususi. Load balancer, API handler, kanzi data (database), na worker lazima wote waheshimu utambulisho ule ule.

Jenga mfumo wako ili majaribio ya marudio (retries) na njia mbadala (fallbacks) yaonekane kama majaribio mapya chini ya kazi moja thabiti. Linda kazi hiyo kwa kutumia funguo ya idempotency inayounganishwa na kanzi data. Linda mabadiliko (transitions). Weka majaribio katika mbio. Ruhusu moja tu ishinde. Hivyo ndivyo unavyozuia mbofyo mmoja wa mtumiaji usigeuke kuwa wikendi nzima ya kusafisha data.