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:
@@ -765,13 +765,11 @@ public sealed class GatewaySession
|
||||
// distributor guarantees a single consumer) and maps each frame to the public MxEvent,
|
||||
// preserving worker order. Mirrors the former ProduceEventsAsync mapping exactly.
|
||||
//
|
||||
// This deliberately duplicates the three lines of ReadEventsAsync rather than enumerating
|
||||
// it: every worker event crosses this source, and routing it through a second pure
|
||||
// pass-through iterator cost two extra MoveNextAsync state-machine hops per event for no
|
||||
// semantic value. ReadEventsAsync stays for ISessionManager.ReadEventsAsync; keep the two
|
||||
// bodies in step. Only one of them may run per attach — WorkerClient.ReadEventsAsync
|
||||
// single-reader-claims the event channel and throws on a second consumer — and on the
|
||||
// distributor path that one consumer is this method.
|
||||
// This is the session's only reader of the worker event channel: every gateway consumer —
|
||||
// gRPC subscribers, the dashboard mirror, the alarm monitor — attaches to the distributor
|
||||
// this source feeds. WorkerClient.ReadEventsAsync single-reader-claims that channel and
|
||||
// throws on a second consumer, so any future path that drains it directly fails loudly
|
||||
// rather than splitting events.
|
||||
private async IAsyncEnumerable<MxEvent> MapWorkerEventsAsync(
|
||||
[EnumeratorCancellation] CancellationToken cancellationToken)
|
||||
{
|
||||
@@ -1522,33 +1520,6 @@ public sealed class GatewaySession
|
||||
cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads events from the worker as an asynchronous enumerable stream.
|
||||
/// </summary>
|
||||
/// <param name="cancellationToken">Token to cancel the asynchronous operation.</param>
|
||||
/// <remarks>
|
||||
/// Backs <c>ISessionManager.ReadEventsAsync</c>. The distributor does <em>not</em> come
|
||||
/// through here — <c>MapWorkerEventsAsync</c> inlines this body to save a per-event
|
||||
/// iterator hop, so changes made here belong there too. The two are mutually exclusive
|
||||
/// per attach: <see cref="IWorkerClient.ReadEventsAsync"/> claims the worker event
|
||||
/// channel for a single reader and throws on the second consumer.
|
||||
/// </remarks>
|
||||
/// <returns>An asynchronous stream of worker events.</returns>
|
||||
public async IAsyncEnumerable<WorkerEvent> ReadEventsAsync(
|
||||
[EnumeratorCancellation] CancellationToken cancellationToken)
|
||||
{
|
||||
IWorkerClient workerClient = await GetReadyWorkerClientAsync(cancellationToken).ConfigureAwait(false);
|
||||
TouchClientActivity(_eventStreaming.TimeProvider.GetUtcNow());
|
||||
|
||||
await foreach (WorkerEvent workerEvent in workerClient
|
||||
.ReadEventsAsync(cancellationToken)
|
||||
.WithCancellation(cancellationToken)
|
||||
.ConfigureAwait(false))
|
||||
{
|
||||
yield return workerEvent;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Closes the session and shuts down the worker process.
|
||||
/// </summary>
|
||||
|
||||
@@ -35,14 +35,6 @@ public interface ISessionManager
|
||||
WorkerCommand command,
|
||||
CancellationToken cancellationToken);
|
||||
|
||||
/// <summary>Reads events streamed from the worker for the specified session.</summary>
|
||||
/// <param name="sessionId">Identifier of the session.</param>
|
||||
/// <param name="cancellationToken">Token to cancel the asynchronous operation.</param>
|
||||
/// <returns>Events emitted by the worker.</returns>
|
||||
IAsyncEnumerable<WorkerEvent> ReadEventsAsync(
|
||||
string sessionId,
|
||||
CancellationToken cancellationToken);
|
||||
|
||||
/// <summary>Closes a session and terminates its worker process.</summary>
|
||||
/// <param name="sessionId">Identifier of the session to close.</param>
|
||||
/// <param name="cancellationToken">Token to cancel the asynchronous operation.</param>
|
||||
|
||||
@@ -187,16 +187,6 @@ public sealed class SessionManager : ISessionManager
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IAsyncEnumerable<WorkerEvent> ReadEventsAsync(
|
||||
string sessionId,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
GatewaySession session = GetRequiredSession(sessionId);
|
||||
|
||||
return session.ReadEventsAsync(cancellationToken);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<SessionCloseResult> CloseSessionAsync(
|
||||
string sessionId,
|
||||
|
||||
@@ -464,11 +464,6 @@ public sealed class AlarmFailoverEndToEndTests
|
||||
return Task.FromResult(new WorkerCommandReply { Reply = reply });
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IAsyncEnumerable<WorkerEvent> ReadEventsAsync(
|
||||
string sessionId,
|
||||
CancellationToken cancellationToken) => throw new NotSupportedException();
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool TryGetSession(string sessionId, [MaybeNullWhen(false)] out GatewaySession session)
|
||||
{
|
||||
|
||||
@@ -618,11 +618,6 @@ public sealed class GatewayAlarmMonitorAttachOrderTests
|
||||
return new WorkerCommandReply { Reply = reply };
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IAsyncEnumerable<WorkerEvent> ReadEventsAsync(
|
||||
string sessionId,
|
||||
CancellationToken cancellationToken) => throw new NotSupportedException();
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool TryGetSession(string sessionId, [MaybeNullWhen(false)] out GatewaySession session)
|
||||
{
|
||||
|
||||
@@ -777,11 +777,6 @@ public sealed class GatewayAlarmMonitorProviderModeTests
|
||||
return Task.FromResult(new WorkerCommandReply { Reply = reply });
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IAsyncEnumerable<WorkerEvent> ReadEventsAsync(
|
||||
string sessionId,
|
||||
CancellationToken cancellationToken) => throw new NotSupportedException();
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool TryGetSession(string sessionId, [MaybeNullWhen(false)] out GatewaySession session)
|
||||
{
|
||||
|
||||
@@ -261,10 +261,6 @@ public sealed class DashboardLiveDataServiceTests
|
||||
CancellationToken cancellationToken) =>
|
||||
throw new NotSupportedException();
|
||||
|
||||
/// <inheritdoc />
|
||||
public IAsyncEnumerable<WorkerEvent> ReadEventsAsync(string sessionId, CancellationToken cancellationToken) =>
|
||||
throw new NotSupportedException();
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<SessionCloseResult> KillWorkerAsync(
|
||||
string sessionId,
|
||||
|
||||
@@ -370,14 +370,6 @@ public sealed class DashboardSessionAdminServiceTests
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IAsyncEnumerable<WorkerEvent> ReadEventsAsync(
|
||||
string sessionId,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<SessionCloseResult> CloseSessionAsync(
|
||||
string sessionId,
|
||||
|
||||
@@ -761,14 +761,6 @@ public sealed class EventStreamServiceTests
|
||||
return Task.FromResult(new WorkerCommandReply());
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IAsyncEnumerable<WorkerEvent> ReadEventsAsync(
|
||||
string sessionId,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
return _sessions[sessionId].ReadEventsAsync(cancellationToken);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<SessionCloseResult> CloseSessionAsync(
|
||||
string sessionId,
|
||||
|
||||
@@ -935,19 +935,6 @@ public sealed class MxAccessGatewayServiceConstraintTests
|
||||
return Task.FromResult(InvokeReply);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async IAsyncEnumerable<WorkerEvent> ReadEventsAsync(
|
||||
string sessionId,
|
||||
[System.Runtime.CompilerServices.EnumeratorCancellation] CancellationToken cancellationToken)
|
||||
{
|
||||
foreach (WorkerEvent ev in Events)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
await Task.Yield();
|
||||
yield return ev;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<SessionCloseResult> CloseSessionAsync(
|
||||
string sessionId,
|
||||
|
||||
@@ -517,7 +517,7 @@ public sealed class MxAccessGatewayServiceTests
|
||||
/// <summary>The last owner key id passed to OpenSessionAsync.</summary>
|
||||
public string? LastOwnerKeyId { get; private set; }
|
||||
|
||||
/// <summary>The last session ID passed to ReadEventsAsync.</summary>
|
||||
/// <summary>The last session ID the event stream service was asked to stream.</summary>
|
||||
public string? LastReadEventsSessionId { get; private set; }
|
||||
|
||||
/// <summary>The last worker command passed to InvokeAsync.</summary>
|
||||
@@ -540,10 +540,10 @@ public sealed class MxAccessGatewayServiceTests
|
||||
/// <summary>The number of times InvokeAsync was called.</summary>
|
||||
public int InvokeCount { get; private set; }
|
||||
|
||||
/// <summary>The events to return from ReadEventsAsync.</summary>
|
||||
/// <summary>The events the fake event stream service replays for this manager.</summary>
|
||||
public List<WorkerEvent> Events { get; } = [];
|
||||
|
||||
/// <summary>Records the session ID passed to ReadEventsAsync.</summary>
|
||||
/// <summary>Records the session ID the event stream service was asked to stream.</summary>
|
||||
/// <param name="sessionId">Identifier of the session.</param>
|
||||
public void RecordReadEventsSessionId(string sessionId)
|
||||
{
|
||||
@@ -602,20 +602,6 @@ public sealed class MxAccessGatewayServiceTests
|
||||
return Task.FromResult(InvokeReply);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async IAsyncEnumerable<WorkerEvent> ReadEventsAsync(
|
||||
string sessionId,
|
||||
[EnumeratorCancellation] CancellationToken cancellationToken)
|
||||
{
|
||||
LastReadEventsSessionId = sessionId;
|
||||
foreach (WorkerEvent workerEvent in Events)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
await Task.Yield();
|
||||
yield return workerEvent;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<SessionCloseResult> CloseSessionAsync(
|
||||
string sessionId,
|
||||
|
||||
@@ -347,11 +347,6 @@ public sealed class GatewaySessionDashboardMirrorTests
|
||||
WorkerCommand command,
|
||||
CancellationToken cancellationToken) => Task.FromResult(new WorkerCommandReply());
|
||||
|
||||
/// <inheritdoc />
|
||||
public IAsyncEnumerable<WorkerEvent> ReadEventsAsync(
|
||||
string sessionId,
|
||||
CancellationToken cancellationToken) => session.ReadEventsAsync(cancellationToken);
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<SessionCloseResult> CloseSessionAsync(
|
||||
string sessionId,
|
||||
|
||||
-8
@@ -848,14 +848,6 @@ public sealed class GatewayGrpcAuthorizationInterceptorTests
|
||||
return Task.FromResult(new WorkerCommandReply());
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IAsyncEnumerable<WorkerEvent> ReadEventsAsync(
|
||||
string sessionId,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
return AsyncEnumerable.Empty<WorkerEvent>();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<SessionCloseResult> CloseSessionAsync(
|
||||
string sessionId,
|
||||
|
||||
Reference in New Issue
Block a user