Leveret
Leveret is an easy to use RabbitMQ backed job runner.
It's designed specifically to execute long running jobs (multiple hours) while allowing the applicatioin to be restarted with no adverse effects on the currently running jobs.
Leveret has been tested with Ruby 2.2.3+ and RabbitMQ 3.5.0+.
Installation
Add this line to your application's Gemfile:
gem 'leveret'
And then execute:
$ bundle
Or install it yourself as:
$ gem install leveret
To use Leveret you need a running RabbitMQ installation. If you don't have it installed yet, you can do so via Homebrew (MacOS), APT (Debian/Ubuntu) or RPM (Redhat/Fedora).
RabbitMQ version 3.5.0 or higher is recommended as this is the first version to support message priorities.
Usage
To create a job include Leveret::Job
in a new class, and define a perform
method that will do the work in
your job. Call enqueue
on your new class with any parameters you want passed to the job at execution time.
class MyJob
include Leveret::Job
def perform
File.open('/tmp/leveret-test-file.txt', 'a+') do |f|
f.puts params[:test_text]
end
sleep 5 # Job takes a long time
end
end
MyJob.enqueue(test_text: "Hi there! Please write me to the test file.")
Now start a worker to execute the job:
bundle exec leveret_worker
Queues
By default all are defined on a single standard queue (see Configuration for details). However, it's possible to use
multiple queues for different jobs. To do this set the queue_name
in your job class. You'll also need to tell the
worker about your new queue when starting that.
class MyOtherQueueJob
include Leveret::Job
queue_name 'other'
def perform
# ...
end
end
MyOtherQueueJob.enqueue(test_text: "Hi there! Please write me to the test file.")
If you don't always want to place the job on your other queue, you can specify the queue name when enqueuing it. Pass
the queue_name
option when enqueuing the job.
MyJob.enqueue(test_text: "Hi there! Please write me to the test file.", queue_name: 'other')
Priorities
Leveret supports 3 levels of job priority, :low
, :normal
and :high
. To set the priority you can define it in your
job class, or specify it at enqueue time by passing the priority
option.
class MyHighPriorityMyJob
include Leveret::Job
priority :high
def perform
# very important work...
end
end
MyHighPriorityJob.enqueue
To specify priority at enqueue time:
MyJob.enqueue(test_text: "Hi there! Please write me to the test file.", priority: :high)
Workers
To start a leveret worker, simply run the leveret_worker
executable included in the gem. Started with no arguments it
will create a worker monitoring the default queue and process one job at a time.
Changing the queues that a worker monitors requires passing a comma separated list of queue names in the option
--queues
. The example below watches for jobs on the queues standard
and other
.
bundle exec leveret_worker --queues standard,other
By default, workers will only process one job at a time. For each job that is executed, a child process is forked, and
the job run in the new process. When the job completes, the fork exits. We can process more jobs simultaniously simply
by allowing more forks to run. To increase this limit set the --processes
option. There is no limit to
this variable in Leveret, but you should be aware of your own OS and resource limits.
bundle exec leveret_worker --processes 5
It's also possible to set the log level and output from the command line, call up --help
for more details.
Configuration
Configuration in Leveret is done via a configure block. In a Rails application it is recommended you place your
configuration in config/initializers/leveret.rb
. Leveret comes configured with sane defaults for development, but you
may wish to change some for production use.
Leveret.configure do |config|
# Location of your RabbitMQ server
config.amqp = "amqp://guest:guest@localhost:5672/"
# Name of the exchange Levert will create on RabbitMQ
config.exchange_name = 'leveret_exch'
# Path to send log output to
config.log_file = STDOUT
# Verbosity of log output
config.log_level = Logger::DEBUG
# String that is prepended to all queues created in RabbitMQ
config.queue_name_prefix = 'leveret_queue'
# Name of the queue to use if none other is specified
config.default_queue_name = 'standard'
# A block that should be called every time a child fork is created to process a job
config.after_fork = proc {}
# A block that is called whenever an exception occurs in a job
config.error_handler = proc { |ex| ex }
# The default number of jobs to process simultaniously, this can be overridden by the PROCESSES
# environment variable when starting a worker
config.concurrent_fork_count = 1
end
Most of these are pretty self-explanatory and can be left to their default values, however after_fork
and
error_handler
could use a little more explaining.
after_fork
Is called immediately after a child process is forked to run a job. Any connections that need to be
reinitialized on fork should be done so here. For example, if you're using Rails you'll probably want to reconnect
to ActiveRecord here:
Leveret.configure do |config|
config.after_fork = proc do
ActiveRecord::Base.establish_connection
end
end
error_handler
is called whenever an exception is raised in your job. These exceptions are caught and logged, but not
raised afterwards. error_handler
is your chance to decide what to do with these exceptions. You may wish to log them
using a service such as Airbrake or Sentry. To configure an
error handler to log to Sentry the following would be necessary:
Leveret.configure do |config|
config.error_handler = proc do |exception, job|
job_name = job.class.name
Raven.capture_exception(exception, tags: {component: job_name})
end
end
Development
After checking out the repo, run bin/setup
to install dependencies. Then, run rake spec
to run the tests. You can also run bin/console
for an interactive prompt that will allow you to experiment.
To install this gem onto your local machine, run bundle exec rake install
. To release a new version, update the version number in version.rb
, and then run bundle exec rake release
, which will create a git tag for the version, push git commits and tags, and push the .gem
file to rubygems.org.
Contributing
Bug reports and pull requests are welcome on GitHub at https://github.com/darkphnx/leveret.
License
The gem is available as open source under the terms of the MIT License.