fix(audit-log): explicit long CommandTimeout on partition-switch and per-channel purge maintenance paths
This commit is contained in:
@@ -227,8 +227,13 @@ VALUES
|
||||
};
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<long> SwitchOutPartitionAsync(DateTime monthBoundary, CancellationToken ct = default)
|
||||
public async Task<long> SwitchOutPartitionAsync(DateTime monthBoundary, TimeSpan? commandTimeout = null, CancellationToken ct = default)
|
||||
{
|
||||
// Maintenance timeout in whole seconds (ADO.NET CommandTimeout unit). Null leaves the
|
||||
// provider default in place. See AuditLogPurgeOptions.MaintenanceCommandTimeoutMinutes /
|
||||
// arch-review 04 S2 for why the ~30s default is unsafe for the switch-out dance.
|
||||
var commandTimeoutSeconds = commandTimeout is { } ct2 ? (int)ct2.TotalSeconds : (int?)null;
|
||||
|
||||
// GUID-suffixed staging name: prevents collision with any concurrent
|
||||
// purge attempt and avoids polluting the AuditLog object namespace with
|
||||
// a predictable identifier.
|
||||
@@ -351,6 +356,10 @@ VALUES
|
||||
await using (var sampleCmd = conn.CreateCommand())
|
||||
{
|
||||
sampleCmd.CommandText = sampleSql;
|
||||
if (commandTimeoutSeconds is { } sampleTimeout)
|
||||
{
|
||||
sampleCmd.CommandTimeout = sampleTimeout;
|
||||
}
|
||||
var sampleResult = await sampleCmd.ExecuteScalarAsync(ct).ConfigureAwait(false);
|
||||
if (sampleResult is not null && sampleResult is not DBNull)
|
||||
{
|
||||
@@ -366,7 +375,27 @@ VALUES
|
||||
}
|
||||
}
|
||||
|
||||
await _context.Database.ExecuteSqlRawAsync(sql, ct);
|
||||
// Apply the maintenance timeout to the drop-and-rebuild dance. The context is scoped per
|
||||
// purge tick (see AuditLogPurgeActor.OnTickAsync's CreateAsyncScope), so a restore is not
|
||||
// strictly required — but we restore the previous value in a finally for hygiene in case a
|
||||
// future caller reuses the context for other work after the switch-out.
|
||||
var previousTimeout = _context.Database.GetCommandTimeout();
|
||||
if (commandTimeout is { } maintenanceTimeout)
|
||||
{
|
||||
_context.Database.SetCommandTimeout(maintenanceTimeout);
|
||||
}
|
||||
try
|
||||
{
|
||||
await _context.Database.ExecuteSqlRawAsync(sql, ct);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (commandTimeout is not null)
|
||||
{
|
||||
_context.Database.SetCommandTimeout(previousTimeout);
|
||||
}
|
||||
}
|
||||
|
||||
return rowsDeleted;
|
||||
}
|
||||
|
||||
@@ -375,6 +404,7 @@ VALUES
|
||||
string channel,
|
||||
DateTime threshold,
|
||||
int batchSize,
|
||||
TimeSpan? commandTimeout = null,
|
||||
CancellationToken ct = default)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(channel))
|
||||
@@ -389,6 +419,9 @@ VALUES
|
||||
|
||||
var thresholdUtc = DateTime.SpecifyKind(threshold.ToUniversalTime(), DateTimeKind.Utc);
|
||||
|
||||
// Maintenance timeout in whole seconds; null leaves the provider default (arch-review 04 S2).
|
||||
var commandTimeoutSeconds = commandTimeout is { } cto ? (int)cto.TotalSeconds : (int?)null;
|
||||
|
||||
// Per-channel retention override purge. This is the ONLY DELETE
|
||||
// against dbo.AuditLog in the codebase and it runs on the purge/maintenance
|
||||
// path, NOT the append-only writer role (which has INSERT + SELECT only — see
|
||||
@@ -427,6 +460,10 @@ VALUES
|
||||
|
||||
await using var cmd = conn.CreateCommand();
|
||||
cmd.CommandText = deleteBatchSql;
|
||||
if (commandTimeoutSeconds is { } batchTimeout)
|
||||
{
|
||||
cmd.CommandTimeout = batchTimeout;
|
||||
}
|
||||
|
||||
var pBatch = cmd.CreateParameter();
|
||||
pBatch.ParameterName = "@batch";
|
||||
|
||||
Reference in New Issue
Block a user