perf(metrics): cache MxEventFamily names for per-event metric tags
WorkerClient.StageWorkerEvent and StreamEvents called Enum.ToString per event (per subscriber on the stream path). MxEventFamilyNames serves the contract-fixed names from a frozen lookup, falling back to ToString for undefined values so exported tag values are unchanged.
This commit is contained in:
@@ -183,7 +183,7 @@ public sealed class MxAccessGatewayService(
|
||||
// call is cancelled, instead of leaving it to the transport's own timeout.
|
||||
await responseStream.WriteAsync(publicEvent, context.CancellationToken).ConfigureAwait(false);
|
||||
metrics.RecordEventStreamSend(
|
||||
publicEvent.Family.ToString(),
|
||||
MxEventFamilyNames.Get(publicEvent.Family),
|
||||
Stopwatch.GetElapsedTime(sendStartTimestamp));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
using System.Collections.Frozen;
|
||||
using ZB.MOM.WW.MxGateway.Contracts.Proto;
|
||||
|
||||
namespace ZB.MOM.WW.MxGateway.Server.Metrics;
|
||||
|
||||
/// <summary>
|
||||
/// Cached <see cref="MxEventFamily"/> names for the per-event metric tags.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <see cref="Enum.ToString()"/> allocates a new string on every call, and the event-received
|
||||
/// and stream-send metrics tag every event with its family — once per event at the worker
|
||||
/// boundary and once per event per subscriber on the stream. The names are fixed by the
|
||||
/// contract, so they are computed once here. A value the contract does not define falls back
|
||||
/// to <see cref="Enum.ToString()"/>, which is the same string the uncached call produced.
|
||||
/// </remarks>
|
||||
public static class MxEventFamilyNames
|
||||
{
|
||||
private static readonly FrozenDictionary<MxEventFamily, string> Names =
|
||||
Enum.GetValues<MxEventFamily>()
|
||||
.Distinct()
|
||||
.ToFrozenDictionary(family => family, family => family.ToString());
|
||||
|
||||
/// <summary>Gets the name of an event family.</summary>
|
||||
/// <param name="family">The event family.</param>
|
||||
/// <returns>The family's name, identical to <see cref="Enum.ToString()"/>.</returns>
|
||||
public static string Get(MxEventFamily family) =>
|
||||
Names.TryGetValue(family, out string? name) ? name : family.ToString();
|
||||
}
|
||||
@@ -692,7 +692,7 @@ public sealed class WorkerClient : IWorkerClient
|
||||
{
|
||||
if (workerEvent.Event is not null)
|
||||
{
|
||||
_metrics?.EventReceived(SessionId, workerEvent.Event.Family.ToString());
|
||||
_metrics?.EventReceived(SessionId, MxEventFamilyNames.Get(workerEvent.Event.Family));
|
||||
}
|
||||
|
||||
if (_eventStaging.Writer.TryWrite(workerEvent))
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
using ZB.MOM.WW.MxGateway.Contracts.Proto;
|
||||
using ZB.MOM.WW.MxGateway.Server.Metrics;
|
||||
|
||||
namespace ZB.MOM.WW.MxGateway.Tests.Metrics;
|
||||
|
||||
public sealed class MxEventFamilyNamesTests
|
||||
{
|
||||
/// <summary>
|
||||
/// Every defined family maps to exactly the name <see cref="Enum.ToString()"/> produces, so
|
||||
/// switching the per-event metric tags to the cached lookup changes no exported tag value.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Get_ForEveryDefinedFamily_MatchesEnumToString()
|
||||
{
|
||||
foreach (MxEventFamily family in Enum.GetValues<MxEventFamily>())
|
||||
{
|
||||
Assert.Equal(family.ToString(), MxEventFamilyNames.Get(family));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>A defined family returns the same cached string instance on every call (no per-event allocation).</summary>
|
||||
[Fact]
|
||||
public void Get_ForDefinedFamily_ReturnsCachedInstance()
|
||||
{
|
||||
Assert.Same(
|
||||
MxEventFamilyNames.Get(MxEventFamily.OnDataChange),
|
||||
MxEventFamilyNames.Get(MxEventFamily.OnDataChange));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A numeric value the contract does not define (a newer worker, or a malformed frame) still
|
||||
/// produces the <see cref="Enum.ToString()"/> form rather than throwing.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Get_ForUndefinedValue_FallsBackToToString()
|
||||
{
|
||||
MxEventFamily undefined = (MxEventFamily)987654;
|
||||
|
||||
Assert.Equal(undefined.ToString(), MxEventFamilyNames.Get(undefined));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user