Watch at Ease看見世界,讀懂生活。

科技 · 實用指南

PostgreSQL SQLSTATE 40001?要重試整個交易,不是失敗敘述

記錄 SQLSTATE、隔離層級與失敗邊界;回滾後在有限重試迴圈內重做全部讀取與決策,並把不可逆副作用留到提交之後。

原創技術示意圖: 原創圖:40001 後從 BEGIN 重做整個交易,外部副作用位於提交後。
原創技術示意圖: 原創圖:40001 後從 BEGIN 重做整個交易,外部副作用位於提交後。 Watch at Ease

SQLSTATE 40001 是 PostgreSQL 的 serialization_failure。它可在 Repeatable Read 或 Serializable 出現,也可能到 COMMIT 才回傳。資料庫拒絕的是整個交易嘗試;只重跑最後一個敘述會沿用失效快照所產生的應用決策。

安全處理器要包住完整交易 callback 與 COMMIT:確認 40001、回滾、有限退避並加入 jitter,再從 BEGIN 重做所有讀取、決策與寫入。資料庫無法自動重建選擇 SQL 與參數的應用邏輯。

重試不是掩蓋競爭。設定次數或時間預算並留下指標;持續失敗必須顯示。郵件、扣款、發佈等不可逆操作應放在提交後,可用 transactional outbox 或冪等鍵連接已提交狀態與後續交付。

重試前記錄失敗邊界

記錄五位 SQLSTATE、隔離層級、嘗試次數、交易時間,以及錯誤在敘述或 COMMIT。控制流程應依 SQLSTATE,而不是本地化訊息。

COMMIT 失敗並不反常;伺服器可能要看到完整讀寫集合,才能判斷是否有安全的序列順序。

從 BEGIN 重做讀取與決策

回滾後從第一個讀取重新進入。存在檢查、計數、價格、庫存、授權與衍生值都要重算。只重試失敗 UPDATE 或 savepoint 會保留失效假設。

讓一次 callback 等於一次資料庫嘗試,並同時捕捉 callback 與 COMMIT。

限制重試並暴露競爭

設定少量最大次數或總時間,使用退避與 jitter。每次都要重置交易與連線狀態,不能沿用已中止交易。

按操作記錄重試。持續耗盡要檢查長交易、熱點鍵、索引、連線池與隔離需求,不要直接降低隔離層級。

把副作用放在提交安全線外

重試體內程式可能執行多次。不要在提交前寄信、扣款、發訊息或呼叫非冪等 API。

可在同一交易寫入 outbox,再由獨立 worker 提交後傳送;外部系統支援時使用穩定業務請求的冪等鍵。

為相鄰 SQLSTATE 分流

40P01 死鎖也會回滾並可重試,但應診斷鎖順序。23505 有時像競爭,卻需要應用層證明才可自動重試。

40003 表示完成狀態未知,應先對帳再重複;將所有 class 40 錯誤統一重試可能重複工作。

原創技術示意圖: 原創表:依 SQLSTATE 分流 40001、40P01、23505 與 40003。
原創技術示意圖: 原創表:依 SQLSTATE 分流 40001、40P01、23505 與 40003。 Watch at Ease