shared: add independent IPv4 and IPv6 collection [CI]
This commit is contained in:
1 parent
c974a7dddb
commit
0eda0600cc
60 files changed
+699
-137
No files matched your search
@@ -3,35 +3,35 @@ using MoShiro.Protocol;
|
||||
|
||||
namespace MoShiro.Backend.Application;
|
||||
|
||||
/// <summary>一条线路 = 一对(Agent,Probe)。存储里它的序列是 <c>line:<probe_id></c>,对外的 id 是 <c><agent_id>~<probe_id></c>。</summary>
|
||||
public readonly record struct LineKey(NodeId Agent, ProbeId Probe)
|
||||
/// <summary>线路按(Agent,Probe,地址族)隔离。IPv4 保留 <c>agent~probe</c> 和 <c>line:probe</c>;IPv6 分别追加 <c>~ipv6</c>、<c>:ipv6</c>。</summary>
|
||||
public readonly record struct LineKey(NodeId Agent, ProbeId Probe, string IpVersion = IpVersions.V4)
|
||||
{
|
||||
public string Series => RawChunkHeader.LinePrefix + Probe.Value;
|
||||
public string Series => IpVersions.LineSeries(Probe.Value, IpVersion);
|
||||
|
||||
public string Id => Agent.Value + "~" + Probe.Value;
|
||||
public string Id => Agent.Value + "~" + Probe.Value + (IpVersion == IpVersions.V6 ? "~ipv6" : "");
|
||||
|
||||
public static bool TryParse(string text, out LineKey key)
|
||||
{
|
||||
int split = text.IndexOf('~', StringComparison.Ordinal);
|
||||
if (split <= 0 || split == text.Length - 1)
|
||||
string[] parts = text.Split('~');
|
||||
if (parts.Length is < 2 or > 3 || parts[0].Length == 0 || parts[1].Length == 0 || (parts.Length == 3 && parts[2] != IpVersions.V6))
|
||||
{
|
||||
key = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
key = new LineKey(new NodeId(text[..split]), new ProbeId(text[(split + 1)..]));
|
||||
key = new LineKey(new NodeId(parts[0]), new ProbeId(parts[1]), parts.Length == 3 ? IpVersions.V6 : IpVersions.V4);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>一条线路的展示视图:Agent 的状态、Probe 记录,以及这一对现在是否连着。</summary>
|
||||
public sealed record LineView(AgentView Agent, ProbeRecord Probe)
|
||||
public sealed record LineView(AgentView Agent, ProbeRecord Probe, string IpVersion = IpVersions.V4)
|
||||
{
|
||||
public LineKey Key => new(Agent.Agent.Id, new ProbeId(Probe.Id));
|
||||
public LineKey Key => new(Agent.Agent.Id, new ProbeId(Probe.Id), IpVersion);
|
||||
|
||||
public bool Connected => Agent.Probes.Any(p => p.Id == Probe.Id);
|
||||
public bool Connected => Agent.Probes.Any(p => p.Id == Probe.Id && p.IpVersion == IpVersion);
|
||||
|
||||
public ConnectedProbeInfo? Connection => Agent.Probes.FirstOrDefault(p => p.Id == Probe.Id);
|
||||
public ConnectedProbeInfo? Connection => Agent.Probes.FirstOrDefault(p => p.Id == Probe.Id && p.IpVersion == IpVersion);
|
||||
}
|
||||
|
||||
/// <summary>线路目录:把 id 解析成(Agent,Probe),列出所有线路。</summary>
|
||||
@@ -54,7 +54,20 @@ public sealed class LineService
|
||||
IReadOnlyList<AgentView> agents = await _agents.ListAsync(ct);
|
||||
IReadOnlyList<ProbeRecord> probes = [.. (await _probes.ListAsync(ct)).Where(p => p.RevokedAtMs is null)];
|
||||
LinkSnapshot links = await _links.SnapshotAsync(ct);
|
||||
return [.. agents.Where(include).SelectMany(a => probes.Where(p => links.IsLinked(p.Id, a.Agent.Id.Value)).Select(p => new LineView(a, p)))];
|
||||
var result = new List<LineView>();
|
||||
foreach (AgentView agent in agents.Where(include))
|
||||
{
|
||||
foreach (ProbeRecord probe in probes.Where(p => links.IsLinked(p.Id, agent.Agent.Id.Value)))
|
||||
{
|
||||
result.Add(new LineView(agent, probe));
|
||||
if (agent.Agent.Ipv6Host.Length > 0)
|
||||
{
|
||||
result.Add(new LineView(agent, probe, IpVersions.V6));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -64,6 +77,7 @@ public sealed class LineService
|
||||
{
|
||||
string agentPart = id;
|
||||
string? probePart = null;
|
||||
string ipVersion = IpVersions.V4;
|
||||
if (id.Contains('~', StringComparison.Ordinal))
|
||||
{
|
||||
if (!LineKey.TryParse(id, out LineKey key))
|
||||
@@ -73,6 +87,7 @@ public sealed class LineService
|
||||
|
||||
agentPart = key.Agent.Value;
|
||||
probePart = key.Probe.Value;
|
||||
ipVersion = key.IpVersion;
|
||||
}
|
||||
|
||||
AgentView? agent = await _agents.GetAsync(new NodeId(agentPart), ct);
|
||||
@@ -102,6 +117,6 @@ public sealed class LineService
|
||||
probe = active[0];
|
||||
}
|
||||
|
||||
return Result<LineView>.Ok(new LineView(agent, probe));
|
||||
return Result<LineView>.Ok(new LineView(agent, probe, ipVersion));
|
||||
}
|
||||
}
|
||||
@@ -167,7 +167,7 @@ public sealed class QueryService
|
||||
}
|
||||
|
||||
IReadOnlyDictionary<string, double> overlay = selection.WantsScores ? await TestOverlayAsync(line.Key, ct) : new Dictionary<string, double>();
|
||||
IReadOnlyList<TestRecord> recent = await _tests.ListAsync(line.Key.Agent, line.Key.Probe, 1, ct);
|
||||
IReadOnlyList<TestRecord> recent = await _tests.ListAsync(line.Key.Agent, line.Key.Probe, 1, line.Key.IpVersion, ct);
|
||||
result.Add(new OverviewLine(line, Summarize(merged, count, p.Plan, p.Range, selection, overlay), recent.Count > 0 ? recent[0] : null));
|
||||
}
|
||||
|
||||
@@ -372,7 +372,7 @@ public sealed class QueryService
|
||||
long cutoff = _time.GetUtcNow().ToUnixTimeMilliseconds() - (7 * Steps.Day);
|
||||
var overlay = new Dictionary<string, double>(StringComparer.Ordinal);
|
||||
var seenPlans = new HashSet<string>();
|
||||
foreach (TestRecord test in await _tests.ListAsync(line.Agent, line.Probe, 20, ct))
|
||||
foreach (TestRecord test in await _tests.ListAsync(line.Agent, line.Probe, 20, line.IpVersion, ct))
|
||||
{
|
||||
if (test.Status != TestStatuses.Succeeded || test.FinishedAtMs is not { } finished || finished < cutoff || !seenPlans.Add(test.Plan))
|
||||
{
|
||||
|
||||
@@ -139,7 +139,8 @@ public sealed class RowLoader
|
||||
IReadOnlyList<MetricRow> all = await _metrics.ReadAgentsAsync(ToLevel(plan.Source), agents, bySeries.Key, range.FromMs, range.ToMs, ct);
|
||||
foreach (IGrouping<NodeId, MetricRow> group in all.GroupBy(r => r.AgentId))
|
||||
{
|
||||
result[new LineKey(group.Key, new ProbeId(bySeries.Key[RawChunkHeader.LinePrefix.Length..]))] = Decode([.. group]);
|
||||
LineKey key = bySeries.First(l => l.Agent == group.Key);
|
||||
result[key] = Decode([.. group]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user