15

This documentation says that you can specify a queue by using the Queue attribute on the method to be invoked. This assumes that you always want execute a method on the same queue. Is there a way for the process that calls Enqueue to specify the name of the queue to put the job into (effectively putting the decision-making in the hands of the job generator, not the definition of the job).

Suraj
  • 35,905
  • 47
  • 139
  • 250

3 Answers3

17

With a instance of IBackgroundJobClient you can specify a queue.

IBackgroundJobClient hangFireClient = new BackgroundJobClient();
EnqueuedState myQueueState = new Hangfire.States.EnqueuedState("myQueue");
hangFireClient.Create<SomeClass>(c => c.SomeMethod(), myQueueState);

Note that in this way, a retry will put the job back to the default queue. You will require additional code to retry in the same queue, using a JobFilter

http://discuss.hangfire.io/t/one-queue-for-the-whole-farm-and-one-queue-by-server/490/3

Xavero
  • 3,333
  • 1
  • 15
  • 17
  • I've tried that but it gives me "Cannot create an instance of an inteface" despite using the ...Create(x=>x.Bar(), myQueueState). Any idéas? – JohanLarsson Aug 04 '15 at 12:59
  • Probably missing the Dependency Inject Resolver, but i'm guessing by now you already fix your issue. Sorry, didnt see your question sooner. – Xavero Sep 18 '15 at 21:20
  • Worked for me. The class and method cannot be static, but it works fine. – Daniel Abbatt Feb 02 '16 at 13:10
  • This works. Something to note is, if your method throws an exception while processing, Hangfire will requeue it to a "default" queue (unless you specify a queue through an attribute). – Alberto Guerrero Dec 23 '22 at 22:45
3

Since adding an extra parameter seems to be so hard for the Hangfire team ;-)....

...I've found the most convenient way is to make two methods that just call the actual implementation, and put different [Queue] attributes on each.

Usually if I need to switch queues it's between dev / production and I want to just call something like RunOrder(...) with isTestOrder=boolean and not be concerned about queues at that level.

public void RunOrder(int orderId, bool isTestOrder) 
{
   if (isTestOrder) 
   {
      BackgroundJob.Enqueue(() => _RunTestOrder(orderId));
   } 
   else 
   {
      BackgroundJob.Enqueue(() => _RunOrder(orderId));
   }
}

[Queue("dev")]
public void _RunTestOrder(int orderId) {
  OrderProcessor.RunOrder(orderId); // actual code to call processor
}

[Queue("production")]`
public void _RunProductionOrder(int orderId) {
  OrderProcessor.RunOrder(orderId); // is the same in both 'hangfire proxies'
}

Note usage of _ to indicate these aren't meant to be called directly. I don't remember off hand if hangfire methods need to be public or not - but if they do need to be then the _ is more important.

Simon_Weaver
  • 140,023
  • 84
  • 646
  • 689
2

If you want to change the queue Dynamically you can follow this way.

This implementation works for all Enqueue , Schedule and ContinueJobWith

The trick happen on [Queue("{0}")]. Hangfire will pass job arguments to the given pattern ("{0}") using String.Format("{0}", job.Args) to get the actual queue on every state change, so the target queue will be applied even for retries.

Highlighted Code Syntax :

// Enqueue "MyBackgroundJob1" under "critical" queue
var job2 = BackgroundJob.Enqueue(() => MyBackgroundJob1("critical", 523));

// Use one of following Attributes depending on your hangfire version
//[AdvancedQueue("{0}")] //  1.6.X 
[Queue("{0}")] // In 1.7.X
public void MyBackgroundJob1(string queueName, int arg)
{
    // Job implementation
}

Full Implementation :

public class HangfireDynamicQueue
{
    public void EnqueJobs()
    {
        // Enqueue "MyBackgroundJob1" under "default" queue
        var job1 = BackgroundJob.Enqueue(() => MyBackgroundJob1("default", 123));

        // Enqueue "MyBackgroundJob1" under "critical" queue
        var job2 = BackgroundJob.Enqueue(() => MyBackgroundJob1("critical", 523));

        // Execute "MyBackgroundJob1" after 10 seconds under "delayed" queue
        var job3 = BackgroundJob.Schedule(() => MyBackgroundJob1("delayed", 678), TimeSpan.FromSeconds(10));

        // Run "MyBackgroundJob2" after job3 under "delayed" queue
        var job4 = BackgroundJob.ContinueJobWith(job3, () => MyBackgroundJob2("delayed", 435));
    }

    // Use one of following Attributes depending on your hangfire version
    //[AdvancedQueue("{0}")] //  1.6.X 
    [Queue("{0}")] // In 1.7.X
    public void MyBackgroundJob1(string queueName, int arg)
    {
        // Job implementation
    }

    // Use one of following Attributes depending on your hangfire version
    //[AdvancedQueue("{0}")] //  1.6.X 
    [Queue("{0}")] // In 1.7.X
    public void MyBackgroundJob2(string queueName, int arg)
    {
        // Job implementation
    }
}
  • This is an underrated hack. I'm disappointed in the hangfire team for not implementing this on "BackgroundJobClient" level at least. – CME64 Jul 17 '22 at 07:53