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.

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.

Posted in performance | Tags memcachedsessions


blog comments powered by Disqus