perf(sessions): fold the ReadEventsAsync pass-through into MapWorkerEventsAsync — one fewer iterator per event
This commit is contained in:
@@ -764,11 +764,24 @@ public sealed class GatewaySession
|
|||||||
// The distributor's single event source. Drains the worker event stream once (the
|
// The distributor's single event source. Drains the worker event stream once (the
|
||||||
// distributor guarantees a single consumer) and maps each frame to the public MxEvent,
|
// distributor guarantees a single consumer) and maps each frame to the public MxEvent,
|
||||||
// preserving worker order. Mirrors the former ProduceEventsAsync mapping exactly.
|
// 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.
|
||||||
private async IAsyncEnumerable<MxEvent> MapWorkerEventsAsync(
|
private async IAsyncEnumerable<MxEvent> MapWorkerEventsAsync(
|
||||||
[EnumeratorCancellation] CancellationToken cancellationToken)
|
[EnumeratorCancellation] CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
MxAccessGrpcMapper mapper = _eventStreaming.Mapper;
|
MxAccessGrpcMapper mapper = _eventStreaming.Mapper;
|
||||||
await foreach (WorkerEvent workerEvent in ReadEventsAsync(cancellationToken)
|
IWorkerClient workerClient = await GetReadyWorkerClientAsync(cancellationToken).ConfigureAwait(false);
|
||||||
|
TouchClientActivity(_eventStreaming.TimeProvider.GetUtcNow());
|
||||||
|
|
||||||
|
await foreach (WorkerEvent workerEvent in workerClient
|
||||||
|
.ReadEventsAsync(cancellationToken)
|
||||||
|
.WithCancellation(cancellationToken)
|
||||||
.ConfigureAwait(false))
|
.ConfigureAwait(false))
|
||||||
{
|
{
|
||||||
yield return mapper.MapEvent(workerEvent);
|
yield return mapper.MapEvent(workerEvent);
|
||||||
@@ -1513,6 +1526,13 @@ public sealed class GatewaySession
|
|||||||
/// Reads events from the worker as an asynchronous enumerable stream.
|
/// Reads events from the worker as an asynchronous enumerable stream.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="cancellationToken">Token to cancel the asynchronous operation.</param>
|
/// <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>
|
/// <returns>An asynchronous stream of worker events.</returns>
|
||||||
public async IAsyncEnumerable<WorkerEvent> ReadEventsAsync(
|
public async IAsyncEnumerable<WorkerEvent> ReadEventsAsync(
|
||||||
[EnumeratorCancellation] CancellationToken cancellationToken)
|
[EnumeratorCancellation] CancellationToken cancellationToken)
|
||||||
|
|||||||
Reference in New Issue
Block a user