Files
maat/Maat.Data/Services/MsSqlTaskService.cs
Samuele Locatelli 0cbffd8613 Modifica gestione task
- solo task attivi
- esce da gruppo se fallisce uno step
2025-07-21 12:49:38 +02:00

337 lines
12 KiB
C#

using Maat.Core;
using Maat.Data.Controllers;
using Maat.Data.DbModels;
using NLog;
using StackExchange.Redis;
using static Maat.Core.Enums;
using System.Diagnostics;
using Newtonsoft.Json;
using System.Xml.Linq;
using Newtonsoft.Json.Linq;
namespace Maat.Data.Services
{
public class MsSqlTaskService : BaseServ, IDisposable
{
#region Public Constructors
/// <summary>
/// Servizio gestione Task su DB
/// </summary>
/// <param name="threadName">nome del thread</param>
/// <param name="dbConnString">stringa completa di connessione</param>
/// <param name="redisConnString">stringa completa REDIS</param>
/// <param name="apiUrl">url x chiamate api REST</param>
/// <param name="execTimeOut">timeout esecuzione comandi sql (in minuti)</param>
public MsSqlTaskService(string threadName, string dbConnString, string redisConnString, string apiUrl, int execTimeOut)
{
tName = threadName;
Log.Info($"{tName} | Starting MsSqlTaskService");
connString = dbConnString;
// setup compoenti REDIS
redisConn = ConnectionMultiplexer.Connect(redisConnString);
redisDb = redisConn.GetDatabase();
// conf DB
if (string.IsNullOrEmpty(connString))
{
Log.Error($"{tName} | ConnString empty!");
}
else
{
dbController = new MsSqlController(tName, dbConnString, execTimeOut);
Log.Info($"{tName} | DbController OK");
}
// conf rest call service
RCallService = new RestCallService(apiUrl);
}
#endregion Public Constructors
#region Public Events
/// <summary>
/// Evento richiesta rilettura dati pagina (x refresh pagine aperte)
/// </summary>
public event EventHandler ReloadRequest = delegate { };
#endregion Public Events
#region Public Methods
public List<TaskResultModel> CheckExecute()
{
List<TaskResultModel> answ = new List<TaskResultModel>();
List<TaskListModel> listTask = TaskListAll(tName, Enums.Task2ExeType.ND, "");
// verifico SE ci siano task in scadenza...
DateTime adesso = DateTime.Now;
List<TaskListModel> task2exe = listTask.Where(x => x.Enabled && x.DtNextExec <= adesso).ToList();
int numDone = 0;
if (task2exe != null && task2exe.Count > 0)
{
Log.Info($"{tName} | Found {task2exe.Count} task to execute");
// suddivisione x gruppi
var ListGrouped = task2exe
.OrderBy(x => x.Group)
.ThenBy(x => x.Ordinal)
.GroupBy(x => x.Group)
.ToDictionary(g => g.Key, g => g.ToList());
// ciclo, e all'interno del gruppo SE non passa uno step si ferma....
foreach (var taskGroup in ListGrouped)
{
foreach (var taskRec in taskGroup.Value)
{
TaskResultModel result = ExecuteTask(taskRec);
answ.Add(result);
numDone++;
// SE non eseguito --> esce, come anche se passato timeout (e ultima esecuzione è prima della scadenza..
if (result.ExecResult < 0)
{
break;
}
}
}
#if false
foreach (var taskRec in task2exe)
{
TaskResultModel result = ExecuteTask(taskRec);
answ.Add(result);
}
#endif
}
else
{
Log.Trace($"{tName} | Chiamata CheckExecute | eseguiti {numDone} task");
}
// resituisco
return answ;
}
public string CheckRestServer()
{
return RCallService.CheckServer();
}
public void Dispose()
{
// Clear database controller
dbController.Dispose();
// redis dispose
redisConn.Dispose();
}
/// <summary>
/// Chiamata esecuzione di un singolo task programmato
/// </summary>
/// <param name="TaskRec">Task richiesto</param>
/// <returns></returns>
public TaskResultModel ExecuteTask(TaskListModel TaskRec)
{
TaskResultModel dbResult = new TaskResultModel()
{
Task = $"TaskId: {TaskRec.TaskId} | {TaskRec.TType}",
ExecResult = -1,
TextResult = "Task Not recognized"
};
// verifico tipo di task ed eseguo di conseguenza...
switch (TaskRec.TType)
{
//case Task2ExeType.ND:
// break;
//case Task2ExeType.Exe:
// break;
//case Task2ExeType.SqlCommand:
// break;
case Task2ExeType.SqlStored:
dbResult = dbController.ExecuteSqlTask(TaskRec.TaskId, true);
Log.Info($"{tName} | Executed | TaskRec: {TaskRec}");
break;
case Task2ExeType.RestCallGet:
// in primis testo la chiamata al servizio Health
string rAnsw = RCallService.CheckServer();
DateTime dtStart = DateTime.Now;
// se ok effettuo vera chiamata...
if (rAnsw.ToUpper() == "OK")
{
var callResp = RCallService.CallRestGet(TaskRec.Command, TaskRec.Args);
DateTime dtEnd = DateTime.Now;
string formattedJson = "";
if (!string.IsNullOrEmpty(callResp.Content))
{
formattedJson = JValue.Parse(callResp.Content).ToString(Formatting.Indented);
}
TaskExecModel tExeMod = new TaskExecModel()
{
DtEnd = dtEnd,
DtStart = dtStart,
IsError = callResp.StatusCode != System.Net.HttpStatusCode.OK,
TaskId = TaskRec.TaskId,
// deserializzazione come json indentato?!?
Result = formattedJson// $"{callResp.Content}".Replace("\"", ""),
};
// salvo su DB
dbResult = dbController.TaskExecSaveExecuted(TaskRec.TaskId, true, tExeMod);
}
break;
default:
break;
}
// svuoto cache!
FlushCache("Task");
return dbResult;
}
/// <summary>
/// Pulizia cache Redis (tutta)
/// </summary>
/// <returns></returns>
public bool FlushCache()
{
RedisValue pattern = new RedisValue($"{Const.RedisBaseKey}:*");
bool answ = ExecFlushRedisPattern(pattern);
return answ;
}
/// <summary>
/// Pulizia cache Redis per chiave specifica (da redisBaseKey...)
/// </summary>
/// <returns></returns>
public bool FlushCache(string KeyReq)
{
RedisValue pattern = new RedisValue($"{Const.RedisBaseKey}:{KeyReq}:*");
bool answ = ExecFlushRedisPattern(pattern);
return answ;
}
/// <summary>
/// Invio notifica rilettura (con parametro)
/// </summary>
/// <param name="message"></param>
public void NotifyReloadRequest(string message)
{
if (ReloadRequest != null)
{
// messaggio
ReloadEventArgs rea = new ReloadEventArgs(message);
ReloadRequest.Invoke(this, rea);
}
}
/// <summary>
/// Elenco TaskList gestiti
/// </summary>
/// <param name="CurrFilter"></param>
/// <param name="searchVal"></param>
/// <returns></returns>
public List<TaskListModel> TaskListAll(string threadName, Task2ExeType TType, string searchVal = "")
{
// setup parametri costanti
string source = "DB";
Stopwatch sw = new Stopwatch();
sw.Start();
List<TaskListModel> result = new List<TaskListModel>();
// cerco in redis...
DateTime adesso = DateTime.Now;
string currKey = $"{Const.RedisBaseKey}:Task:{threadName.Replace("_", ":")}:List:{TType}";
RedisValue rawData = redisDb.StringGet(currKey);
if (false && rawData.HasValue && rawData.Length() > 4)
{
var rawResult = JsonConvert.DeserializeObject<List<TaskListModel>>($"{rawData}");
result = rawResult ?? new List<TaskListModel>();
source = "REDIS";
}
else
{
result = dbController.TaskListGetAll(TType);
// serializzp e salvo...
rawData = JsonConvert.SerializeObject(result);
redisDb.StringSet(currKey, rawData, FastCache);
}
if (result == null)
{
result = new List<TaskListModel>();
}
// se necessario filtro..
if (!string.IsNullOrEmpty(searchVal))
{
result = result
.Where(x => x.Name.Contains(searchVal, StringComparison.InvariantCultureIgnoreCase)
|| x.Descript.Contains(searchVal, StringComparison.InvariantCultureIgnoreCase))
.ToList();
}
sw.Stop();
Log.Debug($"{tName} | TaskListAll | {source} | {sw.Elapsed.TotalMilliseconds}ms");
return result;
}
#endregion Public Methods
#region Protected Fields
/// <summary>
/// Oggetto per connessione a REDIS
/// </summary>
protected ConnectionMultiplexer redisConn = null!;
/// <summary>
/// Oggetto DB redis da impiegare x chiamate R/W
/// </summary>
protected StackExchange.Redis.IDatabase redisDb = null!;
#endregion Protected Fields
#region Private Fields
private static Logger Log = LogManager.GetCurrentClassLogger();
private string connString = "";
private string tName = "";
#endregion Private Fields
#region Private Properties
private MsSqlController dbController { get; set; } = null!;
private RestCallService RCallService { get; set; } = null!;
#endregion Private Properties
#region Private Methods
/// <summary>
/// Esegue flush memoria redis dato pattern
/// </summary>
/// <param name="pattern"></param>
/// <returns></returns>
private bool ExecFlushRedisPattern(RedisValue pattern)
{
bool answ = 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)
{
redisDb.KeyDelete(item);
}
answ = true;
}
}
// notifico update ai client in ascolto x reset cache
NotifyReloadRequest($"{tName} | FlushRedisCache | {pattern}");
return answ;
}
#endregion Private Methods
}
}