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.Services;
using Microsoft.EntityFrameworkCore;
using Minio;
using NodaTime;
using Prometheus;
using Serilog;
@ -57,16 +58,6 @@ public static class WebApplicationExtensions
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)
{
var file = Environment.GetEnvironmentVariable("FOXNOUNS_CONFIG_FILE") ?? "config.ini";
@ -78,18 +69,40 @@ public static class WebApplicationExtensions
.AddEnvironmentVariables();
}
public static IServiceCollection AddCustomServices(this IServiceCollection services) => services
.AddSingleton<IClock>(SystemClock.Instance)
.AddSnowflakeGenerator()
.AddScoped<UserRendererService>()
.AddScoped<MemberRendererService>()
.AddScoped<AuthService>()
.AddScoped<KeyCacheService>()
.AddScoped<RemoteAuthService>()
.AddScoped<ObjectStorageService>()
// Transient jobs
.AddTransient<MemberAvatarUpdateInvocable>()
.AddTransient<UserAvatarUpdateInvocable>();
/// <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)
.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
.AddScoped<ErrorHandlerMiddleware>()

View file

@ -1,12 +1,9 @@
using Coravel;
using Foxnouns.Backend;
using Foxnouns.Backend.Database;
using Serilog;
using Foxnouns.Backend.Extensions;
using Foxnouns.Backend.Services;
using Foxnouns.Backend.Utils;
using Microsoft.AspNetCore.Mvc;
using Minio;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using Prometheus;
@ -19,9 +16,7 @@ var builder = WebApplication.CreateBuilder(args);
var config = builder.AddConfiguration();
builder
.AddSerilog()
.AddMetrics(config);
builder.AddSerilog();
builder.WebHost
.UseSentry(opts =>
@ -64,16 +59,10 @@ JsonConvert.DefaultSettings = () => new JsonSerializerSettings
};
builder.Services
.AddQueue()
.AddDbContext<DatabaseContext>()
.AddCustomServices()
.AddServices(config)
.AddCustomMiddleware()
.AddEndpointsApiExplorer()
.AddSwaggerGen()
.AddMinio(c =>
c.WithEndpoint(config.Storage.Endpoint)
.WithCredentials(config.Storage.AccessKey, config.Storage.SecretKey)
.Build());
.AddSwaggerGen();
var app = builder.Build();
@ -97,23 +86,5 @@ app.Urls.Add(config.Address);
Metrics.DefaultRegistry.AddBeforeCollectCallback(async 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();
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();
}
Log.CloseAndFlush();

View file

@ -37,9 +37,9 @@ public class KeyCacheService(DatabaseContext db, IClock clock, ILogger logger)
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);
}

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