jpskill.com
💬 コミュニケーション コミュニティ

backend-hang-debug

ニュース配信ルートでFastAPIが停止する原因を、py-spyで特定し、ThreadPoolExecutorのシャットダウンを妨げないパターンを適用して、問題を診断・修正するSkill。

📜 元の英語説明(参考)

Diagnose and fix FastAPI hangs caused by blocking ThreadPoolExecutor shutdown in the news stream route; includes py-spy capture and non-blocking executor pattern.

🇯🇵 日本人クリエイター向け解説

一言でいうと

ニュース配信ルートでFastAPIが停止する原因を、py-spyで特定し、ThreadPoolExecutorのシャットダウンを妨げないパターンを適用して、問題を診断・修正するSkill。

※ jpskill.com 編集部が日本のビジネス現場向けに補足した解説です。Skill本体の挙動とは独立した参考情報です。

⚡ おすすめ: コマンド1行でインストール(60秒)

下記のコマンドをコピーしてターミナル(Mac/Linux)または PowerShell(Windows)に貼り付けてください。 ダウンロード → 解凍 → 配置まで全自動。

🍎 Mac / 🐧 Linux
mkdir -p ~/.claude/skills && cd ~/.claude/skills && curl -L -o backend-hang-debug.zip https://jpskill.com/download/17502.zip && unzip -o backend-hang-debug.zip && rm backend-hang-debug.zip
🪟 Windows (PowerShell)
$d = "$env:USERPROFILE\.claude\skills"; ni -Force -ItemType Directory $d | Out-Null; iwr https://jpskill.com/download/17502.zip -OutFile "$d\backend-hang-debug.zip"; Expand-Archive "$d\backend-hang-debug.zip" -DestinationPath $d -Force; ri "$d\backend-hang-debug.zip"

完了後、Claude Code を再起動 → 普通に「動画プロンプト作って」のように話しかけるだけで自動発動します。

💾 手動でダウンロードしたい(コマンドが難しい人向け)
  1. 1. 下の青いボタンを押して backend-hang-debug.zip をダウンロード
  2. 2. ZIPファイルをダブルクリックで解凍 → backend-hang-debug フォルダができる
  3. 3. そのフォルダを C:\Users\あなたの名前\.claude\skills\(Win)または ~/.claude/skills/(Mac)へ移動
  4. 4. Claude Code を再起動

⚠️ ダウンロード・利用は自己責任でお願いします。当サイトは内容・動作・安全性について責任を負いません。

🎯 このSkillでできること

下記の説明文を読むと、このSkillがあなたに何をしてくれるかが分かります。Claudeにこの分野の依頼をすると、自動で発動します。

📦 インストール方法 (3ステップ)

  1. 1. 上の「ダウンロード」ボタンを押して .skill ファイルを取得
  2. 2. ファイル名の拡張子を .skill から .zip に変えて展開(macは自動展開可)
  3. 3. 展開してできたフォルダを、ホームフォルダの .claude/skills/ に置く
    • · macOS / Linux: ~/.claude/skills/
    • · Windows: %USERPROFILE%\.claude\skills\

Claude Code を再起動すれば完了。「このSkillを使って…」と話しかけなくても、関連する依頼で自動的に呼び出されます。

詳しい使い方ガイドを見る →
最終更新
2026-05-18
取得日時
2026-05-18
同梱ファイル
1

📖 Skill本文(日本語訳)

※ 原文(英語/中国語)を Gemini で日本語化したものです。Claude 自身は原文を読みます。誤訳がある場合は原文をご確認ください。

バックエンドハングのデバッグ

目的

  • SSEニュースストリームにおける同期executorのシャットダウンが原因で、FastAPIアプリが応答しなくなる(例:curl http://localhost:8000/ がタイムアウトする)イベントループのハングを検出し、解決します。
  • py-spy を使用してライブスタックをキャプチャし、ブロッキングコードを特定するための、再現可能なトリアージフローを提供します。

範囲

  • バックエンド: backend/app/api/routes/stream.py (ニュースストリーム), backend/app/services/rss_ingestion.py (RSSワーカー), 起動プロセス。
  • ツール: ライブスタックダンプ用の py-spy; スモークテスト用のタイムアウト付き curl

クイックトリアージ

  1. ハングの再現: curl -m 5 http://localhost:8000/ および curl -m 5 http://localhost:8000/health を実行し、タイムアウトに注意してください。
  2. プロセスチェック: ss -tlnp | grep 8000 でリスナーを確認します。ls /proc/$(pgrep -f "uvicorn app.main")/fd | wc -l でFDリークを除外します。
  3. スタックキャプチャ (バックエンドのvenv内): uv pip install py-spy を実行後、sudo /home/bender/classwork/Thesis/backend/.venv/bin/py-spy dump --pid $(pgrep -f "uvicorn app.main") を実行します (マルチプロセスの場合はワーカーのpidも)。api/routes/stream.py のフレームで ThreadPoolExecutor.shutdown を探します。

修正パターン (ノンブロッキングexecutor)

  • event_generator 内の同期コンテキストマネージャー with ThreadPoolExecutor(...): を、長寿命のexecutorと明示的な ノンブロッキング シャットダウンに置き換えます。
    • コンテキストマネージャーの外でexecutorを作成します。
    • クライアントが切断されたら、シャットダウンを待機する代わりに、保留中のfutureをキャンセルします。
    • finally で、executor.shutdown(wait=False, cancel_futures=True) を呼び出します。
  • 理由: コンテキストマネージャーは shutdown(wait=True) を呼び出し、RSSワーカーのスレッドがネットワークI/Oでハングすると、イベントループをブロックします。

実装手順

  1. backend/app/api/routes/stream.pyストリームexecutorの使用法を更新 します。
    • executor = concurrent.futures.ThreadPoolExecutor(max_workers=5) をインスタンス化します。
    • loop.run_in_executor(executor, _process_source_with_debug, ...) を介して作業をディスパッチします。
    • 切断時に、保留中のfutureを cancel() します。
    • finally で、executor.shutdown(wait=False, cancel_futures=True) を実行します。
  2. RSS executorは現状維持 (rss_ingestion.py) とします。これはバックグラウンドスレッドで実行されるためですが、リクエストのタイムアウトが妥当な範囲内(現在はRSS requests.get ごとに60秒)であることを確認してください。
  3. 再テスト:
    • uvicornを再起動します。curl -m 5 http://localhost:8000/health が応答するはずです。
    • ストリームリクエストを開始し、クライアントを中断します。サーバーは応答性を維持する必要があります。
    • py-spy dump を再実行して、メインスレッドに ThreadPoolExecutor.shutdown(wait=True) フレームがないことを確認します。

検証チェックリスト

  • [ ] curl -m 5 http://localhost:8000/ が応答を返します (ハングしない)。
  • [ ] curl -m 5 http://localhost:8000/health が成功します。
  • [ ] /news/stream を中断しても、後続のリクエストがフリーズしません。
  • [ ] py-spy dump は、イベントループが ThreadPoolExecutor.shutdown でブロックされていないことを示しています。
  • [ ] バックエンドがストリームでビジーの間、フロントエンドがルート/ヘルスを待機して停止することがなくなりました。

注記と将来の強化

  • 低速なハンドラーで高速に失敗するように、リクエストタイムアウトミドルウェアの追加を検討してください。
  • 長寿命のスレッドを減らすために、ソースごとのネットワークタイムアウトと、RSSフィードのより短いリトライを追加します。
  • マルチワーカーのuvicornを使用している場合は、ハングを診断するときに、各ワーカーのpidで py-spy を実行します。
📜 原文 SKILL.md(Claudeが読む英語/中国語)を展開

Backend Hang Debug

Purpose

  • Detect and resolve event-loop hangs where the FastAPI app stops responding (e.g., curl http://localhost:8000/ times out) due to synchronous executor shutdown in the SSE news stream.
  • Provide a repeatable triage flow using py-spy to capture live stacks and pinpoint blocking code.

Scope

  • Backend: backend/app/api/routes/stream.py (news stream), backend/app/services/rss_ingestion.py (RSS workers), startup processes.
  • Tooling: py-spy for live stack dumps; curl with timeouts for smoke tests.

Quick Triage

  1. Reproduce hang: curl -m 5 http://localhost:8000/ and curl -m 5 http://localhost:8000/health; note timeouts.
  2. Process check: ss -tlnp | grep 8000 to confirm listener; ls /proc/$(pgrep -f "uvicorn app.main")/fd | wc -l to rule out FD leak.
  3. Stack capture (inside backend venv): uv pip install py-spy then sudo /home/bender/classwork/Thesis/backend/.venv/bin/py-spy dump --pid $(pgrep -f "uvicorn app.main") (and worker pid if multiprocess). Look for ThreadPoolExecutor.shutdown in api/routes/stream.py frames.

Fix Pattern (non-blocking executor)

  • Replace synchronous context manager with ThreadPoolExecutor(...): inside event_generator with a long-lived executor plus explicit non-blocking shutdown:
    • Create executor outside the context manager.
    • On client disconnect, cancel pending futures instead of awaiting shutdown.
    • In finally, call executor.shutdown(wait=False, cancel_futures=True).
  • Rationale: context manager calls shutdown(wait=True), blocking the event loop if RSS worker threads hang on network I/O.

Implementation Steps

  1. Update stream executor usage in backend/app/api/routes/stream.py:
    • Instantiate executor = concurrent.futures.ThreadPoolExecutor(max_workers=5).
    • Dispatch work via loop.run_in_executor(executor, _process_source_with_debug, ...).
    • On disconnect, cancel() pending futures.
    • In finally, executor.shutdown(wait=False, cancel_futures=True).
  2. Keep RSS executor as-is (rss_ingestion.py) since it runs in background threads, but ensure request timeouts remain reasonable (currently 60s per RSS requests.get).
  3. Retest:
    • Restart uvicorn; curl -m 5 http://localhost:8000/health should respond.
    • Start a stream request and abort the client; server must stay responsive.
    • Re-run py-spy dump to verify no ThreadPoolExecutor.shutdown(wait=True) frames in main thread.

Verification Checklist

  • [ ] curl -m 5 http://localhost:8000/ returns a response (no hang).
  • [ ] curl -m 5 http://localhost:8000/health succeeds.
  • [ ] Aborting /news/stream does not freeze subsequent requests.
  • [ ] py-spy dump shows event loop not blocked on ThreadPoolExecutor.shutdown.
  • [ ] Frontend no longer stalls waiting on root/health while backend is busy with streams.

Notes & Future Hardening

  • Consider adding request timeout middleware to fail fast on slow handlers.
  • Add per-source network timeouts and shorter retries for RSS feeds to reduce long-lived threads.
  • If multi-worker uvicorn is used, run py-spy on each worker pid when diagnosing hangs.