FastAPIの実行モデルと前提
FastAPIの実行モデルと前提
Section titled “FastAPIの実行モデルと前提”FastAPIの実行モデルと、実務で事故を防ぐための前提条件を詳しく解説します。
実行モデルとリソースの物理的制約
Section titled “実行モデルとリソースの物理的制約”コンピュータ資源は有限であり、性能ではなく制約を前提に設計することが基本です。
主な物理的制約
Section titled “主な物理的制約”CPU・メモリよりも先に枯渇するリソース:
-
DB・外部APIのコネクション数
- 接続プールの上限(例: SQLAlchemyの
pool_size=5) - 接続リークは数時間後にシステム全体を停止させる
- 接続プールの上限(例: SQLAlchemyの
-
イベントループのブロック
- 同期I/O操作により、イベントループがブロックされる
- すべてのリクエストが処理できなくなる
-
ファイル記述子
- OSレベルの制限(通常1024〜65536)
- ファイルやソケットを適切にクローズしないと枯渇
-
メモリリーク
- オブジェクトの参照が保持される
- グローバル変数やクラス変数の不適切な使用
実際の事故例:
10:00:00 - アプリケーション起動(接続プール: 5/5)10:00:01 - リクエスト1受信(接続取得: 6/5 → 待機)10:00:02 - リクエスト2受信(接続取得: 7/5 → 待機)...10:30:00 - 接続が解放されず、すべてのリクエストが待機状態10:30:01 - タイムアウトエラーが大量発生10:30:02 - システム全体が応答不能FastAPIの実行モデル
Section titled “FastAPIの実行モデル”Pythonの実行モデル
Section titled “Pythonの実行モデル”実行モデル:
Pythonコード (.py) ↓ インタープリタで実行ネイティブコード(実行時)重要な特徴:
- 動的型付け: 実行時に型が決定される
- ガベージコレクション: 自動メモリ管理(ただし、参照が保持されている場合は動作しない)
- GIL(Global Interpreter Lock): 同時に1つのスレッドのみが実行される(非同期処理で並行性を実現)
- 非同期処理:
async/awaitによる非同期処理(同期I/O操作は避ける)
トランザクション境界
Section titled “トランザクション境界”FastAPIのトランザクション管理:
# SQLAlchemyでのトランザクション管理from sqlalchemy.orm import Session
async def create_order(db: Session, order_data: OrderData) -> Order: # トランザクション内の処理 order = Order(**order_data.dict()) db.add(order)
for item in order_data.items: inventory = db.query(Inventory).filter_by(product_id=item.product_id).first() inventory.stock -= item.quantity
db.commit() return order特徴:
- 明示的なトランザクション境界:
db.commit()で明示 - 手動ロールバック: エラー時に手動でロールバック
- 依存性注入: FastAPIの依存性注入により、セッションを管理
他言語との比較:
// Java: 宣言的トランザクション管理@Transactionalpublic Order createOrder(OrderData orderData) { Order order = orderRepository.save(new Order(orderData)); return order;}FastAPIの非同期処理:
# async/awaitを使用from fastapi import FastAPIimport httpx
app = FastAPI()
@app.post("/orders")async def create_order(order_data: OrderData): # 非同期処理 async with httpx.AsyncClient() as client: response = await client.post("https://api.example.com/charge", json={ "orderId": order.id, "amount": order_data.amount, }) return response.json()特徴:
- 信頼できる非同期:
async/awaitによる制御 - エラーハンドリング: try-exceptでエラーを処理
- 再実行: 手動で実装する必要がある
他言語との比較:
// Node.js: Promise/async-awaitasync function createOrder(orderData: OrderData) { const response = await fetch('https://api.example.com/charge', { method: 'POST', body: JSON.stringify(orderData), }); return await response.json();}実行環境による特性
Section titled “実行環境による特性”| 環境 | 特徴 | 主なリスク |
|---|---|---|
| Serverless (Lambda/Vercel) | 短寿命・自動スケール | コールドスタート、接続バースト、DBパンク、実行時間制限(Lambda: 15分、Vercel: 300秒) |
| 常駐プロセス (Uvicorn) | 長寿命・安定動作 | メモリリーク、イベントループのブロック、接続リーク |
Serverless環境での実行
Section titled “Serverless環境での実行”制約:
# ❌ 悪い例: Serverless環境で問題のあるコードfrom fastapi import FastAPI
app = FastAPI()
@app.post("/orders")async def create_order(order_data: OrderData): # 問題: 長時間実行される可能性がある # 問題: トランザクションが長時間保持される # 問題: 接続プールが適切に管理されない order = await create_order_in_db(order_data) return order問題点:
- 実行時間の制限: Lambdaは最大15分、Vercelは最大300秒
- コールドスタート: Pythonの起動に時間がかかる(500ms-2s)
- メモリ制限: メモリ使用量に制限がある(Lambda: 128MB〜10GB)
- 接続バースト: スケールアウト時に接続プールが急増し、DBがパンクする可能性
解決策:
# ✅ 良い例: Serverless環境に適したコードfrom fastapi import FastAPIfrom celery import Celery
app = FastAPI()celery_app = Celery('tasks', broker='redis://localhost:6379')
@app.post("/orders")async def create_order(order_data: OrderData): # 1. バリデーション(短時間) validate_order_data(order_data)
# 2. 注文を作成(短時間) order = await create_order_in_db(order_data)
# 3. 非同期処理をキューに投入 process_order.delay(order.id)
# 4. 即座にレスポンスを返す return {"orderId": order.id, "status": "PROCESSING"}常駐プロセス環境での実行
Section titled “常駐プロセス環境での実行”Uvicornアプリケーションサーバー
Section titled “Uvicornアプリケーションサーバー”特徴:
# 常駐プロセス環境での実行import uvicorn
if __name__ == "__main__": uvicorn.run(app, host="0.0.0.0", port=8000)メリット:
- 長時間実行可能: 実行時間の制限がない
- 接続プール: データベース接続プールを保持
- キャッシュ: メモリキャッシュを保持
- バックグラウンド処理: Celeryによる処理
実装例:
# ✅ 良い例: 常駐プロセス環境に適したコードfrom fastapi import FastAPIfrom celery import Celery
app = FastAPI()celery_app = Celery('tasks', broker='redis://localhost:6379')
@app.post("/orders")async def create_order(order_data: OrderData): # トランザクション内で処理 order = await create_order_in_db(order_data)
# バックグラウンド処理をCeleryで実行 process_order.delay(order.id)
return orderFastAPIの実行モデルと前提のポイント:
- リソースの物理的制約: CPU・メモリよりも先に枯渇するのは、DB接続数・イベントループのブロック・ファイル記述子・メモリリーク
- Python: 動的型付け、ガベージコレクション、GIL、非同期処理
- トランザクション境界:
db.commit()で明示、手動ロールバック - 非同期処理:
async/awaitによる制御、エラーハンドリングが必要(同期I/O操作は避ける) - Serverless環境: 実行時間制限、コールドスタート、メモリ制限、接続バースト
- 常駐プロセス環境: 長時間実行可能、接続プール、キャッシュ、Celery(メモリリーク・接続リークに注意)
重要な原則: 性能ではなく制約を前提に設計する。イベントループのブロックや接続リークは数時間後にシステム全体を停止させる。