namespace Mbproxy; /// /// Service-wide counters for the mbproxy host. Tracks reload accept/reject counts and /// timestamps so the status page can surface them without coupling to the reconciler. /// /// Constructed once at DI startup and shared as a singleton. All writes are via /// dedicated methods that use so reads from the status page /// are always coherent without locking. /// public sealed class ServiceCounters { // LastReloadUtc: stored as ticks-since-epoch via Interlocked.Exchange. // 0 = "never reloaded". DateTimeOffset.MinValue.UtcTicks works as the sentinel // but 0 is simpler. DateTimeOffset.UtcNow.UtcTicks is always > 0 after 1970. private long _lastReloadUtcTicks; // 0 = never; Interlocked private int _reloadAppliedCount; // Interlocked private int _reloadRejectedCount; // Interlocked /// Instant at which this service instance was constructed (service start proxy). public DateTimeOffset StartedAtUtc { get; } = DateTimeOffset.UtcNow; /// /// UTC timestamp of the last successfully applied hot-reload, or null if no /// reload has been accepted since the service started. /// public DateTimeOffset? LastReloadUtc { get { long ticks = Interlocked.Read(ref _lastReloadUtcTicks); return ticks == 0 ? null : new DateTimeOffset(ticks, TimeSpan.Zero); } } /// Total number of configuration reloads accepted since service start. public int ReloadAppliedCount => Interlocked.CompareExchange(ref _reloadAppliedCount, 0, 0); /// Total number of configuration reloads rejected since service start. public int ReloadRejectedCount => Interlocked.CompareExchange(ref _reloadRejectedCount, 0, 0); /// /// Records one accepted reload. Bumps and updates /// . /// public void RecordReloadApplied(DateTimeOffset timestamp) { Interlocked.Increment(ref _reloadAppliedCount); Interlocked.Exchange(ref _lastReloadUtcTicks, timestamp.UtcTicks); } /// Bumps . public void RecordReloadRejected() => Interlocked.Increment(ref _reloadRejectedCount); }