Relaylab

ACK 遺失、租約過期與冪等寫入的事件模擬。

訊息副作用成功提交,但 ACK 在途中遺失。投遞租約到期後,另一個 worker 再次取到同一筆訊息。Relaylab 以固定種子的離散事件模型,重播這段處理過程。

TypeScriptDeterministic simulationBrowser / CLI 共用核心
Relaylab worker 時間軸、事件紀錄與訊息狀態的實際畫面
互動時間軸呈現模擬投遞;這不是對真實 message broker 的壓力測試。

ACK 遺失與重複寫入

第一次處理就已提交效果,但確認訊息遺失。系統無法從沒有 ACK 這件事推論「處理失敗」,因此到期後重送。單靠重試能提高處理成功機會,卻不能保證每筆業務效果只執行一次。

內建 Lost ACK 情境的固定結果
指標冪等關閉冪等開啟
Deliveries1313
Committed effects1312
Duplicate effects10

重複投遞沒有消失;被抑制的是第二次寫入。這組數字由程式的範例情境與固定結果測試支持,不代表實際營運統計。

事件與租約流程

Produce / Ready

新訊息依到達順序進入佇列,等待可用 worker。

Dispatch / Lease

每次投遞建立獨立 receipt ID,並登記 visibility timeout。

Write / ACK

成功的 worker 先提交效果,然後才可能遇到 ACK 遺失;只有有效 receipt 能確認完成。

Expire / Retry

租約過期不代表舊 worker 停止執行;重試和舊工作可能同時進行。

排程器以虛擬時間排序事件,同時刻則依插入順序處理。從同一筆 seed 與 scenario 重新運行狀態機,可回到任何一步觀察結果,不依賴預先錄製好的影格。

npm ci
npm run simulate -- --preset lost-ack --out lost-ack.json
npm run simulate -- --preset lost-ack --idempotent --out idempotent.json
npm test

需要 Node.js 24 或更新版本。CLI 拒絕覆寫已存在的輸出檔案;匯出的情境與報告有不同格式,避免把快照誤當成可重播的輸入。

競態與不變量測試

測試在每次轉換後檢查產生數守恆、時間不倒退、重試次數限制、worker 是否只持有一筆工作,以及啟用冪等時「每筆訊息最多一筆效果」的規則。

測試還涵蓋同時刻的完成與過期、已失效 receipt 的 ACK、不允許舊失敗覆蓋新租約、worker crash/recovery,及 100 組不同種子與參數的驗證。

模型假設與死信狀態

模型採單一 queue、原子冪等鍵集合、不過期的 idempotency record、虛擬時間及預先定義的失敗位置。沒有實際網路、資料庫交易、broker 複寫、時鐘飄移或真實 I/O,不能用來聲稱模擬了 SQS、Kafka 或 RabbitMQ 的完整行為。

即使訊息因重試耗盡進入 dead state,舊 worker 仍可能在租約失效後完成副作用。「已進 DLQ」不等於「這筆操作從未發生」。

可從 Lost ACK 情境開始,比較兩種冪等模式,再切換短租約情境,對照 模型假設與測試。