これは本質的に2つの別々の問題として扱われるべきです:
- 各労働者が処理する仕事を見つける。理想的には、これは非常に効率的であり、次に来るステップ2の失敗を先制的に回避する必要があります。
- 各ジョブが最大で1回または正確に1回処理されるようにします。何が起こっても、同じジョブを複数のワーカーが同時に処理することはできません。バギー/クラッシュしたワーカーが原因でジョブが失われないようにすることをお勧めします。
どちらの問題にも、実行可能な解決策が複数あります。私の好みについていくつか提案します:
処理する仕事を見つける
低速システムの場合は、最新の未処理のジョブを探すだけで十分です。あなたはまだその仕事を引き受けたくはありません、ただそれを候補者として特定してください。これは次のようになります。
SELECT id FROM jobs ORDER BY created_at ASC LIMIT 1
(これにより、最も古いジョブ(FIFOオーダー)が最初に処理され、処理後に行が削除されると想定されることに注意してください。)
仕事を要求する
この単純な例では、これは次のように単純になります(物事を不明確にする可能性のある最適化を回避していることに注意してください)。
BEGIN;
SELECT * FROM jobs WHERE id = <id> FOR UPDATE;
DELETE FROM jobs WHERE id = <id>;
COMMIT;
SELECT
によって照会されたときにリターンがジョブを返す場合、id
これでロックされました。別のワーカーがすでにこのジョブを引き受けている場合は、空のセットが返されるため、別のジョブを探す必要があります。SELECT ... FOR UPDATE
2人の労働者が同じ仕事を求めて競争している場合、前のステートメントが普遍的に真実であるように、彼らはそれ以降、お互いをブロックします。これにより、各ジョブが最大で1回処理されるようになります。でも...
ジョブを1回だけ処理する
以前の設計のリスクは、ワーカーがジョブを取得し、それを処理できず、クラッシュすることです。仕事は今失われています。そのため、ほとんどのジョブ処理システムは、要求時にジョブを削除せず、代わりに、一部のワーカーによって要求されたものとしてマークを付け、ジョブ再利用システムを実装します。
job
これは、テーブル内の追加の列または別のテーブルのいずれかを使用してクレーム自体を追跡することで実現できclaim
ます。通常、ホスト名、PIDなどのワーカーに関する情報が書き込まれ(claim_description
)、クレームには有効期限(claim_expires_at
)が1時間先などに提供されます。次に、追加のプロセスがそれらのクレームを処理し、有効期限を過ぎたクレームをトランザクションで解放します(claim_expires_at < NOW()
)。次に、ジョブを要求するにはclaim_expires_at IS NULL
、選択時とで要求するときの両方で、ジョブ行の要求()をチェックする必要がありますSELECT ... FOR UPDATE
。
このソリューションにはまだ問題があることに注意してください。ジョブが正常に処理されたが、ジョブが完了として正常にマークされる前にワーカーがクラッシュした場合、最終的にクレームを解放してジョブを再処理する可能性があります。読者の練習問題として残されている、より高度なシステムを必要とする修正。;)