feat(api, Core, TaskReconciliationWorker): Воркер обработки зависших задач. Доработка логики.

This commit is contained in:
Mikhail Trubnikov
2026-04-23 16:08:16 +10:00
parent 7da2265d1c
commit e8086b3a7d
13 changed files with 275 additions and 6 deletions

View File

@@ -89,6 +89,10 @@ namespace PARR.Core.Services.Task.Implementations
// Не пробрасываем исключение дальше — задача создана, просто не в очереди.
// задача останется в БД со статусом pending, и Reconciliation Task позже ее возмет в работу
logger.LogError("Не удалось опубликовать задачу {TaskId} в очередь. Задача осталась в БД со статусом Pending.", task.Id);
// так как задачу не смогли отправить в очередь повторно, обновим ей DateModified, относительно нее считается время обработки, и задача встанет на повтор позже сама
task.DateModified = DateTimeOffset.UtcNow;
await taskRepository.CommitAsync();
}
return task.Id;

View File

@@ -98,12 +98,15 @@ namespace PARR.Core.Services.Task.Implementations
return await taskRepository.Get()
.Where(t =>
t.TypeCode == taskType.Code && t.DateModified < timeoutThreshold &&
(
//t.TypeCode == taskType.Code && t.DateModified < timeoutThreshold &&
t.TypeCode == taskType.Code
// если это вновь созданная задача и она упала, то у нее может не быть DateModified, тогда сравним с DateCreated
&& (t.DateModified ?? t.DateCreated) < timeoutThreshold
&& (
// Зависла в Processing
t.StatusCode == TaskItemStatusEnum.Processing
//Зависли в Pending
|| (t.StatusCode == TaskItemStatusEnum.Pending && t.RetryCount > 0)
|| (t.StatusCode == TaskItemStatusEnum.Pending /*&& t.RetryCount > 0*/)
)
)
.OrderBy(t => t.DateModified) // Сначала самые старые

View File

@@ -0,0 +1,54 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using PARR.Core.Common.Interfaces;
using PARR.Core.Services.Task.Interfaces;
using PARR.Domain.Settings;
namespace PARR.Core.Services.Task.ReconciliationHosted
{
/// <summary>
/// Фоновая задача для периодического запуска Reconciliation Job.
/// Обработка зависших задач.
/// </summary>
internal class ReconciliationHostedService : BackgroundService
{
// Это реализация воркера, подключается напрямую в воркер
private readonly ILogger<ReconciliationHostedService> logger;
private readonly IReconciliationSettings reconciliationSettings;
private readonly IServiceProvider serviceProvider;
private readonly IIntervalService intervalService;
public ReconciliationHostedService(
ILogger<ReconciliationHostedService> logger,
IReconciliationSettings reconciliationSettings,
IServiceProvider serviceProvider,
IIntervalService intervalService
)
{
this.logger = logger;
this.reconciliationSettings = reconciliationSettings;
this.serviceProvider = serviceProvider;
this.intervalService = intervalService;
}
protected override async System.Threading.Tasks.Task ExecuteAsync(CancellationToken stoppingToken)
{
await intervalService.IntervalInitAsync(async () =>
{
await using (var scope = serviceProvider.CreateAsyncScope())
{
var reconciliationService = scope.ServiceProvider.GetRequiredService<ITaskReconciliationService>();
// Запускаем проверку
var report = await reconciliationService.RunAsync();
if (report.HasChanged)
logger.LogInformation("Reconciliation Job выполнен. Проверено: {Checked}, Исправлено: {Fixed}", report.CheckedCount, report.FixedCount);
}
}, reconciliationSettings.CheckInterval);
}
}
}

View File

@@ -2,7 +2,6 @@
using Microsoft.Extensions.Logging;
using PARR.Core.Common.Interfaces;
using PARR.Core.Common.Interfaces.RabbitServices;
using PARR.Core.Services.Task.Handlers;
using PARR.Core.Services.Task.Handlers.Factory;
using PARR.Core.Services.Workload.Interfaces;
using PARR.Domain.Common.Rabbit.Messages;