0cbffd8613
- solo task attivi - esce da gruppo se fallisce uno step
337 lines
12 KiB
C#
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
|
|
}
|
|
} |