From c20d22838457dffc4d05acf8daab9ebfd4cff18c Mon Sep 17 00:00:00 2001 From: Joseph Doherty Date: Thu, 11 Jun 2026 12:49:44 -0400 Subject: [PATCH] fix(historian): volatile _backoffIndex + read _evictedCount under lock (thread-safety) --- .../SqliteStoreAndForwardSink.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/Core/ZB.MOM.WW.OtOpcUa.Core.AlarmHistorian/SqliteStoreAndForwardSink.cs b/src/Core/ZB.MOM.WW.OtOpcUa.Core.AlarmHistorian/SqliteStoreAndForwardSink.cs index f887990f..570ded36 100644 --- a/src/Core/ZB.MOM.WW.OtOpcUa.Core.AlarmHistorian/SqliteStoreAndForwardSink.cs +++ b/src/Core/ZB.MOM.WW.OtOpcUa.Core.AlarmHistorian/SqliteStoreAndForwardSink.cs @@ -71,7 +71,7 @@ public sealed class SqliteStoreAndForwardSink : IAlarmHistorianSink, IDisposable private readonly SemaphoreSlim _drainGate = new(1, 1); private Timer? _drainTimer; private TimeSpan _tickInterval; - private int _backoffIndex; + private volatile int _backoffIndex; private bool _disposed; // Core.AlarmHistorian-005: status fields written by the drain timer thread and @@ -655,10 +655,11 @@ public sealed class SqliteStoreAndForwardSink : IAlarmHistorianSink, IDisposable await cmd.ExecuteNonQueryAsync(ct).ConfigureAwait(false); } Interlocked.Add(ref _queuedRowCount, -toEvict); - lock (_statusLock) { _evictedCount += toEvict; } + long lifetimeEvicted; + lock (_statusLock) { _evictedCount += toEvict; lifetimeEvicted = _evictedCount; } _logger.Warning( "Historian queue at capacity {Cap} — evicted {Count} oldest row(s) to make room (lifetime evictions: {Total})", - _capacity, toEvict, _evictedCount); + _capacity, toEvict, lifetimeEvicted); } private void PurgeAgedDeadLetters(SqliteConnection conn)