ci/woodpecker/push/main-amd64-build Pipeline was successful
Namespaces, assemblies, solution, binaries, images, systemd units, config paths, environment variables, protocol methods and the Linux user are all renamed. Breaking: existing databases, node identities and installed units must be recreated.
92 lines
3.3 KiB
C#
92 lines
3.3 KiB
C#
using Fleet.Core;
|
||
using MoShiro.Backend.Analysis;
|
||
using MoShiro.Protocol;
|
||
|
||
namespace MoShiro.Backend.Application;
|
||
|
||
/// <summary>请求的指标:导出 id(<c>rtt.p95</c>)与评分 id(<c>score.mc</c>)。</summary>
|
||
public sealed record MetricSelection(IReadOnlyList<string> ExportIds, IReadOnlyList<string> ScoreModelIds)
|
||
{
|
||
public bool WantsScores => ScoreModelIds.Count > 0;
|
||
|
||
public IEnumerable<string> AllIds => ExportIds.Concat(ScoreModelIds.Select(m => "score." + m));
|
||
|
||
public static Result<MetricSelection> Resolve(string? metrics, string? presets, int maxMetrics)
|
||
{
|
||
var ids = new List<string>();
|
||
if (presets is not null)
|
||
{
|
||
foreach (string presetId in presets.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries))
|
||
{
|
||
PresetDef? preset = MetricRegistry.FindPreset(presetId);
|
||
if (preset is null)
|
||
{
|
||
return Result<MetricSelection>.Fail(MoShiroErrors.UnknownPreset, $"unknown preset {presetId}");
|
||
}
|
||
|
||
ids.AddRange(preset.MetricIds);
|
||
}
|
||
}
|
||
|
||
if (metrics is not null)
|
||
{
|
||
ids.AddRange(metrics.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries));
|
||
}
|
||
|
||
ids = [.. ids.Distinct(StringComparer.Ordinal)];
|
||
if (ids.Count > maxMetrics)
|
||
{
|
||
return Result<MetricSelection>.Fail(MoShiroErrors.TooManyMetrics, $"at most {maxMetrics} metrics per request");
|
||
}
|
||
|
||
var exports = new List<string>();
|
||
var scores = new List<string>();
|
||
foreach (string id in ids)
|
||
{
|
||
if (id.StartsWith("score.", StringComparison.Ordinal))
|
||
{
|
||
string model = id["score.".Length..];
|
||
if (ModelRegistry.Find(model) is null)
|
||
{
|
||
return Result<MetricSelection>.Fail(MoShiroErrors.UnknownMetric, $"unknown metric {id}");
|
||
}
|
||
|
||
scores.Add(model);
|
||
}
|
||
else if (MetricRegistry.TryFindExport(id, out _, out _))
|
||
{
|
||
exports.Add(id);
|
||
}
|
||
else
|
||
{
|
||
return Result<MetricSelection>.Fail(MoShiroErrors.UnknownMetric, $"unknown metric {id}");
|
||
}
|
||
}
|
||
|
||
return Result<MetricSelection>.Ok(new MetricSelection(exports, scores));
|
||
}
|
||
}
|
||
|
||
public static class ExportEvaluator
|
||
{
|
||
/// <summary>导出指定的值;没有样本的指标不出现在结果里。</summary>
|
||
public static Dictionary<string, double> Evaluate(MetricSet set, IEnumerable<string> exportIds, double durationSeconds)
|
||
{
|
||
var values = new Dictionary<string, double>(StringComparer.Ordinal);
|
||
foreach (string id in exportIds)
|
||
{
|
||
if (MetricRegistry.TryFindExport(id, out MetricDef def, out MetricExport export)
|
||
&& set.TryGet(def.Id, out Accumulator acc)
|
||
&& MetricDef.Export(acc, export, durationSeconds) is { } value
|
||
&& double.IsFinite(value))
|
||
{
|
||
values[id] = value;
|
||
}
|
||
}
|
||
|
||
return values;
|
||
}
|
||
|
||
public static IEnumerable<string> AllExportIds() => MetricRegistry.AllExports().Select(e => e.Export.Id);
|
||
}
|