Galaxy: a SubscribeAsync still awaiting the old session's reply can register old-session handles after a reconnect #861

Closed
opened 2026-11-11 10:27:03 -05:00 by dohertj2 · 1 comment
Owner

Found while fixing #859 (pre-existing, not introduced by it). _runtimeGeneration changes only on teardown, not on reconnect. A SubscribeAsync (or subscribe-once read) whose SubscribeBulk was issued on the old gateway session and returns after ReopenAsync + ResubscribeAllAsync can still Register the old session's item handles in the registry. The new session may reuse those handle numbers for other tags (MXAccess handles are small integers), so events could fan to the wrong subscription, or the subscription stays bound to a dead handle. #859's per-pump epoch stops held events crossing sessions but not this registration. Fix idea: carry a session epoch into the subscribe call and refuse/redo registration when it changed while awaiting the reply; add a test with a reply that completes after a reconnect.

Found while fixing #859 (pre-existing, not introduced by it). `_runtimeGeneration` changes only on teardown, not on reconnect. A `SubscribeAsync` (or subscribe-once read) whose `SubscribeBulk` was issued on the old gateway session and returns after `ReopenAsync` + `ResubscribeAllAsync` can still `Register` the old session's item handles in the registry. The new session may reuse those handle numbers for other tags (MXAccess handles are small integers), so events could fan to the wrong subscription, or the subscription stays bound to a dead handle. #859's per-pump epoch stops *held* events crossing sessions but not this registration. Fix idea: carry a session epoch into the subscribe call and refuse/redo registration when it changed while awaiting the reply; add a test with a reply that completes after a reconnect.
dohertj2 added the type:followuppriority:p3 labels 2026-11-11 10:27:03 -05:00
Author
Owner

Fixed with #859 (d08e9002, 133bc07d, a8d3790b). Pushed to master in e857135e. The registry is per-session: StartNewSession() runs before and after the gateway session swap in ReopenAsync (and in ReplayAsync when no reopen did); registration with a stale session is refused atomically; a straddling SubscribeAsync is re-issued (≤3), a straddling read returns BadCommunicationError; old-session handle numbers are never sent to UnsubscribeBulk; the replay re-subscribes only older-session entries and the probe watcher re-subscribes only its stale platforms.

Fixed with #859 (`d08e9002`, `133bc07d`, `a8d3790b`). Pushed to master in `e857135e`. The registry is per-session: `StartNewSession()` runs before and after the gateway session swap in `ReopenAsync` (and in `ReplayAsync` when no reopen did); registration with a stale session is refused atomically; a straddling `SubscribeAsync` is re-issued (≤3), a straddling read returns `BadCommunicationError`; old-session handle numbers are never sent to UnsubscribeBulk; the replay re-subscribes only older-session entries and the probe watcher re-subscribes only its stale platforms.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: dohertj2/lmxopcua#861