Update gestione script LUA:

- cartella con script
- conf x scelta
- gestione script letti 1 sola volta all'avvio
This commit is contained in:
Samuele Locatelli
2026-04-20 10:51:38 +02:00
parent 2d77838a01
commit 91f433e41c
12 changed files with 196 additions and 8 deletions
+7
View File
@@ -0,0 +1,7 @@
namespace MP.Core.Conf
{
public class RedisScriptsConfig
{
public Dictionary<string, string> Scripts { get; set; } = new();
}
}
+7 -1
View File
@@ -4,7 +4,7 @@
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<Version>6.16.2604.2008</Version>
<Version>6.16.2604.2010</Version>
</PropertyGroup>
<ItemGroup>
@@ -48,6 +48,12 @@
<None Update="post-build.ps1">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="RedisScript\RedisUpdateScript_v5.lua">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="RedisScript\RedisUpdateScript_v6.lua">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup>
<ItemGroup>
+6
View File
@@ -1,6 +1,7 @@
using Microsoft.AspNetCore.Http.Extensions;
using Microsoft.EntityFrameworkCore;
using Microsoft.OpenApi.Models;
using MP.Core.Conf;
using MP.Data;
using MP.Data.Repository.Utils;
using MP.Data.Services.Utils;
@@ -59,11 +60,16 @@ builder.Services.AddSingleton(sp =>
});
logger.Info("YARP reverse proxy configured");
builder.Services.Configure<RedisScriptsConfig>(
builder.Configuration.GetSection("RedisScripts"));
logger.Info("RedisScript Provider configured");
// base services
builder.Services.AddSingleton<PreserveBodyTransformer>();
builder.Services.AddSingleton<RouteStatsManager>();
builder.Services.AddHostedService<MetricsCalcService>();
builder.Services.AddHostedService<MetricsDbFlushService>();
builder.Services.AddSingleton<LuaScriptProvider>();
// Registra i servizi per Blazor
builder.Services.AddRazorComponents()
@@ -0,0 +1,33 @@
-- RedisUpdateScript_v5
local key = KEYS[1]
local countInc = tonumber(ARGV[1]) or 0
local totalMsInc = tonumber(ARGV[2]) or 0
local newMax = tonumber(ARGV[3])
local newMin = tonumber(ARGV[4])
local sentinel = tonumber(ARGV[5])
-- Incrementi base
redis.call('HINCRBY', key, 'count', countInc)
redis.call('HINCRBYFLOAT', key, 'totalMs', totalMsInc)
-- MAX
local currentMaxStr = redis.call('HGET', key, 'maxMs')
local currentMax = tonumber(currentMaxStr)
if newMax ~= nil and newMax < sentinel then
if currentMax == nil or newMax > currentMax then
redis.call('HSET', key, 'maxMs', newMax)
end
end
-- MIN
local currentMinStr = redis.call('HGET', key, 'minMs')
local currentMin = tonumber(currentMinStr)
if newMin ~= nil and newMin < sentinel then
if currentMin == nil or newMin < currentMin then
redis.call('HSET', key, 'minMs', newMin)
end
end
return 1
@@ -0,0 +1,33 @@
-- RedisUpdateScript_v6
local key = KEYS[1]
local countInc = tonumber(ARGV[1]) or 0
local totalMsInc = tonumber(ARGV[2]) or 0
local newMax = tonumber(ARGV[3])
local newMin = tonumber(ARGV[4])
local sentinel = tonumber(ARGV[5])
-- Incrementi
redis.call('HINCRBY', key, 'count', countInc)
redis.call('HINCRBYFLOAT', key, 'totalMs', totalMsInc)
-- MAX
local currentMaxStr = redis.call('HGET', key, 'maxMs')
local currentMax = tonumber(currentMaxStr)
if newMax ~= nil and newMax < sentinel then
if currentMax == nil or newMax > currentMax then
redis.call('HSET', key, 'maxMs', tostring(newMax))
end
end
-- MIN
local currentMinStr = redis.call('HGET', key, 'minMs')
local currentMin = tonumber(currentMinStr)
if newMin ~= nil and newMin < sentinel then
if currentMin == nil or newMin < currentMin then
redis.call('HSET', key, 'minMs', tostring(newMin))
end
end
return 1
+1 -1
View File
@@ -1,6 +1,6 @@
<body>
<i>Modulo MP-IOC </i>
<h4>Versione: 6.16.2604.2008</h4>
<h4>Versione: 6.16.2604.2010</h4>
<br /> Note di rilascio:
<ul>
<li>
+1 -1
View File
@@ -1 +1 @@
6.16.2604.2008
6.16.2604.2010
+1 -1
View File
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<item>
<version>6.16.2604.2008</version>
<version>6.16.2604.2010</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>
+43
View File
@@ -0,0 +1,43 @@
using Microsoft.Extensions.Options;
using MP.Core.Conf;
namespace MP.IOC.Services
{
public sealed class LuaScriptProvider
{
private readonly Dictionary<string, string> _scripts;
public IReadOnlyDictionary<string, string> Scripts => _scripts;
public LuaScriptProvider(
IOptions<RedisScriptsConfig> cfg,
IWebHostEnvironment env)
{
_scripts = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
foreach (var kv in cfg.Value.Scripts)
{
var name = kv.Key;
var relativePath = kv.Value;
var fullPath = Path.Combine(env.ContentRootPath, relativePath);
if (!File.Exists(fullPath))
throw new FileNotFoundException($"Script Lua non trovato: {fullPath}");
var content = File.ReadAllText(fullPath);
_scripts[name] = content;
}
}
public string Get(string name)
{
if (!_scripts.TryGetValue(name, out var script))
throw new KeyNotFoundException($"Script Lua '{name}' non trovato.");
return script;
}
}
}
+54 -4
View File
@@ -14,12 +14,16 @@ namespace MP.IOC.Services
/// <param name="stats"></param>
/// <param name="config"></param>
/// <param name="mux"></param>
public MetricsCalcService(RouteStatsManager stats, IConfiguration config, IConnectionMultiplexer mux)
public MetricsCalcService(RouteStatsManager stats,
LuaScriptProvider luaProvider,
IConfiguration config,
IConnectionMultiplexer mux)
{
_stats = stats;
_config = config;
_db = mux.GetDatabase();
_redisBaseKey = _config.GetValue<string>("ServerConf:RedisBaseKey") ?? "MP_IOC";
_updateScript = luaProvider.Get("Update");
}
#endregion Public Constructors
@@ -33,11 +37,18 @@ namespace MP.IOC.Services
/// </summary>
private const string SentinelValue = "999999999";
/// <summary>
/// Script update Redis in Lua, recuperato dal provider script.
/// Lo script gestisce l'incremento e la logica condizionale per Min/Max in un'unica operazione atomica.
/// </summary>
private readonly string _updateScript;
#if false
/// <summary>
/// Script update Redis in Lua, definito come costante per efficienza.
/// Lo script gestisce l'incremento e la logica condizionale per Min/Max in un'unica operazione atomica.
/// </summary>
private const string RedisUpdateScript = @"
private const string RedisUpdateScript_v6 = @"
local key = KEYS[1]
local countInc = tonumber(ARGV[1])
local totalMsInc = tonumber(ARGV[2])
@@ -63,6 +74,45 @@ namespace MP.IOC.Services
return 1
";
private const string RedisUpdateScript_v5 = @"
local key = KEYS[1]
local countInc = tonumber(ARGV[1]) or 0
local totalMsInc = tonumber(ARGV[2]) or 0
local newMax = tonumber(ARGV[3])
local newMin = tonumber(ARGV[4])
local sentinel = tonumber(ARGV[5])
-- Incrementi base
redis.call('HINCRBY', key, 'count', countInc)
redis.call('HINCRBYFLOAT', key, 'totalMs', totalMsInc)
-- MAX
local currentMaxStr = redis.call('HGET', key, 'maxMs')
local currentMax = tonumber(currentMaxStr)
if newMax ~= nil and newMax < sentinel then
if currentMax == nil or newMax > currentMax then
redis.call('HSET', key, 'maxMs', newMax)
end
end
-- MIN
local currentMinStr = redis.call('HGET', key, 'minMs')
local currentMin = tonumber(currentMinStr)
if newMin ~= nil and newMin < sentinel then
if currentMin == nil or newMin < currentMin then
redis.call('HSET', key, 'minMs', newMin)
end
end
return 1
";
#endif
// Classe di supporto per l'aggregazione locale dei valori Daily
private class AggregatedStats
{
@@ -124,7 +174,7 @@ namespace MP.IOC.Services
var hourScore = ToEpochSeconds(hourStart);
// Usiamo lo script Lua per l'aggiornamento atomico dell'ora
tasks.Add(batch.ScriptEvaluateAsync(RedisUpdateScript,
tasks.Add(batch.ScriptEvaluateAsync(_updateScript,
new RedisKey[] { hourKey },
new RedisValue[] {
count.ToString(),
@@ -167,7 +217,7 @@ namespace MP.IOC.Services
? double.Parse(SentinelValue)
: agg.MinMs;
tasks.Add(batch.ScriptEvaluateAsync(RedisUpdateScript,
tasks.Add(batch.ScriptEvaluateAsync(_updateScript,
new RedisKey[] { key },
new RedisValue[] {
agg.Count.ToString(),
+5
View File
@@ -31,5 +31,10 @@
"Redis": "localhost:6379,DefaultDatabase=5,connectTimeout=5000,syncTimeout=5000,asyncTimeout=5000,abortConnect=false,ssl=false",
"RedisAdmin": "localhost:6379,DefaultDatabase=5,connectTimeout=5000,syncTimeout=5000,asyncTimeout=5000,abortConnect=false,ssl=false,allowAdmin=true",
"mdbConnString": "mongodb://localhost:27017"
},
"RedisScripts": {
"Scripts": {
"Update": "RedisScript/RedisUpdateScript_v5.lua"
}
}
}
+5
View File
@@ -88,6 +88,11 @@
"redisLongTimeCache": 60,
"redisShortTimeCache": 30
},
"RedisScripts": {
"Scripts": {
"Update": "RedisScript/RedisUpdateScript_v6.lua"
}
},
"ConnectionStrings": {
"MP.Data": "Server=SQL2016DEV;Database=MoonPro; User ID=sa;Password=keyhammer16; integrated security=False; App=MP.IOC;",
"MP.Flux": "Server=SQL2016DEV;Database=MoonPro_FluxData; User ID=sa;Password=keyhammer16; integrated security=False; MultipleActiveResultSets=True; App=MP.SPEC;",