Scaling Rails to 125,000 Requests per Minute on Heroku
zeemee.engineering
zeemee.engineering
I think we're paying around $2000/mo for our app servers, a database which is over 2TB in size, and we ingest about 10 megabytes of text data per second, on top of a couple thousand requests per second to the user facing application.
I don't think we'll be turning to Heroku any time soon. Sorry if this seems like a pissing contest, just giving some basis for comparison.
I work at Pivotal, we donate the majority of engineering to CF. We also dogfood it by running Pivotal Web Services (PWS)[0], which is a Heroku competitor.
One major difference: PWS (and CF) doesn't have fixed instance sizes. You can specify, to the megabyte, how much RAM you want your app to have. Other resources are dished out proportionally.
PWS has thousands of customers, thousands of VMs under management by BOSH, I'm not sure how many apps and services at this point. Last I checked, it's run by about 8-10 people in two shifts (SF and Beijing).
I was talking to a group in a startup last week, who had ~40 developers, and they looked horrified when I asked why, at their size, they were still running Heroku. They went on to equate "running your own VPS" with "writing your own crypto code", as though it was a cardinal sin for mere mortals.
However, last I looked, persistent storage is still under active development and moving to a distributed database is not in our priority at the moment.
I get that many teams don't want the overhead of managing their own instances, but when you find yourself optimizing for a platform that is engineered around convenience instead of performance i think it may be time to make the switch.
My "secret" is to use a language that is not slow-as-mollasses. A decade ago Rails had a definite productivity advantage over the (mostly Java) web frameworks of the time. That hasn't been true for a while, and there are many performant alternatives. If you're culturally a Ruby or Python programmer you can go pick up Go, or Node, or Elixir and feel at home. If you live in JVM or FP land you've always had fast alternatives (in my case it was Scala).
You pay a one-time cost to learn new things but you reap the benefits forever.
And then you're 10 years behind the curve when it comes to community support which is really the only thing that matters when it comes to being productive.
That's 10 years of less blog posts, tutorials and most importantly third party gems / packages that have been extracted from many years of real world use cases.
The libraries seem to be filling out quite quickly – and libraries may be a long tail problem. The set of 10 to 15 requirements every project has is basically there.
Even a lot of the really basic stuff like file uploading and image handling libraries aren't in a good state yet. For example, there's nothing that comes close to Carrierwave, Paperclip or Shrine and the Image Magick libraries are nowhere close to their equivalents in Ruby or Python.
It needs to be extracted from a real project and then carefully groomed and enhanced based on real problems.
Otherwise you're just sitting there trying to invent something with no use case. You can't invent a beautiful API or account for edge cases that you don't even know exist.
This is why Rails will be around and thriving for another 10 years. Until something so drastic comes along that it fully disrupts everything we know about web development, I will still reach for Rails.
Right now all of these alternative solutions in other languages aren't offering enough benefits to even think about switching.
P.S., I'm not even a Rails fanboy. I use other techs too and tried to get on the bandwagon with Node back in the early days and did the same thing with Go. It took a long time to realize how much of an utter waste of time it is to chase the new shiny thing.
For building a traditional monolithic website with server side rendering Rails and Django are far faster to develop in than anything in Go, Java or Node and the overwhelming majority of websites will never run into any performance problems with either.
> By way of contrast several years ago I benchmarked a real application hitting 1000 requests/s on a $20 per month Linode box
I have Rails applications that hit the database on every request doing 1000 rps on $10 a month Digital Ocean boxes. 1000 rps is not that much and all of these performance metrics are meaningless without extensive knowledge of how the application works and exactly what it is doing.
That's an indication of poor programming skills rather than programming language being "slow".
Python has a lot of rich libraries in a broad range of topics. It may not have the very best or fastest library in each topic, but it has at least one powerful easy to use library for anything you can think of.
This pretty much makes Python my go-to for explorative programming, or large-application development. The performance intensive parts can always become extensions (thanks to Cython), or be services written in Elixir or Java.
Assuming linear load – which we all know is never the case – is 2,000 req/s on average, which is nothing to be shy about.
This is phrased in a confusing way. If we're talking about sustained burst periods of traffic, then you almost definitely expect a fairly smooth distribution of requests over the course of a minute. You would only really start to expect serious variance at the daily granularity (i.e. time of day).
Thanks for all the discussion around this! The point of the article wasn't "How to get the most performance per $" - I would never recommend Ruby, Rails, or Heroku for that.
Rather, it was "If you're using Rails on Heroku, this is what it looks like to scale, and this is how far we could get it to go" For us, it's great! We can sit nearly idle (cheap), most of the year, and can crank it up to "expensive" a few days a year when needed.
If you need more/bigger/better scale than that for most of the year, then this article is still helpful because you know which technology to not use.
At ZeeMee we currently have about 2.5 people working on backend/rails stuff, and right now it makes more sense for us to work on features at this scale, rather than work on scaling itself. We were happy with how easy it was to crank up to this scale when we needed without really optimizing anything else.
Of course we'll switch off of Rails+Heroku when the (cost to swtich) < (cost to continue). Keep in mind that the cost to switch includes engineering costs, and opportunity costs (other things we could be working on).
Love the discussion, thanks!
And it is... Seriously, 2,000 requests per second is considered fast in the Rails community?
The JVM achieves several orders of magnitude that before the JIT even kicks in.
To be precise, in Java 8, if the JIT is not heavily loaded and the code cache has space, then for a method with no loops, compilation will happen when the invocation count exceeds the Tier3InvocationThreshold:
http://hg.openjdk.java.net/jdk8u/jdk8u/hotspot/file/2b2511bd...
On my machine, with 1.8.0_11-b12, that is 200.
There's the TechEmpower benchmarks for that though. And you can't deny Ruby is among the worst there.
Again you are comparing apples to oranges.
Rails and Heroku: two things that don't really seem like good value these days.
Your codebase is as good as your worst developer and it's quite easy to write faulty code in Ruby. The claims of any savings using Ruby are highly dubious in my experience of making lots of money being called in to unf*ck many a Ruby codebase as a contractor.
Sorry to bashing, but that is the reality
[1] Probably because Elixir is Ruby-like, and Phoenix has some Rails feel to it (so I was told).
> An important outcome of these tests is that the slow-response situation is outside of your control unless you’re running single-tenant (performance) dynos.
If you are on shared tenancy dynos, and your current configuration is nearing maximum capacity, then adding dynos can reduce performance. Not per dyno, but across the entire platform.
Counter-intuitive but true.
In my experience it should be tested as an independent variable (to dyno number and type) to optimise performance.
I will however 100% echo that it is never worth scaling on shared tenancy dynos. In my experience the combination of noisy neighbours and random routing means you'll very likely end up with worse performance by adding shared tenancy dynos.
With all of the talk about how slow ruby is, that sounds too intensive for a 2gb vps to handle, but I'm not a rails dev so I don't know.
Bothers me whenever I look at my New Relic dashboard but I haven't bothered to see if one can change to seconds rather than minutes.
The trouble with the dedicated dynos is that you need at least two for high availability (to not miss requests on dyno crashes / restarts). That puts the minimum cost per service at $500/mo. Not necessarily an issue with monolithic apps, but quickly untenable with microservices.
Anyone have any idea about what DB HN runs on and what is the size of the schema?
Oh, god.