perf(worker): exact-format timestamp parse and compiled status-field accessors on the event path

This commit is contained in:
Joseph Doherty
2026-08-15 16:59:52 -04:00
parent f4a6cb1db2
commit 94fdc18c3c
4 changed files with 386 additions and 36 deletions
@@ -135,6 +135,90 @@ public sealed class MxStatusProxyConverterTests
Assert.Equal("Invalid reference", second.DiagnosticText);
}
/// <summary>
/// Verifies every accessor strategy converts the same status values
/// identically. PERF-20 reads a public field of a publicly visible type
/// through a delegate compiled from an expression tree instead of
/// <c>FieldInfo.GetValue</c>; a field type whose conversion to int is not
/// a lossless widening (<see cref="FakeWideStatusProxy"/>) and a type the
/// compiled delegate may not touch (<see cref="HiddenStatusProxy"/>, not
/// visible outside this assembly) keep the reflection read. All four
/// doubles below carry the same logical status, so all four messages must
/// be equal.
/// </summary>
[Fact]
public void Convert_AcrossAccessorStrategies_ProducesIdenticalMessages()
{
// Struct, widening field types: the compiled accessor unboxes in place.
MxStatusProxy compiledStruct = _converter.Convert(new FakeMxStatusProxy
{
success = 1,
category = 5,
detectedBy = 3,
detail = 21,
});
// Reference type, widening field types: the compiled accessor casts.
MxStatusProxy compiledClass = _converter.Convert(new FakeStatusProxyClass
{
success = 1,
category = 5,
detectedBy = 3,
detail = 21,
});
// Non-widening field types: reflection + Convert.ToInt32 is kept.
MxStatusProxy reflectedWide = _converter.Convert(new FakeWideStatusProxy
{
success = 1L,
category = 5L,
detectedBy = 3L,
detail = 21L,
});
// Type not visible outside the assembly: reflection is kept.
MxStatusProxy reflectedHidden = _converter.Convert(new HiddenStatusProxy
{
success = 1,
category = 5,
detectedBy = 3,
detail = 21,
});
Assert.Equal(compiledStruct, compiledClass);
Assert.Equal(compiledStruct, reflectedWide);
Assert.Equal(compiledStruct, reflectedHidden);
Assert.Equal(MxStatusCategory.OperationalError, compiledStruct.Category);
Assert.Equal(MxStatusSource.RespondingNmx, compiledStruct.DetectedBy);
Assert.Equal("Invalid reference", compiledStruct.DiagnosticText);
}
/// <summary>
/// Verifies the compiled accessor widens a negative 16-bit field the way
/// <c>Convert.ToInt32</c> did — sign-extended, not reinterpreted. The
/// interop MXSTATUS_PROXY declares 16-bit fields, so a sign-extension
/// mistake here would silently change every failing status the gateway
/// reports.
/// </summary>
[Fact]
public void Convert_WithExtremeInt16FieldValues_SignExtendsLikeConvertToInt32()
{
FakeMxStatusProxy status = new()
{
success = short.MinValue,
category = 2,
detectedBy = 0,
detail = short.MaxValue,
};
MxStatusProxy converted = _converter.Convert(status);
Assert.Equal((int)short.MinValue, converted.Success);
Assert.Equal((int)short.MaxValue, converted.Detail);
Assert.Equal(MxStatusCategory.Warning, converted.Category);
Assert.Equal(MxStatusSource.RequestingLmx, converted.DetectedBy);
}
public struct FakeMxStatusProxy
{
public short success;
@@ -146,6 +230,39 @@ public sealed class MxStatusProxyConverterTests
public short detail;
}
public struct FakeWideStatusProxy
{
public long success;
public long category;
public long detectedBy;
public long detail;
}
public sealed class FakeStatusProxyClass
{
public int success;
public int category;
public int detectedBy;
public int detail;
}
private struct HiddenStatusProxy
{
public short success;
public int category;
public int detectedBy;
public short detail;
}
private sealed class MissingFields
{
}
@@ -238,6 +238,97 @@ public sealed class MxAccessEventMapperTests
Assert.False(MxAccessEventMapper.TryParseSourceTimestamp(text, out _));
}
/// <summary>
/// Verifies the exact-format fast path PERF-20 added in front of the
/// general parse chain returns exactly what that chain returned. The
/// cases cover the captured MXAccess shape (which the fast path is meant
/// to catch on a US-shaped host), its zero-padded and second-precision
/// siblings, and strings the derived formats cannot match — an ISO
/// round-trip form, a long-date form, and a day-first European form —
/// which must fall through to the unchanged two-stage chain. The
/// expectation is computed with the pre-PERF-20 chain in this test, so it
/// holds on any host culture: the fast path is a shortcut, never a
/// different answer.
/// </summary>
/// <param name="text">Timestamp string to parse.</param>
[Theory]
[InlineData("3/26/2026 1:38:22.907 PM")]
[InlineData("3/26/2026 1:38:22 PM")]
[InlineData("03/26/2026 01:38:22 PM")]
[InlineData("12/31/2026 11:59:59.999 PM")]
[InlineData("2026-03-26T13:38:22.9070000")]
[InlineData("Thursday, March 26, 2026 1:38:22 PM")]
[InlineData("26.03.2026 13:38:22")]
[InlineData("not a timestamp")]
public void TryParseSourceTimestamp_MatchesPreFastPathChain(string text)
{
bool expectedParsed = TryParseWithGeneralChainOnly(text, out DateTime expectedUtc);
bool parsed = MxAccessEventMapper.TryParseSourceTimestamp(text, out DateTime utc);
Assert.Equal(expectedParsed, parsed);
Assert.Equal(expectedUtc, utc);
if (parsed)
{
Assert.Equal(DateTimeKind.Utc, utc.Kind);
}
}
/// <summary>
/// Verifies a timestamp written in the host culture's own patterns —
/// including the millisecond fraction MXAccess appends, which no culture
/// publishes in its long-time pattern — parses as local wall-clock time
/// and comes back as UTC. This is the string shape the derived exact
/// formats are built for, so it exercises the fast path on the worker's
/// own host regardless of which culture that host runs.
/// </summary>
[Fact]
public void TryParseSourceTimestamp_WithCultureShapedMillisecondTimestamp_ReturnsLocalWallClockAsUtc()
{
CultureInfo culture = CultureInfo.CurrentCulture;
string longTime = culture.DateTimeFormat.LongTimePattern;
int secondsIndex = longTime.IndexOf("ss", StringComparison.Ordinal);
// Every Windows culture publishes seconds in its long-time pattern; the
// guard keeps the test honest rather than green-by-accident if one does not.
Assert.True(secondsIndex >= 0, $"Long-time pattern '{longTime}' has no seconds specifier.");
DateTime localWall = new(2026, 3, 26, 13, 38, 22, 907, DateTimeKind.Unspecified);
string text = localWall.ToString(
culture.DateTimeFormat.ShortDatePattern + " " + longTime.Insert(secondsIndex + 2, ".fff"),
culture);
Assert.True(MxAccessEventMapper.TryParseSourceTimestamp(text, out DateTime utc));
Assert.Equal(DateTimeKind.Utc, utc.Kind);
Assert.Equal(DateTime.SpecifyKind(localWall, DateTimeKind.Local).ToUniversalTime(), utc);
}
/// <summary>
/// The parse chain exactly as it stood before PERF-20 added the
/// exact-format stage, used as the parity oracle above.
/// </summary>
/// <param name="text">Timestamp string to parse.</param>
/// <param name="utc">The parsed UTC timestamp on success.</param>
/// <returns><see langword="true"/> when the string parsed successfully.</returns>
private static bool TryParseWithGeneralChainOnly(string? text, out DateTime utc)
{
utc = default;
if (string.IsNullOrWhiteSpace(text))
{
return false;
}
const DateTimeStyles styles = DateTimeStyles.AssumeLocal | DateTimeStyles.AdjustToUniversal;
if (DateTime.TryParse(text, CultureInfo.CurrentCulture, styles, out DateTime parsed)
|| DateTime.TryParse(text, CultureInfo.InvariantCulture, styles, out parsed))
{
utc = DateTime.SpecifyKind(parsed, DateTimeKind.Utc);
return true;
}
return false;
}
private sealed class FakeStatus
{
public int success;