Author Archive

This is part two in my caching series. Part one covered the concept behind the full page caching as well as potential problems to keep in mind. This part will focus on implementing the concept in actual PHP code. By the end of this you’ll have a working implementation that can cache full pages and invalidate them intelligently when an update happens.

Requirements

I’ll provide a fully functional framework with the simple application I used to get my benchmark figures. You’ll need the following software to be able to run it.

  • Nginx. I’m not sure which exact version but I generally use and recommend the latest development version.
  • PHP 5.3.0. I recommend at least 5.3.3 so you’ll have PHP-FPM for your fastcgi process management.
  • MySQL
  • Memcached

The Framework

You can download the framework here: Evil Genius Framework. I’ll be referencing code in the files instead of pasting it in this post to keep the size down, so you will probably want to download it.

Read More »

flattr this!

Recently a post of mine was linked on yCombinator and for some reason a lot of the comments talked about the efficiency of WordPress. While it’s technically not related to the subject of the linked post I just want to point out that the performance of WordPress is pretty horrible regardless of whether you use Apache or Nginx.

My friend Karl Blessing and I recently talked about WordPress caching plugins. He uses WP SuperCache and I use W3 Total Cache and he subsequently decided to do some WordPress caching benchmarks on the different methods. He’s done an awesome job and generated some pretty graphs for you to look at.

What I took away from the whole thing is that W3 Total Cache and WP SuperCache can offer similar performance if you’re willing to do static file caching, however, W3 Total Cache can offer a cleaner solution with caching in Memcached if you’re willing to sacrifice a bit of performance. The benefit to this, and why I use this method, is that you don’t need complicated rules in your Nginx (or Apache) configuration files.

flattr this!

The Big Picture

So you’ve finally decided to make the switch from Apache to Nginx. You most likely did this for performance reasons; perhaps all those blogs have been writing about how fast Nginx is or perhaps your webmaster friends have been raving about how they can now handle a lot more traffic without spending money on hardware.

This is usually all true, but why exactly is Nginx so much faster than the typical Apache setup of the prefork MPM and mod_php? The technical explanation is that Nginx is a non-blocking event based architecture while Apache is a blocking process based architecture. To simplify it heavily the theory is like this:

Apache Prefork Processes:

  • Receive PHP request, send it to a process.
  • Process receives the request and pass it to PHP.
  • Receive an image request, see process is busy.
  • Process finishes PHP request, returns output.
  • Process gets image requests and returns the image.

While the process is handling the request it is not capable of serving another request, this means the amount of requests you can do simultaneously is directly proportional to the amount of processes you have running. Now, if a process took up just a small bit of memory that would not be too big of an issue as you could run a lot of processes. However, the way a typical Apache + PHP setup has the PHP binary embedded directly into the Apache processes. This means Apache can talk to PHP incredibly fast and without much overhead, but it also means that the Apache process is going to be 25-50MB in size. Not just for requests for PHP requests, but also for all static file requests. This is because the processes keep PHP embedded at all times due to cost of spawning new processes. This effectively means you will be limited by the amount of memory you have as you can only run a small amount of processes and a lot of image requests can quickly make you hit your maximum amount of processes.

Compare this to the Nginx event based method.

Read More »

flattr this!

“No input file specified” is one of the most frequently encountered issues in Nginx. People on serverfault and in the #nginx IRC channel asks for help with this so often that this post is mostly to allow me to be lazy and not have to type up the same answer every time.

This is actually an error from PHP and due to display_errors being 0 people will often just get a blank page with no output. In a typical setup PHP will then send the error to stderr or stdout and Nginx will pick up on it and log it in the Nginx error log file. Thus people spend a ton of time trying to figure out why Nginx isn’t working.

The root cause of the error is that PHP cannot find the file Nginx is telling it to look for, and there are two common cases that causes this. Either you’re not giving PHP the right path to the file or your file permissions are incorrect.

Wrong Path Sent to PHP

The most common reason at the time of writing happens because a user uses a horrible tutorial found via google instead of actually understanding Nginx. Reading my primer will equip you to actually solve this on your own but since this post is actually dedicated to the error I’ll cheat this once and allow you to be lazy by just giving you the full solution.

Nginx tells PHP about the file to execute via the SCRIPT_FILENAME fastcgi_param value. Most examples in the wiki should define this as $document_root$fastcgi_script_name. The horrible tutorials will often hardcode the path value but this is not desirable as we don’t want to duplicate information and invite future screw ups. So you’ve gone with the $document_root$fastcgi_script_name option and suddenly it’s no longer working.

This happens because Nginx has 3 levels of inheritance commonly referred to as blocks, these being http, server and location, each being a sub-block of the parent. Directives in nginx inherit downwards but never up or across, so if you define something in one location block it will never be applied in any other location block under any circumstance.

Typically users define their index and root directive in location / because a tutorial told them to. So when they then define SCRIPT_FILENAME using $document_root the root directive is not actually defined and thus the SCRIPT_FILENAME value becomes just the URI making PHP look at the root server dir.

The simple solution here is to just define the directive in your server block. (or http block even!) Generally the higher up your can define a directive the less duplicate directives you’ll need.

Incorrect File Permissions

Most people don’t really believe me when I tell them their file permissions are incorrect. They’re looking at the damn permissions and the PHP user can read the file just fine! Sadly, this shows a lack of understanding of Unix user permissions. Being able to read a file is not enough, a user must also be able to traverse to the file.

This effectively means that not only should the file have read permission, but the entire directory structure should have execute permission so that the PHP user can traverse the path. An example of this:

Say you have an index.php file in /var/www. /var/www/index.php must have read permission and both /var and /var/www must have execute permissions!

If you’ve corrected both things and still have this issue then please put a comment so I can look into it, as far as I know there should be no other reasonsĀ  for this error.

flattr this!

Edit: Part 2 is now available.

This is the first entry in a short series I’ll do on caching in PHP. During this series I’ll explore some of the options that exist when caching PHP code and provide a unique (I think) solution that I feel works well to gain high performance without sacrificing real-time data.

Caching in PHP is usually done on a per-object basis, people will cache a query or some CPU intensive calculations to prevent redoing these CPU intensive operations. This can get you a long way. I have an old site which uses this method and gets 105 requests per second on really old hardware.

An alternative that is used, for example in the Super Cache WordPress plug-in, is to cache the full-page data. This essentially mean that you create a page only once. This introduces the problem of stale data which people usually solve by checking whether data is still valid or by using a TTL caching mechanism and accepting stale data.

The method I propose is a spin on full-page caching. I’m a big fan of Nginx and I tend to use it to solve a lot of my problems, this case is no exception. Nginx has a built-in Memcached module, with this we can store a page in Memcached and have Nginx serve it – thus never touching PHP at all. This essentially turns this:

Read More »

flattr this!

PHP Namespace Mindfuck

I love PHP, I really do… just, sometimes you fall into these situations where you just can’t help bang your head against the table until even the table starts bleeding.

I’m currently working on a blog series about intelligent caching with PHP. During the preparation for this I had to create a framework which could demonstrate the caching concepts I was going to be discussing, and what better than using namespaces for it. Namespaces makes auto loading easy as pie, they give you an incredible freedom in class naming due to the extra encapsulation layer and in general they’re just brilliant. Except for this.

$controller = '\Evil\Controller\News';
$data = $controller::dataKeyInvalidates($invalidate);

It simply wouldn’t work. I was certain it worked fine without namespaces so for kicks I tried this:

$data = \Evil\Controller\News::dataKeyInvalidates($invalidate);

And it ran without problems. Fun. What eventually let me figure out what was going on was by echoing the variable passed to my auto loader. When I used a variable to reference the class name the auto loader was passed “\Evil\Controller\News” when I didn’t the auto loader was passed “Evil\Controller\News”. Obviously some magic goes on inside PHP that translates \Evil\Controller\News into “Evil\Controller\News”. Setting $controller to ‘Evil\Controller\News’ made it work perfectly fine.

So to recap this:

// Does not work.
$controller = '\Evil\Controller\News';
$data = $controller::dataKeyInvalidates($invalidate); 
 
// Works.
$controller = 'Evil\Controller\News';
$data = $controller::dataKeyInvalidates($invalidate); 
 
// Works.
$data = \Evil\Controller\News::dataKeyInvalidates($invalidate); 
 
// Does not work.
$data = Evil\Controller\News::dataKeyInvalidates($invalidate);

flattr this!