Fix flux orario x RIOC
This commit is contained in:
@@ -66,6 +66,59 @@ namespace MP.RIOC.Services
|
||||
|
||||
#region Private Methods
|
||||
|
||||
/// <summary>
|
||||
/// Cancellazione ricorsiva chiavi ausiliarie (:status, :errors) e rimozione dall'indice
|
||||
/// </summary>
|
||||
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 { }
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Recupera il TTL residuo di una chiave Redis (-1 = nessun TTL, -2 = chiave non esiste)
|
||||
/// </summary>
|
||||
private TimeSpan? GetKeyTtl(RedisKey key)
|
||||
{
|
||||
try
|
||||
{
|
||||
return _db.KeyTimeToLive(key);
|
||||
}
|
||||
catch { return null; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Processing dati giornalieri (da Redis a DB)
|
||||
/// </summary>
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Recupera il TTL residuo di una chiave Redis (-1 = nessun TTL, -2 = chiave non esiste)
|
||||
/// </summary>
|
||||
private TimeSpan? GetKeyTtl(RedisKey key)
|
||||
{
|
||||
try
|
||||
{
|
||||
return _db.KeyTimeToLive(key);
|
||||
}
|
||||
catch { return null; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cancellazione ricorsiva chiavi ausiliarie (:status, :errors) e rimozione dall'indice
|
||||
/// </summary>
|
||||
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 { }
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Processing dati orari (da Redis a DB)
|
||||
/// </summary>
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user