fix(deploy): guardrail estimate is best-effort, never blocks a valid deploy
Wrap the script compile-cost guardrail block in its own inner try/catch so a transient SQL failure on ToListAsync cannot fall through to the outer catch and produce a Rejected reply for an otherwise-valid deploy. advisory is declared in the outer scope so the Accepted StartDeploymentResult Message is unaffected on the happy path; the inner catch logs a Warning and leaves advisory null.
This commit is contained in:
@@ -112,20 +112,32 @@ public sealed class AdminOperationsActor : ReceiveActor
|
||||
// sources are counted (the compile cache keys on source, so duplicates collapse to one unit).
|
||||
// This only surfaces the estimate to the operator — the DraftValidator gate above is the hard
|
||||
// reject; this advisory rides in the Accepted Message so the UI can show it.
|
||||
const double PerScriptMiB = 1.66;
|
||||
var scriptSources = await db.Scripts.AsNoTracking().Select(s => s.SourceCode).ToListAsync();
|
||||
var compiled = scriptSources
|
||||
.Where(src => !PassthroughScript.TryMatch(src, out _))
|
||||
.Distinct(StringComparer.Ordinal)
|
||||
.Count();
|
||||
// advisory is declared outside the inner try so the Accepted reply below can still read it even
|
||||
// when the guardrail query throws (e.g. transient SQL). A failed estimate must NEVER block a
|
||||
// valid deploy — the outer catch is reserved for genuine seal/save failures.
|
||||
string? advisory = null;
|
||||
if (compiled > 0)
|
||||
try
|
||||
{
|
||||
var estMiB = compiled * PerScriptMiB;
|
||||
_log.Warning(
|
||||
"StartDeployment: {Compiled} script(s) will compile (~{EstMiB:F0} MiB RSS per node); ensure node mem_limit covers it",
|
||||
compiled, estMiB);
|
||||
advisory = $"{compiled} script(s) will compile (~{estMiB:F0} MiB/node)";
|
||||
const double PerScriptMiB = 1.66; // measured post-A0 per-script RSS (design doc 2026-06-07)
|
||||
var scriptSources = await db.Scripts.AsNoTracking().Select(s => s.SourceCode).ToListAsync();
|
||||
var compiled = scriptSources
|
||||
.Where(src => !PassthroughScript.TryMatch(src, out _))
|
||||
.Distinct(StringComparer.Ordinal)
|
||||
.Count();
|
||||
if (compiled > 0)
|
||||
{
|
||||
var estMiB = compiled * PerScriptMiB;
|
||||
_log.Warning(
|
||||
"StartDeployment: {Compiled} script(s) will compile (~{EstMiB:F0} MiB RSS per node); ensure node mem_limit covers it",
|
||||
compiled, estMiB);
|
||||
advisory = $"{compiled} script(s) will compile (~{estMiB:F0} MiB/node)";
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// Guardrail is advisory-only — a failed estimate must never block a valid deploy.
|
||||
_log.Warning(ex, "StartDeployment: script compile-cost estimate failed; advisory skipped");
|
||||
advisory = null;
|
||||
}
|
||||
|
||||
var artifact = await ConfigComposer.SnapshotAndFlattenAsync(db);
|
||||
|
||||
Reference in New Issue
Block a user