diff --git a/Hcs.WebApp/BackgroundServices/CampaignManagementService.cs b/Hcs.WebApp/BackgroundServices/CampaignManagementService.cs index 589402e..3715bcf 100644 --- a/Hcs.WebApp/BackgroundServices/CampaignManagementService.cs +++ b/Hcs.WebApp/BackgroundServices/CampaignManagementService.cs @@ -8,16 +8,21 @@ namespace Hcs.WebApp.BackgroundServices ManagerFactory managerFactory, IServiceScopeFactory scopeFactory) : BackgroundService { + private const int ITERATION_TIME = 1000; private const int SLEEP_TIME = 30000; private readonly CampaignManagementState campaignManagementState = campaignManagementState; private readonly ManagerFactory managerFactory = managerFactory; private readonly IServiceScopeFactory scopeFactory = scopeFactory; + private readonly List managers = []; protected override async Task ExecuteAsync(CancellationToken stoppingToken) { await InitializeStateAsync(); + using var scope = scopeFactory.CreateScope(); + var headquartersService = scope.ServiceProvider.GetRequiredService(); + while (!stoppingToken.IsCancellationRequested) { while (campaignManagementState.TryDequeueCampaign(out var campaign)) @@ -28,15 +33,25 @@ namespace Hcs.WebApp.BackgroundServices { var manager = managerFactory.CreateManager(campaign); await manager.StartAsync(stoppingToken); + + managers.Add(manager); } - catch + catch (Exception e) { - // TODO: Добавить таймауты - campaignManagementState.EnqueueCampaign(campaign); + await headquartersService.SetCampaignEndedWithFail(campaign.Id, e.Message); } } - await Task.Delay(SLEEP_TIME, stoppingToken); + managers.RemoveAll(x => x.State == IManager.ManagerState.Ended); + + if (managers.Count > 0) + { + await Task.Delay(ITERATION_TIME, stoppingToken); + } + else + { + await Task.Delay(SLEEP_TIME, stoppingToken); + } } } diff --git a/Hcs.WebApp/BackgroundServices/CampaignManagers/IManager.cs b/Hcs.WebApp/BackgroundServices/CampaignManagers/IManager.cs index 5eeb06b..0960ceb 100644 --- a/Hcs.WebApp/BackgroundServices/CampaignManagers/IManager.cs +++ b/Hcs.WebApp/BackgroundServices/CampaignManagers/IManager.cs @@ -2,6 +2,16 @@ { public interface IManager { + public enum ManagerState + { + Created, + Started, + Working, + Ended + } + + public ManagerState State { get; } + Task StartAsync(CancellationToken cancellationToken); } } diff --git a/Hcs.WebApp/BackgroundServices/CampaignManagers/ManagerBase.cs b/Hcs.WebApp/BackgroundServices/CampaignManagers/ManagerBase.cs index fd1b276..43f868e 100644 --- a/Hcs.WebApp/BackgroundServices/CampaignManagers/ManagerBase.cs +++ b/Hcs.WebApp/BackgroundServices/CampaignManagers/ManagerBase.cs @@ -8,6 +8,8 @@ namespace Hcs.WebApp.BackgroundServices.CampaignManagers protected readonly IServiceScopeFactory scopeFactory = scopeFactory; protected readonly Campaign campaign = campaign; + public IManager.ManagerState State { get; } = IManager.ManagerState.Created; + public abstract Task StartAsync(CancellationToken cancellationToken); } } diff --git a/Hcs.WebApp/BackgroundServices/OperationExecutionService.cs b/Hcs.WebApp/BackgroundServices/OperationExecutionService.cs index fb6bee8..8930cfa 100644 --- a/Hcs.WebApp/BackgroundServices/OperationExecutionService.cs +++ b/Hcs.WebApp/BackgroundServices/OperationExecutionService.cs @@ -38,12 +38,12 @@ namespace Hcs.WebApp.BackgroundServices try { var executor = executorFactory.CreateExecutor(client, operation); + await headquartersService.SetOperationStarted(operation.Id); messageGuid = await executor.ExecuteAsync(stoppingToken); } - catch + catch (Exception e) { - // TODO: Добавить таймауты и макс количество попыток выполнения операции - state.EnqueueOperation(operation); + await headquartersService.SetOperationEndedWithFail(operation.Id, e.Message); } if (!string.IsNullOrEmpty(messageGuid)) diff --git a/Hcs.WebApp/Data/Hcs/Campaign.cs b/Hcs.WebApp/Data/Hcs/Campaign.cs index 15bd1e3..7786636 100644 --- a/Hcs.WebApp/Data/Hcs/Campaign.cs +++ b/Hcs.WebApp/Data/Hcs/Campaign.cs @@ -15,10 +15,16 @@ namespace Hcs.WebApp.Data.Hcs public string InitiatorId { get; set; } - public DateTime StartedAt { get; set; } + public DateTime CreatedAt { get; set; } + + public DateTime? StartedAt { get; set; } public DateTime? EndedAt { get; set; } + public int Step { get; set; } + + public string FailureReason { get; set; } + public virtual ICollection Operations { get; set; } = []; [NotMapped] diff --git a/Hcs.WebApp/Data/Hcs/Operation.cs b/Hcs.WebApp/Data/Hcs/Operation.cs index 89a8367..dce6c53 100644 --- a/Hcs.WebApp/Data/Hcs/Operation.cs +++ b/Hcs.WebApp/Data/Hcs/Operation.cs @@ -17,12 +17,16 @@ namespace Hcs.WebApp.Data.Hcs public OperationType Type { get; set; } - public DateTime StartedAt { get; set; } + public DateTime CreatedAt { get; set; } + + public DateTime? StartedAt { get; set; } public DateTime? EndedAt { get; set; } public string? MessageGuid { get; set; } + public string FailureReason { get; set; } + public virtual ICollection Registries { get; set; } = []; [NotMapped] diff --git a/Hcs.WebApp/Services/HeadquartersService.cs b/Hcs.WebApp/Services/HeadquartersService.cs index feb0106..a162c62 100644 --- a/Hcs.WebApp/Services/HeadquartersService.cs +++ b/Hcs.WebApp/Services/HeadquartersService.cs @@ -36,7 +36,7 @@ namespace Hcs.WebApp.Services { Type = type, InitiatorId = initiatorId, - StartedAt = DateTime.UtcNow + CreatedAt = DateTime.UtcNow }; await context.Campaigns.AddAsync(campaign); await context.SaveChangesAsync(); @@ -50,13 +50,51 @@ namespace Hcs.WebApp.Services { CampaignId = campaignId, Type = type, - StartedAt = DateTime.UtcNow + CreatedAt = DateTime.UtcNow }; await context.Operations.AddAsync(operation); await context.SaveChangesAsync(); return operation; } + public async Task SetCampaignEndedWithFail(int campaignId, string failureReason) + { + using var context = factory.CreateDbContext(); + var campaign = await context.Campaigns.FirstOrDefaultAsync(x => x.Id == campaignId); + if (campaign != null) + { + campaign.EndedAt = DateTime.UtcNow; + campaign.FailureReason = failureReason; + + await context.SaveChangesAsync(); + } + } + + public async Task SetOperationStarted(int operationId) + { + using var context = factory.CreateDbContext(); + var operation = await context.Operations.FirstOrDefaultAsync(x => x.Id == operationId); + if (operation != null) + { + operation.StartedAt = DateTime.UtcNow; + + await context.SaveChangesAsync(); + } + } + + public async Task SetOperationEndedWithFail(int operationId, string failureReason) + { + using var context = factory.CreateDbContext(); + var operation = await context.Operations.FirstOrDefaultAsync(x => x.Id == operationId); + if (operation != null) + { + operation.EndedAt = DateTime.UtcNow; + operation.FailureReason = failureReason; + + await context.SaveChangesAsync(); + } + } + public async Task SetOperationMessageGuidAsync(int operationId, string messageGuid) { using var context = factory.CreateDbContext();