మీరు Node.js తో డెవలప్ చేస్తున్నప్పుడు, మొదట్లో ఎర్రర్ హ్యాండ్లింగ్ చాలా సులభంగా అనిపిస్తుంది. ఒక రూట్ను try-catch లో ఉంచి, 500 స్టేటస్ కోడ్ను పంపితే సరిపోతుంది, ఆ తర్వాత క్లయింట్ ఏం చేయాలో నిర్ణయించుకుంటుంది. ఈ మోడల్ HTTP కోసం బాగా పనిచేస్తుంది, కానీ మీరు బ్యాక్గ్రౌండ్ జాబ్స్ (background jobs) వైపు మళ్ళిన వెంటనే ఇది విఫలమవుతుంది. క్యూ సిస్టమ్లో, వేచి ఉండే క్లయింట్ ఉండడు. అక్కడ కేవలం ఒక వర్కర్, ఒక పేలోడ్ (payload), మరియు Redis, RabbitMQ లేదా SQS లో ఎక్కడో పెరుగుతున్న రీట్రై కౌంటర్ మాత్రమే ఉంటాయి. మీరు వెబ్ రిక్వెస్ట్ల వైఫల్యాలను హ్యాండిల్ చేసే విధానంలోనే ఈ వైఫల్యాలను కూడా హ్యాండిల్ చేస్తే, మీరు కేవలం ఒక ట్రాన్సాక్షన్ను మాత్రమే కోల్పోరు. మీ మొత్తం పైప్లైన్ నిలిచిపోవచ్చు, కంప్యూట్ వనరులు వృథా కావచ్చు, లేదా ఒకే పాజిన్డ్ మెసేజ్ (poisoned message) వల్ల మీ వర్కర్లు పదేపదే క్రాష్ కావచ్చు.
బ్యాక్గ్రౌండ్ జాబ్స్లో HTTP మైండ్సెట్ విఫలమవుతుంది
రిక్వెస్ట్-రెస్పాన్స్ సైకిల్లో, ఫీడ్బ్యాక్ లూప్ తక్షణమే ఉంటుంది. ఒక యూజర్ బటన్ను క్లిక్ చేస్తారు, సర్వర్ ఎర్రర్ను ఇస్తుంది, మరియు యూజర్ వెంటనే ఫెయిల్యూర్ స్క్రీన్ను చూస్తారు. ఇక్కడ క్లీనప్ చేయడం సులభం. కానీ ఒక క్యూ వర్కర్ ఒంటరిగా (isolation) పనిచేస్తుంది. అది ఒక జాబ్ను తీసుకుంటుంది, కొన్ని సెకన్ల లేదా నిమిషాల పాటు దానిపై పని చేస్తుంది, ఆపై విజయాన్ని తెలియజేస్తుంది (acknowledgment). మధ్యలో ఏదైనా తప్పు జరిగితే, క్యూకి అది ఎందుకు జరిగిందో తెలియదు. కేవలం అక్నాలెడ్జ్మెంట్ అందలేదని మాత్రమే దానికి తెలుస్తుంది. మీ కాన్ఫిగరేషన్ను బట్టి, అది మళ్ళీ మళ్ళీ రీట్రై చేస్తూనే ఉండవచ్చు. ఒకే ఒక తప్పుగా ఉన్న పేలోడ్ (malformed payload) వందల సార్లు వర్కర్ల మధ్య తిరుగుతూనే ఉంటుంది, దీనివల్ల CPU వృథా అవ్వడమే కాకుండా, నిజంగా ప్రాసెస్ కావాల్సిన ఇతర జాబ్స్ వెనుక ఇది దాగిపోతుంది.
రెండు రకాల వైఫల్యాలు
రెసిలియంట్ క్యూల (resilient queues) యొక్క మొదటి నియమం ఏమిటంటే, ప్రతి ఎర్రర్ను ఒకేలా చూడటం ఆపడం. వైఫల్యాలు సంభవించిన వెంటనే వాటిని రెండు గ్రూపులుగా విభజించాలి.
రీట్రై చేయదగిన వైఫల్యాలు (Retryable failures) తాత్కాలికమైనవి. నెట్వర్క్ టైమ్ అవుట్స్, థర్డ్-పార్టీ API నుండి వచ్చే రేట్ లిమిట్స్, లేదా డేటాబేస్ కనెక్షన్ పోవడం వంటివి వీటి కిందకు వస్తాయి. ఇవి ఒత్తిడిలో ఉన్న సిస్టమ్ యొక్క లక్షణాలు మాత్రమే. రెండు నిమిషాల తర్వాత చేసే తదుపరి ప్రయత్నంలో ఇవి విజయవంతం కావచ్చు.
శాశ్వత వైఫల్యాలు (Permanent failures) 'పాయిజన్ పిల్స్' (poison pills) వంటివి. తప్పుగా ఉన్న పేలోడ్లు, స్కీమా వాలిడేషన్ ఎర్రర్స్, లేదా అప్స్ట్రీమ్ సర్వీస్ తన కాంట్రాక్ట్ను మార్చడం వల్ల ఏదైనా అవసరమైన ఫీల్డ్ మిస్ అవ్వడం వంటివి వీటి కిందకు వస్తాయి. వీటిని మళ్ళీ మళ్ళీ రీట్రై చేయడం వల్ల కేవలం వృథా తప్ప ఏమీ జరగదు. వందవ ప్రయత్నంలో కూడా ఇవి అదే విధంగా విఫలమవుతాయి.
మీ catch బ్లాక్ ఈ రెండింటి మధ్య తేడాను గుర్తించలేకపోతే, మీ క్యూ చీకట్లో ప్రయాణిస్తున్నట్లే.
ప్యాటర్న్ 1: Catch బ్లాక్ వద్ద ఎర్రర్లను వర్గీకరించండి
మీ వర్కర్ యొక్క catch బ్లాక్ ఆ ఫైల్లో అత్యంత జాగ్రత్తగా రాసిన కోడ్గా ఉండాలి. ఎర్రర్ వచ్చినప్పుడు, దానిని వెంటనే పరిశీలించండి. ఎర్రర్ కోడ్ ECONNRESET లేదా టైమ్ అవుట్ అయిందా? అయితే దానిని రీట్రై కోసం క్యూ చేయండి. అది SyntaxError, Joi వాలిడేషన్ రిజెక్షన్, లేదా మిస్సింగ్ ఫారిన్ కీ కన్స్ట్రైంట్ (missing foreign key constraint) అయితే? దానిని నేరుగా డెడ్-లెటర్ క్యూ (dead-letter queue, లేదా DLQ) కి పంపండి మరియు దానిని మీ రీట్రై లిమిట్తో లెక్కించకండి.
BullMQ మరియు Bee Queue వంటి చాలా Node.js క్యూ లైబ్రరీలు మీకు కస్టమ్ బ్యాకoff స్ట్రాటజీలు మరియు ఎర్రర్ హుక్స్ (error hooks) నిర్వచించుకునే సౌలభ్యాన్ని ఇస్తాయి. వాటిని ఉపయోగించండి. ఒక శాశ్వత ఎర్రర్ను డిఫాల్ట్గా మూడుసార్లు రీట్రై చేయకూడదు. మిగిలిన జాబ్స్ సజావుగా సాగడానికి దానిని మెయిన్ క్యూ నుండి తొలగించాలి. DLQ ఖచ్చితమైన పేలోడ్ మరియు ఎర్రర్ కాంటెక్స్ట్ను భద్రపరుస్తుంది, దీనివల్ల మీరు బగ్ను సరిదిద్దిన తర్వాత లేదా స్కీమాను మార్చిన తర్వాత ఆ జాబ్ను మళ్ళీ రన్ చేయవచ్చు.
ప్యాటర్న్ 2: జిట్టర్తో ఎక్స్పోనెన్షియల్ బ్యాకoff (Exponential Backoff with Jitter)
వెంటనే రీట్రై చేయడం అనేది చాలా దూకుడుగా (aggressive) ఉంటుంది. ఒకవేళ డౌన్స్ట్రీమ్ డేటాబేస్ ఇప్పటికే లోడ్తో ఇబ్బంది పడుతుంటే, యాభై మంది వర్కర్లు ప్రతి రెండు సెకన్లకు దానిని మళ్ళీ హిట్ చేస్తే అది పూర్తిగా దెబ్బతింటుంది. మీరు కొంచెం విరామం ఇచ్చి, సిస్టమ్ కోలుకోవడానికి సమయం ఇవ్వాలి.
ఎక్స్పోనెన్షియల్ బ్యాకoff ఉపయోగించండి. మొదటి వైఫల్యంపై ఒక సెకను వేచి ఉండండి. రెండవ దానిపై రెండు సెకన్లు, తర్వాత నాలుగు, తర్వాత ఎనిమిది... ఇలా ఐదు నిమిషాల వంటి సహేతుకమైన గరిష్ట పరిమితి వరకు పెంచుకుంటూ వెళ్ళండి. కానీ కేవలం టైమింగ్ మాత్రమే సరిపోదు. ప్రతి ఫెయిల్ అయిన జాబ్ ఒకే రకమైన ఇంటర్వల్ను ఉపయోగిస్తే, బ్యాకoff ముగిసినప్పుడు అవన్నీ ఒకేసారి సిస్టమ్పై దాడి చేస్తాయి. ఈ సమకాలీకరించబడిన అలలను (synchronized wave), కొన్నిసార్లు 'థండరింగ్ హెర్డ్' (thundering herd) అని పిలుస్తారు, ఇవి కోలుకుంటున్న సర్వీస్ను మళ్ళీ కుప్పకూల్చగలవు.
దీనికి జిట్టర్ను (jitter) జోడించండి. మీరు లెక్కించిన డిలే (delay) కి ఒక రాండమ్ శాతాన్ని (బహుశా పది నుండి ఇరవై శాతం) జోడించండి. అంటే నాలుగు సెకన్లు అనేది 4.2 లేదా 4.7 సెకన్లు అవుతుంది. ఈ చిన్న రాండమ్ మార్పు రీట్రై స్పైక్ను తగ్గిస్తుంది మరియు మీ ఇన్ఫ్రాస్ట్రక్చర్ పదేపదే దెబ్బతినకుండా కాపాడుతుంది.
ప్యాటర్న్ 3: ఐడెంపోటెన్సీ (Idempotency) కోసం డిజైన్ చేయండి
ఇక్కడే క్యూ ఇన్ఫ్రాస్ట్రక్చర్ మరియు బిజినెస్ లాజిక్ కలుస్తాయి. ఒక పేమెంట్ ప్రొవైడర్ ద్వారా కస్టమర్కు ఛార్జ్ చేసే జాబ్ను ఊహించుకోండి. వర్కర్ విజయవంతంగా ఛార్జ్ చేస్తుంది, కానీ మీ డేటాబేస్లో ఆ విజయాన్ని రికార్డ్ చేసేలోపు లేదా జాబ్ను అక్నాలెడ్జ్ చేసేలోపు కనెక్షన్ కట్ అయిపోతుంది. అప్పుడు క్యూ వైఫల్యం జరిగినట్లు భావిస్తుంది. అది మళ్ళీ రీట్రై చేస్తుంది. ఫలితంగా కస్టమర్ నుండి రెండుసార్లు డబ్బులు కట్ అవుతాయి.
Node.jsలో, ప్రతి side effect ని idempotent గా చేయడం ద్వారా దీనిని నివారించవచ్చు. Job ID లేదా business-specific identifier నుండి ఒక idempotency key ని రూపొందించండి. మీరు charge చేయడానికి లేదా email పంపడానికి లేదా inventory ని సర్దుబాటు చేయడానికి ముందు, ఆ పని ఇప్పటికే జరిగిందో లేదో తనిఖీ చేయండి. ఆ key ని మీ database కి మరియు దానిని అంగీకరించే ఏవైనా third-party APIs కి పంపండి. ఒకే payload ని పదిసార్లు రన్ చేసినా, అది ఒక్కసారి రన్ చేసిన ఫలితాన్నే ఇచ్చేలా మీ jobs ని రూపొందించండి. ఈ ఒక్క అలవాటు వల్ల ఆర్థిక మరియు data-integrity bugs కి సంబంధించిన సమస్యలు పూర్తిగా తొలగిపోతాయి.
Pattern 4: మీ Dead-Letter Queue ని ఒక Dashboard లాగా పరిగణించండి
DLQ అనేది చెడు jobs మర్చిపోవడానికి వెళ్లే స్మశానం కాదు. ఇది ఒక operational tool, మరియు మీరు నిరంతరం గమనించాల్సిన ముఖ్యమైన అంశాలలో ఒకటి కావాలి.
DLQ లోని మెసేజ్ల సంఖ్య పెరిగినప్పుడు అలర్ట్స్ వచ్చేలా సెటప్ చేయండి. DLQ లో ఒక్క మెసేజ్ ఉన్నా, మీ validation logic విఫలమైందని, upstream schema మారిందని లేదా downstream service మీరు గుర్తించలేని తప్పుడు డేటాను పంపుతోందని అర్థం కావచ్చు. కస్టమర్లు ఫిర్యాదు చేయడం ప్రారంభించకముందే మీరు పట్టుకోవాల్సిన సంకేతాలు ఇవే. raw payload, stack trace మరియు timestamp ని తనిఖీ చేయడానికి వీలుగా ఒక dashboard ని నిర్మించండి. ఒక runbook ని సిద్ధంగా ఉంచుకోండి: వైఫల్యాన్ని తనిఖీ చేయండి, కోడ్ను సరిచేయండి (patch), ఆపై మెసేజ్లను సరైన క్రమంలో మళ్ళీ పంపండి (replay). మీ queue implementation అనుమతిస్తే, కేవలం సంఖ్య మాత్రమే కాకుండా, పెరిగే రేటు (growth rate) ఆధారంగా కూడా అలర్ట్స్ సెట్ చేయండి, ఎందుకంటే ఒక తప్పుడు deploy వల్ల నిమిషాల్లోనే DLQ నిండా మెసేజ్లు పేరుకుపోయే అవకాశం ఉంది.
Pattern 5: సందేహం ఉన్నప్పుడు, Process ని Crash చేయండి
Node.js అనేది V8 isolate లోపల ఒకే single event loop పై నడుస్తుంది. ఒక unhandled promise rejection లేదా ఒక
