Background workers are good for you
As time and coding goes, your application will grow and become more and more complex. In such a system you will sometimes have to run time-consuming tasks or tasks that are very computationally intensive. You know what this means – app slows down to a crawl.
Fortunately, this is where background jobs/workers come into play.
With background workers you can save jobs in a queue and process them later without blocking your current thread. You can even run several jobs in parallel!
Another advantage of background jobs is the better user experience. When you execute time-consuming jobs in the background, requests return immediately and users don’t have to wait and get angry at you. They just browse your website and get notified when the job is done.
Since Rails 4.2 it’s very easy to use different background workers in your application. In this version they introduced Active Job. That’s a framework for declaring jobs and making them run on different queueing backends. It provides a place for jobs in your rails apps and ensures that you don’t have to worry about differences between various background workers like Resque, Sidekig or DelayedJob.
So, how do we use background workers in our apps?
Workers at work
In our example app we’ll use Rails 4.2 to benefit from the features of Active Job. As the background worker we’ll use DelayedJob, a database-based asynchronous priority queue system.
The finished app can be found here. It’s a simple blog app from the rails guides, but in our example we use a background job to delete comments.
When you have set up the base app we can start implementing the background job.
First, add the gem to your Gemfile:
And install it with:
As DelayedJob is based on a database, you have to create the required table:
rails generate delayed_job:active_record rake db:migrate
Now let’s make sure that our app uses the desired background worker – in order to do that, specify the adapter in config/application.rb:
module Rails4WorkerExample class Application < Rails::Application config.active_job.queue_adapter = :delayed_job # comments and other things end end
After you’ve configured everything correctly, let’s create your first job.
Create your job
Rails has a built-in generator for creating jobs. To create a job run in your console:
rails generate job delete_comment_job
The generated jobs are located in app/jobs. A job looks like as follows:
class DeleteCommentJob < ActiveJob::Base queue_as :default def perform(*args) # Do something later end end
Now update the perform method so that it deletes a comment:
def perform(comment_id) # Do something later sleep 5 comment = Comment.find comment_id comment.destroy end
Your job is ready! We just have to use it in our app.
Use your job
As the job deletes comments, we’ll use it in the destroy method of our comments_controller:
def destroy @article = Article.find(params[:article_id]) @comment = @article.comments.find(params[:id]) DeleteCommentJob.perform_later(@comment.id) redirect_to article_path(@article) end
To add a job to the job queue, call perform_later on our DeleteCommentsJob class. At this point, DelayedJob will run the job for you in the background. Just sit and enjoy your coffee :)
Aaaand we’re almost there – the last thing you have to do is deploy the app on anynines and try it out.
Let’s deploy! Enter anynines
Let us deploy your app on anynines. First, you have to select the anynines api:
cf api https://api.de.a9s.eu
Then login with your credentials and select your org and space:
cf login cf target -o <your org_name> -s <your space_name>
Before you can push your app to anynines, you need to create the required service. In this case it’s mysql:
cf create-service mysql Pluto-free <SERVICE NAME>
After that we create 2 manifest files: app_manifest.yml for the app and worker_manifest.yml for the worker.
app_manifest.yml --- applications: - name: <APP NAME> memory: 512M instances: 1 host: <APP NAME> domain: de.a9sapp.eu path: . buildpack: https://github.com/cloudfoundry/heroku-buildpack-ruby.git services: - <SERVICE NAME>
worker_manifest.yml --- applications: - name: <WORKER NAME> memory: 512M instances: 1 path: . no-route: true command: bundle exec rake jobs:work buildpack: https://github.com/cloudfoundry/heroku-buildpack-ruby.git services: - <SERVICE NAME>
They may look almost the same but have some important differences. In the worker_manifest.yml we set the option no-route to true so that no route is assigned to our worker. With the command option we can specify our custom start command for the worker.
Both need the same mysql service, though – otherwise our worker and app won’t work together.
Now’s the time to deploy the app and worker to anynines:
cf push -f app_manifest.yml cf push -f worker_manifest.yml
The app is now available at <APP_NAME>.de.a9sapp.eu. To see the effect of our background job, do the following:
- create an article and write some comments,
- delete a comment,
- and refresh yout page after 5 sec – voila! the comment is gone
Our brave background worker is doing its job perfectly. And you know how to configure your rails app to use background jobs and handle complex work in the background!
Workers work wonders
When you have a time-consuming or complex task you should definitely use background workers. Your users will thank you for the better user experience – they won’t wait while their tasks are executed and the requests will return immediately. Plus, background workers also make your app more scalable and easier to manage. With Active Job you can now easily declare jobs and run them on different queueing backends.
It all boils down to one thing – just add more workers if you need them!