namespace AVEVA.Historian.Client.Redundancy; /// /// Mutable per-member health used by to route reads and /// fan out writes. Thread-safe: ops update it from multiple call sites and the watchdog loop. /// internal sealed class MemberState { private readonly Lock _lock = new(); private readonly int _failureThreshold; private bool _isHealthy = true; private int _consecutiveFailures; private string? _lastError; private DateTime? _lastSuccessUtc; private DateTime? _lastCheckUtc; public MemberState(IHistorianMember member, int failureThreshold) { Member = member; _failureThreshold = failureThreshold; } public IHistorianMember Member { get; } public bool IsHealthy { get { lock (_lock) { return _isHealthy; } } } public void MarkSuccess(DateTime utc) { lock (_lock) { _consecutiveFailures = 0; _isHealthy = true; _lastError = null; _lastSuccessUtc = utc; _lastCheckUtc = utc; } } public void MarkFailure(string? error, DateTime utc) { lock (_lock) { _consecutiveFailures++; _lastError = error; _lastCheckUtc = utc; if (_consecutiveFailures >= _failureThreshold) { _isHealthy = false; } } } public HistorianMemberStatus Snapshot() { lock (_lock) { return new HistorianMemberStatus { Name = Member.Name, IsHealthy = _isHealthy, ConsecutiveFailures = _consecutiveFailures, LastError = _lastError, LastSuccessUtc = _lastSuccessUtc, LastCheckUtc = _lastCheckUtc, }; } } }