ancora reviiew procedura export verso DB

This commit is contained in:
Samuele Locatelli
2025-12-16 17:06:50 +01:00
parent 0665acdd66
commit bab8aa71af
2 changed files with 122 additions and 3 deletions
+121 -2
View File
@@ -221,7 +221,10 @@ namespace EgwCoreLib.Lux.Data.Services
});
// Chiave aggregata per slot orario
var aggHourKey = Key($"stats:agg:{DateTime.UtcNow:yyyyMMddHH}");
string dayKey = $"stats:agg:{DateTime.UtcNow:yyyyMMddHH}");
var aggHourKey = Key($"{dayKey}");
await _db.SetAddAsync("stats:agg:index", dayKey);
// --- Aggiornamento globale (ALL|ALL) ---
var globalKey = "ALL|ALL";
@@ -270,6 +273,120 @@ namespace EgwCoreLib.Lux.Data.Services
await Task.WhenAll(tasks);
}
/// <summary>
/// Espostazione statistiche da REDIS al DB
/// </summary>
/// <param name="clearRedisAfterExport"></param>
/// <returns></returns>
public async Task ExportStatsToDbAsync(bool clearRedisAfterExport = false)
{
var aggregatedData = new List<StatsAggregated>();
var detailData = new List<StatsDetail>();
// Recupero tutte le chiavi dall'indice (sono RedisValue)
RedisValue[] indexValues = await _db.SetMembersAsync("stats:agg:index");
// Ora corrente (slot da saltare)
string currentHourKey = $"stats:agg:{DateTime.UtcNow:yyyyMMddHH}";
// Lista chiavi da cancellare dopo export (uso RedisValue perché l'indice è un set di valori)
var keysToDelete = new List<RedisValue>();
foreach (RedisValue indexValue in indexValues)
{
string keyStr = indexValue.ToString();
if (keyStr == currentHourKey) continue; // salto slot corrente
// Estraggo l'ora dalla chiave
string hourStr = keyStr.Replace("stats:agg:", "");
if (!DateTime.TryParseExact(hourStr, "yyyyMMddHH", null,
System.Globalization.DateTimeStyles.None, out DateTime hour))
continue;
// Recupero tutti i valori dell'hash (qui serve RedisKey)
HashEntry[] entries = await _db.HashGetAllAsync((RedisKey)keyStr);
// --- Globali ALL|ALL ---
RedisValue sumGlobal = entries.FirstOrDefault(e => e.Name == "ALL|ALL:sum").Value;
RedisValue countGlobal = entries.FirstOrDefault(e => e.Name == "ALL|ALL:count").Value;
RedisValue maxGlobal = entries.FirstOrDefault(e => e.Name == "ALL|ALL:max").Value;
if (!sumGlobal.IsNull && !countGlobal.IsNull)
{
long count = (long)countGlobal;
double sum = (double)sumGlobal;
double max = maxGlobal.IsNull ? 0 : (double)maxGlobal;
aggregatedData.Add(new StatsAggregated
{
Hour = hour,
RequestCount = count,
AvgDuration = count > 0 ? sum / count : 0,
MaxDuration = max
});
}
// --- Dettagli per env|tipo ---
IEnumerable<string> grouped = entries
.Select(e => e.Name.ToString())
.Where(n => !n.StartsWith("ALL|ALL"))
.Select(n => n.Split(':')[0])
.Distinct();
foreach (string envTipo in grouped)
{
string[] envTipoSplit = envTipo.Split('|');
if (envTipoSplit.Length != 2) continue;
string env = envTipoSplit[0];
string tipo = envTipoSplit[1];
RedisValue sumVal = entries.FirstOrDefault(e => e.Name == $"{envTipo}:sum").Value;
RedisValue countVal = entries.FirstOrDefault(e => e.Name == $"{envTipo}:count").Value;
RedisValue maxVal = entries.FirstOrDefault(e => e.Name == $"{envTipo}:max").Value;
if (!sumVal.IsNull && !countVal.IsNull)
{
long count = (long)countVal;
double sum = (double)sumVal;
double max = maxVal.IsNull ? 0 : (double)maxVal;
detailData.Add(new StatsDetail
{
Environment = env,
Type = tipo,
Hour = hour,
RequestCount = count,
AvgDuration = count > 0 ? sum / count : 0,
MaxDuration = max
});
}
}
// Se richiesto, preparo cancellazione
if (clearRedisAfterExport)
{
keysToDelete.Add(indexValue); // RedisValue coerente
}
}
// Scrittura su DB
long aggrResult = await dbController.StatsAggrUpsertAsync(aggregatedData, true);
long detailResult = await dbController.StatsDetailUpsertAsync(detailData, true);
// Se entrambe hanno successo, eseguo batch di cancellazione
if (clearRedisAfterExport && aggrResult > 0 && detailResult > 0 && keysToDelete.Any())
{
var batch = _db.CreateBatch();
foreach (RedisValue indexValue in keysToDelete)
{
string keyStr = indexValue.ToString();
_ = batch.KeyDeleteAsync((RedisKey)keyStr); // cancellazione hash
_ = batch.SetRemoveAsync("stats:agg:index", indexValue); // rimozione dall'indice
}
batch.Execute();
}
}
public async Task<List<StatsDetail>> GetOldStatsFromRedisAsync()
@@ -319,6 +436,7 @@ namespace EgwCoreLib.Lux.Data.Services
return results;
}
#if false
public async Task AggregateAndSaveStatsAsync(TimeSpan? timeRange = null, bool deleteFromRedis = false)
{
var now = DateTime.UtcNow;
@@ -443,7 +561,8 @@ namespace EgwCoreLib.Lux.Data.Services
{
Log.Info("Nessun dato da salvare sul DB");
}
}
}
#endif
/// <summary>
/// Metodo Recupero combinazioni envir/tipo
@@ -128,7 +128,7 @@
isLoading = true;
await InvokeAsync(StateHasChanged);
TimeSpan defPeriod = TimeSpan.FromHours(24);
//await Calc.ArchiveOldStatsAsync(defPeriod, false);
await Calc.ExportStatsToDbAsync(false);
isLoading = false;
}