第5章 バックグラウンドタスクと Celery の使い分け
Web サービスを開発していると、メール送信、PDF 出力、画像のリサイズ、機械学習モデルの推論など、時間のかかる処理を実行したい場面が頻繁に訪れます。
FastAPI には軽量な BackgroundTasks が組み込まれていますが、システムの規模や信頼性の要件によって、本格的な分散タスクキューである Celery が必要になります。本章では、両者の違いと使い分けの基準を学びます。
5.1 なぜ Web リクエスト内で重い処理をしてはいけないのか
Web クライアント(ブラウザやアプリ)は、API リクエストを送った後、レスポンスが返ってくるまで待機します。
もしリクエスト処理の中で数秒から数十秒かかる重い処理を実行すると、ユーザーの画面がフリーズするだけでなく、以下の深刻な問題が発生します。
- クライアントタイムアウト: ブラウザやロードバランサーが通信を切断してしまう。
- API サーバーの枯渇: サーバーのリソース(スレッドやワーカー)が重い処理に占有され、他のユーザーの簡単なアクセスすら捌けなくなる。
したがって、「リクエストを受け付けたら即座に応答を返し、重い処理は裏(バックグラウンド)で実行する」という設計が基本原則となります。
5.2 FastAPI 標準の BackgroundTasks
FastAPI には、標準機能として BackgroundTasks が用意されています。
レスポンスをクライアントに返却した直後に、同じサーバープロセスの裏で関数を実行させることができます。
from fastapi import BackgroundTasks, FastAPI
app = FastAPI()
def write_notification_log(email: str, message: str):
with open("notifications.log", "a", encoding="utf-8") as f:
f.write(f"Sent to {email}: {message}\n")
@app.post("/send-notification")
def send_notification(email: str, background_tasks: BackgroundTasks):
# レスポンス返却後に実行するタスクを登録
background_tasks.add_task(write_notification_log, email, message="ようこそ!")
return {"status": "accepted", "message": "通知送信を受け付けました"}
追加のミドルウェアやインフラを用意する必要がなく、手軽に利用できるのが最大のメリットです。
5.3 BackgroundTasks の限界
非常に便利な BackgroundTasks ですが、実務の大規模システムやクリティカルな要件では以下のような重大な限界があります。
- 同一プロセス・インメモリ実行: API サーバーと同じプロセス内で実行されるため、タスクが大量のメモリや CPU を消費すると API サーバー全体の動作が重くなります。
- プロセスの再起動によるデータ消失: タスクの実行待ちキューはサーバーのメモリ上にしか存在しません。サーバーが再起動したりクラッシュしたりすると、待機中・実行中のタスクは永久に失われます。
- リトライ機能の欠如: ネットワークエラー等でタスクが失敗した場合に、数分後に自動再試行する仕組みが標準では存在しません。
- 複数サーバーへの分散不可: API サーバーを水平スケール(台数を増やす)しても、タスク処理を別台の専用ワーカーマシンに任せることができません。
5.4 分散タスクキュー(Celery)が必要になる判断基準
上記の問題を解決するのが、分散タスクキューである Celery です。
以下の条件に 1 つでも当てはまる場合は、BackgroundTasks ではなく Celery を選択するべきです。
- タスクのロストが許されない: 決済処理、注文確定後の在庫引き当て、重要通知など、サーバー再起動時でも確実に実行したい処理。
- 処理時間が長い(目安として 1 秒以上): 音声・動画エンコード、大規模データの一括集計、機械学習モデルの推論。
- 自動リトライが必要: 外部 API のレート制限や一時的なネットワーク障害時に、指数バックオフで再試行したい場合。
- CPU / メモリ消費が激しい: API サーバーとワーカーサーバーをインフラレベルで物理的に分離してスケールさせたい場合。
5.5 機能比較まとめ
| 項目 | FastAPI BackgroundTasks | Celery (+ Redis) |
|---|---|---|
| 追加インフラ | 不要(Python のみ) | 必要(Redis または RabbitMQ) |
| 実行場所 | API サーバーと同じプロセス | 独立した専用ワーカープロセス |
| キューの永続化 | なし(メモリ上、再起動で消失) | あり(Broker に安全に保持) |
| 自動リトライ | なし(自前実装が必要) | 標準装備(回数・間隔・例外指定可) |
| 進捗・結果管理 | なし | 標準装備(タスク ID で状態照会可能) |
| 適した用途 | 簡易ログ記録、軽微な通知 | 重い非同期バッチ、決済、画像・機械学習処理 |