Heroku is a great tool to launch a new app easily. However, dynos can quickly reach their limit if your app is memory intensive. It was the case of my Rails app and I started to spend way more money than expected!
If you have already optimized memory following those Heroku tips, it's time to find another solution to avoid spending too much money.
How to restart Heroku dynos periodically
One efficient workaround is to restart dynos when the memory limit is close. Unfortunately, you will have to pass the whole day (and night!) in front of your computer if your app is leaking memory!
The solution is to create a scheduled task to restart the dynos consuming too much memory before they hit the limit.
Write a task in your app
First, add the platform-api gem to your Rails app:
gem 'platform-api'
bundle install
Create an OAuth token using the heroku-oauth toolbelt plugin:
$ heroku plugins:install heroku-cli-oauth
$ heroku authorizations:create -d "Platform API example token"
Created OAuth authorization.
ID: 2f01aac0-e9d3-4773-af4e-3e510aa006ca
Description: Platform API example token
Scope: global
Token: e7dd6ad7-3c6a-411e-a2be-c9fe52ac7ed2
Then, create a scheduler.rake
file in the lib/tasks folder of your Rails app.
require 'platform-api'
task :restart_dyno => :environment do
puts "task restart_worker is on"
heroku = PlatformAPI.connect_oauth(YOUR OAUTH TOKEN)
heroku.dyno.restart("APP_NAME", "web.1")
end
You can specify which dyno you want to restart by passing its name. According to the documentation, you can also restart all dynos at the same time:
require 'platform-api'
task :restart_dyno => :environment do
puts "task restart_worker is on"
heroku = PlatformAPI.connect_oauth(YOUR OAUTH TOKEN)
heroku.dyno.restart_all("APP_NAME")
end
Schedule your task
In order to perform a task on a repetitive basis, you will have to install the Heroku Scheduler add-on.
The Scheduler comes with a frequency limitation: you can only choose between "Daily", "Hourly" or "Every 10 minutes".
In case those frequencies are not ideal regarding your memory consumption, you can add a condition in your task to match the frequency you want. For example, if you want to restart your dyno twice a day:
require 'platform-api'
task :restart_dyno => :environment do
t = Time.now.strftime('%H')
if t == "07" or t == "14"
puts "task restart_worker is on"
heroku = PlatformAPI.connect_oauth(YOUR OAUTH TOKEN)
heroku.dyno.restart("APP_NAME", "web.1")
end
end
Once you have defined your frequency, add a new job to the Scheduler: rake restart_dyno
.
Now, you will have your dynos restarting automatically before hitting the memory limit. If you have any questions, feel free to contact me on Twitter.
Top comments (8)
Thanks for this post! That gem looks handy.
For those running Rails & Puma, the gem puma_worker_killer can do this as well. The nice thing about that one is it will restart your dynos sequentially to avoid downtime.
I once made a pull request to puma_worker_killer to use the Heroku log drain to measure memory use and restart if it gets too high. Sadly, it didn’t get merged. 😢
It's my first post here (and one of my first technical post ever), please let me know if you have any feedback ;)
This is just the kind of post I would have loved to have found while dealing with this issue myself.
For further on this subject, this talk is great:
Thanks Ben!
This just saved me so many headaches! Thanks!
I'm glad it's useful!
I finally implemented this.
By the way
t = Time.now.day.strftime('%H')
should bet = Time.now.strftime('%H')
.Thanks again!
Thanks, I corrected it.