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:
Joseph Doherty
2026-10-22 09:48:55 -04:00
parent e60c316f30
commit d646f816a4
3 changed files with 93 additions and 9 deletions
@@ -230,4 +230,57 @@ public class DeploymentManagerActorNameTests : TestKit, IDisposable
return Task.CompletedTask;
}, TimeSpan.FromSeconds(10));
}
/// <summary>
/// Follow-up L3: an enable that lands while a redeploy owns the instance (here: queued behind
/// the redeploy's compile warm, so it is dispatched the moment the predecessor actor starts
/// terminating) is refused with a clear "deployment in progress" reply. It used to race the
/// deploy into <c>CreateInstanceActor</c>, collide with the terminating predecessor's name,
/// and then REVERT is_enabled — leaving the freshly redeployed instance disabled.
/// </summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task Enable_DuringRedeploy_IsRefused_AndDoesNotDisableTheInstance()
{
var dm = CreateBootedDeploymentManager();
Assert.Equal(DeploymentStatus.Success, Deploy(dm, "Pump1").Status);
dm.Tell(new DeployInstanceCommand(
$"dep-{Guid.NewGuid():N}", "Pump1", "rev-2", ConfigJson("Pump1", "S2"),
"tester", DateTimeOffset.UtcNow));
dm.Tell(new EnableInstanceCommand("cmd-en", "Pump1", DateTimeOffset.UtcNow));
InstanceLifecycleResponse? enable = null;
DeploymentStatusResponse? deploy = null;
for (var i = 0; i < 2; i++)
{
var msg = ExpectMsg<object>(TimeSpan.FromSeconds(20));
if (msg is InstanceLifecycleResponse l) enable = l;
if (msg is DeploymentStatusResponse d) deploy = d;
}
Assert.NotNull(enable);
Assert.NotNull(deploy);
Assert.Equal(DeploymentStatus.Success, deploy!.Status);
Assert.False(enable!.Success);
Assert.Contains("deployment", enable.ErrorMessage, StringComparison.OrdinalIgnoreCase);
Assert.Contains("in progress", enable.ErrorMessage, StringComparison.OrdinalIgnoreCase);
// Nothing reverts the redeployed instance to disabled.
await Task.Delay(500);
Assert.True((await _storage.GetDeployedConfigMetadataAsync("Pump1"))!.IsEnabled);
}
/// <summary>
/// Follow-up L3: only a name Akka will NEVER accept reverts is_enabled after a failed enable;
/// a uniqueness collision (a valid name still held by a terminating actor) is transient and
/// must not flip the durable row to disabled.
/// </summary>
[Theory]
[InlineData("a b", true)]
[InlineData("Pump1", false)]
public void EnableCreateFailure_RevertsOnlyForAnInvalidName(string instanceName, bool expectRevert)
{
Assert.Equal(expectRevert, DeploymentManagerActor.ShouldRevertEnableAfterCreateFailure(instanceName));
}
}