動作
Bug #1456
已結束[Backend Bug] A17 CP001 重連遇 Boot cooldown 後四支狀態殘留 UNAVAILABLE
開始日期:
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,因此同秒被改寫。
已確認的事件時序¶
- 2026-08-27 06:04:17:CP001 WebSocket 因 pong timeout 關閉。
- 06:04:47:重連成功,BootNotification Accepted;Branch 記錄 fresh Boot。
- 06:06:47:WebSocket 再次因 pong timeout 關閉。
- 06:07:17:再次重連,但距前次 Boot 未滿 300 秒,BootNotification 因 cooldown 被跳過。
- 06:07:37~06:08:35:CP001 各 connector 重新送出實際狀態,四支最後都曾回到
AVAILABLE。 - 06:10:53:watchdog 使用 cutoff
06:05:23.505;當時lastHeartbeat=05:39:48、lastBootNotification=06:04:47,兩者都已過期。 - 06:10:54:四支 connectors 同時被改成
UNAVAILABLE / OCPP heartbeat stale。 - 06:11:29:Heartbeat 成功並把 CP001 恢復為
ONLINE,但沒有恢復 connector operational status。 - 至 2026-08-29 21:18:Branch 四支仍是 stale
UNAVAILABLE;edge 四支皆為AVAILABLE且 meter read 新鮮。
根因判定¶
已確認¶
-
BootNotificationScheduler使用 300 秒 cooldown;快速重連時可跳過 Boot。 -
HeartbeatScheduler已啟動時,reconnect 不會重建排程或立即補送 Heartbeat,只等待原本的 fixed-delay 時點。 - 因此可能出現「Boot 被 cooldown 跳過 + 下一筆 Heartbeat 晚於 Branch 330 秒 cutoff」。
- Branch 的下一筆 Heartbeat 只恢復 charge point connection,不會重新同步已被 offline propagation 覆寫的 connector 狀態。
- edge 若一直維持
AVAILABLE、沒有新狀態變化,就不會再自然補送 StatusNotification,造成 Branch/edge 長期分裂。
尚待驗證的上游觸發因素¶
- 2026-08-27 只有 CP001 出現 pong timeout,共 68 次;CP002/CP003 都是 0。
- CP001 同期有兩筆長時間 transaction、大量 MeterValues 與 970 次
WebSocket not connectedqueue 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。 - 沒有中斷任何充電交易。
此操作只恢復現場,沒有永久修補。
下週待確認的修正方向¶
- CP 每次成功 reconnect 時,即使 BootNotification 因 cooldown 略過,也應立即送出一筆 Heartbeat 或等價 liveness;需避免 heartbeat storm 與重複 scheduler。
- Branch 在 OFFLINE → ONLINE 後應有 connector convergence 機制,使狀態能與 edge 實況重新同步;不得以 Heartbeat 任意偽造
AVAILABLE或覆寫FAULTED/CHARGING。 - 調查 CP001 pong timeout 與 MeterValues queue/pending request 壓力,評估 backpressure、批次上傳、pending request timeout/cleanup 與 WebSocket thread isolation。
- 保留既有 active transaction 保護、TCP half-open 偵測與 #1436 Boot grace 行為。
以上只是候選方向,須下週依 Redmine SOP 完成需求確認與規劃 gate 後才能實作。
驗收條件¶
- 舊 Heartbeat + reconnect 落在 Boot cooldown 內時,必須在 watchdog cutoff 前建立 fresh liveness。
- 重複 WebSocket close/reconnect 不產生多個 Heartbeat scheduler,也不造成 Boot/Heartbeat storm。
- reconnect 後已上報
AVAILABLE的 connectors 不得再被舊 liveness 覆寫成 staleUNAVAILABLE。 - 即使 watchdog 先投影離線,下一筆 Heartbeat 使 CP 回到 ONLINE 後,Branch connectors 最終仍須與 edge 當前狀態收斂。
-
FAULTED、CHARGING、active transaction 與 current transaction 保護不得退化。 - CP002/CP003 與第三方 OCPP Charge Point 行為不得受 CloudLink-specific scheduler 修改影響。
- 補可控制時間的 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建立並 pushfeature/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-docsHTTP 200;host-local OCPP listener18082與 Modbus gateway192.168.127.254:502TCP 可達。 - 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%。
動作