This site is dedicated to further knowledge about creating Ruby on Rails applications professionaly. We discuss Ruby on Rails features from a performance angle, discuss Ruby on Rails performance analysis methods, provide information on Ruby on Rails scaling and benchmark Ruby on Rails performance for each release. We discuss best practices for selecting Ruby on Rails session containers, fragment and page caching and optimizing database queries.

Ruby en Rails 2006

Posted 11 May 2006

Next week, there’s a Rails conference in the Netherlands, Ruby en Rails 2006, were I will present my wisdom about Rails performance tuning. If you live nearby, and haven’t registered yet, now’s your last chance!

I’m sure it will prove to be a very interesting event. I’m especially looking forward to hear about a Ruby.net implementation project, presented by Wilco Bauwer.

And, of course I anticipate the after conference event, where we should have plenty of time to relax and dream up new ideas to save the world from joyless programming!

Update: I must say this was a very well organized event at a beautyful location, were I’ve met very nice people. It was a real pleasure to be there.

My slides are now available for download.

View Comments

Performance related changes in Rails 1.1

Posted 03 Apr 2006

In addition to being packed with new features and also some forgotten ones, the recently released Rails 1.1 contains a number of performance related changes. For some cases, you will see significant improvements.

The components implementation received a complete refactoring, making it both faster and simpler to use.

  1. inside a controller you can easily determine whether your current action is being run on behalf of an embedding controller by calling component_request?
  2. session and flash information will now be passed on from the embedding controller to the component. As a consequence, it is no longer necessary to save session information to the session container before a component action is invoked by the embedding controller. For pages embedding n components, this saves n session queries and updates, reducing DB load significantly if you use ActiveRecordStore or MysqlSessionStore.
  3. It is also possible to specify the controller as a class constant, bypassing the inflector code to compute the controller class at runtime. So
    render_component :controller => "greeter",
                     :action => "hello_world"
    
    becomes
    render_component :controller => GreeterController,
                     :action => "hello_world"
    

Due to the component refactoring, it is no longer necessary to save newly created sessions twice. This has the biggest impact for people using ActiveRecodStore.

We have changed the default for ActiveRecord::Base.allow_concurrency from true to false, since almost all apps run in a single threaded request container. In addition, you can now safely set ActiveRecord::Base.allow_concurrency in environment.rb or any other config file.

Connection cache management got a serious overhaul. Previously, the connection cache was emptied after each request, in order to solve problems with dropped database connections. You can now use lazy connection verification by setting ActiveRecord::Base.verification_timeout to a number of seconds timeout interval. It defaults to 0, to be compatible with the previous release. However, I strongly recommend setting it to a higher value, for a simple reason:

Dropped database connections occur mostly on sites not seeing traffic for the database internal connection timeout period. High traffic sites will see them rarely. However, for high traffic sites, reducing the number of database accesses by setting verification_timeout to a value slightly below the database internal value will result in a significant reduction of DB requests. For example, if you use memcached for session management, the DB won't see any hit for action cached pages.

Additionally, the connection cache size was reduced from the number of ActiveRecord classes involved in a query, to the number of active database connections. So for most applications this means that instead of retrieving and verifying n connections, at most 1 retrieval and verification will occur, and only when the timeout interval has passed.

Update: the correct way of determining whether you're in a component request is calling component_request?. In a view you can call controller.component_request?

View Comments

Rails 1.1 Release

Posted 30 Mar 2006

As you probably know already, Rails 1.1 was released a few days ago.

We have worked hard to ensure that 1.1 performs on the same level as 1.0. For some cases, it performs significantly better than 1.0 (explanation will follow in a future post).

The following performance data table shows the speed difference for the fastest available configuration for my application.

page c1 totalc2 total c1 r/sc2 r/s c1 ms/rc2 ms/r c1/c2
/empty/index 7.861397.10545 636.0703.7 1.571.42 1.11
/welcome/index 8.387438.32744 596.1600.4 1.681.67 1.01
/rezept/index 8.870388.75717 563.7571.0 1.771.75 1.01
/rezept/myknzlpzl 8.861728.76325 564.2570.6 1.771.75 1.01
/rezept/show/713 22.2253020.12046 225.0248.5 4.454.02 1.10
/rezept/cat/Hauptspeise 25.2905124.70123 197.7202.4 5.064.94 1.02
/rezept/cat/Hauptspeise?page=5 25.9252825.40904 192.9196.8 5.195.08 1.02
/rezept/letter/G 25.0624224.96315 199.5200.3 5.014.99 1.00

You can find additional information and lots of performance data in the full report .

View Comments

Presenting at RailsConf 2006

Posted 28 Mar 2006

I’m proud to announce that I will be giving a talk at RailsConf2006 on Rails Application Optimization.

If you have specific topics or questions you’d like to have addressed besides the ones mentioned in the talk proposal, or some general suggestion, I invite you to add an entry to the comment section of this post.

Looking forward to meet you in Chicago!

View Comments

Using memcached for Ruby on Rails session storage

Posted 24 Jan 2006

A few days ago Eric Hodel announced the availability of a new pure Ruby memcached client implementation (memcache-client-1.0.3) with performance improvements over the older Ruby-Memcache-0.4 implementation.

I had measured the old version previously, for use as a Ruby on Rails fragment cache storage container, but found its performance to be abysmal and completely unusable for my purposes.

memcache-client-1.0.3 provides much better performance: much faster than either the old implementation, pstore or ActiveRecordStore, but also faster than my optimized SQLSessionStore using MysqlSession (see Roll your own SQL session store).
In order to determine the relative performance I ran my usual benchmarks using railsbench against memcache-client-1.0.3 and SQLSessionStore using MyslSession.

All components resided on a single machine. Before running the tests, both DB tables and memcached were populated with 10000 sessions.

The first test was run with Mysql query caching disabled:

page c1 totalc2 total c1 r/sc2 r/s c1 ms/rc2 ms/r c1/c2
/empty/index 1.233180.96322 810.91038.2 1.230.96 1.28
/welcome/index 1.443141.15051 692.9869.2 1.441.15 1.25
/rezept/index 1.528691.19616 654.2836.0 1.531.20 1.28
/rezept/myknzlpzl 1.521591.19022 657.2840.2 1.521.19 1.28
/rezept/show/713 4.149063.95939 241.0252.6 4.153.96 1.05
/rezept/cat/Hauptspeise 9.481019.29202 105.5107.6 9.489.29 1.02
/rezept/cat/Hauptspeise?page=5 9.844049.65393 101.6103.6 9.849.65 1.02
/rezept/letter/G 5.423705.20227 184.4192.2 5.425.20 1.04
 c1: DB sessions, c2: memcached sessions, r/s: requests per second, ms/r: milliseconds per request

On the second run, Mysql query caching was enabled:

page c1 totalc2 total c1 r/sc2 r/s c1 ms/rc2 ms/r c1/c2
/empty/index 1.268040.95403 788.61048.2 1.270.95 1.33
/welcome/index 1.470701.13807 679.9878.7 1.471.14 1.29
/rezept/index 1.559761.18066 641.1847.0 1.561.18 1.32
/rezept/myknzlpzl 1.554021.17650 643.5850.0 1.551.18 1.32
/rezept/show/713 3.508763.20310 285.0312.2 3.513.20 1.10
/rezept/cat/Hauptspeise 4.317204.00799 231.6249.5 4.324.01 1.08
/rezept/cat/Hauptspeise?page=5 4.411504.11811 226.7242.8 4.414.12 1.07
/rezept/letter/G 4.311904.02345 231.9248.5 4.314.02 1.07
 c1: DB sessions, c2: memcached sessions, r/s: requests per second, ms/r: milliseconds per request

What can we learn from this data?

  • Pages which force the creation of new sessions (empty, welcome) and action cached pages (rezept/index and rezept/knzlpzl) experience a speedup between 25% and 33%.
  • Pages involving DB queries other than session retrieval don’t see as much speedup.
  • If query caching is disabled and the query is really DB expensive (/rezept/cat issues a LIKE %Hauptspeise%), the speed improvement is negligible.
  • Enabling the Mysql query cache will result in slightly slower creation of new sessions, but can speed up complex queries tremendously (table below).
page c1 totalc2 total c1 r/sc2 r/s c1 ms/rc2 ms/r c1/c2
/empty/index 1.233181.26804 810.9788.6 1.231.27 0.97
/welcome/index 1.443141.47070 692.9679.9 1.441.47 0.98
/rezept/index 1.528691.55976 654.2641.1 1.531.56 0.98
/rezept/myknzlpzl 1.521591.55402 657.2643.5 1.521.55 0.98
/rezept/show/713 4.149063.50876 241.0285.0 4.153.51 1.18
/rezept/cat/Hauptspeise 9.481014.31720 105.5231.6 9.484.32 2.20
/rezept/cat/Hauptspeise?page=5 9.844044.41150 101.6226.7 9.844.41 2.23
/rezept/letter/G 5.423704.31190 184.4231.9 5.424.31 1.26
 c1: DB sessions without query cache, c2: DB sessions with query cache

Like all benchmarks, these results have to be taken with a grain of salt. Choice of either option involves more than just looking at the above numbers.

Database and memcached have quite different scaling properties: adding more memcached daemons is easy, whereas DB based session storage scales mostly by buying a bigger DB machine or running the session DB on a separate machine using a session database.

On the other hand, DB session storage makes it easy to get application usage statistics using SQL queries, e.g. displaying the number of active sessions or checking whether a particular user is currently logged in. I don’t know an easy way to do this using the memcachd API.

It seems to me that using memcached for session storage is a good choice if your DB server experiences a very high load and your scaling options are already exhausted. I wouldn’t recommend to use it per default.

Using memcachd for Ruby on Rails fragment cache storage is a completely different story, on which I hope to report in the future.

View Comments

Older posts: 1 2 3 4 5 6 7 8 9