update parziale DbFlushService

This commit is contained in:
Samuele Locatelli
2026-04-08 11:29:45 +02:00
parent b4291bad33
commit 8bb31472df
+65 -18
View File
@@ -12,6 +12,7 @@ namespace MP.IOC.Services
private readonly RouteStatsManager _stats;
private readonly IConfiguration _config;
private readonly IDatabase _db;
private readonly IConnectionMultiplexer _mux;
private static readonly Logger Log = LogManager.GetCurrentClassLogger();
public MetricsDbFlushService(
@@ -23,6 +24,7 @@ namespace MP.IOC.Services
_stats = stats;
_scopeFactory = scopeFactory;
_config = config;
_mux = mux;
_db = mux.GetDatabase();
}
@@ -79,11 +81,11 @@ namespace MP.IOC.Services
// Get all hours index keys by pattern matching on existing indices
var dests = await GetAllDestinationsAsync(baseKey);
foreach (var dest in dests)
{
var hourIndexKey = $"{baseKey}:stats:hours:{dest}:*";
try
{
var allKeys = await _db.KeysAsync(hourIndexKey, 0, -1);
@@ -92,49 +94,49 @@ namespace MP.IOC.Services
if (!key.IsNullOrEmpty)
{
allHourIndexKeys.Add(key.ToString());
// Read bucket keys from sorted set index
var bucketKeys = await _db.SortedSetRangeByRankAsync(key, 0, -1);
foreach (var bucketKeyRedis in bucketKeys)
{
if (bucketKeyRedis.IsNullOrEmpty) continue;
var bucketStr = bucketKeyRedis.ToString();
var parts = bucketStr.Split(':');
if (parts.Length < 7) continue;
// Parse date from key: {base}:stats:hour:{dest}:{method}:{yyyyMMddHH}
var dayHourPart = string.Join(":", parts.Skip(6));
if (!DateTime.TryParseExact(dayHourPart, "yyyyMMddHH",
if (!DateTime.TryParseExact(dayHourPart, "yyyyMMddHH",
CultureInfo.InvariantCulture, DateTimeStyles.None, out var hourFromKey))
continue;
if (hourFromKey > cutoff) continue;
// Read hash data from bucket
var hashData = await _db.HashGetAllAsync(bucketKeyRedis);
if (hashData.Length == 0) continue;
var countVal = hashData.FirstOrDefault(f => f.Name.ToString() == "count");
var totalMsVal = hashData.FirstOrDefault(f => f.Name.ToString() == "totalMs");
var maxMsVal = hashData.FirstOrDefault(f => f.Name.ToString() == "maxMs");
var minMsVal = hashData.FirstOrDefault(f => f.Name.ToString() == "minMs");
if (countVal.Value.IsNullOrEmpty || totalMsVal.Value.IsNullOrEmpty) continue;
var count = long.TryParse(countVal.Value.ToString(), out var cp) ? cp : 0;
var totalMs = double.TryParse(totalMsVal.Value.ToString(), out var tp) ? tp : 0;
var maxMs = double.TryParse(maxMsVal.Value.ToString(), out var mp) ? mp : 0;
var minMs = double.TryParse(minMsVal.Value.ToString(), out var mip) ? mip : 0;
if (count == 0) continue;
var avgDuration = totalMs / count;
detailRecordsToInsert.Add(new StatsDetailModel
{
Destination = dest,
@@ -659,6 +661,51 @@ namespace MP.IOC.Services
return methods.ToList();
}
/// <summary>
/// Recupera tutte le chiavi dati gli indici
/// </summary>
/// <param name="baseKey"></param>
/// <param name="limit"></param>
/// <returns></returns>
public async Task<List<RedisKey>> GetDayIndicesAsync(string baseKey, int limit = 10000)
{
var allDayIndices = new List<RedisKey>();
var pattern = $"{baseKey}:stats:days:*"; // Assicurati che il pattern sia corretto
// 1. Otteniamo tutti gli endpoint (in caso di cluster o replica, potrebbero essere più di uno)
var endpoints = _mux.GetEndPoints();
foreach (var endpoint in endpoints)
{
// 2. Otteniamo l'oggetto Server per quell'endpoint
var server = _mux.GetServer(endpoint);
// 3. Usiamo KeysAsync che restituisce un IAsyncEnumerable<RedisKey>
// Questo metodo gestisce internamente il comando SCAN e il cursore di Redis!
try
{
// L'iterazione è asincrona e molto efficiente
await foreach (var key in server.KeysAsync(pattern: pattern))
{
//if (key != null)
//{
//}
allDayIndices.Add(key);
// Applichiamo il limite di sicurezza che avevi impostato
if (allDayIndices.Count >= limit)
break;
}
}
catch (RedisException ex)
{
Log.Error(ex, $"Errore durante la scansione sull'endpoint {endpoint}");
}
}
return allDayIndices;
}
private async Task<List<string>> GetExpiredHourKeysAsync(string method, DateTime cutoff)
{
var keys = new List<string>();