News for hackers. Stuff that matters.
Re:Heroku's Ugly Secret: The story of how the cloud-king tur... by nthj on Wednesday February 13, 2013 @06:49PM (#5216553)
I'm inclined to wait until Heroku weighs in to render judgement. Specifically, because their argument depends on this premise:

> But elsewhere in their current docs, they make the same old statement loud and clear: > The heroku.com stack only supports single threaded requests. Even if your applicaExplaintion were to fork and support handling multiple requests at once, the routing mesh will never serve more than a single request to a dyno at a time.

They pull this from Heroku's documentation on the Bamboo stack [1], but then extrapolate and say it also applies to Heroku's Cedar stack.

However, I don't believe this to be true. Recently, I wrote a brief tutorial on implementing Google Apps' openID into your Rails app.

The underlying problem with doing so on a free (single-dyno) Heroku app is that while your app makes an authentication request to Google, Google turns around and makes a "oh hey" request to your app. With a single-concurrency system, Google your app times out waiting for Google to get back to you and Google won't get back to you until your app gets back to you so hey deadlock.

However, there is a work-around on the Cedar stack: configure the unicorn server to supply 4 or so worker processes for your web server, and the Heroku routing mesh appropriately routes multiple concurrent requests to Unicorn/my app. This immediately fixed my deadlock problem. I have code and more details in a blog post I wrote recently. [2]

This seems to be confirmed by Heroku's documentation on dynos [3]: > Multi-threaded or event-driven environments like Java, Unicorn, and Node.js can handle many concurrent requests. Load testing these applications is the only realistic way to determine request throughput.

I might be missing something really obvious here, but to summarize: their premise is that Heroku only supports single-threaded requests, which is true on the legacy Bamboo stack but I don't believe to be true on Cedar, which they consider their "canonical" stack and where I have been hosting Rails apps for quite a while.

[1] https://devcenter.heroku.com/articles/http-routing-bamboo

[2] http://www.thirdprestige.com/posts/your-website-and-email-ac...

[3] https://devcenter.heroku.com/articles/dynos#dynos-and-reques...

[edit: formatting]

Re:Heroku's Ugly Secret: The story of how the cloud-king tur... by wwarnerandrew on Wednesday February 13, 2013 @07:06PM (#5216653)
Yes, it's true that the Cedar stack supports forking web servers like unicorn, and that an individual dyno can run multiple workers and therefore serve multiple requests at the same time.

However, dumb routing is still very problematic – even if your dyno can work on two requests simultaneously it's still bad for it to get sent a third request when there are other open dynos.

Also, for apps with a large-ish memory footprint, you can't run very many workers. A heroku dyno has 512mb memory, so if your app has a 250mb footprint, then you can basically only have two workers.

Another essential point to note is that the routing between cedar and bamboo is essentially unchanged. They simply changed the type of apps you can run.

Re:Heroku's Ugly Secret: The story of how the cloud-king tur... by richcollins on Wednesday February 13, 2013 @07:24PM (#5216732)
Right if the apps internal queue is full and it stops accepting connections I'm assuming it will still queue at the dyno level anyway.
Re:Heroku's Ugly Secret: The story of how the cloud-king tur... by kennystone on Wednesday February 13, 2013 @07:02PM (#5216628)
If you have 2 unicorn servers and you happen to get 3 slow requests routed to it, you are still screwed, right? Seems to me like it will still queue on that dyno.
Re:Heroku's Ugly Secret: The story of how the cloud-king tur... by michaelrkn on Wednesday February 13, 2013 @07:03PM (#5216633)
That's exactly what happened to us - switching to unicorn bought us a little time and a bit of performance, but we hit the exact same problems again after a couple more weeks of growth.
Re:Heroku's Ugly Secret: The story of how the cloud-king tur... by ibdknox on Wednesday February 13, 2013 @07:05PM (#5216648)
Yeah, the only real question is whether or not it's true that they no longer do intelligent routing. If that is the case, then regardless of anything else the problem exists once you pass a certain scale/request cost. It won't matter if that one dyno can handle hundreds of requests at once, it will still queue stupidly.
Re:Heroku's Ugly Secret: The story of how the cloud-king tur... by barmstrong on Wednesday February 13, 2013 @07:13PM (#5216677)
This is true - unicorn masks the symptoms for a period of time but does not solve the underlying problem in the way a global request queue would.

Also, if the unicorn process is doing something cpu intensive (vs waiting on a 3rd party service or io etc) then it won't serve 3 requests simultaneously as fast as single processes would.

Re:Heroku's Ugly Secret: The story of how the cloud-king tur... by rjacoby5 on Thursday February 14, 2013 @10:48AM (#5219954)
One of the hidden costs of Unicorn is spin-up time. Unicorn takes a long time to start, then fork. We would get a ton of request timeouts during this period. Switching back to Thin, we never got timeouts during deploys - even under very heavy load.
Re:Heroku's Ugly Secret: The story of how the cloud-king tur... by adrr on Wednesday February 13, 2013 @11:31PM (#5217742)
Maybe this is a stupid question, but with unicorn it forks the request and can process multiple requests at the same time. Previously it seems that only one request could be handled by the dyno so requests had to queue on the dynamic routing layer but with multiple request support with unicorn or whatever, wouldn't it be more efficient to dump all the requests to dynos? Followup question, also how would intelligent routing work if it just previously checked to see if which dyno had no requests? That seems like an easy thing to do, now you would have to check CPU/IO whatever and route based on load. Not specifically targeted at you but to everyone reading the thread.
Re:Heroku's Ugly Secret: The story of how the cloud-king tur... by vidarh on Thursday February 14, 2013 @05:50AM (#5218868)
> Previously it seems that only one request could be handled by the dyno so requests had to queue on the dynamic routing layer but with multiple request support with unicorn or whatever, wouldn't it be more efficient to dump all the requests to dynos?

It would be if all requests were equal. If all your requests always take 100ms, spreading them equally would work fine.

But consider if one of them takes longer. Doesn't have to be much, but the effect will be much more severe if you e.g. have a request that grinds the disk for a few seconds.

Even if each dyno can handle more than one requests, since those requests share resources, if some of them slows down due to some long running request, response times for the other requests are likely to increase, and as response times increase, it's queue is likely to increase further, and it gets more likely to pile up more long running requests.

> Followup question, also how would intelligent routing work if it just previously checked to see if which dyno had no requests? That seems like an easy thing to do, now you would have to check CPU/IO whatever and route based on load. Not specifically targeted at you but to everyone reading the thread.

There is no perfect answer. Just routing by least connections is one option. it will hurt some queries that will end up being piled up on servers processing a heavy request in high load situations, but pretty soon any heavily loaded servers will have enough connections all the time that most new requests will go to lighter loaded servers.

Adding "buckets" of servers for different types of requests is one option to improve it, if you can easily tell by url which requests will be slow.

Re:Heroku's Ugly Secret: The story of how the cloud-king tur... by nevinera on Wednesday February 13, 2013 @09:33PM (#5217309)
That gets pretty unlikely, especially if you have many dynos and a low frequency of slow requests. The main reason unicorn can drastically reduce queue times here is that it does not use random routing internally.
Re:Heroku's Ugly Secret: The story of how the cloud-king tur... by richcollins on Wednesday February 13, 2013 @07:23PM (#5216727)
how does it decide to queue at the dyno level anyway? Does it check for connection refusal at the TCP level?
Re:Heroku's Ugly Secret: The story of how the cloud-king tur... by dblock on Wednesday February 13, 2013 @08:37PM (#5217061)
The connection is accepted, and a single-threaded web server will do the queuing.
Re:Heroku's Ugly Secret: The story of how the cloud-king tur... by richcollins on Thursday February 14, 2013 @11:20PM (#5224411)
Oh so the server process hosting rails is itself queueing? Is that what they refer to as "dyno queueing"? I thought perhaps there was another server between the router and your apps server process.
Re:Heroku's Ugly Secret: The story of how the cloud-king tur... by spoiler on Wednesday February 13, 2013 @07:49PM (#5216843)
Slightly off topic, but what are everyone's. experience and thoughts about Puma[1]?

I am using it on a small production environment with Heroku and I like it, but when we officially launch the app, should we switch to Unicorn?

[1] http://puma.io/

Re:Heroku's Ugly Secret: The story of how the cloud-king tur... by gizzlon on Thursday February 14, 2013 @03:59AM (#5218532)
I don't have a stake in the Ruby webserver wars, but the unicorn site has a very good discussion about how it works internally, why it's build as it is, pros & cons etc..

This seems to be missing from most of these project sites, which are often just marketing (look! It's better!!), and therefore not very trustworthy.

From the outside it looks like the biggest differentiator in each generation of ruby servers (and, I guess, db managment systems :) is not that the new is better or worse, but simply that has different trade-offs.

Re:Heroku's Ugly Secret: The story of how the cloud-king tur... by ylansegal on Wednesday February 13, 2013 @11:01PM (#5217624)
I did some performance analysis on puma vs unicorn vs thin a while ago:

http://ylan.segal-family.com/blog/2012/08/20/better-performa...

Although as noted in the comments, I neglected to run threadsafe! and should have probably tried rubinius or jruby. I have been meaning to redo. Take with a grain of salt

Re:Heroku's Ugly Secret: The story of how the cloud-king tur... by argarg on Thursday February 14, 2013 @03:49PM (#5222146)
You should give a try to puma 2.0 currently in beta 6 (https://rubygems.org/gems/puma). Lots of performance improvements. I haven't benchmarked it but my guess is it outperforms unicorn.
Re:Heroku's Ugly Secret: The story of how the cloud-king tur... by NatW on Thursday February 14, 2013 @12:52AM (#5217967)
You seem have written one of the very few articles I've seen benchmarking this. I'd love to know more about how Puma compares to Unicorn (especially unicorn configurations mentioned by some in this conversation) and Thin for serving rails on Heroku. Many of the Unicorners pushing their solution don't appear to be aware of Puma and its potential benefits. I'm curious if Puma with MRI has benefits, too. Thanks!
Re:Heroku's Ugly Secret: The story of how the cloud-king tur... by miloshadzic on Thursday February 14, 2013 @08:32AM (#5219331)
AFAIK Puma should have a lower memory footprint on MRI than Thin but I haven't done benchmarks myself.
Re:Heroku's Ugly Secret: The story of how the cloud-king tur... by steveklabnik on Thursday February 14, 2013 @01:37PM (#5221086)
Also note that Rails 4 will have threadsafe! on by default, too, so if you didn't have it on here, that'll make it different for those apps.
Re:Heroku's Ugly Secret: The story of how the cloud-king tur... by tcc619 on Wednesday February 13, 2013 @08:34PM (#5217040)
I have been using it on a few small projects. Haven't ran into an issue yet and the setup has been really easy.
Re:Heroku's Ugly Secret: The story of how the cloud-king tur... by pselbert on Wednesday February 13, 2013 @11:46PM (#5217789)
We are running a smaller app on JRuby using Puma with thread safe on. It has a significantly smaller footprint, as we are only booting one server.

Overall really solid, though more useful if you can use something other than MRI.

Re:Heroku's Ugly Secret: The story of how the cloud-king tur... by jarcoal on Wednesday February 13, 2013 @06:52PM (#5216575)
This is how I run my apps as well, and they seem to handle more than one request concurrently per dyno, but I'm not smart enough to dispute this post, so I'm just sitting back and watching.
Re:Heroku's Ugly Secret: The story of how the cloud-king tur... by dedsm on Wednesday February 13, 2013 @07:06PM (#5216651)
Nevertheless, random routing is a bad idea even if a dyno can handle multiple requests simultaneously
Re:Heroku's Ugly Secret: The story of how the cloud-king tur... by scottshea on Wednesday February 13, 2013 @10:31PM (#5217520)
To amplify, I do a ton of queue adjustment work with Unicorn at the backlog level. It is so frequent that we set up Unicorn startup to read from an ENV variable on Heroku that we set as needed.

With two Unicorn workers we found that 25 was the best backlog threshold to accept (it refuses additional requests). When we were able to go to 5 Unicorn workers on Heroku we had to start to adjust that.

Re:Heroku's Ugly Secret: The story of how the cloud-king tur... by tibbon on Wednesday February 13, 2013 @10:43PM (#5217559)
You don't happen to have any documentation for how to do that do you? Very curious. Never seen anything about setting up Unicorn like this prior (I'm just using 3 or 4 Unicorns/dyno currently)
Re:Heroku's Ugly Secret: The story of how the cloud-king tur... by jwrubel on Thursday February 14, 2013 @11:21AM (#5220124)
Here's a gist of our unicorn.rb config (https://gist.github.com/apangeajwrubel/4953849) Using env variables lets us adjust parameters without a code push (still requires a restart). We saw dramatic reduction in 'long tail' requests when we dropped the backlog to 25. We're experimenting now (thanks to @scottshea) with even lower values. At some point the routing mesh will give up retrying and throw an H21 (https://devcenter.heroku.com/articles/error-codes#h21-backen...). One data point that would be useful from heroku is how many retries we get.
Re:Heroku's Ugly Secret: The story of how the cloud-king tur... by pointful on Thursday February 14, 2013 @11:32AM (#5220187)
To expand on this:

You have to remove the port declaration from the line for Unicorn in your Procfile, and then add a line like this to your unicorn.rb file to define the listener port along with adjusting the backlog size:

listen ENV['PORT'], :backlog => Integer(ENV['UNICORN_BACKLOG'] || 100)

Re:Heroku's Ugly Secret: The story of how the cloud-king tur... by scottshea on Thursday February 14, 2013 @11:04AM (#5220041)
We did this in unicorn.rb `:backlog => Integer(ENV['UNICORN_BACKLOG'] || 200)` and then set the UNICORN_BACKLOG variable by the Heroku command line `heroku config:set UNICORN_BACKLOG=25 -a <app_name>`. We have been as high as 1024 and as low as 10. We settled in at 25 for us.
Re:Heroku's Ugly Secret: The story of how the cloud-king tur... by seivan on Wednesday February 13, 2013 @06:58PM (#5216603)
How I have been doing it for the last year.

Puma define 4:8 threads or Unicorn 3 workers.