diff --git a/src/ZB.MOM.WW.ScadaBridge.Host/Actors/AkkaHostedService.cs b/src/ZB.MOM.WW.ScadaBridge.Host/Actors/AkkaHostedService.cs index 5f9a2930..9ee529c1 100644 --- a/src/ZB.MOM.WW.ScadaBridge.Host/Actors/AkkaHostedService.cs +++ b/src/ZB.MOM.WW.ScadaBridge.Host/Actors/AkkaHostedService.cs @@ -272,6 +272,7 @@ akka {{ }} coordinated-shutdown {{ run-by-clr-shutdown-hook = on + phases.cluster-leave.timeout = 15s }} }}"; } diff --git a/tests/ZB.MOM.WW.ScadaBridge.Host.Tests/HoconBuilderTests.cs b/tests/ZB.MOM.WW.ScadaBridge.Host.Tests/HoconBuilderTests.cs index b3b1b4ec..44bbd822 100644 --- a/tests/ZB.MOM.WW.ScadaBridge.Host.Tests/HoconBuilderTests.cs +++ b/tests/ZB.MOM.WW.ScadaBridge.Host.Tests/HoconBuilderTests.cs @@ -26,6 +26,23 @@ public class HoconBuilderTests FailureDetectionThreshold = TimeSpan.FromSeconds(10), }; + [Fact] + public void BuildHocon_ClusterLeavePhaseTimeout_ExceedsDrainBudget() + { + // Review 01 [Medium]: the five singleton drain tasks GracefulStop(10s) + // inside cluster-leave, whose default phase timeout is 5s — Akka abandons + // the task at 5s and the drain guarantee is silently halved. + var node = new NodeOptions { Role = "Central", NodeHostname = "node1", RemotingPort = 8081 }; + var hocon = AkkaHostedService.BuildHocon( + node, DefaultCluster(), new[] { "Central" }, + TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(15)); + + var config = ConfigurationFactory.ParseString(hocon); + var phaseTimeout = config.GetTimeSpan("akka.coordinated-shutdown.phases.cluster-leave.timeout"); + Assert.True(phaseTimeout >= TimeSpan.FromSeconds(15), + $"cluster-leave phase timeout ({phaseTimeout}) must exceed the 10s singleton GracefulStop budget"); + } + [Fact] public void BuildHocon_PlainValues_ParsesAndPreservesValues() {