using MP.Core.DTO; using StackExchange.Redis; namespace MP.RIOC.Services { public class RedisWeightProvider : IWeightProvider { #region Public Constructors public RedisWeightProvider(IConnectionMultiplexer mux, IConfiguration config) { _config = config; _db = mux.GetDatabase(); _mux = mux; _defaultOld = config.GetValue("RouteMan:DefaultWeightOld", 100); _defaultNew = config.GetValue("RouteMan:DefaultWeightNew", 0); _redisBaseKey = config.GetValue("ServerConf:RedisBaseKey") ?? "MP_IOC"; _keyPrefix = $"{_redisBaseKey}:route_weight:"; } #endregion Public Constructors #region Public Methods /// /// Ritorna (oldWeight, newWeight) per il metodo. Se non esiste, crea la chiave con i default. /// public (int oldWeight, int newWeight) GetWeightsFor(string method) { if (string.IsNullOrEmpty(method)) method = "unknown"; var key = _keyPrefix + method; // Leggi entrambi i campi var oldVal = _db.HashGet(key, "old"); var newVal = _db.HashGet(key, "new"); // Se entrambi mancanti, inizializza con default (usando HSet con When.NotExists per evitare overwrite) if (oldVal.IsNull && newVal.IsNull) { // Imposta i campi singolarmente con When.NotExists per evitare overwrite _db.HashSet(key, "old", _defaultOld, When.NotExists); _db.HashSet(key, "new", _defaultNew, When.NotExists); // Rileggi per essere sicuri oldVal = _db.HashGet(key, "old"); newVal = _db.HashGet(key, "new"); } // Se uno dei due manca, impostalo al default (non sovrascrive l'altro) if (oldVal.IsNull) { _db.HashSet(key, "old", _defaultOld, When.NotExists); oldVal = _defaultOld; } if (newVal.IsNull) { _db.HashSet(key, "new", _defaultNew, When.NotExists); newVal = _defaultNew; } if (!int.TryParse(oldVal.ToString(), out var oldW)) oldW = _defaultOld; if (!int.TryParse(newVal.ToString(), out var newW)) newW = _defaultNew; // clamp 0..100 oldW = Math.Clamp(oldW, 0, 100); newW = Math.Clamp(newW, 0, 100); return (oldW, newW); } // API per aggiornare i pesi a runtime (opzionale) public void SetWeights(string method, int oldWeight, int newWeight) { var key = _keyPrefix + (string.IsNullOrEmpty(method) ? "unknown" : method); _db.HashSet(key, new HashEntry[] { new HashEntry("old", Math.Clamp(oldWeight,0,100)), new HashEntry("new", Math.Clamp(newWeight,0,100)) }); } public async Task> GetAllWeightsAsync() { var result = new List(); var server = _mux.GetServer(_mux.GetEndPoints().First()); if (server.IsReplica) { return result; } await foreach (var key in server.KeysAsync(pattern: $"{_keyPrefix}*")) { var methodName = KeyToString(key.ToString()); if (string.IsNullOrEmpty(methodName)) continue; var oldVal = _db.HashGet(key, "old"); var newVal = _db.HashGet(key, "new"); int oldW = 100; int newW = 0; if (!oldVal.IsNull && int.TryParse(oldVal.ToString(), out var parsedOld)) oldW = Math.Clamp(parsedOld, 0, 100); if (!newVal.IsNull && int.TryParse(newVal.ToString(), out var parsedNew)) newW = Math.Clamp(parsedNew, 0, 100); result.Add(new WeightDTO { Method = methodName, OldWeight = oldW, NewWeight = newW }); } // riordino desc x NEW poi alfabetico... result = result .OrderByDescending(x => x.NewWeight) .ThenBy(x => x.Method) .ToList(); return result; } public bool UpsertWeight(WeightDTO updRecord) { if (updRecord == null || string.IsNullOrEmpty(updRecord.Method)) return false; var key = _keyPrefix + updRecord.Method; _db.HashSet(key, new HashEntry[] { new HashEntry("old", Math.Clamp(updRecord.OldWeight, 0, 100)), new HashEntry("new", Math.Clamp(updRecord.NewWeight, 0, 100)) }); return true; } private string KeyToString(string key) { if (string.IsNullOrEmpty(key)) return ""; var prefix = _keyPrefix ?? ""; if (key.StartsWith(prefix)) return key.Substring(prefix.Length); return key; } #endregion Public Methods #region Private Fields private static string _keyPrefix = "route_weight:"; private static string _redisBaseKey = ""; private readonly IConfiguration _config; private readonly IDatabase _db; private readonly IConnectionMultiplexer _mux; private readonly int _defaultNew; private readonly int _defaultOld; #endregion Private Fields } }