Hangfire icon indicating copy to clipboard operation
Hangfire copied to clipboard

Register BackgroundJobServerHostedService as singleton

Open hankovich opened this issue 4 years ago • 3 comments

I use Hangfire with an unstable JobStorage that sometimes loses data (all information about servers, executions, scheduled jobs).

I have a HostedService which periodacally checks that jobs are still scheduled and reschedules them again if not. But I need to somehow stop the existing server and start a new one which will announce itself and save the corresping data about a new server and queues to the JobStorage (otherwics we see 0 servers on the dashboard).

It can easily be done like this:

var service = provider.GetServices<IHostedService>().OfType<BackgroundJobServerHostedService>().Single();

await service.StopAsync(CancellationToken.None);
await service.StartAsync(CancellationToken.None);

but BackgroundJobServerHostedService is registered with the transient lifetime, so I can't get the original BackgroundJobServerHostedService. It looks like other guys also want to access it somehow (https://github.com/HangfireIO/Hangfire/issues/1894#issuecomment-875828250)

I currently just change the lifetime of BackgroundJobServerHostedService in IServiceCollection after AddHanfireServer, but it feels too hacky.

So I suggest to register BackgroundJobServerHostedService as singleton.

hankovich avatar Oct 20 '21 11:10 hankovich

You can simply avoid using AddHangfireServer method and use BackgroundJobServerHostedService class that implements the IHostedService interface explicitly and register it as a hosted service manually.

services.AddSingleton<IHostedService>(provider => new BackgroundJobServerHostedService(
    provider.GetService<JobStorage>(),
    new BackgroundJobServerOptions(),
    Enumerable.Empty<IBackgroundProcess>()));

odinserj avatar Oct 21 '21 10:10 odinserj

@odinserj What I can see in the latest source code is that AddHangfireServer does more than just

services.AddSingleton<IHostedService>(provider => new BackgroundJobServerHostedService(
    provider.GetService<JobStorage>(),
    new BackgroundJobServerOptions(),
    Enumerable.Empty<IBackgroundProcess>()));

When I dig into HangfireServiceCollectionExtensions.CreateBackgroundJobServerHostedService I see activator, profider and resolvers being attached to the creation of the service.

HangfireServiceCollectionExtensions.ThrowIfNotConfigured(provider);
      storage = storage ?? provider.GetService<JobStorage>() ?? JobStorage.Current;
      additionalProcesses = additionalProcesses ?? provider.GetServices<IBackgroundProcess>();
      options.Activator = options.Activator ?? provider.GetService<JobActivator>();
      options.FilterProvider = options.FilterProvider ?? provider.GetService<IJobFilterProvider>();
      options.TimeZoneResolver = options.TimeZoneResolver ?? provider.GetService<ITimeZoneResolver>();
      IBackgroundJobFactory factory;
      IBackgroundJobStateChanger stateChanger;
      IBackgroundJobPerformer performer;
      HangfireServiceCollectionExtensions.GetInternalServices(provider, out factory, out stateChanger, out performer);
      return new BackgroundJobServerHostedService(storage, options, additionalProcesses, factory, performer, stateChanger);

Does it mean I need to always track Hangfire changes on this place to be sure I'm okay?

krzysiek-b avatar Jul 22 '22 06:07 krzysiek-b

This wiring code is mostly for backward compatibility, because different places were used to get the services in different versions, and this is an attempt to guess the right thing. In actual code specific to application there's no need in this complexity, since you control where your services are stored.

odinserj avatar Aug 01 '22 05:08 odinserj