Reputation: 14630
About 6 years ago there was a rails gem caled HireFire that worked with background jobs to start and stop workers as needed, so the worker wasn't always running and always accumulating charges.
HireFire seems to be defunct (as a gem), and I'm wondering how I can autoscale heroku workers like this these days?
I'm not interested in paying for a service for this.
I've looked around and am surprised that the solution doesn't seem obvious.
Has heroku implemented this type of auto-scaling, or is there another rails gem that will do it for me? Thanks
Upvotes: 1
Views: 893
Reputation: 8561
As of 2019, Heroku gem was sunset. So use the web api instead. In my case, I used rest-client. Authorization credentials can be retrieved from heroku cli using
heroku authorizations:create
if Rails.env.production?
response = RestClient::Request.execute(
method: :delete,
url: 'https://api.heroku.com/apps/yourappname/dynos/worker.1',
headers: {Authorization: 'Bearer xxxx-xxx-xxx-xxx-xxxxx',accept:'application/vnd.heroku+json; version=3'}
)
end
Upvotes: 0
Reputation: 14630
Update: I couldn't find an existing library/gem/plugin to do this for me, so I decided to try to solve it myself.
I decided to post back here after I came up with a solution. Perhaps it will be helpful for others.
DelayedJob has a plugin system (I had to upgrade my gem version to the latest)
This is the Plugin I wrote. I don't think it will win awards for beauty, but it seems to do the trick. Please post comments if I'm doing something stupid.
I also made it so it checks for production/development env and runs the local DelayedJob script when in development.
class HerokuWorkerDelayedJobPlugin < Delayed::Plugin
callbacks do |lifecycle|
lifecycle.before(:enqueue) do |job, *args, &block|
Rails.logger.info "----before enqueue -----"
self.start
end
lifecycle.after(:enqueue) do |job, *args, &block|
Rails.logger.info "----after enqueue -----"
end
lifecycle.after(:invoke_job) do |job, *args, &block|
Rails.logger.info "----after invoke_job -----"
end
lifecycle.after(:perform) do |worker, job, *args, &block|
Rails.logger.info "----after perform-----"
self.stop
end
def self.start
if Rails.env.production?
self.set_heroku_workers 1
else
`script/delayed_job start`
end
end
def self.stop
if Rails.env.production?
self.set_heroku_workers 0
else
`script/delayed_job stop`
end
end
def self.set_heroku_workers(num)
heroku = PlatformAPI.connect_oauth(ENV['PLATFORM_API_OAUTH_TOKEN']) #https://github.com/heroku/platform-api
heroku.formation.update('my-app', 'worker', {quantity: num}) #https://devcenter.heroku.com/articles/platform-api-reference#formation
end
end
end
You'll want to add the plugin to DelayedJob like this in your initializer:
Delayed::Worker.plugins << HerokuWorkerDelayedJobPlugin
Upvotes: 4
Reputation: 52376
You can scale Heroku dynos using the CLI, so building your own service to do this can be pretty simple.
Create another app that uses the scheduler to run every hour, using a unix shell script to download and install the CLI, and use credentials stored as ENV variables to send scale
commands to other apps.
Here's a codebase as an example of the technique: https://github.com/kbaum/heroku-database-backups
Upvotes: 0