2021年11月12日金曜日

celery の s() と si() の使い分け

celery の s() と si() の使い分け

概要

s() は前のタスクの結果を受け取ります
si() は前のタスクの結果を受け取りません

環境

  • macOS 11.6.1
  • Python 3.8.12
    • celery 5.1.2

タスクの定義

  • vim sub_tasks.py
from celery import Celery

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

@app.task
def add(x, y):
    return x + y

@app.task
def multi(x, y):
    return x * y

s() を使う場合

まずは s() を使います
前のタスクの結果は次のタスクの第一引数に渡されます

from celery import chain
from sub_tasks import add, multi

tasks = chain(
    add.s(1, 2),
    multi.s(10)).apply_async()

print(tasks.get())

結果は 1+2=3 -> 3 x 10 = 30 になります

以下のような使い方はエラーになります

from celery import chain
from sub_tasks import add, multi

tasks = chain(
    add.s(1, 2),
    multi.s(10, 10)).apply_async()

print(tasks.get())

TypeError: multi() takes 2 positional arguments but 3 were given 前のタスクの結果と合わせて引数が足りないと言われてエラーになります

si() を使う場合

si() を使うことで前のタスクの結果を使わないようにできます

from celery import chain
from sub_tasks import add, multi

tasks = chain(
    add.s(1, 2),
    multi.si(10, 10)).apply_async()

print(tasks.get())

結果は 10 x 10 = 100 になります
1+2=3 の結果は使っていないことがわかります

以下の使い方はエラーになります

from celery import chain
from sub_tasks import add, multi

tasks = chain(
    add.s(1, 2),
    multi.si(10)).apply_async()

print(tasks.get())

TypeError: multi() missing 1 required positional argument: 'y'

前のタスクが None を返す場合は

None でも関係なく s() を使った場合は次のタスクの第一引数に入るので注意しましょう

どちらを使うのがいいのか

個人的には以下のようなルールで使うのがいいと思っています

  • 定義したタスクは必ず単独で使う -> si() を使う
  • 定義したタスクは前の結果を使う可能性がある -> s() を使う

前者の場合タスクを定義する際の引数に前のタスクの結果を受け取るための引数がなくなるので必ず si() を使い続ける必要があります

後者は前のタスクの結果を受け取るための引数がありながらもケースによっては引数を使わないこともできます
ただ使わない引数を定義することになるので少しコードが紛らわしくなる可能性があります

タスクとして柔軟なのは間違いなく後者です
例えば将来的に定義したタスクの前にタスクが来てその結果を使う場合には後者で実装しなければなりません
前にタスクが来てもタスクの結果を使うことがなければ si() でも OK です

最後に

si() = s() + immutable=True なので素直に s() を使うのがいいのかもしれません

タスクを定義する場合も必ず前のタスクを受け取るようにするのがいいのかもしれません

参考サイト

0 件のコメント:

コメントを投稿