次のようなものがあるとき
group1 = group(task1.si(), task1.si(), task1.si())
group2 = group(task2.si(), task2.si(), task2.si())
workflow = chain(group1, group2, task3.si())
直感的な解釈では、task3は、グループ2のすべてのタスクが終了した後にのみ実行する必要があります。
実際には、タスク3は、group1が開始されているが、まだ完了していないときに実行されます。
私は何が間違っているのですか?
結局のところ、セロリでは2つのグループをつなぐことはできません。
これは、タスクにチェーンされたグループが自動的にコードになるためだと思います
-> Celeryドキュメント: http://docs.celeryproject.org/en/latest/userguide/canvas.html
グループを別のタスクとチェーンすると、自動的にコードにアップグレードされます。
グループは親タスクを返します。 2つのグループをチェーンする場合、最初のグループが完了すると、コードがコールバック「タスク」を開始すると思います。この「タスク」は、実際には2番目のグループの「親タスク」だと思います。さらに、この親タスクは、グループ内のすべてのサブタスクの開始が完了するとすぐに完了し、その結果、2番目のグループが実行された後の次の項目が完了すると思われます。
これを示すために、いくつかのサンプルコードがあります。すでに実行中のセロリインスタンスが必要です。
# celery_experiment.py
from celery import task, group, chain, chord
from celery.signals import task_sent, task_postrun, task_prerun
import time
import logging
import random
random.seed()
logging.basicConfig(level=logging.DEBUG)
### HANDLERS ###
@task_prerun.connect()
def task_starting_handler(sender=None, task_id=None, task=None, args=None, kwargs=None, **kwds):
try:
logging.info('[%s] starting' % kwargs['id'])
except KeyError:
pass
@task_postrun.connect()
def task_finished_handler(sender=None, task_id=None, task=None, args=None, kwargs=None, retval=None, state=None, **kwds):
try:
logging.info('[%s] finished' % kwargs['id'])
except KeyError:
pass
def random_sleep(id):
slp = random.randint(1, 3)
logging.info('[%s] sleep for %ssecs' % (id, slp))
time.sleep(slp)
@task()
def thing(id):
logging.info('[%s] begin' % id)
random_sleep(id)
logging.info('[%s] end' % id)
def exec_exp():
st = thing.si(id='st')
st_arr = [thing.si(id='st_arr1_a'), thing.si(id='st_arr1_b'), thing.si(id='st_arr1_c'),]
st_arr2 = [thing.si(id='st_arr2_a'), thing.si(id='st_arr2_b'),]
st2 = thing.si(id='st2')
st3 = thing.si(id='st3')
st4 = thing.si(id='st4')
grp1 = group(st_arr)
grp2 = group(st_arr2)
# chn can chain two groups together because they are seperated by a single subtask
chn = (st | grp1 | st2 | grp2 | st3 | st4)
# in chn2 you can't chain two groups together. what will happen is st3 will start before grp2 finishes
#chn2 = (st | st2 | grp1 | grp2 | st3 | st4)
r = chn()
#r2 = chn2()
セロリについても同じ問題があり、最初のステップが「100万のタスクを生成する」ワークフローを作成しようとしています。グループ、サブタスクのグループを試しましたが、最終的には、step1が終了する前にstep2が開始されます。
簡単に言えば、コードとダムフィニッシャーを使用して解決策を見つけたかもしれません。
@celery.task
def chordfinisher( *args, **kwargs ):
return "OK"
何もしませんが、これを行うことができます。
tasks = []
for id in ids:
tasks.append( mytask.si( id ) )
step1 = chord( group( tasks ), chordfinisher.si() )
step2 = ...
workflow = chain( step1, step2 )
もともと私はサブタスクにstep1を入れたかったのですが、疑わしいのと同じ理由で、グループを呼び出すアクションが終了し、タスクが終了したと見なされ、ワークフローが続行されます...
誰かがもっと良いものを持っているなら、私は興味があります!