കാഷെ പ്രവർത്തിക്കുന്നുണ്ടായിരുന്നു, എങ്കിലും അത് ഡ്യൂപ്ലിക്കേറ്റ് API കോളുകൾക്ക് കാരണമായി

കാഷെ തകരാറിലായിരുന്നില്ല.

എന്നിരുന്നാലും, ഒരേ യൂസർനെയിമിനായി വന്ന മൂന്ന് കൺകറന്റ് (concurrent) റിക്വസ്റ്റുകൾ GitHub-ലേക്ക് മൂന്ന് തവണ എത്തിച്ചേർന്നു.

GitHub ഡാറ്റയെ SVG ബാഡ്ജുകളാക്കി മാറ്റുന്ന ഒരു Next.js API ആയ CommitPulse-ലാണ് ഇത് സംഭവിച്ചത്. ഒരു README വൈറലാകുമ്പോൾ, ആയിരക്കണക്കിന് ആളുകൾ ഒരേസമയം ആ ബാഡ്ജ് കാണുന്നു. ഇത് വലിയ തോതിലുള്ള ട്രാഫിക് സൃഷ്ടിക്കുന്നു.

സീക്വൻഷ്യൽ (sequential) റിക്വസ്റ്റുകൾക്ക് കാഷെ കൃത്യമായി പ്രവർത്തിച്ചു. എന്നാൽ കൺകറന്റ് റിക്വസ്റ്റുകളിൽ അത് പരാജയപ്പെട്ടു.

പ്രശ്നം ഇതാണ്:

  • റിക്വസ്റ്റ് A കാഷെ പരിശോധിക്കുന്നു. അത് ഒരു 'miss' ആണ്. റിക്വസ്റ്റ് A GitHub-ൽ നിന്ന് ഡാറ്റ എടുക്കാൻ തുടങ്ങുന്നു.
  • 5ms കഴിഞ്ഞ് റിക്വസ്റ്റ് B എത്തുന്നു. അത് കാഷെ പരിശോധിക്കുന്നു. റിക്വസ്റ്റ് A പൂർത്തിയായിട്ടില്ലാത്തതിനാൽ ഇതും ഒരു 'miss' ആണ്. റിക്വസ്റ്റ് B രണ്ടാമതൊരു ഫെച്ച് (fetch) ആരംഭിക്കുന്നു.
  • 10ms കഴിഞ്ഞ് റിക്വസ്റ്റ് C എത്തുന്നു. ഇതും ഒരു കാഷെ മിസ്സ് കാണുകയും മൂന്നാമതൊരു ഫെച്ച് ആരംഭിക്കുകയും ചെയ്യുന്നു.

ഇതാണ് thundering herd പ്രശ്നം. ഉയർന്ന ലോഡിൽ ഒരു കാഷെ മിസ്സ് സംഭവിക്കുമ്പോൾ, നിങ്ങളുടെ അപ്‌സ്ട്രീം പ്രൊവൈഡറിലേക്ക് (upstream provider) ഒരേപോലെയുള്ള കോളുകളുടെ ഒരു പ്രവാഹം തന്നെ ഉണ്ടാകുന്നു. GitHub പോലുള്ള റേറ്റ്-ലിമിറ്റഡ് (rate-limited) API ആണ് നിങ്ങൾ ഉപയോഗിക്കുന്നതെങ്കിൽ, ഇത് നിങ്ങളുടെ ലിമിറ്റുകൾ പെട്ടെന്ന് തന്നെ തീർന്നുപോകാൻ കാരണമാകും.

ഇതിനുള്ള പരിഹാരം request coalescing ആണ്.

പൂർത്തിയായ കാഷെ എൻട്രികളിൽ നിന്ന് വ്യത്യസ്തമായി, നിലവിലുള്ള (pending) റിക്വസ്റ്റുകളെ നിങ്ങൾ പ്രത്യേകം ട്രാക്ക് ചെയ്യണം. ഇത് കൈകാര്യം ചെയ്യാൻ ഞാൻ ഒരു in-flight Map നടപ്പിലാക്കി:

  • ഒരു റിക്വസ്റ്റ് തുടങ്ങുമ്പോൾ, അതിന്റെ Promise ഒരു Map-ൽ സൂക്ഷിക്കുക.
  • ഒരേ കീക്കായി രണ്ടാമതൊരു റിക്വസ്റ്റ് വന്നാൽ, പുതിയൊരു ഫെച്ച് ആരംഭിക്കരുത്.
  • പകരം, Map-ൽ നിലവിലുള്ള Promise തന്നെ തിരികെ നൽകുക.
  • റിക്വസ്റ്റ് പൂർത്തിയായിക്കഴിഞ്ഞാൽ, അത് Map-ൽ നിന്ന് നീക്കം ചെയ്യുകയും ഫലം കാഷെയിൽ സേവ് ചെയ്യുകയും ചെയ്യുക.

എത്ര ആളുകൾ ഒരേസമയം ഒരേ ഡാറ്റയ്ക്കായി റിക്വസ്റ്റ് ചെയ്താലും, ഒരു കോൾ മാത്രമേ API-ലേക്ക് എത്തുന്നുള്ളൂ എന്ന് ഇത് ഉറപ്പാക്കുന്നു.

ഇത് പരിഹരിക്കുന്നതിനിടയിൽ, അതേ ഫയലിലെ മറ്റ് മൂന്ന് എഡ്ജ്-കേസ് (edge-case) ബഗുകളും ഞാൻ പരിഹരിച്ചു:

  • Missing Token Errors: സിസ്റ്റം ഒരു ക്രെഡൻഷ്യലായി "undefined" ആണ് അയച്ചിരുന്നത്. റിക്വസ്റ്റുകൾ ചെയ്യുന്നതിന് മുമ്പ് ടോക്കണുകൾ പരിശോധിക്കുന്ന രീതിയിലേക്ക് ഞാൻ ഇത് മാറ്റി.
  • Memory Leaks: Retry logic AbortSignals-ൽ പഴയ ഇവന്റ് ലിസണറുകൾ (event listeners) അവശേഷിപ്പിച്ചു. ലീക്കുകൾ ഒഴിവാക്കാൻ ഞാൻ ക്ലീനപ്പ് ലോജിക് (cleanup logic) ചേർത്തു.
  • URL Injection: സ്പെഷ്യൽ ക്യാരക്ടറുകളുള്ള യൂസർനെയിമുകൾ API പാത്തുകളെ തകരാറിലാക്കിയിരുന്നു. URL ഘടന സംരക്ഷിക്കുന്നതിനായി ഞാൻ എൻകോഡിംഗ് (encoding) ചേർത്തു.

ഒരു കാഷെ മാത്രം പോരാ. നിലവിൽ നടന്നുകൊണ്ടിരിക്കുന്ന (in flight) റിക്വസ്റ്റുകളെ ഡ്യൂപ്ലിക്കേറ്റ് ആകാതെ നോക്കേണ്ടതുമുണ്ട് (deduplicate).

Source: https://dev.to/eshaanagrawal/the-cache-was-working-and-still-causing-duplicate-api-calls-3n51