# Getting Started With Sidekiq In Rails 7

> A demonstration on getting a local development environment going with Sidekiq and Redis to run a simple demonstration job in Rails 7.

- Canonical: https://dennisokeeffe.com/blog/2022-03-04-getting-started-with-sidekiq-in-rails-7
- Published: 2022-03-04
- Updated: 2022-03-04
- Tags: ruby, ruby-on-rails

Sidekiq makes it easy to run efficient background jobs in Ruby. It is amazingly useful in your Rails projects for jobs that require processing.

This post will cover a basic example of running jobs with [Sidekiq](https://sidekiq.org/) and [Redis](https://redis.io/).

Source code can be found [here](https://github.com/okeeffed/demo-hello-sidekiq).

## Prerequisites

1. Basic familiarity with setting up a [new Rails project](https://guides.rubyonrails.org/getting_started.html).
2. Basic familiarity with [Redis](https://redis.io/) and having it already installed and running.

## Getting started

We will use Rails to initialize the project `demo-hello-sidekiq`:

```s
# Create a new rails project
$ rails new demo-rails-with-react-frontend -j esbuild
$ cd demo-rails-with-react-frontend

# Create some required files
$ touch Procfile.dev config/initializers/sidekiq.rb bin/dev

# Add permissions to run bin/dev
$ chmod u+x bin/dev

# Add required gems
$ bundler add sidekiq

# Generate a controller route for our demo
$ bin/rails g controller jobs create
      create  app/controllers/jobs_controller.rb
       route  get 'jobs/create'
      invoke  erb
      create    app/views/jobs
      create    app/views/jobs/create.html.erb
      invoke  test_unit
      create    test/controllers/jobs_controller_test.rb
      invoke  helper
      create    app/helpers/jobs_helper.rb
      invoke    test_unit

# Generate a basic job
$ bin/rails g sidekiq:job hello
      create  app/sidekiq/hello_job.rb
      create  test/sidekiq/hello_job_test.rb
```

With Sidekiq, we have some helpers to help generate our jobs with `bin/rails g sidekiq:job <job_name>`.

At this stage, we are ready to start updating and editing our code.

## Setting up our development environment

Inside of `Procfile.dev`:

```s
web: bin/rails s
worker: bundle exec sidekiq
```

Inside of `bin/dev`:

```sh
#!/usr/bin/env bash

if ! gem list --silent --installed foreman
then
  echo "Installing foreman..."
  gem install foreman
fi

foreman start -f Procfile.dev "$@"
```

This will enable us to run `bin/dev` to start both our Rails server and our Sidekiq process.

## Updating our routes

```rb
Rails.application.routes.draw do
  resources :jobs, only: [:create]
end
```

## Updating our configuration file

We are going to turn off our `default_protect_from_forgery` in development so we can demonstrate creating the job with our CLI tools.

Update our `config/application.rb` to the following:

```rb
require_relative "boot"

require "rails/all"

# Require the gems listed in Gemfile, including any gems
# you've limited to :test, :development, or :production.
Bundler.require(*Rails.groups)

module DemoHelloSidekiq
  class Application < Rails::Application
    # Initialize configuration defaults for originally generated Rails version.
    config.load_defaults 7.0

		# Enable us to send requests without auth token
		config.action_controller.default_protect_from_forgery = false if ENV['RAILS_ENV'] == 'development'
  end
end
```

## Configuring our Sidekiq initializer file

Let's update our `config/initializers/sidekiq.rb` file to set up our server configuration. Note that this will only be for our local server URL.

```rb
# inside config/initializers/sidekiq.rb

Sidekiq.configure_server do |config|
  config.redis = { url: ENV.fetch('REDIS_URL', 'redis://localhost:6379/1') }
end
Sidekiq.configure_client do |config|
  config.redis = { url: ENV.fetch('REDIS_URL', 'redis://localhost:6379/1') }
end
```

## Writing our first job

Update what is in `app/sidekiq/hello_job.rb`:

```rb
class HelloJob
  include Sidekiq::Job

  def perform(*_args)
    # Do something
    p "HelloJob started with args #{_args}"

    # Sleep to simulate a time-consuming task
    sleep 5

    # Will display current time, milliseconds included
    p "HelloJob #{Time.now.strftime('%F - %H:%M:%S.%L')}"
  end
end
```

## Writing our controller to run a basic job

Finally, let's update our `app/controllers/jobs_controller.rb`:

```rb
class JobsController < ApplicationController
  def create
    HelloJob.perform_async('job', 5)
    render json: { message: 'Accepted' }, status: :accepted
  end
end
```

Our simple job will take 5 seconds to complete and will log out the arguments we've passed to the job as well as the time the job completes.

## Testing our job

Now we can start the server with `bin/dev`.

```s
$ bin/dev
10:36:56 web.1    | started with pid 77188
10:36:56 worker.1 | started with pid 77189
10:36:58 web.1    | => Booting Puma
10:36:58 web.1    | => Rails 7.0.2.2 application starting in development
10:36:58 web.1    | => Run `bin/rails server --help` for more startup options
10:36:58 worker.1 | 2022-03-02T00:36:58.612Z pid=77189 tid=1r01 INFO: Booting Sidekiq 6.4.1 with redis options {:url=>"redis://localhost:6379/1"}
10:36:58 worker.1 | 2022-03-02T00:36:58.913Z pid=77189 tid=1r01 INFO: Booted Rails 7.0.2.2 application in development environment
10:36:58 worker.1 | 2022-03-02T00:36:58.913Z pid=77189 tid=1r01 INFO: Running in ruby 2.7.3p183 (2021-04-05 revision 6847ee089d) [x86_64-darwin20]
10:36:58 worker.1 | 2022-03-02T00:36:58.913Z pid=77189 tid=1r01 INFO: See LICENSE and the LGPL-3.0 for licensing details.
10:36:58 worker.1 | 2022-03-02T00:36:58.913Z pid=77189 tid=1r01 INFO: Upgrade to Sidekiq Pro for more features and support: https://sidekiq.org
10:36:59 web.1    | Puma starting in single mode...
10:36:59 web.1    | * Puma version: 5.6.2 (ruby 2.7.3-p183) ("Birdie's Version")
10:36:59 web.1    | *  Min threads: 5
10:36:59 web.1    | *  Max threads: 5
10:36:59 web.1    | *  Environment: development
10:36:59 web.1    | *          PID: 77188
10:36:59 web.1    | * Listening on http://127.0.0.1:5000
10:36:59 web.1    | * Listening on http://[::1]:5000
10:36:59 web.1    | Use Ctrl-C to stop
```

To start a job, we can make a POST call to `http://localhost:5000/jobs`:

```s
$ http POST http://localhost:5000/jobs
HTTP/1.1 202 Accepted
Cache-Control: no-cache
Content-Type: application/json; charset=utf-8
Referrer-Policy: strict-origin-when-cross-origin
Server-Timing: start_processing.action_controller;dur=0.108642578125, process_action.action_controller;dur=2.332763671875
Transfer-Encoding: chunked
Vary: Accept
X-Content-Type-Options: nosniff
X-Download-Options: noopen
X-Frame-Options: SAMEORIGIN
X-Permitted-Cross-Domain-Policies: none
X-Request-Id: 4b7ed09f-f449-42a6-abcd-6a2c8fb3487a
X-Runtime: 0.062683
X-XSS-Protection: 0

{
    "message": "Accepted"
}
```

Our logs will show us that the web processes the controller, then the job is handled by the working (with job completion occurring 5 seconds later).

```s
10:42:57 web.1    | Started POST "/jobs" for ::1 at 2022-03-02 10:42:57 +1000
10:42:57 web.1    | Processing by JobsController#create as */*
10:42:57 worker.1 | 2022-03-02T00:42:57.674Z pid=79840 tid=2264 class=HelloJob jid=2d01fd26fa36e584afe284a3 INFO: start
10:42:57 web.1    | Completed 202 Accepted in 2ms (Views: 0.3ms | Allocations: 425)
10:42:57 web.1    |
10:42:57 web.1    |
10:42:57 worker.1 | "HelloJob started with args [\"job\", 5]"
10:43:02 worker.1 | "HelloJob 2022-03-02 - 10:43:02.752"
10:43:02 worker.1 | 2022-03-02T00:43:02.752Z pid=79840 tid=2264 class=HelloJob jid=2d01fd26fa36e584afe284a3 elapsed=5.078 INFO: done
```

Hooray!

## Demonstrating other job methods

We also have the capability of using the `perform_in` and `perform_at` API methods to schedule jobs.

Update the controller to see this in action:

```rb
class JobsController < ApplicationController
  def create
    HelloJob.perform_async('perform_async job')
    HelloJob.perform_in(10.seconds, 'perform_in job')
    HelloJob.perform_at(20.seconds.from_now, 'perform_at job')
    render json: { message: 'Accepted' }, status: :accepted
  end
end
```

Again, send a POST request to our `/jobs` routes results in the following:

```s
10:51:38 web.1    | Started POST "/jobs" for ::1 at 2022-03-02 10:51:38 +1000
10:51:38 web.1    | Processing by JobsController#create as */*
10:51:38 worker.1 | 2022-03-02T00:51:38.630Z pid=79840 tid=2214 class=HelloJob jid=e38a834662f55fec2fc0c0a8 INFO: start
10:51:38 web.1    | Completed 202 Accepted in 46ms (Views: 0.3ms | Allocations: 622)
10:51:38 web.1    |
10:51:38 web.1    |
10:51:38 worker.1 | "HelloJob started with args [\"perform_async job\"]"
10:51:43 worker.1 | "HelloJob 2022-03-02 - 10:51:43.710"
10:51:43 worker.1 | 2022-03-02T00:51:43.710Z pid=79840 tid=2214 class=HelloJob jid=e38a834662f55fec2fc0c0a8 elapsed=5.08 INFO: done
10:51:55 worker.1 | 2022-03-02T00:51:55.054Z pid=79840 tid=2214 class=HelloJob jid=56a274fa9a9db51df8591674 INFO: start
10:51:55 worker.1 | "HelloJob started with args [\"perform_in job\"]"
10:52:00 worker.1 | "HelloJob 2022-03-02 - 10:52:00.067"
10:52:00 worker.1 | 2022-03-02T00:52:00.067Z pid=79840 tid=2214 class=HelloJob jid=56a274fa9a9db51df8591674 elapsed=5.013 INFO: done
10:52:02 worker.1 | 2022-03-02T00:52:02.205Z pid=79840 tid=221o class=HelloJob jid=2672ef81124e468da977f789 INFO: start
10:52:02 worker.1 | "HelloJob started with args [\"perform_at job\"]"
10:52:07 worker.1 | "HelloJob 2022-03-02 - 10:52:07.217"
10:52:07 worker.1 | 2022-03-02T00:52:07.217Z pid=79840 tid=221o class=HelloJob jid=2672ef81124e468da977f789 elapsed=5.012 INFO: done
```

In our logs now we can see the 3 different jobs run at the expected intervals over the period of the 20 seconds that we expected them to be scheduled for.

The expected schedule is something you should keep in mind when writing your tests.

## Summary

Today's post demonstrated how to run jobs in a Rails application with Sidekiq.

This was a simple overview to see a first job in action.

Moving forward, you should look into testing, distributed Redis configuration and more complex jobs that are not so contrived for the purpose of demonstration!

Jobs can be great for background processing such as sending emails, compressing images, or performing long-running tasks.

## Resources and further reading

- [Rails getting started](https://guides.rubyonrails.org/getting_started.html)
- [Source code](https://github.com/okeeffed/demo-hello-sidekiq)

_Photo credit: [`sir_moon`](https://unsplash.com/@sir_moon)_
