using Fleet.Core;
using MoShiro.Backend.Analysis;
using MoShiro.Protocol;
namespace MoShiro.Backend.Application;
/// 请求的指标:导出 id(rtt.p95)与评分 id(score.mc)。
public sealed record MetricSelection(IReadOnlyList ExportIds, IReadOnlyList ScoreModelIds)
{
public bool WantsScores => ScoreModelIds.Count > 0;
public IEnumerable AllIds => ExportIds.Concat(ScoreModelIds.Select(m => "score." + m));
public static Result Resolve(string? metrics, string? presets, int maxMetrics)
{
var ids = new List();
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.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.Fail(MoShiroErrors.TooManyMetrics, $"at most {maxMetrics} metrics per request");
}
var exports = new List();
var scores = new List();
foreach (string id in ids)
{
if (id.StartsWith("score.", StringComparison.Ordinal))
{
string model = id["score.".Length..];
if (ModelRegistry.Find(model) is null)
{
return Result.Fail(MoShiroErrors.UnknownMetric, $"unknown metric {id}");
}
scores.Add(model);
}
else if (MetricRegistry.TryFindExport(id, out _, out _))
{
exports.Add(id);
}
else
{
return Result.Fail(MoShiroErrors.UnknownMetric, $"unknown metric {id}");
}
}
return Result.Ok(new MetricSelection(exports, scores));
}
}
public static class ExportEvaluator
{
/// 导出指定的值;没有样本的指标不出现在结果里。
public static Dictionary Evaluate(MetricSet set, IEnumerable exportIds, double durationSeconds)
{
var values = new Dictionary(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 AllExportIds() => MetricRegistry.AllExports().Select(e => e.Export.Id);
}