fix(opcuaclient): scope the #670 F2 late-replay drop to the post-recovery window
The global 'never older than the last Good source time' rule froze a node whenever an upstream clock stepped backwards (NTP correction, PLC clock reset) until the clock caught up. The check is now a replay guard armed only by a same-session recovery (keep-alive bad->good): per ref it holds the last Good upstream source time (raised by the recovery read), a ref disarms on its first in-order notification, and the whole guard expires after max(30 s, 2x the longest subscription keep-alive window). Outside it every notification is accepted. The steady-state tracker records the LAST accepted time, not a high-water mark.
This commit is contained in:
@@ -73,10 +73,12 @@ The session keep-alive (`KeepAliveInterval`, default 5 s) is the liveness signal
|
||||
the refs still Bad are re-read once and published Good (a server whose subscription survived does not resend an
|
||||
unchanged value, and a `(value, status)` dedup downstream lets the identical value through), alarm quality goes
|
||||
back Good and a `ConditionRefresh` re-reports anything missed. A notification that lands first wins over the read.
|
||||
A notification whose source time is OLDER than the newest Good upstream source time already published for its ref
|
||||
(notification or recovery read) is dropped — the SDK can deliver a missed one seconds late after a same-session
|
||||
recovery, which used to regress the node (live-gate F2). Equal timestamps pass (status-only changes); the outage
|
||||
Bad never moves the comparison; a re-initialise resets it.
|
||||
For a bounded **replay window** after such a recovery — max(30 s, 2 x the longest subscription keep-alive window) —
|
||||
a notification whose source time is OLDER than the ref's last Good upstream source time (raised by the recovery
|
||||
read) is dropped: the SDK was seen delivering a missed one 4–5 s late, which regressed the node (live-gate F2).
|
||||
A ref disarms on its first in-order (equal or newer) notification. Outside that window every notification is
|
||||
accepted whatever its timestamp, so an upstream clock that steps backwards (NTP, a PLC clock reset) never
|
||||
freezes a node. The outage Bad never moves the comparison; a re-initialise resets it.
|
||||
- **The session does not come back** (an upstream restart: the server forgot the session) ⇒ the driver stays
|
||||
`Degraded` with its read stamp frozen, which is exactly the #527 comms-loss watchdog's trip condition. After 90 s
|
||||
`DriverInstanceActor` runs `ShutdownAsync` + `InitializeAsync` (retried every 10 s while the server is down) and
|
||||
@@ -234,7 +236,7 @@ Session/publish events are published on the shared OTel meter as the counter
|
||||
`otopcua.driver.opcuaclient.events`, tagged `driver_instance` (the driver instance id) and `event` =
|
||||
`publish_response`, `notification` (items), `data_change`, `bad_status_value`, `publish_error`,
|
||||
`keepalive_miss`, `keepalive_recovered` (a bad keep-alive followed by a good one on the same session),
|
||||
`stale_notification_dropped` (an out-of-order late notification, see above). A climbing
|
||||
`stale_notification_dropped` (a late replay dropped inside the post-recovery window, see above). A climbing
|
||||
`keepalive_miss` / `keepalive_recovered` rate is the signature of a flapping upstream (`reconnect_*` were removed with
|
||||
the reconnect handler, #670). They are not part of `DriverHealth` or the
|
||||
`/hosts` panel.
|
||||
|
||||
Reference in New Issue
Block a user