Add more diagnostics to BackgroundWorker

This commit is contained in:
ngfrolov 2023-11-02 16:20:48 +05:00
parent 2c14aabe05
commit 552553bb64
Signed by untrusted user who does not match committer: ng.frolov
GPG Key ID: E99907A0357B29A7
5 changed files with 77 additions and 24 deletions

View File

@ -1,6 +1,7 @@
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;
@ -18,6 +19,8 @@ public class BackgroundWorker : BackgroundService
public WorkStore WorkStore { get; } = new WorkStore(); public WorkStore WorkStore { get; } = new WorkStore();
public Work? CurrentWork; public Work? CurrentWork;
public Exception? MainLoopLastException { get; private set; } = null;
public BackgroundWorker(IServiceProvider serviceProvider) public BackgroundWorker(IServiceProvider serviceProvider)
{ {
this.serviceProvider = serviceProvider; this.serviceProvider = serviceProvider;
@ -27,23 +30,39 @@ public class BackgroundWorker : BackgroundService
{ {
while (!token.IsCancellationRequested) while (!token.IsCancellationRequested)
{ {
var work = WorkStore.GetNext(); try
if (work is null)
{ {
await Task.Delay(executePeriod, token); var work = WorkStore.GetNext();
continue; if (work is null)
{
await Task.Delay(executePeriod, token);
continue;
}
CurrentWork = work;
using var scope = serviceProvider.CreateScope();
var result = await work.Start(scope.ServiceProvider, token);
if (!result)
WorkStore.Felled.Add(work);
else
WorkStore.Done.Add(work);
CurrentWork = null;
await Task.Delay(minDelay, token);
}
catch (Exception ex)
{
MainLoopLastException = ex;
var mainLoopLastException = $"BackgroundWorker " +
$"MainLoopLastException: \r\n" +
$"date: {DateTime.Now:O}\r\n" +
$"message: {ex.Message}\r\n" +
$"inner: {ex.InnerException?.Message}\r\n" +
$"stackTrace: {ex.StackTrace}";
Trace.TraceError(mainLoopLastException);
} }
CurrentWork = work;
using var scope = serviceProvider.CreateScope();
var result = await work.Start(scope.ServiceProvider, token);
if (!result)
WorkStore.Felled.Add(work);
CurrentWork = null;
await Task.Delay(minDelay, token);
} }
} }
} }

View File

@ -1,5 +1,6 @@
using AsbCloudApp.Data; using AsbCloudApp.Data;
using System; using System;
using System.Diagnostics;
using System.Text; using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -79,10 +80,18 @@ public abstract class Work : BackgroundWorkDto
SetLastError(message); SetLastError(message);
if (OnErrorAsync is not null) if (OnErrorAsync is not null)
{ {
var task = Task.Run( try
async () => await OnErrorAsync(Id, exception, token), {
token); var task = Task.Run(
await task.WaitAsync(OnErrorHandlerTimeout, token); async () => await OnErrorAsync(Id, exception, token),
token);
await task.WaitAsync(OnErrorHandlerTimeout, token);
}
catch (Exception onErrorAsyncException)
{
var message2 = FormatExceptionMessage(onErrorAsyncException);
Trace.TraceError($"Backgroud work:\"{Id}\" OnError handler throws exception: {message2}");
}
} }
} }
return false; return false;

View File

@ -23,12 +23,17 @@ public class WorkStore
/// Работы выполняемые один раз /// Работы выполняемые один раз
/// </summary> /// </summary>
public Queue<Work> RunOnceQueue { get; private set; } = new(8); public Queue<Work> RunOnceQueue { get; private set; } = new(8);
/// <summary> /// <summary>
/// Завершившиеся с ошибкой /// последние 16 завершившиеся с ошибкой
/// </summary> /// </summary>
public CyclycArray<Work> Felled { get; } = new(16); public CyclycArray<Work> Felled { get; } = new(16);
/// <summary>
/// последние 16 успешно завершенных
/// </summary>
public CyclycArray<Work> Done { get; } = new(16);
/// <summary> /// <summary>
/// Добавить фоновую работу выполняющуюся с заданным периодом /// Добавить фоновую работу выполняющуюся с заданным периодом
/// </summary> /// </summary>

View File

@ -26,7 +26,7 @@ public class WorkSubsystemOperationTimeCalc : Work
public WorkSubsystemOperationTimeCalc() public WorkSubsystemOperationTimeCalc()
: base("Subsystem operation time calc") : base("Subsystem operation time calc")
{ {
Timeout = TimeSpan.FromMinutes(20); Timeout = TimeSpan.FromMinutes(30);
} }
protected override async Task Action(string id, IServiceProvider services, Action<string, double?> onProgressCallback, CancellationToken token) protected override async Task Action(string id, IServiceProvider services, Action<string, double?> onProgressCallback, CancellationToken token)

View File

@ -2,7 +2,10 @@
using AsbCloudInfrastructure.Background; using AsbCloudInfrastructure.Background;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using System;
using System.Linq; using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace AsbCloudWebApi.Controllers namespace AsbCloudWebApi.Controllers
{ {
@ -23,14 +26,16 @@ namespace AsbCloudWebApi.Controllers
{ {
var result = new { var result = new {
CurrentWork = (BackgroundWorkDto?)backgroundWorker.CurrentWork, CurrentWork = (BackgroundWorkDto?)backgroundWorker.CurrentWork,
backgroundWorker.MainLoopLastException,
RunOnceQueue = backgroundWorker.WorkStore.RunOnceQueue.Select(work => (BackgroundWorkDto)work), RunOnceQueue = backgroundWorker.WorkStore.RunOnceQueue.Select(work => (BackgroundWorkDto)work),
Periodics = backgroundWorker.WorkStore.Periodics.Select(work => (BackgroundWorkDto)work.Work), Periodics = backgroundWorker.WorkStore.Periodics.Select(work => (BackgroundWorkDto)work.Work),
Done = backgroundWorker.WorkStore.Done.Select(work => (BackgroundWorkDto)work),
Felled = backgroundWorker.WorkStore.Felled.Select(work => (BackgroundWorkDto)work), Felled = backgroundWorker.WorkStore.Felled.Select(work => (BackgroundWorkDto)work),
}; };
return Ok(result); return Ok(result);
} }
[HttpGet("Current")] [HttpGet("current")]
public IActionResult GetCurrent() public IActionResult GetCurrent()
{ {
var work = backgroundWorker.CurrentWork; var work = backgroundWorker.CurrentWork;
@ -40,11 +45,26 @@ namespace AsbCloudWebApi.Controllers
return Ok(work); return Ok(work);
} }
[HttpGet("Failed")] [HttpGet("failed")]
public IActionResult GetFelled() public IActionResult GetFelled()
{ {
var result = backgroundWorker.WorkStore.Felled.Select(work => (BackgroundWorkDto)work); var result = backgroundWorker.WorkStore.Felled.Select(work => (BackgroundWorkDto)work);
return Ok(result); return Ok(result);
} }
[HttpGet("done")]
public IActionResult GetDone()
{
var result = backgroundWorker.WorkStore.Done.Select(work => (BackgroundWorkDto)work);
return Ok(result);
}
[HttpPost("restart"), Obsolete("temporary method")]
public async Task<IActionResult> RestartAsync(CancellationToken token)
{
await backgroundWorker.StopAsync(token);
await backgroundWorker.StartAsync(token);
return Ok();
}
} }
} }