Celery with Redis で何度もタスクが実行される

Broker として Redis を使い Celery で非同期タスクを実行するアプリケーションを開発中、時々そのタスクが複数回実行されることがあり困っていた。2回どころか5〜6回実行されることも。
Worker となるサーバが複数台あるのなら、そんなこともあるかもねと思うところだが、そのサーバは1台だけ。1台が何度もタスクを実行している状況。

Task ID started twice in rapid succession · Issue #176 · celery/django-celery
こんな風に似たような状況で困っている人がいるし、これは Redis がダメなんだ、やっぱり RabbitMQ 使わんとあかんのだと半ば諦めて、タスク側で重複呼び出しに対する制御を入れ、後ろめたい気持ちを抱えつつ知らんぷりしていたこの1ヶ月。

アプリケーションのリリースを間近に控え、喉に刺さったままの小骨を取り除きたいと再度調査を始めたところ、なんだドキュメントに書いてあるじゃない。こんなの前から書いてあったか、俺が前に読んだ後にこっそり書き加えたんじゃね? と、拍子抜けするほど明記してあるこの Visibility timeout

If a task is not acknowledged within the Visibility Timeout the task will be redelivered to another worker and executed.

This causes problems with ETA/countdown/retry tasks where the time to execute exceeds the visibility timeout; in fact if that happens it will be executed again, and again in a loop.

開発中のプログラムでは10時間後くらいにタスクが実行されるように countdown を指定して Task.apply_async() を実行していたので、デフォルトの Visibility Timeout の一時間は超えまくっている。
「redelivered to another worker and executed」の another worker のところが、一台しか Worker はないのにおかしいなと思うところだけど、アプリケーションのテスト中はこの countdown の時間を数分に指定することが多かったので、時々しかこの重複タスク実行が起こらなかったことは納得はいく。

この Visibility Timeout を12時間に設定して、10時間くらいの countdown タスク実行の再テストを各環境のサーバで行っているが、今のところは大丈夫そう。

Last updated on September 16, 2015