We are using Hangfire in a multi-tenant environment and are trying to separate jobs into queues per tenant, but have run into an issue with Schedule.
We have created a custom dynamic queue attribute that sets the queue based on a current application setting. This works perfectly for a regular Enqueue, but as far as I can see BackgroundJob.Schedule runs on a random queue instead of the queue of the scheduled function. No queue state seems to be saved for a scheduled job in the database.
Since any server can pick up a scheduled job it gets enqueued in the queue of whatever server picked up the job. Do we have any way at all to work around this, or do we need hangfire to support this feature for our use-case to be possible?
We have found a possible solution using JobFilterAttribute by saving the queue in a table during OnStateElection in the ScheduledState, and then loading it from db during EnqueuedState.
I tried a number of things and the simplest approach I could come up with was to simply wrap the job that was getting scheduled in another job that would then dispatch the original job at the appropriate time. Calling BackgroundJob.Enqueue does respect the queue if set via the Queue attribute, but Schedule does not for whatever reason. My wrapper job then sits in the default queue, waiting for its scheduled time to arrive, at which point it will get executed and the original job gets enqueued directly, at which point it will go into the correct queue by virtue of the attribute being present. My setup involves messages getting processed by handlers, so my wrapper job has two inputs - the message and the type of handler to execute, and then it performs a IBackgroundJobClient.Create(methodCall, state) based on that information.
Just wanted to add incase anyone see’s this, Stepan_Fomenko’s solution does not work as expected. If you change the queue, the task no longer runs on delay and instead runs immediately. Therefore changing queue after scheduling isn’t really doing what I need.
I need to delay a job but also put it in a specific queue. Not sure this is possible.
@Foomaniac - hi - it’s been three years but I’m still using the setup I described previously in this thread to do as you’re describing. It requires some wrappers around the core Hangfire abstractions, but it has served us well across a number of systems now. The principle remains the same - you wrap the job that you want to delay in another job that gets scheduled in the default queue, and then once that scheduled job fires all it does internally is immediately enqueue the original job for execution in the intended queue:
var backgroundJobId = _backgroundJobClient.Create(() => yourActualJob(), new EnqueuedState(queue));
Here I’m using EnqueuedState to have complete control over the queue the job actually winds up in - so whether you need to dynamically determine the queue as you would in a multi-tenant application or you simply have a static registry of job types to queue names you have control over where the job will wind up.