Update campaign handling

This commit is contained in:
2025-10-27 16:56:08 +09:00
parent 445bfe8273
commit 6b3733001a
7 changed files with 86 additions and 11 deletions

View File

@ -8,16 +8,21 @@ namespace Hcs.WebApp.BackgroundServices
ManagerFactory managerFactory, ManagerFactory managerFactory,
IServiceScopeFactory scopeFactory) : BackgroundService IServiceScopeFactory scopeFactory) : BackgroundService
{ {
private const int ITERATION_TIME = 1000;
private const int SLEEP_TIME = 30000; private const int SLEEP_TIME = 30000;
private readonly CampaignManagementState campaignManagementState = campaignManagementState; private readonly CampaignManagementState campaignManagementState = campaignManagementState;
private readonly ManagerFactory managerFactory = managerFactory; private readonly ManagerFactory managerFactory = managerFactory;
private readonly IServiceScopeFactory scopeFactory = scopeFactory; private readonly IServiceScopeFactory scopeFactory = scopeFactory;
private readonly List<IManager> managers = [];
protected override async Task ExecuteAsync(CancellationToken stoppingToken) protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{ {
await InitializeStateAsync(); await InitializeStateAsync();
using var scope = scopeFactory.CreateScope();
var headquartersService = scope.ServiceProvider.GetRequiredService<HeadquartersService>();
while (!stoppingToken.IsCancellationRequested) while (!stoppingToken.IsCancellationRequested)
{ {
while (campaignManagementState.TryDequeueCampaign(out var campaign)) while (campaignManagementState.TryDequeueCampaign(out var campaign))
@ -28,15 +33,25 @@ namespace Hcs.WebApp.BackgroundServices
{ {
var manager = managerFactory.CreateManager(campaign); var manager = managerFactory.CreateManager(campaign);
await manager.StartAsync(stoppingToken); await manager.StartAsync(stoppingToken);
managers.Add(manager);
} }
catch catch (Exception e)
{ {
// TODO: Добавить таймауты await headquartersService.SetCampaignEndedWithFail(campaign.Id, e.Message);
campaignManagementState.EnqueueCampaign(campaign);
} }
} }
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);
}
} }
} }

View File

@ -2,6 +2,16 @@
{ {
public interface IManager public interface IManager
{ {
public enum ManagerState
{
Created,
Started,
Working,
Ended
}
public ManagerState State { get; }
Task StartAsync(CancellationToken cancellationToken); Task StartAsync(CancellationToken cancellationToken);
} }
} }

View File

@ -8,6 +8,8 @@ namespace Hcs.WebApp.BackgroundServices.CampaignManagers
protected readonly IServiceScopeFactory scopeFactory = scopeFactory; protected readonly IServiceScopeFactory scopeFactory = scopeFactory;
protected readonly Campaign campaign = campaign; protected readonly Campaign campaign = campaign;
public IManager.ManagerState State { get; } = IManager.ManagerState.Created;
public abstract Task StartAsync(CancellationToken cancellationToken); public abstract Task StartAsync(CancellationToken cancellationToken);
} }
} }

View File

@ -38,12 +38,12 @@ namespace Hcs.WebApp.BackgroundServices
try try
{ {
var executor = executorFactory.CreateExecutor(client, operation); var executor = executorFactory.CreateExecutor(client, operation);
await headquartersService.SetOperationStarted(operation.Id);
messageGuid = await executor.ExecuteAsync(stoppingToken); messageGuid = await executor.ExecuteAsync(stoppingToken);
} }
catch catch (Exception e)
{ {
// TODO: Добавить таймауты и макс количество попыток выполнения операции await headquartersService.SetOperationEndedWithFail(operation.Id, e.Message);
state.EnqueueOperation(operation);
} }
if (!string.IsNullOrEmpty(messageGuid)) if (!string.IsNullOrEmpty(messageGuid))

View File

@ -15,10 +15,16 @@ namespace Hcs.WebApp.Data.Hcs
public string InitiatorId { get; set; } 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 DateTime? EndedAt { get; set; }
public int Step { get; set; }
public string FailureReason { get; set; }
public virtual ICollection<Operation> Operations { get; set; } = []; public virtual ICollection<Operation> Operations { get; set; } = [];
[NotMapped] [NotMapped]

View File

@ -17,12 +17,16 @@ namespace Hcs.WebApp.Data.Hcs
public OperationType Type { get; set; } 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 DateTime? EndedAt { get; set; }
public string? MessageGuid { get; set; } public string? MessageGuid { get; set; }
public string FailureReason { get; set; }
public virtual ICollection<Registry> Registries { get; set; } = []; public virtual ICollection<Registry> Registries { get; set; } = [];
[NotMapped] [NotMapped]

View File

@ -36,7 +36,7 @@ namespace Hcs.WebApp.Services
{ {
Type = type, Type = type,
InitiatorId = initiatorId, InitiatorId = initiatorId,
StartedAt = DateTime.UtcNow CreatedAt = DateTime.UtcNow
}; };
await context.Campaigns.AddAsync(campaign); await context.Campaigns.AddAsync(campaign);
await context.SaveChangesAsync(); await context.SaveChangesAsync();
@ -50,13 +50,51 @@ namespace Hcs.WebApp.Services
{ {
CampaignId = campaignId, CampaignId = campaignId,
Type = type, Type = type,
StartedAt = DateTime.UtcNow CreatedAt = DateTime.UtcNow
}; };
await context.Operations.AddAsync(operation); await context.Operations.AddAsync(operation);
await context.SaveChangesAsync(); await context.SaveChangesAsync();
return operation; 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) public async Task SetOperationMessageGuidAsync(int operationId, string messageGuid)
{ {
using var context = factory.CreateDbContext(); using var context = factory.CreateDbContext();