2025, Oct 31 00:01

Celery retries के साथ DLQ क्यों नहीं भरता और acks_late से समाधान

जानें क्यों RabbitMQ में Celery टास्क retries के बाद संदेश DLQ में नहीं पहुँचते, acks_late कैसे मदद करता है, और Reject से सही dead-lettering कॉन्फ़िगर करें.

जब RabbitMQ में Celery टास्क dead-letter queues (DLQ) से जुड़े होते हैं, तो साधारण विफलताओं को रूट करना आसान रहता है। उलझन तब शुरू होती है जब retries शामिल होते हैं: टास्क अपेक्षित रूप से दोबारा चलते हैं, अंततः विफल भी हो जाते हैं, फिर भी संदेश dead-letter queue पर दिखाई नहीं देता। नीचे संक्षेप में बताया गया है कि यह क्यों होता है और retries तथा dead-lettering को साथ‑साथ सही तरह से कैसे काम कराएँ।

समस्या को पुनरुत्पादित करना

नीचे दिया गया उदाहरण ऐसा टास्क दिखाता है जो फेल होता है, retries ट्रिगर करता है और उनके खत्म हो जाने पर संदेश को dead-letter queue में भेजने के लिए Reject उठाता है। लेकिन सही acknowledgment व्यवहार न होने पर, अंत में संदेश dead-letter नहीं होगा।

from celery import Celery, Task
from celery.exceptions import Reject
app = Celery("dlq_retry_demo")
class DlqAwareTask(Task):
    # यहां acks_late जानबूझकर सेट नहीं किया गया है
    def on_failure(self, exc, task_id, args, kwargs, einfo):
        # retries खत्म होने के बाद हम संदेश को DLQ में भेजना चाहते हैं
        raise Reject()
@app.task(bind=True, base=DlqAwareTask, autoretry_for=(RuntimeError,), retry_kwargs={"max_retries": 3, "countdown": 1})
def flaky_work(self):
    # ऐसी विफलता का अनुकरण करें जो autoretry ट्रिगर करे
    raise RuntimeError("boom")

ऐसा क्यों होता है

जब retries सक्षम होते हैं, Celery निष्पादन से ठीक पहले टास्क को acknowledge कर देता है। इसके बाद यदि टास्क विफल हो जाए, तो broker संदेश को पहले ही संसाधित मान लेता है। उस समय Reject उठाने से संदेश dead-letter queue तक नहीं पहुँचता, क्योंकि broker के पास अस्वीकार या dead-letter करने के लिए कोई unacked संदेश बचता ही नहीं।

लगता है कि आपको acks_late का उपयोग करना होगा। दोबारा चलाने के समय, टास्क निष्पादन से ठीक पहले acknowledge हो जाता है, भले ही टास्क exception के साथ विफल क्यों न हो। FAQ देखें। acks_late पर अधिक विशिष्ट दस्तावेज भी उपलब्ध हैं।

कारगर समाधान

Late acknowledgements सक्षम करें ताकि टास्क सफलतापूर्वक पूरा होने तक संदेश unacked बना रहे। जब late acks चालू हों, तो retries खत्म होने के बाद स्पष्ट त्रुटि या Reject के जरिए संदेश को आपकी dead-letter queue में भेजा जा सकता है।

from celery import Celery, Task
from celery.exceptions import Reject
app = Celery("dlq_retry_demo")
# यह सुनिश्चित करें कि late acknowledgements उपयोग में हैं
app.conf.task_acks_late = True
class DlqAwareTask(Task):
    # वैकल्पिक रूप से, इसे प्रति-टास्क सेट करें
    acks_late = True
    def on_failure(self, exc, task_id, args, kwargs, einfo):
        # retries पूरा होते ही, यह संदेश को DLQ तक रूट करेगा
        raise Reject()
# late acks के साथ स्वचालित retries काम करते हैं
@app.task(bind=True, base=DlqAwareTask, autoretry_for=(RuntimeError,), retry_kwargs={"max_retries": 3, "countdown": 1})
def flaky_work(self):
    raise RuntimeError("boom")
# स्पष्ट (explicit) retry भी काम करता है
@app.task(bind=True, base=DlqAwareTask)
def flaky_manual(self):
    try:
        raise RuntimeError("oops")
    except RuntimeError as exc:
        if self.request.retries < 3:
            raise self.retry(exc=exc, countdown=1)
        # अंतिम retry के बाद, संदेश को dead-letter करें
        raise Reject()

on_failure और retries को साथ मिलाने पर एक महत्वपूर्ण बारीकी है। यदि on_failure खुद एक retry ट्रिगर करता है, तो उस मार्ग में Reject पहले जैसा व्यवहार नहीं करता और संदेश dead-letter queue तक नहीं पहुँचता। यानी, retries खत्म होने पर Reject के लिए on_failure का उपयोग करें, लेकिन अगर आप वही dead-lettering व्यवहार चाहते हैं तो on_failure के भीतर से retry शुरू करने से बचें।

class RetryInFailureTask(Task):
    acks_late = True
    def on_failure(self, exc, task_id, args, kwargs, einfo):
        # यहां retry ट्रिगर करने से Reject के हैंडल होने का तरीका बदल जाता है
        if self.request.retries < 3:
            raise self.retry(exc=exc, countdown=1)
        # retries के बाद, यह Reject उसी तरह हैंडल नहीं होगा
        raise Reject()

यह क्यों मायने रखता है

टास्क निष्पादन में retries सामान्य हैं, लेकिन वे आपके dead-letter वर्कफ़्लो से विफल संदेशों को गायब नहीं करने चाहिए। acks_late सक्षम होने से संदेश का जीवनचक्र आपकी failure हैंडलिंग के अनुरूप रहता है, ताकि अंतिम (terminal) त्रुटियाँ वहीं दिखें जहाँ आप उनकी अपेक्षा करते हैं।

व्यावहारिक निष्कर्ष

टास्क को late acknowledge करने के लिए कॉन्फ़िगर करें, ताकि broker retries के बाद अंतिम विफलताओं को dead-letter कर सके। retries खत्म होने पर Reject का उपयोग या तो टास्क के भीतर करें या on_failure में। यदि आप on_failure से retry ट्रिगर करना चुनते हैं, तो ध्यान रखें कि Reject का रूटिंग पहले जैसा नहीं होगा और संदेश dead-letter queue तक नहीं पहुँचेगा।

यह लेख StackOverflow पर प्रश्न और grahamlyons के उत्तर पर आधारित है।