fix(historian): T9 review round 1 - dup-key retry counter, alarm Classify retries unknown, test hardening
Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
This commit is contained in:
+19
-5
@@ -68,18 +68,32 @@ public sealed class ProvisioningTests(SqlHistorianFixture fx)
|
||||
var s = SqlHistorianFixture.UniqueSchema();
|
||||
var (p0, _) = Make(s);
|
||||
await p0.EnsureTagsAsync([new("warm", DriverDataType.Int32, null, null)], CancellationToken.None); // bootstrap once
|
||||
for (var round = 0; round < 10; round++)
|
||||
for (var round = 0; round < 20; round++)
|
||||
{
|
||||
var reqs = Enumerable.Range(0, 60).Select(i => new HistorianTagProvisionRequest($"r{round}.t{i}", DriverDataType.Float64, "u", null)).ToList();
|
||||
var rs = await Task.WhenAll(Enumerable.Range(0, 4).Select(_ => Task.Run(() => Make(s).P.EnsureTagsAsync(reqs, CancellationToken.None))));
|
||||
foreach (var r in rs) r.ShouldBe(new HistorianProvisionResult(60, 60, 0, 0));
|
||||
var ps = Enumerable.Range(0, 16).Select(_ => Make(s).P).ToList();
|
||||
// Warm each instance (its own bootstrap-once) and release all together, else bootstrap serialisation staggers the race away.
|
||||
foreach (var p in ps) await p.EnsureTagsAsync([new("warm", DriverDataType.Int32, null, null)], CancellationToken.None);
|
||||
var gate = new TaskCompletionSource();
|
||||
// Staggered windows over one pool: every worker's FIRST name differs, so a MERGE without HOLDLOCK cannot hide behind
|
||||
// a lock convoy on a shared first key and the overlap really races (union per round = 6 names; the single/few-row shape is the one T8 proved races).
|
||||
var tasks = ps.Select((p, k) => Task.Run(async () =>
|
||||
{
|
||||
var reqs = Enumerable.Range(k % 4, 3).Select(i => new HistorianTagProvisionRequest($"r{round}.t{i}", DriverDataType.Float64, "u", null)).ToList();
|
||||
await gate.Task;
|
||||
return await p.EnsureTagsAsync(reqs, CancellationToken.None);
|
||||
})).ToList();
|
||||
gate.SetResult();
|
||||
var rs = await Task.WhenAll(tasks);
|
||||
foreach (var r in rs) r.ShouldBe(new HistorianProvisionResult(3, 3, 0, 0));
|
||||
// The production dup-key retry would mask a missing HOLDLOCK; HOLDLOCK must mean it never fires.
|
||||
ps.Sum(p => p.DupKeyRetryCount).ShouldBe(0);
|
||||
}
|
||||
|
||||
await using var c = new SqlConnection(fx.ConnectionString);
|
||||
await c.OpenAsync();
|
||||
await using var cmd = c.CreateCommand();
|
||||
cmd.CommandText = $"SELECT COUNT(*) FROM [{s}].Tag";
|
||||
Convert.ToInt32(await cmd.ExecuteScalarAsync()).ShouldBe(601);
|
||||
Convert.ToInt32(await cmd.ExecuteScalarAsync()).ShouldBe(20 * 6 + 1);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
||||
Reference in New Issue
Block a user