Express రూట్‌లో ఇమేజ్ రీసైజింగ్ చేయడం అనేది విపత్తుకు దారితీసే పని. ఒక యూజర్ పది మెగాబైట్ల ఫోటోను అప్‌లోడ్ చేస్తే, మీ సర్వర్ పిక్సెల్స్‌ను ప్రాసెస్ చేయడం ప్రారంభిస్తుంది, మరియు ముప్పై సెకన్ల తర్వాత రిక్వెస్ట్ టైమ్ అవుట్ అవుతుంది. సరిగ్గా ఇటువంటి ఇబ్బందులను నివారించడానికే బ్యాక్‌గ్రౌండ్ జాబ్ క్యూలు (Background job queues) ఉంటాయి. Node.js ఎకోసిస్టమ్‌లో, Redis ద్వారా అసమకాలిక పనులను (asynchronous work) నిర్వహించడానికి Bull మరియు BullMQ రెండు ప్రధానమైన లైబ్రరీలుగా మారాయి. ఇవి ఒకే మూలాల నుండి వచ్చినప్పటికీ, వాటి తత్వశాస్త్రం (philosophy) మరియు రోజువారీ వినియోగంలో (ergonomics) గణనీయంగా భిన్నంగా ఉంటాయి. సరైన దానిని ఎంచుకోవడం చాలా ముఖ్యం, ఎందుకంటే తర్వాత వాటిని మార్చడం అనేది కేవలం ఒక ప్యాకేజీ అప్‌డేట్ లాంటిది కాదు.

ఉమ్మడి పునాది

రెండు లైబ్రరీలు Redisను వాటి వెన్నెముకగా ఉపయోగిస్తాయి. Redis అటామిక్ ఆపరేషన్లు, ఆలస్యమయ్యే జాబ్‌ల కోసం సార్టెడ్ సెట్లు (sorted sets), మరియు ఈవెంట్‌ల కోసం పబ్/సబ్ (pub/sub)లను నిర్వహిస్తుంది. మీరు ఇప్పటికే క్యాషింగ్ లేదా సెషన్ల కోసం Redisని ఉపయోగిస్తుంటే, జాబ్ క్యూను జోడించడానికి కొత్త ఇన్‌ఫ్రాస్ట్రక్చర్ అవసరం లేదు. Bull మరియు BullMQ రెండూ ప్రయారిటీలు, బ్యాక్‌ఆఫ్‌తో రీట్రైలు (retries with backoff), కన్కరెన్సీ కంట్రోల్స్ మరియు రిపీటబుల్ జాబ్‌లను సపోర్ట్ చేస్తాయి. ఆ పోలికల వల్ల ఎంపిక చేయడం సులభం కాకపోగా, మరింత కష్టమవుతుంది. మీరు కేవలం ఫీచర్ల జాబితాను చూసి నిర్ణయం తీసుకోలేరు. దానికి బదులుగా, ప్రతి లైబ్రరీ మీ కోడ్‌ను ఎలా స్ట్రక్చర్ చేయాలని కోరుకుంటుందో చూడాలి.

Bull: అనుభవజ్ఞుడైన వెటరన్

Bull సంవత్సరాలుగా అందుబాటులో ఉంది మరియు వేలాది ప్రొడక్షన్ అప్లికేషన్లలో నడుస్తోంది. ఇది బాగా పనిచేస్తుంది. దీని API ప్రతిదీ ఒకే Queue ఇన్‌స్టన్స్‌లో పొందుపరుస్తుంది. మీరు ఒకే ఆబ్జెక్ట్‌పై దానిని ఇన్‌స్టాంటియేట్ చేయవచ్చు, ప్రాసెసింగ్ ఫంక్షన్‌ను నిర్వచించవచ్చు మరియు ఈవెంట్‌లను వినవచ్చు (listen). మీరు పాత Node.js ప్యాటర్న్‌ల నుండి వస్తే, ఈ మోనోలిథిక్ డిజైన్ మీకు పరిచయంగా అనిపిస్తుంది. విస్తృతమైన async/await రాకముందు ఉన్న కోడ్‌బేస్‌లకు Bull సహజంగా సరిపోతుంది, ఎందుకంటే ఇది కాల్‌బ్యాక్‌లు మరియు పాత Redis క్లయింట్‌లతో పాటు అభివృద్ధి చెందింది.

దీని లోపం ఏమిటంటే టైట్ కప్లింగ్ (tight coupling). మీ API సర్వర్ ఒక జాబ్‌ను సృష్టించినప్పుడు, అది వర్కర్ లాజిక్‌ను కలిగి ఉన్న అదే Queue ఆబ్జెక్ట్‌ను ఇంపోర్ట్ చేస్తుంది. వాస్తవానికి, దీని అర్థం మీ వెబ్ ప్రాసెస్ ఎప్పుడూ అమలు చేయని డిపెండెన్సీలను కూడా తనతో పాటు లాక్కొస్తుంది. ఇది పెద్ద లోపం కాకపోయినా, క్లీన్ ఆర్కిటెక్చర్‌కు అంతరాయం కలిగిస్తుంది. సాధారణ వర్క్‌లోడ్ల కోసం మీరు దీనిని గమనించకపోవచ్చు. కానీ డజన్ల కొద్దీ మాడ్యూల్స్ ఉన్న పెద్ద టీమ్‌లలో, ఈ సమస్య క్రమంగా పెరుగుతుంది.

BullMQ: మొదటి నుండి పునర్నిర్మించబడింది

BullMQ అధికారిక వారసుడు. ఇది మొదటి రోజు నుండి TypeScriptలో తిరిగి రాయబడింది, కాబట్టి టైప్స్ (types) అనేవి జావాస్క్రిప్ట్ సోర్స్ మీద అదనంగా చేర్చినవి కావు. దీని API బాధ్యతలను విభిన్న క్లాస్‌లుగా విభజిస్తుంది. Queue జాబ్‌లను జోడించడాన్ని నిర్వహిస్తుంది. Worker వాటిని ప్రాసెస్ చేయడాన్ని నిర్వహిస్తుంది. QueueEvents అబ్జర్వబిలిటీని (observability) నిర్వహిస్తుంది. ఈ విభజన ఆధునిక డిస్ట్రిబ్యూటెడ్ సిస్టమ్స్ ఎలా పనిచేస్తాయో దానికి ప్రతిబింబంలా ఉంటుంది. మీ API పాడ్స్‌కు కేవలం Queue క్లాస్ మరియు Redis కనెక్షన్ మాత్రమే అవసరం. మీ వర్కర్ పాడ్స్ Worker క్లాస్‌ను ఇంపోర్ట్ చేస్తాయి. ఈ విభజన కేవలం భావన మాత్రమే కాదు, భౌతికంగా కూడా ఉంటుంది.

పెద్ద టీమ్‌లలో ఈ మార్పు వల్ల ప్రయోజనం ఉంటుంది. కొత్త ఫీచర్‌ను డెవలప్ చేసే డెవలపర్, ప్రాసెసర్ ఏ ఫైల్‌లో ఉందో తెలియకపోయినా జాబ్‌ను ఎన్‌క్యూ (enqueue) చేయవచ్చు. రన్‌టైమ్‌లో కాకుండా, జాబ్ డేటా మరియు హ్యాండ్లర్‌ల మధ్య టైప్ మిస్‌మ్యాచ్‌లను కంపైలర్ ముందే గుర్తిస్తుంది. ఆధునిక Node.jsలో async/await API కూడా నేటివ్ (native) గా అనిపిస్తుంది. మీరు పాత పద్ధతులతో పోరాడాల్సిన అవసరం ఉండదు.

జాబ్ ఫ్లోస్: హ్యాక్స్ నుండి ఫస్ట్-క్లాస్ సిటిజన్స్ వరకు

మల్టీ-స్టెప్ వర్క్‌ఫ్లోలు ఈ రెండు లైబ్రరీల మధ్య ఉన్న అతిపెద్ద వ్యత్యాసాన్ని చూపుతాయి.

మీరు ఒక ఇ-కామర్స్ ఇన్‌వాయిసింగ్ పైప్‌లైన్‌ను నిర్మిస్తున్నారని అనుకుందాం. ఒక కస్టమర్ చెక్ అవుట్ చేస్తారు. మీరు ఇన్వెంటరీని రిజర్వ్ చేయాలి, కార్డును ఛార్జ్ చేయాలి, PDFని జనరేట్ చేయాలి మరియు ఈమెయిల్ పంపాలి. Bullతో, ఈ దశలను అనుసంధానించడం (chaining) అంటే మాన్యువల్ బుక్‌కీపింగ్ చేయడం. ఒక ప్రాసెసర్ తదుపరి జాబ్‌ను ప్రారంభించి, Redis ద్వారా లేదా భారీ డేటా పేలోడ్‌ల ద్వారా స్టేట్‌ను పంపవచ్చు. మీరు పేరెంట్-చైల్డ్ కోఆర్డినేషన్‌ను మీరే రాయాలి. ఇది పని చేస్తుంది, కానీ ఎప్పుడైనా సమస్యలు రావచ్చు. రీట్రై లాజిక్ గందరగోళంగా మారుతుంది. ఒకవేళ PDF దశ విఫలమైతే, ఛార్జ్‌ను వెనక్కి తీసుకోవడానికి (unwinding the charge) కస్టమ్ కాంపెన్సేషన్ కోడ్ రాయాల్సి ఉంటుంది, అందులో తప్పులు జరిగే అవకాశం ఉంది.

BullMQ FlowProducerను పరిచయం చేస్తుంది. మీరు జాబ్‌ల యొక్క ఒక ట్రీని నిర్వచిస్తారు, ఇక్కడ పేరెంట్ జాబ్‌లు వాటి చైల్డ్ జాబ్‌ల కోసం ఆటోమేటిక్‌గా వేచి ఉంటాయి. ఇన్‌వాయిసింగ్ ఉదాహరణలో, మీరు finalize-order అనే రూట్ జాబ్‌ను మూడు చైల్డ్ జాబ్‌లతో సృష్టిస్తారు: reserve-inventory, charge-payment, మరియు generate-pdf. మీరు ఈమెయిల్ నోటిఫికేషన్‌ను PDF జాబ్ యొక్క చైల్డ్‌గా చేయవచ్చు. Redis ఈ గ్రాఫ్ స్ట్రక్చర్‌ను నిల్వ చేస్తుంది. ప్రతి డిపెండెన్సీ విజయవంతమైనప్పుడు మాత్రమే పేరెంట్ యాక్టివేట్ అవుతుంది. ఒక చైల్డ్ విఫలమైతే, మొత్తం బ్రాంచ్ ఆగిపోతుంది. మీరు పోలింగ్ లూప్‌లు లేదా రికర్సివ్ జాబ్ స్పానర్లను రాయాల్సిన అవసరం లేదు. ఇది కేవలం సింటాక్టిక్ షుగర్ (syntactic sugar) మాత్రమే కాదు. ఇది మీరు బిజినెస్ లాజిక్‌ను మోడల్ చేసే విధానాన్ని మారుస్తుంది.

రేట్ లిమిటింగ్: బ్లంట్ ఇన్‌స్ట్రుమెంట్ వర్సెస్ స్కాల్పెల్

రెండు లైబ్రరీలు త్రూపుట్‌ను (throughput) నియంత్రించగలవు, కానీ వాటి యొక్క ఖచ్చితత్వం (granularity) చాలా భిన్నంగా ఉంటుంది.

Bull ప్రతి క్యూకి రేట్ లిమిట్‌లను వర్తింపజేస్తుంది. మీరు ఒక క్యూ సెకనుకు వంద జాబ్‌లను ప్రాసెస్ చేసేలా సెట్ చేస్తే, ఆ పరిమితి క్యూలోని ప్రతి జాబ్‌కు సమానంగా వర్తిస్తుంది. ఇది ఒకే రకమైన (homogeneous) వర్క్‌లోడ్‌లకు సరిపోతుంది. కానీ మల్టీటెనెంట్ SaaS ప్లాట్‌ఫారమ్‌లలో ఇది సరిగ్గా పనిచేయదు. ఒక కస్టమర్ షేర్డ్ క్యూలోకి లక్షలాది వెబ్‌హుక్ డెలివరీలను పంపిస్తూ మిగతా వారిని ఇబ్బంది పెడుతున్నారని ఊహించుకోండి. Bull యొక్క క్యూ-లెవల్ లిమిట్ వల్ల, మిగిలిన వారందరినీ నెమ్మదింపజేయకుండా కేవలం ఆ టెనెంట్‌ను మాత్రమే మీరు నియంత్రించలేరు. మీ ముందున్న ఆప్షన్లు అంత సులభం కావు. ప్రతి కస్టమర్‌కు విడివిడిగా Redis క్యూలను సృష్టించి వాటిని డైనమిక్‌గా నిర్వహించాల్సి ఉంటుంది, లేదా ఈ అసమానతను అంగీకరించాల్సి ఉంటుంది.

BullMQ గ్రూప్-ఆధారిత రేట్ లిమిటింగ్‌ను జోడిస్తుంది. మీరు ప్రతి జాబ్‌కు ఒక గ్రూప్ కీని (సాధారణంగా టెనెంట్ లేదా యూజర్ ID) ట్యాగ్ చేసి, ప్రతి గ్రూప్‌కు పరిమితులను నిర్వచించవచ్చు. ఒకే క్యూ అన్ని టెనెంట్‌ల జాబ్‌లను ప్రాసెస్ చేస్తుంది, కానీ షెడ్యూలర్ ప్రతి గ్రూప్‌ను స్వతంత్రంగా నియంత్రిస్తుంది. కస్టమర్ A నుండి వచ్చే భారీ లోడ్ వల్ల కస్టమర్ B పనులు ఆగిపోవు. దీనివల్ల క్యూల సంఖ్య అనవసరంగా పెరగకుండా ఉంటుంది మరియు మీ Redis keyspace కూడా క్రమబద్ధంగా ఉంటుంది. 'నోయిసీ-నెయిబర్' (noisy-neighbor) సమస్యలు ఉన్న ప్లాట్‌ఫారమ్‌లకు, కేవలం ఈ ఫీచర్ కోసమే మైగ్రేషన్ చేయడం సరైన నిర్ణయం కావచ్చు.

ప్రాక్టికల్‌లో క్లీనర్ ఆర్కిటెక్చర్

ప్రొడక్షన్ ఇన్‌సిడెంట్‌ను డీబగ్ చేసే వరకు క్యూ మరియు వర్కర్ల మధ్య ఉన్న విభజన పెద్దగా అర్థం కాదు. Bullలో, రూట్ హ్యాండ్లర్ల లోపల జాబ్ క్రియేషన్ కోడ్ ఉండటం సాధారణం, ఇవి భారీ ప్రాసెసింగ్ డిపెండెన్సీలను కూడా ఇంపోర్ట్ చేస్తాయి. BullMQ పని ఎక్కడ జరగాలి అనే దానిపై మీకు స్పష్టతనిస్తుంది. దీనివల్ల మీ వెబ్ సర్వర్లు తేలికగా (lean) ఉంటాయి. మీ వర్కర్ కంటైనర్లు భారీ లైబ్రరీలు, ఇమేజ్ ప్రాసెసర్లు లేదా హెడ్‌లెస్ బ్రౌజర్‌లను కలిగి ఉంటాయి. మెమరీ లీక్ కనిపిస్తే, ఏ ప్రాసెస్ రకాన్ని ప్రొఫైల్ చేయాలో మీకు ఖచ్చితంగా తెలుస్తుంది. దీని మెంటల్ మోడల్ Celery లేదా Sidekiq వంటి సిస్టమ్‌లకు దగ్గరగా ఉంటుంది.

సరైన దానిని ఎంచుకోవడం

మీరు కొత్త ప్రాజెక్ట్‌ను (greenfield project) ప్రారంభిస్తుంటే, BullMQతో మొదలుపెట్టండి. దీని TypeScript డెఫినిషన్లు ఖచ్చితంగా మరియు సంపూర్ణంగా ఉంటాయి. జాబ్ ఫ్లోస్ వల్ల ఆర్కెస్ట్రేషన్ కోడ్ అవసరం తగ్గుతుంది. గ్రూప్ రేట్ లిమిటింగ్ వల్ల అసమానత సమస్యలు రాకముందే పరిష్కరించబడతాయి. దీని async/await API చాలా సహజంగా అనిపిస్తుంది. కొత్త ప్రాజెక్ట్ కోసం పాత లైబ్రరీని ఎంచుకోవడానికి పెద్దగా కారణం లేదు.

ఒకవేళ Bull ఇప్పటికే బాగా పనిచేస్తుంటే, దానితోనే కొనసాగండి. మైగ్రేషన్ వల్ల సమయం వృథా అవ్వడమే కాకుండా, స్టెబిలిటీకి కూడా ముప్పు ఉండవచ్చు. మీ జాబ్‌లు సరళంగా మరియు స్వతంత్రంగా ఉంటే, మీకు నిజంగా అవసరమైన ఫీచర్లు ఏవీ మిస్ కావడం లేదు. పాస్‌వర్డ్ రీసెట్ ఈమెయిల్స్ పంపే లేదా అవతార్‌లను రీసైజ్ చేసే క్యూలకు ఫ్లో గ్రాఫ్‌ల అవసరం లేదు. కేవలం సిద్ధాంతపరమైన స్వచ్ఛత (theoretical purity) కోసం పనిచేస్తున్న కోడ్‌ను మళ్ళీ రాయడం ఇంజనీరింగ్ కాదు, అది కేవలం ఒక అభిరుచి మాత్రమే.

మైగ్రేషన్ వాస్తవ పరిస్థితులు

మీరు మారాలని నిర్ణయించుకుంటే, దానిని కోడ్ రీఫ్యాక్టర్‌గా కాకుండా ఇన్‌ఫ్రాస్ట్రక్చర్ మార్పుగా పరిగణించండి. Bull మరియు BullMQ వేర్వేరు Redis కీ స్కీమాలను ఉపయోగిస్తాయి. అవి ఒకదానికొకటి జాబ్ డేటా లేదా స్టేట్‌ను చదవలేవు. మీరు కేవలం ఒక ఫీచర్ ఫ్లాగ్‌ను మార్చి, పాత జాబ్‌లు పూర్తవుతాయని ఆశించలేరు. మీరు ఉన్న ప్రతి క్యూలోని జాబ్‌లను పూర్తిగా పూర్తి చేయాలి (drain), కొత్త వర్కర్లను డిప్లాయ్ చేయాలి మరియు BullMQతో కొత్తగా ఎన్‌క్యూ (enqueue) చేయడం ప్రారంభించాలి. దీని కోసం మెయింటెనెన్స్ విండోను లేదా బ్లూ-గ్రీన్ డిప్లాయ్‌మెంట్‌ను ప్లాన్ చేయండి; తద్వారా పాత వర్కర్లు పాత క్యూను, కొత్త వర్కర్లు కొత్త క్యూను హ్యాండిల్ చేస్తారు.

అసలు సారాంశం