GitHub Repo
Hermes AgentコミュニティがWebhookスケジューリングのデッドロックを報告、共有スレッドプールの枯渇でゲートウェイが停止し続ける可能性
10月27日のHermes Agentコミュニティからの報告によると、Webhookが複数のスケジュール済みジョブを同時に起動すると、デフォルトのスレッドプールが枯渇し、ジョブが長時間`running`のままになる可能性がある。報告には最小再現コードとスタック解析が含まれている。確認時点で問題は未解決のままで、正式な修正はまだない。

Hermes Agentコミュニティは10月27日、ゲートウェイのデッドロックに関する問題を報告した。複数のWebhookが同時にcron_jobを起動すると、ジョブが永久にrunningのままになり、前のジョブが終了していないため後続の起動もスキップされる可能性がある。報告者はスレッドのスタック情報と、モデルサービスに依存しない最小の再現コードを提示した。別のプロジェクト参加者は、この問題をゲートウェイ修正の追跡対象に加えた。確認時点では、Issueはオープンのままで、修正ブランチやPRへのリンクはない。問題報告
報告の分析によると、Webhookはまずasyncio.to_threadを使い、スケジュール実行をデフォルトのスレッドプールに渡す。そのスレッドはさらにrun_coroutine_threadsafe(...).result()を使い、メインイベントループが処理を完了するまで同期的に待機する。一方、メインループが状態の書き込みやジョブの実行を処理する際にも、同じプールの空きスレッドが必要になる。すべてのスレッドが待機状態になると、必要な処理を開始できず、循環待ちが発生する。呼び出し経路と再現コード
Pythonの公式ドキュメントは、スレッドプール内の処理が別のFutureの完了を待つことで、デッドロックが起こり得ると明示している。Python 3.13以降、デフォルトのスレッド数の上限はmin(32, (os.process_cpu_count() or 1) + 4)だ。そのため、報告にある6コアの環境ではデフォルトのワーカースレッドは10個となる。「並行ジョブが10個なら発生する」と、すべてのデプロイに共通する固定のしきい値として捉えることはできない。スレッドプールのドキュメント
この問題は、非同期インターフェースでも待機関係を確認する必要があることを示している。run_coroutine_threadsafeが返すFutureは、別のOSスレッドから結果を待つために使える。公式の例では、.result()にタイムアウトを設定している。こうした仕組みから考えると、デプロイの監視ではイベントループが応答しているかだけでなく、ジョブの状態が継続的に進んでいるかも確認し、実行リソースが停滞した状況を検知する必要がある。スレッド間スケジューリングのドキュメント
報告者によると、Webhookのタイミングをずらして重複を減らした後は、停止が再発していない。ただし、これは単一環境での観察にすぎない。今後は、独立したエグゼキューターへの切り替え、メインループの操作を直接待機する方法、タイムアウトやリソースの稼働状況のチェックの導入が検討点となる。正式な修正は引き続き検証が必要であり、問題の追跡を修正のリリースとみなすことはできない。対応案