1380 字
7 分鐘

Durable Objects 的 ctx.abort() 怎麼避免 alarm 重試?用 retryAlarm: false

Cloudflare Durable Object 的 ctx.abort() 會立即重設目前的 object instance。過去只要在 alarm 執行期間呼叫它,被中斷的 alarm 預設會在 Durable Object 重設後再次重試;這對避免無關 request 意外取消排程很安全,但對「清理成功後不要再跑一次」的工作就可能造成重複執行。

現在要明確停止被中斷的 alarm,可以在 ctx.abort() 傳入 { retryAlarm: false }。最小修正如下:

import { DurableObject } from "cloudflare:workers";
export class CleanupTask extends DurableObject {
async alarm(): Promise<void> {
await this.ctx.storage.deleteAll();
this.ctx.abort("Cleanup complete", { retryAlarm: false });
}
}

重點不是把每一個 ctx.abort() 都加上選項,而是把「這一次 abort 是否應該讓 alarm 再試一次」變成每個呼叫點都能說清楚的決策。

ctx.abort() 的預設行為是什麼#

Cloudflare 文件目前的規則可以整理成三種情況:

呼叫方式被中斷的 alarm適合的情境
ctx.abort("reason")Durable Object reset 後預設重試無關 request abort,但不希望它永久取消既有 alarm
ctx.abort("reason", { retryAlarm: true })明確保留重試你希望把重試意圖直接寫在程式碼上
ctx.abort("reason", { retryAlarm: false })不重試這次被中斷的 alarm清理完成、使用者取消工作,或該 alarm 不應再執行

retryAlarm 控制的是「被這次 abort 中斷的 alarm 要不要重試」,不是把 alarm 本身改成一次性,也不是關閉 Durable Object 的所有後續 alarm。之後若程式再次呼叫 setAlarm(),仍要依那次排程的邏輯處理。

另外,ctx.abort() 產生的錯誤不能由 application code catch;它會立即重設 object,runtime 會記錄傳入的訊息。因此不要把它當成可以在 try/catch 裡攔截、再繼續執行的普通例外。

哪些 abort 路徑應該設定 retryAlarm: false#

alarm 自己完成不可重複的工作#

例如清空儲存資料、寫入一次性的完成標記,然後希望 instance reset 後不再重跑:

export class CleanupTask extends DurableObject {
async alarm(): Promise<void> {
const pending = await this.ctx.storage.get("pending");
if (!pending) {
this.ctx.abort("Nothing to clean", { retryAlarm: false });
}
await this.ctx.storage.deleteAll();
this.ctx.abort("Cleanup complete", { retryAlarm: false });
}
}

這裡的 deleteAll() 在 abort 前完成;retryAlarm: false 則避免同一個 alarm 因為 reset 再次執行。若清理中途真的可能安全重做,則應先設計好 idempotency,再決定是否保留預設重試。

使用者取消或管理 request 明確要終止 alarm#

alarm 可以和同一個 Durable Object 的其他 request 並行執行。如果取消 endpoint 的業務語意是「不要讓正在跑的 alarm 再回來」,該 request 的 abort 也要傳入選項:

export class CleanupTask extends DurableObject {
async cancel(): Promise<void> {
this.ctx.abort("Canceled by operator", { retryAlarm: false });
}
}

不要只修改 alarm() 內的呼叫點。Cloudflare 明確指出,另一個 request 在 alarm 執行時呼叫 ctx.abort(),重試行為仍由那個 request 傳入的 retryAlarm 控制。

不確定是否應該停止時,保留預設值並補測試#

預設重試的目的,是避免一個無關 request 永久取消 alarm。如果 abort 只是為了重設卡住的 instance,而不是取消排程,直接改成 false 可能讓真正需要完成的工作消失。這種呼叫點應保留預設行為,或明確寫 { retryAlarm: true },並在程式碼旁邊註明原因。

升級 Wrangler 並找出所有呼叫點#

本機開發需要 Wrangler 4.126.0 或更新版本才能使用 retryAlarm。先查目前 CLI 和 package 來源:

Terminal window
pnpm exec wrangler --version
pnpm list wrangler --depth 0
pnpm why wrangler
rg -n 'ctx\.abort|async alarm|alarm\(' src

若版本低於最低要求,先在專案的 dependency policy 允許範圍內更新,再重跑 lockfile、型別檢查和本機 Durable Object 測試。不要只在其中一個 handler 加選項後就結束;用搜尋結果建立一份 abort path 清單,逐一標記「保留重試」或「停止重試」。

如何驗證 alarm 沒有不必要地重跑#

測試的重點是觀察狀態轉移,不是只看 ctx.abort() 的 log:

  1. 準備一筆只應清理一次的 storage 資料,並安排 alarm。
  2. 在 alarm 完成清理後呼叫 ctx.abort("...", { retryAlarm: false })
  3. 重新請求同一個 Durable Object,確認清理結果維持完成狀態,沒有第二次副作用。
  4. 另測一條普通 request 呼叫 ctx.abort() 的路徑,確認你預期的 alarm retry 行為仍存在。
  5. 用另一個 request 在 alarm 執行時呼叫 abort,驗證該 request 的 retryAlarm 設定,而不是只測 alarm handler。

部署後若需要觀察流量和版本,可搭配 Durable Objects Deployments 分辨設定與實際流量;儲存量的圖表則要注意 namespace 與單一 object 的範圍不同,可參考 Durable Objects SQLite 儲存用量檢查

常見問題#

Q: 加上 retryAlarm: false 後,Durable Object 還能再設定 alarm 嗎?#

A: 可以。這個選項只決定被該次 ctx.abort() 中斷的 alarm 是否重試,不是永久停用該 Durable Object 的 alarm API。後續由程式排程的新 alarm 仍按新的設定執行。

Q: 所有 ctx.abort() 都應該改成 retryAlarm: false 嗎?#

A: 不應該。預設重試可以避免無關 request 讓正在執行的 alarm 永久消失。只有業務語意確定要取消該次工作時才設 false;其餘呼叫點保留預設或明確使用 true

Q: ctx.abort() 可以用 try/catch 接住再回傳 JSON 嗎?#

A: 不行。Cloudflare 文件將它定義為立即 reset,application code 無法 catch 這個錯誤。需要回傳取消結果時,應在呼叫 abort 前先完成可持久化的狀態更新,並讓下一次 request 讀取該狀態。

參考資料:

Cloudflare Changelog:Prevent Durable Object alarm retries when using ctx.abort()

Cloudflare Docs:Durable Object State API 的 abort

Durable Objects 的 ctx.abort() 怎麼避免 alarm 重試?用 retryAlarm: false
https://laplusda.com/posts/cloudflare-durable-objects-alarm-retry-abort/
作者
Zero
發佈於
2026-08-28
許可協議
CC BY-NC-SA 4.0
這篇文章有幫助嗎?

回報錯字、失效連結,或告訴我你想看的延伸主題。