एक भरोसेमंद email api integration ज़्यादातर इस बात पर निर्भर करता है कि कुछ गलत होने पर क्या होता है। सामान्य स्थिति में बस एक HTTP रिक्वेस्ट होती है। कठिन स्थितियां हैं: ऐसा timeout जहां आपको पता नहीं कि संदेश स्वीकार हुआ या नहीं, batch के बीच में क्रैश होने वाला worker, और ऐसा retry लूप जो छोटे आउटेज को बाढ़ में बदल देता है। यह गाइड हमें पैटर्न का एक सेट देती है, जो हर ईमेल को ठीक एक बार, या कम से कम अनुमानित रूप से एक बार, भेजना सुनिश्चित करता है, चाहे आप किसी भी प्रोवाइडर endpoint को कॉल करें।
हम इन पैटर्न को सामान्य रूप में बताते हैं ताकि ये MigoSMTP सहित किसी भी HTTP email API के साथ काम करें। जहां कोई विशेष फ़ीचर मायने रखता है, जैसे idempotency header, batch endpoint या rate limit, वहां मान लेने के बजाय सटीक विवरण के लिए अपना MigoSMTP अकाउंट या API डॉक्यूमेंटेशन देखें। अगर आप अभी प्रोटोकॉल चुन रहे हैं, तो पहले SMTP या API: कैसे चुनें पढ़ें।
ईमेल सेंड उन तरीकों से क्यों फ़ेल होते हैं जिनसे अन्य API कॉल नहीं होते
ज़्यादातर API कॉल बिना नुकसान दोहराई जा सकती हैं। ईमेल नहीं। यूज़र बनाने की दूसरी कॉल आमतौर पर conflict के साथ फ़ेल होती है, लेकिन रसीद भेजने की दूसरी कॉल आराम से दूसरी रसीद भेज देती है। ग्राहक को दो संदेश दिखते हैं, सपोर्ट को शिकायत मिलती है, और दो बार भेजा गया पासवर्ड रीसेट उस कोड को अमान्य कर सकता है जिसे व्यक्ति पढ़ रहा है।
मूल समस्या अज्ञात परिणाम है। आपका क्लाइंट रिक्वेस्ट भेजता है, सर्वर उसे स्वीकार करता है, और जवाब नेटवर्क ड्रॉप या timeout में खो जाता है। आपकी ओर से यह उस रिक्वेस्ट जैसा दिखता है जो कभी पहुंची ही नहीं। अगर आप retry करते हैं, तो डुप्लिकेट हो सकता है। अगर नहीं करते, तो संदेश खो सकता है। नीचे का हर पैटर्न इसी अस्पष्टता को सुलझाने के लिए है।
| फ़ेल्योर | आपको क्या पता है | सीधे-सादे तरीके से संभालने पर जोखिम |
|---|---|---|
| Connection refused या DNS फ़ेल्योर | रिक्वेस्ट API तक कभी पहुंची ही नहीं | retry सुरक्षित है; जोखिम केवल retry storm का है |
| रिक्वेस्ट भेजने के बाद read timeout | स्वीकार होने के बारे में कुछ नहीं | dedupe key के बिना retry से डबल सेंड हो सकता है |
| 429 Too Many Requests | API ने गति नियंत्रण के कारण कॉल अस्वीकार की | तुरंत retry करने से दबाव और बढ़ता है |
| API या gateway से 5xx | आमतौर पर स्वीकार नहीं हुआ, पर गारंटी नहीं | कुछ मामलों में timeout जैसी ही अस्पष्टता |
| 400 या 422 validation एरर | रिक्वेस्ट ही गलत है | हमेशा retry करने से क्षमता बर्बाद होती है और बग छिप जाता है |
| API कॉल के बाद worker क्रैश | स्वीकार हो गया, पर आपने रिकॉर्ड नहीं किया | जॉब दोबारा डिलीवर होती है और ईमेल फिर से भेजा जाता है |
Idempotency keys और डुप्लिकेट-सेंड की रोकथाम
एक idempotent ऑपरेशन चाहे जितनी बार करें, वही परिणाम देता है। कई HTTP API आपको idempotency key देने देते हैं, आमतौर पर header में, ताकि उसी key वाली दोहराई गई रिक्वेस्ट नया रिसोर्स बनाने के बजाय मूल परिणाम लौटाए। IETF के पास Idempotency-Key header field के मानक के लिए एक सक्रिय ड्राफ़्ट है, जो दिखाता है कि यह विचार कितना व्यापक है। MigoSMTP ऐसे header को सपोर्ट करता है या नहीं, और key कितनी देर याद रखी जाती है, इसकी पुष्टि MigoSMTP API डॉक्यूमेंटेशन में करनी होगी। हम यहां इसे मानकर नहीं चलते।
अच्छी बात यह है कि आपको सर्वर सपोर्ट का इंतज़ार नहीं करना है। आप dedupe टेबल से अपनी ओर वही गारंटी बना सकते हैं। मुख्य विचार यह है कि आपकी एप्लिकेशन नेटवर्क से बात करने से पहले ही ईमेल की पहचान तय कर लेती है।
निर्धारक dedupe key चुनें
अच्छी key बिज़नेस इवेंट से बनती है, सेंड के समय जनरेट नहीं होती। order-1042-shipped या user-77-password-reset-2026-10-15T09:14 जैसा कुछ इस्तेमाल करें। worker के अंदर बनाया गया रैंडम UUID मदद नहीं करता, क्योंकि दोबारा डिलीवर हुई जॉब नया UUID बना देगी। संदेश का प्रकार शामिल करें ताकि एक ही ऑर्डर की रसीद और शिपिंग सूचना आपस में न टकराएं, और समय-बकेट या काउंटर केवल तब जोड़ें जब बार-बार भेजना वैध हो, जैसे दैनिक digest।
इसे unique constraint से लागू करें
"पहले देखो, फिर डालो" जैसी एप्लिकेशन जांचें race condition वाली होती हैं। key पर unique index लगाएं और टकराव का फ़ैसला डेटाबेस को करने दें। पहला लिखने वाला जीतता है और send रिकॉर्ड बनाता है। बाद का कोई भी प्रयास या तो constraint एरर पाता है या मौजूदा पंक्ति ढूंढकर उसकी स्थिति पढ़ता है।
CREATE TABLE email_sends (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
dedupe_key VARCHAR(190) NOT NULL,
to_address VARCHAR(320) NOT NULL,
template VARCHAR(80) NOT NULL,
payload_json JSON NOT NULL,
status VARCHAR(20) NOT NULL DEFAULT 'pending',
attempts INT NOT NULL DEFAULT 0,
next_try_at DATETIME NULL,
message_id VARCHAR(255) NULL,
last_error TEXT NULL,
created_at DATETIME NOT NULL,
updated_at DATETIME NOT NULL,
UNIQUE KEY uq_dedupe (dedupe_key)
);
हम जो स्थितियां इस्तेमाल करते हैं वे हैं pending, sending, accepted, failed और unknown। Accepted का मतलब है कि API ने सफलता लौटाई और आपने message ID सहेज ली। Unknown का मतलब है कि कॉल की गई थी और परिणाम निश्चित नहीं है, जिसे हम retry अनुभाग में देखेंगे। अगर API idempotency key सपोर्ट करता है, तो वही dedupe key वहां भी भेजें, ताकि दोनों परतें सहमत रहें। सर्वर-साइड सपोर्ट को दूसरा सुरक्षा जाल मानें, विकल्प नहीं।
Outbox पैटर्न: केवल वही भेजें जो कमिट हुआ हो
क्लासिक बग ऐसा दिखता है: आपका कोड ऑर्डर सहेजता है, फिर ईमेल API को कॉल करता है, फिर लौट जाता है। अगर API कॉल फ़ेल हो जाए, तो ऑर्डर मौजूद है पर ईमेल नहीं गया। अगर आप पहले API कॉल करें और डेटाबेस कमिट फ़ेल हो जाए, तो ग्राहक को ऐसे ऑर्डर की पुष्टि मिल जाती है जो मौजूद ही नहीं है। दो सिस्टम एक ट्रांज़ैक्शन में अपडेट नहीं हो सकते, इसलिए हम समस्या को ही बदल देते हैं।
Outbox पैटर्न में ईमेल रिक्वेस्ट बस एक और पंक्ति होती है जो बिज़नेस बदलाव वाले ट्रांज़ैक्शन में ही लिखी जाती है। या तो दोनों कमिट होते हैं या कोई नहीं। फिर एक अलग प्रोसेस outbox पंक्तियों को API तक पहुंचाती है। डिलीवरी एक retry योग्य बैकग्राउंड जॉब बन जाती है, और डेटाबेस इस बात का एकमात्र स्रोत बन जाता है कि क्या भेजा जाना है।
- ट्रांज़ैक्शन: ऑर्डर और email_sends पंक्ति को एक साथ, pending स्थिति के साथ डालें।
- कमिट: कमिट के बाद ही कुछ भी सेंडर प्रोसेस को दिखाई देता है।
- Claim: एक worker वे pending पंक्तियां चुनता है जिनका समय आ गया है और row locking का उपयोग करके उन्हें sending चिह्नित करता है, ताकि दो workers कभी एक ही पंक्ति न लें।
- API कॉल करें: रिक्वेस्ट भेजें, फिर परिणाम और message ID पंक्ति पर दर्ज करें।
- Reconcile: एक आवधिक जॉब sending या unknown में अटकी पंक्तियों को ढूंढकर सुलझाती है।
आप worker को हर कुछ सेकंड में टेबल poll करके फ़ीड कर सकते हैं, या कमिट के बाद queue में पब्लिश करके। Polling सरल है और इसमें कम हिस्से होते हैं। ज़्यादा वॉल्यूम पर queue मदद करती है, लेकिन टेबल को सच्चाई का रिकॉर्ड बनाए रखें, क्योंकि queues संदेश दोबारा डिलीवर या ड्रॉप कर सकती हैं और टेबल आपको पूछने देती है कि इस ईमेल का क्या हुआ।
आपके ऐप में Queueing: workers, concurrency और क्रम
जब तक कोई मज़बूत कारण न हो, वेब रिक्वेस्ट के अंदर ईमेल API को कॉल न करें। तब किसी भी थर्ड पार्टी का धीमा जवाब आपके धीमे चेकआउट में बदल जाता है, और साइन-अप में उछाल खुले कनेक्शनों में उछाल बन जाता है। outbox पंक्ति कमिट होते ही यूज़र को लौटा दें, और डिलीवरी बैकग्राउंड workers पर छोड़ दें।
- Concurrency सीमित रखें। तय संख्या के workers, हर एक के पास छोटा HTTP connection pool, प्रति सेकंड रिक्वेस्ट की अनुमानित ऊपरी सीमा देते हैं। असीमित parallelism ही वह तरीका है जिससे ऐप अनजाने में API सीमा तक पहुंच जाता है।
- Queues को urgency के अनुसार अलग करें। पासवर्ड रीसेट और one-time codes को कभी न्यूज़लेटर के पीछे इंतज़ार नहीं करना चाहिए। अलग queues या प्राथमिकताएं इस्तेमाल करें, जैसा ट्रांज़ैक्शनल और मार्केटिंग स्ट्रीम को अलग करने में बताया गया है।
- जॉब्स को idempotent बनाएं। मानकर चलें कि हर जॉब दो बार डिलीवर हो सकती है। जॉब का पहला कदम पंक्ति लोड करना है, और अगर स्थिति पहले से accepted है तो बाहर निकल जाना है।
- क्रम पर भरोसा न करें। Retries और parallel workers संदेशों का क्रम बदल देते हैं। अगर क्रम मायने रखता है, जैसे welcome से पहले verification, तो उसे queue के क्रम में नहीं, अपने डेटा में dependency के रूप में मॉडल करें।
- पुराने मेल को expire करें। बीस मिनट बाद पहुंचा लॉगिन कोड न होने से भी बुरा है। समय-संवेदनशील पंक्तियों को expiry दें और भेजने के बजाय उन्हें expired चिह्नित करें।
Payload छोटे रखें। टेम्पलेट का नाम और वेरिएबल, या अंतिम रेंडर किया हुआ कंटेंट सहेजें, लेकिन queue संदेशों के अंदर बड़े attachments नहीं। सहेजी गई फ़ाइलों को ID से संदर्भित करें, जैसा हमारी size limits और MIME गाइड में बताई सीमाओं के अनुरूप हो।
Timeouts और retry वर्गीकरण
हर HTTP कॉल को स्पष्ट timeouts चाहिए। बिना timeout वाला क्लाइंट किसी इंसान की परवाह से कहीं ज़्यादा देर इंतज़ार करेगा, और पूरे समय worker स्लॉट को रोके रखेगा। छोटा connect timeout रखें, अक्सर एक या दो सेकंड, और ऐसा कुल timeout जो सामान्य जवाब के लिए पर्याप्त लंबा हो पर जल्दी फ़ेल होने के लिए पर्याप्त छोटा। मान अपनी मापी हुई latency और अपने MigoSMTP API डॉक्यूमेंटेशन के मार्गदर्शन से चुनें, किसी ब्लॉग पोस्ट की संख्या से नहीं।
कॉल खत्म होने पर परिणाम को तीन बकेट में से एक में रखें। यह वर्गीकरण इंटीग्रेशन का मूल है।
| परिणाम | उदाहरण | कार्रवाई |
|---|---|---|
| स्वीकृत | message ID के साथ 2xx | ID सहेजें, accepted चिह्नित करें, रुक जाएं |
| निश्चित रूप से अस्वीकृत, retry न करें | 400, 401, 403, 404, 422, आपकी रिक्वेस्ट का वर्णन करने वाले अन्य 4xx | failed चिह्नित करें, एरर body दर्ज करें, दोहराव पर अलर्ट दें; कोड, क्रेडेंशियल या डेटा ठीक करें |
| बाद में retry करें, वही dedupe key | 429, 500, 502, 503, 504, connect एरर, DNS एरर | Back off करके retry करें; Retry-After का पालन करें |
| अज्ञात परिणाम | Read timeout, भेजने के बाद connection reset | केवल उसी dedupe key के साथ retry करें, या पहले reconcile करें |
इन कोड के लिए HTTP semantics, जिनमें Retry-After header भी शामिल है, RFC 9110 में परिभाषित हैं, और RFC 6585 429 को परिभाषित करता है। SMTP से अंतर पर ध्यान दें। SMTP में 4xx उत्तर अस्थायी और 5xx स्थायी होते हैं, जिसे हम SMTP bounce codes की अपनी गाइड में बताते हैं। HTTP API में भूमिकाएं अलग हैं: 5xx का मतलब सर्विस में समस्या थी और 4xx का मतलब आपकी रिक्वेस्ट में समस्या थी। दोनों मॉडल को न मिलाएं। HTTP 400 कभी अस्थायी मेल फ़ेल्योर नहीं होता।
Jitter के साथ Backoff
Retry की देरी exponential रूप से बढ़नी चाहिए और उसमें यादृच्छिकता होनी चाहिए, ताकि एक साथ फ़ेल हुए हज़ार workers एक साथ वापस न आएं। एक सामान्य तरीका है आधार देरी को हर प्रयास पर दोगुना करना, अधिकतम सीमा तक, और उस पर एक रैंडम अंश लगाना। अगर response में Retry-After हो, तो उसे न्यूनतम मानें। प्रयासों की अधिकतम संख्या या अधिकतम उम्र तय करें, जिसके बाद पंक्ति failed में चली जाए और अलर्ट उठाए। रणनीति पर और गहराई से जानने के लिए हमारी rate limits, retries और backoff गाइड देखें।
import random, time
RETRY_STATUS = {429, 500, 502, 503, 504}
def next_delay(attempt, retry_after=None, base=2.0, cap=300.0):
delay = min(cap, base * (2 ** attempt))
delay = random.uniform(delay / 2, delay)
if retry_after is not None:
delay = max(delay, float(retry_after))
return delay
def deliver(row, client):
try:
resp = client.post("/send", json=row.payload,
headers={"X-Dedupe-Key": row.dedupe_key},
timeout=(2, 15))
except (ConnectionError, TimeoutError):
return ("retry", next_delay(row.attempts))
if 200 <= resp.status_code < 300:
return ("accepted", resp.json().get("id"))
if resp.status_code in RETRY_STATUS:
ra = resp.headers.get("Retry-After")
return ("retry", next_delay(row.attempts, ra))
return ("failed", resp.text[:500])
इस उदाहरण में header का नाम समझाने के लिए गढ़ा गया है। MigoSMTP API डॉक्यूमेंटेशन में जो बताया गया है वही इस्तेमाल करें, या अगर ऐसा कोई header नहीं है तो केवल अपनी टेबल पर निर्भर रहें।
Response हैंडलिंग और message ID सहेजना
सफलता का response कहानी का अंत नहीं, ट्रैकिंग की शुरुआत है। response से मिली message ID को तुरंत अपने send रिकॉर्ड पर सहेजें, उसी कदम में जिसमें पंक्ति को accepted चिह्नित करते हैं। तब delivery, bounce और complaint के webhook इवेंट उससे जोड़े जा सकते हैं। उस दूसरे हिस्से की विश्वसनीयता ईमेल webhooks को भरोसेमंद ढंग से संभालने में बताई गई है।
- कच्चे API response body को थोड़े समय तक रखें। डिबगिंग में यह अमूल्य है।
- created, पहला प्रयास, accepted और आखिरी इवेंट के timestamps दर्ज करें। इनके बीच के अंतर बताते हैं कि देरी कहां होती है।
- समझें कि accepted का क्या मतलब है। ईमेल API से 2xx response का आमतौर पर मतलब होता है कि सर्विस ने संदेश को डिलीवरी के लिए queue में रखा, यह नहीं कि प्राप्तकर्ता के सर्वर ने उसे स्वीकार किया। अंतिम डिलीवरी बाद में इवेंट से बताई जाती है।
- एरर संदेशों को लॉजिक की तरह parse न करें। स्टेटस कोड और किसी भी डॉक्यूमेंटेड एरर कोड का उपयोग करें, और फ्री टेक्स्ट को इंसानों के लिए जानकारी मानें।
- message ID कॉलम को index करें ताकि सपोर्ट लॉग की ID से एक ही क्वेरी में ग्राहक रिकॉर्ड तक पहुंच सके।
लॉग में secrets और व्यक्तिगत डेटा को redact करें। dedupe key, send ID, स्थिति और message ID लॉग करें, लेकिन API keys या पूरे संदेश body नहीं। Keys को API key security में बताए अनुसार सहेजें और rotate करें।
Batching और bulk sends
कई प्राप्तकर्ताओं को भेजना एक रसीद भेजने से अलग वर्कलोड है। अब आप throughput, मेमोरी और अन्य ट्रैफ़िक के प्रति निष्पक्षता संभाल रहे हैं। कुछ ईमेल API एक batch endpoint देते हैं जो एक रिक्वेस्ट में कई संदेश लेता है, जबकि अन्य प्रति रिक्वेस्ट एक संदेश चाहते हैं और कई concurrent कॉल की अपेक्षा रखते हैं। किसी एक पर डिज़ाइन बनाने से पहले, क्या उपलब्ध है और प्रति-रिक्वेस्ट प्राप्तकर्ता सीमाएं क्या हैं, इसके लिए अपना MigoSMTP अकाउंट या API डॉक्यूमेंटेशन देखें।
- ऑडियंस को chunks में बांटें। प्राप्तकर्ताओं को डेटाबेस से पेजों में पढ़ें, मेमोरी में एक विशाल सूची के रूप में नहीं। प्रति प्राप्तकर्ता send पंक्तियां बनाएं, फिर workers को उन्हें प्रोसेस करने दें।
- प्रति प्राप्तकर्ता वैयक्तिकरण करें। To या Cc में कई प्राप्तकर्ता रखने से बचें। हर व्यक्ति को अपना संदेश मिलना चाहिए, अपने unsubscribe लिंक और ट्रैकिंग डेटा के साथ।
- सेंड की गति नियंत्रित करें। बड़े कैंपेन को सब कुछ एक साथ छोड़ने के बजाय एक समय-खिड़की में फैलाएं। गति नियंत्रण आपकी प्रतिष्ठा की भी रक्षा करता है, खासकर नए डोमेन पर, जैसा warm-up गाइड में बताया गया है।
- कैंपेन को फिर से शुरू करने योग्य बनाएं। क्योंकि हर प्राप्तकर्ता की एक पंक्ति होती है जिसकी unique dedupe key होती है, जैसे campaign-318-user-9001, आप किसी भी बिंदु पर सेंड को रोककर दोबारा शुरू कर सकते हैं और डबल सेंड नहीं होगा।
- आंशिक विफलता संभालें। अगर batch कॉल कुछ संदेशों के लिए सफल और अन्य के लिए विफल हो सकती है, तो प्रति-संदेश परिणाम सहेजें। एक स्टेटस कोड के आधार पर पूरे batch को accepted या failed कभी चिह्नित न करें, जब तक डॉक्यूमेंटेशन न कहे कि यह ऐसे ही काम करता है।
सूची की बुनियादी बातें न छोड़ें: API से bulk sending सहमति, suppression और unsubscribe के नियम नहीं बदलती। हमारी suppression list गाइड के तरीके से, send पंक्तियां बनाने से पहले suppressed पते फ़िल्टर करें।
उदाहरण: एक आउटेज, दो डिज़ाइन
यहां गोल संख्याओं वाला एक काल्पनिक उदाहरण है। एक ऑनलाइन दुकान हर ऑर्डर के लिए ऑर्डर पुष्टि भेजती है। एक व्यस्त दोपहर में वह एक घंटे में 6,000 ऑर्डर बनाती है, और ईमेल API में 4 मिनट तक धीमे जवाब और बीच-बीच में 503 एरर आते हैं।
| चरण | सीधा-सादा डिज़ाइन | dedupe key के साथ Outbox |
|---|---|---|
| ऑर्डर बना | रिक्वेस्ट सीधे API को कॉल करती है | रिक्वेस्ट ऑर्डर और send पंक्ति लिखती है, तुरंत लौट जाती है |
| 4 मिनट तक धीमा API | चेकआउट लंबे timeout पर इंतज़ार करता है, कुछ ग्राहकों को एरर दिखते हैं और वे फिर Pay पर क्लिक करते हैं | चेकआउट अप्रभावित; पंक्तियां pending में इंतज़ार करती हैं |
| 150 कॉल पर timeout | कोड तुरंत retry करता है; उनमें से कई संदेश पहले ही स्वीकार हो चुके थे | पंक्तियां unknown चिह्नित; बाद में उसी dedupe key से retry |
| परिणाम | 150 ऑर्डर पर लगभग 2-2 पुष्टि, साथ में डबल क्लिक से डुप्लिकेट ऑर्डर | लगभग 150 संदेश कुछ मिनट देर से; शून्य डुप्लिकेट |
| सपोर्ट पर असर | दर्जनों टिकट कि कौन सा ईमेल सही है | कोई नहीं; queue age अलर्ट बजा और शांत हो गया |
घंटे में 6,000 ऑर्डर के साथ औसत 100 प्रति मिनट है, इसलिए 4 मिनट की सुस्ती लगभग 400 पंक्तियों का बैकलॉग छोड़ती है। अगर API ठीक होने पर workers 300 प्रति मिनट भेज सकते हैं, तो बैकलॉग लगभग 2 मिनट में साफ़ हो जाता है (400 पंक्तियां भाग 200 प्रति मिनट, जो नए आगमन के 100 प्रति मिनट के ऊपर की अतिरिक्त क्षमता है)। यह गणित अपने सिस्टम के लिए करना सार्थक है: आपके workers की अतिरिक्त क्षमता तय करती है कि आप कितनी जल्दी उबरते हैं, और concurrency की सीमा तय करती है कि रिकवरी आपके अकाउंट की सीमाओं के भीतर रहती है या नहीं।
Rate-limit की जानकारी
Rate limits कई रूपों में आती हैं, जैसे प्रति सेकंड रिक्वेस्ट, प्रति दिन संदेश, concurrent कनेक्शन और प्रति रिक्वेस्ट प्राप्तकर्ता। आपके अकाउंट पर कौन सी लागू हैं और उनके सटीक मान क्या हैं, यह आपके MigoSMTP अकाउंट या API डॉक्यूमेंटेशन में है। अंदाज़ा न लगाएं। अपना क्लाइंट ऐसे बनाएं कि ये संख्याएं कॉन्फ़िगरेशन हों, कोड में स्थिरांक नहीं।
- हर worker में token bucket या रिक्वेस्ट के बीच तय देरी का उपयोग करें, और सभी workers पर एक ग्लोबल सीमा रखें।
- 429 पर, उस worker से Retry-After अवधि तक सेंडिंग रोकें, या अगर कोई न दी गई हो तो backoff देरी तक। वही कॉल बस दोबारा न करें।
- धीमी सफलता पर नज़र रखें। बढ़ती latency अक्सर एरर से पहले आती है, और यह concurrency घटाने का संकेत है।
- circuit breaker इस्तेमाल करें। लगातार विफलताओं के बाद थोड़ी देर कॉल रोकें, फिर पूरी गति पर लौटने से पहले एक रिक्वेस्ट से जांचें।
- प्राप्त करने वाले पक्ष की सीमाओं का ध्यान रखें। प्रोवाइडर द्वारा स्वीकार करना केवल पहला पड़ाव है। मेलबॉक्स प्रोवाइडर अपनी सीमाएं लगाते हैं, जो हमारी bulk sender requirements गाइड में बताई गई हैं।
API बंद होने पर Graceful degradation
कोई भी बाहरी सर्विस कभी न कभी अनुपलब्ध होगी। पहले से तय करें कि आपका प्रोडक्ट क्या करेगा, ताकि जवाब आउटेज के दौरान तुक्के से न बने।
| ईमेल का प्रकार | आउटेज के दौरान सुझाया गया व्यवहार |
|---|---|
| पासवर्ड रीसेट, one-time code | छोटी expiry के साथ queue करें; स्क्रीन पर दिखाएं कि संदेश में देरी हो सकती है और cool-down के बाद दोबारा भेजने का विकल्प दें |
| ऑर्डर या भुगतान पुष्टि | Queue करें और देर से डिलीवर करें; पुष्टि ऑर्डर पेज पर भी दिखाएं |
| अकाउंट सूचनाएं | Queue करें और देर से डिलीवर करें; कई को एक में मिलाने पर विचार करें |
| न्यूज़लेटर और कैंपेन | कैंपेन रोकें और बाद में दोबारा शुरू करें; पकड़ने की जल्दी कभी न करें |
| इंटरनल अलर्ट | चैट या दूसरे प्रोवाइडर जैसे किसी अन्य चैनल पर fallback करें |
केवल एरर काउंट पर नहीं, queue depth और सबसे पुरानी pending पंक्ति की उम्र पर अलर्ट लगाएं। बिना एरर के बढ़ती queue का भी मतलब है कि ग्राहक इंतज़ार कर रहे हैं। queue पर एक सीमा रखें ताकि मेमोरी और स्टोरेज असीमित न बढ़ें, और तय करें कि सीमा पर पहुंचने पर पहले क्या छोड़ा जाएगा। अगर आप fallback के रूप में दूसरा प्रोवाइडर डिज़ाइन करते हैं, तो दूसरे पाथ को warm up और authenticate करने का काम याद रखें, जैसा हमारी migration गाइड में बताया गया है।
इंटीग्रेशन की टेस्टिंग
जो टेस्ट केवल 200 response जांचते हैं, वे आपकी रक्षा नहीं करेंगे। असली महत्व इसका है कि फ़ेल्योर पर क्या होता है, इसलिए फ़ेल्योर पैदा करना आसान बनाएं। अपने क्लाइंट को ऐसे mock सर्वर या sandbox के विरुद्ध चलाएं जिसे धीरे जवाब देने, Retry-After के साथ 429 लौटाने, 503 लौटाने, रिक्वेस्ट पढ़ने के बाद कनेक्शन गिराने या विकृत body लौटाने को कहा जा सके। कंटेंट जांच के लिए mail catcher का उपयोग करें, हमारी staging environment गाइड के अनुसार।
- डुप्लिकेट जॉब टेस्ट: वही जॉब दो बार enqueue करें और पुष्टि करें कि mock API तक केवल एक रिक्वेस्ट पहुंची।
- क्रैश टेस्ट: API कॉल के बाद और डेटाबेस अपडेट से पहले worker को kill करें, फिर पुष्टि करें कि reconciler दूसरा सेंड किए बिना पंक्ति को सुलझा देता है।
- Timeout टेस्ट: mock से रिक्वेस्ट स्वीकार करवाएं और फिर रुकवाएं, और जांचें कि पंक्ति ठीक एक बार accepted के रूप में खत्म होती है।
- Poison message टेस्ट: ऐसा payload भेजें जो 422 लौटाए और पुष्टि करें कि वह एक प्रयास के बाद, एरर सहेजकर, failed में चला जाता है।
- Load टेस्ट: staging पर यथार्थवादी burst चलाएं और पुष्टि करें कि concurrency सीमाएं और backoff डिज़ाइन के अनुसार काम करते हैं।
लॉन्च-पूर्व चेकलिस्ट
- हर ईमेल की एक निर्धारक dedupe key है और unique index उसे लागू करता है।
- Send पंक्तियां बिज़नेस इवेंट वाले ही ट्रांज़ैक्शन में लिखी जाती हैं (outbox)।
- Idempotency header सपोर्ट की पुष्टि MigoSMTP API डॉक्यूमेंटेशन में हो चुकी है, और हमारी अपनी टेबल फिर भी इस्तेमाल होती है।
- API को वेब रिक्वेस्ट नहीं, workers कॉल करते हैं, सीमित concurrency के साथ।
- हर कॉल पर connect और कुल timeouts सेट हैं।
- Responses वर्गीकृत हैं: accepted, retry, failed, unknown। अन्य 4xx responses पर कभी अंधाधुंध retry नहीं होता।
- Backoff में jitter, सीमा और अधिकतम प्रयास संख्या है और वह Retry-After का पालन करता है।
- Message IDs send पंक्तियों पर सहेजी गई हैं और index की गई हैं।
- Bulk sends chunked, गति-नियंत्रित और फिर से शुरू करने योग्य हैं, और पंक्तियां बनाने से पहले suppression फ़िल्टरिंग होती है।
- Rate limits कॉन्फ़िगरेशन हैं, जो आपके अकाउंट या API डॉक्यूमेंटेशन से ली गई हैं।
- सबसे पुरानी pending पंक्ति की उम्र, failed संख्या और 429 दर पर अलर्ट मौजूद हैं।
- डुप्लिकेट, क्रैश, timeouts और 429 के लिए फ़ेल्योर टेस्ट staging में पास होते हैं।
अपने सेंडिंग डिज़ाइन पर दूसरी नज़र चाहिए? हमें बताएं कि आपकी एप्लिकेशन आज ईमेल कैसे भेजती है और retries या डुप्लिकेट को लेकर आपको कहां चिंता है, और हम एक सुरक्षित इंटीग्रेशन की योजना बनाने में आपकी मदद करेंगे।
आगे पढ़ें और स्रोत
अक्सर पूछे जाने वाले प्रश्न
Email API के लिए idempotency key क्या है?
यह एक यूनिक वैल्यू है जिसे आप रिक्वेस्ट के साथ जोड़ते हैं, ताकि सर्वर उसी रिक्वेस्ट के दोहराव को पहचान सके और काम को दो बार न करे। ईमेल के मामले में इसका मतलब है कि timeout के बाद किया गया retry संदेश की दूसरी कॉपी नहीं बनाता। MigoSMTP idempotency header को सपोर्ट करता है या नहीं, और कैसे, यह MigoSMTP API डॉक्यूमेंटेशन में पुष्टि करना ज़रूरी है। किसी भी स्थिति में, आप अपनी dedupe टेबल से वही सुरक्षा पा सकते हैं।
अगर API idempotency सपोर्ट करता है, तो क्या फिर भी मुझे अपनी dedupe टेबल चाहिए?
हां, हम इसकी सलाह देते हैं। सर्वर-साइड idempotency आमतौर पर सीमित अवधि और केवल उन रिक्वेस्ट को कवर करती है जो API तक पहुंचती हैं। आपकी अपनी टेबल डुप्लिकेट जॉब, डुप्लिकेट webhook ट्रिगर और अपस्ट्रीम में डबल क्लिक को भी रोकती है, और यह भी रिकॉर्ड देती है कि आपने क्या भेजने का इरादा किया था।
क्या timeout के बाद retry करना सुरक्षित है?
केवल तभी जब आपके पास dedupe रणनीति हो। timeout का मतलब है कि आपको पता नहीं कि API ने संदेश स्वीकार किया या नहीं। idempotency key या अपने रेफ़रेंस से लुकअप के बिना, retry से ईमेल दो बार जा सकता है। इनके साथ retry सुरक्षित है।
कौन से HTTP स्टेटस कोड पर retry करना चाहिए?
नेटवर्क फ़ेल्योर, timeout, 429 Too Many Requests और 5xx सर्वर एरर पर retry करें, exponential backoff और jitter के साथ और किसी भी Retry-After header का सम्मान करते हुए। 400, 401, 403 या 422 जैसे अन्य 4xx एरर पर retry न करें, क्योंकि जब तक आप कुछ बदलते नहीं, वही रिक्वेस्ट उसी तरह फ़ेल होगी।
Outbox पैटर्न क्या है?
आप ईमेल रिक्वेस्ट को उसी डेटाबेस ट्रांज़ैक्शन में एक टेबल में लिखते हैं जिसमें बिज़नेस बदलाव होता है, फिर एक अलग प्रोसेस न भेजी गई पंक्तियों को पढ़कर API को कॉल करता है। इससे वह अंतर खत्म हो जाता है जिसमें डेटाबेस कमिट सफल हो जाता है पर ईमेल कॉल फ़ेल हो जाती है, या इसका उल्टा।
क्या मुझे वेब रिक्वेस्ट से ईमेल भेजना चाहिए या queue से?
लगभग हर चीज़ के लिए queue से। queue यूज़र-फेसिंग latency की रक्षा करती है, सुरक्षित retry देती है और आपको रेट कंट्रोल करने देती है। अपवाद एक बहुत सरल इंटरनल टूल है जहां फ़ेल हुए सेंड को सीधे ट्रिगर करने वाले व्यक्ति को दिखाया जा सकता है।
मैं API से बड़ा कैंपेन कैसे भेजूं?
प्राप्तकर्ताओं को batches में बांटें, concurrency सीमित रखें, डॉक्यूमेंटेड सीमाओं और Retry-After का पालन करें, और सेंडिंग को समय में फैलाएं। सटीक सीमाओं और batch endpoint है या नहीं, इसके लिए अपना MigoSMTP अकाउंट या API डॉक्यूमेंटेशन देखें। नए डोमेन या IP के लिए warm-up प्लान भी अपनाएं।
API द्वारा लौटाया गया message ID क्यों सहेजना चाहिए?
यह आपके सेंड रिकॉर्ड और बाद के इवेंट, जैसे delivery, bounce और complaint webhooks, के बीच जोड़ने वाली कुंजी है। इसके बिना, "क्या इस ग्राहक को रसीद मिली" जैसे सपोर्ट सवाल अंदाज़े का खेल बन जाते हैं।
असली मेल भेजे बिना ईमेल इंटीग्रेशन को कैसे टेस्ट करूं?
API क्लाइंट को ऐसे mock सर्वर या sandbox के विरुद्ध चलाएं जो मांग पर धीमे जवाब, 429 और 5xx लौटा सके, और कंटेंट जांच के लिए mail catcher का उपयोग करें। केवल HTTP कॉल पर नहीं, अपनी send टेबल पर assert करें। हमारी staging गाइड में सुरक्षित सेटअप बताए गए हैं।