WordPress, Varnish and Edge Side Includes
While talking about WordPress and it’s abysmal performance in high traffic situations to a client, we started looking back at Varnish and other solutions to keep their machine responsive. Since most of the caching solutions generate a page, serve it and cache it, posts and comments tend to lag behind the cache. db-cache does work around this by caching the query objects so that the pages can be generated more quickly and does expire the cache when tables are updated, but, its performance is still lacking. Using APC’s opcode cache or memcached just seemed to add complexity to the overall solution.
Sites like perezhilton.com appear to run behind multiple servers running Varnish, use wp-cache, move the images off to a CDN which results in a 3 request per second site with an 18 second pageload. Varnish’s cache always shows an age of 0 meaning Varnish is acting more as a load balancer than a front-end cache.
Caching isn’t without its downside. Your weblogs will not represent the true traffic. Since Varnish intercepts and serves requests before they get to the backend, those hits never hit the log. Forget pageview/postview stats (even with addons) because the addon won’t get loaded except during caching. Certain Widgets that rely on cookies or IP addresses will need to be modified. A workaround is to use a Text Box Widget and do an ESI include of the widget. For this client, we needed only some of the basic widgets. The hits in the apache logs will come from an IP of 127.0.0.1. Adjust your apache configuration to show the X-Forwarded-For IP address in the logs. If you truly need statistics, you’ll need to use something like Google Analytics. Put their code outside your page elements so that waiting for that javascript to load doesn’t slow down the rendering in the browser.
The test site, http://varnish.cd34.com/ is running Varnish 2.0.4, Apache2-mpm-prefork 2.2.11, Debian/Testing, WordPress 2.8.2. I’ve loaded the default .xml import for testing templates so that there were posts with varied dates and construction in the site. To replicate the client’s site, the following Widgets were added the sidebar: Search, Archives, Categories, Pages, Recent Posts, Tag Cloud, Calendar. Calendar isn’t in the existing site, but, since it is a very ‘expensive’ SQL query to run, it made for a good benchmark.
The demo site is running on:
model name : Intel(R) Celeron(R) CPU 2.40GHz stepping : 9 cpu MHz : 2400.389 cache size : 128 KB
with a Western Digital 80gb 7200RPM IDE drive. Since all of the benchmarking was done on the same machine without any config changes taking place between tests, our benchmarks should represent as even a test base as we can expect.
Regrettably, our underpowered machine couldn’t run the benchmark with 50 concurrent tests, nor, could it run the benchmarks with the Calendar Widget enabled. In order to get apachebench to run, we had to bump the number of requests down and reduce the number of concurrent tests.
These results are from Apache without Varnish.
Server Software: Apache Server Hostname: varnish.cd34.com Server Port: 80 Document Path: / Document Length: 43903 bytes Concurrency Level: 10 Time taken for tests: 159.210 seconds Complete requests: 100 Failed requests: 0 Write errors: 0 Total transferred: 4408200 bytes HTML transferred: 4390300 bytes Requests per second: 0.63 [#/sec] (mean) Time per request: 15921.022 [ms] (mean) Time per request: 1592.102 [ms] (mean, across all concurrent requests) Transfer rate: 27.04 [Kbytes/sec] received Connection Times (ms) min mean[+/-sd] median max Connect: 0 2 7.0 0 25 Processing: 14785 15863 450.2 15841 17142 Waiting: 8209 8686 363.4 8517 9708 Total: 14785 15865 451.4 15841 17142 Percentage of the requests served within a certain time (ms) 50% 15841 66% 15975 75% 16109 80% 16153 90% 16628 95% 16836 98% 17001 99% 17142 100% 17142 (longest request)
Normally we would have run the Varnish enabled test without the Calendar Widget, but, I felt confident enough to run the test with the widget in the sidebar. Varnish was configured with a 12 hour cache (yes, I know, I’ll address that later) and the ESI Widget was loaded.
Server Software: Apache Server Hostname: varnish.cd34.com Server Port: 80 Document Path: / Document Length: 45544 bytes Concurrency Level: 50 Time taken for tests: 18.607 seconds Complete requests: 10000 Failed requests: 0 Write errors: 0 Total transferred: 457980000 bytes HTML transferred: 455440000 bytes Requests per second: 537.44 [#/sec] (mean) Time per request: 93.034 [ms] (mean) Time per request: 1.861 [ms] (mean, across all concurrent requests) Transfer rate: 24036.81 [Kbytes/sec] received Connection Times (ms) min mean[+/-sd] median max Connect: 0 0 1.8 0 42 Processing: 1 92 46.2 105 451 Waiting: 0 91 45.8 104 228 Total: 2 93 46.0 105 451 Percentage of the requests served within a certain time (ms) 50% 105 66% 117 75% 123 80% 128 90% 142 95% 155 98% 171 99% 181 100% 451 (longest request)
As you can see, even with the aging hardware, we went from .63 requests per second to 537.44 requests per second.
But, more about that 12 hour cache. The ESI Widget uses an Edge Side Include to include the sidebar into the template. Rather than just cache the entire page, we instruct Varnish to cache the page and include the sidebar. As a result, when a person surfs the site and goes from the front page to a post page, the sidebar doesn’t need to be regenerated when they go to the 2nd page. With wp-cache, it would have regenerated the sidebar Widgets and then cached the resulting page. Obviously, that 12 hour cache is going to affect the usability of the site, so, ESI widget purges the sidebar, front page and post page any time a post is updated or deleted or commented on. Voila, even with a long cache time, we are presented with a site that is dynamic and not delayed until wp-cache’s page cache expires. As this widget is a concept, I’m sure a little intelligence can be added to prevent the excessive purging in some cases, but, it does handle things reasonably well. There are some issues not currently handled with the ESI including how to handle users that are logged for comments. With some template modifications, I think those pieces can be handled with ESI to provide a lightweight method for the authentication portion.
While I have seen other sites mention Varnish and other methods to keep your wordpress installation alive in high traffic, I believe this approach is a step in the right direction. With the ESI widget, you can focus on your site, and let the server do the hard work. This methodology is based on a CMS that I have contemplated writing for many years, though, using Varnish rather than static files.
It is a concept developed in roughly four hours including the time to write the widget and do the benchmarking. It isn’t perfect, but does address the immediate needs of the one client. I think we can consider this concept a success.
If you don’t have the ability to modify your system to run Varnish, then you would be limited to running wp-cache and db-cache. If you can connect to a memcached server, you might consider running Memcached for WordPress as it will make quite a difference as well.
This blog site, http://cd34.com/blog/ is not running behind Varnish. To see the Varnish enabled site with ESI Widget, go to http://varnish.cd34.com/
Software Mentioned:
* Varnish ESI and Purge and Varnish’s suggestions for helping WordPress
* WordPress
* wp-cache
* db-cache
Sites used for reference:
* Supercharge WordPress
* SSI, Memcached and Nginx (with mentions of a Varnish/ESI configuration)
Varnish configuration used for ESI-Widget:
backend default { .host = "127.0.0.1"; .port = "81"; } sub vcl_recv { if (req.request == "PURGE") { purge("req.url == " req.url); } if (req.url ~ "\.(png|gif|jpg|ico|jpeg|swf|css|js)$") { unset req.http.cookie; } if (!(req.url ~ "wp-(login|admin)")) { unset req.http.cookie; } } sub vcl_fetch { set obj.ttl = 12h; if (req.url ~ "\.(png|gif|jpg|ico|jpeg|swf|css|js)$") { set obj.ttl = 24 h; } else { esi; /* Do ESI processing */ } }
February 24th, 2010 at 6:55 pm
Social comments and analytics for this post…
This post was mentioned on Twitter by mcd34: blog post: WordPress, Varnish and Edge Side Includes http://cli.gs/B8NVB…
February 25th, 2010 at 6:39 pm
[…] WordPress, Varnish and Edge Side Includes. Using Varnish to go from .63 requests per second to 537.44 requests per second. […]
March 5th, 2010 at 7:01 pm
[…] 0.9.9) * WP Widget Cache (version 0.25.2) * WP File Cache(version 1.2.5) * WP Varnish (in beta) * WP Varnish ESI Widget (in beta)What are we testing?* Frontpage hits * httpload through a series of urlsWe take two […]
March 17th, 2010 at 10:51 pm
where is the esi widget code?
March 23rd, 2010 at 7:11 pm
http://code.google.com/p/wordpress-varnish-esi/
The code is very rough and basic as it was written to handle a particular client’s issue. I thought I would have time to turn it into a more full-featured addon, but, with the fact that Varnish doesn’t handle compression of ESI composed pages made this addon less useful.
http://wordpress.org/extend/plugins/wordpress-varnish/
Is another plugin that uses some of the theory behind this, but, doesn’t do fragment caching. It does purge the Varnish server when new posts are made which makes it much more responsive than most of the other caching plugins.
November 4th, 2010 at 7:16 am
[…] WordPress, Varnish and Edge Side Includes […]
March 14th, 2011 at 10:03 am
[…] WP Varnish ESI Widget (in beta) […]
June 5th, 2011 at 10:28 pm
[…] WP-Varnish * W3 Total Cache * WP Super Cache * WordPress-Varnish-ESI * and many […]