Running background workers on anynines

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.

Integrate DelayedJob

First, add the gem to your Gemfile:

gem 'delayed_job_active_record'

And install it with:

bundle install

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!

Hope you liked my blog post – you can tweet directly. Follow @anynines for more hosting news and tutorials!

Leave a Reply

Your email address will not be published. Required fields are marked *

*