perf(gateway): trim event/command hot-path allocations (GWC-06/07/15, IPC-05)

Behavior-preserving allocation cuts on the per-event/per-command path:
- GWC-06: StreamEvents send-timing uses Stopwatch.GetTimestamp() +
  GetElapsedTime() instead of a per-event Stopwatch allocation (same measured
  span).
- GWC-07/IPC-05 (event): MapEvent transfers ownership of the inner MxEvent
  instead of .Clone()-ing it. Safe: WorkerEvent is parsed fresh per pipe frame
  with the distributor pump as its single consumer (GWC-01), MapEvent runs once
  before fan-out, and every downstream consumer (subscribers, replay ring)
  only READS the event (WorkerSequence is stamped upstream; verified no
  post-mapping mutation). Comment documents the invariant + restore-clone
  caveat if a second consumer is added.
- IPC-05 (command): CreateCommandEnvelope no longer re-clones; MapCommand
  already isolated the graph from the caller-owned gRPC message.
- GWC-15: grpc_stream_queue.depth converts from a per-event push counter to an
  ObservableGauge summing registered channel sources at scrape time only
  (name/semantics unchanged); removes all per-event .Count/lock work.

Kept every load-bearing isolation clone (MapCommand, Invoke, bulk filters,
MapCommandReply). Server build clean (0 warnings); EventStream/Metrics/
Distributor/Mapper tests 62/62 (incl. formerly-flaky queue-depth tests, now
green under the lazy gauge, + 2 new MapEvent ownership tests). Docs: Metrics.md,
Grpc.md.

Claude-Session: https://claude.ai/code/session_01DMXXvNuPekkkrTEyPNxEkW
This commit is contained in:
Joseph Doherty
2026-07-09 15:12:43 -04:00
parent baae59ce3b
commit 5e2e40a927
9 changed files with 155 additions and 46 deletions
@@ -997,9 +997,14 @@ public sealed class WorkerClient : IWorkerClient
string correlationId,
WorkerCommand command)
{
// IPC-05: no second clone. MxAccessGrpcMapper.MapCommand already deep-cloned the command
// out of the caller-owned gRPC MxCommandRequest, so this WorkerCommand is a fresh graph
// owned by the invoke pipeline and referenced by no other consumer. The envelope is built
// and owned entirely inside WorkerClient and the command is not touched again after this
// point, so transferring it into the envelope introduces no aliasing hazard.
return CreateEnvelope(
correlationId,
envelope => envelope.WorkerCommand = command.Clone());
envelope => envelope.WorkerCommand = command);
}
/// <summary>Creates shutdown envelope.</summary>