using Microsoft.Extensions.Logging; using System.Collections.Concurrent; namespace StellaOps.Microservice; /// /// Tracks in-flight requests and manages their cancellation tokens. /// public sealed class InflightRequestTracker : IDisposable { private readonly ConcurrentDictionary _inflight = new(); private readonly ILogger _logger; private bool _disposed; /// /// Initializes a new instance of the class. /// public InflightRequestTracker(ILogger logger) { _logger = logger; } /// /// Gets the count of in-flight requests. /// public int Count => _inflight.Count; /// /// Starts tracking a request and returns a cancellation token for it. /// /// The correlation ID of the request. /// A cancellation token that will be triggered if the request is cancelled. public CancellationToken Track(Guid correlationId) { ObjectDisposedException.ThrowIf(_disposed, this); var cts = new CancellationTokenSource(); var request = new InflightRequest(cts); if (!_inflight.TryAdd(correlationId, request)) { cts.Dispose(); throw new InvalidOperationException($"Request {correlationId} is already being tracked"); } _logger.LogDebug("Started tracking request {CorrelationId}", correlationId); return cts.Token; } /// /// Cancels a specific request. /// /// The correlation ID of the request to cancel. /// The reason for cancellation. /// True if the request was found and cancelled; otherwise false. public bool Cancel(Guid correlationId, string? reason) { if (_inflight.TryGetValue(correlationId, out var request)) { try { request.Cts.Cancel(); _logger.LogInformation( "Cancelled request {CorrelationId}: {Reason}", correlationId, reason ?? "Unknown"); return true; } catch (ObjectDisposedException) { // CTS was already disposed, request completed return false; } } _logger.LogDebug( "Cannot cancel request {CorrelationId}: not found (may have already completed)", correlationId); return false; } /// /// Marks a request as completed and removes it from tracking. /// /// The correlation ID of the completed request. public void Complete(Guid correlationId) { if (_inflight.TryRemove(correlationId, out var request)) { request.Cts.Dispose(); _logger.LogDebug("Completed request {CorrelationId}", correlationId); } } /// /// Cancels all in-flight requests. /// /// The reason for cancellation. public void CancelAll(string reason) { var count = 0; foreach (var kvp in _inflight) { try { kvp.Value.Cts.Cancel(); count++; } catch (ObjectDisposedException) { // Already disposed } } _logger.LogInformation("Cancelled {Count} in-flight requests: {Reason}", count, reason); // Clear and dispose all foreach (var kvp in _inflight) { if (_inflight.TryRemove(kvp.Key, out var request)) { request.Cts.Dispose(); } } } /// public void Dispose() { if (_disposed) return; _disposed = true; CancelAll("Disposing tracker"); } private sealed class InflightRequest { public CancellationTokenSource Cts { get; } public InflightRequest(CancellationTokenSource cts) { Cts = cts; } } }