save development progress
This commit is contained in:
@@ -0,0 +1,195 @@
|
||||
// -----------------------------------------------------------------------------
|
||||
// ConcelierCacheMetrics.cs
|
||||
// Sprint: SPRINT_8200_0013_0001_GW_valkey_advisory_cache
|
||||
// Task: VCACHE-8200-027, VCACHE-8200-028
|
||||
// Description: OpenTelemetry metrics for cache operations
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
using System.Diagnostics;
|
||||
using System.Diagnostics.Metrics;
|
||||
|
||||
namespace StellaOps.Concelier.Cache.Valkey;
|
||||
|
||||
/// <summary>
|
||||
/// Metrics instrumentation for the Concelier advisory cache.
|
||||
/// </summary>
|
||||
public sealed class ConcelierCacheMetrics : IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// Activity source name for cache operations.
|
||||
/// </summary>
|
||||
public const string ActivitySourceName = "StellaOps.Concelier.Cache";
|
||||
|
||||
/// <summary>
|
||||
/// Meter name for cache metrics.
|
||||
/// </summary>
|
||||
public const string MeterName = "StellaOps.Concelier.Cache";
|
||||
|
||||
private readonly Meter _meter;
|
||||
private readonly Counter<long> _hitsCounter;
|
||||
private readonly Counter<long> _missesCounter;
|
||||
private readonly Counter<long> _evictionsCounter;
|
||||
private readonly Histogram<double> _latencyHistogram;
|
||||
private readonly ObservableGauge<long> _hotSetSizeGauge;
|
||||
|
||||
private long _lastKnownHotSetSize;
|
||||
|
||||
/// <summary>
|
||||
/// Activity source for tracing cache operations.
|
||||
/// </summary>
|
||||
public static ActivitySource ActivitySource { get; } = new(ActivitySourceName, "1.0.0");
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of <see cref="ConcelierCacheMetrics"/>.
|
||||
/// </summary>
|
||||
public ConcelierCacheMetrics()
|
||||
{
|
||||
_meter = new Meter(MeterName, "1.0.0");
|
||||
|
||||
_hitsCounter = _meter.CreateCounter<long>(
|
||||
"concelier_cache_hits_total",
|
||||
unit: "{hits}",
|
||||
description: "Total number of cache hits");
|
||||
|
||||
_missesCounter = _meter.CreateCounter<long>(
|
||||
"concelier_cache_misses_total",
|
||||
unit: "{misses}",
|
||||
description: "Total number of cache misses");
|
||||
|
||||
_evictionsCounter = _meter.CreateCounter<long>(
|
||||
"concelier_cache_evictions_total",
|
||||
unit: "{evictions}",
|
||||
description: "Total number of cache evictions");
|
||||
|
||||
_latencyHistogram = _meter.CreateHistogram<double>(
|
||||
"concelier_cache_latency_ms",
|
||||
unit: "ms",
|
||||
description: "Cache operation latency in milliseconds");
|
||||
|
||||
_hotSetSizeGauge = _meter.CreateObservableGauge(
|
||||
"concelier_cache_hot_set_size",
|
||||
() => _lastKnownHotSetSize,
|
||||
unit: "{entries}",
|
||||
description: "Current number of entries in the hot advisory set");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Records a cache hit.
|
||||
/// </summary>
|
||||
public void RecordHit() => _hitsCounter.Add(1);
|
||||
|
||||
/// <summary>
|
||||
/// Records a cache miss.
|
||||
/// </summary>
|
||||
public void RecordMiss() => _missesCounter.Add(1);
|
||||
|
||||
/// <summary>
|
||||
/// Records a cache eviction.
|
||||
/// </summary>
|
||||
/// <param name="reason">The reason for eviction.</param>
|
||||
public void RecordEviction(string reason = "ttl")
|
||||
{
|
||||
_evictionsCounter.Add(1, new KeyValuePair<string, object?>("reason", reason));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Records operation latency.
|
||||
/// </summary>
|
||||
/// <param name="milliseconds">Latency in milliseconds.</param>
|
||||
/// <param name="operation">The operation type (get, set, invalidate).</param>
|
||||
public void RecordLatency(double milliseconds, string operation)
|
||||
{
|
||||
_latencyHistogram.Record(milliseconds, new KeyValuePair<string, object?>("operation", operation));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the hot set size gauge.
|
||||
/// </summary>
|
||||
/// <param name="size">Current hot set size.</param>
|
||||
public void UpdateHotSetSize(long size)
|
||||
{
|
||||
_lastKnownHotSetSize = size;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Starts an activity for tracing a cache operation.
|
||||
/// </summary>
|
||||
/// <param name="operationName">Name of the operation.</param>
|
||||
/// <returns>The activity, or null if tracing is disabled.</returns>
|
||||
public static Activity? StartActivity(string operationName)
|
||||
{
|
||||
return ActivitySource.StartActivity(operationName, ActivityKind.Internal);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Starts an activity with tags.
|
||||
/// </summary>
|
||||
/// <param name="operationName">Name of the operation.</param>
|
||||
/// <param name="tags">Tags to add to the activity.</param>
|
||||
/// <returns>The activity, or null if tracing is disabled.</returns>
|
||||
public static Activity? StartActivity(string operationName, params (string Key, object? Value)[] tags)
|
||||
{
|
||||
var activity = ActivitySource.StartActivity(operationName, ActivityKind.Internal);
|
||||
if (activity is not null)
|
||||
{
|
||||
foreach (var (key, value) in tags)
|
||||
{
|
||||
activity.SetTag(key, value);
|
||||
}
|
||||
}
|
||||
return activity;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Dispose()
|
||||
{
|
||||
_meter.Dispose();
|
||||
ActivitySource.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extension methods for timing cache operations.
|
||||
/// </summary>
|
||||
public static class CacheMetricsExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Times an async operation and records the latency.
|
||||
/// </summary>
|
||||
public static async Task<T> TimeAsync<T>(
|
||||
this ConcelierCacheMetrics metrics,
|
||||
string operation,
|
||||
Func<Task<T>> action)
|
||||
{
|
||||
var sw = Stopwatch.StartNew();
|
||||
try
|
||||
{
|
||||
return await action().ConfigureAwait(false);
|
||||
}
|
||||
finally
|
||||
{
|
||||
sw.Stop();
|
||||
metrics.RecordLatency(sw.Elapsed.TotalMilliseconds, operation);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Times an async operation and records the latency.
|
||||
/// </summary>
|
||||
public static async Task TimeAsync(
|
||||
this ConcelierCacheMetrics metrics,
|
||||
string operation,
|
||||
Func<Task> action)
|
||||
{
|
||||
var sw = Stopwatch.StartNew();
|
||||
try
|
||||
{
|
||||
await action().ConfigureAwait(false);
|
||||
}
|
||||
finally
|
||||
{
|
||||
sw.Stop();
|
||||
metrics.RecordLatency(sw.Elapsed.TotalMilliseconds, operation);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user