Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 26 additions & 11 deletions GameFrameX.Discovery/Discovery/DiscoveryRegistry.cs
Original file line number Diff line number Diff line change
Expand Up @@ -355,11 +355,7 @@ public void Dispose()
try
{
_loopCancellation.Cancel();
WriteStoppedAsync().GetAwaiter().GetResult();
}
catch (Exception)
{
// 尽力而为:进程退出路径上数据库不可达时无法补写终态,交给 TTL 兜底清除。
WriteTerminalHeartbeatSafely();
}
finally
{
Expand Down Expand Up @@ -470,13 +466,32 @@ private static async Task WaitLoopAsync(Task loopTask)
/// <param name="eventArguments">事件参数 / The event arguments</param>
private void OnProcessExit(object sender, EventArgs eventArguments)
{
try
{
WriteStoppedAsync().GetAwaiter().GetResult();
}
catch (Exception)
WriteTerminalHeartbeatSafely();
}

/// <summary>
/// 终态心跳的同步尽力写入:2 秒超时 + Task.Run 脱离可能的 SynchronizationContext,
/// 避免存在同步上下文时死锁;失败/超时记日志不阻塞退出(TTL 兜底清除)。
/// </summary>
/// <remarks>
/// Best-effort synchronous write of the terminal heartbeat: a 2-second timeout and
/// Task.Run to escape any SynchronizationContext (deadlock guard); failures and
/// timeouts are logged without blocking exit (TTL cleanup is the fallback).
/// </remarks>
private void WriteTerminalHeartbeatSafely()
{
using (var cancellation = new CancellationTokenSource(TimeSpan.FromSeconds(2)))
{
// 进程退出路径上数据库不可达时无法补写终态,交给 TTL 兜底清除。
try
{
Task.Run(() => WriteStoppedAsync(), cancellation.Token).GetAwaiter().GetResult();
}
catch (Exception exception)
{
// 尽力而为:进程退出路径上数据库不可达/超时时无法补写终态,交给 TTL 兜底清除。
// Localization: Discovery.Registry.TerminalHeartbeatFailed - [DiscoveryRegistry] 终态心跳写入失败(超时 2s 内未完成),交给 TTL 兜底清除
LogHelper.Error(exception, LocalizationService.GetString(Localization.Keys.Discovery.Registry.TerminalHeartbeatFailed));
}
}
}

Expand Down
9 changes: 9 additions & 0 deletions GameFrameX.Localization/Keys.Discovery.cs
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,15 @@ public static class Registry
/// </remarks>
public const string TtlCleanupFailed = "Discovery.Registry.TtlCleanupFailed";

/// <summary>
/// [DiscoveryRegistry] 终态心跳写入失败(超时 2s 内未完成),交给 TTL 兜底清除
/// </summary>
/// <remarks>
/// 键名: Discovery.Registry.TerminalHeartbeatFailed
/// 用途: Dispose/ProcessExit 终态心跳尽力写入失败或超时时的错误日志
/// </remarks>
public const string TerminalHeartbeatFailed = "Discovery.Registry.TerminalHeartbeatFailed";

/// <summary>
/// 附加广播端点未被发布:服务进程类型 '{0}' 不是本进程心跳承载的首选类型 '{1}'(心跳文档单 Role 模型,非首选类型不跨进程可达)
/// </summary>
Expand Down
4 changes: 4 additions & 0 deletions GameFrameX.Localization/Localization/Messages/Resources.resx
Original file line number Diff line number Diff line change
Expand Up @@ -1984,6 +1984,10 @@
<value>[AdvertiseEndpointEnvironment] egress address detection failed; falling back to the machine name {0} as the advertise host. Set {1} explicitly when cross-process routing is required.</value>
</data>


<data name="Discovery.Registry.TerminalHeartbeatFailed" xml:space="preserve">
<value>[DiscoveryRegistry] terminal heartbeat write failed (not completed within the 2s timeout); leaving it to TTL cleanup</value>
</data>
<data name="Discovery.Registry.HeartbeatUpsertFailed" xml:space="preserve">
<value>[DiscoveryRegistry] heartbeat upsert failed for instance {0}; will retry next interval</value>
</data>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1978,6 +1978,10 @@
<value>[AdvertiseEndpointEnvironment] 出口地址探测失败;回退使用机器名 {0} 作为广播主机。需要跨进程路由时请显式设置 {1}</value>
</data>


<data name="Discovery.Registry.TerminalHeartbeatFailed" xml:space="preserve">
<value>[DiscoveryRegistry] 终态心跳写入失败(超时 2s 内未完成),交给 TTL 兜底清除</value>
</data>
<data name="Discovery.Registry.HeartbeatUpsertFailed" xml:space="preserve">
<value>[DiscoveryRegistry] 实例 {0} 心跳写入失败;将在下个间隔重试</value>
</data>
Expand Down
Loading