Recently, I was forced to look into PHP caching, thanks to a series of events. Here’s what happened:
Once upon a fine day, the (windows) server demanded that I will install some windows updates it found and I proceeded. After the restart though, I noticed that all of our PHP sites were timing out. It didn’t take long to find out that the one to blame was MySQL which due to the massive amount of files in the temporary folder, apparently needed a very long time to create its temp files. In the end, I found out the cause and boy, was it ugly or what?
It seemed PHP was creating session files on the Windows’ temporary folder but wouldn’t delete them after the session had expired. As a result, the amount of files in the folder reached the 1.000.000 (!), all of which were 0 or 32KB long, but nevertheless enough to make anything that tried to access the folder crawl like it never crawled before (such as MySQL).
This ugly event must never happen again and I need to ensure that. So, I took a deep breath and began digging into php.ini. It seemed that the following piece of advice was giving me a hint:
|; If you are using the subdirectory option for storing session files
; (see session.save_path above), then garbage collection does *not*
; happen automatically. You will need to do your own garbage
; collection through a shell script, cron entry, or some other method.
Still, that wouldn’t explain why such an option was set, since I neved touched the session section of the php.ini, ever. I refuse to believe that by default php is designed to kill its hosting server (I may be wrong though, feel free to correct me). I took a quick look into the caching option I actually used, which was provided by the session.save_handler setting, which was set to files. Searching around for different options I could try, I found out about WinCache.
WinCache, or Windows Cache Extension for PHP is PHP accelerator in the form of a PHP extension which is designed to work in Windows, but strangely enough, not limited to IIS (although being developed by Microsoft). WinCache though is not just an opcode cache but also offers some other nifty features, such as file caching, user data storage (more on that later) and session storage. Also, tests conducted by other people seem to indicate that it does a pretty great job, even when compared to other solutions. Plus, it won’t mind if you’re having 1 or 16 PHP FastCGI processes, since the cache it creates is centralised, thus avoiding data duplication!
Now go to RuslanY’s (WinCache’s creator) blog and thank him for his great work!
I have to admit though that initially, I was sceptical about WinCache. I had tried WinCache as a beta and it had some serious bugs which made some of our PHP sites to unexpectedly crash and as such, I had removed it completely. Not so with version 1.2.614, a development release that gave me no problem. After verifying that all of our PHP sites would work without issue when this extension was enabled, I was curious to find out more about its session handling option. So, I changed PHP’s temporary folder to one that would be untainted by other processes, changed the session.save_handler option to wincache and went on an angry rampage using openload, which hammered my servers with requests, the best part of which was that it would create a plethora of new sesions. Let me tell you, I was relieved to see that WinCache only creates a single file for handling PHP’s sessions.
So, I had certainly solved that annoying problem, but now WinCache had proven to be interesting and had to find out more about it. After all, I was sold on this nice live stats page that showed the status of WinCache. The best part was already set and forget. Opcode caching and File caching were applied automatically to all of our PHP applications and required no tweaking from my part. The same goes for session caching (although in this picture the hit/miss ratio is low due to using openload which creates lots of new sessions). What peaked my interest was the User Cache session which initially was 0. I found out eventually that user cache is nothing more than an object storage provided by WinCache and which is accessible using the WinCache provided functions.
“Big deal” I thought. “We’re talking about PHP on Windows, who would bother to lock-in its application to Windows and WinCache?”. Although the question is valid, the answer was far simpler: “Caching plugins!” Indeed, applications that up until now used the disk to act as a cache, could benefit greatly from WinCache if they used it instead for that same purpose. First case in point? WordPress, a platform which is favoured by many of our clients.
Let’s take a specific example, extrahype.com. Extrahype is a heavily modified site based on WordPress for all things geek, be it movies, games, pop culture and more and we’re lucky enough to be hosting it. Using my crude benchmark tool, openload, like a an angry caveman wielding an obsidian knife, I went and performed a simple benchmark of its start page, without using WinCache or any form of caching within the site itself. The results on the 2xDual Opteron 2212 were a bit disappointing, even though this was PHP on IIS on a not that fast server processor. I only got about 14 requests/second, which is low. In contrast, our in-house developed site rekonit.com an online review aggregator for stuff that matter (such as cars, movies and games, you know, the geek stuff you see on extrahype.com) which uses ASP.net 4.0 & MSSQL server and self designed caching algorithms gets about 350 rps.
I then proceeded and enabled WinCache in the php.ini. The results were very positive. With WinCache enabled, the average requests/second extrahype.com could handle rose to 31 rps, a difference of more than 2 times! That was good, but still not good enough.
Here’s where WinCache’s user cache came into play in the form of WordPress plugin, WinCache object cache. After I installed this, I could see the user cache section in the WinCache statistics page populated with data. Did it make any difference? Well, a minimal one. The average rps climbed from 31 to 34 rps.
The differences were reasonable and it’s obvious here that the bottleneck appears to be the database server. Still, it provided my PHP websites with a nice performance boost that only costs a little RAM (thankfully, that’s in abudance).
I then proceeded on testing it with PHPBB 3. Using the same method of “before and after WinCache” I would end up with 18 and 30 rps respectively. PHPBB 3 uses a file caching mechanism as its default. I found an experimental WinCache implementation which, usurprisingly, uses WinCache’s user cache. The difference was still minimal, this time from 30 to 33 fps, but still a no brainer compared to other forms of caching. Note that the problem here were the serialized database accesses, since the PHP FastCGI processes were are at a cosy 10% of CPU usage while MySQL was struggling around 25-30% (a whole core in a quad core system, plus a little more).
As a comparison, a PHPBB 3 installation on my development machine (an Intel Q9550 quad core) achieves about 59 rps without WinCache and 82 rps with it enabled, with the same database bottleneck issues.
In the end, WinCache (as other PHP accelerators do) offers an almost free performance upgrade to your PHP applications. Its extra features such as file, user and session caching & handling are indispensible even if you don’t have that much RAM to spare. It’s highly recommended and if you run PHP on Windows you should definitely check it out!
PS. Before closing this article, I’d like to present you with two additional plugins for WordPress that managed to blow my mind with their results:
I’ve also tested W3 total cache for WordPress, a highly sophisticated caching system which is designed to take advantage of any caching option you can think of, such as opcode and file caching, page minifying even CDNs and some additional features that didn’t have the change to use (yet). The improvement is simply spectacular, since with it enabled plus setting it to take advantage of winCache’s features (and WinCache obviously active) the requests/second I achieved for extrahype.com exceeded the 300 rps mark! This result is achieved mostly by creating static versions of components whenever it can.
If your site however does not need a complex caching mechanism nor is very complex in nature (extrahype.com for example is not as complex as it might seem) you can opt for another caching plugin, simply named Quick Cache. Quick Cache is essentially a very simple caching plugin that creates static versions of your site’s pages. It doesn’t have the sophistication level W3TC has and only few differentiation methods of the page are recognised, but if your site’s user base consists mostly of unregistered visitors or you want to deal with a sudden Reddit/Digg/SU/Slashdot attack, it will do a magnificent job. How magnificent? Well, with Quick Cache enabled in extrahype and WinCache enabled in the server, I managed to get 610 rps! This amazing number is due to the web server only serving static content, with minimal PHP processing.
Since most of our WordPress clients don’t really need (yet) the sophisticated features of W3TC and they mostly have unregistered users, we’ve decided to enable Quick Cache and migrate to W3TC when required. No matter what you select though, make sure the plugins have the required permissions on the cache folders they create, or you may end up with a bazillion of cache files!