test(historian): cancel the retention purge only once its DELETE is observed blocked (L6 review)
This commit is contained in:
+21
-4
@@ -195,16 +195,33 @@ public sealed class RetentionTests(SqlHistorianFixture fx)
|
||||
cmd.CommandText = $"SELECT COUNT(*) FROM [{s}].Sample WITH (TABLOCKX, HOLDLOCK)";
|
||||
await cmd.ExecuteScalarAsync();
|
||||
}
|
||||
short holderSpid;
|
||||
await using (var cmd = holder.CreateCommand())
|
||||
{
|
||||
cmd.Transaction = tx;
|
||||
cmd.CommandText = "SELECT CAST(@@SPID AS smallint)";
|
||||
holderSpid = (short)(await cmd.ExecuteScalarAsync())!;
|
||||
}
|
||||
|
||||
using var cts = new CancellationTokenSource();
|
||||
var purger = new SqlServerHistorianRetention(c, b, TimeSpan.FromHours(1), 30, 4000, time: new FixedTime(Now));
|
||||
var purge = purger.PurgeAsync(cts.Token);
|
||||
await Task.Delay(500);
|
||||
purge.IsCompleted.ShouldBeFalse("the DELETE must be blocked behind the holder's table lock");
|
||||
|
||||
// Cancel only once the server reports a DELETE actually blocked behind the holder — a fixed delay could
|
||||
// cancel during bootstrap/applock acquisition on a slow host and never exercise the mid-command path.
|
||||
var deadline = DateTime.UtcNow.AddSeconds(15);
|
||||
while (await CountAsync(
|
||||
$"SELECT COUNT(*) FROM sys.dm_exec_requests WHERE blocking_session_id = {holderSpid} AND command = 'DELETE'") == 0)
|
||||
{
|
||||
purge.IsCompleted.ShouldBeFalse("the purge finished without ever blocking on the holder's table lock");
|
||||
DateTime.UtcNow.ShouldBeLessThan(deadline, "the purge's DELETE never blocked behind the holder");
|
||||
await Task.Delay(50);
|
||||
}
|
||||
cts.Cancel();
|
||||
|
||||
// SqlClient may surface the attention as a SqlException rather than OperationCanceledException.
|
||||
await Should.ThrowAsync<Exception>(() => purge);
|
||||
// SqlClient surfaces the attention as OperationCanceledException or as a SqlException.
|
||||
var ex = await Should.ThrowAsync<Exception>(() => purge);
|
||||
(ex is OperationCanceledException or SqlException).ShouldBeTrue($"unexpected {ex.GetType().Name}: {ex.Message}");
|
||||
|
||||
await tx.RollbackAsync();
|
||||
(await LockIsFreeAsync(s)).ShouldBeTrue();
|
||||
|
||||
Reference in New Issue
Block a user