專案

一般

配置概況

動作

Bug #1456

已結束

[Backend Bug] A17 CP001 重連遇 Boot cooldown 後四支狀態殘留 UNAVAILABLE

是由 陳國瑋 於 約 1 個月 前加入. 於 30 天 前更新.

狀態:
Closed
優先權:
High
被分派者:
開始日期:
2026-09-01
完成日期:
預估工時:

概述

問題摘要

2026-08-29 檢查 CT1(寶台 A17)時,CP001 底下四個 connectors 在 Branch/使用者畫面全部顯示 UNAVAILABLE,無法開始充電;但 CP edge 四支皆為 AVAILABLE、Modbus meter read 持續更新,CP001 本身也持續 ONLINE。

本次不是四支硬體同時故障,而是 CP001 共用的 OCPP WebSocket 反覆斷線重連後,Branch connector projection 長期殘留 OCPP heartbeat stale。

與 #1436 的關係

  • #1436 已修正 Branch watchdog:lastHeartbeat 與 lastBootNotification 任一仍在 330 秒 cutoff 內時,不得投影 OFFLINE/UNAVAILABLE。
  • A17 已部署含此判斷的 Branch JAR;本次不是舊版未部署。
  • #1436 的需求紀錄明確排除「Charge Point 重連後立即補送 Heartbeat」,若 reconnect 沒送出 fresh BootNotification,既有規則不提供 grace。
  • 本次正是該排除路徑:第二次 reconnect 落在 BootNotification cooldown 內,Boot 被跳過;第一筆 post-reconnect Heartbeat 又晚於 watchdog cutoff。
  • 因此另開本單追蹤 CP reconnect liveness 與 ONLINE 後 connector convergence,不回頭擴張已完成的 #1436 範圍。

環境與影響

  • 案場:CT1(寶台 A17)
  • Charge Point:CP001
  • Connectors:CP001-001、CP001-002、CP001-003、CP001-007
  • 實際發生:2026-08-27 06:10:54(Asia/Taipei)
  • 發現及暫時復原:2026-08-29
  • 使用者影響:同一 CP 的四支 connector 全部顯示無法使用,無法開始充電
  • Branch watchdog:Heartbeat interval 300 秒、offline timeout 330 秒

四支會同時受影響,是因為它們共用 CP001 的單一 OCPP session;watchdog 判定 charge point 離線後,會一次投影所有沒有 active transaction 保護的 connectors。最終 watchdog 執行時四支都沒有 active transaction,因此同秒被改寫。

已確認的事件時序

  1. 2026-08-27 06:04:17:CP001 WebSocket 因 pong timeout 關閉。
  2. 06:04:47:重連成功,BootNotification Accepted;Branch 記錄 fresh Boot。
  3. 06:06:47:WebSocket 再次因 pong timeout 關閉。
  4. 06:07:17:再次重連,但距前次 Boot 未滿 300 秒,BootNotification 因 cooldown 被跳過。
  5. 06:07:37~06:08:35:CP001 各 connector 重新送出實際狀態,四支最後都曾回到 AVAILABLE。
  6. 06:10:53:watchdog 使用 cutoff 06:05:23.505;當時 lastHeartbeat=05:39:48、lastBootNotification=06:04:47,兩者都已過期。
  7. 06:10:54:四支 connectors 同時被改成 UNAVAILABLE / OCPP heartbeat stale。
  8. 06:11:29:Heartbeat 成功並把 CP001 恢復為 ONLINE,但沒有恢復 connector operational status。
  9. 至 2026-08-29 21:18:Branch 四支仍是 stale UNAVAILABLE;edge 四支皆為 AVAILABLE 且 meter read 新鮮。

根因判定

已確認

  1. BootNotificationScheduler 使用 300 秒 cooldown;快速重連時可跳過 Boot。
  2. HeartbeatScheduler 已啟動時,reconnect 不會重建排程或立即補送 Heartbeat,只等待原本的 fixed-delay 時點。
  3. 因此可能出現「Boot 被 cooldown 跳過 + 下一筆 Heartbeat 晚於 Branch 330 秒 cutoff」。
  4. Branch 的下一筆 Heartbeat 只恢復 charge point connection,不會重新同步已被 offline propagation 覆寫的 connector 狀態。
  5. edge 若一直維持 AVAILABLE、沒有新狀態變化,就不會再自然補送 StatusNotification,造成 Branch/edge 長期分裂。

尚待驗證的上游觸發因素

  • 2026-08-27 只有 CP001 出現 pong timeout,共 68 次;CP002/CP003 都是 0。
  • CP001 同期有兩筆長時間 transaction、大量 MeterValues 與 970 次 WebSocket not connected queue retry。
  • 這些證據高度指向 CP001 OCPP session/message queue 壓力,但 pong 未回覆的最底層成因尚未直接證明;實作前需補 instrumentation 或可重現測試,不能直接當作定論。

已執行的暫時復原

2026-08-29 21:22 復原前確認:

  • Branch linked ACTIVE transaction:0。
  • Edge linked ACTIVE transaction:0。
  • Branch/edge 四支 current_transaction_id 全為 null。

只重啟 ems-cp-api(CP001),沒有重啟 Branch、CP002、CP003,也沒有直接修改 DB:

  • 21:22:49:BootNotification Accepted、Heartbeat #1 成功。
  • 21:22:59~21:23:04:四支初始 StatusNotification(AVAILABLE/NOERROR) 全部成功。
  • 21:23:Branch 與 edge 四支均確認為 AVAILABLE,CP001 為 ONLINE。
  • 沒有中斷任何充電交易。

此操作只恢復現場,沒有永久修補。

下週待確認的修正方向

  1. CP 每次成功 reconnect 時,即使 BootNotification 因 cooldown 略過,也應立即送出一筆 Heartbeat 或等價 liveness;需避免 heartbeat storm 與重複 scheduler。
  2. Branch 在 OFFLINE → ONLINE 後應有 connector convergence 機制,使狀態能與 edge 實況重新同步;不得以 Heartbeat 任意偽造 AVAILABLE 或覆寫 FAULTED/CHARGING。
  3. 調查 CP001 pong timeout 與 MeterValues queue/pending request 壓力,評估 backpressure、批次上傳、pending request timeout/cleanup 與 WebSocket thread isolation。
  4. 保留既有 active transaction 保護、TCP half-open 偵測與 #1436 Boot grace 行為。

以上只是候選方向,須下週依 Redmine SOP 完成需求確認與規劃 gate 後才能實作。

驗收條件

  1. 舊 Heartbeat + reconnect 落在 Boot cooldown 內時,必須在 watchdog cutoff 前建立 fresh liveness。
  2. 重複 WebSocket close/reconnect 不產生多個 Heartbeat scheduler,也不造成 Boot/Heartbeat storm。
  3. reconnect 後已上報 AVAILABLE 的 connectors 不得再被舊 liveness 覆寫成 stale UNAVAILABLE。
  4. 即使 watchdog 先投影離線,下一筆 Heartbeat 使 CP 回到 ONLINE 後,Branch connectors 最終仍須與 edge 當前狀態收斂。
  5. FAULTED、CHARGING、active transaction 與 current transaction 保護不得退化。
  6. CP002/CP003 與第三方 OCPP Charge Point 行為不得受 CloudLink-specific scheduler 修改影響。
  7. 補可控制時間的 reconnect/Boot cooldown/watchdog/first Heartbeat 回歸測試與 production-equivalent E2E。

E2E Impact

  • 分類:Add
  • 案例:OCP-018
  • Priority:P0
  • 狀態:Gap
  • Trigger:OCPP reconnect、Boot cooldown、Heartbeat scheduler、offline watchdog 或 ONLINE recovery 變更
  • Release pack:Major / Charging / OCPP
  • 相關既有案例:OCP-001、OCP-005、OCP-007、OCP-008、OCP-011

目前狀態

  • 現場已暫時恢復。
  • 本單保持 New。
  • 2026-08-29 只完成診斷、復原與建單,不修改程式、不建立實作 branch、不部署。
  • 預計下週有空時再依 Redmine SOP 進入需求確認、規劃、實作與測試。

是由 陳國瑋 於 30 天 前更新

  • 狀態 從 New 變更為 In Progress
  • 開始日期 從 2026-08-31 變更為 2026-09-01

收案啟動

  • 已依 SOP 同步最新 origin/main。
  • 已建立專屬 branch:feature/redmine-1456-ocpp-reconnect-liveness。
  • 已保存 Issue 快照並完成 CP reconnect、Heartbeat/Boot scheduler、StatusNotification/Recovery、Branch watchdog 與 #1436 修正現況掃描。
  • 既有 production incident 回歸案例 OCP-018 保留在本 branch,待規劃 gate 確認後完成內容校正。
  • 目前尚未修改 production code、尚未執行測試或部署;下一步進入逐題需求確認。

是由 陳國瑋 於 30 天 前更新

需求與規劃確認(2026-09-01)

Ken 已確認開始修改,實作專案為 java/charge_point。

已確認範圍

  • WebSocket 每次重連後立即補送 Heartbeat,同時維持單一週期 scheduler,避免重複排程與 heartbeat storm。
  • Heartbeat 成功後,強制重新送出所有 connector 的 edge 實際狀態;即使狀態值未改變也不得略過。
  • 保留 AVAILABLE、FAULTED、CHARGING 與實際 error code,不以 Heartbeat 偽造 connector 狀態。
  • 修正 recovery 將未實際送出的同值狀態誤記為同步成功。
  • WebSocket 斷線時 fail 並清理舊 session 的 pending OCPP requests,避免跨連線殘留。
  • ems_branch 與 #1436 watchdog 本次不修改。
  • 不在本單重寫 MeterValues queue/backpressure;僅先修復已確認的 reconnect recovery 缺口。

E2E Impact

  • Add:OCP-018,P0,Major / Charging / OCPP release pack。
  • production code、測試案例與文件將於 feature branch feature/redmine-1456-ocpp-reconnect-liveness 完成。
  • 尚未部署;整批 Unit/Integration/E2E 測試需依 SOP 另行取得 Ken 啟動指令。

是由 陳國瑋 於 30 天 前更新

本機實作進度(2026-09-01)

  • 確認 java/charge_point 是獨立 Git repository;已從其最新 main 建立並 push feature/redmine-1456-ocpp-reconnect-liveness,merge-base 與 main 相同。
  • 已完成 WebSocket session generation、斷線 pending CALL cleanup、reconnect immediate Heartbeat、Heartbeat 成功後才啟動 Recovery,以及 connector 實際狀態強制同步。
  • ems_branch 未修改,未部署正式或測試環境。
  • 已新增 5 個隔離 Unit Test class、共 11 個 test method。
  • mvn -DskipTests test:PASS(僅編譯 production/test code,測試未執行)。
  • 單一隔離案例 HeartbeatSchedulerReconnectTest#rapidReconnectKeepsSingleSchedulerAndIgnoresStaleHeartbeatResult:PASS。
  • 其餘 Unit Test 依 SOP 等待 Ken 明確回覆「開始測試」後執行。

是由 陳國瑋 於 30 天 前更新

2026-09-01 設計補充(Ken 已確認):

  • reconnect 的 immediate Heartbeat 成功後,只執行 connector 實際狀態強制同步。
  • 此路徑不呼叫完整 OcppRecoveryProcessor.executeRecovery(),不得斷閘、補送 StopTransaction、改變交易狀態或啟停 MeterValue 採集。
  • 完整 Recovery 保留既有手動/RecoveryCoordinator 入口。
  • 本機實作已依此調整;production code 與 test code 編譯通過(mvn -DskipTests test),尚未執行完整 Unit Test、Integration/E2E、commit、push 或部署。

是由 陳國瑋 於 30 天 前更新

2026-09-01 Unit Test 結果:PASS

  • Command: mvn -Dtest=OcppJsonClientSessionTest,BootNotificationSchedulerReconnectTest,HeartbeatSchedulerReconnectTest,StatusNotificationSchedulerRecoveryTest,OcppRecoveryProcessorStatusSyncTest test
  • Result: 12 run / 0 failures / 0 errors / 0 skipped
  • 範圍:WebSocket session generation/pending CALL cleanup、Boot cooldown retry、reconnect immediate Heartbeat、舊 session callback 隔離、Heartbeat 後 connector-only status sync、AVAILABLE/CHARGING/FAULTED 與 error code 保留、狀態同步失敗傳播。
  • 測試僅使用 mock,不啟動 Spring context、不連接外部服務或資料庫。
  • Integration/E2E、測試環境部署、commit、push、正式部署均尚未執行。

是由 陳國瑋 於 30 天 前更新

2026-09-01 Ken environment E2E preflight(唯讀,未部署/重啟/修改 DB):

  • ems-branch-api、ems-cp-api、ems-branch-admin、MySQL 均為 Up。
  • CP Actuator health HTTP 200;Branch /api-docs HTTP 200;host-local OCPP listener 18082 與 Modbus gateway 192.168.127.254:502 TCP 可達。
  • CP001 最近 24 小時:Heartbeat 288 success / 0 failure / 0 WebSocket close;Branch 目前 ONLINE。
  • Ken 環境有 CP001-001、CP001-002;edge/Branch 目前均為 AVAILABLE / NOERROR,無 active transaction,meter read 新鮮。
  • 注意:兩支 connector 先前為 FAULTED,於 17:39:16~17:39:23 才因 meter 恢復轉回 AVAILABLE;正式 reconnect E2E 前應先確認狀態持續穩定,避免 Modbus 波動干擾結果。
  • 現場 CP JAR 日期為 2026-07-29,未包含 #1456;JAR 無 git revision metadata。Jenkins EMS-CP-API-Ken 固定建置 ocpp_modbus/main,無 feature branch 參數。

是由 陳國瑋 於 30 天 前更新

Ken environment E2E 結果:PASS(2026-09-01)

Ken 已確認測試報告通過。

執行方式

  • 在 CP001 上兩次只關閉 OCPP TCP session,不重啟 CP/Branch container,也不直接修改 DB。
  • 每次操作前均確認 Branch/edge active transaction 為 0,並核對 connector 電流與 relay 狀態。
  • 第一次重連建立新的 BootNotification 基準;第二次重連落在 300 秒 Boot cooldown 內。

核心結果

  • 19:04:46 第二次 WebSocket reconnect 時,BootNotification 明確因 cooldown 跳過(剩餘 217,535 ms)。
  • 同一秒 immediate Heartbeat #5 成功;Branch lastHeartbeat 更新,lastBootNotification 保持 19:03:24,證明存活不是由 Boot 偽造。
  • CP001-001 的 edge 實際狀態 PREPARING、CP001-002 的 AVAILABLE 均原樣同步至 Branch,未偽造狀態。
  • 整個 CP container lifecycle 只有一次 Heartbeat scheduler started;後續 #6/#7 維持原五分鐘 fixed-delay 節奏。
  • Cooldown reconnect 視窗內:完整 Recovery 0、relay control 0、Start/StopTransaction 0、MeterValue 採集啟停 0。
  • 環境既有 OFF_PEAK queue 另行派發的 RemoteStart #844 全程 0A、無 transaction,19:13:28 自然 timeout 後斷閘並回到 AVAILABLE;此行為不是 reconnect Recovery side effect。
  • 19:19:47 最終複查:CP001 ONLINE;CP001-001/002 在 Branch/edge 均為 AVAILABLE / NOERROR;active transaction 0/0。

測試摘要

  • 正式報告:test_report/20260901_001_redmine-1456-ken-reconnect-liveness.md
  • Result:5 PASS / 0 FAIL / 0 PARTIAL
  • 關聯 Unit Test:12 run / 0 failures / 0 errors / 0 skipped
  • 發現一項 Low observation:部分雙語 reconnect log 的 placeholder 數量與 logger 參數不一致,只影響診斷文字,本單不修改。

Catalog 更新草案

  • OCP-018:P0,轉為 Active,保存本次 cooldown reconnect 實測證據。
  • OCP-019:P0/Gap,獨立追蹤「watchdog 已先投影 OFFLINE/UNAVAILABLE 後再收斂」的 330 秒長斷線 E2E,避免把 unit regression 誤宣稱為完整 physical E2E。
  • Catalog 文件完成並通過 validator 後,仍依 SOP 等待 Ken 的文件 gate 確認,再 amend 本地 commit。

是由 陳國瑋 於 30 天 前更新

文件/Catalog gate 完成(2026-09-01)

Ken 已確認文件更新通過。

  • Root repository 本地 commit 已 amend:0dc1d53 docs(#1456): 更新開發流程與環境測試紀錄
  • charge_point implementation commit:0e07f82 fix(#1456): 修正 OCPP 重連存活與連接器狀態收斂
  • E2E report:20260901_001_redmine-1456-ken-reconnect-liveness.md
  • Catalog:OCP-018 P0 Active;新增 OCP-019 P0 Gap;OCP-018 已加入最短 P0 Core Smoke。
  • Catalog validator:PASS(178 cases;P0=58、P1=69、P2=46、P3=5)
  • Root branch 目前只在本地比 origin ahead 1;依 Ken 指示本階段未 push。
  • 未建立 PR/MR,未 merge;Issue 保持 In Progress,等待 Ken 後續 review/release 指示。

是由 陳國瑋 於 30 天 前更新

  • 狀態 從 In Progress 變更為 Resolved

結案摘要

  • charge_point 修補 commit:0e07f82,完成 reconnect immediate Heartbeat、舊 session pending CALL cleanup,以及 Heartbeat 成功後的 connector 實際狀態重新同步。
  • Unit Test:12 PASS,0 failure/error/skip。
  • Ken environment E2E:5 PASS,0 FAIL/PARTIAL;Boot cooldown、單一 Heartbeat scheduler、connector convergence 與無完整 Recovery 副作用均通過。
  • E2E Catalog validator:PASS;OCP-018 已轉 Active,OCP-019 保留為額外 watchdog-first 極端時序回歸 Gap,不阻擋本 bug 結案。
  • Root 文件 commit:0dc1d53,目前依 Ken 先前指示仍只在 local、尚未 push;feature branch 尚未 merge 至 main,不影響本次已驗證完成的結案判定。
  • 本單調整為 Resolved / 100%。

是由 陳國瑋 於 30 天 前更新

  • 狀態 從 Resolved 變更為 Closed

結案狀態補正

Redmine 在 Resolved 狀態下未套用 100% 完成度,因此依已完成並通過 Ken environment E2E 的結案決定,改用正式完成狀態關閉本單。

動作

匯出至 Atom PDF