533414a16d
#113: AuditLogPartitionMaintenance (monthly SPLIT RANGE) and the backfill-source-node endpoint now use the AuditLog:MaintenanceConnectionString login when set (unset = unchanged). Migration AuditMaintenanceGrants gives scadabridge_audit_purger ALTER ANY DATASPACE and column-level UPDATE (SourceNode). #114: archive cleanup deletes the writer's own *.tmp files and manifest-less data files older than 24 h inside {Directory}/{YYYY} (DeletedOrphan, dry-run aware). #115: head-export failures bump scadabridge.audit.chain_head_export.failures and Degrade the /health/ready entry audit-chain-head-export (failing or stale > 2 intervals). Claude-Session: https://claude.ai/code/session_01GT67AEHd2Acv1YPwGfXEoV
92 lines
3.5 KiB
C#
92 lines
3.5 KiB
C#
using System.Diagnostics.Metrics;
|
|
using ZB.MOM.WW.ScadaBridge.Commons.Observability;
|
|
|
|
namespace ZB.MOM.WW.ScadaBridge.AuditLog.Central.HeadExport;
|
|
|
|
/// <summary>
|
|
/// Health signal of the off-box chain-head export (ScadaBridge#115). Updated by
|
|
/// <see cref="AuditChainHeadExportService"/> on every tick; read by the Host's
|
|
/// <c>audit-chain-head-export</c> /health/ready check. A sink that stays down used to be a
|
|
/// log line only, silently stopping the external anchor.
|
|
/// </summary>
|
|
public sealed class AuditChainHeadExportStatus
|
|
{
|
|
private static readonly Meter Meter = new(ScadaBridgeTelemetry.MeterName);
|
|
|
|
private static readonly Counter<long> FailuresCounter =
|
|
Meter.CreateCounter<long>("scadabridge.audit.chain_head_export.failures", unit: "1",
|
|
description: "AuditLog chain-head export ticks that failed to read or write the heads (sink offline / unreachable); retried next tick.");
|
|
|
|
private readonly object _gate = new();
|
|
private long _totalFailures;
|
|
private int _consecutiveFailures;
|
|
private DateTime? _lastSuccessUtc;
|
|
private DateTime? _lastFailureUtc;
|
|
private string? _lastError;
|
|
private DateTime? _startedUtc;
|
|
|
|
/// <summary>Whether the export is running at all (false = disabled, nothing to report).</summary>
|
|
public bool Enabled { get; private set; }
|
|
|
|
/// <summary>Minutes between export ticks (for the staleness test).</summary>
|
|
public int IntervalMinutes { get; private set; } = 60;
|
|
|
|
/// <summary>Total failed ticks since process start.</summary>
|
|
public long TotalFailures { get { lock (_gate) { return _totalFailures; } } }
|
|
|
|
/// <summary>Failed ticks since the last success.</summary>
|
|
public int ConsecutiveFailures { get { lock (_gate) { return _consecutiveFailures; } } }
|
|
|
|
/// <summary>UTC time of the last successful tick, or null.</summary>
|
|
public DateTime? LastSuccessUtc { get { lock (_gate) { return _lastSuccessUtc; } } }
|
|
|
|
/// <summary>UTC time of the last failed tick, or null.</summary>
|
|
public DateTime? LastFailureUtc { get { lock (_gate) { return _lastFailureUtc; } } }
|
|
|
|
/// <summary>Message of the last failure (never a secret: the exception message only), or null.</summary>
|
|
public string? LastError { get { lock (_gate) { return _lastError; } } }
|
|
|
|
/// <summary>UTC time the export loop started, or null.</summary>
|
|
public DateTime? StartedUtc { get { lock (_gate) { return _startedUtc; } } }
|
|
|
|
/// <summary>Marks the export as running.</summary>
|
|
/// <param name="intervalMinutes">Tick interval.</param>
|
|
/// <param name="nowUtc">Start time.</param>
|
|
public void MarkStarted(int intervalMinutes, DateTime nowUtc)
|
|
{
|
|
lock (_gate)
|
|
{
|
|
Enabled = true;
|
|
IntervalMinutes = intervalMinutes;
|
|
_startedUtc = nowUtc;
|
|
}
|
|
}
|
|
|
|
/// <summary>Records a successful tick.</summary>
|
|
/// <param name="nowUtc">Time of the tick.</param>
|
|
public void RecordSuccess(DateTime nowUtc)
|
|
{
|
|
lock (_gate)
|
|
{
|
|
_consecutiveFailures = 0;
|
|
_lastSuccessUtc = nowUtc;
|
|
}
|
|
}
|
|
|
|
/// <summary>Records a failed tick and bumps the failure counter metric.</summary>
|
|
/// <param name="nowUtc">Time of the tick.</param>
|
|
/// <param name="error">The failure message.</param>
|
|
public void RecordFailure(DateTime nowUtc, string error)
|
|
{
|
|
lock (_gate)
|
|
{
|
|
_totalFailures++;
|
|
_consecutiveFailures++;
|
|
_lastFailureUtc = nowUtc;
|
|
_lastError = error;
|
|
}
|
|
|
|
FailuresCounter.Add(1);
|
|
}
|
|
}
|