111 lines
		
	
	
		
			4.2 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
			
		
		
	
	
			111 lines
		
	
	
		
			4.2 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
| using System;
 | |
| using System.Collections.Generic;
 | |
| using System.Threading;
 | |
| using System.Threading.Tasks;
 | |
| using Microsoft.Extensions.Hosting;
 | |
| using Microsoft.Extensions.Logging;
 | |
| using Microsoft.Extensions.Options;
 | |
| using StellaOps.Excititor.Worker.Options;
 | |
| 
 | |
| namespace StellaOps.Excititor.Worker.Scheduling;
 | |
| 
 | |
| internal sealed class VexWorkerHostedService : BackgroundService
 | |
| {
 | |
|     private readonly IOptions<VexWorkerOptions> _options;
 | |
|     private readonly IVexProviderRunner _runner;
 | |
|     private readonly ILogger<VexWorkerHostedService> _logger;
 | |
|     private readonly TimeProvider _timeProvider;
 | |
| 
 | |
|     public VexWorkerHostedService(
 | |
|         IOptions<VexWorkerOptions> options,
 | |
|         IVexProviderRunner runner,
 | |
|         ILogger<VexWorkerHostedService> logger,
 | |
|         TimeProvider timeProvider)
 | |
|     {
 | |
|         _options = options ?? throw new ArgumentNullException(nameof(options));
 | |
|         _runner = runner ?? throw new ArgumentNullException(nameof(runner));
 | |
|         _logger = logger ?? throw new ArgumentNullException(nameof(logger));
 | |
|         _timeProvider = timeProvider ?? throw new ArgumentNullException(nameof(timeProvider));
 | |
|     }
 | |
| 
 | |
|     protected override async Task ExecuteAsync(CancellationToken stoppingToken)
 | |
|     {
 | |
|         var schedules = _options.Value.ResolveSchedules();
 | |
|         if (schedules.Count == 0)
 | |
|         {
 | |
|             _logger.LogWarning("Excititor worker has no configured provider schedules; the service will remain idle.");
 | |
|             await Task.CompletedTask;
 | |
|             return;
 | |
|         }
 | |
| 
 | |
|         _logger.LogInformation("Excititor worker starting with {ProviderCount} provider schedule(s).", schedules.Count);
 | |
| 
 | |
|         var tasks = new List<Task>(schedules.Count);
 | |
|         foreach (var schedule in schedules)
 | |
|         {
 | |
|             tasks.Add(RunScheduleAsync(schedule, stoppingToken));
 | |
|         }
 | |
| 
 | |
|         await Task.WhenAll(tasks);
 | |
|     }
 | |
| 
 | |
|     private async Task RunScheduleAsync(VexWorkerSchedule schedule, CancellationToken cancellationToken)
 | |
|     {
 | |
|         try
 | |
|         {
 | |
|             if (schedule.InitialDelay > TimeSpan.Zero)
 | |
|             {
 | |
|                 _logger.LogInformation(
 | |
|                     "Provider {ProviderId} initial delay of {InitialDelay} before first execution.",
 | |
|                     schedule.ProviderId,
 | |
|                     schedule.InitialDelay);
 | |
| 
 | |
|                 await Task.Delay(schedule.InitialDelay, cancellationToken).ConfigureAwait(false);
 | |
|             }
 | |
| 
 | |
|             using var timer = new PeriodicTimer(schedule.Interval);
 | |
|             do
 | |
|             {
 | |
|                 var startedAt = _timeProvider.GetUtcNow();
 | |
|                 _logger.LogInformation(
 | |
|                     "Provider {ProviderId} run started at {StartedAt}. Interval={Interval}.",
 | |
|                     schedule.ProviderId,
 | |
|                     startedAt,
 | |
|                     schedule.Interval);
 | |
| 
 | |
|                 try
 | |
|                 {
 | |
|                     await _runner.RunAsync(schedule.ProviderId, cancellationToken).ConfigureAwait(false);
 | |
| 
 | |
|                     var completedAt = _timeProvider.GetUtcNow();
 | |
|                     var elapsed = completedAt - startedAt;
 | |
| 
 | |
|                     _logger.LogInformation(
 | |
|                         "Provider {ProviderId} run completed at {CompletedAt} (duration {Duration}).",
 | |
|                         schedule.ProviderId,
 | |
|                         completedAt,
 | |
|                         elapsed);
 | |
|                 }
 | |
|                 catch (OperationCanceledException) when (cancellationToken.IsCancellationRequested)
 | |
|                 {
 | |
|                     _logger.LogInformation("Provider {ProviderId} run cancelled.", schedule.ProviderId);
 | |
|                     break;
 | |
|                 }
 | |
|                 catch (Exception ex)
 | |
|                 {
 | |
|                     _logger.LogError(
 | |
|                         ex,
 | |
|                         "Provider {ProviderId} run failed: {Message}",
 | |
|                         schedule.ProviderId,
 | |
|                         ex.Message);
 | |
|                 }
 | |
|             }
 | |
|             while (await timer.WaitForNextTickAsync(cancellationToken).ConfigureAwait(false));
 | |
|         }
 | |
|         catch (OperationCanceledException) when (cancellationToken.IsCancellationRequested)
 | |
|         {
 | |
|             _logger.LogInformation("Provider {ProviderId} schedule cancelled.", schedule.ProviderId);
 | |
|         }
 | |
|     }
 | |
| }
 |