refactor(historian): one QuestDbTimeClusters helper for writer + updater existence reads; redelivery tombstone test
Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
This commit is contained in:
+1
-24
@@ -31,9 +31,6 @@ internal sealed class QuestDbHistorianDataUpdater : IHistorianDataUpdater
|
||||
/// <summary>Rows per INSERT and per DeleteRaw page (T0 Q2: 9,000 parameters, far under the 32,767 limit).</summary>
|
||||
internal const int BatchSize = 1000;
|
||||
|
||||
/// <summary>Request times further apart than this are read with separate range queries (I-2).</summary>
|
||||
internal static readonly TimeSpan ExistenceClusterGap = TimeSpan.FromMinutes(1);
|
||||
|
||||
private const long ResolutionTicks = 10;
|
||||
private const int LockStripes = 64;
|
||||
private readonly SemaphoreSlim[] _locks = Enumerable.Range(0, LockStripes).Select(_ => new SemaphoreSlim(1, 1)).ToArray();
|
||||
@@ -60,26 +57,6 @@ internal sealed class QuestDbHistorianDataUpdater : IHistorianDataUpdater
|
||||
/// <summary>Rows returned by existence reads so far (test/diagnostic counter).</summary>
|
||||
internal long LiveRowsRead => Interlocked.Read(ref _liveRowsRead);
|
||||
|
||||
/// <summary>Splits times into clusters wherever consecutive sorted times are further apart than <paramref name="gap"/>.</summary>
|
||||
/// <param name="times">Request times (any order).</param>
|
||||
/// <param name="gap">Largest gap kept inside one cluster.</param>
|
||||
/// <returns>Inclusive [Lo, Hi] ranges, ascending.</returns>
|
||||
internal static List<(DateTime Lo, DateTime Hi)> Cluster(IReadOnlyList<DateTime> times, TimeSpan gap)
|
||||
{
|
||||
var result = new List<(DateTime, DateTime)>();
|
||||
if (times.Count == 0) return result;
|
||||
var sorted = times.OrderBy(t => t).ToList();
|
||||
var lo = sorted[0];
|
||||
var prev = lo;
|
||||
foreach (var t in sorted.Skip(1))
|
||||
{
|
||||
if (t - prev > gap) { result.Add((lo, prev)); lo = t; }
|
||||
prev = t;
|
||||
}
|
||||
result.Add((lo, prev));
|
||||
return result;
|
||||
}
|
||||
|
||||
private async Task<IDisposable> LockAsync(string tag, CancellationToken ct)
|
||||
{
|
||||
var sem = _locks[(int)((uint)StringComparer.Ordinal.GetHashCode(tag) % LockStripes)];
|
||||
@@ -231,7 +208,7 @@ internal sealed class QuestDbHistorianDataUpdater : IHistorianDataUpdater
|
||||
{
|
||||
var live = new HashSet<long>();
|
||||
await using var conn = await _conn.OpenAsync(ct).ConfigureAwait(false);
|
||||
foreach (var (lo, hi) in Cluster(times, ExistenceClusterGap))
|
||||
foreach (var (lo, hi) in QuestDbTimeClusters.Cluster(times, QuestDbTimeClusters.ExistenceGap))
|
||||
{
|
||||
await using var cmd = _conn.CreateCommand(conn,
|
||||
$"SELECT ts FROM {_conn.SampleTable} WHERE tag = $1 AND ts >= $2 AND ts <= $3 AND NOT deleted");
|
||||
|
||||
Reference in New Issue
Block a user