Friday 18 February 2011

Magento Enterprise Full Page Caching

We will discuss here how to setup various magento full page cache options for your site. How it works

Basically, magento has defined 3 processors 1) for product pages, 2) catalog lists and 3) for rest of the CMS pages. In order to add full page caching to any page in magento you need to do have the following tags in your module's config.xml in etc... **Note: Magento Enterprise by default has configured all the CMS pages, Product pages and catalog lists for Caching.

<frontend> <cache> <requests> <!-- Supported definitions: <module_front_name>processor_model</module_front_name> - all module urls<br /> <module_front_name><controller>processor_model</controller></module_front_name> - all module controller actions<br /> <module_front_name><controller><action>processor_model</action></controller></module_front_name> - specific action<br /> --> <mymodule_mycontroller_myaction>enterprise_pagecache/processor_default</mymodule_mycontroller_myaction> </requests> </cache> </frontend>

This could be your dashboard for instance which magento does not cache by default in which case you need to add the tags:

<customer_account>enterprise_pagecache/processor_default</customer_account>

But this configuration would cache the whole page regardless, and you would not want that on customer dashboards where you want to see only a valid customer information and the information might also need to update based on some events.

So we now need to have a cache.xml file which tells us which blocks on the page needs to have special behavioral pattern than other blocks on the page. For instance the recent order block in my dashboard would need to update each time an order is placed, where else the address and customer names should only belong to the current logged in customer and not a cached data from another customer. Some of the blocks may on the other hand be used by different customers.

<?xml version="1.0" encoding="UTF-8"?> <config> <placeholders> <myblock_someaction> <block>mymodule/myblock</block> <name>blockname</name> <placeholder>MY_BLOCK</placeholder> <container>MyModule_Model_MyModel</container> <cache_lifetime>84600</cache_lifetime> </myblock_someaction> </placeholders> </config>

Notice in the above tags, the tagname should be any unique cache tagname, in our case its 'myblock_someaction'. The block tag and the name tag should have the block name and the name as it appears in the layout in design. The placeholder tag should have any unique string. The container needs to contain the model which actually defines the behavior of the block cache. And finally, the cache_lifetime is the time till the cache will be valid. Now, all that matters is the model which defines the behavior of the cache and we hope to look it in details.

Magento Enterprise already handles some of the blocks like the cart_container on the header, the catalog navigation and some other blocks for reporting. Let us know try to define three type of cache model 1) Which is customer specific 2) Is some template specific 3) The block that we always want to refresh.

For Customer specific model we would mainly need to override two abstract methods. 1) _getCacheId() and _renderBlock(). An Example is shown below:

class MyModule_Model_MyModel extends Enterprise_PageCache_Model_Container_Abstract { protected function _getCacheId() { return 'CUSTOMER_STATE_' . md5($this->_placeholder->getAttribute('cache_id') . $this->_getCookieValue(Enterprise_PageCache_Model_Cookie::COOKIE_CUSTOMER, '')); } protected function _renderBlock() { $block = $this->_placeholder->getAttribute('block'); $template = $this->_placeholder->getAttribute('template'); $block = new $block; $block->setTemplate($template); $block->setLayout(Mage::app()->getLayout()); return $block->toHtml(); } }

The above code would allow Mage to have browser cookie for the customer and it would use the cookie value to generate an Id. If the customer logs out and logs in as another customer the cookie would refresh and the cache will be invalidated, and so the block will be newly generated for the customer.

Now, we will look into a template specific caching model.

class MyModule_Model_MyModel extends Enterprise_PageCache_Model_Container_Abstract { protected function _getCacheId() { return 'TEMPLATE_' . md5($this->_placeholder->getAttribute('template').$this->_placeholder->getAttribute('cache_id')); } }

Notice that the above does not requires a renderer. This is because we are expecting the page to be there always and we do not expect that it would get invalidated based on some events. This kind of model would help us have template specific caching of a shared block. Finally, we have the ever refreshing block or the ever self invalidating block.

class MyModule_Model_MyModel extends Enterprise_PageCache_Model_Container_Abstract { protected function _getCacheId() { return 'CONSTANT_STRING_' . md5($this->_placeholder->getAttribute('cache_id')); } protected function _renderBlock() { $block = $this->_placeholder->getAttribute('block'); $template = $this->_placeholder->getAttribute('template'); $block = new $block; $block->setTemplate($template); $block->setLayout(Mage::app()->getLayout()); return $block->toHtml(); } protected function _saveCache($data, $id, $tags = array(), $lifetime = null) { return false; } }

Notice in the above we added / overridden _saveCache(...) and it basically never saves. The Id should be return values based on your requirements. And because it always invalidates you would need _renderBlock() funtion.

Finally, here is a tiny little but very useful trick... Because we can never call mage functionalities from these models (yes we cannot call helpers or do logs, AFAIK). We can slip in values to these blocks using a useful mage technique. Suppose we want some dynamic ID based on some block requirements how do we do it in _getCacheId()... remember we cannot even call session or registry.

In order to achieve that you would need a function in your block (the one you mentioned in cache.xml -> block), public function getCacheKeyInfo(). Here is an example below.

public function getCacheKeyInfo() { return $cacheId = array('my_id' => '1234'); }

Now, in the model you can use this as :

class MyModule_Model_MyModel extends Enterprise_PageCache_Model_Container_Abstract { protected function _getCacheId() { return 'SOME_BLOCK_' . md5($this->_placeholder->getAttribute('my_id').$this->_placeholder->getAttribute('cache_id')); } }

Good Luck with your caching!

17 comments:

  1. Hi!
    Could those methods be used with normal (not Enterprise) edition of Magento?

    ReplyDelete
  2. I think the community edition does not have FPC and that is one of the few differences it has with Enterprise... Hope some free caching mechanism would be out soon.

    ReplyDelete
  3. Hi Anas,
    Seems you have access to the latest version of Magento Enterprise. Would you be interested in porting the native pagecachemodule to the community edition? Shouldn't be that hard and would be great for clients who cant spend 13 grand a year.

    ReplyDelete
  4. Yes it is an interesting Idea, I would be happy to implement it for community in my spare time.

    ReplyDelete
  5. we developed a full page cache module for magento ce: http://fullpagecache.mgt-modules.com/


    best regards,
    Stefan

    ReplyDelete
  6. you can use it in ce version.contact me if you interesting

    ReplyDelete
  7. Great article, but I am trying to figure out how to disable full page caching for certain pages. How would I go about doing that? This seems to be how you update the cache control on individual blocks, but once you enable full page cache it just grabs the whole page.

    ReplyDelete
  8. Yes, There is full page caching available by magento but they cost money, are proprietary and most importantly. Fortunately Magento caching mechanism is flexible enough and there is a feature which we may call a hole or a placeholder.

    magento designer

    ReplyDelete
  9. Anas - We are looking for help with custom code to bypass the cache in the product detail page and other pages. Can you help us? Please email developer at jgood dot com. Thank you!

    ReplyDelete
  10. Really its very useful for topic.Keep writing some more interesting topics like a magento development.

    ReplyDelete
  11. Anas,

    Would you be interested in implementing FPC in our Magento site?

    Pls email me at bas at bnrkantoor.nl

    Bas

    ReplyDelete
  12. This comment has been removed by the author.

    ReplyDelete
  13. If anyone's interested, I looked into the issue of caching objects and pages in the CE of Magento, and put together a quick tutorial here.

    ReplyDelete
  14. This comment has been removed by the author.

    ReplyDelete
  15. Magento websites have access to a suite of report and analysis functions, including an abandoned shopping cart report and an analysis of best customers. You have shared informative post regarding magento. Hire Magento Designer

    ReplyDelete