একজন Magento ডেভেলপার ৩০০টি আলাদা ProductRepositoryInterface::getById() কলকে একটি মাত্র collection query দিয়ে পরিবর্তন করেছেন। এর ফলে ডাটাবেস হিট ৩০০ থেকে কমে ১-এ নেমে আসে এবং ক্যাটাগরি পেজটি উল্লেখযোগ্যভাবে দ্রুত লোড হতে শুরু করে। কিন্তু এর বিনিময়ে কী হলো? স্টোরফ্রন্ট থেকে কিছু প্রোডাক্ট গায়েব হয়ে গেল, যা ক্রেতা এবং মালিক উভয়কেই বিভ্রান্ত করল।
Magento ক্যাটাগরি পেজে N+1 সমস্যা
পেজটিতে ৩০০টি প্রোডাক্ট তালিকাভুক্ত ছিল। প্রতিটি প্রোডাক্টের জন্য কোডটি repository-র getById() মেথড ব্যবহার করে দুটি কাস্টম অ্যাট্রিবিউট (custom attributes) সংগ্রহ করছিল, যার ফলে প্রতিটি প্রোডাক্টের জন্য একটি করে কুয়েরি তৈরি হচ্ছিল—এটিই হলো ক্লাসিক N+1 প্যাটার্ন। প্রতিটি প্রোডাক্ট আলাদাভাবে লোড করার পরিবর্তে একটি collection ব্যবহার করে এক কুয়েরিতেই প্রয়োজনীয় সব রো (rows) নিয়ে আসার ফলে কুয়েরির সংখ্যা অনেক কমে যায়। ফ্রন্ট-এন্ড ল্যাটেন্সি (latency) উন্নত হলেও, প্রোডাক্ট লিস্ট থেকে এমন কিছু আইটেম বাদ পড়ে গেল যা আগে দেখা যেত।
কেন collection স্টকে নেই এমন আইটেমগুলো লুকিয়ে ফেলে
Magento-র CatalogInventory মডিউল ফ্রন্টএন্ডের জন্য তৈরি যেকোনো product collection-এ স্বয়ংক্রিয়ভাবে একটি “in-stock” ফিল্টার যোগ করে দেয়। যখন স্টোর সেটিংসে Display Out of Stock Products অপশনটি বন্ধ থাকে, তখন এই ফিল্টারটি সক্রিয় হয়; যেসব মার্চেন্ট স্টোরে নেই এমন আইটেম দেখাতে চান না, তারা সাধারণত এটি ব্যবহার করেন।
এর বিপরীতে, repository কখনোই কোনো স্টক ফিল্টার প্রয়োগ করে না। এটি কেবল দেখে যে প্রোডাক্টটি বিদ্যমান কি না এবং ইনভেন্টরি লেভেল যাই হোক না কেন, সেটি রিটার্ন করে। যখন কোডটি repository কল থেকে collection-এ পরিবর্তিত হলো, তখন স্টক ফিল্টারটি নিঃশব্দে সেই সমস্ত প্রোডাক্ট সরিয়ে ফেলল যেগুলোর পরিমাণ (quantity) ছিল শূন্য। কোনো এরর লগ (error log) তৈরি হয়নি; collection টি ডেভেলপার যা আশা করেছিলেন তার চেয়ে কম সংখ্যক রো রিটার্ন করেছিল।
মার্চেন্টদের জন্য এর অর্থ কী
একটি স্টোরফ্রন্ট যা নিঃশব্দে আউট-অফ-স্টক SKU গুলো বাদ দিয়ে দেয়, তা বেশ কিছু সমস্যা তৈরি করে:
- ফ্রন্ট-এন্ড কম্পোনেন্টগুলো মিসিং এন্ট্রি পায়, যার ফলে খালি লেবেল বা ভুল প্রাইসিং দেখা দিতে পারে।
- ডিবাগিং করা কঠিন হয়ে পড়ে কারণ এই লক্ষণটি—প্রোডাক্ট হারিয়ে যাওয়া—কোনো ওয়ার্নিং (warning) তৈরি করে না।
যেহেতু বেশিরভাগ অটোমেটেড টেস্টে ইন-স্টক ফিক্সচার ব্যবহার করা হয়, তাই সাইটটি রিয়েল ইনভেন্টরির সাথে চলার আগ পর্যন্ত এই বাগটি প্রায়ই লুকিয়ে থাকে।
ইনভেন্টরি লুকিয়ে না ফেলে কীভাবে গতি বজায় রাখা যায়
- ডিফল্ট স্টক ফিল্টার এড়িয়ে চলুন – যদি আপনি প্রাপ্যতা নির্বিশেষে প্রতিটি প্রোডাক্ট পেতে চান, তবে collection থেকে স্পষ্টভাবে ফিল্টারটি সরিয়ে ফেলুন। Magento প্রতিটি কুয়েরির জন্য স্টক প্লাগইন ডিজেবল বা রিপ্লেস করার মেথড প্রদান করে।
- নির্ভুলতার প্রয়োজন হলে repository ব্যবহার করুন – repository নিশ্চিত করে যে অনুরোধ করা প্রতিটি প্রোডাক্ট আইডি রিটার্ন করা হবে, এমনকি সেটি স্টকে না থাকলেও। অতিরিক্ত কুয়েরি নিয়ন্ত্রণে রাখতে ফলাফলগুলো ক্যাশ (cache) করুন অথবা শুধুমাত্র প্রয়োজনীয় আইডিগুলো লোড করুন।
- রিসোর্স মডেল (resource model) বা raw SQL ব্যবহার করুন – প্রয়োজনীয় অ্যাট্রিবিউটের জন্য সরাসরি প্রোডাক্ট টেবিল কুয়েরি করুন। এটি সমস্ত collection প্লাগইনকে বাইপাস করে এবং কোন কোন রো অন্তর্ভুক্ত করা হবে তার ওপর পূর্ণ নিয়ন্ত্রণ দেয়।
গতি বনাম নির্ভুলতা
একটি নয়েজি (noisy) N+1 লুপকে একটি সিঙ্গেল collection query দিয়ে পরিবর্তন করা স্বাভাবিক মনে হতে পারে; ল্যাটেন্সি কমার বিষয়টিও স্পষ্ট। তবুও, একটি দ্রুত কুয়েরি যা ভুল প্রোডাক্টের সেট রিটার্ন করে, তা অপ্টিমাইজেশন নয় বরং একটি ত্রুটি (defect)। ডেভেলপারদের অবশ্যই কাঙ্ক্ষিত গতির বিপরীতে একটি অসম্পূর্ণ ক্যাটালগ দেখানোর ঝুঁকি বিবেচনা করতে হবে।
পরবর্তীতে যা খেয়াল রাখতে হবে
- কনফিগারেশন ড্রিফট (Configuration drift) – নিশ্চিত করুন যে Display Out of Stock Products ফ্ল্যাগটি আপনার কাস্টম collection কোডের কাঙ্ক্ষিত আচরণের সাথে সামঞ্জস্যপূর্ণ কি না।
- প্লাগইন সাইড ইফেক্ট (Plugin side effects) – অন্যান্য মডিউল collection-এ নিজস্ব ফিল্টার যোগ করতে পারে। যখনই আপনি কুয়েরি স্ট্র্যাটেজি পরিবর্তন করবেন, প্লাগইন স্ট্যাকটি রিভিউ করুন।
শিক্ষাটি স্পষ্ট: একটি collection দিয়ে N+1 সমস্যা সমাধান করা তখনই সফল হবে যদি আপনি collection-এর ডিফল্ট ফিল্টারগুলোও অডিট করেন। Magento-র বিল্ট-ইন স্টক ফিল্টার উপেক্ষা করলে তা নিঃশব্দে আপনার ক্যাটালগ কমিয়ে দিতে পারে, যা পারফরম্যান্সের উন্নতিকে রাজস্বের (revenue) ক্ষতির দিকে ঠেলে দিতে পারে।
