feat: Release v0.2.2 with shutdown improvements and connection handling fixes

This commit is contained in:
HueByte
2026-02-19 13:01:51 +01:00
parent 4c192717de
commit 552fe6afa3
6 changed files with 72 additions and 35 deletions
+1
View File
@@ -4,6 +4,7 @@ Release history for EchoHub.
## Releases
- [v0.2.2](v0.2.2.md) - Shutdown Fix
- [v0.2.1](v0.2.1.md) - Shutdown & CI Fixes
- [v0.2.0](v0.2.0.md) - IRC Gateway
- [v0.1.1](v0.1.1.md) - Directory Connection Self-Healing
+2
View File
@@ -1,5 +1,7 @@
- name: Overview
href: index.md
- name: v0.2.2
href: v0.2.2.md
- name: v0.2.1
href: v0.2.1.md
- name: v0.2.0
+7
View File
@@ -0,0 +1,7 @@
# v0.2.2 - Shutdown Fix
## Fixes
- Actually fixed server hanging on Ctrl+C — replaced `await using` with explicit dispose bounded to 3 seconds, so a stuck `HubConnection` can no longer block shutdown
- Reduced host shutdown timeout from 30s (default) to 5s
- Caught `OperationCanceledException` in the directory service reconnect loop so cancellation exits immediately instead of propagating through dispose
+1 -1
View File
@@ -1,6 +1,6 @@
<Project>
<PropertyGroup>
<Version>0.2.1</Version>
<Version>0.2.2</Version>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<NoWarn>$(NoWarn);CS1591</NoWarn>
</PropertyGroup>
+4
View File
@@ -35,6 +35,10 @@ while (true)
{
var builder = WebApplication.CreateBuilder(args);
// ── Host options ────────────────────────────────────────────────────
builder.Services.Configure<HostOptions>(options =>
options.ShutdownTimeout = TimeSpan.FromSeconds(5));
// ── Serilog ──────────────────────────────────────────────────────────
builder.Host.UseSerilog((context, config) =>
config.ReadFrom.Configuration(context.Configuration));
@@ -43,9 +43,11 @@ public sealed class ServerDirectoryService(
// Outer loop: rebuilds the connection if automatic reconnect permanently fails
while (!stoppingToken.IsCancellationRequested)
{
await using var connection = BuildConnection();
var connection = BuildConnection();
_connection = connection;
try
{
var connectionPermanentlyClosed = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
connection.Reconnected += async _ =>
@@ -80,10 +82,19 @@ public sealed class ServerDirectoryService(
return;
// Connection was permanently closed — wait briefly then rebuild
_connection = null;
logger.LogInformation("Rebuilding directory connection...");
await Task.Delay(ReconnectBaseDelay, stoppingToken);
}
catch (OperationCanceledException) when (stoppingToken.IsCancellationRequested)
{
return;
}
finally
{
_connection = null;
await DisposeConnectionAsync(connection);
}
}
}
private HubConnection BuildConnection()
@@ -175,9 +186,21 @@ public sealed class ServerDirectoryService(
}
}
private static async Task DisposeConnectionAsync(HubConnection connection)
{
try
{
await connection.DisposeAsync()
.AsTask().WaitAsync(TimeSpan.FromSeconds(3));
}
catch
{
// Don't let a slow dispose block shutdown
}
}
public override async Task StopAsync(CancellationToken cancellationToken)
{
// Cancel ExecuteAsync first — it disposes the connection via await using
await base.StopAsync(cancellationToken);
_connection = null;
}