कॅश व्यवस्थित काम करत होती, तरीही त्यातून डुप्लिकेट API कॉल्स होत होते
कॅशमध्ये काही बिघाड नव्हता.
तरीही, एकाच युजरनेमसाठी आलेल्या तीन एकाच वेळी (concurrent) विनंत्यांनी GitHub कडे तीन वेळा रिक्वेस्ट पाठवली.
हे CommitPulse मध्ये घडले, जे एक Next.js API आहे आणि ते GitHub डेटाचे SVG बॅजेसमध्ये रूपांतर करते. जेव्हा एखादा README व्हायरल होतो, तेव्हा हजारो लोक एकाच वेळी तो बॅज पाहतात. यामुळे प्रचंड ट्रॅफिक निर्माण होते.
कॅश क्रमाने येणाऱ्या (sequential) विनंत्यांसाठी व्यवस्थित काम करत होती. पण एकाच वेळी येणाऱ्या (concurrent) विनंत्यांसाठी ती अपयशी ठरली.
समस्या खालीलप्रमाणे आहे:
- विनंती A (Request A) कॅश तपासते. तिथे डेटा मिळत नाही (cache miss). विनंती A GitHub कडून डेटा मिळवण्यास सुरुवात करते.
- विनंती B (Request B) ५ मिलीसेकंद (ms) नंतर येते. ती कॅश तपासते. विनंती A अजून पूर्ण झालेली नसल्यामुळे तिथेही डेटा मिळत नाही (cache miss). विनंती B दुसरी फेच (fetch) प्रक्रिया सुरू करते.
- विनंती C (Request C) १० मिलीसेकंद (ms) नंतर येते. तिलाही कॅश मिस आढळतो आणि ती तिसरी फेच प्रक्रिया सुरू करते.
याला 'थंडरिंग हर्ड' (thundering herd) समस्या म्हणतात. जास्त लोड असताना कॅश मिस (cache miss) झाल्यामुळे तुमच्या अपस्ट्रीम प्रोव्हायडरकडे (upstream provider) एकाच प्रकारच्या कॉल्सचा पूर येतो. जर तुम्ही GitHub सारखे रेट-लिमिटेड (rate-limited) API वापरत असाल, तर यामुळे तुमची मर्यादा (limits) त्वरित संपू शकते.
याचे समाधान म्हणजे 'रिक्वेस्ट कोलेसिंग' (request coalescing) होय.
तुम्हाला पूर्ण झालेल्या कॅश एन्ट्रीजपेक्षा प्रलंबित (pending) विनंत्या वेगळ्या प्रकारे ट्रॅक कराव्या लागतील. मी हे व्यवस्थापित करण्यासाठी एक 'in-flight Map' लागू केले:
- जेव्हा एखादी विनंती सुरू होते, तेव्हा तिचा Promise एका Map मध्ये साठवा.
- जर त्याच की (key) साठी दुसरी विनंती आली, तर नवीन फेच (fetch) सुरू करू नका.
- त्याऐवजी, Map मधून असलेला विद्यमान Promise परत करा.
- विनंती पूर्ण झाल्यावर, ती Map मधून काढून टाका आणि निकाल कॅशमध्ये सेव्ह करा.
यामुळे हे सुनिश्चित होते की कितीही लोकांनी एकाच वेळी तो डेटा मागितला तरी, API कडे फक्त एकच कॉल जाईल.
हे ठीक करताना, मी त्याच फाईलमध्ये इतर तीन 'एज-केस' (edge-case) बग्स देखील सोडवले:
- Missing Token Errors: सिस्टमने क्रेडेंशियल म्हणून "undefined" पाठवले होते. मी विनंत्या करण्यापूर्वी टोकन्सची पडताळणी (validate) करण्यासाठी ते अपडेट केले.
- Memory Leaks: Retry लॉजिकमुळे AbortSignals वर जुने (stale) इव्हेंट लिसनर्स (event listeners) राहिले होते. मी लीक्स रोखण्यासाठी क्लीनअप लॉजिक (cleanup logic) जोडले.
- URL Injection: विशेष चिन्हे (special characters) असलेल्या युजरनेम्समुळे API पाथ (paths) बिघडत होते. URL स्ट्रक्चर सुरक्षित ठेवण्यासाठी मी एन्कोडिंग (encoding) जोडले.
केवळ कॅश पुरेसा नाही. सध्या प्रलंबित (in flight) असलेल्या विनंत्यांचे ड्युप्लिकेट्स कमी करणे (deduplicate) देखील आवश्यक आहे.
स्रोत: https://dev.to/eshaanagrawal/the-cache-was-working-and-still-causing-duplicate-api-calls-3n51
