DCL: graceful stop of the Warm Primary still waits out the connectivity retries (66 s) - ServiceLevel 0 is seen but cleared as unknown #141
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Found in the QA Warm gate for #138 / lmxopcua#907 on 2026-11-13. The record is lmxopcua
docs/deploy/2026-11-13-qa-warm-717.md, step 4a.What happened. The
WarmCanaryconnection (vq03 primary, vq04 backup) was on vq03 when vq03's OtOpcUa was stopped gracefully. vq03 was the Warm Primary."Primary" endpoint reports ServiceLevel 0 (Degraded band, < 200) — will check the other endpoint if it persists for 10s. vq04 had already promoted to 250 (13:52:57.3Z).ServiceLevel is unknown — degraded watch cleared.AdapterDisconnected. The connectivity trigger then retried the stopped primaryFailoverRetryCount(3) times, about 17 s each (connect timeout + 5 s), before switching to the backup at 13:53:57.9Z.Mode=Unsetwas the same: 67 s.A graceful stop reports 0 for only about 6 s before the session dies (#906 makes the leave fast). That is shorter than the 10 s degraded window, so #138 never fires on the most common failover. The 66 s is the old connectivity path.
Proposal (pick one or both):
AdapterDisconnected, if the last ServiceLevel sample of this session was below 200, probe the other endpoint at once. If it answers Healthy, switch without the retry cycle (expected: about 10 s, all keep-alive detection).Either option has to keep the anti-flap rules (Healthy and higher, dwell).
Not blocking: Warm is live on QA and every #138 gate passed. The connect-time preference moved the connection off the Standby in 0.1 s.
https://claude.ai/code/session_01GT67ACmpMR7b9LGRPLijMi
Fixed on
maininc4ec51e8. Not deployed; QA live re-check pending.Design: both proposals, plus the ServiceLevel-0 shortcut. All three keep the anti-flap rules: the other endpoint must report Healthy (≥ 200) and higher, and the dwell applies.
Rationale: the 0 trigger is the fast path, because it fires before the session closes. The lost-link trigger is the backstop for a 0 that was seen too late, or a Degraded level followed by a drop. The disconnect probe is not applied to every drop: after only Healthy levels (250/240,
Mode=Unsetwithout a 0), inside the dwell, or with the feature off, the retry-then-failover behaviour is exactly as before. A network blip on a 250 Primary therefore still does not move the connection to the 240 Secondary. ServerState Shutdown was not added: it needs a second read per poll, and OtOpcUa already signals the stop with 0.Evidence
DataConnectionGoingAwayFailoverTests: 10 cases. Each trigger and non-trigger has a positive control, and the "no shortcut" cases assert on the order in which sessions were opened. Cases: 0 skips a 30 s window; 90 and 1 still wait (then 0 moves the connection); 0 with a Degraded peer stays put and probes it exactly once; 0 inside the dwell waits, then switches; the QA sequence (Degraded → unknown → link lost) makes no connect to the stopped node; a lost link after Healthy levels gives 3× Primary, then Backup, as before; a lost link with a Degraded peer gives probe → normal retry; a lost link inside the dwell gives retry, no probe; with the feature disabled there is no probe.ZB.MOM.WW.ScadaBridge.slnx: 0 warnings, 0 errors. DataConnectionLayer.Tests: 608/608 on 3 consecutive full runs.Component-DataConnectionLayer.md→ ServiceLevel-aware failover → A server going away (Gitea #141), anddocs/components/DataConnectionLayer.md.QA re-check to do after deploy: repeat step 4a of lmxopcua
docs/deploy/2026-11-13-qa-warm-717.md. The expected result is Good on vq04 within a few seconds of the stop, with the site eventFailover from Primary to Backup (ServiceLevel).https://claude.ai/code/session_01GT67ACmpMR7b9LGRPLijMi