MacBook ProとMac miniの2台がVPSへ接続する構成では、単に未処理ジョブを取得するだけでは不十分です。

両方のMacが同時に同じジョブを取得すると、同じ記事や画像を二重に生成してしまいます。

また、ジョブを取得したMacがスリープしたり、生成中にアプリが停止したりする可能性もあります。

今回は、ロック、リース、ハートビート、再試行を使って、ジョブ処理を安定させます。

ジョブの状態

ジョブには次の状態を持たせます。

pending

まだどのワーカーにも取得されていない状態です。

processing

ワーカーが取得し、処理を開始した状態です。

completed

生成結果が正常にVPSへ返された状態です。

failed

生成処理に失敗し、再試行回数の上限へ到達した状態です。

必要に応じて、キャンセル状態や確認待ち状態を追加できます。

同じジョブを二重に取得させない

MacBook ProとMac miniが同時にVPSへ問い合わせると、両方が同じpendingジョブを見つける可能性があります。

これを防ぐには、ジョブの取得と状態変更を一つのデータベース処理として実行します。

処理の流れは次のとおりです。

  1. 未処理のジョブを1件選ぶ
  2. そのジョブをロックする
  3. 状態をprocessingへ変更する
  4. 担当ワーカーを記録する
  5. ロック期限を設定する
  6. 変更後のジョブをワーカーへ返す

この一連の処理中は、別のワーカーが同じジョブを取得できないようにします。

重要なのは、ジョブを選んだ後に別処理で状態を更新するのではなく、データベース上で原子的に扱うことです。

リース期限

ジョブを取得したMacが、そのまま処理を完了できるとは限りません。

例えば、次のような状況があります。

  • Macがスリープした
  • Macを再起動した
  • 自宅回線が切れた
  • OllamaやComfyUIが停止した
  • Pythonワーカーが異常終了した
  • 生成中にメモリ不足になった

processing状態のまま永久に残ると、別のMacが引き継げません。

そこで、ジョブのロックには有効期限を設定します。

これをリースとして扱います。

ジョブには次の情報を保存します。

  • 担当ワーカー
  • 処理開始時刻
  • 最終ハートビート時刻
  • リース有効期限

リース期限を過ぎてもワーカーから連絡がない場合、VPSはそのジョブを再びpendingへ戻します。

ハートビート

記事生成や画像生成には、数十秒から数分かかる可能性があります。

処理時間が長いだけなのか、Macが停止しているのかをVPSが判断するため、ワーカーは一定間隔でハートビートを送ります。

例えば30秒ごとに次の情報を送ります。

  • ジョブID
  • ワーカーID
  • 現在の処理段階
  • 処理開始からの経過時間

VPSはハートビートを受信するたびにリース期限を延長します。

画像生成で複数工程がある場合は、進捗段階も送れます。

例としては次のような状態です。

  • loading_model
  • generating_text
  • generating_image
  • upscaling
  • uploading
  • finalizing

管理画面から現在の状態を確認しやすくなります。

通信障害時の再接続

VPSや自宅回線に一時的な障害が発生した場合、ワーカーは再接続します。

ただし、接続失敗後に間隔を空けず何度もリクエストすると、障害中のVPSへ余計な負荷をかけます。

そのため、再試行間隔を段階的に増やします。

例として次の間隔を使用します。

  • 1秒
  • 2秒
  • 5秒
  • 10秒
  • 30秒
  • 60秒

接続に成功したら、待機時間を初期状態へ戻します。

さらに、複数のワーカーが完全に同じタイミングで再接続し続けないよう、少しランダムな待ち時間を加えます。

ジョブの再試行

一時的なエラーであれば、ジョブを再試行します。

例えば次のようなエラーです。

  • モデルの読み込み失敗
  • 一時的なメモリ不足
  • ComfyUIへの接続失敗
  • VPSへのアップロード失敗
  • ネットワークの一時切断

一方で、入力内容そのものに問題がある場合、何度再試行しても成功しません。

例えば次のようなケースです。

  • 必須項目がない
  • 存在しないモデルを指定している
  • 対応していない画像サイズ
  • 不正なジョブ種別
  • ファイル形式が正しくない

エラーの種類を分類し、再試行可能なものだけを再実行します。

再試行回数には上限を設定し、上限を超えたらfailedへ移します。

二重送信への対応

生成は完了していても、結果送信中に通信が切れることがあります。

Mac側では送信に失敗したように見えても、VPS側ではすでに結果を受信している可能性があります。

そのまま再送すると、同じ記事や画像が重複登録される恐れがあります。

これを防ぐため、同じジョブIDに対する完了処理を複数回実行しても、結果が重複しないようにします。

VPS側では、すでにcompletedになっているジョブへ同じ結果が届いた場合、重複登録せず正常応答を返します。

Macが処理途中で戻ってきた場合

MacBook Proがジョブを取得した後に通信が切れ、リース期限切れによってMac miniが引き継ぐ可能性があります。

その後、MacBook Proが復旧して古い結果を返してくるかもしれません。

VPS側は、現在の担当ワーカーやリース情報を確認します。

別のワーカーへ再割り当て済みであれば、古いワーカーからの結果を受け付けないようにします。

これにより、後から届いた古い結果で新しい処理結果が上書きされることを防ぎます。

ログと監視

安定運用するには、次の情報をログとして保存します。

  • ジョブ作成日時
  • 取得したワーカー
  • 処理開始と終了時刻
  • 処理時間
  • ハートビート履歴
  • 再試行回数
  • エラー内容
  • モデル名
  • 生成設定
  • 返却ファイルサイズ

最初から高度な監視システムを導入する必要はありません。

まずはVPS上の管理画面やログファイルで、処理状況を確認できるようにします。

まとめ

複数のMacで生成ジョブを処理するには、単純な未処理ジョブ取得だけでは不十分です。

次の仕組みを組み合わせます。

  • 原子的なジョブ取得
  • processing状態
  • ワーカーごとのロック
  • リース期限
  • ハートビート
  • 再試行
  • 指数バックオフ
  • 二重完了の防止
  • 古いワーカー結果の拒否

これにより、MacBook ProとMac miniが同時に動いても、同じジョブを二重処理しにくくなります。

次回は、自宅で仕事をしている時間と、夜間・外出時で、2台のMacを切り替えて使う方法を考えます。