Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Handling hundreds of simultaneous requests in rails

I am writing a ruby on rails application and one of the most important featuers of the website is live voting. We fully expect that we will get 10k voting requests in as little as 1 minutes. Along with other requests that means we could be getting a ton of requests.

My initial idea is to set up the server to use apache + phusion, however, for the voting specifically I'm thinking about writing a php script on side and to write/read the information in memcached. The data only needs to persist for about 15 minutes, so writing to the database 10,000 times in 1 minute seems pointless. We also need to mark the ip of the user so they don't vote twice thus being extra complicated in memcached.

If anyone has any suggestions or ideas to make this work as best as possible, please help.

like image 260
Mike D Avatar asked Jun 30 '11 21:06

Mike D


People also ask

Can rails handle concurrent requests?

1 Automatic ConcurrencyRails automatically allows various operations to be performed at the same time. When using a threaded web server, such as the default Puma, multiple HTTP requests will be served simultaneously, with each request provided its own controller instance.

How many requests can Ruby on Rails handle?

Ruby applications can normally handle only 1 request at the same time. With Passenger this is improved by running multiple processes of the application using pooled application groups. For each request from the incoming request queue, a non-busy (free) application process is selected to handle the request.

How are requests handled in Rails?

When your application receives a request, the routing will determine which controller and action to run, then Rails creates an instance of that controller and runs the method with the same name as the action.

Is rails single threaded?

Rails as a framework is thread-safe. So, the answer is yes! The second link that you posted here names many of the Rails servers that don't do well with multi-threading. He later mentions that nginx is the way to go (it is definitely the most popular one and highly recommended).


1 Answers

If you're architecting an app for this kind of massive influx, you're going to need to strip down the essential components of it to the absolute minimum.

Using a full Rails stack for that kind of intensity isn't really practical, nor necessary. It would be much better to build a very thin Rack layer that handles the voting by making direct DB calls, skipping even an ORM, basically being a wrapper around an INSERT statement. This is something Sinatra and Sequel, which serves as an efficient query generator, might help with.

You should also be sure to tune your database properly, plus run many load tests against it to be sure it performs as expected, with a healthy margin for higher loading.

Making 10,000 DB calls in a minute isn't a big deal, each call will take only a fraction of a millisecond on a properly tuned stack. Memcached could offer higher performance especially if the results are not intended to be permanent. Memcached has an atomic increment operator which is exactly what you're looking for when simply tabulating votes. Redis is also a very capable temporary store.

Another idea is to scrap the DB altogether and write a persistent server process that speaks a simple JSON-based protocol. Eventmachine is great for throwing these things together if you're committed to Ruby, as is NodeJS if you're willing to build out a specialized tally server in JavaScript.

10,000 operations in a minute is easily achievable even on modest hardware using a specialized server processes without the overhead of a full DB stack.

You will just have to be sure that your scope is very well defined so you can test and heavily abuse your implementation prior to deploying it.

Since what you're describing is, at the very core, something equivalent to a hash lookup, the essential code is simply:

contest = @contest[contest_id]

unless (contest[:voted][ip])
  contest[:voted][ip] = true
  contest[:votes][entry_id] += 1
end

Running this several hundred thousand times in a second is entirely practical, so the only overhead would be wrapping a JSON layer around it.

like image 140
tadman Avatar answered Sep 27 '22 21:09

tadman