forked from ddrilling/AsbCloudServer
Refactor webStore
This commit is contained in:
parent
724c7b0cd8
commit
1560c6bf91
@ -1,8 +1,11 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
|
|
||||||
namespace AsbCloudInfrastructure.Background
|
namespace AsbCloudApp.Data
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Информация о фоновой работе
|
||||||
|
/// </summary>
|
||||||
public class BackgroudWorkDto
|
public class BackgroudWorkDto
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -10,6 +13,9 @@ namespace AsbCloudInfrastructure.Background
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public string Id { get; init; } = null!;
|
public string Id { get; init; } = null!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Класс описания состояния
|
||||||
|
/// </summary>
|
||||||
public class CurrentStateInfo
|
public class CurrentStateInfo
|
||||||
{
|
{
|
||||||
private string state = "start";
|
private string state = "start";
|
||||||
@ -37,6 +43,9 @@ namespace AsbCloudInfrastructure.Background
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Прогресс
|
||||||
|
/// </summary>
|
||||||
public double Progress { get; internal set; } = 0;
|
public double Progress { get; internal set; } = 0;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -45,9 +54,12 @@ namespace AsbCloudInfrastructure.Background
|
|||||||
public DateTime StateUpdate { get; private set; } = DateTime.Now;
|
public DateTime StateUpdate { get; private set; } = DateTime.Now;
|
||||||
}
|
}
|
||||||
|
|
||||||
public class LastErrorInfo: LastCompleteInfo
|
/// <summary>
|
||||||
|
/// Инфо о последней ошибке
|
||||||
|
/// </summary>
|
||||||
|
public class LastErrorInfo : LastCompleteInfo
|
||||||
{
|
{
|
||||||
public LastErrorInfo(CurrentStateInfo state, string errorText)
|
public LastErrorInfo(CurrentStateInfo state, string errorText)
|
||||||
: base(state)
|
: base(state)
|
||||||
{
|
{
|
||||||
ErrorText = errorText;
|
ErrorText = errorText;
|
||||||
@ -59,12 +71,15 @@ namespace AsbCloudInfrastructure.Background
|
|||||||
public string ErrorText { get; init; } = null!;
|
public string ErrorText { get; init; } = null!;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Инфо о последнем завершении
|
||||||
|
/// </summary>
|
||||||
public class LastCompleteInfo
|
public class LastCompleteInfo
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Дата запуска
|
/// Дата запуска
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public DateTime Start {get; init;}
|
public DateTime Start { get; init; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Дата завершения
|
/// Дата завершения
|
||||||
@ -123,9 +138,12 @@ namespace AsbCloudInfrastructure.Background
|
|||||||
/// Максимально допустимое время выполнения работы
|
/// Максимально допустимое время выполнения работы
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public TimeSpan Timeout { get; set; } = TimeSpan.FromMinutes(1);
|
public TimeSpan Timeout { get; set; } = TimeSpan.FromMinutes(1);
|
||||||
|
|
||||||
private string WorkNameForTrace => $"Backgroud work:\"{Id}\"";
|
private string WorkNameForTrace => $"Backgroud work:\"{Id}\"";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Обновления состояния при запуске работы
|
||||||
|
/// </summary>
|
||||||
protected void SetStatusStart()
|
protected void SetStatusStart()
|
||||||
{
|
{
|
||||||
CurrentState = new();
|
CurrentState = new();
|
||||||
@ -133,6 +151,9 @@ namespace AsbCloudInfrastructure.Background
|
|||||||
Trace.TraceInformation($"{WorkNameForTrace} state: starting");
|
Trace.TraceInformation($"{WorkNameForTrace} state: starting");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Обновления состояния в процессе работы
|
||||||
|
/// </summary>
|
||||||
protected void UpdateStatus(string newState, double? progress)
|
protected void UpdateStatus(string newState, double? progress)
|
||||||
{
|
{
|
||||||
if (CurrentState is null)
|
if (CurrentState is null)
|
||||||
@ -145,17 +166,23 @@ namespace AsbCloudInfrastructure.Background
|
|||||||
Trace.TraceInformation($"{WorkNameForTrace} state: {newState}");
|
Trace.TraceInformation($"{WorkNameForTrace} state: {newState}");
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void SetStatusComplete(System.Threading.Tasks.TaskStatus status)
|
/// <summary>
|
||||||
|
/// Обновления состояния при успешном завершении работы
|
||||||
|
/// </summary>
|
||||||
|
protected void SetStatusComplete()
|
||||||
{
|
{
|
||||||
if (CurrentState is null)
|
if (CurrentState is null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
LastComplete = new (CurrentState);
|
LastComplete = new(CurrentState);
|
||||||
CurrentState = null;
|
CurrentState = null;
|
||||||
CountComplete++;
|
CountComplete++;
|
||||||
Trace.TraceInformation($"{WorkNameForTrace} state: completed");
|
Trace.TraceInformation($"{WorkNameForTrace} state: completed");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Обновления состояния при ошибке в работе
|
||||||
|
/// </summary>
|
||||||
protected void SetLastError(string errorMessage)
|
protected void SetLastError(string errorMessage)
|
||||||
{
|
{
|
||||||
if (CurrentState is null)
|
if (CurrentState is null)
|
@ -1,78 +1,49 @@
|
|||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Extensions.Hosting;
|
using Microsoft.Extensions.Hosting;
|
||||||
using System;
|
using System;
|
||||||
using System.Diagnostics;
|
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace AsbCloudInfrastructure.Background
|
namespace AsbCloudInfrastructure.Background;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Сервис для фонового выполнения работы
|
||||||
|
/// </summary>
|
||||||
|
public class BackgroundWorker : BackgroundService
|
||||||
{
|
{
|
||||||
/// <summary>
|
private static readonly TimeSpan executePeriod = TimeSpan.FromSeconds(10);
|
||||||
/// Сервис для фонового выполнения работы
|
private static readonly TimeSpan minDelay = TimeSpan.FromSeconds(2);
|
||||||
/// </summary>
|
private readonly IServiceProvider serviceProvider;
|
||||||
public class BackgroundWorker : BackgroundService
|
|
||||||
|
public WorkStore WorkStore { get; } = new WorkStore();
|
||||||
|
public Work? CurrentWork;
|
||||||
|
|
||||||
|
public BackgroundWorker(IServiceProvider serviceProvider)
|
||||||
{
|
{
|
||||||
private static readonly TimeSpan executePeriod = TimeSpan.FromSeconds(10);
|
this.serviceProvider = serviceProvider;
|
||||||
private static readonly TimeSpan minDelay = TimeSpan.FromSeconds(2);
|
}
|
||||||
private readonly IServiceProvider serviceProvider;
|
|
||||||
private readonly WorkStore workQueue = new WorkStore();
|
|
||||||
public string? CurrentWorkId;
|
|
||||||
|
|
||||||
public BackgroundWorker(IServiceProvider serviceProvider)
|
protected override async Task ExecuteAsync(CancellationToken token)
|
||||||
|
{
|
||||||
|
while (!token.IsCancellationRequested)
|
||||||
{
|
{
|
||||||
this.serviceProvider = serviceProvider;
|
var work = WorkStore.GetNext();
|
||||||
}
|
if (work is null)
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Добавление задачи в очередь.
|
|
||||||
/// Не периодические задачи будут выполняться вперед.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="work"></param>
|
|
||||||
/// <exception cref="ArgumentException">Id mast be unique</exception>
|
|
||||||
public void Push(WorkBase work)
|
|
||||||
{
|
|
||||||
workQueue.Push(work);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Проверяет наличие работы с указанным Id
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="id"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public bool Contains(string id)
|
|
||||||
{
|
|
||||||
return workQueue.Contains(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Удаление работы по ID
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="id"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public bool Delete(string id)
|
|
||||||
{
|
|
||||||
return workQueue.Delete(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override async Task ExecuteAsync(CancellationToken token)
|
|
||||||
{
|
|
||||||
while (!token.IsCancellationRequested)
|
|
||||||
{
|
{
|
||||||
var work = workQueue.Pop();
|
await Task.Delay(executePeriod, token);
|
||||||
if (work is null)
|
continue;
|
||||||
{
|
|
||||||
await Task.Delay(executePeriod, token);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
CurrentWorkId = work.Id;
|
|
||||||
using var scope = serviceProvider.CreateScope();
|
|
||||||
|
|
||||||
await work.Start(scope.ServiceProvider, token);
|
|
||||||
|
|
||||||
CurrentWorkId = null;
|
|
||||||
await Task.Delay(minDelay, token);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CurrentWork = work;
|
||||||
|
using var scope = serviceProvider.CreateScope();
|
||||||
|
|
||||||
|
var result = await work.Start(scope.ServiceProvider, token);
|
||||||
|
|
||||||
|
if (!result)
|
||||||
|
WorkStore.Falled.Add(work);
|
||||||
|
|
||||||
|
CurrentWork = null;
|
||||||
|
await Task.Delay(minDelay, token);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
using System;
|
using AsbCloudApp.Data;
|
||||||
|
using System;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
@ -8,9 +9,9 @@ namespace AsbCloudInfrastructure.Background
|
|||||||
/// Класс разовой работы.
|
/// Класс разовой работы.
|
||||||
/// Разовая работа приоритетнее периодической.
|
/// Разовая работа приоритетнее периодической.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class WorkBase : BackgroudWorkDto
|
public class Work : BackgroudWorkDto
|
||||||
{
|
{
|
||||||
internal Func<string, IServiceProvider, Action<string, double?>, CancellationToken, Task> ActionAsync { get; }
|
private Func<string, IServiceProvider, Action<string, double?>, CancellationToken, Task> ActionAsync { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Делегат обработки ошибки.
|
/// Делегат обработки ошибки.
|
||||||
@ -48,7 +49,7 @@ namespace AsbCloudInfrastructure.Background
|
|||||||
/// </list>
|
/// </list>
|
||||||
/// </para>
|
/// </para>
|
||||||
/// </param>
|
/// </param>
|
||||||
public WorkBase(string id, Func<string, IServiceProvider, Action<string, double?>, CancellationToken, Task> actionAsync)
|
public Work(string id, Func<string, IServiceProvider, Action<string, double?>, CancellationToken, Task> actionAsync)
|
||||||
{
|
{
|
||||||
Id = id;
|
Id = id;
|
||||||
ActionAsync = actionAsync;
|
ActionAsync = actionAsync;
|
||||||
@ -67,7 +68,7 @@ namespace AsbCloudInfrastructure.Background
|
|||||||
{
|
{
|
||||||
var task = ActionAsync(Id, services, UpdateStatus, token);
|
var task = ActionAsync(Id, services, UpdateStatus, token);
|
||||||
await task.WaitAsync(Timeout, token);
|
await task.WaitAsync(Timeout, token);
|
||||||
SetStatusComplete(task.Status);
|
SetStatusComplete();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
catch (Exception exception)
|
catch (Exception exception)
|
@ -1,45 +1,42 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace AsbCloudInfrastructure.Background
|
namespace AsbCloudInfrastructure.Background;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Класс периодической работы.
|
||||||
|
/// </summary>
|
||||||
|
public class WorkPeriodic
|
||||||
{
|
{
|
||||||
|
public Work Work { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Класс периодической работы.
|
/// Период выполнения задачи
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class WorkPeriodic : WorkBase
|
public TimeSpan Period { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Время следующего запуска
|
||||||
|
/// </summary>
|
||||||
|
public DateTime NextStart
|
||||||
{
|
{
|
||||||
/// <summary>
|
get
|
||||||
/// Период выполнения задачи
|
|
||||||
/// </summary>
|
|
||||||
public TimeSpan Period { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Время следующего запуска
|
|
||||||
/// </summary>
|
|
||||||
public DateTime NextStart
|
|
||||||
{
|
{
|
||||||
get
|
var lastStart = Work.LastComplete?.Start ?? DateTime.MinValue;
|
||||||
{
|
if (Work.LastError?.Start > lastStart)
|
||||||
var lastStart = LastComplete?.Start ?? DateTime.MinValue;
|
lastStart = Work.LastError.Start;
|
||||||
if (LastError?.Start > lastStart)
|
return lastStart + Period;
|
||||||
lastStart = LastError.Start;
|
|
||||||
return lastStart + Period;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Класс периодической работы
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="id">Идентификатор работы. Должен быть уникальным. Используется в логах и передается в колбэки</param>
|
|
||||||
/// <param name="actionAsync">Делегат работы</param>
|
|
||||||
/// <param name="period">Период выполнения задачи</param>
|
|
||||||
public WorkPeriodic(string id, Func<string, IServiceProvider, Action<string, double?>, CancellationToken, Task> actionAsync, TimeSpan period)
|
|
||||||
: base(id, actionAsync)
|
|
||||||
{
|
|
||||||
Period = period;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Класс периодической работы
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="id">Идентификатор работы. Должен быть уникальным. Используется в логах и передается в колбэки</param>
|
||||||
|
/// <param name="actionAsync">Делегат работы</param>
|
||||||
|
/// <param name="period">Период выполнения задачи</param>
|
||||||
|
public WorkPeriodic(Work work, TimeSpan period)
|
||||||
|
{
|
||||||
|
Work = work;
|
||||||
|
Period = period;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,118 +2,85 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
|
||||||
namespace AsbCloudInfrastructure.Background
|
namespace AsbCloudInfrastructure.Background;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <para>
|
||||||
|
/// Очередь работ
|
||||||
|
/// </para>
|
||||||
|
/// Не периодические задачи будут возвращаться первыми, как самые приоритетные.
|
||||||
|
/// </summary>
|
||||||
|
public class WorkStore
|
||||||
{
|
{
|
||||||
|
private readonly List<WorkPeriodic> periodics = new(8);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// <para>
|
/// Список периодических задач
|
||||||
/// Очередь работ
|
|
||||||
/// </para>
|
|
||||||
/// Не периодические задачи будут возвращаться первыми, как самые приоритетные.
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
class WorkStore
|
public IEnumerable<WorkPeriodic> Periodics => periodics;
|
||||||
|
/// <summary>
|
||||||
|
/// Работы выполняемые один раз
|
||||||
|
/// </summary>
|
||||||
|
public Queue<Work> RunOnceQueue { get; private set; } = new(8);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Завершывшиеся с ошибкой
|
||||||
|
/// </summary>
|
||||||
|
public CyclycArray<Work> Falled { get; } = new(16);
|
||||||
|
|
||||||
|
public void AddPeriodic(Work work, TimeSpan period)
|
||||||
{
|
{
|
||||||
private readonly List<WorkPeriodic> Periodic = new(8);
|
var periodic = new WorkPeriodic(work, period);
|
||||||
|
periodics.Add(periodic);
|
||||||
/// <summary>
|
|
||||||
/// Работы выполняемые один раз
|
|
||||||
/// </summary>
|
|
||||||
public Queue<WorkBase> RunOnceQueue { get; } = new(8);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Работы выполняемые периодически
|
|
||||||
/// </summary>
|
|
||||||
public IOrderedEnumerable<WorkPeriodic> Periodics => Periodic.OrderBy(work => work.NextStart);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Завершывшиеся с ошибкой
|
|
||||||
/// </summary>
|
|
||||||
public CyclycArray<WorkBase> Falled { get; } = new(16);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Добавление работы.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="work"></param>
|
|
||||||
/// <exception cref="ArgumentException">Id mast be unique</exception>
|
|
||||||
public void Push(WorkBase work)
|
|
||||||
{
|
|
||||||
if (Periodic.Any(w => w.Id == work.Id))
|
|
||||||
throw new ArgumentException("work.Id is not unique", nameof(work));
|
|
||||||
|
|
||||||
//if (Primary.Any(w => w.Id == work.Id))
|
|
||||||
// throw new ArgumentException("work.Id is not unique", nameof(work));
|
|
||||||
|
|
||||||
if (work is WorkPeriodic workPeriodic)
|
|
||||||
{
|
|
||||||
Periodic.Add(workPeriodic);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
//Primary.Enqueue(work);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Удаление работы по ID
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="id"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public bool Delete(string id)
|
|
||||||
{
|
|
||||||
var workPeriodic = Periodic.FirstOrDefault(w => w.Id == id);
|
|
||||||
if (workPeriodic is not null)
|
|
||||||
{
|
|
||||||
Periodic.Remove(workPeriodic);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
//var work = Primary.FirstOrDefault(w => w.Id == id);
|
|
||||||
//if (work is not null)
|
|
||||||
//{
|
|
||||||
// Primary = new Queue<WorkBase>(Primary.Where(w => w.Id != id));
|
|
||||||
// return true;
|
|
||||||
//}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool Contains(string id)
|
|
||||||
{
|
|
||||||
var result = false;//Periodic.Any(w => w.Id == id) || Primary.Any(w => w.Id == id);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// <para>
|
|
||||||
/// Возвращает приоритетную задачу.
|
|
||||||
/// </para>
|
|
||||||
/// <para>
|
|
||||||
/// Если приоритетные закончились, то ищет ближайшую периодическую.
|
|
||||||
/// Если до старта ближайшей периодической работы меньше 20 сек,
|
|
||||||
/// то этой задаче устанавливается время последнего запуска в now и она возвращается.
|
|
||||||
/// Если больше 20 сек, то возвращается null.
|
|
||||||
/// </para>
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="maxTimeToNextWork"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public WorkBase? Pop()
|
|
||||||
{
|
|
||||||
//if (Primary.Any())
|
|
||||||
// return Primary.Dequeue();
|
|
||||||
|
|
||||||
var work = GetNextPeriodic();
|
|
||||||
if (work is null || work.NextStart > DateTime.Now)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
return work;
|
|
||||||
}
|
|
||||||
|
|
||||||
private WorkPeriodic? GetNextPeriodic()
|
|
||||||
{
|
|
||||||
var work = Periodic
|
|
||||||
.OrderBy(w => w.NextStart)
|
|
||||||
.ThenByDescending(w => w.Period)
|
|
||||||
.FirstOrDefault();
|
|
||||||
return work;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Удаление работы по ID из одноразовой очереди
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="id"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public bool TryRemoveFromRunOnceQueue(string id)
|
||||||
|
{
|
||||||
|
var work = RunOnceQueue.FirstOrDefault(w => w.Id == id);
|
||||||
|
if (work is not null)
|
||||||
|
{
|
||||||
|
RunOnceQueue = new Queue<Work>(RunOnceQueue.Where(w => w.Id != id));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <para>
|
||||||
|
/// Возвращает приоритетную задачу.
|
||||||
|
/// </para>
|
||||||
|
/// <para>
|
||||||
|
/// Если приоритетные закончились, то ищет ближайшую периодическую.
|
||||||
|
/// Если до старта ближайшей периодической работы меньше 20 сек,
|
||||||
|
/// то этой задаче устанавливается время последнего запуска в now и она возвращается.
|
||||||
|
/// Если больше 20 сек, то возвращается null.
|
||||||
|
/// </para>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="maxTimeToNextWork"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public Work? GetNext()
|
||||||
|
{
|
||||||
|
if (RunOnceQueue.Any())
|
||||||
|
return RunOnceQueue.Dequeue();
|
||||||
|
|
||||||
|
var work = GetNextPeriodic();
|
||||||
|
if (work is null || work.NextStart > DateTime.Now)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
return work.Work;
|
||||||
|
}
|
||||||
|
|
||||||
|
private WorkPeriodic? GetNextPeriodic()
|
||||||
|
{
|
||||||
|
var work = Periodics
|
||||||
|
.OrderBy(w => w.NextStart)
|
||||||
|
.FirstOrDefault();
|
||||||
|
return work;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,6 @@ namespace AsbCloudInfrastructure.Services.DetectOperations
|
|||||||
public static class OperationDetectionWorkFactory
|
public static class OperationDetectionWorkFactory
|
||||||
{
|
{
|
||||||
private const string workId = "Operation detection";
|
private const string workId = "Operation detection";
|
||||||
private static readonly TimeSpan workPeriod = TimeSpan.FromMinutes(30);
|
|
||||||
private static string progress = "no progress";
|
private static string progress = "no progress";
|
||||||
|
|
||||||
private static readonly DetectorAbstract[] detectors = new DetectorAbstract[]
|
private static readonly DetectorAbstract[] detectors = new DetectorAbstract[]
|
||||||
@ -32,18 +31,16 @@ namespace AsbCloudInfrastructure.Services.DetectOperations
|
|||||||
//new DetectorTemplatingWhileDrilling(),
|
//new DetectorTemplatingWhileDrilling(),
|
||||||
};
|
};
|
||||||
|
|
||||||
public static WorkPeriodic MakeWork()
|
public static Work MakeWork() => new Work(workId, WorkAction)
|
||||||
{
|
|
||||||
var workPeriodic = new WorkPeriodic(workId, WorkAction, workPeriod);
|
|
||||||
workPeriodic.Timeout = TimeSpan.FromSeconds(15 * 60);
|
|
||||||
workPeriodic.OnErrorAsync = (id, exception, token) =>
|
|
||||||
{
|
{
|
||||||
var text = $"work {id}, when {progress}, throw error:{exception.Message}";
|
Timeout = TimeSpan.FromMinutes(20),
|
||||||
Trace.TraceWarning(text);
|
OnErrorAsync = (id, exception, token) =>
|
||||||
return Task.CompletedTask;
|
{
|
||||||
|
var text = $"work {id}, when {progress}, throw error:{exception.Message}";
|
||||||
|
Trace.TraceWarning(text);
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
return workPeriodic;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Разделить этот акшн на более мелкие части И использовать telemetryServiceData<..> вместо прямого обращения к БД.
|
// TODO: Разделить этот акшн на более мелкие части И использовать telemetryServiceData<..> вместо прямого обращения к БД.
|
||||||
private static async Task WorkAction(string _, IServiceProvider serviceProvider, Action<string, double?> onProgress, CancellationToken token)
|
private static async Task WorkAction(string _, IServiceProvider serviceProvider, Action<string, double?> onProgress, CancellationToken token)
|
||||||
|
@ -513,7 +513,7 @@ namespace AsbCloudInfrastructure.Services.DrillingProgram
|
|||||||
if (state.IdState == idStateCreating)
|
if (state.IdState == idStateCreating)
|
||||||
{
|
{
|
||||||
var workId = MakeWorkId(idWell);
|
var workId = MakeWorkId(idWell);
|
||||||
if (!backgroundWorker.Contains(workId))
|
if (!backgroundWorker.WorkStore.RunOnceQueue.Any(w => w.Id == workId))
|
||||||
{
|
{
|
||||||
var well = (await wellService.GetOrDefaultAsync(idWell, token))!;
|
var well = (await wellService.GetOrDefaultAsync(idWell, token))!;
|
||||||
var resultFileName = $"Программа бурения {well.Cluster} {well.Caption}.pdf";
|
var resultFileName = $"Программа бурения {well.Cluster} {well.Caption}.pdf";
|
||||||
@ -540,12 +540,12 @@ namespace AsbCloudInfrastructure.Services.DrillingProgram
|
|||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
};
|
};
|
||||||
|
|
||||||
var work = new WorkBase(workId, workAction)
|
var work = new Work(workId, workAction)
|
||||||
{
|
{
|
||||||
OnErrorAsync = onErrorAction
|
OnErrorAsync = onErrorAction
|
||||||
};
|
};
|
||||||
|
|
||||||
backgroundWorker.Push(work);
|
backgroundWorker.WorkStore.RunOnceQueue.Enqueue(work);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -559,7 +559,7 @@ namespace AsbCloudInfrastructure.Services.DrillingProgram
|
|||||||
private async Task<int> RemoveDrillingProgramAsync(int idWell, CancellationToken token)
|
private async Task<int> RemoveDrillingProgramAsync(int idWell, CancellationToken token)
|
||||||
{
|
{
|
||||||
var workId = MakeWorkId(idWell);
|
var workId = MakeWorkId(idWell);
|
||||||
backgroundWorker.Delete(workId);
|
backgroundWorker.WorkStore.TryRemoveFromRunOnceQueue(workId);
|
||||||
|
|
||||||
var filesIds = await context.Files
|
var filesIds = await context.Files
|
||||||
.Where(f => f.IdWell == idWell &&
|
.Where(f => f.IdWell == idWell &&
|
||||||
|
@ -52,12 +52,12 @@ namespace AsbCloudInfrastructure.Services.Email
|
|||||||
}
|
}
|
||||||
|
|
||||||
var workId = MakeWorkId(notification.IdUser, notification.Title, notification.Message);
|
var workId = MakeWorkId(notification.IdUser, notification.Title, notification.Message);
|
||||||
if (!backgroundWorker.Contains(workId))
|
if (!backgroundWorker.WorkStore.RunOnceQueue.Any(w=>w.Id==workId))
|
||||||
{
|
{
|
||||||
var workAction = MakeEmailSendWorkAction(notification);
|
var workAction = MakeEmailSendWorkAction(notification);
|
||||||
|
|
||||||
var work = new WorkBase(workId, workAction);
|
var work = new Work(workId, workAction);
|
||||||
backgroundWorker.Push(work);
|
backgroundWorker.WorkStore.RunOnceQueue.Enqueue(work);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
|
@ -16,16 +16,11 @@ namespace AsbCloudInfrastructure.Services
|
|||||||
internal static class LimitingParameterCalcWorkFactory
|
internal static class LimitingParameterCalcWorkFactory
|
||||||
{
|
{
|
||||||
private const string workId = "Limiting parameter calc";
|
private const string workId = "Limiting parameter calc";
|
||||||
private static readonly TimeSpan workPeriod = TimeSpan.FromMinutes(30);
|
|
||||||
|
|
||||||
public static WorkPeriodic MakeWork()
|
public static Work MakeWork() => new Work(workId, WorkAction)
|
||||||
{
|
|
||||||
var workPeriodic = new WorkPeriodic(workId, WorkAction, workPeriod)
|
|
||||||
{
|
{
|
||||||
Timeout = TimeSpan.FromMinutes(30)
|
Timeout = TimeSpan.FromMinutes(30)
|
||||||
};
|
};
|
||||||
return workPeriodic;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Разделить этот акшн на более мелкие части И использовать telemetryServiceData<..> вместо прямого обращения к БД.
|
// TODO: Разделить этот акшн на более мелкие части И использовать telemetryServiceData<..> вместо прямого обращения к БД.
|
||||||
private static async Task WorkAction(string _, IServiceProvider serviceProvider, Action<string, double?> onProgress, CancellationToken token)
|
private static async Task WorkAction(string _, IServiceProvider serviceProvider, Action<string, double?> onProgress, CancellationToken token)
|
||||||
|
@ -94,8 +94,8 @@ namespace AsbCloudInfrastructure.Services
|
|||||||
context.SaveChanges();
|
context.SaveChanges();
|
||||||
};
|
};
|
||||||
|
|
||||||
var work = new WorkBase(workId, workAction);
|
var work = new Work(workId, workAction);
|
||||||
backgroundWorkerService.Push(work);
|
backgroundWorkerService.WorkStore.RunOnceQueue.Enqueue(work);
|
||||||
|
|
||||||
progressHandler.Invoke(new ReportProgressDto
|
progressHandler.Invoke(new ReportProgressDto
|
||||||
{
|
{
|
||||||
|
@ -48,12 +48,12 @@ namespace AsbCloudInfrastructure.Services.SAUB
|
|||||||
instance = new TelemetryDataCache<TDto>();
|
instance = new TelemetryDataCache<TDto>();
|
||||||
var worker = provider.GetRequiredService<BackgroundWorker>();
|
var worker = provider.GetRequiredService<BackgroundWorker>();
|
||||||
var workId = $"Telemetry cache loading from DB {typeof(TEntity).Name}";
|
var workId = $"Telemetry cache loading from DB {typeof(TEntity).Name}";
|
||||||
var work = new WorkBase(workId, async (workId, provider, onProgress, token) => {
|
var work = new Work(workId, async (workId, provider, onProgress, token) => {
|
||||||
var db = provider.GetRequiredService<IAsbCloudDbContext>();
|
var db = provider.GetRequiredService<IAsbCloudDbContext>();
|
||||||
await instance.InitializeCacheFromDBAsync<TEntity>(db, onProgress, token);
|
await instance.InitializeCacheFromDBAsync<TEntity>(db, onProgress, token);
|
||||||
});
|
});
|
||||||
|
|
||||||
worker.Push(work);
|
worker.WorkStore.RunOnceQueue.Enqueue(work);
|
||||||
}
|
}
|
||||||
instance.provider = provider;
|
instance.provider = provider;
|
||||||
return instance;
|
return instance;
|
||||||
@ -168,7 +168,7 @@ namespace AsbCloudInfrastructure.Services.SAUB
|
|||||||
.Where(well => well.IdTelemetry != null)
|
.Where(well => well.IdTelemetry != null)
|
||||||
.ToArrayAsync(token);
|
.ToArrayAsync(token);
|
||||||
|
|
||||||
var count = wells.Count();
|
var count = wells.Length;
|
||||||
var i = 0;
|
var i = 0;
|
||||||
foreach (Well well in wells)
|
foreach (Well well in wells)
|
||||||
{
|
{
|
||||||
|
@ -18,7 +18,6 @@ namespace AsbCloudInfrastructure.Services.Subsystems
|
|||||||
internal static class SubsystemOperationTimeCalcWorkFactory
|
internal static class SubsystemOperationTimeCalcWorkFactory
|
||||||
{
|
{
|
||||||
private const string workId = "Subsystem operation time calc";
|
private const string workId = "Subsystem operation time calc";
|
||||||
private static readonly TimeSpan workPeriod = TimeSpan.FromMinutes(30);
|
|
||||||
|
|
||||||
private const int idSubsytemTorqueMaster = 65537;
|
private const int idSubsytemTorqueMaster = 65537;
|
||||||
private const int idSubsytemSpinMaster = 65536;
|
private const int idSubsytemSpinMaster = 65536;
|
||||||
@ -26,14 +25,10 @@ namespace AsbCloudInfrastructure.Services.Subsystems
|
|||||||
private const int idSubsystemAPDSlide = 12;
|
private const int idSubsystemAPDSlide = 12;
|
||||||
private const int idSubsytemMse = 2;
|
private const int idSubsytemMse = 2;
|
||||||
|
|
||||||
public static WorkPeriodic MakeWork()
|
public static Work MakeWork() => new Work(workId, WorkAction)
|
||||||
{
|
{
|
||||||
var workPeriodic = new WorkPeriodic(workId, WorkAction, workPeriod)
|
Timeout = TimeSpan.FromMinutes(20)
|
||||||
{
|
};
|
||||||
Timeout = TimeSpan.FromMinutes(30)
|
|
||||||
};
|
|
||||||
return workPeriodic;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Разделить этот акшн на более мелкие части И использовать telemetryServiceData<..> вместо прямого обращения к БД.
|
// TODO: Разделить этот акшн на более мелкие части И использовать telemetryServiceData<..> вместо прямого обращения к БД.
|
||||||
private static async Task WorkAction(string _, IServiceProvider serviceProvider, Action<string, double?> onProgress, CancellationToken token)
|
private static async Task WorkAction(string _, IServiceProvider serviceProvider, Action<string, double?> onProgress, CancellationToken token)
|
||||||
|
@ -29,7 +29,6 @@ namespace AsbCloudInfrastructure.Services
|
|||||||
}
|
}
|
||||||
|
|
||||||
private const string workId = "Well statistics update";
|
private const string workId = "Well statistics update";
|
||||||
private static readonly TimeSpan workPeriod = TimeSpan.FromMinutes(30);
|
|
||||||
|
|
||||||
private readonly TelemetryDataCache<TelemetryDataSaubDto> telemetryDataSaubCache;
|
private readonly TelemetryDataCache<TelemetryDataSaubDto> telemetryDataSaubCache;
|
||||||
private readonly TelemetryDataCache<TelemetryDataSpinDto> telemetryDataSpinCache;
|
private readonly TelemetryDataCache<TelemetryDataSpinDto> telemetryDataSpinCache;
|
||||||
@ -53,14 +52,10 @@ namespace AsbCloudInfrastructure.Services
|
|||||||
this.gtrRepository = gtrRepository;
|
this.gtrRepository = gtrRepository;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static WorkPeriodic MakeWork()
|
public static Work MakeWork() => new Work(workId, WorkAction)
|
||||||
{
|
{
|
||||||
var workPeriodic = new WorkPeriodic(workId, WorkAction, workPeriod)
|
Timeout = TimeSpan.FromMinutes(20)
|
||||||
{
|
};
|
||||||
Timeout = TimeSpan.FromMinutes(30)
|
|
||||||
};
|
|
||||||
return workPeriodic;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static async Task WorkAction(string workName, IServiceProvider serviceProvider, Action<string, double?> onProgress, CancellationToken token)
|
private static async Task WorkAction(string workName, IServiceProvider serviceProvider, Action<string, double?> onProgress, CancellationToken token)
|
||||||
{
|
{
|
||||||
|
@ -15,7 +15,6 @@ using AsbCloudInfrastructure.Services.SAUB;
|
|||||||
|
|
||||||
namespace AsbCloudInfrastructure
|
namespace AsbCloudInfrastructure
|
||||||
{
|
{
|
||||||
|
|
||||||
public class Startup
|
public class Startup
|
||||||
{
|
{
|
||||||
public static void BeforeRunHandler(IHost host)
|
public static void BeforeRunHandler(IHost host)
|
||||||
@ -32,11 +31,11 @@ namespace AsbCloudInfrastructure
|
|||||||
_ = provider.GetRequiredService<TelemetryDataCache<TelemetryDataSpinDto>>();
|
_ = provider.GetRequiredService<TelemetryDataCache<TelemetryDataSpinDto>>();
|
||||||
|
|
||||||
var backgroundWorker = provider.GetRequiredService<BackgroundWorker>();
|
var backgroundWorker = provider.GetRequiredService<BackgroundWorker>();
|
||||||
backgroundWorker.Push(WellInfoService.MakeWork());
|
backgroundWorker.WorkStore.AddPeriodic(WellInfoService.MakeWork(), TimeSpan.FromMinutes(30));
|
||||||
backgroundWorker.Push(OperationDetectionWorkFactory.MakeWork());
|
backgroundWorker.WorkStore.AddPeriodic(OperationDetectionWorkFactory.MakeWork(), TimeSpan.FromMinutes(15));
|
||||||
backgroundWorker.Push(SubsystemOperationTimeCalcWorkFactory.MakeWork());
|
backgroundWorker.WorkStore.AddPeriodic(SubsystemOperationTimeCalcWorkFactory.MakeWork(), TimeSpan.FromMinutes(30));
|
||||||
backgroundWorker.Push(LimitingParameterCalcWorkFactory.MakeWork());
|
backgroundWorker.WorkStore.AddPeriodic(LimitingParameterCalcWorkFactory.MakeWork(), TimeSpan.FromMinutes(30));
|
||||||
backgroundWorker.Push(MakeMemoryMonitoringWork());
|
backgroundWorker.WorkStore.AddPeriodic(MakeMemoryMonitoringWork(), TimeSpan.FromMinutes(1));
|
||||||
|
|
||||||
var notificationBackgroundWorker = provider.GetRequiredService<NotificationBackgroundWorker>();
|
var notificationBackgroundWorker = provider.GetRequiredService<NotificationBackgroundWorker>();
|
||||||
|
|
||||||
@ -48,17 +47,15 @@ namespace AsbCloudInfrastructure
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
static WorkPeriodic MakeMemoryMonitoringWork()
|
static Work MakeMemoryMonitoringWork()
|
||||||
{
|
{
|
||||||
var workId = "Memory monitoring";
|
|
||||||
var workAction = (string _, IServiceProvider _, Action<string, double?> _, CancellationToken _) => {
|
var workAction = (string _, IServiceProvider _, Action<string, double?> _, CancellationToken _) => {
|
||||||
var bytes = GC.GetTotalMemory(false);
|
var bytes = GC.GetTotalMemory(false);
|
||||||
var bytesString = FromatBytes(bytes);
|
var bytesString = FromatBytes(bytes);
|
||||||
System.Diagnostics.Trace.TraceInformation($"Total memory allocated is {bytesString} bytes. DbContext count is:{AsbCloudDbContext.ReferenceCount}");
|
System.Diagnostics.Trace.TraceInformation($"Total memory allocated is {bytesString} bytes. DbContext count is:{AsbCloudDbContext.ReferenceCount}");
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
};
|
};
|
||||||
var workPeriod = TimeSpan.FromMinutes(1);
|
var work = new Work("Memory monitoring", workAction);
|
||||||
var work = new WorkPeriodic(workId, workAction, workPeriod);
|
|
||||||
return work;
|
return work;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -366,7 +366,7 @@ namespace AsbCloudWebApi.Tests.ServicesTests
|
|||||||
var state = await service.GetStateAsync(idWell, publisher1.Id, CancellationToken.None);
|
var state = await service.GetStateAsync(idWell, publisher1.Id, CancellationToken.None);
|
||||||
|
|
||||||
Assert.Equal(2, state.IdState);
|
Assert.Equal(2, state.IdState);
|
||||||
backgroundWorkerMock.Verify(s => s.Push(It.IsAny<WorkBase>()));
|
backgroundWorkerMock.Verify(s => s.Push(It.IsAny<Work>()));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
@ -29,12 +29,12 @@ public class SignalRNotificationTransportService : INotificationTransportService
|
|||||||
{
|
{
|
||||||
var workId = HashCode.Combine(notifications.Select(n => n.Id)).ToString("x");
|
var workId = HashCode.Combine(notifications.Select(n => n.Id)).ToString("x");
|
||||||
|
|
||||||
if (backgroundWorker.Contains(workId))
|
if (backgroundWorker.WorkStore.RunOnceQueue.Any(w => w.Id == workId))
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
|
|
||||||
var workAction = MakeSignalRSendWorkAction(notifications);
|
var workAction = MakeSignalRSendWorkAction(notifications);
|
||||||
var work = new WorkBase(workId, workAction);
|
var work = new Work(workId, workAction);
|
||||||
backgroundWorker.Push(work);
|
backgroundWorker.WorkStore.RunOnceQueue.Enqueue(work);
|
||||||
|
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user