Here in this article, I going to tell you the best way to implement “delayed job” in rails
“delayed_job” is a ruby gem used to execute tasks as a background process in Rails environment, increasing page rendering speed.
Delayed::Job (or DJ) allows you to move jobs into the background for asynchronous processing.
Contents
Why you need a background process and is it really that important!
Let’s consider a scenario where a mailing application needs to send emails to a huge list of recipients. In such cases it is obvious that the processing time is too long, annoying the users.
Here are some of key points to consider:
- Incredibly quick & easy to get rolling
- No addition to your “stack”, runs just fine with Active Record
- Good choice for beginners while migrating code from foreground to the background
Hence, it’s only wise to move the long running tasks as a background process by using “delayed_job” gem.
Detailed steps to integrate delayed job in a Rails application
Step# 1
- Add gem to the Gemfile
- “delayed_job” supports multiple back-ends for storing the job queue
- To use “delayed_job” with Active Record, use gem ‘delayed_job_active_record’
- To use “delayed_job” with Mongoid, use gem ‘delayed_job_mongoid’
Example
/Gemfile.rb
- gem ‘delayed_job_active_record’, ‘4.0.3’
- Run “bundle install” to install the “delayed_job” gem
Step# 2
- Generate the related file for the Job run
- Generate related files required to run the background job by running the following command
- rails g delayed_job:active_record
It adds following files to the application
- A Script named “delayed_job” inside “/bin” folder to run the jobs which are in queue.
- Migration file to create a table to store the job with other information such as priority, attempts, handler, last_error, run_at, locked_at, failed_at, locked_by, queue.
Run the migration file by using the following command
- rails db:migrate
Set the queue_adapter in config/application.rb
- config.active_job.queue_adapter = :delayed_job
If you are using the protected_attributes gem, it must appear before delayed_job in your gemfile. If your jobs are failing with:
- Setup Delayed::Job config in an initializer (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’))
Step# 3
- Replace script/delayed_job with bin/delayed_job
- Start up the jobs process
There are two ways to do this.
- If application is in development mode, we would use the below rake task instead.
- rake jobs:work
- If application is in production mode, then it is preferred to use the “delayed_job” script. This demonizes the job process and allows multiple background processes to be spawned.
To use this, pursue the following steps
- Add gem “daemons” to your Gemfile
- Run bundle install
- Make sure you’ve run rails generate delayed_job
- If you want to just run all available jobs and exit you can use rake jobs:workoff
- Work off queues by setting the QUEUE or QUEUES environment variable.
- QUEUE=tracking rake jobs:work
- QUEUES=mailers,tasks rake jobs:work
Step# 4
- Add task to run in background
- In Controller just call .delay.method(params) on any object and it will be processed in the background.
Example:
UsersController before adding to background job
class UsersController < ApplicationController def send_email User.find_each(is_subscribed: true) do |user| NewsMailer.newsletter_mail(user).deliver flash[:notice] = "Mail delivered" redirect_to root_path end end end
UsersController after adding to background job
class UsersController < ApplicationController def send_email User.find_each(is_subscribed: true) do |user| # add .delay method to add it to background process. In case of mail sending remove the .deliver method to make it work. NewsMailer.delay.newsletter_mail(user) flash[:notice] = "Mail delivered" redirect_to root_path end end end
Advantages of implementing above steps:
- No more waiting for a response, after clicking a link to do a big stuff.
- Just call .delay.method(params) on any object and it processes in the background.
- Job objects are serialized to yaml and stored in the delayed_jobs table, so they can be restored by the job runner later.
- It automatically retries on failure. If a method throws an exception it’s caught and the method reruns later. The method retries up to 25 times at increasingly longer intervals until it passes.
- “delayed_job” gem maintains log by creating a log file “/log/delayed_job.log”
I am sure this article will give you a clear idea about the way to implement “delayed job” in rails. You can share your thoughts with comments if I have missed anything or if you want to know more.