Skip to content

可靠性、健康與錯誤 ​

送達語意 ​

  1. 至少一次送達:本平台以 (sourceId, table, seq) 去重。
    • 同一個 seq、內容相同(以正規化 JSON 比較,鍵的順序不影響):忽略並照常 ack。
    • 同一個 seq、內容不同:nack SEQ_CONFLICT。
  2. 每條串流內保序:來源必須依 seq 遞增送出;不同桌之間不保證順序。
  3. 缺口偵測:事件的 prev 不等於本平台已接收的最後一個 seq 時,回 resync {table, from},來源必須從 from 之後重送。
  4. ack 在持久化之後才送:事件寫入後才回 ack,ack 是累積式的:{"B001": 1538} 表示這條串流 ≤ 1538 的事件都已接收。
  5. 重連續傳:WebSocket 連線建立時,welcome.resume 給出每條串流已 ack 的最後 seq,來源從它之後補送。HTTPS 來源可以呼叫 GET /ingest/v1/resume?tables=…。
  6. 來源保留期:來源至少要保留 24 小時內可重送的事件。
  7. 無法重送時:送出 table.snapshot(gap: true)後接續即時事件。這筆快照不要求 prev 對上,但 seq 仍須大於本平台已接收的最後一個 seq;之後的事件從快照的 seq 接續。缺口期間未完成的局會轉為異常局,逾時自動作廢退款。
  8. 串流單一擁有者:同一條串流同時只能經由一條 WebSocket 連線送達。新連線在 hello 宣告同一張桌時會接管,舊連線再送這張桌的事件會收到 nack STREAM_TAKEN_OVER。
  9. 未綁定的來源桌:事件照常 ack 並歸檔,但不套用;平台完成綁定後開始套用。

回應格式 ​

HTTPS(POST /ingest/v1/events、/validate)一次回覆整批的結果:

json
{
  "acks": { "B001": 1538 },
  "nacks": [{ "code": "INVALID_EVENT", "table": "B002", "seq": 77, "message": "data.result.cards.P1: must match ^(A|[2-9]|10|J|Q|K)[SHDC]$" }],
  "resync": [{ "table": "B003", "from": 812 }]
}

WebSocket 則逐一送出 {"op":"ack","acks":{…}}、{"op":"nack",…}、{"op":"resync","table":"…","from":…}。

  • nack 帶 accepted: true 時(RESULT_INCONSISTENT),事件已接收並持久化,串流照常前進,只是警告。
  • 其他 nack 表示事件沒有被接收,串流停在前一筆;修正後以相同的 seq 重送。
  • 同一批中某條串流出現 nack 或 resync 後,這條串流在這批裡剩下的事件都不處理;其他串流不受影響。
  • 所有事件(包含被拒絕的)都會寫入原始歸檔,作為爭議與稽核依據。

健康與心跳 ​

  • 心跳(不占用串流序號)每 5 秒送一次,逐桌回報健康狀態 ok、degraded、down:

    json
    {"op":"heartbeat","ts":"2026-09-24T03:00:35.000Z","tables":{"B001":"ok","B002":"down"}}
  • 來源知道某張桌的上游已經中斷時,要立即把該桌標成 down,本平台會馬上停止該桌收注。

  • 15 秒內沒有心跳也沒有事件,視為來源失聯:所有綁定的桌停止收注;進行中的局等待恢復,逾時自動作廢退款。

  • 最後一條 WebSocket 連線中斷時,這條連線負責的桌也會立即停止收注;來源重連並送出心跳或事件後恢復。

錯誤碼 ​

代碼意義來源應採取的行動
UNAUTHORIZED簽章錯誤、時間窗外、nonce 重複、傳輸方式未開放或 IP 不在白名單檢查金鑰、時鐘與設定
SOURCE_DISABLED來源已停用聯絡平台
UNSUPPORTED_VERSIONv 不支援升級
TABLE_NOT_ALLOWED這個來源無權發布這張桌聯絡平台
INVALID_EVENT結構錯誤、欄位不合法修正這一筆後,以相同 seq 重送
SEQ_GAP(resync)prev 對不上從 from 之後重送
SEQ_CONFLICT同一個 seq 內容不同來源端錯誤,需要人工排查
RESULT_CONFLICT同一個 rev 的結果不同(保留;目前版本維持原結果並記錄,不回傳 nack)改用 round.correct
RESULT_INCONSISTENT牌面與點數、輸贏或補牌規則不符(牛牛:重複的牌、牌型或輸贏與牌面不符、發牌順序不符,見牛牛)已接收但保留待審;可以送 round.correct 更正
STREAM_TAKEN_OVER這張桌已由新連線接管停止在舊連線送這張桌
RATE_LIMITED超出流量(保留;目前版本不回傳)退避後重試

WebSocket 關閉碼:

關閉碼意義
4001認證失敗(例如 hello.source 與憑證不符)
4002版本不支援
4003來源已停用
4008違反限制(訊息或批次過大)
1012本平台重新啟動:立即重連並續傳

重連退避:1 秒起、每次加倍、上限 30 秒,另加隨機延遲。

來源信任等級 ​

平台為每個來源設定信任等級,決定自動化程度。每一種情況都有自動處置,差別只在護欄的寬嚴:

等級不完整結果(complete: false)結果驗證不一致結果修正(round.correct)結算後作廢(round.cancel)
trusted可結算的投注區照常結算,其餘退款以來源的 winner 結算並標記自動重算自動沖回
standard同上保留不結算,逾時自動作廢退款護欄內自動重算;護欄外維持原結果並列為爭議局同左
restricted同上同上同上同上

護欄:修正或作廢在結算後 30 分鐘內送達,而且新結果完整、通過補牌規則驗證。超出護欄時不動任何金額,只記為爭議局。

WebSocket 對話範例 ​

text
← {"op":"welcome","v":1,"session":"s_8f2…","heartbeat":5,"maxBatch":500,"maxInflight":2000,
   "resume":{"B001":1530,"D001":0},"serverTime":"2026-09-24T03:00:29.950Z"}
→ {"op":"hello","v":1,"source":"acme-live","agent":"acme-feeder/2.0",
   "tables":["B001","D001"],"capabilities":{"cardByCard":true,"countdown":true,"snapshot":true,"replayHours":168}}
→ {"op":"events","events":[
     {"v":1,"type":"round.open","table":"B001","seq":1531,"prev":1530,"ts":"2026-09-24T03:00:30.000Z",
      "game":"baccarat","shoe":"260924-03","round":12,"roundKey":"B001-260924-03-12",
      "data":{"closesAt":"2026-09-24T03:00:55.000Z","betSeconds":25}}]}
← {"op":"ack","acks":{"B001":1531}}
→ {"op":"heartbeat","ts":"2026-09-24T03:00:35.000Z","tables":{"B001":"ok","D001":"ok"}}
→ {"op":"events","events":[
     {"v":1,"type":"round.close","table":"B001","seq":1532,"prev":1531,"ts":"2026-09-24T03:00:55.010Z",
      "game":"baccarat","shoe":"260924-03","round":12,"roundKey":"B001-260924-03-12","data":{}},
     {"v":1,"type":"round.result","table":"B001","seq":1538,"prev":1532,"ts":"2026-09-24T03:01:20.400Z",
      "game":"baccarat","shoe":"260924-03","round":12,"roundKey":"B001-260924-03-12",
      "data":{"rev":1,"complete":true,"result":{
        "cards":{"P1":"2C","B1":"4H","P2":"3D","B2":"2S","P3":"10S"},
        "player":5,"banker":6,"winner":"B","playerPair":false,"bankerPair":false}}}]}
← {"op":"ack","acks":{"B001":1538}}
  • seq 可以不連號(上例 1532 之後是 1538),但每筆的 prev 必須等於前一筆的 seq。
  • hello 的 source 必須與 X-Source-Id 相同,否則以 4001 關閉。
  • hello.tables 宣告這條連線負責的桌,新連線會接管同一張桌。

上線流程 ​

  1. 向平台申請來源:代號、類型、傳輸方式、IP 白名單 → 取得 HMAC 金鑰。
  2. 先在測試環境(elite-dev-ingest.ewin888.com)串接;可以用 POST /ingest/v1/validate 乾跑,或用 GFI 事件驗證器 檢查事件格式。
  3. 通過相容性檢查:斷線續傳(任意 seq)、resync 重送、無法重送時送快照、心跳與 down 標記、修正與作廢、時鐘偏移 < 500 ms、錯誤事件的處理。
  4. 平台把來源桌綁定到公開桌並設定優先順序後上線。

elite 租戶整合 API v1