Merge fix/c3-tracker-hygiene: GWC-31 perf-pass comments relabeled PERF-nn, archreview prefixes reserved in REVIEW-PROCESS.md, cycle-1/2 tracker housekeeping (GWC-16 Won't fix, IPC-08/IPC-31 Done, TST-06/17 refreshed, WRK-29..31 reserved)
This commit is contained in:
@@ -43,7 +43,7 @@ public sealed class GatewayMetrics : IDisposable
|
||||
private readonly ConcurrentDictionary<long, Func<int>> _eventStreamBacklogSources = new();
|
||||
private long _nextEventStreamBacklogSourceId;
|
||||
|
||||
// GWC-30: the same pull model for the worker event queue depth. It replaces a pushed scalar that
|
||||
// PERF-02: the same pull model for the worker event queue depth. It replaces a pushed scalar that
|
||||
// every WorkerClient wrote twice per event (staged, consumed) under _syncRoot — a process-wide
|
||||
// lock on the hottest path, and last-writer-wins across sessions, so the gauge reported one
|
||||
// arbitrary session's backlog instead of the gateway's. Each client registers a source returning
|
||||
@@ -210,7 +210,7 @@ public sealed class GatewayMetrics : IDisposable
|
||||
/// <param name="method">Name of the command method.</param>
|
||||
public void CommandStarted(string method)
|
||||
{
|
||||
// GWC-30: the three command counters run two-to-three times per gRPC call, so they use
|
||||
// PERF-02: the three command counters run two-to-three times per gRPC call, so they use
|
||||
// Interlocked rather than _syncRoot — the same idiom as EventReceived. Nothing here needs a
|
||||
// consistent multi-field view; GetSnapshot reads each with Interlocked.Read.
|
||||
Interlocked.Increment(ref _commandsStarted);
|
||||
@@ -281,7 +281,7 @@ public sealed class GatewayMetrics : IDisposable
|
||||
/// Registers a live depth source for the worker event queue-depth gauge and returns a handle
|
||||
/// that removes it when disposed. Each <c>WorkerClient</c> registers once and reports its own
|
||||
/// undelivered (staged + queued) event count, so the gauge is the gateway-wide sum instead of
|
||||
/// the last value any one session happened to push (GWC-30).
|
||||
/// the last value any one session happened to push (PERF-02).
|
||||
/// </summary>
|
||||
/// <param name="depth">
|
||||
/// Returns this worker client's current undelivered event count. Invoked only at collection
|
||||
@@ -460,7 +460,7 @@ public sealed class GatewayMetrics : IDisposable
|
||||
{
|
||||
// Compute the live queue depths outside _syncRoot: the sources are the subscriber channels'
|
||||
// Count (their own locks) and the worker clients' interlocked counters, neither of which may
|
||||
// run under this lock. GWC-15, GWC-30.
|
||||
// run under this lock. GWC-15, PERF-02.
|
||||
int workerEventQueueDepth = GetWorkerEventQueueDepth();
|
||||
int grpcEventStreamQueueDepth = GetGrpcEventStreamQueueDepth();
|
||||
lock (_syncRoot)
|
||||
@@ -521,7 +521,7 @@ public sealed class GatewayMetrics : IDisposable
|
||||
}
|
||||
}
|
||||
|
||||
// Sums the undelivered event backlog across every live worker client (GWC-30).
|
||||
// Sums the undelivered event backlog across every live worker client (PERF-02).
|
||||
private int GetWorkerEventQueueDepth() => SumSources(_workerEventQueueDepthSources);
|
||||
|
||||
// Sums the live backlog across every registered event-stream subscriber.
|
||||
|
||||
@@ -41,7 +41,7 @@ public sealed class WorkerClient : IWorkerClient
|
||||
private readonly SemaphoreSlim _pendingCommandSlots;
|
||||
private readonly CancellationTokenSource _stopCts = new();
|
||||
|
||||
// GWC-30: this client's contribution to the gateway-wide worker queue-depth gauge. Registered
|
||||
// PERF-02: this client's contribution to the gateway-wide worker queue-depth gauge. Registered
|
||||
// once here and read only when the gauge is scraped, so staging and consuming an event cost an
|
||||
// Interlocked on _eventQueueDepth and nothing else — previously each of those two hot-path steps
|
||||
// called into GatewayMetrics and took its process-wide lock. Null when metrics are disabled.
|
||||
@@ -233,7 +233,7 @@ public sealed class WorkerClient : IWorkerClient
|
||||
// size the caller controls; checking here keeps a MessageTooLarge in the write loop a
|
||||
// genuine desync signal.
|
||||
//
|
||||
// PERF(GWC-31): this size cannot be handed to WorkerFrameWriter to spare its own
|
||||
// PERF-07: this size cannot be handed to WorkerFrameWriter to spare its own
|
||||
// CalculateSize. WriteLoopAsync stamps envelope.Sequence immediately before the write
|
||||
// (GWC-28), and a non-zero varint field grows the encoding — so the number computed here
|
||||
// is a lower bound on the frame the writer actually emits, never the frame length. Passing
|
||||
@@ -257,7 +257,7 @@ public sealed class WorkerClient : IWorkerClient
|
||||
|
||||
await EnqueueAsync(commandEnvelope, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
// GWC-31: one pooled timer instead of a linked CTS + Task.Delay + WhenAny per command.
|
||||
// PERF-07: one pooled timer instead of a linked CTS + Task.Delay + WhenAny per command.
|
||||
// Task.WaitAsync arms a TimerQueueTimer on the shared timer queue and cancels it when the
|
||||
// reply lands, so the steady-state cost of a command that replies in time is a single
|
||||
// continuation — the old shape allocated a linked CancellationTokenSource, its
|
||||
@@ -354,7 +354,7 @@ public sealed class WorkerClient : IWorkerClient
|
||||
{
|
||||
await foreach (WorkerEvent workerEvent in _events.Reader.ReadAllAsync(cancellationToken).ConfigureAwait(false))
|
||||
{
|
||||
// No metrics call on the hot path: the gauge pulls _eventQueueDepth when scraped (GWC-30).
|
||||
// No metrics call on the hot path: the gauge pulls _eventQueueDepth when scraped (PERF-02).
|
||||
Interlocked.Decrement(ref _eventQueueDepth);
|
||||
yield return workerEvent;
|
||||
}
|
||||
|
||||
@@ -164,7 +164,7 @@ public sealed class WorkerClientTests
|
||||
TestTimeout,
|
||||
CancellationToken.None);
|
||||
|
||||
// The timeout also emits a WorkerCancel for the abandoned correlation (GWC-31), which sits
|
||||
// The timeout also emits a WorkerCancel for the abandoned correlation (PERF-07), which sits
|
||||
// ahead of the second command on the FIFO pipe; skip it rather than mistaking it for the
|
||||
// command this assertion is about.
|
||||
WorkerEnvelope secondCommand = await ReadNextCommandAsync(pipePair, timedOutCommand.CorrelationId);
|
||||
@@ -180,7 +180,7 @@ public sealed class WorkerClientTests
|
||||
/// <summary>
|
||||
/// A command timeout abandons the gateway-side wait, but the worker keeps the correlation on
|
||||
/// its single STA queue and would still run it — so the gateway forwards a <c>WorkerCancel</c>
|
||||
/// for the abandoned correlation id (GWC-31). Without it, a client that retries after a
|
||||
/// for the abandoned correlation id (PERF-07). Without it, a client that retries after a
|
||||
/// timeout stacks work the worker still intends to execute. Asserted on the wire because the
|
||||
/// cancel is protocol behavior the worker depends on, not an internal detail.
|
||||
/// </summary>
|
||||
|
||||
@@ -19,7 +19,7 @@ public sealed class GatewayMetricsTests
|
||||
metrics.CommandFailed("WriteSecured", "AuthorizationFailed", TimeSpan.FromMilliseconds(12));
|
||||
metrics.EventReceived("session-1", "OnDataChange");
|
||||
metrics.EventReceived("session-1", "OnDataChange");
|
||||
// GWC-30: the worker queue-depth gauge sums one live source per worker client, so the two
|
||||
// PERF-02: the worker queue-depth gauge sums one live source per worker client, so the two
|
||||
// registrations below stand in for two concurrent sessions holding 3 and 4 events.
|
||||
using IDisposable workerDepthSourceA = metrics.RegisterWorkerEventQueueDepthSource(static () => 3);
|
||||
using IDisposable workerDepthSourceB = metrics.RegisterWorkerEventQueueDepthSource(static () => 4);
|
||||
@@ -58,7 +58,7 @@ public sealed class GatewayMetricsTests
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// GWC-30: the worker queue-depth gauge sums every registered source rather than holding a
|
||||
/// PERF-02: the worker queue-depth gauge sums every registered source rather than holding a
|
||||
/// single pushed scalar, so concurrent sessions add up instead of overwriting one another,
|
||||
/// and a disposed registration (a worker client going away) drops out of the sum. Disposal
|
||||
/// is idempotent because a client's dispose path can run twice.
|
||||
|
||||
@@ -20,7 +20,7 @@ public sealed class WorkerFrameReader
|
||||
private readonly WorkerFrameProtocolOptions _options;
|
||||
private readonly Stream _stream;
|
||||
|
||||
// Reused across frames rather than allocated per read (GWC-30). Safe because ReadAsync is
|
||||
// Reused across frames rather than allocated per read (PERF-22). Safe because ReadAsync is
|
||||
// single-consumer by construction; the prefix is fully overwritten by every read.
|
||||
//
|
||||
// The single-consumer invariant has to survive teardown as well as steady state, because a read
|
||||
|
||||
@@ -668,10 +668,10 @@ public sealed class WorkerFrameWriter
|
||||
// Serialize once into a single pooled buffer that carries the 4-byte length prefix followed
|
||||
// by the payload, then issue one stream write. This avoids a second serialization pass, a
|
||||
// separate prefix array, a separate prefix write, and any per-frame heap allocation — the
|
||||
// gateway-side writer's GWC-30 shape, now matched on the net48 side. The rented buffer may
|
||||
// be larger than requested, so only the first frameLength bytes are ever written. The buffer
|
||||
// is returned only after this frame's write has completed; the batch flush deferred to the
|
||||
// end of the drain (see DrainQueuedFramesAsync) does not read from it.
|
||||
// gateway-side writer's pooled-buffer shape (PERF-22), now matched on the net48 side. The
|
||||
// rented buffer may be larger than requested, so only the first frameLength bytes are ever
|
||||
// written. The buffer is returned only after this frame's write has completed; the batch
|
||||
// flush deferred to the end of the drain (see DrainQueuedFramesAsync) does not read from it.
|
||||
int frameLength = sizeof(uint) + payloadLength;
|
||||
byte[] frame = ArrayPool<byte>.Shared.Rent(frameLength);
|
||||
try
|
||||
|
||||
Reference in New Issue
Block a user