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:
+53
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user