WorkBase добавлено больше диагностической информации.

This commit is contained in:
Frolov-Nikita 2023-10-08 13:09:09 +05:00
parent 01f04c7ea5
commit 673cb8960c
No known key found for this signature in database
GPG Key ID: 719E3386D12B0760
7 changed files with 120 additions and 35 deletions

View File

@ -14,10 +14,10 @@ namespace AsbCloudInfrastructure.Background
{
private static readonly TimeSpan executePeriod = TimeSpan.FromSeconds(10);
private static readonly TimeSpan minDelay = TimeSpan.FromSeconds(2);
private static readonly TimeSpan exceptionHandleTimeout = TimeSpan.FromSeconds(2);
private readonly IServiceProvider serviceProvider;
private readonly WorkQueue workQueue = new WorkQueue();
public string? CurrentWorkId;
public BackgroundWorker(IServiceProvider serviceProvider)
{
this.serviceProvider = serviceProvider;
@ -58,36 +58,18 @@ namespace AsbCloudInfrastructure.Background
{
while (!token.IsCancellationRequested)
{
var dateStart = DateTime.Now;
var work = workQueue.Pop();
if (work is null)
{
await Task.Delay(executePeriod, token);
continue;
}
CurrentWorkId = work.Id;
using var scope = serviceProvider.CreateScope();
try
{
Trace.TraceInformation($"Backgroud work:\"{work.Id}\" start.");
var task = work.ActionAsync(work.Id, scope.ServiceProvider, token);
await task.WaitAsync(work.Timeout, token);
work.ExecutionTime = DateTime.Now - dateStart;
Trace.TraceInformation($"Backgroud work:\"{work.Id}\" done. ExecutionTime: {work.ExecutionTime:hh\\:mm\\:ss\\.fff}");
}
catch (Exception exception)
{
Trace.TraceError($"Backgroud work:\"{work.Id}\" throw exception: {exception.Message}");
if (work.OnErrorAsync is not null)
{
using var task = Task.Run(
async () => await work.OnErrorAsync(work.Id, exception, token),
token);
await task.WaitAsync(exceptionHandleTimeout, token);
}
}
await work.Start(scope.ServiceProvider, token);
CurrentWorkId = null;
await Task.Delay(minDelay, token);
}

View File

@ -1,10 +1,11 @@
using System;
using System.Diagnostics;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace AsbCloudInfrastructure.Background
{
/// <summary>
/// Класс разовой работы.
/// Разовая работа приоритетнее периодической.
@ -14,7 +15,7 @@ namespace AsbCloudInfrastructure.Background
/// <summary>
/// Идентификатор работы. Должен быть уникальным. Используется в логах и передается в колбэки.
/// </summary>
public string Id { get; private set; }
public string Id { get; }
/// <summary>
/// Делегат работы.
@ -36,7 +37,7 @@ namespace AsbCloudInfrastructure.Background
/// </list>
/// </para>
/// </summary>
internal Func<string, IServiceProvider, CancellationToken, Task> ActionAsync { get; set; }
internal Func<string, IServiceProvider, CancellationToken, Task> ActionAsync { get; }
/// <summary>
/// Делегат обработки ошибки.
@ -50,20 +51,118 @@ namespace AsbCloudInfrastructure.Background
public TimeSpan Timeout { get; set; } = TimeSpan.FromMinutes(1);
/// <summary>
/// Фактическое время успешного выполнения работы
/// Продолжительность последнего выполнения
/// </summary>
public TimeSpan? ExecutionTime { get; internal set; }
public TimeSpan? LastExecutionTime { get; private set; }
/// <summary>
/// Текущее время выполнения
/// </summary>
public TimeSpan? CurrentExecutionTime => CurrentStart.HasValue
? DateTime.Now - CurrentStart.Value
: null;
/// <summary>
/// Время последнего запуска
/// </summary>
public DateTime LastStart { get; set; }
public DateTime? CurrentStart { get; private set; }
/// <summary>
/// Текстовое описание того, что происходит в задаче.
/// </summary>
public string? CurrentStatus { get; private set; }
/// <summary>
/// Время последнего запуска
/// </summary>
public DateTime? CurrentStatusUpdate { get; private set; }
/// <summary>
/// Последняя ошибка
/// </summary>
public string? LastErrorMessage { get; private set; }
/// <summary>
/// Дата последнего запуска
/// </summary>
public DateTime? LastStart { get; private set; }
/// <summary>
/// Дата последней ошибки
/// </summary>
public DateTime? LastError { get; private set; }
/// <summary>
/// Дата завершения последнего выполнения
/// </summary>
public DateTime? LastComplete { get; private set; }
/// <summary>
/// Кол-во завершений
/// </summary>
public int CountComplete { get; private set; }
/// <summary>
/// Кол-во ошибок
/// </summary>
public int CountErrors { get; private set; }
private string WorkNameForTrace => $"Backgroud work:\"{Id}\"";
public WorkBase(string id, Func<string, IServiceProvider, CancellationToken, Task> actionAsync)
{
Id = id;
ActionAsync = actionAsync;
}
}
public async Task Start(IServiceProvider services, CancellationToken token)
{
CurrentStart = DateTime.Now;
LastStart = DateTime.Now;
try
{
SetStatus(" start");
var task = ActionAsync(Id, services, token);
await task.WaitAsync(Timeout, token);
LastComplete = DateTime.Now;
CountComplete++;
SetStatus($" {task.Status}. ExecutionTime: {CurrentExecutionTime:hh\\:mm\\:ss\\.fff}");
}
catch (Exception exception)
{
SetError(exception.Message);
if (OnErrorAsync is not null)
{
var task = Task.Run(
async () => await OnErrorAsync(Id, exception, token),
token);
await task.WaitAsync(Timeout, token);
}
}
LastExecutionTime = CurrentExecutionTime;
CurrentStart = null;
SetStatus(null);
}
protected void SetStatus(string? newStatus)
{
CurrentStatus = newStatus;
if (newStatus is not null)
{
CurrentStatusUpdate = DateTime.Now;
Trace.TraceInformation($"{WorkNameForTrace} state: {newStatus}");
}
else
CurrentStatusUpdate = null;
}
private void SetError(string? errorMessage)
{
CountErrors++;
LastErrorMessage = errorMessage;
LastError = DateTime.Now;
Trace.TraceError($"{WorkNameForTrace} throw exception[{CountErrors}]: {errorMessage}");
}
}
}

View File

@ -18,7 +18,7 @@ namespace AsbCloudInfrastructure.Background
/// <summary>
/// Время следующего запуска
/// </summary>
public DateTime NextStart => LastStart + Period;
public DateTime NextStart => LastStart??DateTime.MinValue + Period;
/// <summary>
/// Класс периодической работы

View File

@ -4,7 +4,6 @@ using System.Linq;
namespace AsbCloudInfrastructure.Background
{
/// <summary>
/// <para>
/// Очередь работ
@ -90,7 +89,6 @@ namespace AsbCloudInfrastructure.Background
if (work is null || work.NextStart > DateTime.Now)
return null;
work.LastStart = DateTime.Now;
return work;
}

View File

@ -1,5 +1,12 @@
# Проблемы фонового сервиса
- нет понимания по загрузке очереди работ
- Нужно состояние по загрузки сервиса и очереди работ.
- Все ли задачи укладываются в таймаут,
- Сколько свободного времени остается,
- Что делает текущая задача,
- нет управления сервисом. Для исключения его влияния на другие процессы сервера.
- отключать/включать целиком
- отключать/включать отдельную периодическую задачу
# Сделать
- Разработать dto статуса задачи
- Отказаться от периодической задачи, при добавлении в хранилище задач период будет параметром метода добавления.

View File

@ -541,7 +541,6 @@ namespace AsbCloudInfrastructure.Services.DrillingProgram
var work = new WorkBase(workId, workAction)
{
ExecutionTime = TimeSpan.FromMinutes(1),
OnErrorAsync = onErrorAction
};

View File

@ -88,7 +88,7 @@ namespace AsbCloudWebApi.Tests.ServicesTests
await BackgroundWorker.StartAsync(CancellationToken.None);
await Task.Delay(10);
Assert.True(work.ExecutionTime > TimeSpan.Zero);
Assert.True(work.LastExecutionTime > TimeSpan.Zero);
}
[Fact]