fix(inbound-api): defer DI-scope disposal to handler completion and count abandoned executions — closes the timeout use-after-dispose

Claude-Session: https://claude.ai/code/session_01MtdgwpEeCUn6cUA5f1LMPj
This commit is contained in:
Joseph Doherty
2026-07-10 04:18:00 -04:00
parent 39e1ca3eae
commit 0b916ff0c5
3 changed files with 75 additions and 4 deletions
@@ -37,6 +37,11 @@ public static class ScadaBridgeTelemetry
Meter.CreateCounter<long>("scadabridge.inbound_api.requests", unit: "1",
description: "Inbound API requests, tagged by method.");
/// <summary>Incremented each time an inbound execution is abandoned (handler outlived its request), tagged with the API method.</summary>
private static readonly Counter<long> _inboundAbandonedExecutions =
Meter.CreateCounter<long>("scadabridge.inbound_api.abandoned_executions", unit: "1",
description: "Inbound API executions abandoned after timeout/abort (handler still running), tagged by method.");
/// <summary>Incremented each time an S&amp;F buffer replication op fails to dispatch/deliver to the peer.</summary>
private static readonly Counter<long> _replicationFailures =
Meter.CreateCounter<long>("scadabridge.store_and_forward.replication.failures", unit: "1",
@@ -75,6 +80,11 @@ public static class ScadaBridgeTelemetry
public static void RecordInboundApiRequest(string method) =>
_inboundApiRequests.Add(1, new KeyValuePair<string, object?>("method", method));
/// <summary>Records that an inbound execution was abandoned (handler outlived its request) for the given <paramref name="method"/>.</summary>
/// <param name="method">The API method whose execution was abandoned.</param>
public static void RecordInboundAbandonedExecution(string method) =>
_inboundAbandonedExecutions.Add(1, new KeyValuePair<string, object?>("method", method));
/// <summary>Records one failed S&amp;F replication dispatch.</summary>
public static void RecordReplicationFailure() => _replicationFailures.Add(1);