7

I am running Rails 4.2.8 and I want to make my job only run under certain conditions. Currently I am doing that check in the code that is calling the job but it would be much cleaner to contain the logic in the job class. Has anyone done that?

class MyJob < ApplicationJob
  before_enqueue do |job|
    # check and stop job from being enqueued under certain conditions
  end
  def perform(args*)
    # code here
  end
end

I am using Sidekiq 4.2.10 as the background job adapter.

Dmitry Polyakovsky
  • 1,535
  • 11
  • 31

2 Answers2

9

You can use around_enqueue to achieve the same result without raising the exception. This can be useful when not enqueuing is something to be expected by your job.

Ex:

around_enqueue do |_job, block|
   if my_condition 
      block.call # this will enqueue your job
   end
end

OBS: Worth noting that this answer is based on Rails 5 ActiveJob code but must work on Rails 4 as well.

Fabricio Buzeto
  • 1,243
  • 1
  • 18
  • 29
  • I think using around_enqueue is the only option to cancel job before adding it to the queue. I tried using before_enqueue in combination with discard_on, but it didn't work. The exception was not catched and it was stopping execution of the code in the process that was enqueueing the job. – mario199 Jun 01 '20 at 07:04
1

throw :abort from your before_enqueue to halt the execution of the callback chain and perform.

Code: https://github.com/rails/rails/blob/fc20050ea69ba3b8d8bc90171d2dcbf93e9a1dae/activesupport/lib/active_support/callbacks.rb#L23

Sergio Tulentsev
  • 226,338
  • 43
  • 373
  • 367
  • 1
    I tried raising exception in `before_enqueue` and using `discard_on` but it didn't work. The exception was not catched and it was stopping execution of the code. So your solution does not work @Sergio. Raising exception and discarding will work if exception is raised in before_perform callback. It is maybe because before_enqueue is run in the same process that enqueued the job, but the before_perform is run in sidekiq worker. – mario199 Jun 01 '20 at 07:03
  • The tests in the Rails repository seem to indicate that by `throw`-ing an `exception` (not to be confused in Ruby with `raise`-ing an `error`) `:abort` it would stop the enqueuing of the job. https://github.com/rails/rails/blob/291a3d2ef29a3842d1156ada7526f4ee60dd2b59/activejob/test/cases/callbacks_test.rb#L154 – Adeynack Mar 09 '21 at 09:17
  • @Adeynack `:abort` is not an exception, though – Sergio Tulentsev Mar 09 '21 at 10:24
  • @SergioTulentsev: In Ruby terms, it is. Exceptions are symbols. See https://coderwall.com/p/lhkkug/don-t-confuse-ruby-s-throw-statement-with-raise – Adeynack Mar 10 '21 at 18:06
  • @Adeynack: no, and no. Exceptions are exceptions and symbols are symbols. In ruby we use "[raise an exception](https://ruby-doc.org/core-2.7.2/Exception.html)" and "raise an error" interchangeably. Not sure what you wanted to say with this post, because it does not back up your claim. – Sergio Tulentsev Mar 10 '21 at 18:37
  • @Adeynack: thanks for pointing this out, though. This stuff clearly changed since 2014 :) – Sergio Tulentsev Mar 10 '21 at 18:59
  • @SergioTulentsev: It got me very confused at first, but `throw` in Ruby is not used for error management, but for interrupting code flows, as the article explains. `catch(:miracle_of_miracles) do ... end` is the beginning of that flow. A method down the stack, called inside that `do...end` block will `throw :miracle_of_miracles`, and Ruby will bubble that up. In the end, it's a similar behavior; but not the same semantic. Does it make more sense in those words? – Adeynack Mar 11 '21 at 21:17
  • As of for changes in the language, @SergioTulentsev, I wouldn't. I started with Ruby in 2019, so for me, lots of +/- "new language features" were just "there" when I learned it. – Adeynack Mar 11 '21 at 21:19
  • 2
    @Adeynack: yep, that sounds more like it. :+1: – Sergio Tulentsev Mar 11 '21 at 22:01