fix(site-runtime): refuse an enable while a deploy pipeline owns the instance; revert is_enabled only for an invalid name (review 2026-10-21 follow-up L3)
This commit is contained in:
@@ -1799,6 +1799,18 @@ public class DeploymentManagerActor : ReceiveActor, IWithTimers
|
||||
// Warm-then-gate: see TryBufferDuringDeployWarm.
|
||||
if (TryBufferDuringDeployWarm(instanceName, command, replyTo)) return;
|
||||
|
||||
// Review 2026-10-21 follow-up L3: a redeploy/delete past its warm still OWNS the instance
|
||||
// (predecessor terminating, or awaiting its persistence/init join) and will create the
|
||||
// actor itself. An enable racing it would collide with the terminating predecessor's
|
||||
// actor name in CreateInstanceActor — refuse it plainly instead.
|
||||
if (InstanceIsOwnedByADeployPipeline(instanceName))
|
||||
{
|
||||
replyTo.Tell(new InstanceLifecycleResponse(command.CommandId, instanceName, false,
|
||||
$"A deployment for instance {instanceName} is in progress; retry the enable after it completes.",
|
||||
DateTimeOffset.UtcNow));
|
||||
return;
|
||||
}
|
||||
|
||||
var sender = replyTo;
|
||||
|
||||
// Row 101 (initiation half) — an is_enabled UPDATE is about to start off-thread.
|
||||
@@ -1853,15 +1865,23 @@ public class DeploymentManagerActor : ReceiveActor, IWithTimers
|
||||
// Lane A L3: is_enabled=1 has already committed; put it back so the durable row
|
||||
// agrees with the runtime (and the sweep does not keep re-trying it). Row 101 bumps at
|
||||
// both ends, as for every other mutation of deployed_configurations.
|
||||
BumpStoreWriteGeneration("enable revert dispatch");
|
||||
var revert = _storage.SetInstanceEnabledAsync(instanceName, false);
|
||||
revert.ContinueWith(t =>
|
||||
//
|
||||
// Follow-up L3: ONLY for a name Akka will never accept. A uniqueness collision (a
|
||||
// valid name still held by a terminating actor) is transient — reverting it would
|
||||
// leave an instance the operator enabled (or a redeploy just started) durably
|
||||
// disabled; left enabled, the sweep starts it once the name is free.
|
||||
if (ShouldRevertEnableAfterCreateFailure(instanceName))
|
||||
{
|
||||
if (!t.IsCompletedSuccessfully)
|
||||
_logger.LogError(t.Exception?.GetBaseException(),
|
||||
"Failed to revert is_enabled for {Instance} after a failed enable", instanceName);
|
||||
});
|
||||
revert.ContinueWith(_ => new StoreWriteCompleted("enable revert")).PipeTo(Self);
|
||||
BumpStoreWriteGeneration("enable revert dispatch");
|
||||
var revert = _storage.SetInstanceEnabledAsync(instanceName, false);
|
||||
revert.ContinueWith(t =>
|
||||
{
|
||||
if (!t.IsCompletedSuccessfully)
|
||||
_logger.LogError(t.Exception?.GetBaseException(),
|
||||
"Failed to revert is_enabled for {Instance} after a failed enable", instanceName);
|
||||
});
|
||||
revert.ContinueWith(_ => new StoreWriteCompleted("enable revert")).PipeTo(Self);
|
||||
}
|
||||
result.OriginalSender.Tell(new InstanceLifecycleResponse(
|
||||
result.Command.CommandId, instanceName, false, createError, DateTimeOffset.UtcNow));
|
||||
return;
|
||||
@@ -1877,6 +1897,17 @@ public class DeploymentManagerActor : ReceiveActor, IWithTimers
|
||||
_logger.LogInformation("Instance {Instance} enabled", instanceName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether a failed enable should put <c>is_enabled</c> back to 0: only when the instance
|
||||
/// name is one Akka refuses outright (it can never start, so leaving it enabled would only
|
||||
/// make the sweep retry it forever). A collision on a VALID name is transient (review
|
||||
/// 2026-10-21 follow-up L3).
|
||||
/// </summary>
|
||||
/// <param name="instanceName">The instance whose enable failed to create its actor.</param>
|
||||
/// <returns><see langword="true"/> when the enable should be reverted.</returns>
|
||||
internal static bool ShouldRevertEnableAfterCreateFailure(string instanceName) =>
|
||||
!ActorPath.IsValidPathElement(instanceName);
|
||||
|
||||
/// <summary>
|
||||
/// Reconcile self-heal (deferred register row 67): makes sure an instance whose config was
|
||||
/// (re)stored out-of-band is actually RUNNING.
|
||||
|
||||
Reference in New Issue
Block a user