Live data from Hacker News

Heroku Blog: Routing Performance Update

blog.heroku.com

171–180 of 197 posts

Re: Heroku Blog: Routing Performance Update

#172
post #90

Come on startups, you should be technically skilled and able to optimize in order to spend little money . If you sum EC2 and Heroku you are going to pay like 10x what it takes to run the same machines power in a dedicated server, all this because you can't handle the operations? This is absurd IMHO. Also people that want to start a business, there is a huge opportunity here , create software that makes managing Apach…

This seems to me like the server admin version of "build a website like Amazon."

http://news.ycombinator.com/item?id=5222581

What's more likely, that everyone else in the world is too dumb to see this opportunity, or that perhaps you have underestimated how hard it is to do?

Re: Heroku Blog: Routing Performance Update

#173

Earlier quoted context omitted.

Actually, no. Using unicorn with only 2 workers makes a tremendous difference, not just incremental. RapGenius' own statistical model demonstrates this. Picture each individual dyno in that case as its own "intelligent router". Since it's not distributed and this requires no network coordination, the job of knowing which workers are available becomes trivial. If you're inclined to read up on queuing theory, you'll se…

Fantastic, that's good to hear. I'm able to run ~5 workers on Heroku with gunicorn (Django), so I imagine that means I'm outta the woods for a while at least. I would love to read up a bit on queuing theory. Any good pointers?

There's a basic interactive + theory overview at http://homepages.inf.ed.ac.uk/jeh/Simjava/queueing/mm1_q/mm1... (NB: via in-browser java applets, which most things have disabled by default just now)

Re: Heroku Blog: Routing Performance Update

#174
post #168

Earlier quoted context omitted.

At this point we've done so much optimization of our app that our requests are not CPU or IO bound (those things have been offloaded to backend processes through a Rabbit message queue) and we still get H12 errors and random slowness. At the time of this writing, in the last 10 minutes, we've had 2 H12 errors. It should be zero.

I'm still confused: since you're using NodeJS, I imagine your dynos are effectively handling a large amount of concurrent requests. This should in turn negate any impact of long-running requests, since they don't cause further requests to be queued in any way. So, where are requests (or responses) being queued (or lost) in your app? In Rabbit? Are you getting errors and slowness as streaks rather than isolated random…

Node handles one request at a time. It isn't multithreaded. It will receive a request, process that request and return a response. If another request comes in at the same time another request is in process, it is queued until the currently processing request is finished. I googled around, here is a good explanation for you. http://howtonode.org/understanding-process-next-tick

The way my application worked is that we had 'long' running things like saving data to a database happening before we return a response to a client (in this case an iphone app). Sometimes mongo, the dyno, networking, phase of the moon, talking to the facebook api, etc... we would get 'slow' processing and it would take a few seconds for a response to make it to the client. As soon as this happens, on a heavily loaded system, the heroku router would get backed up (since it only routes to 2-3 dynos at a time) and would start throwing H12 errors.

So, what we did was rewrite the entire app to do minimal data processing in the web tier, send the response back to the client as quickly as possible. At the same time, we also send a rabbit queue message out with all the instructions in it to process the data 'offline' in a worker task. There is no spinup since these workers are running all the time. We even have several groups of workers depending on the message type so that we can segregate the work across multiple groups of dyno workers. This also allows us to easily scale to more than a 100 dynos to process messages. It works great. Rabbit is a godsend.

I say 'long' and 'slow' above because the longest amount of time we should be taking is a couple seconds at most. Unfortunately, the way that the heroku router is designed is fundamentally broken. As soon as you get a lot of 'slow' requests going to the same dyno's they start to stack up and the router just starts returning H12 errors. It doesn't matter how many dyno's you have because the router only talks to 2-3 dyno's at a time. We get H12's with 50, 100, 200, 300, etc dynos.

We also saw very strange behavior with the dyno's. We use nodetime to log how long things take and we'd see redis/mongo take only a few ms, but we'd have >15s just for the request to complete... somewhere things are slow and we can't figure out where. Until this whole mess came out, Heroku just pointed fingers at everyone else but themselves.

Oh by the way, as soon as you get around 200-300 dyno's deploys start error'ing out as well because heroku can't start up enough dyno's fast enough and that whole process times out too. You can't tell if a deploy worked or didn't. They didn't seem to care about that at all either.

Anyway, I could keep going... but once again, I'll repeat that I'm glad that the Rapgenius guys are calling Heroku out in public on this stuff. There is some big issues here that need to be addressed and the H12/router stuff is the big issue. I'm looking forward to see how they pull out of this one.

Re: Heroku Blog: Routing Performance Update

#175

Rap Genius cofounder here. Below is the full unedited text of https://help.heroku.com/tickets/37665 , a Heroku support ticket I logged about 1 year ago. Sorry it is so long, but I think you'll find it interesting: Tom@Rapgenius| about 1 year ago I know this is a bit of a vague problem, but I've been getting a bunch of Error H12 (Request Timeout)s recently, and I'm not sure what to do about it. It's not like I have so…

I had a string of similar requests with Heroku between Feb 2011 and June 2012, before we migrated off their platform. I would complain about h12 errors, they would tell me to upgrade my resources and/or that it was my problem and there was nothing they can do. We ended up with a solution that was easily 10x as expensive (over-powered DB, too many dynos) as our initial configuration, and it still didn't fix the issue.…

What did you migrate to?

Re: Heroku Blog: Routing Performance Update

#176
post #130

Earlier quoted context omitted.

It is quite possible to know that the mesh is not exactly as documented without having realized that the difference has a severe performance impact.

I think they also addressed the performance issue in addition to saying it is not documented: "...evolving away from the global backlog concept in order to provide better support for different concurrency models"

The fact that a year later, performance for their starting use case had not been addressed says that they failed to address the performance issue.

It is still not clear to me that they actually really understood the performance issue, or how big it was going to be.

Re: Heroku Blog: Routing Performance Update

#177
post #87

So to recap: Ruby on Rails is using a default configuration where each process can serve one request at a time. There is no cooperative switch (as in Node.js) or (near) preemptive switch (as in Erlang, Haskell, Go, ...). The routing infrastructure at Heroku is distributed . There are several routers and one router will queue at most one message per back-end dyno in the Bamboo stack and route randomly in the Cedar sta…

Great summary, and I too think that it's best to separate one discussion about blame/responsibility/trust and a different one about the architecture/technology.

What I feel is also missing from most comments about the technical aspects is the effect of running more than one process on each node (which is possible with rails using e.g. unicorn). Being able to process more than one request at a time on each node should alleviate wait times and bottle-necks, even with a simple e.g. round-robin routing layer. It might not be the absolute optimum, but it could still provide a pretty good / good enough balance.

As a side note about the overload scenario you mentioned - It's very interesting to consider, but we can easily get dragged into Denial-of-Service territory, and designing against DoS is an even harder problem in my opinion (until you considered handling 'normal' load anyway).

Re: Heroku Blog: Routing Performance Update

#178

Earlier quoted context omitted.

Have you guys considered suing Heroku to get some of your money back? Given the nature of Heroku's deception and the resulting ill-gotten gains across its entire customer bases, it would seem like you could work with an enterprising attorney to form a class-action suit against the company and get money back not just for yourselves but for the entire effected customer bases. Just a thought. ;)

It'd be like suing an airline for a delayed flight.

Yes, perfectly possible and regulated in the eu

Re: Heroku Blog: Routing Performance Update

#179
post #86

Earlier quoted context omitted.

> (1) Shard/tier the Bamboo routing nodes... This would work, but would require another layer before routers to do the sharding. I'm not sure if Heroku currently has such layer, or is it beyond their control (provided by AWS). But this is definitely an option. > (2) Enable dynos to refuse requests... The way I understand the architecture is that Dynos code is fully controlled by a client. They would need to introduce…

re (2): The router could just interpret a specific response code, or an early-close of the socket, as a directive to try another dyno. (I think the router already treats a failure-to-connect-to-listening-socket this way.) So, it would be up to the client code to optionally send such a refusal, when appropriate. It doesn't require any new Heroku component to decide when to send rejections... only router support for re…

A simple solution might be to just have the request routers connect in parallel to multiple dynos, and use the first one to connect successfully. You'll increase overall load (which can be mitigated to an extent by waiting a short delay before opening additional sockets), but a single slow dyno won't hold you up either.

Re: Heroku Blog: Routing Performance Update

#180
post #87

So to recap: Ruby on Rails is using a default configuration where each process can serve one request at a time. There is no cooperative switch (as in Node.js) or (near) preemptive switch (as in Erlang, Haskell, Go, ...). The routing infrastructure at Heroku is distributed . There are several routers and one router will queue at most one message per back-end dyno in the Bamboo stack and route randomly in the Cedar sta…

Great summary, and I too think that it's best to separate one discussion about blame/responsibility/trust and a different one about the architecture/technology. What I feel is also missing from most comments about the technical aspects is the effect of running more than one process on each node (which is possible with rails using e.g. unicorn). Being able to process more than one request at a time on each node should…

Overload is as much about establishing a baseline. Even under "perfect" routing, the problem is that you need to know what load your system can cope with.

You need queueing to a certain extent since a queue will absorb spikes in the load and smooth out the sudden arrival of a large number of requests in a short time. But excessive queueing leads to latency.

One of the "intelligent" routing problems is we have to consider something more than queue length. We need to know, prior to running, how expensive a query is going to be. Otherwise we may end up queueing someone after the expensive query while a neighboring dyno could serve the request quickly. But you generally can't do this. Under load, such a system would "amplify" expensive queries: all queries after the expensive one in queue will be expensive as well.

This is why I would advise people to move to a model where concurrency happens "in the process" as well. It is actually easier to dequeue the work off the routing layer as fast as possible and then interleave expensive and cheap work in the dyno.

Post reply on HN