refactor: replace periodic tasks loop with background service
This commit is contained in:
parent
54ec469cd9
commit
fb324e7576
4 changed files with 67 additions and 57 deletions
|
@ -5,6 +5,7 @@ using Foxnouns.Backend.Jobs;
|
||||||
using Foxnouns.Backend.Middleware;
|
using Foxnouns.Backend.Middleware;
|
||||||
using Foxnouns.Backend.Services;
|
using Foxnouns.Backend.Services;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Minio;
|
||||||
using NodaTime;
|
using NodaTime;
|
||||||
using Prometheus;
|
using Prometheus;
|
||||||
using Serilog;
|
using Serilog;
|
||||||
|
@ -57,16 +58,6 @@ public static class WebApplicationExtensions
|
||||||
return config;
|
return config;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static WebApplicationBuilder AddMetrics(this WebApplicationBuilder builder, Config config)
|
|
||||||
{
|
|
||||||
builder.Services.AddMetricServer(o => o.Port = config.Logging.MetricsPort)
|
|
||||||
.AddSingleton<MetricsCollectionService>();
|
|
||||||
if (!config.Logging.EnableMetrics)
|
|
||||||
builder.Services.AddHostedService<BackgroundMetricsCollectionService>();
|
|
||||||
|
|
||||||
return builder;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IConfigurationBuilder AddConfiguration(this IConfigurationBuilder builder)
|
public static IConfigurationBuilder AddConfiguration(this IConfigurationBuilder builder)
|
||||||
{
|
{
|
||||||
var file = Environment.GetEnvironmentVariable("FOXNOUNS_CONFIG_FILE") ?? "config.ini";
|
var file = Environment.GetEnvironmentVariable("FOXNOUNS_CONFIG_FILE") ?? "config.ini";
|
||||||
|
@ -78,18 +69,40 @@ public static class WebApplicationExtensions
|
||||||
.AddEnvironmentVariables();
|
.AddEnvironmentVariables();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IServiceCollection AddCustomServices(this IServiceCollection services) => services
|
/// <summary>
|
||||||
.AddSingleton<IClock>(SystemClock.Instance)
|
/// Adds required services to the IServiceCollection.
|
||||||
.AddSnowflakeGenerator()
|
/// This should only add services that are not ASP.NET-related (i.e. no middleware).
|
||||||
.AddScoped<UserRendererService>()
|
/// </summary>
|
||||||
.AddScoped<MemberRendererService>()
|
public static IServiceCollection AddServices(this IServiceCollection services, Config config)
|
||||||
.AddScoped<AuthService>()
|
{
|
||||||
.AddScoped<KeyCacheService>()
|
services
|
||||||
.AddScoped<RemoteAuthService>()
|
.AddQueue()
|
||||||
.AddScoped<ObjectStorageService>()
|
.AddDbContext<DatabaseContext>()
|
||||||
// Transient jobs
|
.AddMetricServer(o => o.Port = config.Logging.MetricsPort)
|
||||||
.AddTransient<MemberAvatarUpdateInvocable>()
|
.AddMinio(c =>
|
||||||
.AddTransient<UserAvatarUpdateInvocable>();
|
c.WithEndpoint(config.Storage.Endpoint)
|
||||||
|
.WithCredentials(config.Storage.AccessKey, config.Storage.SecretKey)
|
||||||
|
.Build())
|
||||||
|
.AddSingleton<MetricsCollectionService>()
|
||||||
|
.AddSingleton<IClock>(SystemClock.Instance)
|
||||||
|
.AddSnowflakeGenerator()
|
||||||
|
.AddScoped<UserRendererService>()
|
||||||
|
.AddScoped<MemberRendererService>()
|
||||||
|
.AddScoped<AuthService>()
|
||||||
|
.AddScoped<KeyCacheService>()
|
||||||
|
.AddScoped<RemoteAuthService>()
|
||||||
|
.AddScoped<ObjectStorageService>()
|
||||||
|
// Background services
|
||||||
|
.AddHostedService<PeriodicTasksService>()
|
||||||
|
// Transient jobs
|
||||||
|
.AddTransient<MemberAvatarUpdateInvocable>()
|
||||||
|
.AddTransient<UserAvatarUpdateInvocable>();
|
||||||
|
|
||||||
|
if (!config.Logging.EnableMetrics)
|
||||||
|
services.AddHostedService<BackgroundMetricsCollectionService>();
|
||||||
|
|
||||||
|
return services;
|
||||||
|
}
|
||||||
|
|
||||||
public static IServiceCollection AddCustomMiddleware(this IServiceCollection services) => services
|
public static IServiceCollection AddCustomMiddleware(this IServiceCollection services) => services
|
||||||
.AddScoped<ErrorHandlerMiddleware>()
|
.AddScoped<ErrorHandlerMiddleware>()
|
||||||
|
|
|
@ -1,12 +1,9 @@
|
||||||
using Coravel;
|
|
||||||
using Foxnouns.Backend;
|
using Foxnouns.Backend;
|
||||||
using Foxnouns.Backend.Database;
|
|
||||||
using Serilog;
|
using Serilog;
|
||||||
using Foxnouns.Backend.Extensions;
|
using Foxnouns.Backend.Extensions;
|
||||||
using Foxnouns.Backend.Services;
|
using Foxnouns.Backend.Services;
|
||||||
using Foxnouns.Backend.Utils;
|
using Foxnouns.Backend.Utils;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Minio;
|
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using Newtonsoft.Json.Serialization;
|
using Newtonsoft.Json.Serialization;
|
||||||
using Prometheus;
|
using Prometheus;
|
||||||
|
@ -19,9 +16,7 @@ var builder = WebApplication.CreateBuilder(args);
|
||||||
|
|
||||||
var config = builder.AddConfiguration();
|
var config = builder.AddConfiguration();
|
||||||
|
|
||||||
builder
|
builder.AddSerilog();
|
||||||
.AddSerilog()
|
|
||||||
.AddMetrics(config);
|
|
||||||
|
|
||||||
builder.WebHost
|
builder.WebHost
|
||||||
.UseSentry(opts =>
|
.UseSentry(opts =>
|
||||||
|
@ -64,16 +59,10 @@ JsonConvert.DefaultSettings = () => new JsonSerializerSettings
|
||||||
};
|
};
|
||||||
|
|
||||||
builder.Services
|
builder.Services
|
||||||
.AddQueue()
|
.AddServices(config)
|
||||||
.AddDbContext<DatabaseContext>()
|
|
||||||
.AddCustomServices()
|
|
||||||
.AddCustomMiddleware()
|
.AddCustomMiddleware()
|
||||||
.AddEndpointsApiExplorer()
|
.AddEndpointsApiExplorer()
|
||||||
.AddSwaggerGen()
|
.AddSwaggerGen();
|
||||||
.AddMinio(c =>
|
|
||||||
c.WithEndpoint(config.Storage.Endpoint)
|
|
||||||
.WithCredentials(config.Storage.AccessKey, config.Storage.SecretKey)
|
|
||||||
.Build());
|
|
||||||
|
|
||||||
var app = builder.Build();
|
var app = builder.Build();
|
||||||
|
|
||||||
|
@ -97,23 +86,5 @@ app.Urls.Add(config.Address);
|
||||||
Metrics.DefaultRegistry.AddBeforeCollectCallback(async ct =>
|
Metrics.DefaultRegistry.AddBeforeCollectCallback(async ct =>
|
||||||
await app.Services.GetRequiredService<MetricsCollectionService>().CollectMetricsAsync(ct));
|
await app.Services.GetRequiredService<MetricsCollectionService>().CollectMetricsAsync(ct));
|
||||||
|
|
||||||
// Fire off the periodic tasks loop in the background
|
|
||||||
_ = new Timer(_ =>
|
|
||||||
{
|
|
||||||
var __ = RunPeriodicTasksAsync();
|
|
||||||
}, null, TimeSpan.FromSeconds(30), TimeSpan.FromMinutes(1));
|
|
||||||
|
|
||||||
app.Run();
|
app.Run();
|
||||||
Log.CloseAndFlush();
|
Log.CloseAndFlush();
|
||||||
|
|
||||||
return;
|
|
||||||
|
|
||||||
async Task RunPeriodicTasksAsync()
|
|
||||||
{
|
|
||||||
await using var scope = app.Services.CreateAsyncScope();
|
|
||||||
var logger = scope.ServiceProvider.GetRequiredService<ILogger>();
|
|
||||||
logger.Debug("Running periodic tasks");
|
|
||||||
|
|
||||||
var keyCacheSvc = scope.ServiceProvider.GetRequiredService<KeyCacheService>();
|
|
||||||
await keyCacheSvc.DeleteExpiredKeysAsync();
|
|
||||||
}
|
|
|
@ -37,9 +37,9 @@ public class KeyCacheService(DatabaseContext db, IClock clock, ILogger logger)
|
||||||
return value.Value;
|
return value.Value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task DeleteExpiredKeysAsync()
|
public async Task DeleteExpiredKeysAsync(CancellationToken ct)
|
||||||
{
|
{
|
||||||
var count = await db.TemporaryKeys.Where(k => k.Expires < clock.GetCurrentInstant()).ExecuteDeleteAsync();
|
var count = await db.TemporaryKeys.Where(k => k.Expires < clock.GetCurrentInstant()).ExecuteDeleteAsync(ct);
|
||||||
if (count != 0) logger.Information("Removed {Count} expired keys from the database", count);
|
if (count != 0) logger.Information("Removed {Count} expired keys from the database", count);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
26
Foxnouns.Backend/Services/PeriodicTasksService.cs
Normal file
26
Foxnouns.Backend/Services/PeriodicTasksService.cs
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
namespace Foxnouns.Backend.Services;
|
||||||
|
|
||||||
|
public class PeriodicTasksService(ILogger logger, IServiceProvider services) : BackgroundService
|
||||||
|
{
|
||||||
|
private readonly ILogger _logger = logger.ForContext<PeriodicTasksService>();
|
||||||
|
|
||||||
|
protected override async Task ExecuteAsync(CancellationToken ct)
|
||||||
|
{
|
||||||
|
using var timer = new PeriodicTimer(TimeSpan.FromMinutes(1));
|
||||||
|
while (await timer.WaitForNextTickAsync(ct))
|
||||||
|
{
|
||||||
|
_logger.Debug("Collecting metrics");
|
||||||
|
await RunPeriodicTasksAsync(ct);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task RunPeriodicTasksAsync(CancellationToken ct)
|
||||||
|
{
|
||||||
|
_logger.Debug("Running periodic tasks");
|
||||||
|
|
||||||
|
await using var scope = services.CreateAsyncScope();
|
||||||
|
|
||||||
|
var keyCacheSvc = scope.ServiceProvider.GetRequiredService<KeyCacheService>();
|
||||||
|
await keyCacheSvc.DeleteExpiredKeysAsync(ct);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue