How to (properly) harness the Magento EE Full Page Cache (part 2 – application states)

In part 1 we took a look at some of the basics of the Magento EE FPC.  We talked about the general structure and flow of the FPC.

In this article we are going to take a look at the application states in the FPC.  I don’t know if “states” is the official term, but it’s what I use to describe what they are.

There are, essentially, 4 states that the Full Page Cache can be in.

Completely cached

All containers can be rendered by the processor.  This is the best state to be in.  No DB connection is required.  The only infrastructure required is a connection to the cache.  Each container will pull content from the cache and inject its content in the page.

Completely cached, with processing

Most containers will pull their content from the cache, but some might render content inline.  This is done by overriding the applyWithoutApp() method.  We’ll take a look at this in a future article

Partially bootstrapped without blocks

The state is when the applyWithoutApp() method returns false.  The FPC will first call that method.  If ANY of the containers returns false the FPC will render as many containers as it can but keep track of the ones that did not.  Then it will bootstrap the request and forward it to the FPC’s request controller called Enterprise_PageCache_RequestController.  The processAction() method call each un-processed container’s applyWithinApp() method.

Partially bootstrapped without layout

The difference between the two partially bootstrapped states depends on if the individual container overrides the applyInApp() method.  The abstract container will try to render a block when its applyInApp() method is called.  So if you do not want to render the blocks (they will likely have recursion and hierarchy to them) you will need to override the applyInApp() method for that container so it does not automatically try to render its block.

All of the different states can also be categorized by how they are processed.  The following diagram splits them up.


So which should you use?  Well, that’s the wrong question to ask.  The right question is “which should I build for?”  And for that question the answer is obvious.  Build to make your application use the request processor as much as possible.  Why?  Take a look at the code. Here is Mage_Core_Model_App:run().

public function run($params)
  $options = isset($params['options']) ? $params['options'] : array();
  Mage::register('application_params', $params);

  if ($this->_cache->processRequest()) {
  } else {
    $this->loadAreaPart(Mage_Core_Model_App_Area::AREA_GLOBAL, Mage_Core_Model_App_Area::PART_EVENTS);

    if ($this->_config->isLocalConfigLoaded()) {
      $scopeCode = isset($params['scope_code']) ? $params['scope_code'] : '';
      $scopeType = isset($params['scope_type']) ? $params['scope_type'] : 'store';
      $this->_initCurrentStore($scopeCode, $scopeType);

  return $this;

The full page cache is executed in the line that says processRequest().  If that method returns true, meaning that all of the containers were able to process their content, then the response object is called and the request is essentially done.  No routing, no controllers, and, importantly, no layout.  All of those are integral to executing a Magento request, but they do come at a cost.  If you can bypass that cost as often as possible your application will be much, much faster.

How much faster?  Consider the response times of this quick load test on my very, very under-powered virtual machine.


Leave a Reply

Your email address will not be published. Required fields are marked *