refactor: split node agent and Go control plane
This commit is contained in:
@@ -0,0 +1,106 @@
|
||||
using System.Text.Json;
|
||||
|
||||
namespace WxAgent.Core;
|
||||
|
||||
public sealed record WechatStabilitySample(
|
||||
DateTimeOffset Timestamp,
|
||||
int ProcessCount,
|
||||
long WorkingSetBytes,
|
||||
int HandleCount,
|
||||
int ThreadCount,
|
||||
bool WindowFound,
|
||||
IReadOnlyList<WxAgentErrorCode> Errors,
|
||||
long MessageEvents,
|
||||
long ReconnectEvents,
|
||||
long AgentWorkingSetBytes = 0,
|
||||
int AgentHandleCount = 0,
|
||||
int AgentThreadCount = 0,
|
||||
DateTimeOffset? LastSnapshotAt = null);
|
||||
|
||||
public sealed record WechatStabilityReport(
|
||||
DateTimeOffset StartedAt,
|
||||
DateTimeOffset FinishedAt,
|
||||
IReadOnlyList<WechatStabilitySample> Samples,
|
||||
long MessageEvents,
|
||||
long ReconnectEvents,
|
||||
long WorkingSetGrowthBytes,
|
||||
int HandleGrowth,
|
||||
int ThreadGrowth,
|
||||
bool Healthy,
|
||||
IReadOnlyList<string> Findings,
|
||||
double RequestedSeconds = 0,
|
||||
double ObservedSeconds = 0,
|
||||
bool CompletedDuration = false);
|
||||
|
||||
public static class WechatStabilityAnalyzer
|
||||
{
|
||||
public static WechatStabilityReport Analyze(
|
||||
DateTimeOffset startedAt,
|
||||
DateTimeOffset finishedAt,
|
||||
IReadOnlyList<WechatStabilitySample> samples,
|
||||
long messageEvents,
|
||||
long reconnectEvents)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(samples);
|
||||
if (samples.Count == 0)
|
||||
throw new WxAgentException(WxAgentErrorCode.InvalidArgument, "At least one stability sample is required.");
|
||||
|
||||
var first = samples[0];
|
||||
var last = samples[^1];
|
||||
var memoryGrowth = last.WorkingSetBytes - first.WorkingSetBytes;
|
||||
var handleGrowth = last.HandleCount - first.HandleCount;
|
||||
var threadGrowth = last.ThreadCount - first.ThreadCount;
|
||||
var findings = new List<string>();
|
||||
var recoveredWindowLoss = last.WindowFound && reconnectEvents > 0;
|
||||
if (samples.Any(sample => !sample.WindowFound) && !recoveredWindowLoss) findings.Add("wechat-window-unavailable");
|
||||
foreach (var error in last.Errors) findings.Add("final-error:" + error);
|
||||
if (last.AgentWorkingSetBytes - first.AgentWorkingSetBytes > 128L * 1024 * 1024) findings.Add("agent-working-set-growth-over-128mb");
|
||||
if (last.AgentHandleCount - first.AgentHandleCount > 100) findings.Add("agent-handle-growth-over-100");
|
||||
if (last.AgentThreadCount - first.AgentThreadCount > 20) findings.Add("agent-thread-growth-over-20");
|
||||
if (memoryGrowth > 256L * 1024 * 1024) findings.Add("working-set-growth-over-256mb");
|
||||
if (handleGrowth > 500) findings.Add("handle-growth-over-500");
|
||||
if (threadGrowth > 100) findings.Add("thread-growth-over-100");
|
||||
if (samples.Any(sample => sample.Timestamp - (sample.LastSnapshotAt ?? startedAt) > TimeSpan.FromMinutes(5)))
|
||||
findings.Add("listener-snapshot-gap-over-5min");
|
||||
|
||||
return new WechatStabilityReport(
|
||||
startedAt,
|
||||
finishedAt,
|
||||
samples,
|
||||
messageEvents,
|
||||
reconnectEvents,
|
||||
memoryGrowth,
|
||||
handleGrowth,
|
||||
threadGrowth,
|
||||
findings.Count == 0,
|
||||
findings);
|
||||
}
|
||||
|
||||
public static WechatStabilityReport CheckRun(
|
||||
WechatStabilityReport report, TimeSpan requested, TimeSpan observed, bool cancelled,
|
||||
string? listenerFailure = null, string? samplingFailure = null)
|
||||
{
|
||||
var findings = report.Findings.ToList();
|
||||
var completed = observed + TimeSpan.FromSeconds(1) >= requested && !cancelled;
|
||||
if (!completed) findings.Add("duration-incomplete");
|
||||
if (listenerFailure is not null) findings.Add("listener-failed:" + listenerFailure);
|
||||
if (samplingFailure is not null) findings.Add("sampling-failed:" + samplingFailure);
|
||||
if (report.Samples[^1].LastSnapshotAt is not { } snapshot
|
||||
|| report.FinishedAt - snapshot > TimeSpan.FromSeconds(10)) findings.Add("listener-snapshot-stale");
|
||||
return report with { Healthy = findings.Count == 0, Findings = findings, RequestedSeconds = requested.TotalSeconds,
|
||||
ObservedSeconds = observed.TotalSeconds, CompletedDuration = completed };
|
||||
}
|
||||
|
||||
public static async Task WriteJsonAsync(WechatStabilityReport report, string path, CancellationToken cancellationToken)
|
||||
{
|
||||
var fullPath = Path.GetFullPath(path);
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(fullPath)!);
|
||||
var temporary = fullPath + ".tmp";
|
||||
try
|
||||
{
|
||||
await File.WriteAllTextAsync(temporary, JsonSerializer.Serialize(report, new JsonSerializerOptions { WriteIndented = true }), cancellationToken);
|
||||
File.Move(temporary, fullPath, overwrite: true);
|
||||
}
|
||||
finally { if (File.Exists(temporary)) File.Delete(temporary); }
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user