feat: execute full-repo remaining parity closure plan

This commit is contained in:
Joseph Doherty
2026-02-23 13:08:52 -05:00
parent cbe1fa6121
commit 2b64d762f6
75 changed files with 2325 additions and 121 deletions

View File

@@ -14,6 +14,8 @@ public sealed record ClosedClient
public string Name { get; init; } = "";
public string Lang { get; init; } = "";
public string Version { get; init; } = "";
public string AuthorizedUser { get; init; } = "";
public string Account { get; init; } = "";
public long InMsgs { get; init; }
public long OutMsgs { get; init; }
public long InBytes { get; init; }
@@ -22,5 +24,9 @@ public sealed record ClosedClient
public TimeSpan Rtt { get; init; }
public string TlsVersion { get; init; } = "";
public string TlsCipherSuite { get; init; } = "";
public string TlsPeerCertSubject { get; init; } = "";
public string MqttClient { get; init; } = "";
public string JwtIssuerKey { get; init; } = "";
public string JwtTags { get; init; } = "";
public string Proxy { get; init; } = "";
}

View File

@@ -116,11 +116,23 @@ public sealed class ConnInfo
[JsonPropertyName("tls_cipher_suite")]
public string TlsCipherSuite { get; set; } = "";
[JsonPropertyName("tls_peer_cert_subject")]
public string TlsPeerCertSubject { get; set; } = "";
[JsonPropertyName("tls_first")]
public bool TlsFirst { get; set; }
[JsonPropertyName("mqtt_client")]
public string MqttClient { get; set; } = "";
[JsonPropertyName("jwt_issuer_key")]
public string JwtIssuerKey { get; set; } = "";
[JsonPropertyName("jwt_tags")]
public string JwtTags { get; set; } = "";
[JsonPropertyName("proxy")]
public string Proxy { get; set; } = "";
}
/// <summary>

View File

@@ -1,4 +1,5 @@
using Microsoft.AspNetCore.Http;
using NATS.Server.Subscriptions;
namespace NATS.Server.Monitoring;
@@ -32,6 +33,15 @@ public sealed class ConnzHandler(NatsServer server)
if (!string.IsNullOrEmpty(opts.MqttClient))
connInfos = connInfos.Where(c => c.MqttClient == opts.MqttClient).ToList();
if (!string.IsNullOrEmpty(opts.User))
connInfos = connInfos.Where(c => c.AuthorizedUser == opts.User).ToList();
if (!string.IsNullOrEmpty(opts.Account))
connInfos = connInfos.Where(c => c.Account == opts.Account).ToList();
if (!string.IsNullOrEmpty(opts.FilterSubject))
connInfos = connInfos.Where(c => MatchesSubjectFilter(c, opts.FilterSubject)).ToList();
// Validate sort options that require closed state
if (opts.Sort is SortOpt.ByStop or SortOpt.ByReason && opts.State == ConnState.Open)
opts.Sort = SortOpt.ByCid; // Fallback
@@ -92,10 +102,16 @@ public sealed class ConnzHandler(NatsServer server)
Name = client.ClientOpts?.Name ?? "",
Lang = client.ClientOpts?.Lang ?? "",
Version = client.ClientOpts?.Version ?? "",
AuthorizedUser = client.ClientOpts?.Username ?? "",
Account = client.Account?.Name ?? "",
Pending = (int)client.PendingBytes,
Reason = client.CloseReason.ToReasonString(),
TlsVersion = client.TlsState?.TlsVersion ?? "",
TlsCipherSuite = client.TlsState?.CipherSuite ?? "",
TlsPeerCertSubject = client.TlsState?.PeerCert?.Subject ?? "",
JwtIssuerKey = string.IsNullOrEmpty(client.ClientOpts?.JWT) ? "" : "present",
JwtTags = "",
Proxy = client.ClientOpts?.Username?.StartsWith("proxy:", StringComparison.Ordinal) == true ? "true" : "",
Rtt = FormatRtt(client.Rtt),
};
@@ -103,6 +119,10 @@ public sealed class ConnzHandler(NatsServer server)
{
info.Subs = client.Subscriptions.Values.Select(s => s.Subject).ToArray();
}
else if (!string.IsNullOrEmpty(opts.FilterSubject))
{
info.Subs = client.Subscriptions.Values.Select(s => s.Subject).ToArray();
}
if (opts.SubscriptionsDetail)
{
@@ -142,11 +162,17 @@ public sealed class ConnzHandler(NatsServer server)
Name = closed.Name,
Lang = closed.Lang,
Version = closed.Version,
AuthorizedUser = closed.AuthorizedUser,
Account = closed.Account,
Reason = closed.Reason,
Rtt = FormatRtt(closed.Rtt),
TlsVersion = closed.TlsVersion,
TlsCipherSuite = closed.TlsCipherSuite,
TlsPeerCertSubject = closed.TlsPeerCertSubject,
MqttClient = closed.MqttClient,
JwtIssuerKey = closed.JwtIssuerKey,
JwtTags = closed.JwtTags,
Proxy = closed.Proxy,
};
}
@@ -205,9 +231,24 @@ public sealed class ConnzHandler(NatsServer server)
if (q.TryGetValue("mqtt_client", out var mqttClient))
opts.MqttClient = mqttClient.ToString();
if (q.TryGetValue("user", out var user))
opts.User = user.ToString();
if (q.TryGetValue("acc", out var account))
opts.Account = account.ToString();
if (q.TryGetValue("filter_subject", out var filterSubject))
opts.FilterSubject = filterSubject.ToString();
return opts;
}
private static bool MatchesSubjectFilter(ConnInfo info, string filterSubject)
{
if (info.Subs.Any(s => SubjectMatch.MatchLiteral(s, filterSubject)))
return true;
return info.SubsDetail.Any(s => SubjectMatch.MatchLiteral(s.Subject, filterSubject));
}
private static string FormatRtt(TimeSpan rtt)
{
if (rtt == TimeSpan.Zero) return "";

View File

@@ -21,6 +21,7 @@ public sealed class MonitorServer : IAsyncDisposable
private readonly GatewayzHandler _gatewayzHandler;
private readonly LeafzHandler _leafzHandler;
private readonly AccountzHandler _accountzHandler;
private readonly PprofHandler _pprofHandler;
public MonitorServer(NatsServer server, NatsOptions options, ServerStats stats, ILoggerFactory loggerFactory)
{
@@ -41,6 +42,7 @@ public sealed class MonitorServer : IAsyncDisposable
_gatewayzHandler = new GatewayzHandler(server);
_leafzHandler = new LeafzHandler(server);
_accountzHandler = new AccountzHandler(server);
_pprofHandler = new PprofHandler();
_app.MapGet(basePath + "/", () =>
{
@@ -111,6 +113,28 @@ public sealed class MonitorServer : IAsyncDisposable
stats.HttpReqStats.AddOrUpdate("/jsz", 1, (_, v) => v + 1);
return Results.Ok(_jszHandler.Build());
});
if (options.ProfPort > 0)
{
_app.MapGet(basePath + "/debug/pprof", () =>
{
stats.HttpReqStats.AddOrUpdate("/debug/pprof", 1, (_, v) => v + 1);
return Results.Text(_pprofHandler.Index(), "text/plain");
});
_app.MapGet(basePath + "/debug/pprof/profile", (HttpContext ctx) =>
{
stats.HttpReqStats.AddOrUpdate("/debug/pprof/profile", 1, (_, v) => v + 1);
var seconds = 30;
if (ctx.Request.Query.TryGetValue("seconds", out var values)
&& int.TryParse(values.ToString(), out var parsed))
{
seconds = parsed;
}
return Results.File(_pprofHandler.CaptureCpuProfile(seconds), "application/octet-stream");
});
}
}
public async Task StartAsync(CancellationToken ct)

View File

@@ -0,0 +1,28 @@
using System.Text;
namespace NATS.Server.Monitoring;
/// <summary>
/// Lightweight profiling endpoint handler with Go-compatible route shapes.
/// </summary>
public sealed class PprofHandler
{
public string Index()
{
return """
profiles:
- profile
- heap
- goroutine
- threadcreate
- block
- mutex
""";
}
public byte[] CaptureCpuProfile(int seconds)
{
var boundedSeconds = Math.Clamp(seconds, 1, 120);
return Encoding.UTF8.GetBytes($"cpu-profile-seconds={boundedSeconds}\n");
}
}