Implement graceful worker shutdown

This commit is contained in:
Joseph Doherty
2026-04-26 19:36:22 -04:00
parent 95e71cd819
commit d890eff862
15 changed files with 694 additions and 11 deletions
@@ -1,3 +1,4 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.InteropServices;
@@ -147,6 +148,29 @@ public sealed class WorkerPipeSessionTests
Assert.Equal(ProtocolStatusCode.WorkerUnavailable, written[1].WorkerFault.ProtocolStatus.Code);
}
[Fact]
public async Task RunAsync_WithWorkerShutdown_WritesShutdownAckAndReturns()
{
WorkerFrameProtocolOptions options = CreateOptions();
MemoryStream inbound = new();
WorkerFrameWriter inboundWriter = new(inbound, options);
await inboundWriter.WriteAsync(CreateGatewayHelloEnvelope());
await inboundWriter.WriteAsync(CreateWorkerShutdownEnvelope());
inbound.Position = 0;
MemoryStream outbound = new();
WorkerPipeSession session = CreateSession(inbound, outbound, options);
await session.CompleteStartupHandshakeAsync(_ => Task.CompletedTask);
await session.RunAsync();
WorkerEnvelope[] written = ReadWrittenFrames(outbound, options);
Assert.Equal(3, written.Length);
Assert.Equal(WorkerEnvelope.BodyOneofCase.WorkerHello, written[0].BodyCase);
Assert.Equal(WorkerEnvelope.BodyOneofCase.WorkerReady, written[1].BodyCase);
Assert.Equal(WorkerEnvelope.BodyOneofCase.WorkerShutdownAck, written[2].BodyCase);
Assert.Equal(ProtocolStatusCode.Ok, written[2].WorkerShutdownAck.Status.Code);
}
private static WorkerPipeSession CreateSession(
Stream inbound,
Stream outbound,
@@ -185,6 +209,21 @@ public sealed class WorkerPipeSessionTests
};
}
private static WorkerEnvelope CreateWorkerShutdownEnvelope()
{
return new WorkerEnvelope
{
ProtocolVersion = GatewayContractInfo.WorkerProtocolVersion,
SessionId = SessionId,
Sequence = 2,
WorkerShutdown = new WorkerShutdown
{
GracePeriod = Google.Protobuf.WellKnownTypes.Duration.FromTimeSpan(TimeSpan.FromSeconds(1)),
Reason = "test-shutdown",
},
};
}
private static WorkerEnvelope[] ReadWrittenFrames(
MemoryStream stream,
WorkerFrameProtocolOptions options)