為什麼需要這個機制

MES 呼叫 SAP 入庫 API 時,發生 Timeout 不代表 SAP 一定失敗

有可能 SAP 已經完成入庫並 Commit,只是 Response 在回傳途中遺失

Mermaid · 圖表原始碼
sequenceDiagram
    participant MES
    participant SAP
 
    MES->>SAP: 入庫 Request
    SAP->>SAP: 執行入庫
    SAP->>SAP: Commit
    SAP--xMES: Response Timeout

此時 MES 無法確認結果

如果 MES Retry,而 SAP 又重新執行一次,就可能造成重複入庫


解法:Transaction ID

MES 在第一次送出 Request 前,先產生唯一的 transactionId

例如:

transactionId = ABC123

只要是同一次業務操作的 Retry,都必須沿用同一個 transactionId

Mermaid · 圖表原始碼
sequenceDiagram
    participant MES
    participant SAP
 
    MES->>MES: 產生 transactionId = ABC123
    MES->>SAP: 入庫 API + ABC123
 
    SAP->>SAP: 檢查 ABC123
 
    alt 第一次收到
        SAP->>SAP: 執行入庫
        SAP->>SAP: 保存 ABC123 + 處理結果
        SAP->>SAP: Commit
        SAP--xMES: Response Timeout
    end
 
    MES->>SAP: Retry + ABC123
    SAP->>SAP: 發現 ABC123 已完成
    SAP-->>MES: 回傳第一次處理結果

第二次收到相同 transactionId 時,不再重新執行入庫


Retry 與新操作的差異

Retry 必須使用相同的 Transaction ID

新的業務操作則必須使用新的 Transaction ID

Mermaid · 圖表原始碼
sequenceDiagram
    participant MES
    participant SAP
 
    MES->>SAP: ABC123 / 數量 5
    SAP->>SAP: 執行一次入庫
 
    MES->>SAP: ABC123 / 數量 5
    SAP-->>MES: 回傳原本結果
 
    MES->>SAP: DEF456 / 數量 5
    SAP->>SAP: 視為新的入庫操作

因此規則很簡單:

同一次業務操作 Retry
= 相同 Transaction ID

新的業務操作
= 新的 Transaction ID

不能只靠「數量相同、工單相同、時間接近」來判斷是否重複,因為兩筆看起來一樣的資料,也可能真的是兩次合法操作


實作上要注意

Transaction ID 紀錄與實際業務資料,最好在同一個 Transaction 內完成。

不建議:

執行入庫
Commit

保存 Transaction ID
Commit

因為如果兩個 Commit 中間發生錯誤,可能造成入庫已成功,但 Transaction ID 沒有被保存。

Retry 時就可能再次執行。

建議流程:

Mermaid · 圖表原始碼
sequenceDiagram
    participant API
    participant DB
 
    API->>DB: Begin Transaction
    API->>DB: 建立 ABC123 紀錄
    API->>DB: 執行入庫資料更新
    API->>DB: 保存處理結果
    API->>DB: Commit
 
    Note over API,DB: 業務資料與冪等紀錄一起提交

另外,transactionId 應該有 Unique Constraint,避免同一個 ID 在併發情況下被重複執行。


完整流程

Mermaid · 圖表原始碼
sequenceDiagram
    participant MES
    participant SAP
 
    MES->>MES: 產生 ABC123
    MES->>SAP: Request + ABC123
 
    SAP->>SAP: 檢查 Transaction ID
 
    alt ABC123 不存在
        SAP->>SAP: 執行入庫
        SAP->>SAP: 保存結果
        SAP->>SAP: Commit
        SAP-->>MES: 回傳結果
    else ABC123 已完成
        SAP-->>MES: 回傳之前的結果
    end

結論

核心原則是:

MES 可以重送 Request,但 SAP 不能重複產生業務效果

transactionId 的目的,是讓 MES 與 SAP 都能確認:

這次 Request,到底是同一筆操作的 Retry,還是一筆全新的業務操作

簡單來說:

HTTP Request 可以送很多次

實際業務操作只能成功一次

這樣可以避免 Timeout、Retry 或 Response 遺失造成重複入庫,同時也能保留真正合法的多筆入庫操作。

Subscribe to new articles訂閱新文章