From 5175af07694ddf8450dc0b3ca8c0a1db07ef3001 Mon Sep 17 00:00:00 2001 From: Samuele Locatelli Date: Thu, 11 Jun 2026 17:46:05 +0200 Subject: [PATCH] Fix flux orario x RIOC --- MP.RIOC/Services/MetricsDbFlushService.cs | 177 ++++++++-------------- 1 file changed, 61 insertions(+), 116 deletions(-) diff --git a/MP.RIOC/Services/MetricsDbFlushService.cs b/MP.RIOC/Services/MetricsDbFlushService.cs index 10252886..c268e6d0 100644 --- a/MP.RIOC/Services/MetricsDbFlushService.cs +++ b/MP.RIOC/Services/MetricsDbFlushService.cs @@ -66,6 +66,59 @@ namespace MP.RIOC.Services #region Private Methods + /// + /// Cancellazione ricorsiva chiavi ausiliarie (:status, :errors) e rimozione dall'indice + /// + private async Task DeleteAuxKeysAndIndexAsync(RedisKey sKey, RedisKey indexKey, IBatch batch) + { + string sKeyStr = sKey.ToString(); + string keyDir = sKeyStr.Substring(0, sKeyStr.LastIndexOf(':')); + + // Cancella :status dal sorted set e dalla hash + string statusKey = keyDir + ":status"; + await batch.SortedSetRemoveAsync(indexKey, statusKey); + await batch.KeyDeleteAsync(statusKey); + + // Cancella :errors dal sorted set e dalla hash + string errorKey = keyDir + ":errors"; + await batch.SortedSetRemoveAsync(indexKey, errorKey); + await batch.KeyDeleteAsync(errorKey); + + // Cancella chiave ausiliaria :status anche da un eventuale indice days se presente + if (statusKey.Contains(":stats:hours:")) + { + string daysIndex = statusKey.Replace(":stats:hours:", ":stats:days:"); + try + { + await batch.SortedSetRemoveAsync(daysIndex, statusKey); + } + catch { } + } + + // Cancella chiave ausiliaria :errors anche da un eventuale indice days se presente + if (errorKey.Contains(":stats:hours:")) + { + string daysIndex = errorKey.Replace(":stats:hours:", ":stats:days:"); + try + { + await batch.SortedSetRemoveAsync(daysIndex, errorKey); + } + catch { } + } + } + + /// + /// Recupera il TTL residuo di una chiave Redis (-1 = nessun TTL, -2 = chiave non esiste) + /// + private TimeSpan? GetKeyTtl(RedisKey key) + { + try + { + return _db.KeyTimeToLive(key); + } + catch { return null; } + } + /// /// Processing dati giornalieri (da Redis a DB) /// @@ -114,7 +167,7 @@ namespace MP.RIOC.Services // Verifica se la chiave è "orfana" (nessun TTL o TTL troppo lungo >30gg) var keyTtl = GetKeyTtl(sKey); - bool isOrphanKey = keyTtl?.TotalSeconds < 0 || keyTtl?.TotalSeconds > 30.25 * 24 * 3600; + bool isOrphanKey = keyTtl?.TotalSeconds < 0 || keyTtl?.TotalHours > 30.25 * 24; // Se era scaduta o orfana e abbiamo il permesso, segnamola per la cancellazione if ((meta.Timestamp < currentDayStart || isOrphanKey) && deleteConfirmed) @@ -199,59 +252,6 @@ namespace MP.RIOC.Services } } - /// - /// Recupera il TTL residuo di una chiave Redis (-1 = nessun TTL, -2 = chiave non esiste) - /// - private TimeSpan? GetKeyTtl(RedisKey key) - { - try - { - return _db.KeyTimeToLive(key); - } - catch { return null; } - } - - /// - /// Cancellazione ricorsiva chiavi ausiliarie (:status, :errors) e rimozione dall'indice - /// - private async Task DeleteAuxKeysAndIndexAsync(RedisKey sKey, RedisKey indexKey, IBatch batch) - { - string sKeyStr = sKey.ToString(); - string keyDir = sKeyStr.Substring(0, sKeyStr.LastIndexOf(':')); - - // Cancella :status dal sorted set e dalla hash - string statusKey = keyDir + ":status"; - await batch.SortedSetRemoveAsync(indexKey, statusKey); - await batch.KeyDeleteAsync(statusKey); - - // Cancella :errors dal sorted set e dalla hash - string errorKey = keyDir + ":errors"; - await batch.SortedSetRemoveAsync(indexKey, errorKey); - await batch.KeyDeleteAsync(errorKey); - - // Cancella chiave ausiliaria :status anche da un eventuale indice days se presente - if (statusKey.Contains(":stats:hours:")) - { - string daysIndex = statusKey.Replace(":stats:hours:", ":stats:days:"); - try - { - await batch.SortedSetRemoveAsync(daysIndex, statusKey); - } - catch { } - } - - // Cancella chiave ausiliaria :errors anche da un eventuale indice days se presente - if (errorKey.Contains(":stats:hours:")) - { - string daysIndex = errorKey.Replace(":stats:hours:", ":stats:days:"); - try - { - await batch.SortedSetRemoveAsync(daysIndex, errorKey); - } - catch { } - } - } - /// /// Processing dati orari (da Redis a DB) /// @@ -299,10 +299,9 @@ namespace MP.RIOC.Services var sKey = (RedisKey)$"{statKey}"; if (!TryParseKeyMetadata(sKey, out var meta) || !meta.IsHourType) continue; - //// Verifica se la chiave è "orfana" (nessun TTL o TTL troppo lungo) - //var keyTtl = await _db.KeyTtlAsync(sKey); - //bool isOrphanKey = keyTtl?.TotalSeconds < 0 || keyTtl?.TotalSeconds > 30.25 * 24 * 3600; - bool isOrphanKey = false; + // Verifica se la chiave è "orfana" (nessun TTL o TTL troppo lungo >30gg) + var keyTtl = GetKeyTtl(sKey); + bool isOrphanKey = keyTtl?.TotalSeconds < 0 || keyTtl?.TotalHours > 30.25 * 24; // Se era scaduta o orfana e abbiamo il permesso, segnamola per la cancellazione if ((meta.Timestamp < currentHourStart || isOrphanKey) && deleteConfirmed) @@ -379,13 +378,14 @@ namespace MP.RIOC.Services int deletedCount = 0; foreach (var key in keysToDelete) { - _ = batch.KeyDeleteAsync(key); + await batch.KeyDeleteAsync(key); deletedCount++; } batch.Execute(); Log.Info($"[CLEANUP HOUR] Deleted {deletedCount} expired metric keys from Redis"); } } + private bool TryParseKeyMetadata(RedisKey key, out KeyMeta meta) { meta = new KeyMeta(); @@ -414,60 +414,7 @@ namespace MP.RIOC.Services catch { return false; } } -#if false - private bool TryParseKeyMetadata(RedisKey key, out string dest, out string method, out string machId, out DateTime timestamp, out bool isHourType) - { - dest = "NA"; - method = "NA"; - machId = "ALL"; - timestamp = DateTime.MinValue; - isHourType = true; - try - { - string k = key.ToString(); - string relativeKey = k.Replace($"{_redisBaseKey}:", ""); - var parts = relativeKey.Split(':'); - - if (parts.Length < 4) return false; - - string type = parts[1]; // "hour" o "day" - dest = parts[2]; - - if (type == "hour") - { - isHourType = true; - method = parts[3]; - if (parts.Length >= 5 && DateTime.TryParseExact(parts[4], "yyyyMMddHH", CultureInfo.InvariantCulture, DateTimeStyles.None, out var dt)) - { - timestamp = dt; - return true; - } - } - else if (type == "day") - { - isHourType = false; - method = "DAILY"; - string rawDate = ""; - if (parts.Length >= 5) - { - machId = parts[3]; - rawDate = parts[4]; - } - else - { - rawDate = parts[3]; - } - if (DateTime.TryParseExact(rawDate, "yyyyMMdd", CultureInfo.InvariantCulture, DateTimeStyles.None, out var dt)) - { - timestamp = dt; - return true; - } - } - } - catch { } - return false; - } -#endif + #endregion Private Methods private record KeyMeta { @@ -477,7 +424,5 @@ namespace MP.RIOC.Services public DateTime Timestamp = DateTime.MinValue; public bool IsHourType = true; } - - #endregion Private Methods } -} +} \ No newline at end of file