Nate's Tumble Log

  • Archive
  • RSS
  • Ask me anything

How to autoscale Heroku

Heroku’s awesome, but some stuff is still a pain in the ass.

It’s great I can scale my app with the push of a button. But I don’t want to push a button. I just want it to happen.

We’ve been running Cityposh on Heroku. Some days have big spikes in traffic, and some days have real lows. I don’t want to have to keep looking at our web traffic to see if I need to be asking Heroku to scale for me. And it’s a huge waste of money to just keep a bunch of web workers going if there’s no one using them.

There’s a few solutions for this problem currently. Including a business that will do it for you. And numerous plugins/gems that will try and do it. Someone at Heroku even wrote one:

https://github.com/ddollar/heroku-autoscale

I don’t want to pay more money to make this work since it seems easy enough to fix on my own programatically. But all these gems seem to have some kind of issue. This one from Heroku has this fat warning:


WARNING

This gem is a proof of concept and should not be used in production applications. There is currently no mechanism to prevent multiple web workers on the same app all running this code from fighting each other for control.


The gem is a Rack app that intercepts every single request going to you Rails app. And like it says, multiple dynos are running that same app all concurrently. So you could have multiple dynos all sending commands back to Heroku to scale your app on top of each other. We don’t want that.

But there’s a great way to have things run on Heroku without tying them to every single web worker and do it on a frequent schedule: Clockwork!

https://github.com/tomykaira/clockwork

It’s a replacement for cron.

So my approach was to:

  1. Have Clockwork + Delayed Job ask our Rails app to auto scale.
  2. Change ddollar’s gem to scale only when the there’s been an explicit request to do so.

Clockwork is easy to setup. And so is Delayed Job. I’ll assume you have those things figured out.

In my clock.rb file I invoke a Delayed Job I made to ask our app to scale.

every(1.minutes, 'scale_heroku') { Delayed::Job.enqueue ScaleHerokuJob.new }

And all that job does is issue a GET request to Cityposh’s explicit autoscale action (a Rack app).

 class ScaleHerokuJob
   def perform
     HTTParty.get("http://cityposh.com/autoscale/#{ENV['HEROKU_SCALE_KEY']}")
   end
 end

Next, I just made some quick and dirty changes to ddollar’s gem to make it more explicit.

https://github.com/n8/heroku-autoscale

 def call(env)
   if env["PATH_INFO"] == "/autoscale/#{options[:autoscale_key]}"
     autoscale env
     [200, {'Content-Type' => 'text/plain'}, ["Current wait time: #{env["HTTP_X_HEROKU_QUEUE_WAIT_TIME"]}"]]
   else
     app.call(env)
   end
 end

Now our Rails app performs all it’s actions normally unless we specifically ask the rails app to autoscale with our special key. The key being in there might be a little unnecessary, but it just protects the url from being called by just anyone reading this blog post.

Make sense?

  • 7 months ago
  • 1
  • Comments
  • Permalink
  • Share
    Tweet

1 Notes/ Hide

  1. robbym liked this
  2. n8 posted this

Recent comments

Blog comments powered by Disqus
← Previous • Next →

About

I work a bunch at Inkling Prediction Markets, our collaboration tool, and now Cityposh.
 

Enter your email address:

Twitter

loading tweets…

  • RSS
  • Random
  • Archive
  • Ask me anything
  • Mobile

Effector Theme by Carlo Franco.

Powered by Tumblr