ನಿಮ್ಮ ಮಾನಿಟರಿಂಗ್‌ಗೆ ಕಾಣಿಸದ Node.js ಬಗ್

ನಿಮ್ಮ health check ಎಲ್ಲವೂ ಸರಿಯಾಗಿದೆ ಎಂದು ಹೇಳುತ್ತದೆ. ಇದು ಕೇವಲ ಒಂದು ಮಿಲಿಸೆಕೆಂಡ್ ತೆಗೆದುಕೊಳ್ಳುತ್ತದೆ. ನಂತರ ಟ್ರಾಫಿಕ್ ಹೆಚ್ಚಾಗುತ್ತದೆ. ಇದ್ದಕ್ಕಿದ್ದಂತೆ, ನಿಮ್ಮ p99 latency 400ms ಗೆ ಏರುತ್ತದೆ. ನೀವು ನಿಮ್ಮ ಡ್ಯಾಶ್‌ಬೋರ್ಡ್‌ಗಳನ್ನು ನೋಡುತ್ತೀರಿ. ಎಲ್ಲವೂ ಹಸಿರಾಗಿ (green) ಕಾಣುತ್ತದೆ.

CPU ಬಳಕೆ ಸಾಧಾರಣವಾಗಿದೆ. Event loop lag ಸ್ಥಿರವಾಗಿದೆ. ಮೆಮೊರಿ ಆರೋಗ್ಯಕರವಾಗಿದೆ. ನಿಮ್ಮ APM ಒಂದು ನಿಧಾನಗತಿಯ ವಿನಂತಿಯನ್ನು (slow request) ತೋರಿಸುತ್ತದೆ ಆದರೆ ಅದು ಏಕೆ ಎಂದು ಹೇಳುವುದಿಲ್ಲ. ಯಾವುದೇ ನಿಧಾನಗತಿಯ ಡೇಟಾಬೇಸ್ ಕರೆಗಳಿಲ್ಲ (database calls). ಯಾವುದೇ ದೋಷಗಳಿಲ್ಲ (errors).

ಸಮಯವು libuv thread pool ನಲ್ಲಿ ವ್ಯಯವಾಗುತ್ತಿದೆ.

ಪ್ರಮಾಣಿತ Node observability ಇವೆಂಟ್ ಲೂಪ್ (event loop) ಮೇಲೆ ಗಮನಹರಿಸುತ್ತದೆ. ಈ pool ಒಂದು ಪ್ರತ್ಯೇಕ ಕ್ಯೂ (queue). ಇದು ನಿಮ್ಮ ಗಮನಕ್ಕೆ ಬರದೇ ಹೊರಗಿದೆ.

Node ಇವೆಂಟ್ ಲೂಪ್‌ನಲ್ಲಿ JavaScript ಅನ್ನು ರನ್ ಮಾಡುತ್ತದೆ. ಇದು ಭಾರೀ ಕೆಲಸಗಳನ್ನು (heavy tasks) libuv thread pool ಗೆ ಕಳುಹಿಸುತ್ತದೆ. ಇದರಲ್ಲಿ ಇವು ಸೇರಿವೆ:

  • Filesystem ಕೆಲಸ (fs.readFile, fs.writeFile).
  • Crypto ಕಾರ್ಯಗಳು (bcrypt, scrypt, pbkdf2).
  • Compression (zlib gzip, deflate).
  • DNS lookups (dns.lookup).

Pool ಡಿಫಾಲ್ಟ್ ಆಗಿ ಕೇವಲ ನಾಲ್ಕು thread ಗಳನ್ನು ಮಾತ್ರ ಹೊಂದಿರುತ್ತದೆ. ನಿಮ್ಮ ಯಂತ್ರದಲ್ಲಿ ಎಷ್ಟೇ CPU cores ಇದ್ದರೂ ಇದು ನಿಜ.

ನಾಲ್ಕು thread ಗಳು ಸಾಕಾಗುವುದಿಲ್ಲ. Pool ಹೇಗೆ ವಿಫಲವಾಗಬಹುದು ಎಂಬುದಕ್ಕೆ ಇಲ್ಲಿ ಮೂರು ದಾರಿಗಳಿವೆ:

  1. ಲಾಗಿನ್ ಸಮಯದಲ್ಲಿ Bcrypt. ಒಂದು bcrypt hash 250ms ತೆಗೆದುಕೊಳ್ಳಬಹುದು. ಏಕಕಾಲದಲ್ಲಿ ನಾಲ್ವರು ಜನರು ಲಾಗಿನ್ ಆದರೆ, ಎಲ್ಲಾ ಸ್ಲಾಟ್‌ಗಳು (slots) ತುಂಬಿಹೋಗುತ್ತವೆ. ಐದನೇ ವ್ಯಕ್ತಿಯು ಕ್ಯೂನಲ್ಲಿ ಕಾಯಬೇಕಾಗುತ್ತದೆ. ಅವರು ಪ್ರಾರಂಭಿಸಲು ಮಾತ್ರ 250ms ಕಾಯಬೇಕಾಗುತ್ತದೆ. ಇದರಿಂದ ನಿಮ್ಮ latency ದ್ವಿಗುಣಗೊಳ್ಳುತ್ತದೆ.

  2. ದೊಡ್ಡ gzip ಕಾರ್ಯಾಚರಣೆಗಳು. ದೊಡ್ಡ ರೆಸ್ಪಾನ್ಸ್ ಅನ್ನು ಕಂಪ್ರೆಸ್ ಮಾಡುವುದು ಒಂದು pool slot ಅನ್ನು ಹಿಡಿದಿಟ್ಟುಕೊಳ್ಳುತ್ತದೆ. ನಾಲ್ಕು ವಿನಂತಿಗಳು ಏಕಕಾಲದಲ್ಲಿ ಇದನ್ನು ಮಾಡಿದರೆ, ಉಳಿದ ಎಲ್ಲಾ ಕೆಲಸಗಳು ಕಾಯಬೇಕಾಗುತ್ತದೆ. DNS lookups ಮತ್ತು file reads ಸಾಲಿನಲ್ಲಿ ಸಿಲುಕಿಕೊಳ್ಳುತ್ತವೆ.

  3. DNS lookups. ಹೆಚ್ಚಿನ Node ಅಪ್ಲಿಕೇಶನ್‌ಗಳು dns.lookup ಅನ್ನು ಬಳಸುತ್ತವೆ. ಇದು blocking system call ಅನ್ನು ಬಳಸುತ್ತದೆ. ಇದು ಕಾರ್ಯವನ್ನು pool ಮೇಲೆ ಹಾಕುತ್ತದೆ. ನಿಮ್ಮ ನೆಟ್‌ವರ್ಕ್‌ನಲ್ಲಿ ಅಡಚಣೆಯಾದರೆ, ಈ lookups ಇಡೀ pool ಅನ್ನು ಸ್ಥಗಿತಗೊಳಿಸುತ್ತವೆ.

Pool queue ನಲ್ಲಿ ಸಿಲುಕಿಕೊಂಡ ವಿನಂತಿಯು (request) ಅದೃಶ್ಯವಾಗಿರುತ್ತದೆ. ಅದು CPU ಅನ್ನು ಬಳಸುತ್ತಿಲ್ಲ. ಅದು JavaScript ಅನ್ನು ರನ್ ಮಾಡುತ್ತಿಲ್ಲ. ಅದು ಕೇವಲ ತಡೆಹಿಡಿಯಲ್ಪಟ್ಟಿದೆ (parked).

ಇದನ್ನು ಕಂಡುಹಿಡಿಯುವುದು ಹೇಗೆ:

ಲೋಡ್ ಅಡಿಯಲ್ಲಿ ನಿಮ್ಮ p99 latency ಹೆಚ್ಚಾಗುತ್ತಿದ್ದರೆ ಆದರೆ event loop lag ಸ್ಥಿರವಾಗಿದ್ದರೆ, pool ಅನ್ನು ಪರಿಶೀಲಿಸಿ.

ಅತ್ಯಂತ ವೇಗದ ಪರೀಕ್ಷೆ: UV_THREADPOOL_SIZE ಅನ್ನು ಹೆಚ್ಚಿಸಿ. ನಿಮ್ಮ ಎನ್ವಿರಾನ್ಮೆಂಟ್‌ನಲ್ಲಿ ಇದನ್ನು 64 ಗೆ ಸೆಟ್ ಮಾಡಿ ಮತ್ತು ರೀಸ್ಟಾರ್ಟ್ ಮಾಡಿ. ಒಂದು ವೇಳೆ latency ಕಡಿಮೆಯಾದರೆ, ನೀವು ಸಮಸ್ಯೆಯನ್ನು ಪತ್ತೆಹಚ್ಚಿದ್ದೀರಿ ಎಂದರ್ಥ.

ಇದನ್ನು ಸರಿಯಾಗಿ ಸರಿಪಡಿಸುವುದು ಹೇಗೆ:

  • bcrypt ನಂತಹ ಭಾರೀ crypto ಗಾಗಿ worker_threads ಬಳಸಿ. ಇದು ಅವುಗಳನ್ನು libuv pool ನಿಂದ ದೂರವಿಡುತ್ತದೆ.
  • dns.lookup ಬದಲಿಗೆ dns.resolve ಬಳಸಿ. ಇದು ನಿಜವಾದ async resolver ಆಗಿದೆ.
  • ಸ್ಲಾಟ್‌ಗಳನ್ನು ವೇಗವಾಗಿ ಬಿಡುಗಡೆ ಮಾಡಲು zlib ಕೆಲಸಕ್ಕಾಗಿ streams ಬಳಸಿ.
  • ನಿಮ್ಮ ಮುಖ್ಯ ವಿನಂತಿ ಮಾರ್ಗಗಳಲ್ಲಿ (main request paths) ಭಾರೀ filesystem ಕೆಲಸಗಳನ್ನು ತಪ್ಪಿಸಿ.

ನಿಮ್ಮ ಬಳಕೆದಾರರು ಕಾಯುತ್ತಿರುವಾಗ ಹಸಿರು ಡ್ಯಾಶ್‌ಬೋರ್ಡ್‌ಗಳನ್ನು ನೋಡುವುದು ನಿಲ್ಲಿಸಿ.

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