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.
This commit is contained in:
2026-07-12 01:57:08 +09:00
parent 489da25f1b
commit 7283532c38
@@ -289,8 +289,16 @@ public static class HangfireServiceExtensions
Authorization = new[] { new HangfireAuthorizationFilter() }
});
// Initialize schedules
var schedulerService = serviceProvider.GetRequiredService<SchedulerService>();
// 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>();
schedulerService.InitializeSchedules();
return app;