¶ ↑
resque-statusresque-status is an extension to the resque queue system that provides simple trackable jobs.
¶ ↑
Aboutresque-status provides a set of simple classes that extend resque’s default functionality (with 0% monkey patching) to give apps a way to track specific job instances and their status. It achieves this by giving job instances UUID’s and allowing the job instances to report their status from within their iterations.
¶ ↑
Installationresque-status *requires Redis >= 1.1* (though I recommend getting the latest stable version). You can download Redis here: code.google.com/p/redis/ or install it using homebrew (brew install redis).
Install the resque-status gem (which will pull in the dependencies).
gem install resque-status
With newer Rails add this to your Gemfile:
# Gemfile gem 'resque-status'
Then in an initializer:
# config/initializers/resque.rb Resque.redis = "your/redis/socket" # default localhost:6379 Resque::Plugins::Status::Hash.expire_in = (24 * 60 * 60) # 24hrs in seconds
¶ ↑
UsageThe most direct way to use resque-status is to create your jobs using the Resque::Plugins::Status module. An example job would look something like:
class SleepJob include Resque::Plugins::Status def perform total = (options['length'] || 1000).to_i total.times do |i| num = i+1 at(num, total, "At #{num} of #{total}") sleep(1) end end end
One major difference is that instead of implementing perform
as a class method, we do our job implementation within instances of the job class.
In order to queue a SleepJob up, we also won’t use Resque.enqueue
, instead we’ll use the create
class method which will wrap enqueue
and creating a unique id (UUID) for us to track the job with.
job_id = SleepJob.create(length: 100)
This will create a UUID enqueue the job and pass the :length option on the SleepJob instance as options (as you can see above).
Now that we have a UUID its really easy to get the status:
status = Resque::Plugins::Status::Hash.get(job_id)
This returns a Resque::Plugins::Status::Hash object, which is a Hash (with benefits).
status.pct_complete #=> 0 status.status #=> 'queued' status.queued? #=> true status.working? #=> false status.time #=> Time object status.message #=> "Created at ..."
Once the worker reserves the job, the instance of SleepJob updates the status at each iteration using at()
status = Resque::Plugins::Status::Hash.get(job_id) status.working? #=> true status.num #=> 5 status.total #=> 100 status.pct_complete #=> 5
If an error occurs within the job instance, the status is set to ‘failed’ and then the error is re-raised so that Resque can capture it.
Its also possible to get a list of current/recent job statuses:
Resque::Plugins::Status::Hash.statuses #=> [#<Resque::Plugins::Status::Hash>, ...]
¶ ↑
Passing back data from the jobYou may want to save data from inside the job to access it from outside the job.
A common use-case is web-triggered jobs that create files, later available for download by the user.
A Status is actually just a hash, so inside a job you can do:
set_status(filename: "myfilename")
Also, all the status setting methods take any number of hash arguments. So you could do:
completed('filename' => '/myfilename')
¶ ↑
Kill! Kill! Kill!Because we’re tracking UUIDs per instance, and we’re checking in/updating the status on each iteration (using at
or tick
) we can kill specific jobs by UUID.
Resque::Plugins::Status::Hash.kill(job_id)
The next time the job at job_id calls at
or tick
, it will raise a Killed
error and set the status to killed.
¶ ↑
Percent Complete and setting the messageUse at
or tick
to show progress in your job’s perform
function (which is displayed on the resque-web status tab). This will also be where Killed
is raised if the job is killed.
at(steps_completed, total_steps, "${steps_completed} of #{total_steps} steps completed!")
¶ ↑
ExpirationSince Redis is RAM based, we probably don’t want to keep these statuses around forever (at least until @antirez releases the VM feature). By setting expire_in, all statuses and their related keys will expire in expire_in seconds from the last time theyre updated:
Resque::Plugins::Status::Hash.expire_in = (60 * 60) # 1 hour
¶ ↑
TestingRecent versions of Resque introduced ‘Resque.inline` which changes the behavior to instead of enqueueing and performing jobs to just executing them inline. In Resque itself this removes the dependency on a Redis, however, `Resque::Status` uses Redis to store information about jobs, so though `inline` “works”, you will still need to use or mock a redis connection. You should be able to use a library like github.com/causes/mock_redis alongside `inline` if you really want to avoid Redis connections in your test.
¶ ↑
resque-webThough the main purpose of these trackable jobs is to allow you to surface the status of user created jobs through your apps’ own UI, I’ve added a simple example UI as a plugin to resque-web.
To use, you need to setup a resque-web config file:
# ~/resque_conf.rb require 'resque/status_server'
Then start resque-web with your config:
resque-web ~/resque_conf.rb
This should launch resque-web in your browser and you should see a ‘Statuses’ tab.
¶ ↑
MoreSource: github.com/quirkey/resque-status API Docs: rdoc.info/projects/quirkey/resque-status Examples: github.com/quirkey/resque-status/tree/master/examples Resque: github.com/defunkt/resque
¶ ↑
ThanksResque is awesome, @defunkt needs a shout-out.
¶ ↑
Note on Patches/Pull Requests-
Fork the project.
-
Make your feature addition or bug fix.
-
Add tests for it. This is important so I don’t break it in a future version unintentionally.
-
Commit, do not mess with rakefile, version, or history. (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
-
Send me a pull request. Bonus points for topic branches.
¶ ↑
CopyrightCopyright © 2010 Aaron Quint. See LICENSE for details.