جب آپ Node.js کے ساتھ کام کر رہے ہوں، تو شروع میں ایرر ہینڈلنگ (error handling) بہت آسان محسوس ہوتی ہے۔ آپ ایک روٹ (route) کو try-catch میں لپیٹتے ہیں، 500 اسٹیٹس کوڈ بھیجتے ہیں، اور کلائنٹ فیصلہ کرتا ہے کہ آگے کیا کرنا ہے۔ یہ ماڈل HTTP کے لیے تو ٹھیک کام کرتا ہے، لیکن جیسے ہی آپ بیک گراؤنڈ جابز (background jobs) کی طرف بڑھتے ہیں، یہ ناکام ہو جاتا ہے۔ ایک کیو سسٹم (queue system) میں، کوئی کلائنٹ انتظار نہیں کر رہا ہوتا۔ وہاں صرف ایک ورکر (worker)، ایک پے لوڈ (payload)، اور Redis، RabbitMQ، یا SQS میں کہیں بڑھتا ہوا ری ٹرائی کاؤنٹر (retry counter) ہوتا ہے۔ اگر آپ ناکامیوں کو اسی طرح ہینڈل کرتے ہیں جیسے آپ فیل شدہ ویب ریکویسٹس کو کرتے ہیں، تو آپ صرف ایک ٹرانزیکشن نہیں کھوئیں گے۔ بلکہ آپ اپنا پورا پائپ لائن (pipeline) روک دیں گے، کمپیوٹ ریسورسز ضائع کریں گے، یا ایک ہی زہریلے پیغام (poisoned message) پر اپنے ورکرز کو بار بار کریش کر دیں گے۔

بیک گراؤنڈ جابز میں HTTP کا طرزِ فکر کام نہیں کرتا

ریکویسٹ-رسپانس سائیکل (request-response cycle) میں، فیڈ بیک لوپ فوری ہوتا ہے۔ صارف ایک بٹن پر کلک کرتا ہے، سرور ایرر دیتا ہے، اور صارف کو فیل ہونے والی اسکرین نظر آتی ہے۔ صفائی ستھرائی (cleanup) آسان ہوتی ہے۔ ایک کیو ورکر تنہائی میں کام کرتا ہے۔ یہ ایک جاب لیتا ہے، اس پر سیکنڈوں یا منٹوں تک کام کرتا ہے، اور پھر کامیابی کی تصدیق (acknowledge) کرتا ہے۔ اگر درمیان میں کچھ غلط ہو جائے، تو کیو کو معلوم نہیں ہوتا کہ کیوں ہوا۔ اسے صرف یہ معلوم ہوتا ہے کہ تصدیق (acknowledgment) کبھی موصول نہیں ہوئی۔ آپ کی کنفیگریشن کے مطابق، یہ ری ٹرائی کرے گا، شاید ہمیشہ کے لیے۔ ایک غلط طریقے سے بنایا گیا پے لوڈ (malformed payload) سینکڑوں بار ورکرز کے درمیان ادھر ادھر ہو سکتا ہے، جس سے CPU ضائع ہوتا ہے اور وہ جائز جابز کے پیچھے چھپ جاتا ہے جنہیں اصل میں پروسیسنگ کی ضرورت ہوتی ہے۔

ناکامی کی دو اقسام

لچکدار کیوز (resilient queues) کا پہلا اصول یہ ہے کہ ہر ایرر کے ساتھ ایک جیسا سلوک کرنا بند کریں۔ آپ کو ناکامیوں کو فوری طور پر دو گروہوں میں تقسیم کرنے کی ضرورت ہے۔

ری ٹرائی ایبل (Retryable) ناکامیاں عارضی ہوتی ہیں۔ نیٹ ورک ٹائم آؤٹ، تھرڈ پارٹی API سے ریٹ لمٹس (rate limits)، یا ڈیٹا بیس کنکشن کا ری سیٹ ہونا (کیونکہ پول عارضی طور پر ختم ہو گیا تھا) کے بارے میں سوچیں۔ یہ دباؤ میں موجود ایک زندہ سسٹم کی علامات ہیں۔ یہ دو منٹ بعد اگلی کوشش میں کامیاب ہو سکتی ہیں۔

مستقل (Permanent) ناکامیاں 'پوائزن پِلز' (poison pills) ہوتی ہیں۔ ان میں غلط پے لوڈ، اسکیما ویلیڈیشن ایررز (schema validation errors)، یا کسی ضروری فیلڈ کا نہ ہونا شامل ہے کیونکہ کسی اپ اسٹریم سروس نے اپنا معاہدہ (contract) بدل لیا ہے۔ ان کو ری ٹرائی کرنا محض وقت کا ضیاع ہے۔ یہ سویں کوشش پر بھی بالکل اسی طرح فیل ہوں گی۔

اگر آپ کا catch block ان دونوں کے درمیان فرق نہیں کر سکتا، تو آپ کی کیو اندھیرے میں اڑ رہی ہے۔

پیٹرن 1: Catch Block پر ایررز کی درجہ بندی کریں

آپ کے ورکر کا catch block فائل کا سب سے سوچ سمجھ کر لکھا گیا کوڈ ہونا چاہیے۔ جب کوئی ایرر سامنے آئے، تو فوراً اس کا معائنہ کریں۔ کیا ایرر کوڈ ECONNRESET ہے یا ٹائم آؤٹ؟ اسے ری ٹرائی کے لیے کیو میں ڈال دیں۔ کیا یہ SyntaxError ہے، Joi ویلیڈیشن ریجیکشن ہے، یا کوئی مسنگ فارن کی (foreign key) کنسٹرینٹ ہے؟ اسے براہ راست ڈیڈ لیٹر کیو (dead-letter queue) یا DLQ میں منتقل کر دیں، اور اسے اپنی ری ٹرائی کی حد (retry limit) میں شمار نہ کریں۔

زیادہ تر Node.js کیو لائبریریز، بشمول BullMQ اور Bee Queue، آپ کو کسٹم بیک آف اسٹریٹجیز (backoff strategies) اور ایرر ہکس (error hooks) کی تعریف کرنے کی اجازت دیتی ہیں۔ انہیں استعمال کریں۔ ایک مستقل ایرر کو ڈیفالٹ کے طور پر کبھی بھی سونا اور تین بار ری ٹرائی نہیں کرنا چاہیے۔ اسے مین کیو سے نکال دینا چاہیے تاکہ آپ کی باقی جابز کا بہاؤ جاری رہے۔ DLQ بالکل وہی پے لوڈ اور ایرر کا سیاق و سباق (context) محفوظ رکھتا ہے، جو آپ کو بگ ٹھیک کرنے یا اسکیما درست کرنے کے بعد بعد میں جاب کو دوبارہ چلانے (replay) کی اجازت دیتا ہے۔

پیٹرن 2: Jitter کے ساتھ Exponential Backoff

فوری طور پر ری ٹرائی کرنا جارحانہ عمل ہے۔ اگر کوئی ڈاؤن اسٹریم ڈیٹا بیس پہلے ہی لوڈ کے نیچے تڑپ رہا ہے، تو پچاس ورکرز کی طرف سے ہر دو سیکنڈ بعد اسے ہٹ کرنا اسے مکمل طور پر ختم کر دے گا۔ آپ کو پیچھے ہٹنا ہوگا اور سسٹم کو سنبھلنے کا موقع دینا ہوگا۔

Exponential backoff استعمال کریں۔ پہلی ناکامی پر، ایک سیکنڈ انتظار کریں۔ دوسری پر، دو سیکنڈ۔ پھر چار، پھر آٹھ، یہاں تک کہ پانچ منٹ جیسی مناسب حد تک۔ لیکن صرف ٹائمنگ کافی نہیں ہے۔ اگر ہر فیل شدہ جاب بالکل ایک ہی وقفہ استعمال کرتی ہے، تو بیک آف ختم ہونے پر وہ سب آپس میں ٹکرا جائیں گی۔ وہ ہم آہنگ لہر (synchronized wave)، جسے کبھی کبھی 'تھنڈرنگ ہേراڈ' (thundering herd) کہا جاتا ہے، ایک بحال ہوتے ہوئے سروس کو مفلوج کر سکتی ہے۔

Jitter شامل کریں۔ اپنے حساب شدہ وقفے کو لیں اور اسے ایک رینڈم فیصد، شاید دس سے بیس فیصد تک تبدیل کر دیں۔ چار سیکنڈ 4.2 یا 4.7 بن جائیں گے۔ یہ سادہ سی بے ترتیبی ری ٹرائی کے جھٹکے (spike) کو پھیلا دیتی ہے اور آپ کے انفراسٹرکچر کو لہروں کی صورت میں ضرب لگنے سے بچاتی ہے۔

پیٹرن 3: Idempotency کے لیے ڈیزائن کریں

یہ وہ جگہ ہے جہاں کیو انفراسٹرکچر بزنس لاجک سے ملتا ہے۔ ایک ایسی جاب کا تصور کریں جو پیمنٹ پرووائیڈر کے ذریعے صارف سے چارج کرتی ہے۔ ورکر کامیابی سے چارج پوسٹ کر دیتا ہے، لیکن اس سے پہلے کہ وہ کامیابی کو آپ کے ڈیٹا بیس میں ریکارڈ کرے یا جاب کی تصدیق کرے، کنکشن ٹوٹ جاتا ہے۔ کیو اسے ناکامی سمجھتا ہے۔ یہ ری ٹرائی کرتا ہے۔ صارف سے دو بار چارج کر لیا جاتا ہے۔

Node.js میں، ہر side effect کو idempotent بنا کر اس سے بچیں۔ Job ID یا کسی کاروباری مخصوص شناختی نمبر (business-specific identifier) سے ایک idempotency key تیار کریں۔ چارج کرنے، ای میل بھیجنے یا انوینٹری (inventory) کو ایڈجسٹ کرنے سے پہلے، چیک کریں کہ آیا وہ کام پہلے ہی ہو چکا ہے۔ اس key کو اپنے ڈیٹا بیس اور ان تمام third-party APIs تک پہنچائیں جو اسے قبول کرتے ہیں۔ اپنے jobs کو اس طرح ترتیب دیں کہ ایک ہی payload کو دس بار چلانے سے وہی نتیجہ نکلے جو اسے ایک بار چلانے سے نکلتا ہے۔ یہ ایک عادت مالیاتی اور ڈیٹا کی سالمیت (data-integrity) سے متعلق تمام بگز (bugs) کے ایک پورے زمرے کو ختم کر دیتی ہے۔

پیٹرن 4: اپنی Dead-Letter Queue کو ایک ڈیش بورڈ کی طرح سمجھیں

DLQ کوئی قبرستان نہیں ہے جہاں خراب jobs کو بھول جانے کے لیے چھوڑ دیا جائے۔ یہ ایک آپریشنل ٹول ہے، اور اسے آپ کی سب سے زیادہ نگرانی کی جانے والی سطحوں (surfaces) میں سے ایک ہونا چاہیے۔

ایسے الرٹس (alerts) سیٹ کریں جو DLQ کی گہرائی (depth) بڑھنے پر متحرک ہوں۔ DLQ میں ایک پیغام کا ہونا بھی اکثر اس بات کی علامت ہوتا ہے کہ آپ کا validation logic خراب ہو گیا ہے، کوئی upstream schema تبدیل ہو گیا ہے، یا کوئی downstream service ایسا کچرا (garbage) بھیج رہی ہے جسے آپ اب پہچان نہیں سکتے۔ یہ بالکل وہی سگنلز ہیں جنہیں آپ صارفین کے شکایت کرنے سے پہلے پکڑنا چاہتے ہیں۔ ایک ایسا ڈیش بورڈ بنائیں جو آپ کو raw payload، stack trace، اور timestamp کا معائنہ کرنے کی اجازت دے۔ ایک runbook تیار رکھیں: ناکامی کا معائنہ کریں، کوڈ کو پیچ (patch) کریں، اور پھر پیغامات کو درست ترتیب میں دوبارہ چلائیں (replay)۔ اگر آپ کی queue implementation اس کی اجازت دیتی ہے، تو مطلق تعداد (absolute count) کے ساتھ ساتھ بڑھنے کی شرح (growth rate) پر بھی الرٹ سیٹ کریں، کیونکہ ایک غلط ڈیپلائمنٹ (deploy) منٹوں میں DLQ کو بھر سکتا ہے۔

پیٹرن 5: جب شک ہو، تو پروسیس کو کریش (crash) کر دیں

Node.js ایک V8 isolate کے اندر ایک سنگل ایونٹ لوپ (event loop) پر چلتا ہے۔ ایک ان ہینڈلڈ پرامیس ریجیکشن (unhandled promise rejection) یا ایک