Watch at Ease看見世界,讀懂生活。

科技 · 實用指南

Python 出現「BrokenProcessPool」?重試批次前先找出死亡 worker

父程序例外只證明某個 worker 非正常結束,並不能說明死因。先保留 PID 與任務標記,區分啟動失敗、原生崩潰和 OOM,再安全重建程序池。

Original technical diagram joining a ProcessPoolExecutor parent task ledger, worker PID markers, and operating-system exit evidence.
原創示意圖:把失敗的 Future 連接到 worker PID 和外部退出證據。 Watch at Ease

BrokenProcessPool 表示至少一個 ProcessPoolExecutor worker 非正常結束。它不等於普通任務例外,也不自動證明 OOM、段錯誤或匯入錯誤。worker 消失後,多條待處理 Future 可能收到同一個池級例外。

應把父程序任務帳本、worker PID/任務標記與作業系統證據連接起來。只有隔離出一個原因分支和一個可疑輸入後,才重建程序池。

重試前凍結父程序帳本

為每次提交分配任務 ID 和穩定輸入鍵,記錄提交時間、Future、副作用邊界及首個結果。池損壞後停止批量重試並保存完整 stderr 與服務日誌。

把已完成副作用與僅提交的任務分開。多條 Future 都報 BrokenProcessPool,不代表每個輸入都殺死了 worker。

標記 worker 啟動並劃分階段

在任務入口 flush 包含 PID、任務 ID、輸入鍵、Python 版本、啟動方式與階段的結構化標記。initializer 只做小而確定的初始化。

若沒有入口標記,用真實 .py 檔測試:主模組可匯入、需要時用 main guard、函式位於模組頂層、參數可序列化。不要盲目切換 fork。

收集原生崩潰和訊號證據

在可疑原生程式碼前啟用 faulthandler,讓致命訊號寫入持久 stderr。保存 crash report、service manager 狀態、容器終止原因及可用退出碼。

負退出碼可表示訊號,但池判壞後其他 worker 也可能被終止。用最早死亡 PID 和時間關聯根因。

證明或排除 OOM 分支

Linux cgroup v2 環境在運行前後讀 memory.events 或 memory.events.local。oom_kill 增量才證明 OOM killer 殺過程序。

同時記錄 cgroup 路徑、memory.current、可用時的 memory.peak、worker 數與輸入大小。降低 worker 只是實驗。

縮減到一個 worker 和一個輸入

先直接串行執行同一 callable,再用單 worker executor。保持任務/PID 標記,二分加入輸入。單 worker 仍死亡時檢查原生函式庫、分配或資料邊界。

單 worker 成功而多 worker 失敗時,比較總記憶體、檔案描述符、原生執行緒池與啟動方式。

用因果閉環測試收尾

閉環需有一條完整任務、一個已知 PID、正常結果或普通 Future 例外、oom_kill 不變、沒有致命 crash。再用小並發確認每個輸入只處理一次。

最後才調整 worker 數、記憶體限制、啟動方式、依賴、initializer 或重試策略,並記錄原因、版本、證據窗口和冪等規則。

Original decision diagram separating Python process-pool spawn failures, native crashes, OOM kills, and normal task exceptions.
原創示意圖:修改並發或重試策略前先關閉一個死亡原因分支。 Watch at Ease