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);
}