【PR】この記事にはA8.netのアフィリエイト広告を含みます。
先に結論:XServer VPS上のPostgreSQLで20件のAIジョブを作り、workerが10件をIN_PROGRESSにした直後に止まる状態を再現しました。leaseなしでは10件が取り残されましたが、3秒leaseありでは3.095秒で回収し、20/20件完了、重複完了0件でした。この限定した悩みの判定は解決です。
AIワーカーが処理中に落ちるとジョブはどうなる?
statusをIN_PROGRESSへ変えるだけでは、永遠に残る可能性があります。今回のleaseなし条件では、worker-aがclaimしたjob 1〜10は3.5秒待ってもIN_PROGRESSのままでした。worker-bは残り10件を完了しましたが、最終結果はSUCCEEDED 10件、IN_PROGRESS 10件です。
Android実機を操作する自宅workerが停電、通信断、process crashで戻らない場合、VPS側から見ると「まだ処理中」なのか「死んだ」のかをstatusだけでは判別できません。
PostgreSQLのjob leaseとは?
jobを永久所有せず、期限付きで借りる仕組みです。workerがjobをclaimするときにlease_untilを保存し、期限までに完了またはlease延長がなければ、別workerが回収できるようにします。
悩みの起点にしたZenn「DBポーリング型ジョブ実行基盤の設計」でも、READYからIN_PROGRESSへの排他遷移、古いIN_PROGRESSの回収、処理の冪等性が重要点として挙げられています。
PostgreSQL 17公式のSELECT locking clauseにあるFOR UPDATE SKIP LOCKEDを使い、他workerが確保した行を待たずに次のjobを選びました。
XServer VPSでどんな実験をした?
| 項目 | 実測条件 |
|---|---|
| VPS | XServer VPS、4 vCPU、4GB RAM、150GB |
| OS | Ubuntu 26.04 |
| DB | postgres:17.10-alpine |
| job | 各条件20件 |
| 停止worker | worker-aが10件claim後に戻らない状態 |
| 比較 | leaseなし/3秒lease |
| container上限 | 0.5 CPU、256MiB RAM、追加swapなし、PID 128 |
| network | none、公開portなし |
本番PostgreSQLへ障害を入れず、条件ごとに新しい隔離containerを作成しました。worker crashは、claimをcommitしたclientが終了し、その後一切更新しない状態として再現しています。
leaseなしと3秒leaseの結果は?
| 指標 | leaseなし | 3秒lease |
|---|---|---|
| worker-aが残したjob | 10件 | 10件 |
| 期限後の回収 | できない | 10件 |
| 回収時間 | なし | 3.095157秒 |
| 最終SUCCEEDED | 10/20 | 20/20 |
| 最終IN_PROGRESS | 10/20 | 0/20 |
| retry | 0件 | 10件 |
| 重複完了 | 0件 | 0件 |
| cgroup peak RAM | 85.56MiB | 85.57MiB |
| container CPU | 2.001873秒 | 2.628144秒 |
3秒leaseでは0.1秒間隔で期限切れを確認し、18回目のpoll、claimから3.095157秒後にjob 1〜10を回収しました。その後worker-bが再claimし、全20件を完了しました。
二重実行は起きなかった?
今回の完了記録では0件でした。完了eventは20件、異なるjob IDも20件、1つのjobに付いた完了eventの最大数は1です。
ただし、これは「古いworkerが戻ってこない」条件です。lease切れ後にworker-aが復活して外部APIを呼ぶと、新workerと二重実行になる可能性があります。DB更新にはfencing tokenを加え、決済、投稿、通知などの外部副作用にはidempotency keyが必要です。
実験中に何が失敗した?
初回はpg_isready -d exp26が成功したのでtable作成へ進みましたが、直後にdatabase "exp26" does not existで失敗しました。
PostgreSQL containerの初期化中は、serverが接続を受けられても、指定したdatabaseの作成がまだ終わっていない瞬間があります。pg_isreadyだけでは、アプリが本当に使えるready状態を確認できませんでした。
対策は、実際にexp26へ接続し、SELECT 1が成功するまで待つことです。修正後は両条件とも成功しました。単なるprocess生存確認と、依存先まで使えるready確認を分ける必要があります。
CPU・RAM・ストレージへの影響は?
cgroup peak RAMはleaseなし85.56MiB、3秒lease85.57MiBでほぼ同じでした。3秒leaseのcontainer CPUは2.628144秒で、pollとretryの分だけleaseなしの2.001873秒より増えています。
hostのMemAvailableは3297.63MiBから3236.47MiB、root disk使用量は57,344 byte増でした。test containerとPostgreSQL匿名volumeは削除し、dangling volumeは前後0件です。
本番のCaddy・FastAPI・PostgreSQLは止まった?
止まりませんでした。公開/health/readyは実験前、leaseなし実行中、leaseあり実行中、実験後の4回すべてHTTP 200でした。応答本文もstatus=ready、database=ok、artifact_storage=okです。
実験containerは--network=noneで外部portを公開せず、本番containerの停止・再起動・設定変更は行っていません。
AIスマホ調査の司令塔へどう組み込む?
実運用では、VPSのPostgreSQLに次の列と処理を持たせます。
status、worker_id、lease_until、attemptsを保存する。FOR UPDATE SKIP LOCKEDで複数workerのclaim競合を避ける。- Android操作中はheartbeatでleaseを延長する。
- 期限切れをPENDINGへ戻し、指数backoff付きでretryする。
- 最大retry超過はdead-letterへ移し、失敗理由とscreenshotを残す。
- 結果更新にはlease世代を表すfencing tokenを照合する。
- 記事投稿や課金操作にはidempotency keyを付ける。
3秒は短い実験値です。実際のアプリ調査は数分かかるため、job timeoutを長くするだけでなく、処理中にleaseを小刻みに延長する設計が適しています。
初心者が迷いそうなポイントは?
IN_PROGRESSはworkerが生きている証明ではない。- DBのrow lockはtransaction終了時に解放されるが、status値は自動で戻らない。
pg_isready成功と、目的のDB・tableが使えることは同じではない。- leaseが短すぎると正常処理まで回収し、長すぎると復旧が遅い。
- retry可能にするには処理を冪等に設計する必要がある。
- DB上の重複0件だけでは、外部サービスへの二重送信0件を保証できない。
XServer VPSでこの悩みはどこまで解決した?
判定は解決です。「workerが止まるとjobが永遠にIN_PROGRESSへ残る」という問題は、3秒leaseで10件を回収し、20/20件を完了できました。PostgreSQL 17の隔離containerは最大約85.57MiBで動き、本番serviceも正常でした。
一方、leaseだけでexactly-once処理になるわけではありません。古いworkerの遅延書き込み、外部副作用、時計、heartbeat、dead-letterは別途検証が必要です。
よくある質問
Q. PostgreSQLだけでAIジョブキューを作れますか?
A. 小〜中規模の司令塔なら候補になります。今回も20件のclaim、期限切れ回収、retry、完了記録をPostgreSQLだけで実行できました。
Q. FOR UPDATEを使えばworker crash時に自動復旧しますか?
A. いいえ。row lockはconnectionやtransaction終了で解放されますが、commit済みのIN_PROGRESSは残ります。leaseと回収処理が必要です。
Q. leaseは何秒に設定すべきですか?
A. jobの正常処理時間より十分長くし、処理中はheartbeatで延長します。今回の3秒は復旧を短時間で観測するための実験値です。
Q. retryするとAI操作が二重実行されませんか?
A. 可能性があります。fencing token、idempotency key、処理済み判定を組み合わせ、古いworkerの結果を拒否する必要があります。
Q. 4GBのXServer VPSでPostgreSQLキューは重いですか?
A. 今回の20件・単一worker相当ではcgroup peak約85.57MiBでした。ただし大規模queueや高同時実行の値ではありません。
実験日:2026年9月2日。数値は筆者が契約中のXServer VPS 4GB環境における単発の実測です。契約plan、host負荷、software version、job件数により結果は変わります。

コメント