単なる疑似コードであるため、CodeReview.Stackexchange から転載
タスク プロセッサのワーカー ロールを構築しています。タスクは単純な DB テーブル (Guid、Json 文字列、ステータス) に挿入され、Guid は Azure メッセージ キューにプッシュされます。worker ロールはキューからメッセージをポップし、DB から JSON 命令を取得してそのタスクを処理します。
冪等性と再試行のすべてのコーナーケースを確実にカバーし、次の擬似コードを作成しようとしています。
私の主な問題は、「ProcessTask」フェーズが非トランザクション (外部サービスへの HTTP 投稿とサービス呼び出し) であり、繰り返し呼び出しが悪い (Mkay) ことです。
誰かがこれを再確認して、私が特定していないコーナーケースがあるかどうかを確認できますか.
次の情報を持つタスクがデータベースに存在すると仮定します。
| Id | TaskInstruction | Status | PopReceipt |
+------+-----------------------+---------+------------+
| GUID | { ... some json ... } | Pending | NULL |
次の疑似コードは、次の状況をカバーしています。
- ワーカーはメッセージを取得し、時間内に処理し、データベースを更新してメッセージを削除します。
- #1 以外は、30 秒の非表示タイムアウトよりも長くかかりますが、それでも成功します。
- メッセージの処理に失敗したため、再試行する必要があります。
- メッセージの処理に何度も失敗し、許可されている最大デキュー カウントを超えています。
計画では、Azure BlobStorage の AquireLease メカニズムを使用して、冪等性の理由から重要なコードの周りでファイル ロックとして使用する予定です。
疑似コード
BlobAquireLease //LOCK Only Worker Role Can Retrieve at a time
var message = Queue.GetMessage(30); // 30 second invisibility timeout
var dbTask = DBSession.GetTask(message.Id); //Retrieve the DB Entry
If dbTask.Status != Pending OR Retrying
//Crap another role is processing this task but has
//overshot the 30 second timeout and now my popreceipt
//supercedes his - put my popreceit in the DB, and do nothing more
DBSession.UpdateTask(message.Id, newPopReceipt) // put the new pop receipt in the db
BlobReleaseLease //Release the LOCK
return; //Get out of here
EndIf
//Otherwise, I'm ok to handle this task.
DBSession.UpdateTask(message.Id, Status.Processing);
BlobReleaseLock
//At this point, the task is mine, and I've got 30 seconds to do stuff
TRY
ProcessTask(dbtask);
//Successful
BlobAquireLease //Grab the LOCK Again
//Get from DB Again just incase I've
//over shot the 30 seconds and someone else has updated the popreceipt
var dbTaskRefreshed = DBSession.GetTask(message.Id);
DBSession.UpdateTask(message.Id, Status.SUCCESS); //Update the status in the DB
Queue.DeleteMessage(message, dbTaskRefreshed.PopReceipt); //Delete the message
BlobReleaseLease
ENDTRY
CATCH
//An Error Occured and the task couldn't be processed.
//need to update the database
BlobAcquireLease //Grab the LOCK again
IF message.DequeueCount > MAX ALLOWED //Too many Attempts
//Get from DB Again just incase I've
//over shot the 30 seconds and someone else has updated the popreceipt
var dbTaskRefreshed = DBSession.GetTask(message.Id);
DBSession.UpdateTask(message.Id, Status.FAILED);
Queue.DeleteMessage(message, dbTaskRefreshed.PopReceipt)
ELSE
//Haven't reached the max dequeue count yet, so just
//let the invisibility timeout elapse on it's own and someone will retry
DBSession.UpdateTask(message.Id, Status.RETRYING);
END IF
BlobReleaseLease
ENDCATCH