ক্যাশ কাজ করছিল, তবুও এটি ডুপ্লিকেট API কল ঘটাচ্ছিল

ক্যাশটি নষ্ট ছিল না।

তবুও, একই ইউজারনেমের জন্য তিনটি কনকারেন্ট (concurrent) রিকোয়েস্ট GitHub-এ তিনবার হিট করেছিল।

এটি ঘটেছিল CommitPulse-এ, যা একটি Next.js API এবং এটি GitHub ডেটাকে SVG ব্যাজে রূপান্তরিত করে। যখন কোনো README ভাইরাল হয়, তখন হাজার হাজার মানুষ একসাথে ব্যাজটি দেখে। এর ফলে প্রচুর ট্রাফিক তৈরি হয়।

ক্যাশটি সিকোয়েন্সিয়াল (sequential) রিকোয়েস্টের জন্য কাজ করছিল। কিন্তু কনকারেন্ট রিকোয়েস্টের ক্ষেত্রে এটি ব্যর্থ হয়েছিল।

সমস্যাটি হলো:

  • রিকোয়েস্ট A ক্যাশ চেক করে। এটি একটি 'miss'। রিকোয়েস্ট A GitHub থেকে ডেটা ফেচ (fetch) করা শুরু করে।
  • রিকোয়েস্ট B ৫ মিলি-সেকেন্ড (5ms) পরে আসে। এটি ক্যাশ চেক করে। এটি তখনও একটি 'miss', কারণ রিকোয়েস্ট A এখনও শেষ হয়নি। রিকোয়েস্ট B দ্বিতীয়বার ফেচ শুরু করে।
  • রিকোয়েস্ট C ১০ মিলি-সেকেন্ড (10ms) পরে আসে। এটিও একটি ক্যাশ মিস দেখে এবং তৃতীয়বার ফেচ শুরু করে।

এটি হলো 'thundering herd problem'। উচ্চ লোডের সময় একটি ক্যাশ মিস আপনার আপস্ট্রিম প্রোভাইডারের কাছে একই ধরণের কলের বন্যা বয়ে আনে। আপনি যদি GitHub-এর মতো রেট-লিমিটেড (rate-limited) API ব্যবহার করেন, তবে এটি মুহূর্তের মধ্যেই আপনার লিমিট শেষ করে দিতে পারে।

এর সমাধান হলো রিকোয়েস্ট কোয়ালসিং (request coalescing)।

আপনাকে সম্পন্ন হওয়া ক্যাশ এন্ট্রি থেকে আলাদাভাবে পেন্ডিং রিকোয়েস্টগুলো ট্র্যাক করতে হবে। আমি এটি ম্যানেজ করার জন্য একটি 'in-flight Map' ইমপ্লিমেন্ট করেছি:

  • যখন একটি রিকোয়েস্ট শুরু হয়, তখন তার Promise একটি Map-এ সংরক্ষণ করুন।
  • যদি একই কী (key)-এর জন্য দ্বিতীয় কোনো রিকোয়েস্ট আসে, তবে নতুন করে ফেচ শুরু করবেন না।
  • পরিবর্তে, Map থেকে বিদ্যমান Promise-টি রিটার্ন করুন।
  • রিকোয়েস্ট শেষ হয়ে গেলে, এটি Map থেকে সরিয়ে ফেলুন এবং ফলাফলটি ক্যাশে সেভ করুন।

এটি নিশ্চিত করে যে, একসাথে যত মানুষই একই ডেটা রিকোয়েস্ট করুক না কেন, শুধুমাত্র একটি কল API-তে হিট করবে।

এটি ঠিক করার সময়, আমি একই ফাইলের আরও তিনটি এজ-কেস (edge-case) বাগ সমাধান করেছি:

  • মিসিং টোকেন এরর (Missing Token Errors): সিস্টেম ক্রেডেনশিয়াল হিসেবে "undefined" পাঠাচ্ছিল। আমি রিকোয়েস্ট করার আগে টোকেন ভ্যালিডেট করার জন্য এটি আপডেট করেছি।
  • মেমরি লিক (Memory Leaks): রিট্রাই লজিক AbortSignals-এ পুরনো ইভেন্ট লিসেনার রেখে দিচ্ছিল। লিক প্রতিরোধ করতে আমি ক্লিনিং লজিক যোগ করেছি।
  • URL ইনজেকশন (URL Injection): স্পেশাল ক্যারেক্টারযুক্ত ইউজারনেমগুলো API পাথ ভেঙে দিচ্ছিল। URL স্ট্রাকচার সুরক্ষিত রাখতে আমি এনকোডিং যোগ করেছি।

শুধুমাত্র একটি ক্যাশ যথেষ্ট নয়। বর্তমানে চলমান (in flight) রিকোয়েস্টগুলোকে ডিডুপ্লিকেট (deduplicate) করাও প্রয়োজন।

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