Read more

How to monitor Sidekiq: A working example

Thomas Eisenbarth
November 10, 2016Software engineer at makandra GmbH

In order to have monitoring for Sidekiq (like queue sizes, last run of Sidekiq) your application should have a monitoring route which returns a json looking like this:

{
  "sidekiq": {
    "totals": {
      "failed": 343938,
      "processed": 117649167
    },
    "recent_history": {
      "failed": {
        "2016-11-06": 1,
        "2016-11-07": 46,
        "2016-11-08": 0,
        "2016-11-09": 0,
        "2016-11-10": 0
      },
      "processed": {
        "2016-11-06": 230653,
        "2016-11-07": 230701,
        "2016-11-08": 230645,
        "2016-11-09": 230645,
        "2016-11-10": 163385
      }
    },
    "queue_sizes": {
      "dead": 0,
      "retries": 0,
      "monitoring": 0,
      "low_priority": 0,
      "mails": 0,
      "default": 0,
      "elasticsearch": 0,
      "high_priority": 0,
      "file_upload": 0,
      "scheduled": 0
    },
    "active_workers": 0
  },
  "timestamps": {
    "sidekiq_performed": "2016-11-10 17:51:03",
    "whenever_ran": "2016-11-10 17:51:02",
    "requested": "2016-11-10 17:56:45"
  }
}

Illustration book lover

Growing Rails Applications in Practice

Check out our e-book. Learn to structure large Ruby on Rails codebases with the tools you already know and love.

  • Introduce design conventions for controllers and user-facing models
  • Create a system for growth
  • Build applications to last
Read more Show archive.org snapshot

Our friends from bitcrowd built a gem for it https://github.com/bitcrowd/sidekiq_monitoring Show archive.org snapshot .

Manual steps

In your application you need - at least - the following:

A controller rendering the monitoring output as JSON

class MonitoringController < ApplicationController

  def status
    data = {
      timestamps: {
        whenever_ran: Redis.current.get('monitoring:timestamp:whenever_ran'),
        sidekiq_performed: Redis.current.get('monitoring:timestamp:sidekiq_performed'),
        requested: Time.now.to_s(:db),
      },
      sidekiq: {
        active_workers: sidekiq_stats.workers_size,
        queue_sizes: sidekiq_queue_sizes,
        recent_history: {
          processed: sidekiq_history.processed,
          failed: sidekiq_history.failed
        },
        totals: {
          processed: sidekiq_stats.processed,
          failed: sidekiq_stats.failed
        }
      }
    }

    render json: data
  end

  private

  def sidekiq_stats
    @sidekiq_stats ||= Sidekiq::Stats.new
  end

  def sidekiq_history
    @sidekiq_history ||= Sidekiq::Stats::History.new(5)
  end

  def sidekiq_queue_sizes
    queue_sizes = sidekiq_stats.queues

    queue_sizes.merge({
      scheduled: sidekiq_stats.scheduled_size,
      retries: sidekiq_stats.retry_size,
      dead: sidekiq_stats.dead_size
    })
  end

end

A cronjob that puts a simple job to Sidekiq to ensure it is running

Put this into config/schedule.rb (if you're using whenever). Use whatever else to build the Cronjob on your server.

job_type :enqueue, 'cd :path && :environment_variable=:environment bundle exec bin/enqueue :task'

every 5.minutes do
  enqueue 'monitoring'
end

The worker itself should live in app/workers/cron_workers/monitoring_worker.rb and look like that:

require_relative 'base_worker'

class CronWorkers::MonitoringWorker < CronWorkers::BaseWorker

  def perform
    Redis.current.set('monitoring:timestamp:sidekiq_performed', Time.now.to_s(:db))
  end

end

And finally you need this in bin/enqueue:

#!/usr/bin/env ruby

require_relative '../config/initializers/sidekiq'

def enqueue(worker_class, args = [])
  job = worker_class.sidekiq_options.merge('args' => args, 'class' => worker_class.to_s)
  Sidekiq::Client.push(job)
end

case ARGV.shift
when 'monitoring'

  require_relative '../config/initializers/redis'
  require_relative '../app/workers/cron_workers/monitoring_worker'

  Redis.current.set 'monitoring:timestamp:whenever_ran', Time.now.strftime('%Y-%m-%d %H:%M:%S')

  enqueue(CronWorkers::MonitoringWorker)

end

Finally - and this is propably most important - you need a monitoring system (Icinga, Nagios, etc.) that checks the JSON output generated with the stuff above.

We built this into http://railscomplete.de Show archive.org snapshot .

Thomas Eisenbarth
November 10, 2016Software engineer at makandra GmbH
Posted by Thomas Eisenbarth to makandra dev (2016-11-10 17:53)