Skip to end of metadata
Go to start of metadata
You are viewing an old version of this page. View the current version. Compare with Current  |   View Page History

<ac:macro ac:name="unmigrated-inline-wiki-markup"><ac:plain-text-body><![CDATA[

Zend Framework: Zend_Cache_Backend_Static Component Proposal

Proposed Component Name Zend_Cache_Backend_Static
Developer Notes
Proposers Pádraic Brady
Revision 0.9 - 21 January 2009 (wiki revision: 3)

Table of Contents

1. Overview

Zend_Cache_Backend_Static aims to provide a backend for caching full pages as static files within the public directory, or any public subdirectory. The advantage of this caching strategy is that it allows the HTTP server to directly serve static HTML (and other) files without involving PHP allowing for impressive increases in throughput and responsiveness compared to dynamic pages requiring PHP. The caching mechanism will support tagging in full ensuring that expiry and invalidation management is simplified.

This proposal is dependent on some Zend_Cache refactoring to be proposed in a related proposal.

2. References

3. Component Requirements, Constraints, and Acceptance Criteria

  • MUST implement a fully funtional and configurable static file cache
  • MUST provide sufficient features to be easily adapted at a high level by a Cache Manager (see related proposal)
  • WILL require some refactoring of Zend_Cache_Core to remove restrictions on ID validity and proxy non-Interface methods back to the Backend objects. By removal, it means allowing Backends to set their own validity rules to replace any defaults set by Zend_Cache_Core.

4. Dependencies on Other Framework Components

Zend Framework Classes

  • Zend_Cache_Core
  • Zend_Exception
  • Zend_Cache_Backend_Interface

Additionally Proposed Classes

  • Zend_Cache_Frontend_Capture

5. Theory of Operation

The Zend Framework currently offers a frontend called Zend_Cache_Frontend_Page which caches the output from an entire document into memory or file based caches. This cache is then tested for the next request, and if a hit is detected, the page is loaded from the cache and served. However, while it is many times faster than dynamically generating pages (by a factor of 9-10 on my VPS), it suffers from one problem - it still needs Zend_Cache and PHP. This limits the speed of even the best cached page using Zend_Cache_Frontend_Page since Apache and PHP remains one of the slowest ways of delivering responses.

The fastest way of delivering pages is to save them as ordinary HTML files any HTTP server can serve. The only limit then is your HTTP server's maximum throughput for any given user concurrency level. Apache generally has a consistent performance when optimised, but some popular lightweight HTTP servers like nginx or lighttpd can outpace Apache quite easily in many circumstances, making static files a valuable option when optimising applications on minimally scaled hardware.

A Static File Cache is generated at the exact same level as Zend_Cache_Frontend_Page. The difference is that it caches pages into files within a public directory of the application with a valid file extension (e.g. .html) and content. On any given request, the HTTP server can skip PHP, and serve this file directly.

In many circumstances, this is a lot faster than the current style of Page caching. A simple benchmark on my VPS using a single PHP echo statment vs a static HTML file showed a throughput increase of around 600% (711.20 vs 4,208.52 requests/sec). It also eliminates the need for using a caching proxy like Squid which has traditionally performed a similar role for applications without static file caching implemented natively, and enhances the benefit of using a low memory/CPU reverse proxy HTTP server (like nginx or lighttpd) to serve static content instead of Apache. This is particularly true on small single servers where Squid is overkill, and even more powerful servers where the reverse proxy HTTP server can overtake Apache with ease.

In the proposed caching system, there will also be a tagging mechanism supported using a backend cache (to maintain the tag data - a cache within a cache) which would make invalidating such statically cached files a lot easier to perform than relying on manual expire instructions (which are near impossible to track in large applications). I would hope forward a Zend_Cache_Backend_Db proposal for a database backed cache (not simply for tag caching) as a separate proposal.

An example:

Assuming Static Caching is enabled for the URI:


This would be cached to file as:


To keep the public directory free, 2.html is actually saved to /public/static/2.html (a default convention). Since this obviously does not match the preserved route, the incoming REQUEST_URI is rewritten onto this file location using the following Rewrite Rule additions:

RewriteEngine On

RewriteRule ^/(.*)/$ /$1 [QSA]
RewriteRule ^$ static/index.html [QSA]
RewriteRule ^([^.]+)/$ static/$1.html [QSA]
RewriteRule ^([^.]+)$ static/$1.html [QSA]

RewriteCond %{REQUEST_FILENAME} -s [OR]
RewriteCond %{REQUEST_FILENAME} -l [OR]
RewriteCond %{REQUEST_FILENAME} -d 

RewriteRule ^.*$ - [NC,L]
RewriteRule ^.*$ /index.php [NC,L]

A few of these rules handle cases where the URI has a trailing forward-slash, or where the request is for the root URI (i.e. the index file should be served). The rest are the usual recommended rules for Zend Framework applications preceded by rules to rewrite requests initially to the static cache.

Unfortunately, offering this backend requires either changes to Zend_Cache (backend parameter validation is performed by private static methods in Zend_Cache_Core) or workarounds to bypass the validation altogether. This is necessary because the static file cache's ID is its path in the filesystem and not an alphanumeric key. Also the backend class itself could offer additional methods (largely used by the integrated helpers/plugins discussed in a related proposal) which are otherwise blocked.

The static cache also may require a new Frontend which can capture output without relying on a pre-set ID (see Zend_Cache_Frontend_Output), as the ID is assigned dynamically based on the value of $_SERVER['REQUEST_URI'] but this is a relatively minor change.

It is anticipated that static file caching will be of use in small to medium applications on limited hardware in a minimally scaled system. Obviously, the static cache method is not very scalable since it's bound to a filesystem rather than shared memory. There are shared memory equivalents using memcached (and a memcached aware HTTP server), but that would another proposal (which we can live without for now I bet ).

6. Milestones / Tasks

  • Milestone 1: Complete any relevant Zend_Cache refactoring to support more dynamic self-validating backends
  • Milestone 2: Complete final feature list incorporating feedback
  • Milestone 3: Verify operation using Unit Tests
  • Milestone 4: Documentation

7. Class Index

  • Zend_Cache_Frontend_Capture
  • Zend_Cache_Backend_Static

8. Use Cases

Note: All use cases have the same problem - the cache ID is actually the REQUEST URI of the current request and this can easily contain characters forbidden by Zend_Cache_Core's private static validation methods. I have omitted any workaround on the assumption this restriction can be lifted.

UC-01: Starting/Ending Cache
UC-02: Deleting Caches based on $_SERVER['REQUEST_URI']
UC-03: Assigning Tags
UC-04: Deleting static files by tag

9. Class Skeletons

Class skeletons will follow the current Zend_Cache_Backend_Interface but also include additional methods to be determined including a "removeRecursive" command to clean entire directories of the cache. Extra methods do not break the interface, but they are blocked by Zend_Cache_Core counting as one of the restrictions to be addressed and removed for any new non-alphanumeric-ID caches to be possible.

Please refer to the referenced blog posts for a prototype implementation demonstrating how Zend_Cache_Backend_Static would, in theory, operate.


Enter labels to add to this page:
Please wait 
Looking for a label? Just start typing.