Show:
Delayed Job – Example on How to Deal with Long Running Rails Tasks
When an application needs to deal with some long running tasks, we probably should move those tasks to run asynchronously as background jobs, more precisely to use Rails Delayed Job. Let’s examine what we need to do in order to enqueue and execute some tasks in the background.
Set up Delayed Job
With Rails Active Job we can easily define our jobs and execute them on various queuing backends. Delayed Job is one of those backends, that perfectly match out needs.
Let’s assume that we already have a Rails application with one model named Team
. For start, we will need to add delayed_job_active_record gem to app’s Gemfile
,
gem 'delayed_job_active_record'and run
bundle install
to install it.Then we will run the following commands, to create delayed_jobs database table and delayed_job file in the bin directory
rails generate delayed_job:active_record rails db:migrateNext we will set delayed_job as ActiveJob queue adapter,
# config/application.rb config.active_job.queue_adapter = :delayed_joband set default delayed job configuration.
# config/initializers/delayed_job_config.rb Delayed::Worker.destroy_failed_jobs = false Delayed::Worker.sleep_delay = 60 Delayed::Worker.max_attempts = 3 Delayed::Worker.max_run_time = 5.minutes Delayed::Worker.read_ahead = 10 Delayed::Worker.default_queue_name = 'default' Delayed::Worker.delay_jobs = !Rails.env.test? Delayed::Worker.raise_signal_exceptions = :term Delayed::Worker.logger = Logger.new(File.join(Rails.root, 'log', 'delayed_job.log')) In order to menage background processes we will add `gem to our Gemfile. That makes us completely ready to create and test out our first background job. Create the Job [ruby] rails generate job teams_statisticThis will create
teams_statistic_job.rb
file under theapp/jobs
directory, with the following content:class TeamsStatisticJob < ApplicationJob queue_as :default def perform(*args) # Do something later end endLet’s suppose that, within
Team
class, we have one instance method for long and complex calculations, namedcalculate_statistic
.class Team < ApplicationRecord ... def calculate_statistic # some complex calculation puts "I am calculating #{self.name} statistic!" end endNow we can easily call that method from the job’s
perform
method, passing the team as an argument.def perform(team) team.calculate_statistic endLet’s enqueue a job through Rails console.
red_star = Team.find_by_name("Red Star") TeamsStatisticJob.perform_later(red_star)Because Delayed Job is database based queue system, previous command will insert one row in the
delayed_jobs
table.Also through logs we can see that we just enqueued a job to be performed as soon as the queuing system is free.
Enqueued TeamsStatisticJob (Job ID: new-job-id) to DelayedJob(default) with arguments: GlobalID:...Start background worker
We can start background workers with this rake task
bundle exec rake jobs:work
or by runningbin/deleyed_job
script (by the way, that’s the reason why we addeddaemons
gem).# commands that runs bin/deleyed_job script RAILS_ENV=production bin/delayed_job start RAILS_ENV=production bin/delayed_job stopLet’s open new terminal and run the rake task to actually perform the job.
As our logs are showing, long running task is now successfully completed.
Starting job worker Job ActiveJob::QueueAdapters::DelayedJobAdapter::JobWrapper (id=12) (queue=default) RUNNING I am calculating Red Star statistic! # calculate_statistic method output Job ActiveJob::QueueAdapters::DelayedJobAdapter::JobWrapper (id=12) (queue=default) COMPLETED after 0.0771And that is it.
However, there are many useful features (priorities, named queues, …) in the Delayed Job documentation, that I didn’t cover in this post. I encourage everyone to check those out…