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…
	
	Add table
		Add a link
		
	
		Reference in a new issue