docs(archreview): R2-01 S7 fault-path hardening complete — STAB-14/15 fixed, STAB-8 seam documented (task 12)
v2-ci / build (pull_request) Successful in 3m28s
v2-ci / integration (tests/Server/ZB.MOM.WW.OtOpcUa.Host.IntegrationTests) (pull_request) Failing after 3m34s
v2-ci / unit-tests (pull_request) Failing after 9m12s
v2-ci / integration (tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.IntegrationTests) (pull_request) Failing after 1m46s

This commit is contained in:
Joseph Doherty
2026-07-13 10:09:03 -04:00
parent a1b22f979d
commit bdb7ed412d
3 changed files with 42 additions and 2 deletions
+21
View File
@@ -167,6 +167,13 @@ are the new STAB-14..17 below.
### 1. Stability
> **REMEDIATED by R2-01 (branch `r2/01-s7-fault-hardening`).** Connect-timeout now surfaces as a
> `TimeoutException` at the `EnsureConnectedAsync`/`InitializeAsync` source (not an escaping OCE), plus
> `when (token.IsCancellationRequested)` filters on the read/write ensure-wrappers (`:488`/`:1000`) and all
> three poll-loop OCE catches — so a non-teardown OCE degrades/backs-off instead of killing the loop. Unit
> guards T1T4 (`S7DriverReconnectTests`, incl. the poll-loop-survival regression). Live gate
> `S7_1500ConnectTimeoutOutageTests` (env-gated SYN-blackhole, `deferred-live`).
**STAB-14 — High — NEW — S7's reconnect wrapper rethrows connect-timeout
cancellation, and the poll loop's bare OCE catch then permanently kills the
subscription.** `EnsureConnectedAsync` bounds the reopen with a linked
@@ -192,6 +199,14 @@ degrade-to-`BadCommunicationError` path), and filter the poll loop's OCE
catches on `ct.IsCancellationRequested`; add a fake-factory unit test where
`OpenAsync` honours a token that only the `CancelAfter` fires.
> **REMEDIATED by R2-01 (branch `r2/01-s7-fault-hardening`).** `IsS7ConnectionFatal` broadened to the
> ISO-on-TCP framing surface (`TPKTInvalidException`/`TPDUInvalidException`/`WrongNumberOfBytesException` +
> `PlcException{WrongNumberReceivedBytes}`; deliberately NOT `InvalidDataException` — that is the driver's own
> config-mismatch decode backstop on a healthy socket). Cancellation observed mid-PDU on read + write now
> marks `_plcDead` before propagating; the probe restructured to classify + mark dead under `_gate` so the next
> tick reopens a lying/hung handle. Unit guards T5T9 (`S7DriverReconnectTests`) + negative control
> `Read_does_not_reopen_on_a_data_address_error`.
**STAB-15 — High — NEW — S7's connection-fatal classification misses
framing/desync errors and cancellation-during-I/O, reproducing Modbus's
STAB-3 desync; and the probe never marks the handle dead.**
@@ -340,6 +355,12 @@ S7's `ComputeBackoffDelay`, `S7Driver.cs:1447`) and a small connect-attempt
throttle to the lazy-reconnect drivers (S7's `EnsureConnectedAsync` is now
the natural home for a last-attempt timestamp).
> **SEAM DOCUMENTED by R2-01 (branch `r2/01-s7-fault-hardening`).** The S7 connect-throttle is left to
> R2-09 (fleet-wide reconnect-backoff), not implemented driver-locally. `EnsureConnectedAsync`'s `<remarks>`
> now names itself as the single gate-serialized choke-point where a last-failed-attempt timestamp plugs in.
> (Note: R2-01's STAB-14/15 fixes already *reduce* dead-PLC cost — poll ticks that used to die now back off to
> `PollBackoffCap`, and connect-timeouts no longer escape as anomalous OCEs.)
**STAB-9 — Medium — `PollGroupEngine`'s `onError` sink is dead code: no
driver passes it.** All five consumers construct the engine without the
error callback (`ModbusDriver.cs:115`, `AbCipDriver.cs:133`,