Files
MoShiro/backend/src/MoShiro.Backend.Analysis/Engine/WindowEvaluator.cs
T
justme 83e5120377 refactor!: rename the project from Monitor to MoShiro
Namespaces, assemblies, projects and directories, the solution, binaries,
images, systemd units, config paths, environment variables, protocol method
names, the media type and the raw chunk magic all move to MoShiro/moshiro.
Existing data and deployments are not compatible and must be recreated.
Also fix the architecture tests after the solution layout change and make
the update tests independent of their order.
2026-10-07 01:49:04 +08:00

72 lines
2.3 KiB
C#

using MoShiro.Protocol;
namespace MoShiro.Backend.Analysis;
/// <summary>
/// 把原始块按时间分桶算成累加器。流式路径(1s 桶)和持久路径(分钟窗口)调用同一份代码,
/// 所以实时值与历史值使用的是同一套口径。
/// </summary>
public sealed class WindowEvaluator
{
private readonly IReadOnlyList<MetricDef> _metrics;
public WindowEvaluator(IReadOnlyList<MetricDef> metrics)
{
_metrics = metrics;
}
public static WindowEvaluator Default { get; } = new(MetricRegistry.All);
/// <summary>
/// 评估一个块,结果合并进 <paramref name="buckets"/>(键是桶起点的 UTC 毫秒)。
/// <paramref name="only"/> 不为空时只计算这些指标(用于回填新增或变更的指标)。
/// </summary>
public void Evaluate(RawChunk chunk, long bucketMs, IDictionary<long, MetricSet> buckets, ISet<string>? only = null)
{
var view = new ChunkView(chunk);
foreach (RawStream stream in chunk.Streams)
{
foreach (MetricDef metric in _metrics)
{
if ((only is not null && !only.Contains(metric.Id)) || !metric.Matches(chunk.Header.Kind, stream.Name))
{
continue;
}
metric.Op.Evaluate(view, stream, new BucketSink(metric, bucketMs, buckets));
}
}
}
private sealed class BucketSink : IObservationSink
{
private readonly MetricDef _metric;
private readonly long _bucketMs;
private readonly IDictionary<long, MetricSet> _buckets;
public BucketSink(MetricDef metric, long bucketMs, IDictionary<long, MetricSet> buckets)
{
_metric = metric;
_bucketMs = bucketMs;
_buckets = buckets;
}
public void Emit(long utcMs, double a, double b = 0)
{
long start = Math.DivRem(utcMs, _bucketMs, out long rem) * _bucketMs;
if (rem < 0)
{
start -= _bucketMs;
}
if (!_buckets.TryGetValue(start, out MetricSet? set))
{
set = new MetricSet();
_buckets[start] = set;
}
set.GetOrAdd(_metric.Id, _metric.Kind).Add(a, b);
}
}
}