refactor(sessions): remove the dead ISessionManager.ReadEventsAsync chain
ISessionManager.ReadEventsAsync had zero production call sites: the worker event channel is drained once by GatewaySession.MapWorkerEventsAsync (the distributor pump), and every consumer — gRPC subscribers, the dashboard mirror, the alarm monitor — attaches to the distributor. The interface member, SessionManager's forwarder, and GatewaySession.ReadEventsAsync are gone; IWorkerClient/WorkerClient.ReadEventsAsync is untouched, it is the live worker-channel claim. No test was removed or rewired: nothing invoked the member through the interface. Nine ISessionManager test fakes carried a required-member stub (seven threw NotSupportedException or yielded nothing; EventStreamServiceTests and GatewaySessionDashboardMirrorTests forwarded to the session; the two MxAccessGatewayService fakes yielded their Events list) — all nine stubs were deleted. The MxAccessGatewayService suites' streaming tests already run through FakeEventStreamService, which reads the same Events list, so their coverage is unchanged; only the now-inaccurate doc comments on Events / LastReadEventsSessionId were reworded. The MapWorkerEventsAsync comment no longer describes a twin to keep in step; it now states the single-reader claim directly. docs/Sessions.md drops ReadEventsAsync from the SessionManager member list and from the Run-state prose. The 2026-08-15 deferred-remediation as-built note records the removal.
This commit is contained in:
+2
-2
@@ -49,7 +49,7 @@ public void TransitionTo(SessionState nextState)
|
||||
|
||||
### SessionManager (ISessionManager)
|
||||
|
||||
`SessionManager` is the orchestrator. It exposes `OpenSessionAsync`, `TryGetSession`, `InvokeAsync`, `ReadEventsAsync`, `CloseSessionAsync`, `KillWorkerAsync`, `CloseExpiredLeasesAsync`, and `ShutdownAsync`. It composes `ISessionRegistry`, `ISessionWorkerClientFactory`, `GatewayMetrics`, and `GatewayOptions`.
|
||||
`SessionManager` is the orchestrator. It exposes `OpenSessionAsync`, `TryGetSession`, `InvokeAsync`, `CloseSessionAsync`, `KillWorkerAsync`, `CloseExpiredLeasesAsync`, and `ShutdownAsync`. It composes `ISessionRegistry`, `ISessionWorkerClientFactory`, `GatewayMetrics`, and `GatewayOptions`.
|
||||
|
||||
`CloseSessionAsync` and `KillWorkerAsync` are both end-of-life paths but differ in what they offer the worker:
|
||||
|
||||
@@ -191,7 +191,7 @@ The order — fault, deregister, dispose, release slot, record metric, log, reth
|
||||
|
||||
### Run
|
||||
|
||||
While `Ready`, callers reach the worker through `SessionManager.InvokeAsync` or `ReadEventsAsync`. Both delegate to `GatewaySession`, which checks the state under lock and updates `LastClientActivityAt` on every invocation. `GatewaySession` also exposes typed bulk helpers (`AddItemBulkAsync`, `SubscribeBulkAsync`, etc.) that wrap `WorkerCommand` round-trips and translate non-`Ok` `ProtocolStatus` replies into `SessionManagerException` with `SessionNotReady`.
|
||||
While `Ready`, callers reach the worker through `SessionManager.InvokeAsync`, which delegates to `GatewaySession`, which checks the state under lock and updates `LastClientActivityAt` on every invocation. Events do not travel this path: every consumer attaches to the session's `SessionEventDistributor` instead (see below), so the manager exposes no event-read member. `GatewaySession` also exposes typed bulk helpers (`AddItemBulkAsync`, `SubscribeBulkAsync`, etc.) that wrap `WorkerCommand` round-trips and translate non-`Ok` `ProtocolStatus` replies into `SessionManagerException` with `SessionNotReady`.
|
||||
|
||||
Event streaming uses `AttachEventSubscriber` which returns a disposable lease. When `allowMultipleSubscribers` is false (single-subscriber mode) a second attach throws `EventSubscriberAlreadyActive`; this prevents two gRPC streams from racing on the same worker event channel. When it is true, up to `MaxEventSubscribersPerSession` concurrent external subscribers are allowed and the next attach throws `EventSubscriberLimitReached`. The count-check-and-increment is atomic under the session lock, so concurrent attaches can never exceed the cap. The gateway-owned internal dashboard mirror subscriber is registered directly on the distributor and does not count toward the cap. Active event subscribers keep the session lease from expiring until the stream is disposed.
|
||||
|
||||
|
||||
@@ -321,7 +321,8 @@ is worth keeping, this is the record.
|
||||
through `ISessionManager.ReadEventsAsync`. That interface member itself has zero
|
||||
production call sites — only test fakes implement and exercise it. Deleting it is a
|
||||
mechanical but wide change (~15 test-fake touches), so it is recorded as a follow-up
|
||||
rather than done here.
|
||||
rather than done here. Removed by `docs/plans/2026-08-17-deferred-closeout.md` Task 1,
|
||||
2026-08-17.
|
||||
|
||||
**Task 5 — dashboard event feed, two review rounds.** Review caught two races that
|
||||
the first cut did not have. First, subscription lifetime: subscriptions are now
|
||||
|
||||
Reference in New Issue
Block a user