tdtran/resque-uniq — pull request #13 by tdtran (2014-05-27 14:49:17+00:00)
Title: Fix: it's possible to enqueue duplicate jobs

The bug: while one instance of the job is running, it's possible to enqueue **any number** of instances of the same job.

We got caught by a contract breakage in resque 1.x.x.

resque-uniq uses two job locks: the normal lock to prevent the second job instance from being enqueued and another so called run lock to detect if the first lock is stale. The run lock is created before the job runs and deleted after the job finishes. To determine which jobs are currently running we use `Resque::Job.constantize`

Our usage of this method is taken directly from Resque::Job doc. However resque 1.x broke it. This method doesn't exist anymore. We end up with NoMethodError. Unfortunately NoMethodError is a subclass of NameError, an exception which we fully expect can be thrown by Resque::Job.constantize and we ignore it.

The net effect is while a job is running our `stale_lock?` method always return `true` and a new job instance can enter the queue.

The change of behaviour happened in this resque commit: https://github.com/resque/resque/commit/93eca9a7b0b9a899d37347a0dcd6c4ca9039fc6d

As you can see with this change `Resque::Job` stop to extends the `Resque::Helpers` module where the **class** method `constantize` is defined. It adds a new method `constantize` to `Resque::Job` but it's instance method not class method.

Things to note:

- there are now two development lines of resque: 2.0.x series and 1.x.x series. We use 1.x.x. which is also the official gem they publish on rubygem.org

- the official gem version 1.25.2 contains the "faulty" Job class: https://github.com/resque/resque/blob/v1.25.2/lib/resque/job.rb

- same for the tip of 1.x.x branch: https://github.com/resque/resque/blob/1-x-stable/lib/resque/job.rb

- it's different in resque master branch (where resque 2.0.x development happens). `constantize` is now monkeypatched into `String` class. (doesn't Rails already do it?)

I am going to clean up this patch a bit and add some unit tests, but please feel free to comment