Skip to content

Instantly share code, notes, and snippets.

@nhocki
Last active January 13, 2022 02:14
Show Gist options
  • Save nhocki/323eeaa5deeb09a0bb11439dd61b30d2 to your computer and use it in GitHub Desktop.
Save nhocki/323eeaa5deeb09a0bb11439dd61b30d2 to your computer and use it in GitHub Desktop.
Most naive worker autoscaler for Heroku.
class Autoscaler
def initialize(app_name:, token: ENV["AUTOSCALE_TOKEN"])
@client = PlatformAPI.connect_oauth(token)
@app_name = app_name
end
def run!
puts "[Autoscaler] We currently have #{current_number_of_workers} workers and #{number_of_queued_jobs} jobs queued."
if number_of_queued_jobs > 0 && current_number_of_workers < 1
puts "[Autoscaler] We have queued jobs & no workers! Scaling up!"
scale_workers(1)
elsif number_of_queued_jobs.zero? && current_number_of_workers > 0
puts "[Autoscaler] No queued jobs. Removing all workers"
scale_workers(0)
else
puts "[Autoscaler] No action needed!"
end
end
private
def number_of_queued_jobs
Sidekiq::Queue.all.sum(&:size)
end
def current_number_of_workers
worker_process["quantity"]
end
def scale_workers(count)
if current_number_of_workers == count
puts "[Autoscaler] No action needed!"
end
@client.formation.batch_update(
@app_name,
updates: [{quantity: count, size: worker_process["size"], type: "worker"}]
)
puts "[Autoscaler] Done!"
end
def worker_process
@worker_process ||= processes.find { |process| process["type"] == "worker" }
end
def processes
@processes ||= @client.formation.list(@app_name)
end
end
# Add this to Heroku scheduler to run every 10 minutes: bundle exec rails infrastructure:autoscale_workers
namespace :infrastructure do
desc "Autoscale sidekiq workers based on currently scheduled jobs"
task autoscale_workers: [:environment] do
Autoscaler.new(app_name: "your_app_name").run!
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment