12 अग॰ 2025·8 मिनट पढ़ने में

विश्वसनीय सूचनाओं के लिए ट्रिगर बनाम बैकग्राउंड जॉब वर्कर्स

सीखें कब ट्रिगर और कब बैकग्राउंड जॉब वर्कर नोटिफिकेशन के लिए ज्यादा सुरक्षित होते हैं—रिट्राई, ट्रांज़ैक्शन सीमाएँ और डुप्लिकेट रोकने के व्यावहारिक निर्देशों के साथ।

विश्वसनीय सूचनाओं के लिए ट्रिगर बनाम बैकग्राउंड जॉब वर्कर्स

असली ऐप्स में नोटिफिकेशन डिलीवरी क्यों टूटती है

नोटिफिकेशन सरल लगते हैं: एक उपयोगकर्ता कुछ करता है, फिर एक ईमेल या SMS भेज दिया जाता है। अधिकांश असली विफलताएँ समय और डुप्लीकेशन से जुड़ी होती हैं। संदेश तब भेजे जाते हैं जब डेटा सच में सेव नहीं हुआ होता, या आंशिक विफलता के बाद वे दो बार भेज दिए जाते हैं।

“नोटिफिकेशन” बहुत कुछ हो सकता है: ईमेल रसीदें, SMS एक-बार के कोड, पुश अलर्ट, इन-ऐप संदेश, Slack या Telegram पिंग्स, या किसी दूसरे सिस्टम को वेबहुक। साझा समस्या हमेशा एक जैसी रहती है: आप डेटाबेस में हुए बदलाव का समन्वय किसी बाहरी सिस्टम के साथ कर रहे होते हैं।

बाहरी दुनिया गड़बड़ हो सकती है। प्रोवाइडर धीमे हो सकते हैं, टाइमआउट दे सकते हैं, या अनुरोध स्वीकार कर ले सकते हैं जबकि आपका ऐप सफलता प्रतिक्रिया कभी प्राप्त न करे। आपका अपना ऐप रिक्वेस्ट के बीच क्रैश या रिस्टार्ट हो सकता है। “सफल” भेज भी इन्फ्रास्ट्रक्चर रिट्राइज़, वर्कर रिस्टार्ट, या उपयोगकर्ता के फिर से बटन दबाने से दोहराए जा सकते हैं।

टूटे हुए नोटिफिकेशन डिलीवरी के सामान्य कारणों में नेटवर्क टाइमआउट, प्रोवाइडर आउटेज या रेट लिमिट, गलत समय पर ऐप रिस्टार्ट, बिना यूनिक गार्ड के एक जैसा भेज लॉजिक री-रन होना, और डिज़ाइन जहाँ डेटाबेस लिखना और बाहरी भेजना एक ही संयुक्त चरण में हो, शामिल हैं।

जब लोग “विश्वसनीय नोटिफिकेशन” मांगते हैं, वे आमतौर पर इनमें से एक मतलब रखते हैं:

  • बिल्कुल एक बार डिलीवर करना, या
  • कम से कम डुप्लिकेट कभी न होना (डुप्लिकेट अक्सर देरी से भी बदतर होते हैं)।

तेज़ और पूरी तरह सुरक्षित दोनों पाना कठिन है, इसलिए आपको गति, सुरक्षा, और जटिलता के बीच ट्रेडऑफ़ चुनने पड़ते हैं।

इसीलिए ट्रिगर्स और बैकग्राउंड जॉब वर्कर्स के बीच चयन सिर्फ आर्किटेक्चर की बहस नहीं है। यह तय करता है कि कब भेजने की अनुमति है, विफलताओं को कैसे रिट्राई किया जाता है, और कुछ गलत होने पर किस तरह डुप्लिकेट ईमेल या SMS रोके जाएँ।

ट्रिगर्स और बैकग्राउंड वर्कर्स: उनका क्या अर्थ है

जब लोग ट्रिगर्स की तुलना बैकग्राउंड जॉब वर्कर्स से करते हैं, वे वास्तव में यह तुलना कर रहे होते हैं कि नोटिफिकेशन लॉजिक कहाँ चलता है और वह उसे उत्पन्न करने वाली क्रिया के कितने कड़ाई से जुड़ा है।

एक ट्रिगर का मतलब है “X होने पर अभी करें।” कई ऐप्स में इसका अर्थ है कि उपयोगकर्ता की कार्रवाई के तुरंत बाद उसी वेब रिक्वेस्ट के अंदर ईमेल या SMS भेजना। ट्रिगर्स डेटाबेस स्तर पर भी हो सकते हैं: जब कोई रो इंसर्ट या अपडेट होता है तब डेटाबेस ट्रिगर स्वचालित रूप से चलता है। दोनों प्रकार तत्काल लगते हैं, लेकिन वे उस चीज़ की समय सीमाएँ और सीमाएँ अपनाते हैं जिसने उन्हें चलाया।

एक बैकग्राउंड वर्कर का अर्थ है “जल्दी करें, पर अग्रभूमि में नहीं।” यह एक अलग प्रोसेस होता है जो कतार से जॉब्स खींचता है और उन्हें पूरा करने की कोशिश करता है। आपका मुख्य ऐप क्या होना चाहिए रिकॉर्ड कर लेता है, फिर तेज़ी से रिटर्न करता है, जबकि वर्कर धीमे और विफल-प्रवण हिस्सों जैसे ईमेल या SMS प्रोवाइडर कॉल करना संभालता है।

एक “जॉब” वह कार्य इकाई है जिसे वर्कर प्रोसेस करता है। इसमें आम तौर पर किसे सूचित करना है, कौन सा टेम्पलेट, कौन सा डेटा भरना है, वर्तमान स्थिति (queued, processing, sent, failed), कितनी कोशिशें हो चुकी हैं, और कभी-कभी शेड्यूल समय शामिल होता है।

एक सामान्य नोटिफिकेशन फ्लो ऐसा दिखता है: आप संदेश का विवरण तैयार करते हैं, जॉब enqueue करते हैं, प्रोवाइडर के जरिए भेजते हैं, परिणाम रिकॉर्ड करते हैं, फिर तय करते हैं कि रिट्राई करना है, रोकना है, या किसी को अलर्ट करना है।

ट्रांज़ैक्शन सीमाएँ: कब वास्तव में भेजना सुरक्षित है

ट्रांज़ैक्शन सीमा वह रेखा है जहाँ “हमने इसे सेव करने की कोशिश की” और “यह वाकई सेव हो गया है” के बीच फर्क होता है। जब तक डेटाबेस कमिट नहीं होता, बदलाव रोलबैक हो सकता है। यह महत्वपूर्ण है क्योंकि नोटिफिकेशन वापस लेना मुश्किल होता है।

अगर आप कमिट से पहले ईमेल या SMS भेजते हैं, तो आप किसी ऐसी घटना के बारे में संदेश भेज सकते हैं जो कभी नहीं हुई। ग्राहक को मिल सकता है “आपका पासवर्ड बदल दिया गया” या “आपका ऑर्डर कन्फर्म हो गया,” और फिर लेखन किसी constraint error या टाइमआउट के कारण फेल हो जाता है। अब उपयोगकर्ता भ्रमित है और सपोर्ट को इसे सुलझाना पड़ता है।

डेटाबेस ट्रिगर से भेजना आकर्षक लगता है क्योंकि यह डेटा बदलते ही स्वचालित रूप से चलता है। समस्या यह है कि ट्रिगर्स उसी ट्रांज़ैक्शन के अंदर चलते हैं। अगर ट्रांज़ैक्शन रोलबैक हो जाए, तो आप पहले ही किसी ईमेल या SMS प्रोवाइडर को कॉल कर चुके होंगे।

डेटाबेस ट्रिगर्स की निगरानी, टेस्ट और सुरक्षित रूप से रिट्राई करना भी कठिन होता है। और जब वे धीमी बाहरी कॉल करते हैं, तो वे लॉक लंबे समय तक पकड़ सकते हैं और डेटाबेस समस्याओं को समझना कठिन बना देते हैं।

एक सुरक्षित तरीका आउटबॉक्स विचार है: नोटिफाई करने की इच्छा को डेटा के रूप में रिकॉर्ड करें, उसे कमिट करें, फिर भेजें।

आप बिजनेस चेंज करते हैं और उसी ट्रांज़ैक्शन में एक आउटबॉक्स रो डालते हैं जो संदेश को वर्णित करता है (किसे, क्या, कौन सा चैनल, साथ में एक यूनिक की)। कमिट के बाद, एक बैकग्राउंड वर्कर पेंडिंग आउटबॉक्स रो पढ़ता है, संदेश भेजता है, और फिर उसे भेजा हुआ चिह्नित करता है।

तुरंत भेजना कम प्रभाव वाले, सूचना-उन्मुख संदेशों के लिए ठीक हो सकता है जहाँ गलत होना स्वीकार्य है, जैसे "हम आपका अनुरोध प्रोसेस कर रहे हैं।" किसी भी चीज़ के लिए जिसे अंतिम राज्य से मेल खाना जरूरी है, कमिट के बाद ही भेजें।

रिट्राइज़ और फेल्योर हैंडलिंग: किसमें क्या जीत है

रिट्राइज़ अक्सर निर्णायक फैक्टर होते हैं।

ट्रिगर्स: तेज़, पर विफलताओं पर नाजुक

अधिकांश ट्रिगर-आधारित डिज़ाइनों के पास अच्छा रिट्राई स्टोरी नहीं होती।

अगर एक ट्रिगर ईमेल/SMS प्रोवाइडर को कॉल करे और कॉल फेल हो जाए, तो आमतौर पर आपके पास दो बुरी पसंदें रहती हैं:

  • ट्रांज़ैक्शन को फेल कर देना (और मूल अपडेट को ब्लॉक कर देना), या
  • त्रुटि को निगल लेना (और नोटिफिकेशन खो जाना)।

जब विश्वसनीयता मायने रखती है तो दोनों अस्वीकार्य हैं।

ट्रिगर के अंदर लूप या डिले आज़माना चीज़ों को बदतर बना सकता है क्योंकि यह ट्रांज़ैक्शन को अधिक समय तक खुला रखेगा, लॉक समय बढ़ाएगा, और डेटाबेस को धीमा कर देगा। और अगर डेटाबेस या ऐप भेजने के बीच मर जाए, तो अक्सर आप नहीं बता पाते कि प्रोवाइडर ने अनुरोध प्राप्त किया था या नहीं।

बैकग्राउंड वर्कर्स: रिट्राइज़ के लिए डिज़ाइन किए गए

एक वर्कर भेजने को एक अलग टास्क के रूप में लेता है जिसकी अपनी स्थिति होती है। इससे यह स्वाभाविक रूप से समझ आता है कि कब रिट्राई करना चाहिए।

व्यवहारिक नियम के रूप में, आप अस्थायी विफलताओं (टाइमआउट, अस्थायी नेटवर्क समस्याएँ, सर्वर त्रुटियाँ, रेट लिमिट्स जिनमें लंबा इंतज़ार करना शामिल है) को आमतौर पर रिट्राई करते हैं। आप स्थायी समस्याओं (अमान्य फोन नंबर, ख़राब ईमेल, हार्ड रिजेक्शन जैसे अनसब्सक्राइब) को आमतौर पर रिट्राई नहीं करते। “अनजान” त्रुटियों के लिए आप प्रयासों को सीमित करते हैं और स्थिति दिखाई देती है।

बैकऑफ वह चीज़ है जो रिट्राइज़ को और बदतर होने से रोकता है। छोटी देरी से शुरू करें, फिर हर बार इसे बढ़ाएँ (उदाहरण के लिए 10s, 30s, 2m, 10m), और एक निश्चित संख्या के बाद बंद कर दें।

इसे deploys और रिस्टार्ट से बचाने के लिए, प्रत्येक जॉब के साथ retry स्टेट रिकॉर्ड करें: प्रयासों की गिनती, अगला प्रयास कब है, आखिरी त्रुटि (संक्षिप्त और पठनीय), आखिरी प्रयास का समय, और एक स्पष्ट स्थिति जैसे pending, sending, sent, failed।

अगर आपका ऐप भेजते समय रिस्टार्ट हो जाता है, तो वर्कर फंसे हुए जॉब्स (उदा. status = sending और पुराना टाइमस्टैम्प) को फिर से चेक कर सकता है और सुरक्षित रूप से रिट्राई कर सकता है। इसी जगह idempotency बहुत जरूरी होती है ताकि रिट्राई डबल-सेंड न करे।

इडेम्पोटेंसी से डुप्लिकेट ईमेल और SMS रोकना

आज ही आउटबॉक्स पैटर्न अपनाएँ
आउटबॉक्स और वर्कर फ़्लो बनाएं ताकि ईमेल और SMS केवल कमिट के बाद ही भेजे जाएँ।
AppMaster आज़माएँ

इडेम्पोटेंसी का मतलब है कि आप एक ही “नोटिफिकेशन भेजें” कार्रवाई को कई बार चला सकें और उपयोगकर्ता को केवल एक बार ही संदेश मिले।

डुप्लिकेशन का क्लासिक केस टाइमआउट है: आपका ऐप ईमेल या SMS प्रोवाइडर को कॉल करता है, अनुरोध टाइमआउट हो जाता है, और आपका कोड रिट्राई करता है। पहली रिक्वेस्ट असल में सफल हो सकती है, इसलिए रिट्राई डुप्लिकेट बना देता है।

व्यवहारिक समाधान यह है कि हर संदेश को एक स्थिर की दें और उस की को सिंगल स्रोत ऑफ ट्रूथ मानें। अच्छी की उस संदेश का अर्थ बताती है, न कि कब आपने उसे भेजने की कोशिश की।

सामान्य तरीके शामिल हैं:

  • एक जनरेट किया हुआ notification_id जो तब बनता है जब आप तय करते हैं “यह संदेश मौजूद होना चाहिए,” या
  • एक बिज़नेस-व्युत्पन्न की जैसे order_id + template + recipient (सिर्फ तब जब यह वास्तव में यूनिकनेस परिभाषित करे)।

फिर एक सेंड लेजर रखें (अकसर आउटबॉक्स तालिका खुद) और सभी रिट्राइज़ भेजने से पहले उसका परामर्श करें। स्टेट्स को सरल और दिखाई देने योग्य रखें: created (निर्धारित), queued (तैयार), sent (पुष्ट), failed (पुष्ट विफलता), canceled (अब आवश्यक नहीं)। महत्वपूर्ण नियम यह है कि आप idempotency की पर प्रति केवल एक सक्रिय रिकॉर्ड की अनुमति देते हैं।

जब प्रोवाइडर तरफ़ idempotency सपोर्ट करे तो वह मदद कर सकता है, पर यह आपकी अपनी लेजर की जगह नहीं लेता। आपको अभी भी अपने रिट्राइज़, डिप्लॉयमेंट, और वर्कर रिस्टार्ट्स को संभालना होगा।

“अज्ञात” परिणामों को भी प्रथम श्रेणी की तरह ट्रीट करें। अगर अनुरोध टाइमआउट हुआ, तो तुरंत दोबारा न भेजें। उसे पुष्टि-पेंडिंग के रूप में चिह्नित करें और प्रोवाइडर डिलीवरी स्टेटस बतौर संभव जांचें। अगर पुष्टि नहीं मिलती, तो डिले करें और अलर्ट करें बजाय डबल-सेंड करने के।

एक सुरक्षित डिफ़ॉल्ट पैटर्न: आउटबॉक्स + बैकग्राउंड वर्कर (कदम दर कदम)

अगर आप एक सुरक्षित डिफ़ॉल्ट चाहते हैं, तो आउटबॉक्स पैटर्न प्लस वर्कर सबसे अच्छा विकल्प है। यह भेजने को आपके बिजनेस ट्रांज़ैक्शन के बाहर रखता है, साथ ही यह सुनिश्चित करता है कि नोटिफिकेशन का इरादा सेव हुआ है।

फ्लो

“नोटिफिकेशन भेजना” को एक डेटा के रूप में ट्रिट करें, एक कार्रवाई के रूप में नहीं।

आप बिजनेस चेंज (उदा. ऑर्डर स्टेटस अपडेट) अपने सामान्य टेबल्स में सेव करते हैं। उसी डेटाबेस ट्रांज़ैक्शन में, आप एक आउटबॉक्स रिकॉर्ड जोड़ते हैं जिसमें रिसिपिएंट, चैनल (ईमेल/SMS), टेम्पलेट, पेलोड, और एक idempotency की होता है। आप ट्रांज़ैक्शन कमिट करते हैं। केवल इस बिंदु के बाद कोई भी चीज़ भेजी जा सकती है।

एक बैकग्राउंड वर्कर नियमित रूप से पेंडिंग आउटबॉक्स रो उठाता है, उन्हें भेजता है, और परिणाम रिकॉर्ड करता है।

दो वर्कर्स द्वारा एक ही रो को पकड़ने से रोकने के लिए एक सरल क्लेमिंग स्टेप जोड़ें। यह processing को स्थिति बदलकर या एक लॉक्ड टाइमस्टैम्प के रूप में हो सकता है।

डुप्लिकेट रोकना और विफलताओं को संभालना

डुप्लिकेट अक्सर तब होते हैं जब एक भेज सफल हो गया पर आपका ऐप "sent" रिकॉर्ड करने से पहले क्रैश हो गया। आप इसे इसलिए सुलझाते हैं कि “mark sent” लिखने को दोहराना सुरक्षित बनाते हैं।

एक यूनिकनेस नियम का उपयोग करें (उदा. idempotency की और चैनल पर यूनिक कॉन्स्ट्रेंट)। स्पष्ट नियमों के साथ रिट्राई करें: सीमित प्रयास, बढ़ती देरी, और केवल रिट्राइएबल त्रुटियों के लिए। आख़िरी रिट्राई के बाद जॉब को dead-letter स्थिति में ले जाएँ (जैसे failed_permanent) ताकि कोई इंसान उसे देख कर मैन्युअली फिर से प्रोसेस कर सके।

मॉनिटरिंग सरल रखी जा सकती है: pending, processing, sent, retrying, और failed_permanent की गिनती, और सबसे पुराना pending टाइमस्टैम्प।

ठोस उदाहरण: जब एक ऑर्डर “Packed” से “Shipped” में बदलता है, तो आप ऑर्डर रो अपडेट करते हैं और order-4815-shipped जैसे idempotency की के साथ एक आउटबॉक्स रो बनाते हैं। भले ही वर्कर भेजते समय क्रैश हो, फिर से चलाने पर डबल-सेंड नहीं होगा क्योंकि “sent” लिखना उस यूनिक की से संरक्षित है।

कब बैकग्राउंड वर्कर्स बेहतर विकल्प हैं

समर्थन को स्पष्ट टूल दें
विफल संदेशों की समीक्षा के लिए एडमिन व्यू जोड़ें और सुरक्षित रीप्रोसेस करें।
ऐप बनाएं

डेटाबेस ट्रिगर्स डेटा बदलते ही तुरंत प्रतिक्रिया देने में अच्छे हैं। लेकिन अगर जॉब का लक्ष्य "असली दुनिया की गड़बड़ परिस्थितियों में विश्वसनीय रूप से नोटिफाई करना" है, तो बैकग्राउंड वर्कर्स आमतौर पर अधिक नियंत्रण देते हैं।

वॉर्मर्स उन अवस्थाओं में बेहतर मेल खाते हैं जब आपको समय-आधारित भेज (रिमाइंडर, डाइजेस्ट), उच्च वॉल्यूम जिनमें रेट लिमिट और बैकप्रेशर हैं, प्रोवाइडर विविधता सहन करने की ज़रूरत (429 लिमिट, धीमी प्रतिक्रियाएँ, छोटे आउटेज), मल्टी-स्टेप वर्कफ़्लोज़ (भेजना, डिलीवरी प्रतीक्षा, फिर फ़ॉलो-अप), या क्रॉस-सिस्टम इवेंट्स जो reconciliation मांगते हैं।

सरल उदाहरण: आप ग्राहक से चार्ज करते हैं, फिर SMS रसीद भेजते हैं, फिर ईमेल इनवॉइस। अगर SMS किसी गेटवे समस्या के कारण फेल हो जाता है, तब भी आप चाहते हैं कि ऑर्डर का भुगतान कायम रहे और आप बाद में सुरक्षित रिट्राई करना चाहें। उस लॉजिक को ट्रिगर में डालने से “डेटा सही है” और “तीसरा पक्ष अभी उपलब्ध है” को मिला देना जोखिम भरा बनता है।

बैकग्राउंड वर्कर्स ऑपरेशनल नियंत्रण भी आसान बनाते हैं। आप किसी घटना के दौरान कतार को रोक सकते हैं, विफलताओं की जाँच कर सकते हैं, और देरी के साथ रीट्राई कर सकते हैं।

अक्सर की जाने वाली गलतियाँ जो संदेश छूटने या डुप्लीकेट होने का कारण बनती हैं

सेंड्स को देखने योग्य बनाएं
नोटिफिकेशन जॉब्स को PostgreSQL में मॉडल करें और retries व स्टेटस दिखाई दें।
निर्माण शुरू करें

सबसे तेज़ तरीका असंगत नोटिफिकेशन पाने का यह है कि “जहाँ सुविधाजनक लगे वहां सिर्फ भेज दो” और आशा करो कि रिट्राइज़ आपको बचा लेंगे। चाहे आप ट्रिगर्स का उपयोग करें या वर्कर्स का, विफलता और स्टेट के आसपास के विवरण तय करते हैं कि उपयोगकर्ताओं को एक संदेश मिलेगा, दो मिलेंगे, या कोई नहीं मिलेगा।

एक आम जाल डेटाबेस ट्रिगर से भेजना और मान लेना है कि यह फेल नहीं होगा। ट्रिगर्स उसी ट्रांज़ैक्शन के अंदर चलते हैं, इसलिए कोई धीमा प्रोवाइडर कॉल लिखने को अधिक समय तक रोक सकता है, टाइमआउट में फंस सकता है, या तालिकाओं को अपेक्षा से अधिक लॉक कर सकता है। और बदतर यह कि अगर भेज फेल हो और आप ट्रांज़ैक्शन रोलबैक कर दें, तो बाद में आप दो बार भेज सकते हैं अगर प्रोवाइडर ने पहली कॉल स्वीकार कर ली थी।

बार-बार दिखने वाली गलतियाँ:

  • सभी चीज़ों को एक ही तरह से रिट्राई करना, स्थायी त्रुटियों सहित (खराब ईमेल, ब्लॉक्ड नंबर)।
  • queued और sent को अलग न रखना, इसलिए क्रैश के बाद यह पता नहीं चलता कि क्या सुरक्षित रूप से रिट्राई किया जा सकता है।
  • डुप्लिकेट-रोकी के रूप में timestamps का उपयोग करना, जिससे retries आसानी से यूनिकनेस को बायपास कर देते हैं।
  • प्रोवाइडर कॉल्स को उपयोगकर्ता रिक्वेस्ट पथ में करना (चेकआउट और फ़ॉर्म सबमिट गेटवे पर न रुकें)।
  • प्रोवाइडर टाइमआउट को “डिलीवर नहीं हुआ” मानना, जबकि कई बार वह वास्तव में “अज्ञात” होता है।

सरल उदाहरण: आप एक SMS भेजते हैं, प्रोवाइडर टाइमआउट हो जाता है, और आप रिट्राई करते हैं। अगर पहली रिक्वेस्ट असल में सफल थी, तो उपयोगकर्ता को दो कोड मिल जाते हैं। समाधान यह है कि एक स्थिर idempotency की (जैसे notification_id) रिकॉर्ड करें, भेजने से पहले संदेश को queued के रूप में चिन्हित करें, फिर स्पष्ट सफलता प्रतिक्रिया मिलने पर ही उसे sent के रूप में चिह्नित करें।

भेजने से पहले तेज़ जाँचें

अधिकांश नोटिफिकेशन बग टूल की समस्या नहीं हैं। वे समय, रिट्राइज़, और गायब रिकॉर्ड्स से जुड़े होते हैं।

पुष्टि करें कि आप केवल डेटाबेस लेखन के सुरक्षित रूप से कमिट होने के बाद ही भेजते हैं। यदि आप उसी ट्रांज़ैक्शन के दौरान भेजते हैं और वह बाद में रोलबैक हो जाता है, तो उपयोगकर्ताओं को ऐसी घटना के बारे में संदेश मिल सकता है जो कभी हुई ही नहीं।

इसके बाद, हर नोटिफिकेशन को यूनिक रूप से पहचानने योग्य बनाएं। प्रत्येक संदेश को एक स्थिर idempotency की दें (उदाहरण के लिए order_id + event_type + channel) और स्टोरेज में इसे लागू करें ताकि रिट्राई एक दूसरा “नया” नोटिफिकेशन न बना सके।

रिलीज़ से पहले इन मूल बातों की जाँच करें:

  • भेजना कमिट के बाद ही होता है, लेखन के दौरान नहीं।
  • प्रत्येक नोटिफिकेशन के पास एक यूनिक idempotency की है, और डुप्लिकेट अस्वीकार किए जाते हैं।
  • रिट्राइज़ सुरक्षित हैं: सिस्टम वही जॉब फिर से चला सके और अधिकतम एक बार ही भेजे।
  • हर प्रयास रिकॉर्ड होता है (status, last_error, timestamps)।
  • प्रयासों की सीमा है, और फंसे हुए आइटमों के पास स्पष्ट समीक्षा और रीप्रोसेस स्थान है।

रिस्टार्ट व्यवहार को जानबूझकर टेस्ट करें। वर्कर को भेजते समय मार डालें, फिर रीस्टार्ट करें, और सत्यापित करें कि कुछ भी डबल-भेजा नहीं गया। डेटाबेस लोड के दौरान भी यही करें।

सत्यापित करने के लिए एक सरल परिदृश्य: उपयोगकर्ता अपना फोन नंबर बदलता है, फिर आप SMS वेरिफिकेशन भेजते हैं। अगर SMS प्रोवाइडर टाइमआउट दे देता है, तो आपके पास अच्छी idempotency की और प्रयास लॉग होने पर या तो आप एक बार भेजते हैं या सुरक्षित रूप से बाद में फिर से कोशिश करते हैं, पर स्पैम नहीं करते।

उदाहरण परिदृश्य: ऑर्डर अपडेट बिना डबल-सेंड के

हर प्रयास ट्रैक करें
pending, processing, sent, और failed जैसी स्टेटस के साथ एक सरल लेजर बनाएं।
अब शुरू करें

एक स्टोर दो प्रकार के संदेश भेजता है: (1) भुगतान के तुरंत बाद ऑर्डर कन्फर्मेशन ईमेल, और (2) जब पैकेज "Out for delivery" और "Delivered" होता है तो SMS अपडेट्स।

यहाँ गलत क्या होता है जब आप बहुत जल्दी भेजते हैं (उदा. डेटाबेस ट्रिगर के अंदर): भुगतान चरण orders रो लिखता है, ट्रिगर ग्राहक को ईमेल फायर कर देता है, और फिर भुगतान कैप्चर कुछ सेकंड बाद फेल हो जाता है। अब आपके पास "Thanks for your order" ईमेल है उस ऑर्डर के लिए जो असल में बन ही नहीं पाया।

उल्टा समस्या यह है: डिलीवरी स्टेटस "Out for delivery" पर बदलता है, आप अपने SMS प्रोवाइडर को कॉल करते हैं, और प्रोवाइडर टाइमआउट हो जाता है। आपको पता नहीं कि संदेश भेजा गया या नहीं। अगर आप तुरंत रिट्राई करते हैं, तो आप दो SMS भेजने का जोखिम लेते हैं। अगर आप रिट्राई नहीं करते, तो संदेश न भेजने का जोखिम रहता है।

एक सुरक्षित फ्लो आउटबॉक्स रिकॉर्ड + बैकग्राउंड वर्कर का उपयोग करता है। ऐप ऑर्डर या स्टेटस चेंज को कमिट करता है, और उसी ट्रांज़ैक्शन में "send template X to user Y, channel SMS, idempotency key Z" जैसी एक आउटबॉक्स रो लिखता है। केवल कमिट के बाद वर्कर संदेश डिलीवर करता है।

सरल टाइमलाइन इस तरह दिखती है:

  • भुगतान सफल होता है, ट्रांज़ैक्शन कमिट होता है, और कन्फर्मेशन ईमेल के लिए आउटबॉक्स रो सेव हो जाती है।
  • वर्कर ईमेल भेजता है, फिर आउटबॉक्स को provider message ID के साथ sent के रूप में चिह्नित करता है।
  • डिलीवरी स्टेटस बदलता है, ट्रांज़ैक्शन कमिट होता है, और SMS अपडेट के लिए एक आउटबॉक्स रो सेव हो जाती है।
  • प्रोवाइडर टाइमआउट देता है, वर्कर आउटबॉक्स को retryable मार्क करता है और बाद में उसी idempotency की का उपयोग करते हुए फिर से कोशिश करता है।

रिट्राई पर, आउटबॉक्स रो सिंगल स्रोत ऑफ ट्रूथ होती है। आप नया "send" अनुरोध नहीं बना रहे होते, आप पहले वाले को पूरा कर रहे होते हैं।

सपोर्ट के लिए भी यह स्पष्ट होता है। वे देख सकते हैं कि संदेश "failed" में फंसा है और आखिरी त्रुटि (टाइमआउट, खराब फोन नंबर, ब्लॉक्ड ईमेल), कितनी कोशिशें की गईं, और क्या यह सुरक्षित है बिना डबल-सेंड किए रीट्राइ करने के।

अगले कदम: एक पैटर्न चुनें और उसे साफ़-सुथरे तरीके से लागू करें

एक डिफ़ॉल्ट चुनें और उसे लिखकर रखें। असंगत व्यवहार आमतौर पर तब आता है जब ट्रिगर्स और वर्कर्स को बेतरतीब ढंग से मिलाया जाता है।

छोटा शुरू करें: एक आउटबॉक्स तालिका और एक वर्कर लूप बनाएं। पहला लक्ष्य गति नहीं बल्कि सच्चाई होनी चाहिए: जो आप भेजने का इरादा रखते हैं उसे स्टोर करना, कमिट के बाद भेजना, और केवल प्रोवाइडर के पुष्टिकरण पर उसे sent के रूप में चिह्नित करना।

सरल रोलआउट प्लान:

  • घटनाओं को परिभाषित करें (order_paid, ticket_assigned) और कौन से चैनल उपयोग कर सकते हैं।
  • एक आउटबॉक्स तालिका जोड़ें जिसमें event_id, recipient, payload, status, attempts, next_retry_at, sent_at हो।
  • एक वर्कर बनाएं जो pending रो पोल करे, भेजे, और स्टेटस एक ही जगह अपडेट करे।
  • हर संदेश के लिए यूनिक idempotency की और "अगर पहले से भेजा जा चुका है तो कुछ न करें" नियम जोड़ें।
  • त्रुटियों को retryable (टाइमआउट, 5xx) और not retryable (खराब नंबर, ब्लॉक्ड ईमेल) में विभाजित करें।

वॉल्यूम बढ़ाने से पहले बुनियादी दृश्यता जोड़ें। pending काउंट, फेल्योर रेट, और सबसे पुराने pending संदेश की आयु ट्रैक करें। अगर सबसे पुराना pending लगातार बढ़ता जा रहा है, तो संभवतः वर्कर फंसा हुआ है, प्रोवाइडर आउटेज है, या लॉजिक में बग है।

यदि आप AppMaster (appmaster.io) में बना रहे हैं, तो यह पैटर्न साफ़-सुथरे ढंग से मैप होगा: Data Designer में आउटबॉक्स मॉडल करें, बिजनेस चेंज और आउटबॉक्स रो को एक ट्रांज़ैक्शन में लिखें, फिर send-and-retry लॉजिक एक अलग बैकग्राउंड प्रोसेस में चलाएँ। यही अलगाव होता है जो नोटिफिकेशन डिलीवरी को विश्वसनीय रखता है भले ही प्रोवाइडर या डिप्लॉयमेंट में गड़बड़ी हो।

सामान्य प्रश्न

क्या मुझे नोटिफिकेशन के लिए ट्रिगर्स या बैकग्राउंड वर्कर्स का प्रयोग करना चाहिए?

Background workers आमतौर पर सुरक्षित डिफ़ॉल्ट होते हैं क्योंकि भेजना धीमा और विफल-प्रवण होता है, और वर्कर्स retries व दृश्यता के लिए बने होते हैं। ट्रिगर्स तेज़ हो सकते हैं, लेकिन वे उसी ट्रांज़ैक्शन या रिक्वेस्ट से सख्ती से जुड़ जाते हैं, जिससे विफलताओं और डुप्लिकेट्स को साफ़-सुथरे ढंग से संभालना मुश्किल होता है।

डेटाबेस कमिट से पहले नोटिफिकेशन भेजना जोखिम भरा क्यों है?

यह ख़तरनाक है क्योंकि डेटाबेस का लेखन अभी भी रोलबैक हो सकता है। आप उपयोगकर्ताओं को ऐसे ऑर्डर, पासवर्ड बदलाव, या भुगतान के बारे में सूचित कर सकते हैं जो वास्तव में कमिट नहीं हुए, और एक बार ईमेल या SMS निकल जाने के बाद आप उसे वापस नहीं ले सकते।

डेटाबेस ट्रिगर से भेजने में सबसे बड़ी समस्या क्या है?

एक डेटाबेस ट्रिगर उसी ट्रांज़ैक्शन के अंदर चलता है जो रो चेंज करता है। अगर वह किसी ईमेल/SMS प्रोवाइडर को कॉल करे और ट्रांज़ैक्शन बाद में फेल हो जाए, तो आपने किसी ऐसे बदलाव के बारे में असल संदेश भेज दिया हो सकता है जो टिक नहीं पाया, या धीमा बाहरी कॉल होने से ट्रांज़ैक्शन अटक सकता है।

साधारण शब्दों में आउटबॉक्स पैटर्न क्या है?

आउटबॉक्स पैटर्न उस इरादे को एक रो के रूप में डेटाबेस में स्टोर करता है कि संदेश भेजना है, और यह वही ट्रांज़ैक्शन में किया जाता है जिसमें बिजनेस चेंज होता है। कमिट के बाद, एक वर्कर पेंडिंग आउटबॉक्स रो पढ़ता है, संदेश भेजता है और उसे भेजा हुआ चिह्नित कर देता है—इससे टाइमिंग और रिट्राई सुरक्षित होते हैं।

जब ईमेल/SMS प्रोवाइडर का अनुरोध टाइमआउट हो तो मुझे क्या करना चाहिए?

जब प्रोवाइडर टाइमआउट दे तो वास्तविक नतीजा अक्सर “अज्ञात” होता है, न कि स्पष्ट रूप से “असफल”। एक अच्छा सिस्टम प्रयास रिकॉर्ड करता है, देरी और रिट्राई सुरक्षित रूप से करता है और उसी संदेश पहचान का उपयोग करके दोबारा भेजने के बजाय पुष्टि की कोशिश करता है।

रिट्राई होने पर डुप्लिकेट ईमेल या SMS कैसे रोका जाए?

इडेम्पोटेंसी का उपयोग करें: प्रत्येक नोटिफिकेशन को एक स्थिर की दें जो संदेश का अर्थ दर्शाती हो (न कि कब भेजा गया)। उस की को एक लेजर (अकसर आउटबॉक्स टेबल) में स्टोर करें और एक ही की के लिए केवल एक सक्रिय रिकॉर्ड की अनुमति दें, ताकि retries पहले वाले संदेश को पूरा करें न कि नया बनाएं।

कौन सी त्रुटियों को रिट्राई करना चाहिए और किसे स्थायी मानना चाहिए?

टेम्पररी त्रुटियों जैसे टाइमआउट, 5xx या रेट लिमिट्स को रिट्राई करें (उचित देरी के साथ)। स्थायी त्रुटियों जैसे अवैध पते, ब्लॉक्ड नंबर या हार्ड बाउंस को रिट्राई न करें; उन्हें फेल मार्क करें और दिखाई देने योग्य रखें ताकि डेटा ठीक किया जा सके।

वॉकर्स रिस्टार्ट्स या क्रैश के बीच भेजते समय कैसे संभालते हैं?

एक बैकग्राउंड वर्कर sending स्थिति में लंबे समय से फंसे जॉब्स को स्कैन कर सकता है, उन्हें फिर से retryable कर सकता है और बैकऑफ के साथ दोबारा कोशिश कर सकता है। यह तभी सुरक्षित है जब हर जॉब का स्टेट रिकॉर्डेड हो (प्रयासों की गिनती, टाइमस्टैम्प, आखिरी त्रुटि) और idempotency डबल-सेंड को रोकती हो।

नोटिफिकेशन डिलीवरी को दिखाई देने योग्य बनाने के लिए मुझे किस जॉब डेटा की आवश्यकता है?

इसका मतलब है कि आप यह उत्तर दे सकें कि "क्या इसे सुरक्षित रूप से फिर से भेजा जा सकता है?" स्पष्ट स्टेटस जैसे pending, processing, sent, और failed, साथ में प्रयासों की गिनती और आखिरी त्रुटि स्टोर करें। इससे सपोर्ट और डिबगिंग आसान होती है और सिस्टम अनुमान लगाने के बजाय सुरक्षित रूप से रिकवर कर सकता है।

मैं यह पैटर्न AppMaster में कैसे लागू करूँ?

Data Designer में एक आउटबॉक्स टेबल मॉडल करें, बिजनेस अपडेट और आउटबॉक्स रो को एक ही ट्रांज़ैक्शन में लिखें, और फिर अलग बैकग्राउंड प्रोसेस में send-and-retry लॉजिक चलाएँ। हर संदेश के लिए एक idempotency की रखें और प्रयास रिकॉर्ड करें, ताकि deploys, retries और वर्कर रिस्टार्ट से डुप्लिकेट न हों।

शुरू करना आसान
कुछ बनाएं अद्भुत

फ्री प्लान के साथ ऐपमास्टर के साथ प्रयोग करें।
जब आप तैयार होंगे तब आप उचित सदस्यता चुन सकते हैं।

शुरू हो जाओ