feat(health): oldest-parked-message age on the site health report (parked-retention aging signal)
This commit is contained in:
@@ -107,6 +107,15 @@ public class HealthReportSender : BackgroundService
|
||||
{
|
||||
var parkedCount = await _sfStorage.GetParkedMessageCountAsync();
|
||||
_collector.SetParkedMessageCount(parkedCount);
|
||||
|
||||
// Parked-retention aging signal: age of the oldest parked row
|
||||
// (null when none are parked), so a forgotten site's stale
|
||||
// backlog is visible by age, not just count.
|
||||
var oldestParked = await _sfStorage.GetOldestParkedCreatedAtAsync();
|
||||
_collector.SetOldestParkedMessageAgeSeconds(
|
||||
oldestParked.HasValue
|
||||
? (DateTimeOffset.UtcNow - oldestParked.Value).TotalSeconds
|
||||
: null);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
||||
@@ -111,6 +111,19 @@ public interface ISiteHealthCollector
|
||||
/// <param name="count">The number of parked messages.</param>
|
||||
void SetParkedMessageCount(int count);
|
||||
|
||||
/// <summary>
|
||||
/// Sets the age (seconds) of the oldest parked store-and-forward message for the
|
||||
/// next <see cref="CollectReport"/> call; <c>null</c> when no rows are parked.
|
||||
/// Point-in-time (not reset on collect), refreshed by the parked-count poller in
|
||||
/// <c>HealthReportSender</c>. Default interface implementation is a no-op so
|
||||
/// existing test fakes continue to compile without per-fake updates.
|
||||
/// </summary>
|
||||
/// <param name="ageSeconds">Age in seconds of the oldest parked row, or <c>null</c> if none are parked.</param>
|
||||
void SetOldestParkedMessageAgeSeconds(double? ageSeconds)
|
||||
{
|
||||
// Default no-op so test fakes do not need to be updated.
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Replace the latest cumulative
|
||||
/// site-event-log write-failure count (SQLite error, disk full,
|
||||
|
||||
@@ -25,6 +25,10 @@ public class SiteHealthCollector : ISiteHealthCollector
|
||||
private IReadOnlyDictionary<string, int> _sfBufferDepths = new Dictionary<string, int>();
|
||||
private int _deployedInstanceCount, _enabledInstanceCount, _disabledInstanceCount;
|
||||
private int _parkedMessageCount;
|
||||
// Age (seconds) of the oldest parked message, stored as raw double bits so it can
|
||||
// be read/written atomically via Interlocked (double? is neither atomic nor
|
||||
// volatile-able). NaN is the "no parked rows" sentinel (ages are always >= 0).
|
||||
private long _oldestParkedAgeBits = BitConverter.DoubleToInt64Bits(double.NaN);
|
||||
private volatile string _nodeHostname = "";
|
||||
private volatile IReadOnlyList<Commons.Messages.Health.NodeStatus>? _clusterNodes;
|
||||
private volatile bool _isActiveNode;
|
||||
@@ -123,6 +127,20 @@ public class SiteHealthCollector : ISiteHealthCollector
|
||||
Interlocked.Exchange(ref _parkedMessageCount, count);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void SetOldestParkedMessageAgeSeconds(double? ageSeconds)
|
||||
{
|
||||
Interlocked.Exchange(ref _oldestParkedAgeBits,
|
||||
BitConverter.DoubleToInt64Bits(ageSeconds ?? double.NaN));
|
||||
}
|
||||
|
||||
/// <summary>Reads the atomically-stored oldest-parked age, mapping the NaN sentinel back to null.</summary>
|
||||
private double? ReadOldestParkedAgeSeconds()
|
||||
{
|
||||
var value = BitConverter.Int64BitsToDouble(Interlocked.Read(ref _oldestParkedAgeBits));
|
||||
return double.IsNaN(value) ? null : value;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void SetNodeHostname(string hostname) => _nodeHostname = hostname;
|
||||
|
||||
@@ -214,6 +232,7 @@ public class SiteHealthCollector : ISiteHealthCollector
|
||||
SiteAuditWriteFailures: siteAuditWriteFailures,
|
||||
AuditRedactionFailure: auditRedactionFailures,
|
||||
SiteAuditBacklog: _siteAuditBacklog,
|
||||
SiteEventLogWriteFailures: Interlocked.Read(ref _siteEventLogWriteFailures));
|
||||
SiteEventLogWriteFailures: Interlocked.Read(ref _siteEventLogWriteFailures),
|
||||
OldestParkedMessageAgeSeconds: ReadOldestParkedAgeSeconds());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user