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

backend-hang-debug

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

⚡ おすすめ: コマンド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
📖 Claude が読む原文 SKILL.md(中身を展開)

この本文は AI(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.