നിങ്ങളുടെ ഇമെയിൽ ടെസ്റ്റുകൾ ലാപ്ടോപ്പിൽ കൃത്യമായി പ്രവർത്തിക്കുകയും CI-ൽ എത്തുന്ന നിമിഷം തകരാറിലാകുകയും ചെയ്യുന്നുണ്ടെങ്കിൽ, നിങ്ങൾ ഒറ്റയ്ക്കല്ല. ടെസ്റ്റ് കോഡിൽ sleep കോളുകൾ ചേർക്കുകയോ ബിൽഡ് പാസാകുന്നത് വരെ റീട്രൈ (retry) കൗണ്ട് കൂട്ടുകയോ ചെയ്യുക എന്നതാണ് സാധാരണയായി ആളുകൾ ചെയ്യുന്ന കാര്യം. ഇത് ഒരു ദിവസത്തേക്ക് പ്രശ്നങ്ങൾ കുറച്ചേക്കാം, പക്ഷേ ഇത് ബഗ് പരിഹരിക്കുന്നില്ല. ഇത് പ്രശ്നത്തെ മറച്ചുവെക്കുക മാത്രമാണ് ചെയ്യുന്നത്.

യഥാർത്ഥ പ്രശ്നം, ഏത് ഇമെയിൽ തുറക്കണമെന്ന് നിങ്ങളുടെ ടെസ്റ്റ് എങ്ങനെ തിരിച്ചറിയുന്നു എന്നതാണ്.

ഷെയർഡ് ഇൻബോക്സ് പ്രശ്നം

നിങ്ങളുടെ ലോക്കൽ മെഷീനിൽ, നിങ്ങൾ ഓരോ തവണയും ഒരു ടെസ്റ്റ് മാത്രമേ നടത്തുന്നുള്ളൂ. ഒരു ഇമെയിൽ വരുന്നു. നിങ്ങൾ അത് എടുക്കുന്നു. ലളിതം.

CI എന്നത് തികച്ചും വ്യത്യസ്തമായ ഒരു എൻവയോൺമെന്റാണ്. ഒരു സിംഗിൾ പുൾ റിക്വസ്റ്റ് (pull request) നാലോ എട്ടോ പതിനാറോ പാരലൽ ജോബുകൾ (parallel jobs) പ്രവർത്തിപ്പിച്ചേക്കാം. അവയെല്ലാം ഒരു ടെസ്റ്റ് ഇൻബോക്സ് പങ്കിടുന്നുണ്ടെങ്കിൽ—അത് ഒരു Mailosaur സെർവറാണോ, Mailtrap ഇൻബോക്സാണോ, അതോ ഒരു സ്റ്റേജിംഗ് ഡൊമൈനിലെ യഥാർത്ഥ അക്കൗണ്ടാണോ ആകട്ടെ—അവയെല്ലാം ഒരേ സമയം ഒരേ ബക്കറ്റിലേക്കാണ് ഡാറ്റ എഴുതുന്നത്. ജോബ് A ഒരു പാസ്‌വേഡ് റീസെറ്റ് അയക്കുന്നു. ജോബ് B ഒരു ഇൻവൈറ്റ് അയക്കുന്നു. ജോബ് C പരാജയപ്പെട്ട ഒരു വെൽക്കം ഫ്ലോ റീട്രൈ ചെയ്യുന്നു. ഇതിനിടയിൽ, ബാക്ക്ഗ്രൗണ്ട് വർക്കറുകളും ഡെലിവറി ക്യൂകളും നിങ്ങൾക്ക് നിയന്ത്രിക്കാൻ കഴിയാത്ത തരത്തിലുള്ള വ്യതിയാനങ്ങൾ (jitter) ഉണ്ടാക്കുന്നു.

ഓരോ ജോബും ആ ഷെയർഡ് ഇൻബോക്സിലേക്ക് പോയി "Reset your password" എന്ന സബ്ജക്റ്റുള്ള ഏറ്റവും പുതിയ മെസേജ് ആവശ്യപ്പെടുമ്പോൾ, അത് ഒരു മത്സരമായി മാറുന്നു. വിജയിക്കുന്ന ടെസ്റ്റിന് ശരിയായ ഇമെയിൽ ലഭിക്കുന്നു. പരാജയപ്പെടുന്ന ടെസ്റ്റ് മറ്റൊരു ജോബിന് വേണ്ടിയുള്ള ലിങ്കിൽ ക്ലിക്ക് ചെയ്യുകയും തെറ്റായ ഉള്ളടക്കം പരിശോധിക്കുകയും (assert) ചെയ്യുന്നു. ഇത് ഒരു ടൈമിംഗ് പ്രശ്നമായി തോന്നിക്കുന്ന ഒരു എററിലൂടെ പരാജയപ്പെടുന്നു. ഇത് ടൈമിംഗ് പ്രശ്നമല്ല, മറിച്ച് ഐഡന്റിറ്റി (identity) പ്രശ്നമാണ്.

എന്തുകൊണ്ടാണ് "Newest Message" പരാജയപ്പെടുന്നത്

ഈ രീതി പിന്തുടരാൻ എളുപ്പമാണ്, കാരണം ഇത് സ്വാഭാവികമായി തോന്നാം:

  1. യൂസർ ഫ്ലോ ട്രിഗർ ചെയ്യുക.
  2. ഓരോ കുറച്ച് സെക്കൻഡിലും ഇൻബോക്സ് പരിശോധിക്കുക (poll).
  3. സബ്ജക്റ്റ് ലൈനുമായി പൊരുത്തപ്പെടുന്ന ഏറ്റവും പുതിയ മെസേജ് തുറക്കുക.
  4. ആദ്യത്തെ ലിങ്കിൽ ക്ലിക്ക് ചെയ്യുകയും അസർഷനുകൾ (assertions) നടത്തുകയും ചെയ്യുക.

വെറും പാരലലിസം കൂടാതെ മറ്റ് പല കാരണങ്ങളാലും ഇത് പരാജയപ്പെടുന്നു. മുമ്പത്തെ പരാജയപ്പെട്ട ഒരു റണ്ണിൽ നിന്നിൽ നിന്നുള്ള റീട്രൈ വൈകി വന്നേക്കാം, നിങ്ങളുടെ നിലവിലെ ടെസ്റ്റ് പരിശോധിക്കുന്ന സമയത്ത് അത് പെട്ടെന്ന് ഏറ്റവും പുതിയ മെസേജായി മാറാം. നിങ്ങളുടെ ആപ്ലിക്കേഷനുള്ളിലെ ബാക്ക്ഗ്രൗണ്ട് വർക്കറുകൾ രണ്ട് ഇമെയിലുകൾ ക്യൂ ചെയ്യുകയും ആദ്യത്തേതിന് മുമ്പ് രണ്ടാമത്തേത് ഡെലിവർ ചെയ്യുകയും ചെയ്തേക്കാം. സബ്ജക്റ്റ് ലൈനുകൾ മാത്രം ഉപയോഗിക്കുന്നത് ദുർബലമായ ഒരു രീതിയാണ്; നിങ്ങളുടെ സ്റ്റേജിംഗ് ആപ്ലിക്കേഷൻ വ്യത്യസ്ത പാത്തുകളിൽ നിന്ന് സമാനമായ ഇമെയിലുകൾ അയച്ചേക്കാം. ടൈംസ്റ്റാമ്പ് (timestamp) ഉപയോഗിച്ച് ക്രമീകരിക്കുന്നത് കാണുന്നതിനേക്കാൾ മോശമാണ്, കാരണം CI റണ്ണറും മെയിൽ പ്രൊവൈഡറും തമ്മിലുള്ള ക്ലോക്ക് വ്യത്യാസം (clock skew) യഥാർത്ഥമാണ്, കൂടാതെ മെയിൽ API-കൾ പലപ്പോഴും അവയുടെ ഇൻഡക്സുകൾ കാഷെ (cache) ചെയ്യുകയോ ബാച്ച് ചെയ്യുകയോ ചെയ്യാറുണ്ട്.

തിരക്കേറിയ എൻവയോൺമെന്റുകളിൽ ടൈംസ്റ്റാമ്പുകൾ കൃത്യതയില്ലാത്തതാകാം. നിങ്ങൾക്ക് നേരിട്ടുള്ള ഒരു മാർഗ്ഗം ആവശ്യമാണ്.

എന്താണ് യഥാർത്ഥത്തിൽ ഒരു റൺ ടോക്കൺ (Run Token)?

നിങ്ങളുടെ ടെസ്റ്റിന്റെ തുടക്കത്തിൽ നിർമ്മിക്കുകയും ആപ്ലിക്കേഷൻ അയക്കുന്ന ഇമെയിലിൽ ഉൾപ്പെടുത്തുകയും ചെയ്യുന്ന ഒരു യുണീക് സ്ട്രിംഗ് (unique string) മാത്രമാണ് റൺ ടോക്കൺ. ഇത് ഉപയോക്താക്കൾ കാണേണ്ട ഒന്നല്ല, അത് കാണാൻ ഭംഗിയുള്ളതാകണമെന്നുമില്ല. ഈ പ്രത്യേക മെസേജ് ഈ പ്രത്യേക ടെസ്റ്റ് എക്സിക്യൂഷന് (test execution) ഉള്ളതാണെന്ന് തെളിയിക്കാൻ സാധിക്കണം എന്ന് മാത്രം ഉറപ്പാക്കിയാൽ മതി.

വ്യക്തമായ ഉദാഹരണങ്ങൾ നോക്കാം. ടെസ്റ്റ് തുടങ്ങുന്നതിന് മുമ്പ്, താഴെ പറയുന്നവ പോലുള്ള ഒരു ടോക്കൺ നിർമ്മിക്കുക:

  • ഒരു UUID: 550e8400-e29b-41d4-a716-446655440001
  • ഒരു build-scoped request ID: req_ci_build_4821_a7f3
  • ഒരു invite slug അല്ലെങ്കിൽ metadata suffix: signup-token-8k2m9n
  • ടെസ്റ്റ് റണ്ണർ നിർമ്മിക്കുന്ന ഒരു random hex string: test-run-a4f9c2d1

നിങ്ങൾക്ക് ബാക്കെൻഡ് കോഡ് നിയന്ത്രിക്കാൻ കഴിയുമെങ്കിൽ, ടോക്കൺ ഇമെയിൽ കോൺടെക്സ്റ്റിലേക്ക് പാസ്സ് ചെയ്യുകയും ബോഡിയിൽ എവിടെയെങ്കിലും കാണിക്കുകയും ചെയ്യുക. നിങ്ങൾ ഒരു ബ്ലാക്ക്-ബോക്സ് (black-box) ആപ്ലിക്കേഷനാണ് ടെസ്റ്റ് ചെയ്യുന്നതെങ്കിൽ, നിങ്ങൾക്ക് ഉപയോഗിക്കാൻ കഴിയുന്ന ഒരു റഫറൻസ് ഫീൽഡ് ആപ്പ് നേരത്തെ തന്നെ സ്വീകരിക്കുന്നുണ്ടോ എന്ന് പരിശോധിക്കുക. ഇല്ലെങ്കിൽ, പ്ലസ് അഡ്രസിംഗ് (plus addressing) ഉപയോഗിച്ച് റിസീപ്യന്റുടെ ലോക്കൽ പാർട്ടിനുള്ളിൽ ടോക്കൺ ഉൾപ്പെടുത്താം—testuser+a4f9c2d1@example.com—എന്നാൽ നിങ്ങളുടെ ആപ്ലിക്കേഷൻ അത് നിലനിർത്തുകയും ഇമെയിലിൽ തിരികെ നൽകുകയും ചെയ്യുന്നുണ്ടെങ്കിൽ മാത്രമേ ഇത് പ്രവർത്തിക്കൂ.

മെയിൽ സിസ്റ്റം തന്നെ കൈവശം വെച്ചിരിക്കുന്ന മെറ്റാഡാറ്റ ഉപയോഗിച്ച് മാച്ച് ചെയ്യുന്നത് നിർത്തുക എന്നതാണ് ഇതിന്റെ ലക്ഷ്യം. നിങ്ങളുടെ ടെസ്റ്റിന് നിയന്ത്രണമുള്ള ഡാറ്റ ഉപയോഗിച്ച് മാച്ച് ചെയ്യുക.

വിശ്വസനീയമായ രീതി

"Newest message" എന്ന അൽഗോരിതത്തിന് പകരം ടോക്കൺ അടിസ്ഥാനമാക്കിയുള്ള ഒരു സെർച്ച് രീതി ഉപയോഗിക്കുക:

  1. ഏതെങ്കിലും ഫ്ലോ ട്രിഗർ ചെയ്യുന്നതിന് മുമ്പ് റൺ ടോക്കൺ നിർമ്മിക്കുക.
  2. ആപ്ലിക്കേഷൻ ഔട്ട്‌ബൗണ്ട് ഇമെയിലിൽ (outbound email) ടോക്കൺ ഉൾപ്പെടുത്തുന്നുണ്ടെന്ന് ഉറപ്പാക്കി യൂസർ ആക്ഷൻ ആരംഭിക്കുക.
  3. ആ ടോക്കണിലേക്ക് പരിമിതപ്പെടുത്തിയ ഫിൽട്ടറുകൾ ഉപയോഗിച്ച് മെയിൽ പ്രൊവൈഡറോട് വിവരങ്ങൾ ചോദിക്കുക (poll). API ബോഡി സെർച്ച് പിന്തുണയ്ക്കുന്നുണ്ടെങ്കിൽ അത് ഉപയോഗിക്കുക. ഇല്ലെങ്കിൽ, അനുമാനിക്കാവുന്ന മെസേജുകൾ എടുത്ത് ക്ലയന്റ് സൈഡിൽ അവയുടെ ബോഡി പരിശോധിക്കുക (grep).
  4. ലിങ്കുകളോ ബട്ടണുകളോ വെരിഫിക്കേഷൻ കോഡുകളോ പരിശോധിക്കുന്നതിന് മുമ്പ്, മെസേജ് ബോഡിയിൽ ടോക്കൺ ഉണ്ടെന്ന് ഉറപ്പുവരുത്തുക (assert).
  5. അതിനുശേഷം മാത്രം കൺഫർമേഷൻ URL അല്ലെങ്കിൽ കോഡ് എടുത്ത് മുന്നോട്ട് പോവുക.

ഈ ക്രമം വളരെ പ്രധാനമാണ്. നിങ്ങൾ ആദ്യം ഒരു ലിങ്ക് എടുക്കുകയും രണ്ടാമത് ടോക്കൺ പരിശോധിക്കുകയും ചെയ്താൽ, നിങ്ങൾ തെറ്റായ ഇമെയിലിൽ ക്ലിക്ക് ചെയ്തുകഴിഞ്ഞു എന്നാണ് അർത്ഥം. അസർഷൻ (assertion) ആണ് നിങ്ങളുടെ കാവൽക്കാരൻ.

പ്രായോഗികമായി, നിങ്ങളുടെ ഹെൽപ്പർ Subject:"Welcome to AppName" sort:-received എന്നതിന് പകരം Subject:"Welcome to AppName" AND Body:"a4f9c2d1" എന്ന് തിരയണം. ബോഡി കണ്ടന്റ് ഫിൽട്ടറുകൾ സ്വീകരിക്കുന്ന സെർച്ച് API-കൾ പല മെയിൽ ടെസ്റ്റിംഗ് സർവീസുകളും നൽകുന്നുണ്ട്. അവ ഉപയോഗിക്കുക. നിങ്ങൾ ലളിതമായ ഒരു പ്രൊവൈഡറാണ് ഉപയോഗിക്കുന്നതെങ്കിൽ, എല്ലാ ടെസ്റ്റുകളിലും ഒരേപോലെ ക്ലയന്റ് സൈഡ് ഫിൽട്ടറിംഗ് ചേർക്കാൻ പാകത്തിൽ നിങ്ങളുടെ പോളിംഗ് ലോജിക് ഒരിടത്ത് തന്നെ സൂക്ഷിക്കുക.

സിസ്റ്റം കൃത്യതയോടെ നിലനിർത്താൻ മൂന്ന് നിയമങ്ങൾ

ഒരു റൺ ടോക്കൺ (run token) സെലക്ഷൻ കൃത്യമാക്കുന്നു, എങ്കിലും നിങ്ങൾ എങ്ങനെ പോൾ ചെയ്യുന്നു എന്നതിലും കാര്യങ്ങൾ തെറ്റായി പോകുമ്പോൾ എന്ത് ചെയ്യുന്നു എന്നതിലും അച്ചടക്കം ആവശ്യമാണ്.

പരാജയപ്പെടുമ്പോൾ ഇൻബോക്സ് സ്റ്റേറ്റ് ലോഗ് ചെയ്യുക. ഒരു ടെസ്റ്റ് പരാജയപ്പെടുമ്പോൾ, ഇൻബോക്സ് ഐഡന്റിഫയർ, നിങ്ങൾ തിരഞ്ഞ സബ്ജക്ട് ലൈൻ, കൃത്യമായ ടൈംസ്റ്റാമ്പ് വിൻഡോ, എത്ര സന്ദേശങ്ങൾ നിങ്ങളുടെ മാനദണ്ഡങ്ങളുമായി പൊരുത്തപ്പെട്ടു എന്നിവ ഔട്ട്‌പുട്ട് ആയി നൽകുക. ഇത് അവ്യക്തമായ ഒരു "email not found" എററിനെ വ്യക്തമായ ഒരു വിവരണമാക്കി മാറ്റുന്നു. ജോബ് 7823, ജോബ് 7821-ൽ നിന്നുള്ള ഒരു റീട്രൈ മെസ്സേജ് മൂന്ന് സെക്കൻഡ് വൈകി വന്നതുകൊണ്ട് എടുത്തതാണെങ്കിൽ, നിങ്ങളുടെ ലോഗുകൾ അത് വ്യക്തമാക്കണം. ഈ വിവരങ്ങൾ ഇല്ലാതെ, നിങ്ങൾ സമയത്തെ (timing) കുറ്റപ്പെടുത്തുകയും വീണ്ടും ഒരു 'sleep' ചേർക്കുകയും ചെയ്യും.

എല്ലാ ഇമെയിൽ പോളിംഗും ഒരു ഹെൽപ്പർ ഫയലിൽ മാത്രം സൂക്ഷിക്കുക. setTimeout, cy.task എന്നിവ ഇരുപതോളം ടെസ്റ്റ് ഫയലുകളിലായി ചിതറിക്കിടക്കാൻ അനുവദിക്കരുത്. സന്ദേശങ്ങൾക്കായി കാത്തിരിക്കുന്നതും, API കോൾ വീണ്ടും ശ്രമിക്കുന്നതും (retry), ബാക്ക്ഓഫ് (backoff) പ്രയോഗിക്കുന്നതുമായ ലോജിക് കേന്ദ്രീകരിക്കുക. എല്ലാ ടെസ്റ്റുകളും ഒരേ ഹെൽപ്പർ ഉപയോഗിക്കുകയാണെങ്കിൽ, നിങ്ങളുടെ ഫിൽട്ടറിംഗ് നിയമങ്ങൾ സ്ഥിരമായിരിക്കും, കൂടാതെ സെർച്ച് ലോജിക് മെച്ചപ്പെടുമ്പോൾ എല്ലാ ടെസ്റ്റുകൾക്കും അതിന്റെ ഗുണം ലഭിക്കും. ഇത് ടോക്കൺ ചെക്ക് നടപ്പിലാക്കുന്നത് എളുപ്പമാക്കുന്നു; ഹെൽപ്പറിന് ഒരു ടോക്കൺ ആർഗ്യുമെന്റ് ആവശ്യമാണെങ്കിൽ, ആർക്കും അബദ്ധവശാൽ "latest message" എന്ന രീതിയിലേക്ക് മാറാൻ കഴിയില്ല.

റീട്രൈകൾ (retries) ശ്രദ്ധിക്കുക. CI-യിൽ ടെസ്റ്റ് റീട്രൈകൾ സാധാരണമാണ്, എന്നാൽ ഓരോ റീട്രൈയും ഇൻബോക്സിൽ മറ്റൊരു ഇമെയിൽ സൃഷ്ടിക്കുന്നു. മൂന്നാമത്തെ ശ്രമത്തിൽ നിങ്ങളുടെ ടെസ്റ്റ് വിജയിച്ചാൽ, നിങ്ങൾ അത് ആഘോഷിച്ച് മുന്നോട്ട് പോയേക്കാം. എന്നാൽ ഒന്നാമത്തെയും രണ്ടാമത്തെയും ശ്രമങ്ങൾ ഒരു യഥാർത്ഥ ബഗ്—ഒരു റേസ് കണ്ടീഷൻ (race condition), ഡ്യൂപ്ലിക്കേറ്റ് സെൻഡ്, അല്ലെങ്കിൽ ഒരു മിസ്സിംഗ് ഇൻഡക്സ്—വെളിപ്പെടുത്തിയതാകാം, ആ അധിക സന്ദേശങ്ങൾ അത് മറച്ചുവെച്ചു എന്ന് നിങ്ങൾ തിരിച്ചറിയുന്നില്ല. നിങ്ങൾ റീട്രൈകൾ ഉപയോഗിക്കണമെന്നുണ്ടെങ്കിൽ, പരാജയപ്പെട്ടതിന് ശേഷം ഇൻബോക്സിൽ അപ്രതീക്ഷിതമായ ഡ്യൂപ്ലിക്കേറ്റുകൾ ഉണ്ടോ എന്ന് പരിശോധിക്കുക. അതിനേക്കാളും നല്ലത്, നിങ്ങളുടെ പ്രൊവൈഡർ ഡൈനാമിക് ഇൻബോക്സുകൾ പിന്തുണയ്ക്കുന്നുണ്ടെങ്കിൽ ഇൻബോക്സ് ക്ലീൻ ചെയ്യുന്നതോ ഓരോ ജോബിനും ഒരു യുണീക് അഡ്രസ് ഉപയോഗിക്കുന്നതോ ആണ്. അവിശ്വസനീയമായ സെലക്ഷൻ ലോജിക്കുകളെ മറച്ചുവെക്കാനുള്ള ഒരു തന്ത്രമായി റീട്രൈകൾ മാറരുത്.

യഥാർത്ഥ പാഠം

ഒരു ഇൻബോക്സിനെ തീയതി അനുസരിച്ച് ക്രമീകരിച്ച് (sorting) ആദ്യത്തെ റിസൾട്ട് എടുക്കുന്നത് ടെസ്റ്റിംഗ് അല്ല. അത് കോഡിംഗിലൂടെ നടത്തുന്ന ഒരു ഊഹം മാത്രമാണ്. ഒരു റൺ ടോക്കൺ ഉപയോഗിക്കുന്നത് വലിയ ചിലവുള്ള കാര്യമല്ല—ഒരു സ്ട്രിംഗ് വേരിയബിൾ, ഒരു അധിക ഫിൽട്ടർ പാരാമീറ്റർ, ഒരു ചെറിയ ടെംപ്ലേറ്റ് മാറ്റം—ഇത് നിങ്ങളുടെ ടെസ്റ്റിന് കൃത്യമായ ഒരു ഐഡന്റിറ്റി നൽകുന്നു. നിങ്ങളുടെ മുന്നിലുള്ള സന്ദേശം നിങ്ങൾ ഇപ്പോൾ എക്സിക്യൂട്ട് ചെയ്യുന്ന റണ്ണിന്റേതാണെന്ന് ഇത് തെളിയിക്കുന്നു.

'sleep' ചേർത്ത് നെറ്റ്‌വർക്ക് ശരിയാകുമെന്ന് പ്രതീക്ഷിക്കുന്നത് നിർത്തുക. ഒരു ടോക്കൺ ജനറേറ്റ് ചെയ്യുക, അത് ഇമെയിലിൽ ഉൾപ്പെടുത്തുക, എന്നിട്ട് നേരിട്ട് അത് തിരയുക. നിങ്ങളുടെ CI റണ്ണുകൾ വേഗതയുള്ളതാകും, ലോഗുകൾ വായിക്കാൻ എളുപ്പമാകും, ഒടുവിൽ നിങ്ങളുടെ ഇമെയിൽ സ്യൂട്ട് പറയുന്നത് നിങ്ങൾക്ക് വിശ്വസിക്കാൻ സാധിക്കും.