fix(runtime): explicit 30s command timeout on the Direct-apply artifact read
This commit is contained in:
@@ -2093,6 +2093,35 @@ public sealed class DriverHostActor : ReceiveActor, IWithTimers, IWithUnboundedS
|
||||
try
|
||||
{
|
||||
using var db = _dbFactory.CreateDbContext();
|
||||
|
||||
// Bound this read EXPLICITLY. It runs off the mailbox on a pool thread with no
|
||||
// CancellationToken (BeginDirectApply's Task.Run), so the actor itself cannot be wedged by a
|
||||
// hung central-SQL read — but the read would pin a pool thread for as long as SQL takes to
|
||||
// answer. Until now the only bound was SqlClient's IMPLICIT 30 s command-timeout default:
|
||||
// nothing on the ConfigDb path sets one (AddPooledDbContextFactory uses a plain
|
||||
// UseSqlServer(connectionString), and no connection string carries "Command Timeout="), so a
|
||||
// future connection-string edit or provider-default change could silently unbound it. Stating
|
||||
// it here pins the bound to this call site.
|
||||
//
|
||||
// Deliberately LOCAL to this lease, not on the pooled factory's options: re-timing the global
|
||||
// factory would re-time every ConfigDb consumer — migrations and long admin queries included.
|
||||
// Verified against EF Core 10.0.7 (pool size 1, same context instance re-leased):
|
||||
// Database.SetCommandTimeout does NOT survive the return to the pool — RelationalConnection
|
||||
// resets it as an IResettableService — so the next lease sees the default again and there is
|
||||
// no pool pollution to undo.
|
||||
//
|
||||
// Deliberately NOT a CancellationToken: threading one would mean making the FirstOrDefault
|
||||
// below async, and the only caller-side token is actor lifetime — the timeout gives the same
|
||||
// bound without touching the actor seam. A timeout surfaces as an exception through this
|
||||
// method's existing catch-to-null, so the apply FAILS and the revision does not advance,
|
||||
// keeping last-known-good served (the #485-family "no answer, never tear down" semantics —
|
||||
// already correct, unchanged here).
|
||||
//
|
||||
// Guarded on IsRelational: the timeout is a relational-only concept and the extension throws
|
||||
// on a non-relational provider (the in-memory provider the actor tests run against).
|
||||
if (db.Database.IsRelational())
|
||||
db.Database.SetCommandTimeout(TimeSpan.FromSeconds(30));
|
||||
|
||||
return db.Deployments.AsNoTracking()
|
||||
.Where(d => d.DeploymentId == deploymentId.Value)
|
||||
.Select(d => d.ArtifactBlob)
|
||||
|
||||
Reference in New Issue
Block a user