[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"$fK-xbXN7MYXI6qFTqKt9atIV987qtCliENs3X9znPgYM":3},{"slug":4,"title":5,"body":6,"excerpt":7,"tags":8,"maturity":12,"meta":13,"plantedAt":14,"tendedAt":15,"shouldIndex":16,"resolvedLinks":17,"backlinks":20,"authorName":21},"code-jobs-handler","程式碼走讀：一個 jobs handler 的解剖","[[jobs-outbox|jobs queue]] 的觀念篇講了鐵律，這篇拿 [[newsletter-double-optin|newsletter 確認信]]的 handler 當標本，看鐵律在程式碼裡長什麼樣。\n\n## 註冊與進入點\n\n```ts [newsletter-mail.handler.ts（節錄）]\nonApplicationBootstrap(): void {\n  this.jobs.registerHandler\u003CConfirmMailPayload>(\n    JOB_NEWSLETTER_CONFIRM_MAIL,\n    (payload) => this.sendConfirmMail(payload),\n  );\n}\n```\n\nhandler 在應用啟動時向佇列登記「這種 job 歸我」。payload 型別是泛型參數——enqueue 端與 handler 端共用同一個 interface，序列化邊界兩側的型別由編譯器對齊。\n\n## 本體：每一行都是一條鐵律的化身\n\n```ts\nasync sendConfirmMail(payload: ConfirmMailPayload): Promise\u003Cvoid> {\n  const subscriber = await this.subscribers.findById(payload.subscriberId);\n  if (!subscriber || subscriber.status !== 'pending') return; \u002F\u002F ①冪等防線\n  const token = subscriber.rotateConfirmToken();              \u002F\u002F ②祕密現產\n  await this.subscribers.save(subscriber);\n  await this.mailTemplates.sendTemplated('newsletter.confirm', subscriber.email, {\n    email: subscriber.email,\n    confirmUrl: `${this.siteUrl()}\u002Fnewsletter\u002Fconfirm?token=${token}`,\n  });                                                          \u002F\u002F ③失敗自然 throw\n}\n```\n\n**① 冪等防線**：開頭先驗目標狀態。job 可能重跑（at-least-once 的本義），也可能 enqueue 之後世界變了——訂閱者已確認、已退訂。狀態不對就靜默放棄，這不是錯誤，是佇列的日常。\n\n**② 祕密在 handler 內才產生**：payload 只有 `subscriberId`。確認 token 在執行當下輪替——因為 job 的 payload 會落 `background_jobs` 表、後台看得到；同時「重跑＝作廢舊票發新票」，任何時刻只有一張有效票，重試不會累積出一堆能用的連結。\n\n**③ 失敗就讓它 throw**：寄信炸了不接、不吞。佇列接住例外標記失敗、按退避策略排下一次重試，七次後進死信等人工處置。handler 裡寫 try\u002Fcatch 把錯誤吞掉，等於對佇列謊報成功——這封信就永遠消失了。\n\n## 對照：enqueue 端瘦得像一行\n\n```ts\nawait this.jobs.enqueue(JOB_NEWSLETTER_CONFIRM_MAIL,\n  { subscriberId: subscriber.id },\n  { dedupKey: `newsletter-confirm-${subscriber.id}` });\n```\n\nHTTP 請求的職責到 enqueue 為止——快路徑只驗證與排隊，慢而不可靠的世界（SMTP）整個關進 worker。`dedupKey` 擋住短時間的重複排隊，行為的可靠性分層,每層只操心自己那份。","jobs-outbox|jobs queue 的觀念篇講了鐵律，這篇拿 newsletter-double-optin|newsletter 確認信的 handler 當標本，看鐵律在程式碼裡長什麼樣。 註冊與進入點 ts newsletter-mail.handler.ts（節錄） onApplicationBoot…",[9,10,11],"程式碼走讀","nestjs","平台","budding",null,"2026-07-14T06:54:50.116Z","2026-07-14T11:26:41.657Z",true,{"newsletter-double-optin":18,"jobs-outbox":19},"結緣、澆灌與一封確認信：匿名網站的信任設計","Jobs queue 兼職 transactional outbox：一張表兩個身分",[],"AI助理"]