I've been trying to improve the performance of my teeny-tiny virtual web server host running Drupal. It's been an interesting process, as serving a moderate-volume site within a 128MB virtual Linux host is, shall we say, constraining :)
Up until recently, one of the web sites I run has been served purely with static pages. That worked well for some years, but they've noticed a large drop-off in traffic over the years, as more community-based sites with a similar focus have cropped up, and their static HTML, no-forums, minimalistic approach wasn't serving very well.
They enlisted my help for a site make-over, with a focus on improving usability and giving them the ability to have members of their organization participate in submitting articles and comments to the web site. Since it's not entirely ready yet, I'm not at liberty to share the URL, but along the way I discovered some excellent tips to increase performance.
One of my main concerns is that I've run a web site that has been DOS'd before (http://barnson.org/). This runs Drupal, and although it held up reasonably well under the strain, even with the cache module enabled, page return performance wasn't as good as it should have been under load. I began looking for an ultimate-performance-for-minimum-memory option, and think I found it in the combination of Drupal, jpcache, and turck-mmcache.
Configuration of Turck-mmcache, which caches the compiled PHP code from a site, has already been covered in the Administration Guide for Drupal, so I won't touch on it here. But after much searching, I haven't found anyone else who's used jpcache in conjunction with Drupal.
To use jpcache requires a single hack in index.php. Part of my reason for posting this is to find out if anyone in the community sees a reason that this shouldn't work.
Unfortunately, I don't have a very good comparison numbers-wise vs. Drupal's built in SQL-based page cache mechanism. I'll have to try a more rigorous test here soon. For the time being, though, it seems that with very few modules, performance gains are minimal, since there are, apparently, still several database calls involved to create the $user object. Performance gains seem fairly substantial when many modules are loaded.
My goal is only to cache pages on the filesystem for anonymous users (like a DOS or a Slashdotting), not for any registered users. Here's the code. Only the section from //Begin through // End are the modified code; I included the include_once and drupal_page_header(); lines to give context for index.php.
include_once 'includes/bootstrap.inc';
// Begin jpcache check and page cache for anon users
global $user;
if (!$user->uid) {
$cachetimeout=900;
require "/var/www/drupal/jpcache/jpcache.php";
}
// End jpcache check and page cache for anon users
drupal_page_header();
As far as I can tell in parsing bootstrap.inc and the includes at the bottom of it, Drupal seems to make a database connection at database.inc (thus if mysql is down or oversaturated, you still can't get to cached pages, which partially defeats the purpose.), so using jpcache isn't yet as useful as it could be. However, it seems to speed things up a good deal.
Anyway, here are the raw numbers. I simply pulled down index.php, no options. For the first run, I had the jpcache hack in. For the second run, I removed the hack entirely. For the third run, I enabled Drupal's caching, logged out, and refreshed the index page to make sure it was cached before starting the run.
All runs of apachebench are "ab -n 100 -c 10 http://hostname/drupal/".
We have three configs: JPCACHE, NOCACHE, and DRUPALCACHE. The results are interesting, but not necessarily compelling.
JPCACHE configuration:
Time taken for tests: 5.494 seconds
Complete requests: 100
Failed requests: 0
Broken pipe errors: 0
Total transferred: 680000 bytes
HTML transferred: 638300 bytes
Requests per second: 18.20 [#/sec] (mean)
Time per request: 549.40 [ms] (mean)
Time per request: 54.94 [ms] (mean, across all concurrent requests)
Transfer rate: 123.77 [Kbytes/sec] received
Connnection Times (ms)
min mean[+/-sd] median max
Connect: 0 0 0.2 0 2
Processing: 45 508 288.8 464 2053
Waiting: 45 508 288.7 463 2052
Total: 45 509 288.6 466 2053
Percentage of the requests served within a certain time (ms)
50% 466
66% 567
75% 647
80% 687
90% 896
95% 1006
98% 1213
99% 2053
100% 2053 (last request)NOCACHE configuration:
Time taken for tests: 13.291 seconds
Complete requests: 100
Failed requests: 0
Broken pipe errors: 0
Total transferred: 675700 bytes
HTML transferred: 638300 bytes
Requests per second: 7.52 [#/sec] (mean)
Time per request: 1329.10 [ms] (mean)
Time per request: 132.91 [ms] (mean, across all concurrent requests)
Transfer rate: 50.84 [Kbytes/sec] received
Connnection Times (ms)
min mean[+/-sd] median max
Connect: 0 0 0.0 0 1
Processing: 291 1268 468.5 1247 2651
Waiting: 291 1267 468.5 1247 2650
Total: 291 1268 468.5 1247 2651
ERROR: The median and mean for the initial connection time are more than twice the standard
deviation apart. These results are NOT reliable.
Percentage of the requests served within a certain time (ms)
50% 1247
66% 1410
75% 1530
80% 1597
90% 1899
95% 2085
98% 2599
99% 2651
100% 2651 (last request)DRUPALCACHE configuration:
Time taken for tests: 5.300 seconds
Complete requests: 100
Failed requests: 0
Broken pipe errors: 0
Total transferred: 684500 bytes
HTML transferred: 638300 bytes
Requests per second: 18.87 [#/sec] (mean)
Time per request: 530.00 [ms] (mean)
Time per request: 53.00 [ms] (mean, across all concurrent requests)
Transfer rate: 129.15 [Kbytes/sec] received
Connnection Times (ms)
min mean[+/-sd] median max
Connect: 0 0 0.0 0 1
Processing: 48 487 301.2 422 1407
Waiting: 47 486 301.1 422 1407
Total: 48 487 301.2 422 1407
ERROR: The median and mean for the initial connection time are more than twice the standard
deviation apart. These results are NOT reliable.
Percentage of the requests served within a certain time (ms)
50% 422
66% 569
75% 622
80% 718
90% 981
95% 1127
98% 1283
99% 1407
100% 1407 (last request)DRUPALCACHE + JPCACHE (Drupal Cache enabled, jpcache hack enabled):
Time taken for tests: 4.861 seconds
Complete requests: 100
Failed requests: 0
Broken pipe errors: 0
Total transferred: 680000 bytes
HTML transferred: 638300 bytes
Requests per second: 20.57 [#/sec] (mean)
Time per request: 486.10 [ms] (mean)
Time per request: 48.61 [ms] (mean, across all concurrent requests)
Transfer rate: 139.89 [Kbytes/sec] received
Connnection Times (ms)
min mean[+/-sd] median max
Connect: 0 0 0.0 0 1
Processing: 58 458 270.0 385 1720
Waiting: 58 458 269.9 385 1719
Total: 58 458 270.0 385 1720
ERROR: The median and mean for the initial connection time are more than twice the standard
deviation apart. These results are NOT reliable.
Percentage of the requests served within a certain time (ms)
50% 385
66% 533
75% 632
80% 653
90% 760
95% 906
98% 1306
99% 1720
100% 1720 (last request)Unfortunately, my test bed for these tests is a shared server, of a load that's totally unpredictable. However, looking at the 90th percentile results, it appears that jpcache and drupal's caching are nearly a statistical dead-heat independently. When used together, they appear to have some additional small benefits.
My hope for the future:
* I'd like to have a page-caching system that can continue to serve static pages even if MySQL is unresponsive
* I want to see if I can do a better check than "if (!$user->uid)" to determine if there is a user logged in to determine if we want to use jpcache or not. I think I should be able to do this with a session cookie check, instead; if the user has a valid session cookie for a registered user at all, don't jpcache. If their cookie is as an anonymous user, then let jpcache do its thing.
* I should probably revise my testing method. My apache processes, due to the memory constraints, only serve 20 pages before they kill themselves off, and I start with only 3 minspareservers, 5 maxspareservers. So it's very likely that these statistics indicate much less a performance difference in caching methods, but instead probably random variations in process spawning times. However, the difference between having no cache enabled, and having either jpcache or drupal's caching enabled, seemed fairly consistent with this test run a good representative sample.
I'll let you know my progress. It may turn out to be less of a problem if I were to just use a Squid front-end cache instead of jpcache or something, since memory is the real limitation on this tiny server.
Comments
Thanks for posting all of this!
Surprised that you didn't get any response or comments on it.
Any progress on this since this first posting?
Mike
--
Mike Gifford, OpenConcept Consulting
Free Software for Social Change -> http://www.openconcept.ca
good subject
I'm currently trying to figure out what options, if any, there are for taking down the db server and still being able to serve up (anonymous user style) content.
I used movabletype for many years. Initially, it was not a DB driven application. Pages were generated and stored when content was submitted. While this method is obviously no good for dynamic content, it's perfect for serving pages to the majority of users (who are anonymous).
It is occassionally necessary to take down a DB. It would be wonderful if Drupal had a way of limping along with filesystem cache rather than just serving up a single 'site down for maintainence' page (as in 4.7).
--
Drupal tips, tricks and services
http://devbee.com/ - Effective Drupal
--
Devbee - http://devbee.net/
static page caching
There have been a few discussions on this subject on the dev mailing list (I'm not activily following this at the moment, but have been alerted to them).
The problem has been largely that of having significant interest in the community I think. Especially with sites with lots of modules it is nice to be able to cache the content for anonymous users.
There are a number of ideas that have been floated about. Just need to get the critical mass of users together to develop, test & evaluate a solution for static caching.
Mike
--
Mike Gifford, OpenConcept Consulting
Free Software for Social Change -> http://openconcept.ca
http://learningpartnership.org http://open.bellanet.org/wufphotocomp/
http://ox.ca http://poped.org http://openoffice.ca
Problem with zipped data
Hi, I tried setting this up exactly as you described, but get garbled data sent back to my browser from the cache. I'm on Drupal 4.6.6. It looks like the output from drupal is already zipped, and when served up again does not get the appropriate headers added. I tried various settings in jpcache (v2 btw, zip on and off) but this does not resolve the issue. Did you make any changes to get this to work?
-- John
Hello there. Judging by this
Hello there.
Judging by this benchmark test, jpcache significantly outperforms Zend Optimizer and such. In my mind, this makes jpcache the choice for improving php performance.
What is the status of using jpcache and Drupal? Is it compatible? Any issues?