One of the most utilized but least understood features of Magento Enterprise edition is the Enterprise_PageCache module, better known to most as Full Page Cache. Aside from Vinai’s very informative summary of the essentials of full page cache, I have yet to come across a solid developer resource for Full Page Cache, so I figure I ought to write one. In this overview, I will touch on some of the subjects explored above, and share some of my own experience with the module as well.
What is Full Page Cache and Why Does Magento Use It?
The answer the the first question is, quite literally, full page caching involves the storage of the full output of a page in a cache, so that in subsequent page loads will not require much server load at all. For high-traffic Enterprise level eCommerce sites, full page caching is imperative in order to keep server load as low as possible, and to avoid downtime during periods of high traffic. If 1000 users are visiting the site at the same time, the amount of memory used with full page caching enabled is inconsequential when compared to the server load caused by 1000 users instantiating Magento at once. Additionally, full page caching will increase site speed, since all the server needs to do is fetch and render the page from the cache. So, now that we’ve established that full page caching (henceforth FPC) is important, let’s evaluate it critically.
Are There Any Drawbacks to Using Full Page Cache?
Good question, and the answer is an unequivocal yes. What happens if two users are on the homepage, but one is logged in, and the other is not. Or one has three items in the cart, and the other has one? How can we serve up a dynamic website if the entire page is cached statically? These issues are very important to consider, and that’s where Enterprise_PageCache comes in. By utilizing placeholders and containers (objects I will further explain later), the full page cache can process whichever blocks need to be dynamically loaded, such as mini-cart content or account links, and append it to the rest of the page that is fetched from the cache. This process ensures a (usually) happy medium between optimized site speed, minimized server load, and a dynamic, personalized, webstore.
This is the fun part, and the easiest place to get lost, so I will try to spell out the caching process as clearly as possible.
Finding the Subprocessor:
When Mage_Core_Model_App::run() is called, it first checks to see if the cache (Mage_Core_Model_Cache) can processRequest(), which in turn will call Enterprise_PageCache_Model_Processor::extractContent() to try to grab the full page from the cache. After checking to see if the cache is enabled, extractContent will check to see if there are any subprocessors available to handle the request. The subprocessors are page-specific cache processors, each with custom functionality for manipulating the cache. Enterprise_PageCache includes subprocessors only for CMS pages (including the homepage), Category View, Product View and 404 pages. This makes sense, considering there is no reason at all to fully cache the content of user-specific pages such as cart, checkout, or account pages. If a subprocessor is available for the page request, then Enterprise_PageCache_Model_Processor::_processContent() is called to determine which blocks are within cache, and which blocks need to be fully processed.
Placeholders and Containers:
FPC placeholders are defined in a cache.xml file (the default placeholders are defined in Enterprse_PageCache’s config, but you can also add your own), and have four attributes. Block, Placeholder, Container, and Lifetime. The following is the process with which these placeholders interact with FPC.
When an FPC page is built in Magento, each block defined within a placeholder node in the config is rendered with a placeholder comment before and after. This is a comment to denote where each placeholder block begins and ends, and is composed of a human readable comment, fetched from the placeholder attribute, and a cache_id. An example is the global messages placeholder comment shown below.
Each one of these placeholders’ container classes (an instance or extension from Enterprise_PageCache_Model_Container_Abstract) is investigated by Enterprise_PageCache_Model_Processor::_processContent() to determine if its block can be applied without instantiating the Magento Application in a function aptly called applyWithoutApp(). This function will generally just check to see if that block can be loaded from the current cache. If so, the block is fetched from the cache and the cache processor moves onto the next placeholder.
Each container whose applyWithoutApp() function returns false, which will happen whenever the placeholders’ lifetime attribute expires or the block is not found in cache, will be registered in the ‘cached_page_containers’ array in the Mage registry.
If every container can be rendered using applyWithoutApp(), then the entire page is returned as $content back to Mage_Core_Model_Cache::processRequest(), and the page will be rendered without instantiating Magento.
If, however, there are containers registered in ‘cached_page_containers’, then the request will be rerouted to the Enterprise_PageCache_ActionController::processAction(), where each container will be applied inside the application using its aptly named, applyInApp() function. While this is obviously slower than having all blocks in cache and fully rendering the page, some content simply cannot be cached, such as session-specific notices or shopping carts.
Optimize with FPC
Optimize the performance of your Magento Enterprise site with full page caching! Despite being one of the most utilized features of Magento Enterprise, there’s very little understood about it, so ensure you do your research to determine whether FPC will be beneficial to your site. As I explained above FPC becomes a necessity if your online store experiences regular instances of high-traffic. Streamline the user experience and reduce server response time by implementing full page caching. Comment below if you have any questionx about what I’ve outlined in this post!