fix(hosting): exit on our own when the orderly shutdown hangs [CI]
ci/woodpecker/push/web-build Pipeline was successful
ci/woodpecker/push/verify Pipeline was successful
ci/woodpecker/push/dev-image Pipeline is running

This commit is contained in:
justme committed 2026-10-18 10:22:34 +08:00
1 parent 839c45c7d4
commit f9be98e801
1 file changed
+22
@@ -156,6 +156,9 @@ public static class ServiceShell
log.Info("stopping", ("result", "SIGHUP"));
cts.Cancel();
});
// 有序退出卡住(比如某个已经死掉的会话收不到对端的关闭回复)时不能一直挂着:supervisor 的 stop 超时之前自己退出,由它重新拉起
using var exited = new CancellationTokenSource();
using CancellationTokenRegistration deadline = cts.Token.Register(() => _ = ExitIfStuckAsync(time ?? TimeProvider.System, log, exited.Token));
Task trimmer = TrimHeapAsync(cts.Token);
Task scheduled = restart is null ? Task.CompletedTask : WaitForRestartAsync(restart, time ?? TimeProvider.System, log, cts);
try
@@ -171,9 +174,28 @@ public static class ServiceShell
await cts.CancelAsync();
await trimmer;
await scheduled;
await exited.CancelAsync();
}
}
/// <summary>退出请求发出之后给有序退出的时间(要短于容器的 StopTimeout,否则 supervisor 会先强杀)。</summary>
public static readonly TimeSpan ShutdownGrace = TimeSpan.FromSeconds(10);
private static async Task ExitIfStuckAsync(TimeProvider time, INodeLog log, CancellationToken exited)
{
try
{
await Task.Delay(ShutdownGrace, time, exited);
}
catch (OperationCanceledException)
{
return; // 正常退出了
}
log.Warn("orderly shutdown did not finish in time, exiting", ("grace_s", (long)ShutdownGrace.TotalSeconds));
Environment.Exit(0);
}
private static async Task WaitForRestartAsync(RestartController restart, TimeProvider time, INodeLog log, CancellationTokenSource cts)
{
while (!cts.IsCancellationRequested)