fix(opcuaclient): #670 F2 re-gate — hold the replay guard for the whole window
The F2 live gate failed (10 value regressions over 4 blips): IsLateReplay disarmed a ref on its first in-order notification, which arrives ~1 s after recovery, while the SDK's late replay lands 4-6 s later. Equal source times also passed, so a replay equal to the recovery read got through. While the window is open each ref now carries a high-water mark (seeded at arming, raised by the recovery read, advanced by every accepted notification); a notification at or below it is dropped (equal included — OPC UA moves the source timestamp on every value/status change). Null source time passes without moving the mark. Window expiry still disarms entirely, so an upstream clock step-back is never frozen outside the window. Arm/expiry serialised by a lock so an expiry racing a re-arm cannot wipe fresh seeds.
This commit is contained in:
@@ -74,9 +74,13 @@ The session keep-alive (`KeepAliveInterval`, default 5 s) is the liveness signal
|
||||
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.
|
||||
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
|
||||
each ref is guarded by a high-water mark — its last Good upstream source time, raised by the recovery read and
|
||||
advanced by every notification accepted in the window — and a notification whose source time is at or BELOW it
|
||||
(equal included: OPC UA moves the source timestamp on every value or status change, so an equal one is a replay)
|
||||
is dropped: the SDK was seen delivering a missed one 4–6 s late, which regressed the node (live-gate F2). The
|
||||
guard holds for the WHOLE window — fresh notifications arrive ~1 s after recovery, before the late replay, so a
|
||||
ref is never disarmed early (the F2 re-gate failed on exactly that). A notification with no source time passes
|
||||
without moving the mark. 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
|
||||
|
||||
Reference in New Issue
Block a user