web-dev-qa-db-ja.com

セロリタスクをタスク内から失敗させる方法は?

ある状況下では、セロリのタスクをそのタスク内から失敗させたいと思います。私は以下を試しました:

from celery.task import task
from celery import states

@task()
def run_simulation():
    if some_condition:
        run_simulation.update_state(state=states.FAILURE)
        return False

ただし、タスクは引き続き成功したと報告します。

タスクsim.tasks.run_simulation [9235e3a7-c6d2-4219-bbc7-acf65c816e65]は1.17847704887で成功しました:False

状態は、タスクの実行中にのみ変更できるようで、タスクが完了すると、セロリは状態を結果と見なすものに変更します( この質問 を参照)。例外を発生させてタスクを失敗させずに、タスクが失敗したことをセロリに返す方法はありますか?

32
Meilo

Ask Solemからこの質問に対して 興味深い回答 を受け取りました。彼は問題を解決するために「after_return」ハンドラーを提案しています。これは将来的に興味深いオプションになるかもしれません。

それまでの間、タスクを失敗させたいときにタスクから文字列 'FAILURE'を返すだけで問題を解決し、次のようにチェックしました。

result = AsyncResult(task_id)
if result.state == 'FAILURE' or (result.state == 'SUCCESS' and result.get() == 'FAILURE'):
    # Failure processing task 
2
Meilo

例外を発生させずにタスクを失敗としてマークするには、タスクの状態をFAILUREに更新してから、Ignore例外を発生させます。これは、値を返すとタスクが成功したと記録されるためです。例:

from celery import Celery, states
from celery.exceptions import Ignore

app = Celery('tasks', broker='amqp://guest@localhost//')

@app.task(bind=True)
def run_simulation(self):
    if some_condition:
        # manually update the task state
        self.update_state(
            state = states.FAILURE,
            meta = 'REASON FOR FAILURE'
        )

        # ignore the task so no other state is recorded
        raise Ignore()

ただし、最善の方法は、タスクから例外を発生させることです。カスタム例外を作成して、これらの失敗を追跡できます。

class TaskFailure(Exception):
   pass

そして、あなたのタスクからこの例外を発生させます:

if some_condition:
    raise TaskFailure('Failure reason')
22
Pierre

提案された解決策を使用していくつかの問題が発生したので、ピエールの答えをさらに拡張したいと思います。

タスクの状態をstates.FAILUREに更新するときにカスタムフィールドを許可するには、FAILURE状態が持ついくつかの属性をモックすることも重要です(exc_typeとexc_messageに注意してください)。ソリューションがタスクを終了している間、状態をクエリしようとすると(例-「REASONFORFAILURE」値をフェッチする)は失敗します。

以下は私が引用した参照用のスニペットです: https://www.distributedpython.com/2018/09/28/celery-task-states/

@app.task(bind=True)
def task(self):
    try:
        raise ValueError('Some error')
    except Exception as ex:
        self.update_state(
            state=states.FAILURE,
            meta={
                'exc_type': type(ex).__name__,
                'exc_message': traceback.format_exc().split('\n')
                'custom': '...'
            })
        raise Ignore()
1
Idan Mann