Test riscrittura flushredis + ottimizzazione insert remoteRebootLog

This commit is contained in:
Samuele Locatelli
2026-04-27 11:20:09 +02:00
parent 7a01793bfd
commit 1cf7a61e74
9 changed files with 136 additions and 37 deletions
File diff suppressed because one or more lines are too long
+28
View File
@@ -1051,6 +1051,34 @@ namespace MP.Data.Controllers
return await dbCtx.SaveChangesAsync() > 0;
}
/// <summary>
/// Aggiunta record RemoteRebootLog
/// </summary>
/// <param name="newRec"></param>
/// <returns></returns>
public async Task<bool> RemRebootLogAddAndCleanAsync(RemoteRebootLogModel newRec, int num2keep)
{
bool fatto = false;
using var dbCtx = new MoonProContext(_configuration);
using var transaction = await dbCtx.Database.BeginTransactionAsync();
try
{
dbCtx.DbSetRemRebLog.Add(newRec);
fatto = await dbCtx.SaveChangesAsync() > 0;
var param = new SqlParameter("@num2keep", num2keep);
await dbCtx.Database.ExecuteSqlRawAsync("EXEC dbo.stp_RRL_KeepLatest @num2keep", param);
await transaction.CommitAsync();
return true;
}
catch
{
await transaction.RollbackAsync();
throw;
}
}
/// <summary>
/// Recupera tutti i record di RemoteRebootLog
+2 -24
View File
@@ -1,15 +1,11 @@
using Microsoft.Extensions.Configuration;
using MP.Core.Conf;
using MP.Data.DbModels;
using Newtonsoft.Json;
using NLog;
using StackExchange.Redis;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Data;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
@@ -298,7 +294,7 @@ namespace MP.Data.Services
.Where(ep => _redisConn.GetServer(ep).IsConnected && !_redisConn.GetServer(ep).IsReplica)
.FirstOrDefault();
// sepattern è "*" elimino intero DB...
// se pattern è "*" elimino intero DB...
if (masterEndpoint != null && (pat2Flush.Equals(new RedisValue("*")) || pat2Flush == RedisValue.Null))
{
_redisConn.GetServer(masterEndpoint).FlushDatabase(database: _redisDb.Database);
@@ -322,26 +318,8 @@ namespace MP.Data.Services
{
await Task.WhenAll(deleteTasks);
}
answ = true;
}
answ = true;
#if false
var listEndpoints = redisConn.GetEndPoints();
foreach (var endPoint in listEndpoints)
{
//var server = redisConnAdmin.GetServer(listEndpoints[0]);
var server = redisConn.GetServer(endPoint);
if (server != null)
{
var keyList = server.Keys(redisDb.Database, pattern);
foreach (var item in keyList)
{
await redisDb.KeyDeleteAsync(item);
}
answ = true;
}
}
#endif
return answ;
}
+16 -7
View File
@@ -810,7 +810,6 @@ namespace MP.IOC.Controllers
if (string.IsNullOrEmpty(id)) return BadRequest("Missing ID");
id = id.Replace('|', '#');
try
{
await DService.ScriviKeepAliveAsync(id, DateTime.UtcNow);
@@ -1150,15 +1149,14 @@ namespace MP.IOC.Controllers
{
if (string.IsNullOrEmpty(id)) return BadRequest("Missing ID");
// Multi: gestione carattere "|" trasformato in "#"
id = id.Replace("|", "#");
string answ = "NO";
try
{
// recupero IP del client remoto
var agent = Request.Headers["User-Agent"].ToString();
var iPv4 = HttpContext.Connection.RemoteIpAddress?.ToString();
//var ipv4 = HttpContext.Connection.RemoteIpAddress?.ToString();
var ipv4 = HttpContext.Connection.RemoteIpAddress?.MapToIPv4().ToString();
// chiamo registrazione reboot
RemoteRebootLogModel newRec = new()
@@ -1166,11 +1164,22 @@ namespace MP.IOC.Controllers
Agent = agent,
DataOraBoot = DateTime.Now,
IdxMacchina = id,
IPv4 = iPv4,
IPv4 = ipv4,
MacAddr = mac
};
bool fatto = await DService.RemRebootLogAddAsync(newRec);
answ = fatto ? "OK" : "KO";
var success = await DService.RemRebootLogAddAsync(newRec);
answ = success ? "OK" : "KO";
if (success)
{
return Ok(answ);
//return Ok(new { status = "ok", id = id });
}
else
{
return BadRequest(new { status = "ko", message = "DB operation failed" });
}
}
catch (Exception exc)
{
+85 -1
View File
@@ -3323,7 +3323,6 @@ namespace MP.IOC.Data
var listEndpoints = redisConnAdmin.GetEndPoints();
foreach (var endPoint in listEndpoints)
{
//var server = redisConnAdmin.GetServer(listEndpoints[0]);
var server = redisConnAdmin.GetServer(endPoint);
if (server != null)
{
@@ -3343,6 +3342,7 @@ namespace MP.IOC.Data
/// </summary>
/// <param name="pattern"></param>
/// <returns></returns>
#if false
public async Task<bool> RedisFlushPatternAsync(RedisValue pattern)
{
bool answ = false;
@@ -3361,8 +3361,82 @@ namespace MP.IOC.Data
}
}
return answ;
}
#endif
public async Task<bool> RedisFlushPatternAsync(RedisValue pattern)
{
Log.Debug($"Richiesta flush pattern: {pattern}");
// 1. Target ONLY master (le replica sono in read-only)
var master = redisConnAdmin.GetEndPoints()
.Where(ep => redisConnAdmin.GetServer(ep).IsConnected && !redisConnAdmin.GetServer(ep).IsReplica)
.FirstOrDefault();
if (master == null)
{
Log.Warn($"Nessun master Redis raggiungibile per il pattern {pattern}");
return false;
}
// 2. Flush intero DB se richiesto
if (pattern.ToString() == "*")
{
Log.Debug($"Full DB reset da pattern {pattern}");
if (master != null)
{
redisConnAdmin.GetServer(master).FlushDatabase(redisDb.Database);
Log.Info($"Flush database {redisDb.Database} completato");
}
return true;
}
// altrimenti faccio ciclo!
var server = redisConnAdmin.GetServer(master);
var db = redisConnAdmin.GetDatabase(redisDb.Database);
const int batchSize = 500;
var batch = new List<RedisKey>(batchSize);
int deletedCount = 0;
try
{
// KeysAsync usa SCAN automaticamente quando i risultati sono grandi
await foreach (var key in server.KeysAsync(
database: redisDb.Database,
pattern: pattern.ToString(),
pageSize: batchSize))
{
batch.Add(key);
if (batch.Count >= batchSize)
{
// Esecuzione batch in parallelo controllato
await Task.WhenAll(batch.Select(k => db.KeyDeleteAsync(k)));
batch.Clear();
deletedCount += batchSize;
}
}
// Restanti
if (batch.Count > 0)
{
await Task.WhenAll(batch.Select(k => db.KeyDeleteAsync(k)));
deletedCount += batch.Count;
}
Log.Info("Flush pattern {Pattern}: eliminate {Count} chiavi", pattern, deletedCount);
return true;
}
catch (RedisConnectionException ex)
{
Log.Error(ex, "Connessione Redis persa durante il flush di {pattern}", pattern);
return false;
}
catch (Exception ex)
{
Log.Error(ex, "Errore imprevisto nel flush pattern {pattern}", pattern);
throw;
}
}
public KeyValuePair<string, string>[] RedisGetHash(RedisKey redKey)
{
HashEntry[] rawData = redisDb.HashGetAll(redKey);
@@ -3455,6 +3529,7 @@ namespace MP.IOC.Data
public async Task<bool> RemRebootLogAddAsync(RemoteRebootLogModel newRec)
{
bool fatto = false;
#if false
// insert del record
fatto = await IocDbController.RemRebootLogAddAsync(newRec);
// pulizia record vecchi
@@ -3465,12 +3540,21 @@ namespace MP.IOC.Data
int.TryParse(confVal, out num2keep);
}
fatto = await IocDbController.RemRebootLogKeepLastAsync(num2keep);
#endif
// insert del record + pulizia
string confVal = await tryGetConfig("IO_NumReboot2Keep");
int num2keep = int.TryParse(confVal, out int n) ? n : 5;
fatto = await IocDbController.RemRebootLogAddAndCleanAsync(newRec, num2keep);
// svuota cache
var currKey = $"{Utils.redisRemRebLog}:*";
await RedisFlushPatternAsync(currKey);
return fatto;
}
/// <summary>
/// Recupera tutti i record di RemoteRebootLog
/// </summary>
+1 -1
View File
@@ -4,7 +4,7 @@
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<Version>6.16.2604.2409</Version>
<Version>6.16.2604.2711</Version>
</PropertyGroup>
<ItemGroup>
+1 -1
View File
@@ -1,6 +1,6 @@
<body>
<i>Modulo MP-IOC </i>
<h4>Versione: 6.16.2604.2409</h4>
<h4>Versione: 6.16.2604.2711</h4>
<br /> Note di rilascio:
<ul>
<li>
+1 -1
View File
@@ -1 +1 @@
6.16.2604.2409
6.16.2604.2711
+1 -1
View File
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<item>
<version>6.16.2604.2409</version>
<version>6.16.2604.2711</version>
<url>https://nexus.steamware.net/repository/SWS/MP-IOC/stable/LAST/MP.IOC.zip</url>
<changelog>https://nexus.steamware.net/repository/SWS/MP-IOC/stable/LAST/ChangeLog.html</changelog>
<mandatory>false</mandatory>