FastAPI + Celery信頼性設計: 非同期ジョブを本番で壊さないための実装パターン

FastAPI + Celery信頼性設計: 非同期ジョブを本番で壊さないための実装パターン FastAPIでAPIを作ると、重い処理はすぐに非同期ジョブへ逃がしたくなります。画像変換、レポート生成、外部API連携、メール配信など、Celeryは非常に便利です。ですが、本番で問題になるのは「動くかどうか」ではなく、失敗したときに壊れないか です。 同じジョブが二重実行される 一時障害で永遠にリトライしてキューが詰まる ワーカー再起動で中途半端な状態が残る 完了通知が先に飛んで実データがない 本記事では FastAPI + Celery + Redis 構成を前提に、再実行安全性(idempotency)と運用信頼性を上げる実装手順をまとめます。 1. まず守るべき設計原則 非同期基盤の事故は、ほぼ次の4原則で防げます。 At-least-once前提(同一タスク再実行は必ず起こる) 副作用は冪等化(何回実行されても結果が壊れない) 状態遷移を明示(PENDING/RUNNING/SUCCEEDED/FAILED) 失敗を可観測化(リトライ回数・死活・滞留時間を計測) この原則を外すと、障害時に「何が完了して何が未完了か」が追えなくなります。 2. 参照アーキテクチャ API: FastAPI Queue Broker: Redis Worker: Celery Result Store: PostgreSQL(業務状態) Monitoring: Flower + Prometheus + Sentry ポイントは、業務上重要な状態はRedis結果バックエンドに依存しない ことです。Redisは一時的に使い、真実の状態はRDBに持たせます。 3. 実装の土台: タスク受付API 3.1 受け付け時に idempotency_key を必須化 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 from fastapi import FastAPI, HTTPException from pydantic import BaseModel from sqlalchemy import select app = FastAPI() class JobRequest(BaseModel): idempotency_key: str report_type: str user_id: str @app.post("/reports") def create_report(req: JobRequest): existing = find_job_by_key(req.idempotency_key) if existing: return {"job_id": existing.id, "status": existing.status} job = create_job_record( idempotency_key=req.idempotency_key, status="PENDING", report_type=req.report_type, user_id=req.user_id, ) generate_report.delay(job.id) return {"job_id": job.id, "status": "PENDING"} これでクライアント再送が来てもジョブ多重作成を防げます。 ...

March 6, 2026 · 2 min · AI2CORE 編集部

Redisキャッシュスタンピード対策ガイド:高負荷時にDBを守る設計と実装

Redisキャッシュスタンピード対策ガイド:高負荷時にDBを守る設計と実装 Redis を使っていても、ピークトラフィック時に DB が突然落ちることがあります。原因の多くはキャッシュスタンピードです。人気キーの TTL が同時に切れると、大量リクエストが一斉に DB へ流れ、接続プールが飽和します。 「Redis を入れたのに遅い」「ピーク時だけ 500 が増える」という現象は、このパターンで説明できることが非常に多いです。 本記事では、キャッシュスタンピードを実運用で防ぐために、設計原則・実装パターン・監視方法を順に解説します。 1. キャッシュスタンピードとは何か 典型シナリオ: 商品ランキング API が ranking:daily を Redis に 300 秒で保存 300 秒後、人気時間帯にキー期限切れ 同時に 1000 リクエストが miss 1000 回 DB 集計が走ってレイテンシ急増 このとき Redis 自体は正常でも、背後の DB が壊れます。つまり、問題はキャッシュ障害ではなく「再生成の同時実行制御」です。 2. 防御の基本は三層構え スタンピード対策は単一施策では不十分です。次の三層を組み合わせると安定します。 同時再生成の抑制(singleflight / 分散ロック) 期限切れの分散(TTL ジッター) 期限切れ後の挙動制御(stale-while-revalidate) 3. パターン1: singleflight で同時再生成を止める 同一キーの miss が同時発生しても、1 リクエストだけ再生成し、他は待つ設計です。 TypeScript 例: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 const inflight = new Map<string, Promise<string>>(); async function getOrCompute(key: string, ttlSec: number, compute: () => Promise<string>) { const cached = await redis.get(key); if (cached) return cached; if (!inflight.has(key)) { const p = (async () => { try { const value = await compute(); await redis.set(key, value, { EX: ttlSec }); return value; } finally { inflight.delete(key); } })(); inflight.set(key, p); } return await inflight.get(key)!; } 単一プロセスではこれで十分ですが、複数インスタンス構成では分散ロックも必要です。 ...

March 2, 2026 · 2 min · AI2CORE 編集部