feat: add background task
This commit is contained in:
parent
b095ca9749
commit
80e74b34c1
15
Endpoint/Common/Services/ScheduleSyncManager.cs
Normal file
15
Endpoint/Common/Services/ScheduleSyncManager.cs
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Mirea.Api.Endpoint.Common.Services;
|
||||||
|
|
||||||
|
public static class ScheduleSyncManager
|
||||||
|
{
|
||||||
|
public static event Action? OnUpdateIntervalRequested;
|
||||||
|
public static event Action? OnForceSyncRequested;
|
||||||
|
|
||||||
|
public static void RequestIntervalUpdate() =>
|
||||||
|
OnUpdateIntervalRequested?.Invoke();
|
||||||
|
|
||||||
|
public static void RequestForceSync() =>
|
||||||
|
OnForceSyncRequested?.Invoke();
|
||||||
|
}
|
@ -0,0 +1,121 @@
|
|||||||
|
using Cronos;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Microsoft.Extensions.Hosting;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Microsoft.Extensions.Options;
|
||||||
|
using Mirea.Api.Endpoint.Common.Services;
|
||||||
|
using Mirea.Api.Endpoint.Configuration.Model;
|
||||||
|
using Mirea.Api.Endpoint.Sync;
|
||||||
|
using System;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using IServiceProvider = System.IServiceProvider;
|
||||||
|
|
||||||
|
namespace Mirea.Api.Endpoint.Configuration.Core.BackgroundTasks;
|
||||||
|
|
||||||
|
public class ScheduleSyncService : IHostedService, IDisposable
|
||||||
|
{
|
||||||
|
private Timer? _timer;
|
||||||
|
private readonly IOptionsMonitor<GeneralConfig> _generalConfigMonitor;
|
||||||
|
private readonly ILogger<ScheduleSyncService> _logger;
|
||||||
|
private CancellationTokenSource _cancellationTokenSource = new();
|
||||||
|
private readonly IServiceProvider _serviceProvider;
|
||||||
|
|
||||||
|
public ScheduleSyncService(IOptionsMonitor<GeneralConfig> generalConfigMonitor, ILogger<ScheduleSyncService> logger, IServiceProvider serviceProvider)
|
||||||
|
{
|
||||||
|
_generalConfigMonitor = generalConfigMonitor;
|
||||||
|
_logger = logger;
|
||||||
|
_serviceProvider = serviceProvider;
|
||||||
|
|
||||||
|
ScheduleSyncManager.OnForceSyncRequested += OnForceSyncRequested;
|
||||||
|
ScheduleSyncManager.OnUpdateIntervalRequested += OnUpdateIntervalRequested;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnForceSyncRequested()
|
||||||
|
{
|
||||||
|
StopAsync(default).ContinueWith(_ =>
|
||||||
|
{
|
||||||
|
_cancellationTokenSource = new CancellationTokenSource();
|
||||||
|
ExecuteTask(null);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnUpdateIntervalRequested()
|
||||||
|
{
|
||||||
|
StopAsync(default).ContinueWith(_ =>
|
||||||
|
{
|
||||||
|
StartAsync(default);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ScheduleNextRun()
|
||||||
|
{
|
||||||
|
var cronExpression = _generalConfigMonitor.CurrentValue.ScheduleSettings?.CronUpdateSchedule;
|
||||||
|
if (string.IsNullOrEmpty(cronExpression))
|
||||||
|
{
|
||||||
|
_logger.LogWarning("Cron expression is not set. The scheduled task will not run.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var nextRunTime = CronExpression.Parse(cronExpression).GetNextOccurrence(DateTimeOffset.Now, TimeZoneInfo.Local);
|
||||||
|
|
||||||
|
if (!nextRunTime.HasValue)
|
||||||
|
{
|
||||||
|
_logger.LogWarning("No next run time found. The task will not be scheduled. Timezone: {TimeZone}", TimeZoneInfo.Local.DisplayName);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_logger.LogInformation("Next task run in {Time}", nextRunTime.Value.ToString("G"));
|
||||||
|
|
||||||
|
var delay = (nextRunTime.Value - DateTimeOffset.Now).TotalMilliseconds;
|
||||||
|
|
||||||
|
// The chance is small, but it's better to check
|
||||||
|
if (delay <= 0)
|
||||||
|
delay = 1;
|
||||||
|
|
||||||
|
_cancellationTokenSource = new CancellationTokenSource();
|
||||||
|
_timer = new Timer(ExecuteTask, null, (int)delay, Timeout.Infinite);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void ExecuteTask(object? state)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using var scope = _serviceProvider.CreateScope();
|
||||||
|
var syncService = ActivatorUtilities.GetServiceOrCreateInstance<ScheduleSynchronizer>(scope.ServiceProvider);
|
||||||
|
await syncService.StartSync(_cancellationTokenSource.Token);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "Error occurred during schedule synchronization.");
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
ScheduleNextRun();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task StartAsync(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
ScheduleNextRun();
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task StopAsync(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
_cancellationTokenSource.Cancel();
|
||||||
|
_timer?.Change(Timeout.Infinite, 0);
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
StopAsync(default).GetAwaiter().GetResult();
|
||||||
|
_timer?.Dispose();
|
||||||
|
ScheduleSyncManager.OnForceSyncRequested -= OnForceSyncRequested;
|
||||||
|
ScheduleSyncManager.OnUpdateIntervalRequested -= OnUpdateIntervalRequested;
|
||||||
|
_cancellationTokenSource.Dispose();
|
||||||
|
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
|
}
|
||||||
|
}
|
@ -5,9 +5,9 @@
|
|||||||
<ImplicitUsings>disable</ImplicitUsings>
|
<ImplicitUsings>disable</ImplicitUsings>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<Company>Winsomnia</Company>
|
<Company>Winsomnia</Company>
|
||||||
<Version>1.0-rc3</Version>
|
<Version>1.0-rc4</Version>
|
||||||
<AssemblyVersion>1.0.2.3</AssemblyVersion>
|
<AssemblyVersion>1.0.2.4</AssemblyVersion>
|
||||||
<FileVersion>1.0.2.3</FileVersion>
|
<FileVersion>1.0.2.4</FileVersion>
|
||||||
<AssemblyName>Mirea.Api.Endpoint</AssemblyName>
|
<AssemblyName>Mirea.Api.Endpoint</AssemblyName>
|
||||||
<RootNamespace>$(AssemblyName)</RootNamespace>
|
<RootNamespace>$(AssemblyName)</RootNamespace>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
|
@ -10,6 +10,7 @@ using Mirea.Api.DataAccess.Persistence;
|
|||||||
using Mirea.Api.DataAccess.Persistence.Common;
|
using Mirea.Api.DataAccess.Persistence.Common;
|
||||||
using Mirea.Api.Endpoint.Common.Interfaces;
|
using Mirea.Api.Endpoint.Common.Interfaces;
|
||||||
using Mirea.Api.Endpoint.Common.Services;
|
using Mirea.Api.Endpoint.Common.Services;
|
||||||
|
using Mirea.Api.Endpoint.Configuration.Core.BackgroundTasks;
|
||||||
using Mirea.Api.Endpoint.Configuration.Core.Middleware;
|
using Mirea.Api.Endpoint.Configuration.Core.Middleware;
|
||||||
using Mirea.Api.Endpoint.Configuration.Core.Startup;
|
using Mirea.Api.Endpoint.Configuration.Core.Startup;
|
||||||
using Mirea.Api.Endpoint.Configuration.Model;
|
using Mirea.Api.Endpoint.Configuration.Model;
|
||||||
@ -63,6 +64,8 @@ public class Program
|
|||||||
builder.Services.AddSingleton<IMaintenanceModeService, MaintenanceModeService>();
|
builder.Services.AddSingleton<IMaintenanceModeService, MaintenanceModeService>();
|
||||||
builder.Services.AddSingleton<ISetupToken, SetupTokenService>();
|
builder.Services.AddSingleton<ISetupToken, SetupTokenService>();
|
||||||
|
|
||||||
|
builder.Services.AddHostedService<ScheduleSyncService>();
|
||||||
|
|
||||||
builder.Services.AddMemoryCache();
|
builder.Services.AddMemoryCache();
|
||||||
builder.Services.AddCustomRedis(builder.Configuration, healthCheckBuilder);
|
builder.Services.AddCustomRedis(builder.Configuration, healthCheckBuilder);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user