ഒരു ഉപയോക്താവ് ഒരു ബട്ടൺ ക്ലിക്ക് ചെയ്യുന്നു. റിക്വസ്റ്റ് തടസ്സപ്പെടുന്നു. പത്ത് സെക്കൻഡ് നിശബ്ദത. അവർ ഫോളബാക്ക് (fallback) ബട്ടൺ ക്ലിക്ക് ചെയ്യുന്നു. ഇപ്പോൾ ഒരൊറ്റ ഉദ്ദേശ്യത്തിനായി (intention) രണ്ട് ജോബുകൾ പ്രവർത്തിക്കുന്നു. ഇതിന്റെ ഫലമായി ഡ്യൂപ്ലിക്കേറ്റ് സൈഡ് ഇഫക്റ്റുകൾ (side effects), ഇരട്ട ചാർജുകൾ, കൂടാതെ നിങ്ങളുടെ ഒരു വൈകുന്നേരം മുഴുവൻ നശിപ്പിക്കുന്ന ഡാറ്റാ പ്രശ്നങ്ങൾ എന്നിവ ഉണ്ടാകുന്നു.
ഇതൊരു ഫ്രണ്ട്എൻഡ് ബഗ്ഗ് (frontend bug) അല്ല. ഒരു ഡിസേബിൾഡ് ബട്ടണോ (disabled button) അല്ലെങ്കിൽ React-ലെ ഒരു ഡിബൗൺസ് ടൈമറോ (debounce timer) നിങ്ങളെ രക്ഷിക്കില്ല. ആദ്യത്തെ റിക്വസ്റ്റ് ഇതിനകം തന്നെ നടന്നുകൊണ്ടിരിക്കുകയായിരുന്നു. നെറ്റ്വർക്ക് അതിന്റെ റെസ്പോൺസ് (response) വിഴുങ്ങിപ്പോയി എന്ന് മാത്രം. നിങ്ങളുടെ ബാക്കെൻഡ് ഓരോ പുതിയ റിക്വസ്റ്റിനെയും ഒരു പുതിയ നിർദ്ദേശമായിട്ടാണ് കാണുന്നതെങ്കിൽ, റീട്രൈകൾ (retries) വലിയൊരു ബാധ്യതയായി മാറും. നിങ്ങളുടെ API ഡിസൈനിലും ഡാറ്റാബേസ് സ്കീമയിലും (database schema) ആണ് നിങ്ങൾ ഇത് പരിഹരിക്കേണ്ടത്.
ഇതിനുള്ള പരിഹാരം ലളിതമായ ഒരു ഘടനാപരമായ വിഭജനത്തിൽ (structural split) നിന്നാണ് തുടങ്ങുന്നത്.
ജോബുകളെ അറ്റംപ്റ്റുകളിൽ (Attempts) നിന്ന് വേർതിരിക്കുക
ഒരു ഉപയോക്താവ് എന്താണ് ആഗ്രഹിക്കുന്നത് എന്നതിന്റെ സ്ഥിരമായ രേഖയായി ഒരു ജോബിനെ (job) കരുതുക. അത് ഉടമസ്ഥൻ (owner), പാരാമീറ്ററുകൾ (parameters), ടാർഗെറ്റ് പ്രൊവൈഡർ (target provider), കൃത്യമായ ഉദ്ദേശ്യം (intent) എന്നിവ രേഖപ്പെടുത്തുന്നു. ആ ഉദ്ദേശ്യം നിറവേറ്റാനുള്ള ഒരു പ്രത്യേക ശ്രമമാണ് അറ്റംപ്റ്റ് (attempt).
ഒരു പ്രിന്റ് ഷോപ്പ് സങ്കൽപ്പിക്കുക. നിങ്ങൾ ഒരു ഫയൽ നൽകുന്നു, അവർ നിങ്ങൾക്ക് ടിക്കറ്റ് #45 നൽകുന്നു. ആ ടിക്കറ്റാണ് ജോബ്. ഷോപ്പ് ഇൻക്ജെറ്റ് പ്രിന്റർ ഉപയോഗിക്കാൻ ശ്രമിക്കുന്നു. അത് ജാം ആകുന്നു. അതാണ് ആദ്യത്തെ അറ്റംപ്റ്റ്. അവർ ആ ഫയൽ ലേസർ പ്രിന്ററിലേക്ക് മാറ്റുന്നു. അതാണ് രണ്ടാമത്തെ അറ്റംപ്റ്റ്. ഈ പ്രക്രിയയിലുടനീളം, ടിക്കറ്റ് #45 മാറുന്നില്ല. ഓരോ പ്രിന്റർ പരീക്ഷിക്കുമ്പോഴും ഷോപ്പ് പുതിയ ടിക്കറ്റ് നൽകുകയാണെങ്കിൽ, നിങ്ങൾ മൂന്ന് തവണ പണം നൽകേണ്ടി വരികയും മൂന്ന് ആവശ്യമില്ലാത്ത കോപ്പികൾ ലഭിക്കുകയും ചെയ്യും.
നിങ്ങളുടെ ഡാറ്റാബേസും ഇത് തന്നെയായിരിക്കണം. ഒരു ടേബിൾ ജോബുകൾ സൂക്ഷിക്കുന്നു. മറ്റൊരു ടേബിൾ അറ്റംപ്റ്റുകൾ സൂക്ഷിക്കുന്നു. അറ്റംപ്റ്റുകൾ കൂട്ടിച്ചേർക്കപ്പെടുമ്പോൾ ജോബ് റോ (job row) മാറ്റമില്ലാതെ തുടരുന്നു.
ഈ വേർതിരിക്കൽ നിങ്ങൾക്ക് നിയന്ത്രണം നൽകുന്നു. കൂടാതെ നെറ്റ്വർക്ക് തകരാറുകൾക്കിടയിലും നിലനിൽക്കുന്ന ഒരു ഐഡംപോട്ടൻസി കീ (idempotency key) ഘടിപ്പിക്കാൻ ഒരു ഇടവും ഇത് നൽകുന്നു.
ഓരോ ജോബിനും ഒരു ഐഡംപോട്ടൻസി കീ (Idempotency Key) നിർബന്ധമാക്കുക
ഒരു ജോബ് നിർമ്മിക്കുന്ന ഓരോ POST റിക്വസ്റ്റും ഒരു യുണീക് ഐഡംപോട്ടൻസി കീ (unique idempotency key) ഉൾക്കൊള്ളണം. ഈ കീ ഉപയോക്താവിന്റേതാണ്, സെഷന്റേതല്ല (session). ഉടമസ്ഥന്റെ ഐഡിയും (owner ID) കീയും സംയോജിപ്പിക്കുക, തുടർന്ന് ആ രണ്ട് കോളങ്ങൾക്കും ഇടയിൽ ഒരു യുണീക് ഡാറ്റാബേസ് കൺസ്ട്രയിന്റ് (unique database constraint) നടപ്പിലാക്കുക.
എന്തുകൊണ്ടാണ് ഡാറ്റാബേസ് കൺസ്ട്രയിന്റ് ഉപയോഗിക്കുന്നത്? കാരണം, ഇൻസേർട്ട് ചെയ്യുന്നതിന് മുമ്പ് ആപ്ലിക്കേഷൻ കോഡിൽ നിലനിൽപ്പ് പരിശോധിക്കുന്നത് ഒരു 'റേസ് കണ്ടീഷൻ' (race condition) ഉണ്ടാക്കാൻ സാധ്യതയുള്ള കാര്യമാണ്. ഒരേ മൈക്രോസെക്കൻഡ് ഇടവേളയിൽ രണ്ട് ഒരേപോലെയുള്ള റിക്വസ്റ്റുകൾ കടന്നുപോയേക്കാം. ഡാറ്റാബേസിനെ അത് നടപ്പിലാക്കാൻ അനുവദിക്കുക. ഒരു ഉപയോക്താവ് ഒരേ ഉടമസ്ഥ ഐഡിയും കീയും രണ്ടുതവണ അയച്ചാൽ, രണ്ടാമത്തെ റിക്വസ്റ്റ് ആ യുണീക് വൈലേഷൻ (unique violation) കണ്ടെത്തും, തുടർന്ന് നിങ്ങൾ നിലവിലുള്ള ജോബ് നൽകുന്നു. രണ്ട് റിക്വസ്റ്റുകൾക്കും ഒരേ ജോബ് ഐഡി ലഭിക്കുന്നു. ഡ്യൂപ്ലിക്കേറ്റ് ജോലികൾ ആരംഭിക്കില്ല.
സ്കോപ്പിന്റെ (scope) കാര്യത്തിൽ കർശനമായിരിക്കുക. ആരെങ്കിലും കീ വീണ്ടും ഉപയോഗിക്കുകയും എന്നാൽ ഇൻപുട്ട് പെയ്ലോഡ് (input payload) മാറ്റുകയും ചെയ്താൽ, ഒരു 'കോൺഫ്ലിക്റ്റ്' (conflict) നൽകുക. ഐഡംപോട്ടൻസി കീ കൃത്യമായ ഉദ്ദേശ്യവുമായി ബന്ധപ്പെട്ടിരിക്കണം, ഉപയോക്താവുമായി മാത്രമല്ല. ഒരേ കീയും വ്യത്യസ്ത ഇൻപുട്ടും എന്നാൽ ക്ലയന്റ് ആശയക്കുഴപ്പത്തിലാണെന്ന് അർത്ഥമാക്കുന്നു, നിങ്ങളുടെ സിസ്റ്റം അത് ഊഹിക്കുന്നതിന് പകരം നിരസിക്കണം.
സ്റ്റേറ്റ് ട്രാൻസിഷനുകളെ (State Transitions) സംരക്ഷിക്കുക
ഒരു അറ്റംപ്റ്റ് എന്നത് ഒരു സ്റ്റേറ്റ് ട്രാൻസിഷൻ (state transition) ആണ്, പുതിയ ജോബ് അല്ല. ഒരു മുൻപത്തെ അറ്റംപ്റ്റ് ഇപ്പോഴും സ്റ്റാർട്ടിംഗ് (starting) അല്ലെങ്കിൽ അൺനോൺ (unknown) അവസ്ഥയിൽ നിൽക്കുകയാണെങ്കിൽ, പുതിയൊരു അറ്റംപ്റ്റ് തുടങ്ങാൻ നിങ്ങളുടെ API വിസമ്മതിക്കണം.
ടൈമൗട്ടുകളാണ് (timeouts) ഇതിന് കാരണം. ഒരു പ്രൊവൈഡർ റിക്വസ്റ്റ് ടൈമൗട്ട് ചെയ്യുമ്പോൾ, ക്ലയന്റിന് അത് പരാജയമായി കാണാം, എന്നാൽ സെർവർ സൈഡ് പ്രോസസ്സ് ഇപ്പോഴും പ്രവർത്തിച്ചുകൊണ്ടിരിക്കാം. നിങ്ങളുടെ ഇൻഫറൻസ് റിക്വസ്റ്റിനായി (inference request) GPU ക്ലസ്റ്റർ ഇപ്പോഴും പ്രവർത്തിച്ചുകൊണ്ടിരിക്കാം. കണ്ടെയ്നർ ഇപ്പോഴും ബ്ലോബ് സ്റ്റോറേജിലേക്ക് (blob storage) എഴുതിക്കൊണ്ടിരിക്കാം. ടൈമൗട്ട് ആയ അറ്റംപ്റ്റിനെ പരാജയപ്പെട്ടതായി അടയാളപ്പെടുത്തുകയും ഉടൻ തന്നെ രണ്ടാമതൊരു അറ്റംപ്റ്റ് തുടങ്ങുകയും ചെയ്താൽ, നിങ്ങൾ ഡ്യൂപ്ലിക്കേറ്റ് സൈഡ് ഇഫക്റ്റുകൾ ഉണ്ടാകാൻ സാധ്യതയുള്ള ഒരു ചൂതാട്ടത്തിലാണ് ഏർപ്പെടുന്നത്.
ഒരു ടൈമൗട്ടിനെ പരാജയപ്പെട്ട ഒന്നായിട്ടല്ല, മറിച്ച് അജ്ഞാതമായ (unknown) ഒരു അവസ്ഥയായി കാണുക. ആദ്യത്തെ അറ്റംപ്റ്റ് ഒരു ടെർമിനൽ സ്റ്റേറ്റിൽ (terminal state) എത്തുന്നതുവരെയോ അല്ലെങ്കിൽ ഒരു ഔട്ട്-ഓഫ്-ബാൻഡ് പ്രോസസ്സ് (out-of-band process) വഴി അത് വ്യക്തമായി റദ്ദാക്കുന്നത് വരെയോ പുതിയ അറ്റംപ്റ്റുകൾ തടയുക. ഈ ഇടവേള അസ്വസ്ഥതയുണ്ടാക്കിയേക്കാം. ഇത് ഉപയോക്താവിനെ കാത്തിരിക്കാൻ നിർബന്ധിക്കുന്നു. എന്നാൽ രണ്ട് വർക്കർമാർ ഒരേ റിസോഴ്സുകളിൽ മാറ്റം വരുത്തുന്ന അരാജകത്വം ഇത് തടയുന്നു.
കംപയർ-ആൻഡ്-സ്വാപ്പ് (Compare-and-Swap) ഉപയോഗിച്ച് റേസുകൾ പരിഹരിക്കുക
ഒന്നിലധികം അറ്റംപ്റ്റുകൾ പൂർത്തിയാകുമ്പോഴാണ് ഏറ്റവും പ്രയാസകരമായ പ്രശ്നങ്ങൾ ഉണ്ടാകുന്നത്. ഒരുപക്ഷേ നിങ്ങളുടെ സിസ്റ്റം പ്രൈമറി പ്രൊവൈഡർക്കെതിരെ ആദ്യത്തെ അറ്റംപ്റ്റ് നടത്തിയിരിക്കാം. പത്ത് സെക്കൻഡ് നിശബ്ദതയ്ക്ക് ശേഷം, അത് ഫോളബാക്കിനെതിരെ രണ്ടാമത്തെ അറ്റംപ്റ്റ് നടത്തിയിരിക്കാം. ഇപ്പോൾ രണ്ട് അറ്റംപ്റ്റുകളും പൂർത്തിയായിരിക്കുന്നു. രണ്ട് അറ്റംപ്റ്റുകളും ഒരേ ജോബ് റോയിൽ തങ്ങളുടെ ഫലങ്ങൾ എഴുതാൻ നിങ്ങൾ അനുവദിക്കരുത്.
കംപയർ-ആൻഡ്-സ്വാപ്പ് (compare-and-swap) ലോജിക് ഉപയോഗിക്കുക. ജോബ് റോയിൽ ഒരു വേർഷൻ നമ്പർ (version number) ചേർക്കുക. ഒരു അറ്റംപ്റ്റ് പൂർത്തിയാകുമ്പോൾ, താഴെ പറയുന്ന നിബന്ധനകളോടെ ഒരു അപ്ഡേറ്റ് നടത്തുക:
- നിലവിലെ വേർഷൻ, അറ്റംപ്റ്റ് തുടക്കത്തിൽ വായിച്ച വേർഷനുമായി പൊരുത്തപ്പെടണം.
- മറ്റ് അറ്റംപ്റ്റുകളൊന്നും ഫലത്തിനുള്ള ഇടം (result slot) നേരത്തെ കൈവശപ്പെടുത്തിയിരിക്കരുത്.
- ഇവ രണ്ടും ശരിയാണെങ്കിൽ, ഫലം എഴുതുകയും വേർഷൻ വർദ്ധിപ്പിക്കുകയും ചെയ്യുക.
SQL പദങ്ങളിൽ പറഞ്ഞാൽ, ഇത് WHERE id = $1 AND version = $2 AND completed_by IS NULL എന്ന കണ്ടീഷനുള്ള ഒരു അപ്ഡേറ്റ് സ്റ്റേറ്റ്മെന്റ് പോലെയായിരിക്കും. അപ്ഡേറ്റ് പൂജ്യം റോകൾ (zero rows) ആണ് തിരികെ നൽകുന്നതെങ്കിൽ, മറ്റൊരു അറ്റംപ്റ്റ് ഇതിനകം വിജയിച്ചിരിക്കുന്നു എന്നാണ് അർത്ഥം. വൈകി വന്ന റിക്വസ്റ്റ് അവഗണിക്കണം. അതിന്റെ ഫലം ഉപേക്ഷിക്കുക. അവ രണ്ടും കൂട്ടിച്ചേർക്കരുത് (merge/append). ആ ജോലി വലിച്ചെറിയുക. നേരത്തെ വിജയിച്ച ഫലത്തിന് മുകളിൽ വൈകി വന്ന ഫലം എഴുതുന്നത് ഡാറ്റാ കറപ്ഷന് (data corruption) കാരണമാകും, അത് ഒഴിവാക്കുക എന്നതാണ് ഏക സുരക്ഷിത മാർഗ്ഗം.
ഇത് റിവേഴ്സ്-ഓർഡർ ഫിനിഷ് (reverse-order finish) കൃത്യമായി കൈകാര്യം ചെയ്യുന്നു. ശ്രമം A ആദ്യം പുറപ്പെടുന്നുണ്ടെങ്കിലും മുപ്പത് സെക്കൻഡിന് ശേഷമാണ് തിരികെ വരുന്നത്. ശ്രമം B രണ്ടാമതാണ് പുറപ്പെടുന്നത് എങ്കിലും അഞ്ച് സെക്കൻഡിന് ശേഷം തിരികെ വരുന്നു. ശ്രമം B 'compare-and-swap' വിജയിക്കുന്നു. ശ്രമം A-യുടെ അപ്ഡേറ്റ് പൂജ്യം വരികളെ (rows) മാത്രമേ സ്പർശിക്കുന്നുള്ളൂ. നിങ്ങളുടെ സിസ്റ്റം ഈ റേസ് (race) ലോഗ് ചെയ്യുകയും, കാലഹരണപ്പെട്ട പേലോഡ് (stale payload) അവഗണിക്കുകയും, തുടർന്നുപോവുകയും ചെയ്യുന്നു.
ബ്രേക്ക്പോയിന്റുകൾ പരിശോധിക്കുക
ഹാപ്പി-പാത്ത് ടെസ്റ്റിംഗിലൂടെ (happy-path testing) നിങ്ങൾക്ക് ഈ ബഗുകൾ കണ്ടെത്താൻ കഴിയില്ല. നിങ്ങളുടെ ടെസ്റ്റ് സ്യൂട്ട് വിള്ളലുകളെ (fractures) ലക്ഷ്യം വെക്കേണ്ടതുണ്ട്.
- ഒരു ഡബിൾ-ക്ലിക്ക് സിമുലേറ്റ് ചെയ്യുക. ഒരേ idempotency key ഉള്ള രണ്ട് ഒരേസമയം വരുന്ന POST റിക്വസ്റ്റുകൾ ഒരേ ജോബ് ഐഡികൾ (job IDs) തന്നെ നൽകണം.
- പൊരുത്തപ്പെടാത്ത ഇൻപുട്ട് ഉപയോഗിച്ച് ഒരേ കീ അയക്കുക. ഒരു കോൺഫ്ലിക്റ്റ് റെസ്പോൺസ് (conflict response) പ്രതീക്ഷിക്കുക. പാരാമീറ്ററുകൾ വ്യത്യാസപ്പെട്ടിട്ടുണ്ടെങ്കിൽ സിസ്റ്റം നിലവിലുള്ള ജോബ് നിശബ്ദമായി നൽകാൻ പാടില്ല.
- ഒരു ടൈമൗട്ട് (timeout) ഉണ്ടാക്കുക. ജോബ് ഒരു 'failed state'-ലല്ല, മറിച്ച് ഒരു 'unknown state'-ൽ ആണെന്ന് ഉറപ്പുവരുത്തുക. കൂടാതെ, അവ്യക്തത മാറുന്നത് വരെ സിസ്റ്റം തുടർന്നുള്ള ശ്രമങ്ങൾ തടയുന്നുണ്ടെന്നും പരിശോധിക്കുക.
- രണ്ട് ശ്രമങ്ങളും റിവേഴ്സ് ഓർഡറിൽ അവസാനിക്കാൻ നിർബന്ധിക്കുക. ആദ്യത്തേത് ഔദ്യോഗിക പ്രൈമറി പ്രൊവൈഡർ ആണെങ്കിൽ പോലും, രണ്ടാമത് തിരികെ വരുന്ന ശ്രമം പരാജയപ്പെടുന്നുണ്ടെന്ന് ഉറപ്പാക്കുക.
ഈ ടെസ്റ്റുകൾ വെറും എഡ്ജ്-കേസ് (edge-case) ആഡംബരങ്ങളല്ല. അവ നിങ്ങളുടെ API മറ്റ് സിസ്റ്റങ്ങളുമായി ഉണ്ടാക്കുന്ന കരാറാണ്.
ഫെയിൽ ഓവർ (Fail Over) ചെയ്യുന്നതിന് മുമ്പ് പ്രൊവൈഡറുടെ ഉദ്ദേശ്യം പരിശോധിക്കുക
നിങ്ങൾ ഒരു മൾട്ടി-പ്രൊവൈഡർ സെറ്റപ്പ് ആണ് ഉപയോഗിക്കുന്നതെങ്കിൽ, വ്യത്യസ്ത AI മോഡലുകളെ പരസ്പരം മാറ്റാൻ കഴിയുന്ന സ്ലോട്ടുകളായി കാണാൻ നിങ്ങൾ പ്രേരിതരാകാം. അവ ഒരേ കോഡ് പാത്ത്, ഒരേ HTTP ക്ലയന്റ്, ഒരേ JSON സ്കീമ എന്നിവ പങ്കിടുന്നുണ്ടാകാം. എന്നാൽ അവ ഒരേപോലെ പെരുമാറുന്നു എന്ന് ഇതിനർത്ഥമില്ല.
ഒരു മോഡൽ ഒരു ടോപ്പ്-ലെവൽ കീ (top-level key) ഹാളുസിനേറ്റ് (hallucinate) ചെയ്തേക്കാം. മറ്റൊന്ന് നിങ്ങളുടെ സിസ്റ്റം പ്രോംപ്റ്റ് ഫോർമാറ്റിംഗ് അവഗണിക്കാം. സ്കീമ വാലിഡേഷൻ (Schema validation) സിന്റാക്സ് പിശകുകൾ കണ്ടെത്തുമെങ്കിലും, നിങ്ങളുടെ ബിസിനസ് ലോജിക്സിന് വ്യാഖ്യാനിക്കാൻ കഴിയാത്ത ഒരു റെസ്പോൺസ് അത് പാസ്സ് ചെയ്തേക്കാം. ഒരു പ്രൊവൈഡർ നിങ്ങളുടെ പ്രോംപ്റ്റ് ടെംപ്ലേറ്റിൽ തെറ്റായ രീതിയിൽ പ്രവർത്തിക്കുന്ന സാധുവായ (valid) JSON നൽകിയേക്കാം.
ഓട്ടോമാറ്റിക് മോഡൽ സ്വിച്ചിംഗ് അനുവദിക്കുന്നതിന് മുമ്പ് പ്രൊവൈഡർ-സ്പെസിഫിക് ടെസ്റ്റുകൾ നടത്തുക. ലോ ടെമ്പറേച്ചറിൽ (low temperature) ഫാള்பാക്ക് മോഡൽ (fallback model) നിങ്ങളുടെ ഔട്ട്പുട്ട് സ്ട്രക്ചർ ശരിയായി പാലിക്കുന്നുണ്ടെന്ന് ഉറപ്പാക്കുക. ആ പ്രൊവൈഡറുടെ ടോക്കണൈസർ (tokenizer) വഴി നിങ്ങളുടെ പ്രോംപ്റ്റ് ശരിയായി റെൻഡർ ആകുന്നുണ്ടോ എന്ന് പരിശോധിക്കുക. യഥാർത്ഥ ഇൻപുട്ടുകൾ ഉപയോഗിച്ച് ഫുൾ റൗണ്ട് ട്രിപ്പ് ടെസ്റ്റ് ചെയ്യുക. ഫാള்பാക്ക് മോഡൽ ഒരേ ഓപ്പറേഷണൽ കോൺട്രാക്റ്റ് പങ്കിടുന്നുണ്ടെന്ന് നിങ്ങൾ തെളിയിച്ചാൽ മാത്രമേ ഓട്ടോമാറ്റിക് ഫെയിൽഓവർ സുരക്ഷിതമാകൂ.
ഓരോ ഉദ്ദേശ്യത്തിനും (Intent) ഒരു ജോബ് മാത്രം നിലനിർത്തുക
ഫാള்பാക്ക് പാത്തുകൾ നല്ലതാണ്. എന്നാൽ നിയന്ത്രണമില്ലാത്ത ഫാള்பാക്ക് മൾട്ടിപ്ലിക്കേഷൻ ഒരു ബഗ് ആണ്. നിങ്ങളുടെ സ്റ്റാക്കിന്റെ (stack) ഓരോ പാളിയും കൃത്യമായ ടാസ്ക് ഇതിനകം കണ്ടോ എന്ന് വിലയിരുത്തേണ്ടതുണ്ട്. ലോഡ് ബാലൻസർ, API ഹാൻഡ്ലർ, ഡാറ്റാബേസ്, വർക്കർ എന്നിവയെല്ലാം ഒരേ ഐഡന്റിറ്റി തന്നെ മാനിക്കണം.
റീട്രൈകളും (retries) ഫാള்பാക്കുകളും ഒരു സ്റ്റേബിൾ ജോബിന് കീഴിലുള്ള പുതിയ ശ്രമങ്ങളായി മാറുന്ന രീതിയിൽ നിങ്ങളുടെ സിസ്റ്റം നിർമ്മിക്കുക. ഡാറ്റാബേസ് അടിസ്ഥാനമാക്കിയുള്ള ഒരു idempotency key ഉപയോഗിച്ച് ജോബിനെ ലോക്ക് ചെയ്യുക. ട്രാൻസിഷനുകൾ സംരക്ഷിക്കുക. ശ്രമങ്ങൾ തമ്മിൽ മത്സരിക്കട്ടെ (Race the attempts). കൃത്യം ഒന്ന് മാത്രം വിജയിക്കട്ടെ. ഒരു യൂസർ ക്ലിക്ക് ഡാറ്റ ക്ലീനപ്പിംഗിനായി ഒരു വാരാന്ത്യം മുഴുവൻ ചിലവഴിക്കേണ്ടി വരുന്ന അവസ്ഥയിൽ നിന്ന് നിങ്ങളുടെ സിസ്റ്റത്തെ സംരക്ഷിക്കുന്നത് ഇങ്ങനെയാണ്.
