زیادہ تر Node.js ٹیوٹوریلز میں ایرر ہینڈلنگ (error handling) کو ایک ضمنی چیز سمجھا جاتا ہے۔ آپ ایک روٹ ہینڈلر کو try/catch بلاک میں لپیٹتے ہیں، اسٹیک ٹریس (stack trace) لاگ کرتے ہیں، اور 500 ایرر واپس کر دیتے ہیں۔ یہ سوچ اس لیے برقرار رہتی ہے کیونکہ HTTP ریکویسٹ کے دوسرے سرے پر ایک حقیقی انسان انتظار کر رہا ہوتا ہے۔ بیک گراؤنڈ جابز (Background jobs) مختلف ہوتی ہیں۔ ایک کیو سسٹم (queue system) میں، جواب دینے کے لیے کوئی بے صبر کلائنٹ نہیں ہوتا، اور نہ ہی براؤزر خود بخود ریفریش ہوتا ہے۔ وہاں صرف ایک ورکر (worker)، ایک پے لوڈ (payload)، اور خاموشی سے بڑھتا ہوا ری ٹرائی کاؤنٹر (retry counter) ہوتا ہے۔ جب چیزیں غلط ہوتی ہیں، تو وہ پہلے آہستہ آہستہ، پھر اچانک سب کچھ ایک ساتھ غلط ہو جاتا ہے۔ ایک غلط درجہ بندی شدہ ایرر پورے پائپ لائن کو روک سکتا ہے یا رات کے تین بجے کسی انجینئر کو جگا سکتا ہے۔

فرق سادہ ہے۔ ریکویسٹ-رسپانس سائیکلز (Request-response cycles) تیزی سے اور واضح طور پر فیل ہوتے ہیں۔ کیو کی ناکامی خاموش ہوتی ہے۔ ڈیٹا بیس کنکشن ٹوٹنے سے پہلے ایک ورکر سینکڑوں جابز مکمل کر سکتا ہے۔ واضح ہینڈلنگ رولز کے بغیر، ورکر فوری طور پر ری ٹرائی کرتا ہے، پہلے سے ہی جدوجہد کر رہے ڈیٹا بیس پر دباؤ ڈالتا ہے، اور کریش ہو جاتا ہے۔ چونکہ کوئی بھی ورکر کو براہ راست نہیں دیکھ رہا ہوتا، اس لیے پریشانی کی پہلی علامت اکثر کیسکیڈنگ بیک اپ (cascading backup) یا لاگ فائلوں سے بھری ہوئی ڈسک ہوتی ہے۔ آپ کو صرف catch بلاکس سے زیادہ کی ضرورت ہے۔ آپ کو ایک ایسی حکمت عملی کی ضرورت ہے جو مختلف ناکامیوں کے ساتھ مختلف طریقے سے پیش آئے اور ایک خراب جاب سے پورے سسٹم کو محفوظ رکھے۔

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

ہر ایرر کو دو حصوں میں تقسیم کر کے آغاز کریں۔

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

مستقل (Permanent) ایررز غلطیاں ہیں۔ پے لوڈ میں غلط JSON، مسنگ یوزر آئی ڈی، یا اسٹوریج میں موجود نہ ہونے والی کوئی ضروری فائل۔ یہ سویں کوشش پر بھی بالکل اسی طرح فیل ہوں گے جیسے پہلی بار ہوئے تھے۔ انہیں ری ٹرائی کرنا CPU سائیکلز ضائع کرتا ہے، کیو سلاٹس (queue slots) کو ضائع کرتا ہے، اور ایک زہریلا بیک پریشر (back-pressure) پیدا کرتا ہے جو صحت مند جابز میں تاخیر کا باعث بنتا ہے۔ مستقل ناکامی کے لیے واحد مفید جگہ لاگ، الرٹ، یا ڈیڈ لیٹر کیو (dead-letter queue) ہے۔ یہ ری ٹرائی لوپ کا حصہ نہیں ہونی چاہیے۔

ایک فیصلہ کرنے والا انجن (Decision Engine) بنائیں

فوری طور پر درجہ بندی کریں۔ یہ فیصلہ کیو فریم ورک (queue framework) پر نہ چھوڑیں۔ جیسے ہی آپ کوئی ایرر پکڑیں، اس کی قسمت کا فیصلہ کریں۔

عملی طور پر، اس کا مطلب ہے کہ کسٹم ایرر کلاسز یا ریپر فنکشنز (wrapper functions) بنانا جو ایرر کو اوپر بھیجنے سے پہلے اس کی جانچ کریں۔ اگر ڈیٹا بیس ڈرائیور کنکشن ری سیٹ (connection reset) کا ایرر دے، تو آپ کے ہینڈلر کو اسے ری ٹرائی ایبل کے طور پر ٹیگ کرنا چاہیے۔ اگر پے لوڈ ویلیڈیٹر اسکیمہ مِس میچ (schema mismatch) کا ایرر دے، تو اسے مستقل کے طور پر ٹیگ کریں۔ بہت سے جاب پروسیسرز ڈیفالٹ کے طور پر ہر چیز کو ری ٹرائی کرتے ہیں، جو کہ آپ کا سب سے مہنگا فیصلہ ہو سکتا ہے۔ مستقل جابز کو فوری طور پر مسترد کر دیں۔ یا تو انہیں چھوڑ دیں یا انہیں ڈیڈ لیٹر کیو (dead-letter queue) میں ری روٹ کر دیں جہاں وہ مین پائپ لائن کو خراب نہ کر سکیں۔ یہ ایک عادت کسی بھی انفراسٹرکچر کی تبدیلی کے مقابلے میں زیادہ قابل اعتماد طریقے سے اسنو بال ایفیکٹس (snowball effects) کو روکتی ہے۔

پیچھے ہٹیں، لیکن بہتر طریقے سے

جب آپ ری ٹرائی کریں، تو اسے کبھی بھی فوری طور پر نہ کریں۔ اگر ڈیٹا بیس ڈاؤن ہے، تو ہر سیکنڈ میں اس پر حملہ کرنے والے ورکرز کا ایک ہجوم اندرونی طور پر ڈینائل آف سروس (denial-of-service) حملے جیسا لگتا ہے۔ ایکسپونینشل بیک آف (exponential backoff) کا استعمال کریں۔ ایک منٹ انتظار کریں، پھر پانچ، پھر پندرہ۔ اپ اسٹریم سسٹم (upstream system) کو ریکور ہونے کا موقع دیں۔

لیکن صرف ایکسپونینشل بیک آف کافی نہیں ہے۔ اگر کسی سروس کے ری اسٹارٹ ہونے کی وجہ سے ایک ہزار جابز ایک ہی وقت میں فیل ہو جاتی ہیں، تو ان کے ری ٹرائی شیڈول ایک جیسے ہو جائیں گے۔ جب وہ سروس دوبارہ آن لائن آئے گی، تو وہ سب ایک ساتھ اس پر حملہ کریں گے، جس سے وہ دوبارہ ڈاؤن ہو سکتی ہے۔ جیٹر (jitter) شامل کریں: ہر تاخیر میں ایک چھوٹا سا رینڈم آفسیٹ (random offset)۔ ری ٹرائیز کو چند سیکنڈوں میں پھیلا دینے سے سنکرونائزڈ اسٹیمپیز (synchronized stampedes) سے بچا جا سکتا ہے۔ ریاضی سادہ ہے، لیکن اس سے حاصل ہونے والا استحکام بہت زیادہ ہے۔

ثبوت محفوظ رکھیں

ڈیڈ لیٹر کیو (dead-letter queue) آپ کا آڈٹ ٹریل (audit trail) ہے، کچرا دان نہیں۔ جب کوئی جاب اپنی آخری ری ٹرائی مکمل کر لے، تو اسے صرف ڈیلیٹ نہ کریں۔ پورے پے لوڈ کو، ایرر کانٹیکسٹ (error context) اور ری ٹرائی کی ہسٹری کے ساتھ، DLQ میں منتقل کر دیں۔

یہ ثبوت محفوظ رکھتا ہے۔ ایک انسان جاب کا معائنہ کر سکتا ہے، بگ کو ٹھیک کر سکتا ہے، اور ضرورت پڑنے پر اسے دستی طور پر دوبارہ چلا سکتا ہے۔ اس سے بھی اہم بات یہ ہے کہ اپنی DLQ کی گہرائی (depth) کی نگرانی کریں۔ ڈیڈ لیٹر جابز میں اچانک اضافہ اکثر کسی خراب ڈیپلائمنٹ (deploy)، غلط اسکیمہ تبدیلی، یا کسی بیرونی وینڈر کے معاہدے کی خلاف ورزی کی ابتدائی وارننگ ہوتا ہے۔ DLQ کے بڑھنے کو ایک 'لیڈنگ انڈیکیٹر' (leading indicator) کے طور پر دیکھیں، نہ کہ 'ٹریلنگ انڈیکیٹر' (trailing indicator) کے طور پر۔ اگر آپ کی DLQ بھر رہی ہے، تو اس کا مطلب ہے کہ اپ اسٹریم میں کچھ بدلا ہے اور آپ کی ٹیم کو بیک لاگ پھیلنے سے پہلے اس کا علم ہونا چاہیے۔

ری ٹرائی کے لیے ڈیزائن کریں

ہر جاب کو اس طرح ڈیزائن کریں جیسے کہ وہ دو بار چلے گی، کیونکہ ایسا ہو سکتا ہے۔ ایک ورکر پروسیسنگ کے دوران آدھے راستے میں ناکام ہو سکتا ہے، دوبارہ شیڈول ہو سکتا ہے، اور دوبارہ چل سکتا ہے۔ اگر آپ کی جاب کسی صارف سے چارج کرتی ہے، ای میل بھیجتی ہے، یا انوینٹری کاؤنٹ میں اضافہ کرتی ہے، تو ایک سادہ ری ٹرائی (retry) ڈپلیکیٹس پیدا کر دے گی۔

اس کا حل idempotency ہے۔ کوئی بھی سائیڈ ایفیکٹ (side effect) کرنے سے پہلے، یہ چیک کریں کہ آیا وہ پہلے ہی ہو چکا ہے۔ جاب پے لوڈ (job payload) سے ایک منفرد شناختی نمبر (unique identifier) کو idempotency key کے طور پر استعمال کریں۔ اس کی (key) کو کسی مختصر مدت کے کیش (cache) یا یونیکنس کنسٹرینٹ (uniqueness constraint) والے ڈیٹا بیس ٹیبل میں محفوظ کریں۔ اگر وہ کی (key) موجود ہے، تو کام کو چھوڑ دیں اور کامیابی (success) کا نتیجہ واپس کریں۔ یہ ری ٹرائیز کو ایک خطرے سے بدل کر ایک بے ضرر no-op بنا دیتا ہے۔ اس میں کوڈ کی چند اضافی لائنیں لگتی ہیں، لیکن یہ آپ کو فنانس ڈیپارٹمنٹ کو یہ سمجھانے سے بچاتا ہے کہ راتوں رات آمدنی دوگنی کیوں ہو گئی۔

عمل کا تحفظ کریں

غیر محدود promise rejections اور stray exceptions بغیر کسی وارننگ کے Node.js پروسیس کو ختم کر سکتے ہیں۔ ایک ورکر میں، اس کا مطلب ہے کہ جابز ضائع ہو گئیں اور ایک آرکیسٹریٹر (orchestrator) کنٹینر کو دوبارہ شروع کرنے کے لیے تگ و دو کر رہا ہے۔

unhandledRejection اور uncaughtException کے لیے گلوبل ہینڈلرز (global handlers) رجسٹر کریں۔ ان کا کام ایپلی کیشن کو بچانا نہیں ہے۔ ان کا کام صرف ضروری کم سے کم صفائی (cleanup) کرنا اور پھر باہر نکلنا (exit) ہے۔ Docker، Kubernetes، یا systemd کو ایک صاف میموری اسٹیٹ کے ساتھ ورکر کو دوبارہ شروع کرنے دیں۔ گلوبل ہینڈلر چلنے کے بعد لڑکھڑاتے ہوئے چلتے رہنا میموری لیکس (memory leaks) اور خراب شدہ اسٹیٹ (corrupted state) کو دعوت دیتا ہے۔ ایک سست زومبی پروسیس (zombie process) جو کاموں کو غلط طریقے سے پروسیس کرتا ہے، اس کے مقابلے میں ایک تیز اور صاف خاتمہ زیادہ محفوظ ہے۔ اپنے آرکیسٹریٹر پر بھروسہ کریں کہ وہ آپ کو واپس لے آئے گا؛ خراب شدہ رن ٹائم (runtime) کو دھوکہ دینے کی کوشش نہ کریں۔

سگنل کا احترام کریں

ورکرز کو ڈیپلائمنٹس (deployments)، اسکیلنگ ایونٹس (scaling events)، اور نوڈ روٹیشنز (node rotations) کے دوران بند کر دیا جاتا ہے۔ اگر آپ کا پروسیس SIGTERM وصول کرتے ہی ختم ہو جاتا ہے، تو آپ اس کام کو منسوخ کر دیتے ہیں جو اس وقت جاری (in flight) ہے۔ وہ کام شاید کبھی مکمل نہ ہو، اور اس کا ری ٹرائی کاؤنٹر (retry counter) شاید ابھی تک بڑھا بھی نہ ہو۔

SIGTERM اور SIGINT کے لیے انتظار کریں۔ جب کوئی سگنل آئے، تو کیو (queue) سے نئے کام لینا بند کر دیں۔ اگر آپ کر سکیں تو موجودہ کام کو مکمل کریں۔ ایک سخت ٹائم آؤٹ (hard timeout) مقرر کریں، شاید تیس سیکنڈ، جس کے بعد آپ ہر صورت میں باہر نکل جائیں۔ یہ 'graceful shutdown' کیو کا احترام کرتا ہے اور غلط ناکامیوں سے بچاتا ہے۔ آپ کے ڈیپلائمنٹ پائپ لائن کو صاف طریقے سے باہر نکلنے والے ورکر کو 'healthy' سمجھنا چاہیے، جبکہ کریش ہونے والے ورکر کو الرٹ (alert) جاری کرنا چاہیے۔

اصل حاصلِ کلام

قابل اعتماد کیو ہینڈلنگ (queue handling) کا مطلب ہر غلطی کو پکڑنا نہیں ہے۔ یہ ہر ناکامی کے طریقے (failure mode) کے لیے سوچ سمجھ کر فیصلے کرنے کے بارے میں ہے۔ عارضی (transient) غلطیوں کو صبر کے ساتھ دوبارہ آزمائیں۔ مستقل (permanent) غلطیوں کو جلدی ختم کریں۔ اپنے ورکرز کو اسٹیمپیز (stampedes) سے بچائیں، اپنے ڈیٹا کو idempotency keys کے ذریعے محفوظ کریں، اور مرتے ہوئے پروسیسز کو صاف طریقے سے ختم ہونے دیں۔ جب ہر ناکامی کا ایک متعین راستہ ہو، تو رات کے تین بجے بھی محض ایک عام سا گھنٹہ بن جاتا ہے۔ آپ کا پائپ لائن چلتا رہتا ہے، اور آپ کی ٹیم سوتی رہتی ہے۔