મોટાભાગના Node.js ટ્યુટોરિયલ્સ એરર હેન્ડલિંગને ગૌણ બાબત તરીકે જુએ છે. તમે રુટ હેન્ડલરને try/catch બ્લોકમાં લપેટી દો છો, સ્ટેક ટ્રેસ લોગ કરો છો અને 500 રિટર્ન કરો છો. આ માનસિકતા એટલા માટે ટકી રહે છે કારણ કે HTTP રિક્વેસ્ટના બીજા છેડે કોઈ વ્યક્તિ રાહ જોઈ રહી હોય છે. બેકગ્રાઉન્ડ જોબ્સ અલગ છે. ક્યુ સિસ્ટમમાં, જવાબ આપવા માટે કોઈ અધીરો ક્લાયન્ટ નથી હોતો, કોઈ ઓટોમેટિક બ્રાઉઝર રિફ્રેશ નથી હોતું. ત્યાં ફક્ત એક વર્કર, એક પેલોડ અને શાંતિથી વધતો જતો રિટ્રાય કાઉન્ટર હોય છે. જ્યારે વસ્તુઓ ખોટી થાય છે, ત્યારે તે ધીમે ધીમે અને પછી એકસાથે થાય છે. એક ખોટી રીતે વર્ગીકૃત કરેલી એરર આખી પાઇપલાઇનને અટકાવી શકે છે અથવા વહેલી સવારે ત્રણ વાગ્યે એન્જિનિયરને જગાડી શકે છે.
આ તફાવત સરળ છે. રિક્વેસ્ટ-રિસ્પોન્સ સાયકલ ઝડપથી અને સ્પષ્ટ રીતે નિષ્ફળ જાય છે. ક્યુ ફેલ્યોર શાંત હોય છે. ડેટાબેઝ કનેક્શન તૂટતા પહેલા વર્કર સેંકડો જોબ્સ પૂરા કરી શકે છે. સ્પષ્ટ હેન્ડલિંગ નિયમો વિના, વર્કર તરત જ ફરી પ્રયાસ (retry) કરે છે, પહેલેથી જ સંઘર્ષ કરી રહેલા ડેટાબેઝ પર દબાણ લાવે છે અને ક્રેશ થઈ જાય છે. કારણ કે કોઈ વર્કર પર સીધું ધ્યાન નથી આપતું, મુશ્કેલીનું પ્રથમ સંકેત ઘણીવાર કેસ્કેડિંગ બેકઅપ અથવા લોગ ફાઇલોથી ભરાયેલ ડિસ્ક હોય છે. તમારે માત્ર catch બ્લોક્સ કરતાં વધુની જરૂર છે. તમારે એવી વ્યૂહરચનાની જરૂર છે જે વિવિધ નિષ્ફળતાઓને અલગ રીતે જુએ અને બાકીની સિસ્ટમને એક ખરાબ જોબથી સુરક્ષિત રાખે.
નિષ્ફળતાના બે પ્રકારો
દરેક એરરને બેમાંથી એક વિભાગમાં વહેંચીને શરૂઆત કરો.
રિટ્રાયેબલ (Retryable) એરર્સ ક્ષણિક હોય છે. થર્ડ-પાર્ટી API માટે નેટવર્ક ટાઈમઆઉટ, 429 રેટ-લિમિટ રિસ્પોન્સ, અથવા ટેમ્પરરી ડેટાબેઝ રેપ્લિકાનું તેના પ્રાઈમરી કરતા પાછળ રહેવું. આ દબાણના લક્ષણો છે, બગ્સ નથી. સિસ્ટમ ત્રીસ સેકન્ડમાં પોતાની જાતે ઠીક થઈ શકે છે. રિટ્રાયેબલ જોબ્સને બીજો પ્રયાસ મળવો જોઈએ, પરંતુ માત્ર નિયંત્રિત પરિસ્થિતિઓમાં જ.
પરમેનન્ટ (Permanent) એરર્સ ભૂલો છે. પેલોડમાં અમાન્ય JSON, મિસિંગ યુઝર ID, સ્ટોરેજમાં ન મળે તેવી જરૂરી ફાઇલ. આ સો વાર પ્રયાસ કરવા છતાં પણ પ્રથમ પ્રયાસની જેમ જ નિષ્ફળ જશે. તેમને ફરીથી પ્રયાસ કરવાથી CPU સાયકલનો બગાડ થાય છે, ક્યુ સ્લોટ્સ વેડફાય છે અને ઝેરી બેક-પ્રેશર (back-pressure) પેદા થાય છે જે હેલ્ધી જોબ્સમાં વિલંબ કરે છે. પરમેનન્ટ ફેલ્યોર માટે એકમાત્ર ઉપયોગી જગ્યા લોગ, એલર્ટ અથવા ડેડ-લેટર ક્યુ (dead-letter queue) છે. તે રિટ્રાય લૂપમાં હોવું જોઈએ નહીં.
ડિસિઝન એન્જિન બનાવો
તરત જ વર્ગીકરણ કરો. આ નિર્ણય ક્યુ ફ્રેમવર્ક પર છોડી ન દો. જે ક્ષણે તમે એરર પકડો છો, તેનો નસીબ નક્કી કરો.
વ્યવહારમાં, આનો અર્થ એ છે કે કસ્ટમ એરર ક્લાસ અથવા રેપર ફંક્શન્સ બનાવવું જે એરરને ઉપર મોકલતા પહેલા નિષ્ફળતાની તપાસ કરે. જો ડેટાબેઝ ડ્રાઈવર કનેક્શન રીસેટ (connection reset) ફેંકે, તો તમારા હેન્ડલરે તેને રિટ્રાયેબલ તરીકે ટેગ કરવો જોઈએ. જો પેલોડ વેલિડેટર સ્કીમા મિસમેચ (schema mismatch) ફેંકે, તો તેને પરમેનન્ટ તરીકે ટેગ કરો. ઘણા જોબ પ્રોસેસર્સ બધું જ રિટ્રાય કરવા માટે ડિફોલ્ટ સેટ હોય છે, જે તમારો સૌથી મોંઘો નિર્ણય હોઈ શકે છે. પરમેનન્ટ જોબ્સને તરત જ નકારી કાઢો. કાં તો તેને ડ્રોપ કરો અથવા તેને ડેડ-લેટર ક્યુમાં રીરૂટ કરો જ્યાં તેઓ મુખ્ય પાઇપલાઇનને બગાડી ન શકે. આ એક આદત કોઈપણ ઇન્ફ્રાસ્ટ્રક્ચર ફેરફાર કરતાં વધુ વિશ્વસનીય રીતે સ્નોબોલ ઇફેક્ટ્સ અટકાવે છે.
બેક ઓફ કરો, પણ સ્માર્ટલી
જ્યારે તમે રિટ્રાય કરો, ત્યારે ક્યારેય તરત જ ન કરો. જો ડેટાબેઝ ડાઉન હોય, તો દર સેકન્ડે તેના પર હુમલો કરતા વર્કર્સનો ઝબકારો અંદરથી 'ડિનાયલ-ઓફ-સર્વિસ' (denial-of-service) એટેક જેવો લાગે છે. એક્સપોનેન્શિયલ બેકઓફ (exponential backoff) નો ઉપયોગ કરો. એક મિનિટ રાહ જુઓ, પછી પાંચ, પછી પંદર. અપસ્ટ્રીમ સિસ્ટમને રિકવર થવા માટે સમય આપો.
પરંતુ માત્ર એક્સપોનેન્શિયલ બેકઓફ પૂરતું નથી. જો કોઈ સર્વિસ રીસ્ટાર્ટ થવાને કારણે એકસાથે હજારો જોબ્સ નિષ્ફળ જાય, તો તેમના રિટ્રાય શેડ્યૂલ એકસરખા થઈ જશે. જ્યારે તે સર્વિસ ફરીથી ઓનલાઇન આવશે ત્યારે તેઓ એકસાથે તેના પર હુમલો કરશે, જે સંભવતઃ તેને ફરીથી ડાઉન કરી શકે છે. જિટર (jitter) ઉમેરો: દરેક વિલંબમાં થોડો રેન્ડમ ઓફસેટ. રિટ્રાયઝને થોડી સેકન્ડોમાં ફેલાવવાથી સિંક્રનાઇઝ્ડ સ્ટેમ્પિડ્સ (synchronized stampedes) અટકાવી શકાય છે. ગણિત સરળ છે, પરંતુ તે જે સ્થિરતા આપે છે તે અદભૂત છે.
પુરાવા સાચવી રાખો
ડેડ-લેટર ક્યુ એ તમારો ઓડિટ ટ્રેલ છે, કચરાપેટી નથી. જ્યારે કોઈ જોબ તેનો અંતિમ રિટ્રાય પૂરો કરે, ત્યારે તેને ફક્ત ડિલીટ ન કરો. આખો પેલોડ, એરર કોન્ટેક્સ્ટ અને રિટ્રાય હિસ્ટ્રી સાથે DLQ માં ખસેડો.
આ પુરાવા સાચવે છે. જો જરૂર હોય તો માણસ જોબનું નિરીક્ષણ કરી શકે છે, બગને પેચ કરી શકે છે અને તેને મેન્યુઅલી ફરીથી ચલાવી શકે છે. વધુ મહત્વનું છે કે, તમારા DLQ ની ઊંડાઈનું મોનિટરિંગ કરો. ડેડ-લેટર થયેલ જોબ્સમાં અચાનક વધારો એ ઘણીવાર ખરાબ ડિપ્લોયમેન્ટ, ખોટી રીતે થયેલ સ્કીમા ફેરફાર અથવા એક્સટર્નલ વેન્ડર દ્વારા કરાર તોડવાનો પ્રારંભિક સંકેત હોય છે. DLQ ના વધારાને લીડિંગ ઇન્ડિકેટર (leading indicator) તરીકે જુઓ, ટ્રેલિંગ ઇન્ડિકેટર તરીકે નહીં. જો તમારું DLQ ભરાઈ રહ્યું હોય, તો અપસ્ટ્રીમમાં કંઈક બદલાયું છે અને બેકલોગ ફેલાય તે પહેલાં તમારી ટીમે તે જાણવાની જરૂર છે.
રિટ્રાય માટે ડિઝાઇન કરો
દરેક જોબને એવી રીતે ડિઝાઇન કરો જાણે કે તે બે વાર ચાલશે, કારણ કે તે થઈ શકે છે. એક વર્કર પ્રોસેસિંગની વચ્ચે જ નિષ્ફળ જઈ શકે છે, ફરીથી શેડ્યૂલ થઈ શકે છે, અને ફરીથી એક્ઝિક્યુટ થઈ શકે છે. જો તમારી જોબ ગ્રાહક પાસેથી ચાર્જ લેતી હોય, ઈમેલ મોકલતી હોય, અથવા ઇન્વેન્ટરી કાઉન્ટ વધારેતી હોય, તો એક સાધારણ રીટ્રાય ડુપ્લીકેટ્સ (duplicates) બનાવી શકે છે.
આનો ઉકેલ idempotency છે. કોઈ પણ સાઈડ ઇફેક્ટ (side effect) કરતા પહેલા, તપાસો કે તે પહેલેથી થઈ ગઈ છે કે નહીં. જોબ પેલોડમાંથી એક યુનિક આઈડેન્ટિફાયરનો ઉપયોગ idempotency key તરીકે કરો. તે કીને શોર્ટ-લિવ્ડ કેશ (short-lived cache) અથવા યુનિકનેસ કન્સ્ટ્રેન્ટ (uniqueness constraint) ધરાવતા ડેટાબેઝ ટેબલમાં સ્ટોર કરો. જો કી પહેલેથી અસ્તિત્વમાં હોય, તો કામ છોડી દો અને સફળતા (success) દર્શાવો. આ રીટ્રાયઝને જોખમમાંથી એક નુકસાન વગરના no-op માં ફેરવી દે છે. આમાં કોડની થોડી વધારાની લાઈનો લાગે છે, પરંતુ તે તમને ફાઇનાન્સ ટીમને એ સમજાવવાથી બચાવે છે કે રાતોરાત આવક કેમ બમણી થઈ ગઈ.
પ્રોસેસનું રક્ષણ કરો
અનબાઉન્ડેડ પ્રોમિસ રિજેક્શન (unbounded promise rejections) અને સ્ટ્રે એક્સેપ્શન (stray exceptions) ચેતવણી વગર Node.js પ્રોસેસને મારી શકે છે. વર્કરના કિસ્સામાં, તેનો અર્થ એ છે કે જોબ્સ ડ્રોપ થઈ જાય છે અને ઓર્કેસ્ટ્રેટર (orchestrator) કન્ટેનરને ફરીથી શરૂ કરવા માટે દોડધામ કરે છે.
unhandledRejection અને uncaughtException માટે ગ્લોબલ હેન્ડલર્સ રજિસ્ટર કરો. તેમનું કામ એપ્લિકેશનને બચાવવાનું નથી. તેમનું કામ જરૂરી લઘુત્તમ ક્લીનઅપ (cleanup) કરવાનું છે અને પછી એક્ઝિટ (exit) કરવાનું છે. Docker, Kubernetes, અથવા systemd ને ક્લીન મેમરી સ્ટેટ સાથે વર્કરને ફરીથી શરૂ કરવા દો. ગ્લોબલ હેન્ડલર ફાયર થયા પછી લંગડાતા ચાલવું એ મેમરી લીક અને કરપ્ટ સ્ટેટને આમંત્રણ આપે છે. ખોટી રીતે જોબ્સ પ્રોસેસ કરતા ધીમા ઝોમ્બી પ્રોસેસ કરતા ઝડપી અને સ્વચ્છ અંત (clean death) વધુ સુરક્ષિત છે. તમને પાછા લાવવા માટે તમારા ઓર્કેસ્ટ્રેટર પર વિશ્વાસ રાખો; કરપ્ટ રનટાઇમને છેતરવાનો પ્રયાસ કરશો નહીં.
સિગ્નલનો આદર કરો
ડિપ્લોયમેન્ટ્સ, સ્કેલિંગ ઇવેન્ટ્સ અને નોડ રોટેશન દરમિયાન વર્કર્સ બંધ થઈ જાય છે. જો તમારી પ્રોસેસ SIGTERM મળતાની સાથે જ મરી જાય, તો તમે હાલમાં ચાલતી કોઈપણ જોબને અધવચ્ચેથી અટકાવી દો છો. તે જોબ કદાચ ક્યારેય પૂરી ન થાય, અને તેનો રીટ્રાય કાઉન્ટર પણ હજુ સુધી વધ્યો ન હોય.
SIGTERM અને SIGINT માટે લિસન (listen) કરો. જ્યારે સિગ્નલ આવે, ત્યારે ક્યુ (queue) માંથી નવી જોબ્સ લેવાનું બંધ કરો. જો તમે કરી શકો તો વર્તમાન જોબ પૂર્ણ કરો. એક હાર્ડ ટાઈમઆઉટ સેટ કરો, કદાચ ત્રીસ સેકન્ડ, જે પછી તમે ગમે તે હોય એક્ઝિટ કરો. આ ગ્રેસફુલ શટડાઉન (graceful shutdown) ક્યુનું સન્માન કરે છે અને ખોટી નિષ્ફળતાઓ ટાળે છે. તમારી ડિપ્લોયમેન્ટ પાઇપલાઇને સ્વચ્છ રીતે એક્ઝિટ કરનાર વર્કરને હેલ્ધી ગણવો જોઈએ, જ્યારે ક્રેશ થયેલ વર્કર એલર્ટ ટ્રિગર કરવો જોઈએ.
મુખ્ય સારાંશ
વિશ્વસનીય ક્યુ હેન્ડલિંગ એ દરેક ભૂલને પકડવા વિશે નથી. તે દરેક નિષ્ફળતાના મોડ (failure mode) માટે વિચારપૂર્વક નિર્ણયો લેવા વિશે છે. કામચલાઉ (transient) ભૂલોને ધીરજ સાથે રીટ્રાય કરો. કાયમી ભૂલોને ઝડપથી સમાપ્ત કરો. તમારા વર્કર્સને સ્ટેમ્પિડ્સ (stampedes) થી બચાવો, આઈડેમપોટન્સી કી સાથે તમારા ડેટાનું રક્ષણ કરો, અને મૃત્યુ પામતી પ્રોસેસને સ્વચ્છ રીતે એક્ઝિટ કરવા દો. જ્યારે દરેક નિષ્ફળતા માટે એક નિર્ધારિત માર્ગ હોય, ત્યારે વહેલી સવારના ત્રણ વાગ્યા પણ માત્ર એક સામાન્ય સમય બની જાય છે. તમારી પાઇપલાઇન ચાલતી રહે છે, અને તમારી ટીમ શાંતિથી સૂતી રહે છે.
