2020年6月20日土曜日

Celery のリトライ機能を使ってみた

概要

Celery にはタスクのリトライ機能が標準で備わっています
基本はタスク内の raise に反応してリトライ処理が走ります
様々なオプションがあるので今回はオプションをいろいろと変更して挙動を確認してみました

環境

  • macOS 10.15.5
  • Python 3.8.3
    • celery 4.4.4

リトライ処理を追加した普通のタスク

まずは普通にリトライ機能を追加しただけのタスクです
必須なのは bind=True です
これを使うとタスク内で self が参照できるようになり、リトライする場合は self.retry() をコールすることで同じタスクをリトライできるようになります

from celery import Celery

app = Celery('tasks', broker='redis://localhost', backend='redis://localhost')

@app.task(bind=True)
def add(self, x, y):
    try:
        return x + y
    except Exception as exc:
        self.retry(exc=exc)

特定の時間ウェイトしたあとにリトライする方法

default_retry_delay を使います
単位は秒になります

from celery import Celery

app = Celery('tasks', broker='redis://localhost', backend='redis://localhost')

@app.task(bind=True, default_retry_delay=5)
def add(self, x, y):
    try:
        raise
        return x + y
    except Exception as exc:
        self.retry(exc=exc)

サンプルは必ずリトライします
デフォルトだとリトライ回数は 3 回のようです
変更する場合には self.retry(exc=exc, max_retries=5) こんな感じで retry の引数に指定できます

try, except を使わずにリトライさせる

タスクないで発生するエラーのクラスがわかっているのであればデコレータの引数でエラーを指定することで自動的にリトライさせることもできます

from celery import Celery

app = Celery('tasks', broker='redis://localhost', backend='redis://localhost')

@app.task(bind=True, default_retry_delay=5, autoretry_for=(Exception,), retry_kwargs={'max_retries': 5})
def add(self, x, y):
    raise
    return x + y

リトライの回数は retry_kwargs を使って辞書形式でオプションを渡すことができます

exponential backoff を使う

指定の時間待ってリトライだと効率がよくない場合があります
その場合は exponential backoff の機能があるのでこれを使いましょう
簡単に言えば失敗するたびにウェイトする時間が増える仕組みのことです

@app.task(bind=True, autoretry_for=(Exception,), retry_kwargs={'max_retries': 5}, retry_backoff=True)
def add(self, x, y):
    raise
    return x + y

これで確認してみるとウェイトの時間が 0s -> 1s -> 2s -> 4s のように指数的に増えているのが確認できると思います
exponential backoff の場合回数ではなく時間でリトライ回数を指定することもできます
retry_backoff_max を使います
デフォルトでは 600秒が指定されており 600秒間の間 exponential backoff でリトライを繰り返してくれます

ステータスはどうなっているか

リトライ中のタスクのステータスも一応確認してみました
flower を使っています

リトライが一度でも発生すると RETRY の状態になるようです
リトライ中だからと言って STARTED や PENDING にはならないようです

最後に

Celery のリトライ処理を試してみました
基本はリトライを実装したほうが良いと思いますが無駄なリトライなどが発生してタスクの完了が遅くならないように注意しましょう

0 件のコメント:

コメントを投稿