• What is Full Page Cache?
  • Explanation of feature with some example.
  • Directory Structure & Configuration files required/used?
  • Important flow-chart related to FPC.
  • Important function list related to FPC.
  • Explanation of those important functions.
  • How to manage FPC via Admin.
  • Known issues for FPC.
  • Most common mistake with FPC.
  • Important references related to FPC.
  • What is the difference between cache and Full Page cache in Magento
  • What is Full Page Cache?

    Magento is a well known open source e-Commerce platform. On other side it is also a complex pieces of PHP software ever created. When it comes to complexity Magento has relatively low performance when compared to custom made e-Commerce solutions. In order to improve this situation, Magento uses the concept of Full Page Cache.

    Basically Cache is a component that stores duplicate data that can served faster. Stored data is in the form of values that have been processed earlier or duplicate of some values that are stored elsewhere. If a user request some data and that data is already present in cache, this request can be served by simply reading the cache, which in result is faster serving. Otherwise fetch from its original storage location which in result is slow.

    In general, caching improves response time and reduces load on server to increase page speed. The pages which takes a second to load now loads in fraction of the time. Most techniques cache static pages whose contents are rarely changes. However, most e-commerce sites serve dynamic content, containing personalized information or data that changes more frequently. Caching dynamic content requires more sophisticated caching techniques, such as those provided by Full Page Cache (FPC) module for Magento Enterprise Edition.

    Full page caching is a way for Magento to load content for a user without having to fully initialize the application. This makes the entire website fast, but makes it less dynamic. Magento has built-in ways to achieve a more dynamic page through a process called “hole-punching.”

    In Magento, there are three pages which are by default cached.

    • Product Listing Pages
    • Product Display Pages
    • CMS Pages(Homepage)

    Explanation of feature with some example.
    Following are the features of full page cache
    • Supports block updates (hole punching).
    • Reduce servers response time.
    • Supports multiple currencies.
    • Supports multiple stores.
    • Supports multiple languages.
    • Supports mobile themes.
    • Cache management in Magento admin.
    • Cache lifetime.
    Explanation:

    Hole Punch Support In Full Page Cache, we know that the whole content of data stores in cache. Most technique cache's the static content(that rarely changes) but what, if there is dynamic content. At this point the concept of hole punching come into the picture.

    Reduce Server Response Time After using page cache, reduces load on server which in return increases the speed of page to load faster.

    Multiple Currency Support Supports Multiple currency. Whether you have 1 currency or 100 currencies the page cache will not generate issues and user will always see the expected currency.

    Multiple Store / Language Support As we know Magento supports multiple store views,which are typically used for different languages. Even after page cache there is no effect of cache on languages.

    Mobile Theme Support Mobile themes, theme overrides, and more are easily supported . Simply input the required patterns you want to match on (example: iphone|blackberry|android) and your cache will be set accordingly.

    Cache Management in Magento Admin: We can enable or disable page cache in the admin panel. Even we can clean cache storage through Magento admin.

    Cache Lifetime: We can set lifetime of cache. If you set it to false, Zend cache will get default value of 7200 seconds. If you want never expired cache set it to 9999999999, which is Zend_Cache maximum value.

    Directory Structure & Configuration files required/used?
    directory-structure_magento_full_page_cache
    Important flow-chart related to FPC.
    1. Flow chart of magento_full_page_cache_applyInApp. magento_full_page_cache_extractContent_a1. Flow chart of magento_full_page_cache_applyInApp.2. Flow chart magento_full_page_cache_applyWithoutApp

    magento_full_page_cache_applyWithoutApp

    3. Flow Chart magento_full_page_cache_extractContent

    magento_full_page_cache_extractContent_b

    4. Flow chart magento_full_page_cache_processContainers

    magento_full_page_cache_processContainers

    5. Flow chart magento_full_page_cache_processContent

    magento_full_page_cache_processContent

    Important function list related to FPC.
    Following tables are affected with target rule.
    • extractContent($content)
    • processContent($content)
    • processContainers($content)
    • applyWithoutApp($content)
    • applyInApp($content)
    Explanation of those important functions.
    1]extractContent($content)purpose of the function: This function is uesd to get page content from cache storage
        /**
         * Get page content from cache storage
         *
         * @param string $content
         * @return string|false
         */
        public function extractContent($content)
        {
            $cacheInstance = Enterprise_PageCache_Model_Cache::getCacheInstance();
            /*
             * Apply design change
             */
            $designChange = $cacheInstance->load($this->getRequestCacheId() . self::DESIGN_CHANGE_CACHE_SUFFIX);
            if ($designChange) {
                $designChange = unserialize($designChange);
                if (is_array($designChange) && isset($designChange[\'package\']) && isset($designChange[\'theme\'])) {
                    $designPackage = Mage::getSingleton(\'core/design_package\');
                    $designPackage->setPackageName($designChange[\'package\'])
                        ->setTheme($designChange[\'theme\']);
                }
            }
    
            if (!$this->_designExceptionExistsInCache) {
                //no design exception value - error
                //must be at least empty value
                return false;
            }
            if (!$content && $this->isAllowed()) {
                $subprocessorClass = $this->getMetadata(\'cache_subprocessor\');
                if (!$subprocessorClass) {
                    return $content;
                }
    
                /*
                 * @var Enterprise_PageCache_Model_Processor_Default
                 */
                $subprocessor = new $subprocessorClass;
                $this->setSubprocessor($subprocessor);
                $cacheId = $this->prepareCacheId($subprocessor->getPageIdWithoutApp($this));
    
                $content = $cacheInstance->load($cacheId);
    
                if ($content) {
                    if (function_exists(\'gzuncompress\')) {
                        $content = gzuncompress($content);
                    }
                    $content = $this->_processContent($content);
    
                    // restore response headers
                    $responseHeaders = $this->getMetadata(\'response_headers\');
                    if (is_array($responseHeaders)) {
                        foreach ($responseHeaders as $header) {
                            Mage::app()->getResponse()->setHeader($header[\'name\'], $header[\'value\'], $header[\'replace\']);
                        }
                    }
    
                    // renew recently viewed products
                    $productId = $cacheInstance->load($this->getRequestCacheId() . \'_current_product_id\');
                    $countLimit = $cacheInstance->load($this->getRecentlyViewedCountCacheId());
                    if ($productId && $countLimit) {
                        Enterprise_PageCache_Model_Cookie::registerViewedProducts($productId, $countLimit);
                    }
                }
    
            }
            return $content;
        }
    
    
    
    
    2]processContent($content)purpose of the function: This function is used to determine and process all defined containers and it directly request to process action which is in pagecache/request if necessary for additional processing.
    /**
        * Determine and process all defined containers.
        * Direct request to pagecache/request/process action if necessary for additional processing
        *
        * @param string $content
        * @return string|false
        */
        protected function _processContent($content)
        {
            $containers = $this->_processContainers($content);
            $isProcessed = empty($containers);
            // renew session cookie
            $sessionInfo = Enterprise_PageCache_Model_Cache::getCacheInstance()->load($this->getSessionInfoCacheId());
    
            if ($sessionInfo) {
                $sessionInfo = unserialize($sessionInfo);
                foreach ($sessionInfo as $cookieName => $cookieInfo) {
                    if (isset($_COOKIE[$cookieName]) && isset($cookieInfo[\'lifetime\'])
                        && isset($cookieInfo[\'path\']) && isset($cookieInfo[\'domain\'])
                        && isset($cookieInfo[\'secure\']) && isset($cookieInfo[\'httponly\'])
                    ) {
                        $lifeTime = (0 == $cookieInfo[\'lifetime\']) ? 0 : time() + $cookieInfo[\'lifetime\'];
                        setcookie($cookieName, $_COOKIE[$cookieName], $lifeTime,
                            $cookieInfo[\'path\'], $cookieInfo[\'domain\'],
                            $cookieInfo[\'secure\'], $cookieInfo[\'httponly\']
                        );
                    }
                }
            } else {
                $isProcessed = false;
            }
    
            if (isset($_COOKIE[Enterprise_PageCache_Model_Cookie::COOKIE_FORM_KEY])) {
                $formKey = $_COOKIE[Enterprise_PageCache_Model_Cookie::COOKIE_FORM_KEY];
            } else {
                $formKey = Enterprise_PageCache_Helper_Data::getRandomString(16);
                Enterprise_PageCache_Model_Cookie::setFormKeyCookieValue($formKey);
            }
    
            Enterprise_PageCache_Helper_Form_Key::restoreFormKey($content, $formKey);
    
            /**
             * restore session_id in content whether content is completely processed or not
             */
            $sidCookieName = $this->getMetadata(\'sid_cookie_name\');
            $sidCookieValue = $sidCookieName && isset($_COOKIE[$sidCookieName]) ? $_COOKIE[$sidCookieName] : \'\';
            Enterprise_PageCache_Helper_Url::restoreSid($content, $sidCookieValue);
    
            if ($isProcessed) {
                return $content;
            } else {
                Mage::register(\'cached_page_content\', $content);
                Mage::register(\'cached_page_containers\', $containers);
                Mage::app()->getRequest()
                    ->setModuleName(\'pagecache\')
                    ->setControllerName(\'request\')
                    ->setActionName(\'process\')
                    ->isStraight(true);
                // restore original routing info
                $routingInfo = array(
                    \'aliases\'              => $this->getMetadata(\'routing_aliases\'),
                    \'requested_route\'      => $this->getMetadata(\'routing_requested_route\'),
                    \'requested_controller\' => $this->getMetadata(\'routing_requested_controller\'),
                    \'requested_action\'     => $this->getMetadata(\'routing_requested_action\')
                );
    
                Mage::app()->getRequest()->setRoutingInfo($routingInfo);
                return false;
            }
        }
    
    
    3]processContainers($content) purpose of the function: This function is used to process the container of Full Page Cache.
      /**
         * Process Containers
         *
         * @param $content
         * @return array
         */
        protected function _processContainers(&$content)
        {
            $placeholders = array();
            preg_match_all(
                Enterprise_PageCache_Model_Container_Placeholder::HTML_NAME_PATTERN,
                $content, $placeholders, PREG_PATTERN_ORDER
            );
            $placeholders = array_unique($placeholders[1]);
            $containers = array();
            foreach ($placeholders as $definition) {
                $placeholder = new Enterprise_PageCache_Model_Container_Placeholder($definition);
                $container = $placeholder->getContainerClass();
                if (!$container) {
                    continue;
                }
    
                $container = new $container($placeholder);
                $container->setProcessor($this);
                if (!$container->applyWithoutApp($content)) {
                    $containers[] = $container;
                } else {
                    preg_match($placeholder->getPattern(), $content, $matches);
                    if (array_key_exists(1,$matches)) {
                        $containers = array_merge($this->_processContainers($matches[1]), $containers);
                        $content = preg_replace($placeholder->getPattern(), str_replace(\'$\', \'\\\\$\', $matches[1]), $content);
                    }
                }
            }
            return $containers;
        }
    
    
    4]applyInApp($content)purpose of the function: This function is used to generate and apply container content in contoller after application is intialized.
    /**
        * Generate and apply container content in controller after application is initialized
        *
        * @param string $content
        * @return bool
        */
        public function applyInApp(&$content)
        {
            $blockContent = $this->_renderBlock();
            if ($blockContent === false) {
                return false;
            }
    
            if (Mage::getStoreConfig(Enterprise_PageCache_Model_Processor::XML_PATH_CACHE_DEBUG)) {
                $debugBlock = new Enterprise_PageCache_Block_Debug();
                $debugBlock->setDynamicBlockContent($blockContent);
                $debugBlock->setTags($this->_getPlaceHolderBlock()->getCacheTags());
    
                $debugBlock->setType($this->_placeholder->getName());
                $this->_applyToContent($content, $debugBlock->toHtml());
            } else {
                $this->_applyToContent($content, $blockContent);
            }
    
            $subprocessor = $this->_processor->getSubprocessor();
            if ($subprocessor) {
                $contentWithoutNestedBlocks = $subprocessor->replaceContentToPlaceholderReplacer($blockContent);
                $this->saveCache($contentWithoutNestedBlocks);
            }
    
            return true;
        }
    
    
    5]applyWithoutApp($content)purpose of the function: This function is used to generate placeholder content before application is initialized and apply to page content if possible.
    /**
       * Generate placeholder content before application was initialized and apply to page content if possible
       *
       * @param string $content
       * @return bool
       */
        public function applyWithoutApp(&$content)
        {
            $cacheId = $this->_getCacheId();
    
            if ($cacheId === false) {
                $this->_applyToContent($content, \'\');
                return true;
            }
    
            $block = $this->_loadCache($cacheId);
            if ($block === false) {
                return false;
            }
    
            $block = Enterprise_PageCache_Helper_Url::replaceUenc($block);
            $this->_applyToContent($content, $block);
            return true;
        }
    
    
    How to manage FPC via Admin.
    Following tables are affected with target rule.
    • Log in to the Magento Admin Panel as an administrator.
    • Click System --> Configuration
    • Expand Cache ManagmentIt shows Cache Storage Managment:

      For Full page cache Associated tag: FPC

      magento_fpc_admin_configuration

      After clearing or refreshing a cache, always refresh your browser to see new changes the result in your store
    1] To refresh Full Page Cache: Sometimes full page cache appears in yellow color that is “Invalidate” so all time we have to refresh it:
    • Log in to the Magento Admin Panel as an administrator.
    • Click System > Configuration
    • Expand Cache Managment
    • click the checkbox of Page Cache
    • Select Action 'Refresh'
    • Click on submit

    magento FPC refresh admin configuration

    Known issues for FPC.
    • Currency switching issue:

      This issue is found on many sites that the currency not switch. When currency switched, doesn’t refreshed cache. Users does not see the correct currency on site. Sometimes product price is not change when switching currency as per the currency. It always display same currency on site which is saved in full page cache.

    • Cart quantity issue:

      The Cart Sidebar won't get updated when product is added to cart or it shows different cart quantity on Homepage and different cart quantity on another pages. It does not display new or updated cart quantity . Always get quantity of product from cache which is not hole-punch.

    • Welcome container(logIn) issue:

      Issue with Welcome message having random names when Logged in. It shows different online customer name randomly on different page.

    Most common mistake with FPC.

    How to fix cart quantity issue with Full Page Cache

    Before few days client found an issue that the Cart Sidebar won't get updated when product is added to cart or it shows different cart quantity on Homepage and different cart quantity on another pages. It does not display new or updated cart quantity.

    In cartheader.phtml
    
    

    common mistakes in magento fpc

    Then add following script in your cartheader.phtml
    jQuery(document).ready(function(){
         jQuery('.cartcount').html('0') ;
     })
    
    What is the difference between cache and Full Page cache in Magento
    Cache:

    When a user visiting a website page for the first time, Magento generate and deliver this page to the visitor and automatically save a copy of it to the cache. Then all those pages get stored in a cache. Each time when user request arrives, the system does not request Magento to generate a page, but returns the copy of the page from cache. Neither cache will connect to the Magento database or load any Magento files ,it can load the request from cache.

    Magento block caching depends of three things:
    cache_lifetime => cache lifetime in seconds
    cache_tags => cache type identifiers primarly used for deleting right cache at the right time
    cache_key => cache identyfier 
    
    For example :
    protected function _construct()
    {
    $this->addData(array(
    'cache_lifetime' => 3600,
    'cache_tags'     => array(Mage_Catalog_Model_Product::CACHE_TAG),
    'cache_key'      => $this->getProduct()->getId(),
    ));
    }
    

    we need some conditional approach because constructor approach isn’t enough in most scenarios.

    public function getCacheKey(){}
    public function getCacheLifetime(){}
    public function getCacheTags(){}
    

    Full Page Cache:

    Full page caching is a way for Magento to load content for a user without having to fully initialize the application. This makes the entire website fast, but makes it less dynamic. Magento has built-in ways to achieve a more dynamic page through a process called “hole-punching”. With FPC complete page is cached and hole punching(container) system where by containers are replaced with complete html if needed.

    Hole-Punching

    The page is composed of block and template files. To keep this dynamic relationship active with FPC on we are given the ability to surround our block and template files with something called a placeholder, or container.

    To retrieve content, containers have two major functions that are important to us: applyInApp($content) and applyWithoutApp($content).

    Magento FPC knows of four states:

    Depending on the page, content, and cache lifetimes, Magento will handle each request slightly different.

    1. Page is in cache with no dynamic blocks: Magento did a minimal amount of work to achieve a page load

    2. Page is in cache, dynamic blocks are cached: Similar to State 1, Magento gets a request to load a webpage and finds the page is cached. Magento then comes across a container for a dynamic block. From here it determines if the block is cached or not and if so, will proceed to fill the container with the cached block using applyWithoutApp($content) so we have another full page load with very minimal overhead.

    3. Page is in cache, dynamic blocks are not cached: In state 2 was unable to fetch and deliver the dynamic block content, so the block content needs to be generated, even though the rest of the page has been found in the FPC. For this purpose the FPC module sets the request to pagecache/request/process, and the regular Magento application initialization and routing is followed. The method fetches the previously instantiated container class for the dynamic block, and calls applyInApp($content) on it.

    4. Page is not in cache: In this state Magento did not find the requested page in the cache, so the page must be processed as usual.