From 7283532c38e5019cfd7e7a4064cf7a6a369c6efa Mon Sep 17 00:00:00 2001 From: kjh2064 Date: Sun, 12 Jul 2026 01:57:08 +0900 Subject: [PATCH] fix: Hangfire recurring-job initialization failing every startup Discovered while verifying the new Operations page against a local instance (SSH-tunneled to prod Postgres): every startup logged 'Hangfire setup failed: Cannot resolve scoped service QuantEngine.Web.Services.SchedulerService from root provider' and silently skipped InitializeSchedules() entirely. SchedulerService is registered AddScoped, but UseHangfireSetup() resolved it directly from app.Services (the root/singleton-level provider), which cannot construct scoped services without an active scope. This has apparently been broken for a while -- the 4 recurring jobs (daily-collection, hourly-price-update, weekly-report, monthly-optimization) only kept showing up because Hangfire persists recurring job definitions in PostgreSQL from whatever earlier deployment last managed to register them; any code change to those schedules would silently never take effect on redeploy. Fixed by creating an explicit scope (serviceProvider.CreateScope()) before resolving SchedulerService. Verified locally: the warning is gone and the log now shows "Hangfire schedules initialized successfully" followed by the dispatchers starting. --- .../QuantEngine.Web/Services/SchedulerService.cs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/dotnet/QuantEngine.Web/Services/SchedulerService.cs b/src/dotnet/QuantEngine.Web/Services/SchedulerService.cs index 301edfc9..69d118b3 100644 --- a/src/dotnet/QuantEngine.Web/Services/SchedulerService.cs +++ b/src/dotnet/QuantEngine.Web/Services/SchedulerService.cs @@ -289,8 +289,16 @@ public static class HangfireServiceExtensions Authorization = new[] { new HangfireAuthorizationFilter() } }); - // Initialize schedules - var schedulerService = serviceProvider.GetRequiredService(); + // Initialize schedules. SchedulerService is registered as Scoped + // (AddScoped above), so it cannot be resolved directly from the + // root provider passed in here (app.Services) -- doing so silently + // failed every startup with "Cannot resolve scoped service + // 'SchedulerService' from root provider", meaning the recurring + // jobs were never being freshly registered/updated on boot (they + // only appeared to work because Hangfire persists them in + // PostgreSQL from whichever startup last managed to run this). + using var scope = serviceProvider.CreateScope(); + var schedulerService = scope.ServiceProvider.GetRequiredService(); schedulerService.InitializeSchedules(); return app;