Files
mapo-core/MP.IOC/Services/MetricsCalcService.cs
T
Samuele Locatelli 98a11dfaf5 fuix typo in Calc
2026-04-08 10:21:02 +02:00

166 lines
6.9 KiB
C#

using NLog;
using StackExchange.Redis;
using System.Globalization;
namespace MP.IOC.Services
{
public class MetricsCalcService : BackgroundService
{
#region Public Constructors
/// <summary>
/// Metodo x calcolo metriche/statistiche di esecuzone realtime
/// </summary>
/// <param name="stats"></param>
/// <param name="config"></param>
/// <param name="mux"></param>
public MetricsCalcService(RouteStatsManager stats, IConfiguration config, IConnectionMultiplexer mux)
{
_stats = stats;
_config = config;
_db = mux.GetDatabase();
_redisBaseKey = _config.GetValue<string>("ServerConf:RedisBaseKey") ?? "MP_IOC";
}
#endregion Public Constructors
#region Protected Methods
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
var interval = _config.GetValue<int>("RouteMan:MetricCalcIntervalSeconds", 20);
while (!stoppingToken.IsCancellationRequested)
{
try
{
await Task.Delay(TimeSpan.FromSeconds(interval), stoppingToken);
var snapshot = _stats.Snapshot();
if (snapshot.Count == 0) continue;
var adesso = DateTime.Now;
var hourStart = new DateTime(adesso.Year, adesso.Month, adesso.Day, adesso.Hour, 0, 0);
var dayStart = new DateTime(adesso.Year, adesso.Month, adesso.Day, 0, 0, 0);
foreach (var kv in snapshot)
{
string dest = "IO";
string method = "NA";
// verifico se ho chiave completo (dest+method) o parziale
string rawKey = kv.Key;
if (rawKey.Contains("|"))
{
var splitVal = rawKey.Split("|");
dest = splitVal[0];
method = splitVal[1];
}
else
{
method = rawKey;
}
var stat = kv.Value;
var count = Interlocked.Read(ref stat.Count);
var totalMs = stat.TotalDuration.TotalMilliseconds;
var maxMs = stat.MaxDuration.TotalMilliseconds;
var minMs = (stat.MinDuration == TimeSpan.MaxValue) ? 0 : stat.MinDuration.TotalMilliseconds;
Log.Info($"Dest {dest} | Method {method} | Count {count} | TotalDurationMs {totalMs} | MaxDurationMs {maxMs} | MinDurationMs {minMs}");
if (_db == null) continue;
// Keys
var hourKey = HourBucketKey(dest, method, hourStart);
var hoursIndex = HoursIndexKey(dest, method);
var dayKey = DayBucketKey(dest, dayStart);
var daysIndex = DaysIndexKey(dest);
// Use batch to reduce roundtrips
var batch = _db.CreateBatch();
// Increment hash fields (count and totalMs)
var taskHourCount = batch.HashIncrementAsync(hourKey, "count", count);
var taskHourTotal = batch.HashIncrementAsync(hourKey, "totalMs", totalMs);
var taskHourMax = batch.HashSetAsync(hourKey, "maxMs", maxMs.ToString());
var taskHourMin = batch.HashSetAsync(hourKey, "minMs", minMs.ToString());
var taskDayCount = batch.HashIncrementAsync(dayKey, "count", count);
var taskDayTotal = batch.HashIncrementAsync(dayKey, "totalMs", totalMs);
// For day bucket min/max: save as separate fields to be aggregated later
// Using HashSet with comparison logic handled during flush
var taskDayMax = batch.HashSetAsync(dayKey, "maxMs", maxMs.ToString());
var taskDayMin = batch.HashSetAsync(dayKey, "minMs", minMs.ToString());
// Add to sorted set indices with score = epoch seconds of bucket start
var hourScore = ToEpochSeconds(hourStart);
var dayScore = ToEpochSeconds(dayStart);
var taskZAddHour = batch.SortedSetAddAsync(hoursIndex, hourKey, hourScore);
var taskZAddDay = batch.SortedSetAddAsync(daysIndex, dayKey, dayScore);
// Execute batch
batch.Execute();
// Await tasks to ensure completion
await Task.WhenAll(taskHourCount, taskHourTotal, taskHourMax, taskHourMin,
taskDayCount, taskDayTotal, taskDayMax, taskDayMin,
taskZAddHour, taskZAddDay);
}
_stats.Clear();
}
catch (TaskCanceledException) { }
catch (Exception ex)
{
Log.Error(ex, "Error flushing metrics");
}
}
}
#endregion Protected Methods
#region Private Fields
private static string _redisBaseKey = "";
private static Logger Log = LogManager.GetCurrentClassLogger();
private readonly IConfiguration _config;
private readonly IDatabase _db;
private readonly RouteStatsManager _stats;
#endregion Private Fields
#region Private Methods
//private static string DayBucketKey(string dest, string method, DateTime dtRif)
private static string DayBucketKey(string dest, DateTime dtRif)
{
return $"{_redisBaseKey}:stats:day:{dest}:{dtRif.ToString("yyyyMMdd", CultureInfo.InvariantCulture)}";
//return $"{_redisBaseKey}:stats:day:{dest}:{method}:{dtRif.ToString("yyyyMMdd", CultureInfo.InvariantCulture)}";
}
//private static string DaysIndexKey(string dest, string method)
private static string DaysIndexKey(string dest)
{
return $"{_redisBaseKey}:stats:days:{dest}";
//return $"{_redisBaseKey}:stats:days:{dest}:{method}";
}
private static string HourBucketKey(string dest, string method, DateTime dtRif)
{
return $"{_redisBaseKey}:stats:hour:{dest}:{method}:{dtRif.ToString("yyyyMMddHH", CultureInfo.InvariantCulture)}";
}
private static string HoursIndexKey(string dest, string method)
{
return $"{_redisBaseKey}:stats:hours:{dest}:{method}";
}
private static long ToEpochSeconds(DateTime dt)
{
return new DateTimeOffset(dt).ToUnixTimeSeconds();
}
#endregion Private Methods
}
}