review(Core.AlarmHistorian): reset drain state on cancel + volatile _disposed

Re-review at 7286d320. -012 (Medium): OperationCanceledException left _drainState stuck
at Draining on the status surface; now resets to BackingOff + test. -013: _disposed ->
volatile (mirrors _backoffIndex). -014 (post-dispose status guards) deferred cross-module.
This commit is contained in:
Joseph Doherty
2026-06-19 11:21:35 -04:00
parent 48af117bff
commit 6b4210cb17
3 changed files with 125 additions and 4 deletions
@@ -76,7 +76,9 @@ public sealed class SqliteStoreAndForwardSink : IAlarmHistorianSink, IDisposable
private Timer? _drainTimer;
private TimeSpan _tickInterval;
private volatile int _backoffIndex;
private bool _disposed;
// volatile: read by EnqueueAsync, DrainOnceAsync, RescheduleDrain, and StartDrainLoop
// from different threads; Dispose() writes it from the owner's thread (Core.AlarmHistorian-013).
private volatile bool _disposed;
// Core.AlarmHistorian-005: status fields written by the drain timer thread and
// read concurrently by GetStatus() / health-check threads. Guard all reads and
@@ -411,6 +413,10 @@ public sealed class SqliteStoreAndForwardSink : IAlarmHistorianSink, IDisposable
}
catch (OperationCanceledException)
{
// Core.AlarmHistorian-012: reset _drainState so the status surface does
// not stay stuck at Draining after the cancellation unwinds the method.
// The row stays queued; the next drain tick will retry it.
lock (_statusLock) { _drainState = HistorianDrainState.BackingOff; }
throw;
}
catch (Exception ex)