చాలా Node.js ట్యుటోరియల్స్ ఎర్రర్ హ్యాండ్లింగ్ను ఒక అదనపు అంశంగా మాత్రమే చూస్తాయి. మీరు ఒక రూట్ హ్యాండ్లర్ను try/catch బ్లాక్లో చుట్టి, stack traceని లాగ్ చేసి, 500 ఎర్రర్ను రిటర్న్ చేస్తారు. ఒక HTTP రిక్వెస్ట్ అవతలి వైపు నిజమైన వ్యక్తి వేచి ఉన్నాడనే ఆలోచనే ఈ పద్ధతిని కొనసాగిస్తోంది. కానీ బ్యాక్గ్రౌండ్ జాబ్స్ (Background jobs) వేరు. క్యూ సిస్టమ్లో, సమాధానం కోసం వేచి ఉండే అసహనంతో ఉన్న క్లయింట్ ఉండరు, ఆటోమేటిక్ బ్రౌజర్ రిఫ్రెష్ ఉండదు. అక్కడ కేవలం ఒక వర్కర్, ఒక పేలోడ్ మరియు నిశ్శబ్దంగా పెరుగుతున్న రీట్రై కౌంటర్ మాత్రమే ఉంటాయి. ఏదైనా తప్పు జరిగినప్పుడు, అది మొదట నెమ్మదిగా, ఆ తర్వాత ఒక్కసారిగా జరుగుతుంది. సరిగ్గా వర్గీకరించని ఒకే ఒక్క ఎర్రర్ మొత్తం పైప్లైన్ను నిలిపివేయవచ్చు లేదా తెల్లవారుజామున మూడు గంటల సమయంలో ఒక ఇంజనీర్ను నిద్రలేపవచ్చు.
ఈ తేడా చాలా స్పష్టమైనది. రిక్వెస్ట్-రెస్పాన్స్ సైకిల్స్ త్వరగా విఫలమవుతాయి మరియు వెంటనే స్పందిస్తాయి. కానీ క్యూ ఫెయిల్యూర్ (queue failure) నిశ్శబ్దంగా ఉంటుంది. డేటాబేస్ కనెక్షన్ తెగిపోయే ముందు ఒక వర్కర్ వందలాది జాబ్స్ను పూర్తి చేయవచ్చు. స్పష్టమైన హ్యాండ్లింగ్ నియమాలు లేకపోతే, వర్కర్ వెంటనే రీట్రై చేస్తూ, ఇప్పటికే ఇబ్బంది పడుతున్న డేటాబేస్పై ఒత్తిడి పెంచి, చివరకు క్రాష్ అవుతుంది. ఎవరూ వర్కర్ను నేరుగా గమనించకపోవడం వల్ల, సమస్య యొక్క మొదటి సంకేతం తరచుగా క్యాస్కేడింగ్ బ్యాకప్ లేదా లాగ్ ఫైల్స్తో నిండిపోయిన డిస్క్ రూపంలో కనిపిస్తుంది. మీకు కేవలం catch బ్లాక్స్ మాత్రమే సరిపోవు. వివిధ రకాల వైఫల్యాలను విభిన్నంగా పరిగణించే మరియు ఒకే ఒక చెడ్డ జాబ్ వల్ల మిగిలిన సిస్టమ్ దెబ్బతినకుండా రక్షించే వ్యూహం మీకు అవసరం.
రెండు రకాల వైఫల్యాలు (Two Kinds of Failure)
ప్రతి ఎర్రర్ను ఈ క్రింది రెండు వర్గాలుగా విభజించడం ద్వారా ప్రారంభించండి.
రీట్రై చేయదగిన ఎర్రర్స్ (Retryable errors) తాత్కాలికమైనవి. థర్డ్-పార్టీ APIకి నెట్వర్క్ టైమ్ అవుట్ కావడం, 429 రేట్-లిమిట్ రెస్పాన్స్ లేదా డేటాబేస్ రెప్లికా తన ప్రైమరీ నుండి వెనుకబడి ఉండటం వంటివి. ఇవి ఒత్తిడికి సంకేతాలు మాత్రమే, బగ్స్ కాదు. సిస్టమ్ ముప్పై సెకన్లలో తనను తాను సరిదిద్దుకోవచ్చు. రీట్రై చేయదగిన జాబ్స్కు మరో అవకాశం ఇవ్వడం మంచిదే, కానీ అది నియంత్రిత పరిస్థితుల్లో మాత్రమే జరగాలి.
శాశ్వత ఎర్రర్స్ (Permanent errors) అంటే పొరపాట్లు. పేలోడ్లో తప్పుగా ఉన్న JSON, మిస్ అయిన యూజర్ ID, లేదా స్టోరేజ్లో లేని అవసరమైన ఫైల్ వంటివి. ఇవి మొదటి ప్రయత్నంలో ఎలా విఫలమయ్యాయో, వందవ ప్రయత్నంలో కూడా అలాగే విఫలమవుతాయి. వాటిని పదేపదే రీట్రై చేయడం వల్ల CPU సైకిల్స్ వృథా అవుతాయి, క్యూ స్లాట్లు వృథా అవుతాయి మరియు ఆరోగ్యకరమైన జాబ్స్ను ఆలస్యం చేసే టాక్సిక్ బ్యాక్-ప్రెజర్ (toxic back-pressure) ఏర్పడుతుంది. శాశ్వత వైఫల్యానికి ఉపయోగపడే ఏకైక ప్రదేశం లాగ్, అలర్ట్ లేదా డెడ్-లెటర్ క్యూ (dead-letter queue). అది రీట్రై లూప్లో ఉండకూడదు.
ఒక నిర్ణయ యంత్రాంగాన్ని నిర్మించండి (Build a Decision Engine)
వెంటనే వర్గీకరించండి. ఈ నిర్ణయాన్ని క్యూ ఫ్రేమ్వర్క్ మీద వదిలేయకండి. మీరు ఎర్రర్ను గుర్తించిన క్షణమే, దాని గమ్యాన్ని నిర్ణయించండి.
ఆచరణలో, దీని అర్థం కస్టమ్ ఎర్రర్ క్లాసెస్ లేదా రాపర్ ఫంక్షన్స్ (wrapper functions) సృష్టించడం, ఇవి ఎర్రర్ పైకి వెళ్లేముందు వైఫల్యాన్ని పరిశీలిస్తాయి. ఒకవేళ డేటాబేస్ డ్రైవర్ 'కనెక్షన్ రీసెట్'ను ఇస్తే, మీ హ్యాండ్లర్ దానిని రీట్రై చేయదగినదిగా (retryable) గుర్తించాలి. ఒకవేళ పేలోడ్ వాలిడేటర్ 'స్కీమా మిస్మ్యాచ్'ను ఇస్తే, దానిని శాశ్వతమైనదిగా (permanent) గుర్తించాలి. చాలా జాబ్ ప్రాసెసర్లు ప్రతిదీ రీట్రై చేసేలా డిఫాల్ట్గా ఉంటాయి, ఇది మీరు చేయగలిగే అత్యంత ఖరీదైన నిర్ణయం. శాశ్వత జాబ్స్ను వెంటనే తిరస్కరించండి. వాటిని వదిలేయండి లేదా డెడ్-లెటర్ క్యూకి మళ్లించండి, తద్వారా అవి ప్రధాన పైప్లైన్ను దెబ్బతీయలేవు. ఈ చిన్న అలవాటు ఏ ఇన్ఫ్రాస్ట్రక్చర్ మార్పు కంటే కూడా స్నోబాల్ ఎఫెక్ట్స్ (snowball effects)ను మరింత నమ్మకంగా నివారిస్తుంది.
వెనక్కి తగ్గండి, కానీ తెలివిగా (Back Off, But Smarter)
మీరు రీట్రై చేసినప్పుడు, ఎప్పుడూ వెంటనే చేయకండి. డేటాబేస్ డౌన్ అయినప్పుడు, ప్రతి సెకనుకు వర్కర్లు దానిపై దాడి చేయడం అనేది లోపలి నుండి జరుగుతున్న డెనైల్-ఆఫ్-సర్వీస్ (denial-of-service) దాడిలా కనిపిస్తుంది. ఎక్స్పోనెన్షియల్ బ్యాకoff (exponential backoff) ఉపయోగించండి. మొదట ఒక నిమిషం, తర్వాత ఐదు, ఆపై పదిహేను నిమిషాలు వేచి ఉండండి. అప్స్ట్రీమ్ సిస్టమ్ కోలుకోవడానికి సమయం ఇవ్వండి.
కానీ కేవలం ఎక్స్పోనెన్షియల్ బ్యాకoff మాత్రమే సరిపోదు. ఒక సర్వీస్ రీస్టార్ట్ అయినందున వేలాది జాబ్స్ ఒకేసారి విఫలమైతే, వాటి రీట్రై షెడ్యూల్స్ ఒకేలా ఉంటాయి. ఆ సర్వీస్ ఆన్లైన్లోకి రాగానే, అవి అన్నీ ఒకేసారి దానిని హిట్ చేస్తాయి, దీనివల్ల అది మళ్ళీ డౌన్ అయ్యే అవకాశం ఉంది. దీనిని నివారించడానికి 'జిట్టర్' (jitter)ను జోడించండి: అంటే ప్రతి ఆలస్యానికి (delay) ఒక చిన్న రాండమ్ ఆఫ్సెట్ను ఇవ్వండి. రీట్రైలను కొన్ని సెకన్ల వ్యవధిలో విస్తరించడం వల్ల సింక్రొనైజ్డ్ స్టాంపీడ్స్ (synchronized stampedes)ను నివారించవచ్చు. దీని గణితం సరళమైనదే, కానీ ఇది ఇచ్చే స్థిరత్వం అపారమైనది.
సాక్ష్యాలను భద్రపరచండి (Preserve the Evidence)
డెడ్-లెటర్ క్యూ (dead-letter queue) అనేది మీ ఆడిట్ ట్రయల్ (audit trail), అది చెత్త బుట్ట కాదు. ఒక జాబ్ తన చివరి రీట్రైని పూర్తి చేసినప్పుడు, దానిని కేవలం డిలీట్ చేయకండి. మొత్తం పేలోడ్ను, ఎర్రర్ కాంటెక్స్ట్ మరియు రీట్రై హిస్టరీతో సహా DLQకి తరలించండి.
ఇది సాక్ష్యాలను భద్రపరుస్తుంది. ఒక మనిషి ఆ జాబ్ను పరిశీలించి, బగ్ను సరిచేసి, అవసరమైతే దానిని మాన్యువల్గా మళ్ళీ ప్లే చేయవచ్చు. అంతకంటే ముఖ్యంగా, మీ DLQ యొక్క లోతును (depth) పర్యవేక్షించండి. డెడ్-లెటర్ అయిన జాబ్స్ అకస్మాత్తుగా పెరగడం అనేది తప్పుగా చేసిన డిప్లాయ్మెంట్, తప్పుగా జరిగిన స్కీమా మార్పు లేదా ఎక్స్టర్నల్ వెండర్ ఒప్పందాన్ని ఉల్లంఘించడం వంటి వాటికి అతి త్వరగా వచ్చే హెచ్చరిక కావచ్చు. DLQ పెరుగుదలను ఒక 'లీడింగ్ ఇండికేటర్' (leading indicator)గా పరిగణించండి, 'ట్రైలింగ్ ఇండికేటర్'గా కాదు. మీ DLQ నిండిపోతుంటే, అప్స్ట్రీమ్లో ఏదో మారింది మరియు బ్యాక్లాగ్ పెరగకముందే మీ టీమ్ ఆ విషయం తెలుసుకోవాలి.
రీట్రై కోసం డిజైన్ చేయండి (Design for the Retry)
ప్రతి జాబ్ను అది రెండుసార్లు రన్ అవుతుంది అన్నట్లుగా డిజైన్ చేయండి, ఎందుకంటే అది నిజంగా జరగవచ్చు. ఒక వర్కర్ ప్రాసెసింగ్ మధ్యలో విఫలమై, మళ్ళీ షెడ్యూల్ చేయబడి, తిరిగి అమలు కావచ్చు. మీ జాబ్ ఒక కస్టమర్కు ఛార్జ్ చేసినా, ఈమెయిల్ పంపినా లేదా ఇన్వెంటరీ కౌంట్ను పెంచినా, సాధారణ రీట్రై (retry) డూప్లికేట్లను సృష్టిస్తుంది.
దీనికి పరిష్కారం idempotency. మీరు ఏదైనా సైడ్ ఎఫెక్ట్ (side effect) చేసే ముందు, అది ఇప్పటికే జరిగిందో లేదో తనిఖీ చేయండి. జాబ్ పేలోడ్ (payload) నుండి ఒక యూనిక్ ఐడెంటిఫైయర్ను idempotency key గా ఉపయోగించండి. ఆ కీని షార్ట్-లివ్డ్ క్యాచీ (short-lived cache)లో లేదా యూనిక్నెస్ కన్స్ట్రెయింట్ (uniqueness constraint) ఉన్న డేటాబేస్ టేబుల్లో నిల్వ చేయండి. ఒకవేళ ఆ కీ ఇప్పటికే ఉంటే, ఆ పనిని వదిలేసి (skip) సక్సెస్ అని రిటర్న్ చేయండి. ఇది రీట్రైలను ఒక రిస్క్ నుండి హాని లేని no-op గా మారుస్తుంది. దీనికి కొన్ని అదనపు లైన్ల కోడ్ అవసరమవుతుంది, కానీ రాత్రికి రాత్రే ఆదాయం ఎందుకు రెట్టింపు అయిందో ఫైనాన్స్ టీమ్కు వివరించాల్సిన అవసరం లేకుండా ఇది మిమ్మల్ని కాపాడుతుంది.
ప్రాసెస్ను రక్షించండి
అన్బౌండెడ్ ప్రామిస్ రిజెక్షన్స్ (unbounded promise rejections) మరియు స్ట్రే ఎక్సెప్షన్స్ (stray exceptions) హెచ్చరిక లేకుండానే Node.js ప్రాసెస్ను ఆపివేయగలవు. ఒక వర్కర్లో, దీని అర్థం జాబ్లు మిస్ అవ్వడం మరియు కంటైనర్ను మళ్ళీ ప్రారంభించడానికి ఆర్కెస్ట్రేటర్ (orchestrator) కష్టపడటం.
unhandledRejection మరియు uncaughtException కోసం గ్లోబల్ హ్యాండ్లర్లను (global handlers) రిజిస్టర్ చేయండి. వాటి పని అప్లికేషన్ను రక్షించడం కాదు. అవసరమైన కనీస క్లీనప్ (cleanup) చేసి, ఆపై ఎగ్జిట్ అవ్వడం మాత్రమే వాటి లక్ష్యం. Docker, Kubernetes లేదా systemd ద్వారా క్లీన్ మెమరీ స్టేట్తో వర్కర్ను మళ్ళీ రీస్టార్ట్ చేయనివ్వండి. గ్లోబల్ హ్యాండ్లర్ రన్ అయిన తర్వాత కూడా బలవంతంగా నడపడం వల్ల మెమరీ లీక్స్ మరియు కరప్ట్ స్టేట్ వచ్చే అవకాశం ఉంది. తప్పుగా జాబ్లను ప్రాసెస్ చేసే స్లో 'జాంబీ ప్రాసెస్' కంటే, వేగంగా మరియు క్లీన్గా ఆగిపోవడం సురక్షితం. మిమ్మల్ని మళ్ళీ పునరుద్ధరించడానికి మీ ఆర్కెస్ట్రేటర్ను నమ్మండి; కరప్ట్ అయిన రన్టైమ్ను (runtime) మీరే సరిదిద్దాలని ప్రయత్నించకండి.
సిగ్నల్ను గౌరవించండి
డిప్లాయ్మెంట్లు, స్కేలింగ్ ఈవెంట్లు మరియు నోడ్ రొటేషన్ల సమయంలో వర్కర్లు షట్ డౌన్ చేయబడతాయి. మీ ప్రాసెస్ SIGTERM అందిన వెంటనే ఆగిపోతే, ప్రస్తుతం నడుస్తున్న జాబ్ను మీరు మధ్యలోనే ఆపేసినట్లు అవుతుంది. ఆ జాబ్ ఎప్పటికీ పూర్తి కాకపోవచ్చు, మరియు దాని రీట్రై కౌంటర్ కూడా ఇంకా పెరగకపోవచ్చు.
SIGTERM మరియు SIGINT కోసం వేచి చూడండి (Listen). సిగ్నల్ వచ్చినప్పుడు, క్యూ (queue) నుండి కొత్త జాబ్లను తీసుకోవడం ఆపివేయండి. వీలైతే ప్రస్తుతం ఉన్న జాబ్ను పూర్తి చేయండి. ఒక నిర్ణీత సమయం (ఉదాహరణకు ముప్పై సెకన్లు) తర్వాత, ఏది జరిగినా ఎగ్జిట్ అయ్యేలా ఒక హార్డ్ టైమౌట్ను సెట్ చేయండి. ఈ గ్రేస్ఫుల్ షట్డౌన్ (graceful shutdown) క్యూను గౌరవిస్తుంది మరియు తప్పుడు ఫెయిల్యూర్లను నివారిస్తుంది. క్లీన్గా ఎగ్జిట్ అయిన వర్కర్ను మీ డిప్లాయ్మెంట్ పైప్లైన్ 'హెల్తీ'గా పరిగణించాలి, కానీ క్రాష్ అయిన వర్కర్ అలర్ట్ను ట్రిగ్గర్ చేయాలి.
అసలైన సారాంశం
నమ్మకమైన క్యూ హ్యాండ్లింగ్ అంటే ప్రతి ఎర్రర్ను పట్టుకోవడం కాదు. ప్రతి ఫెయిల్యూర్ మోడ్ (failure mode) కోసం స్పష్టమైన నిర్ణయాలు తీసుకోవడం. తాత్కాలికమైన వాటిని (transient ones) ఓపికతో రీట్రై చేయండి. శాశ్వతమైన వాటిని త్వరగా పరిష్కరించండి. మీ వర్కర్లను రద్దీ (stampedes) నుండి రక్షించండి, idempotency కీలతో మీ డేటాను కాపాడండి మరియు ఆగిపోతున్న ప్రాసెస్లను క్లీన్గా ఎగ్జిట్ అవ్వనివ్వండి. ప్రతి ఫెయిల్యర్కు ఒక నిర్దిష్ట మార్గం ఉన్నప్పుడు, తెల్లవారుజామున మూడు గంటలు కూడా మామూలు సమయమే అవుతుంది. మీ పైప్లైన్ ముందుకు సాగుతూనే ఉంటుంది, మీ టీమ్ ప్రశాంతంగా నిద్రపోతూనే ఉంటుంది.
