Skip to content

Instantly share code, notes, and snippets.

@shikendon
Last active August 23, 2022 11:54
Show Gist options
  • Save shikendon/22d4dc9a3821a924898fd7de2b7fa56a to your computer and use it in GitHub Desktop.
Save shikendon/22d4dc9a3821a924898fd7de2b7fa56a to your computer and use it in GitHub Desktop.
require "heroku-api"
# heroku = Heroku::API.new(:api_key => API_KEY) # use API Key
# heroku = Heroku::API.new(:username => USERNAME, :password => PASSWORD) # use username and password
# heroku = Heroku::API.new(:headers => {'User-Agent' => 'custom'}) # use custom header
# NOTE: You can leave out the :api_key if ENV['HEROKU_API_KEY'] is set instead.
# https://github.com/heroku/heroku.rb
namespace :heroku do
namespace :webs do
desc "Restarts all Heroku webs"
task :restart do
heroku = Heroku::API.new
response = heroku.get_ps(ENV["HEROKU_APP_NAME"])
webs = response.body.select { |item| item["process"] =~ /web/ }
webs.each do |web|
process = web["process"]
pretty_state = web["pretty_state"]
puts "Restarting #{process} (#{pretty_state}) . . ."
heroku.post_ps_restart(ENV["HEROKU_APP_NAME"], "ps" => process)
end
end
end
namespace :workers do
desc "Restarts all Heroku workers"
task :restart do
heroku = Heroku::API.new
response = heroku.get_ps(ENV["HEROKU_APP_NAME"])
workers = response.body.select { |item| item["process"] =~ /worker/ }
workers.each do |worker|
process = worker["process"]
pretty_state = worker["pretty_state"]
puts "Restarting #{process} (#{pretty_state}) . . ."
heroku.post_ps_restart(ENV["HEROKU_APP_NAME"], "ps" => process)
end
end
desc "Restarts the Heroku worker that matches the rules"
task :try_restart, [:max_running_time] do |_t, args|
args.with_defaults(max_running_time: 600)
heroku = Heroku::API.new
response = heroku.get_ps(ENV["HEROKU_APP_NAME"])
workers = response.body.select { |item| item["process"] =~ /worker/ }
workers.each do |worker|
process = worker["process"]
elapsed = worker["elapsed"] # Seconds
pretty_state = worker["pretty_state"]
next if elapsed < args[:max_running_time]
puts "Restarting #{process} (#{pretty_state}) . . ."
heroku.post_ps_restart(ENV["HEROKU_APP_NAME"], "ps" => process)
break
end
end
desc "Auto scaling the Heroku workers count according to Sidekiq jobs count"
task :auto_scaling, [:WORKER_NAME] => :environment do |_t, args|
args.with_defaults(WORKER_NAME: "worker")
APP_NAME = ENV["HEROKU_APP_NAME"]
WORKER_NAME = args[:WORKER_NAME]
heroku = Heroku::API.new
queues = Sidekiq::Queue.all # 所有 Sidekiq 佇列名稱物件
queues_size = queues.map { |queue| Sidekiq::Queue.new(queue.name).size }.inject(0, :+) # 剩餘任務數量
# 平均一個 2X dyno 每分鐘可以處理 600 個 jobs
# 目前設定每個小時 50 分的時候會重新檢查一次有無尚未 parse 的 project_log
# 因此所有 jobs 都必須在 45 分以前完成否則會影響到下一個小時的任務效能
now_minutes = Time.now.strftime("%M").to_i # 目前分鐘
left_minutes = now_minutes.between?(0, 45) ? 45 - now_minutes : 0 # 剩餘分鐘
workers_size = queues_size / 500 / [left_minutes, 1].max # 剩餘任務數量 / 處理速度 / 剩餘分鐘
workers_size = 1 if workers_size < 1 # 最少開一個 worker
workers_size = 10 if workers_size > 10 # 最多開 10 個 worker
puts "Scaling #{WORKER_NAME} dyno count to #{workers_size}"
heroku.post_ps_scale(APP_NAME, WORKER_NAME, workers_size)
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment