നിങ്ങളുടെ മോണിറ്ററിംഗിന് കണ്ടെത്താൻ കഴിയാത്ത ആ Node.js ബഗ്ഗ്

നിങ്ങളുടെ ഹെൽത്ത് ചെക്ക് (health check) എല്ലാം ശരിയാണെന്ന് കാണിക്കുന്നു. ഇതിന് ഒരു മില്ലിസെക്കൻഡ് മാത്രമേ എടുക്കുന്നുള്ളൂ. എന്നാൽ ട്രാഫിക് കൂടുമ്പോൾ, പെട്ടെന്ന് നിങ്ങളുടെ p99 ലേറ്റൻസി (latency) 400ms ആയി ഉയരുന്നു. നിങ്ങൾ ഡാഷ്‌ബോർഡുകൾ പരിശോധിക്കുന്നു. എല്ലാം പച്ച നിറത്തിൽ (green) കാണപ്പെടുന്നു.

CPU ഉപയോഗം മിതമാണ്. ഇവന്റ് ലൂപ്പ് ലാഗ് (event loop lag) മാറ്റമില്ലാതെ തുടരുന്നു. മെമ്മറി ആരോഗ്യകരമാണ്. നിങ്ങളുടെ APM ഒരു സ്ലോ റിക്വസ്റ്റ് കാണിക്കുന്നുണ്ടെങ്കിലും അതിന്റെ കാരണം എന്താണെന്ന് പറയുന്നില്ല. സ്ലോ ആയ ഡാറ്റാബേസ് കോളുകൾ (database calls) ഒന്നുമില്ല. എററുകളും (errors) ഇല്ല.

സമയം ചെലവഴിക്കുന്നത് libuv thread pool-ലാണ്.

സാധാരണ Node ഒബ്സർവബിലിറ്റി (observability) ഇവന്റ് ലൂപ്പിന് പ്രാധാന്യം നൽകുന്നു. എന്നാൽ ഈ പൂൾ (pool) ഒരു പ്രത്യേക ക്യൂ (queue) ആണ്. അത് നിങ്ങളുടെ പരിധിക്ക് പുറത്താണ്.

Node ഇവന്റ് ലൂപ്പിലാണ് JavaScript പ്രവർത്തിപ്പിക്കുന്നത്. എന്നാൽ കനത്ത ജോലികൾ (heavy tasks) അത് libuv thread pool-ലേക്ക് മാറ്റുന്നു. ഇതിൽ ഉൾപ്പെടുന്നവ:

  • ഫയൽസിസ്റ്റം ജോലികൾ (fs.readFile, fs.writeFile).
  • ക്രിപ്റ്റോ ടാസ്ക്കുകൾ (bcrypt, scrypt, pbkdf2).
  • കംപ്രഷൻ (zlib gzip, deflate).
  • DNS ലുക്കപ്പുകൾ (dns.lookup).

ഈ പൂളിന് ഡിഫോൾട്ടായി നാല് തിരെഡുകൾ (threads) മാത്രമേ ഉണ്ടാകൂ. നിങ്ങളുടെ മെഷീനിൽ എത്ര CPU കോറുകൾ ഉണ്ടെങ്കിലും ഇത് മാറില്ല.

നാല് തിരെഡുകൾ മതിയാവില്ല. പൂൾ തകരാറിലാകാൻ സാധ്യതയുള്ള മൂന്ന് വഴികൾ താഴെ പറയുന്നവയാണ്:

  1. ലോഗിൻ സമയത്തെ Bcrypt. ഒരു bcrypt ഹാഷ് ചെയ്യാൻ 250ms എടുത്തേക്കാം. ഒരേസമയം നാല് പേർ ലോഗിൻ ചെയ്താൽ എല്ലാ സ്ലോട്ടുകളും നിറയും. അഞ്ചാമത്തെ ആൾ ഒരു ക്യൂവിൽ കാത്തുനിൽക്കേണ്ടി വരും. തുടങ്ങാൻ വേണ്ടി മാത്രം അവർക്ക് 250ms കാത്തുനിൽക്കേണ്ടി വരുന്നു. ഇത് നിങ്ങളുടെ ലേറ്റൻസി ഇരട്ടിയാക്കുന്നു.

  2. വലിയ gzip ഓപ്പറേഷനുകൾ. ഒരു വലിയ റെസ്പോൺസ് കംപ്രസ് ചെയ്യുന്നത് പൂളിലെ ഒരു സ്ലോട്ട് ഉപയോഗിക്കുന്നു. ഒരേസമയം നാല് റിക്വസ്റ്റുകൾ ഇങ്ങനെ ചെയ്താൽ, മറ്റെല്ലാ ജോലികളും കാത്തുനിൽക്കേണ്ടി വരും. DNS ലുക്കപ്പുകളും ഫയൽ റീഡുകളും വരിയിൽ കുടുങ്ങിപ്പോകും.

  3. DNS ലുക്കപ്പുകൾ. മിക്ക Node ആപ്പുകളും dns.lookup ആണ് ഉപയോഗിക്കുന്നത്. ഇത് ഒരു ബ്ലോക്കിംഗ് സിസ്റ്റം കോൾ (blocking system call) ആണ്. ഇത് ടാസ്കിനെ പൂളിലേക്ക് മാറ്റുന്നു. നിങ്ങളുടെ നെറ്റ്‌വർക്കിൽ ചെറിയൊരു തടസ്സം ഉണ്ടായാൽ പോലും, ഈ ലുക്കപ്പുകൾ പൂൾ മുഴുവനായി സ്തംഭിപ്പിക്കും.

പൂൾ ക്യൂവിൽ കുടുങ്ങിക്കിടക്കുന്ന ഒരു റിക്വസ്റ്റ് കണ്ടെത്താൻ കഴിയില്ല. അത് CPU ഉപയോഗിക്കുന്നില്ല. അത് JavaScript പ്രവർത്തിപ്പിക്കുന്നുമില്ല. അത് വെറുതെ പാർക്ക് ചെയ്യപ്പെട്ട അവസ്ഥയിലാണ്.

ഇത് എങ്ങനെ കണ്ടെത്താം:

ലോഡ് കൂടുമ്പോൾ നിങ്ങളുടെ p99 ലേറ്റൻസി ഉയരുകയും എന്നാൽ ഇവന്റ് ലൂപ്പ് ലാഗ് മാറ്റമില്ലാതെ തുടരുകയും ചെയ്യുന്നുണ്ടെങ്കിൽ, പൂൾ പരിശോധിക്കുക.

ഏറ്റവും വേഗത്തിലുള്ള പരിശോധന: UV_THREADPOOL_SIZE വർദ്ധിപ്പിക്കുക. നിങ്ങളുടെ എൻവയോൺമെന്റിൽ ഇത് 64 ആയി സെറ്റ് ചെയ്ത് റീസ്റ്റാർട്ട് ചെയ്യുക. ലേറ്റൻസി കുറയുന്നുണ്ടെങ്കിൽ, നിങ്ങൾ പ്രശ്നം കണ്ടെത്തിയിരിക്കുന്നു.

ഇത് ശരിയായ രീതിയിൽ എങ്ങനെ പരിഹരിക്കാം:

  • bcrypt പോലുള്ള കനത്ത ക്രിപ്റ്റോ ജോലികൾക്കായി worker_threads ഉപയോഗിക്കുക. ഇത് അവയെ libuv പൂളിൽ നിന്ന് ഒഴിവാക്കും.
  • dns.lookup-ന് പകരം dns.resolve ഉപയോഗിക്കുക. ഇതൊരു യഥാർത്ഥ അസിങ്ക് (async) റെസൊൾവർ ആണ്.
  • സ്ലോട്ടുകൾ വേഗത്തിൽ വിട്ടുകൊടുക്കുന്നതിനായി zlib ജോലികൾക്ക് streams ഉപയോഗിക്കുക.
  • പ്രധാന റിക്വസ്റ്റ് പാത്തുകളിൽ (request paths) കനത്ത ഫയൽസിസ്റ്റം ജോലികൾ ഒഴിവാക്കുക.

ഉപയോക്താക്കൾ കാത്തുനിൽക്കുമ്പോൾ പച്ച നിറത്തിലുള്ള ഡാഷ്‌ബോർഡുകളിലേക്ക് നോക്കി നിൽക്കുന്നത് നിർത്തുക.

Source: https://dev.to/r9v/the-nodejs-bug-thats-invisible-to-your-monitoring-oo8