Skip to content

Commit 2d19b5b

Browse files
committed
Backport #8421 to v1.5: flush graceful Disassociate PDUs before transport force-close on shutdown
ActorTransportAdapter.Shutdown() started manager.GracefulStop() and WrappedTransport.Shutdown() concurrently. The manager's child association actors (the ProtocolStateActors under AkkaProtocolTransport) write the graceful Disassociate PDU as part of their own shutdown, so running the wrapped-transport teardown concurrently let the underlying transport force-close the channel before that PDU reached the wire. Peers were then left with a zombie association until the transport failure detector tripped (acceptable-heartbeat-pause, 120s by default); under the trttl/gremlin test adapters, which swallow the raw TCP close, that lost PDU is the sole prompt teardown signal. Sequence the two operations: stop the manager first (bounded by flush-wait-on- shutdown), then tear down the wrapped transport. The wrapped transport is torn down whether or not the manager stopped cleanly, so shutdown cannot hang on this ordering. (cherry picked from commit a918393)
1 parent ea6d0f6 commit 2d19b5b

1 file changed

Lines changed: 15 additions & 2 deletions

File tree

src/core/Akka.Remote/Transport/TransportAdapters.cs

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -550,8 +550,21 @@ protected override void InterceptAssociate(Address remoteAddress, TaskCompletion
550550
public override Task<bool> Shutdown()
551551
{
552552
var stopTask = manager.GracefulStop((RARP.For(System).Provider).RemoteSettings.FlushWait);
553-
var transportStopTask = WrappedTransport.Shutdown();
554-
return Task.WhenAll(stopTask, transportStopTask).ContinueWith(x => x.IsCompleted && !(x.IsFaulted || x.IsCanceled), TaskContinuationOptions.ExecuteSynchronously);
553+
554+
// Sequence the wrapped-transport teardown AFTER the manager (and its child association
555+
// actors) have stopped. Those children write the graceful Disassociate PDU as part of
556+
// their own shutdown; running WrappedTransport.Shutdown() concurrently (as this used to)
557+
// let the underlying transport force-close the channel out from under a not-yet-flushed
558+
// Disassociate. Peers then kept a "zombie" association until the transport failure
559+
// detector tripped (up to acceptable-heartbeat-pause, 120s by default). The manager stop
560+
// is bounded by flush-wait-on-shutdown, and the wrapped transport is torn down regardless
561+
// of whether the manager stopped cleanly, so shutdown can never hang on this ordering.
562+
var transportStopTask = stopTask.ContinueWith(_ => WrappedTransport.Shutdown(),
563+
TaskContinuationOptions.ExecuteSynchronously).Unwrap();
564+
565+
return Task.WhenAll(stopTask, transportStopTask)
566+
.ContinueWith(x => x.IsCompleted && !(x.IsFaulted || x.IsCanceled),
567+
TaskContinuationOptions.ExecuteSynchronously);
555568
}
556569
}
557570

0 commit comments

Comments
 (0)