When I launched this blog last week I thought it was slow because of my small (256MB) Slice at Slicehost. Then I read about people optimizing their rails installations, so I optimized and it got a bit faster. But, it was still slow. So I read up on optimizing the speed and memory usage of Phusion Passenger aka mod_rails. I made some enhancements and it got a bit better. Problem was, it was still REALLY slow. For a second I got a bit discouraged, I thought to myself “Is this the kind of performance I can expect from a simple rails app?”, I quickly reminded myself that I was probably just not realizing that there was something I could do to make things faster. Then I remembered something I read about caching.
Page Caching is EZ
Following the super-duper-awesome page caching tutorial on the RailsEnvy blog, I quickly setup some caching for my posts controller. I also got my sweepers happening to make sure I’m not serving my guests anything old and stale (bad host indeed).
1 2 3 4 5 | class PostsController < ApplicationController ... caches_page :index, :show cache_sweeper :post_sweeper, :only => [:create, :update, :destroy] ... |
All together it took about 15 minutes, I spent some extra time troubleshooting a problem that was the result of naming my sweeper PostSweeper.rb rather than post_sweeper.rb.
mod_rails configuration
In the RailsEnvy tutorial they mention setting up some custom mod_rewrite rules to handle telling Apache to serve your cached pages instead of passing the requests on to your mongrel instance. From what I read in the Passenger/mod_rails user guide, this wasn’t necessary, plus they mention that mod_rails doesn’t play well with mod_rewrite anyhow, so I didn’t change anything about my vhost config.
PLS 2 BEE BENCHMRKIN
I read all about how cool and fast page caching was and not that I didn’t believe it, but I was curious how cool and fast it really was. To test any performance improvements I used Apache Benchmark (ab for short).
The test consisted of running 500 requests with a concurrency level of 5
1 | $> ab -c 5 -n 500 http://www.lengelzigich.com/ |
I ran this three times from start to finish, waiting about 30 seconds between each run. Then I ran it a fourth time and saved those results.
Before Page Caching
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | Server Software: Apache Server Hostname: www.lengelzigich.com Server Port: 80 Document Path: / Document Length: 4599 bytes Concurrency Level: 5 Time taken for tests: 92.220691 seconds Complete requests: 500 Failed requests: 2 (Connect: 2, Length: 0, Exceptions: 0) Write errors: 0 Total transferred: 2552000 bytes HTML transferred: 2299500 bytes Requests per second: 5.42 [#/sec] (mean) Time per request: 922.207 [ms] (mean) Time per request: 184.441 [ms] (mean, across all concurrent requests) Transfer rate: 27.02 [Kbytes/sec] received Connection Times (ms) min mean[+/-sd] median max Connect: 74 392 2023.8 78 19114 Processing: 96 115 34.8 103 536 Waiting: 94 111 24.8 100 207 Total: 172 508 2022.9 183 19215 Percentage of the requests served within a certain time (ms) 50% 183 66% 187 75% 199 80% 223 90% 249 95% 276 98% 7114 99% 11201 100% 19215 (longest request) |
After Page Caching
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | Server Software: Apache Server Hostname: www.lengelzigich.com Server Port: 80 Document Path: / Document Length: 4602 bytes Concurrency Level: 5 Time taken for tests: 24.150369 seconds Complete requests: 500 Failed requests: 0 Write errors: 0 Total transferred: 2553500 bytes HTML transferred: 2301000 bytes Requests per second: 20.70 [#/sec] (mean) Time per request: 241.504 [ms] (mean) Time per request: 48.301 [ms] (mean, across all concurrent requests) Transfer rate: 103.23 [Kbytes/sec] received Connection Times (ms) min mean[+/-sd] median max Connect: 74 88 180.5 78 4008 Processing: 95 119 36.3 105 494 Waiting: 93 116 35.4 103 492 Total: 171 208 183.2 186 4109 Percentage of the requests served within a certain time (ms) 50% 186 66% 192 75% 202 80% 218 90% 247 95% 261 98% 277 99% 392 100% 4109 (longest request) |
The Results
- 4x improvement in requests per second
- Time per request reduced by 75%
- 100% of requests were served in 4 seconds or less compared to 15 seconds or less
Overall I’m more than pleased with the results and plan on taking a look at the next RailsEnvy tutorial on ActionCaching.