refactor: replace periodic tasks loop with background service

This commit is contained in:
sam 2024-09-04 01:46:39 +02:00
parent 54ec469cd9
commit fb324e7576
Signed by: sam
GPG key ID: B4EF20DDE721CAA1
4 changed files with 67 additions and 57 deletions

View file

@ -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,7 +69,21 @@ public static class WebApplicationExtensions
.AddEnvironmentVariables(); .AddEnvironmentVariables();
} }
public static IServiceCollection AddCustomServices(this IServiceCollection services) => services /// <summary>
/// Adds required services to the IServiceCollection.
/// This should only add services that are not ASP.NET-related (i.e. no middleware).
/// </summary>
public static IServiceCollection AddServices(this IServiceCollection services, Config config)
{
services
.AddQueue()
.AddDbContext<DatabaseContext>()
.AddMetricServer(o => o.Port = config.Logging.MetricsPort)
.AddMinio(c =>
c.WithEndpoint(config.Storage.Endpoint)
.WithCredentials(config.Storage.AccessKey, config.Storage.SecretKey)
.Build())
.AddSingleton<MetricsCollectionService>()
.AddSingleton<IClock>(SystemClock.Instance) .AddSingleton<IClock>(SystemClock.Instance)
.AddSnowflakeGenerator() .AddSnowflakeGenerator()
.AddScoped<UserRendererService>() .AddScoped<UserRendererService>()
@ -87,10 +92,18 @@ public static class WebApplicationExtensions
.AddScoped<KeyCacheService>() .AddScoped<KeyCacheService>()
.AddScoped<RemoteAuthService>() .AddScoped<RemoteAuthService>()
.AddScoped<ObjectStorageService>() .AddScoped<ObjectStorageService>()
// Background services
.AddHostedService<PeriodicTasksService>()
// Transient jobs // Transient jobs
.AddTransient<MemberAvatarUpdateInvocable>() .AddTransient<MemberAvatarUpdateInvocable>()
.AddTransient<UserAvatarUpdateInvocable>(); .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>()
.AddScoped<AuthenticationMiddleware>() .AddScoped<AuthenticationMiddleware>()

View file

@ -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();
}

View file

@ -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);
} }

View 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);
}
}