How Should I Manage Heroku Workers For Infrequent Jobs

161 Views Asked by At

I'm working on an application (deployed to Heroku) which is split into public and admin sections. One of the main tasks in the admin section (administered by a handful of users) is uploading images. These images can be very large and need to be processed to a number of sizes (some very large). I am handling the image upload client-side, uploading directly to S3, then handling the image processing using Sidekiq. This avoids blocking the web dyno and problems with Unicorn timeouts.

The problem is that this means I need a worker dyno running at all times, even though the administrators will often only upload a couple of images a day (though they may upload many). My initial thought was to use Hirefire, but it only checks on the queue every minute (though in my tests, it seems closer to every 3 mins). The administrator has tasks to complete once the image is processed, so I need to get it processed as quickly as possible, so this kind of delay (when combined with processing time) is not acceptable.

So I'm looking for a solution that does the following:

  1. Spins up a worker dyno to handle any jobs if needed as soon as a job is added.
  2. Spins down the worker dyno when the queue is empty.

Hirefire gets me halfway there (I don't mind a delay to spin down the dyno).

What are my options?

1

There are 1 best solutions below

0
On

Hopefully someone can shoot this down and suggest a better approach, but here is a simple attempt at this:

This uses Hirefire to take the worker dyno down when the queue is empty, and uses the heroku-api Gem to talk to Heroku.

initializers/heroku.rb

require 'heroku-api'

if Rails.env.production? || Rails.env.staging?
  Rails.application.config.heroku = Heroku::API.new(api_key: ENV['HEROKU_API_KEY'])
end

app/services/heroku_service.rb

require 'heroku-api'

class HerokuService

  def self.ensureWorker
    if Rails.application.config.respond_to? :heroku
      # spawn a worker if needed
      Rails.application.config.heroku.post_ps_scale(ENV['HEROKU_APP_NAME'], 'worker', 1)
    end
  end

end

Obviously the call to the Heroku API is blocking and the dyno takes some time to spin up, but it's far quicker than relying on HireFire alone. I'm wondering if using the scheduler to spawn one-off dynos that check on the queue at regular intervals might be preferable to using Hirefire. For example there would be no need to monitor the application between 10pm and 6am.