Posts Tagged ‘Magento’

Magento performance optimization (continued): custom Block Cache in Magento

Thursday, April 7th, 2011

As we keep improving JewelsBoutique.com to bring faster browsing experience to our visitors, recently we’ve made another step, to use Magento cache aggressively to speed up page processing at backend(server side).

Background

By studying the traffic and visit statistics, we found that many of our visitors were landing on static CMS pages, so keeping these pages fast can make our visitors feel good at the first impression, rather than experiencing a slow page then leave. Originally we thought Magento could cache the CMS pages & blocks if we enable the cache in Admin Panel, but that’s not true after digging into the code. So we planned to change this and make those frequently-visited blocks to be cacheable to accelerate the response speed.

Finding solution

In Magento wiki, there is a tutorial introduces the regular way of using block cache. To make one block cacheable, you just need add the code for the block class like below:

protected function _construct()
{
 $this->addData(array(
   'cache_lifetime' => 3600,
   'cache_tags'     => 'Cache tags for this block',
   'cache_key'      =>' Cache key for this block'
 ));
}

If choosing this way, we need to edit every block class to use cache, this requires certain code change which is not effective yet. Is there a better way? Yes! Because we have Magento Events. After looking into the core module, we found that before getting block output Magento will dispatch an Event (core_block_abstract_to_html_before), this is a good place to inject our cache setting for any block we want rather than editing each block independently.

Implementation

1. Add a new module or editing an existing module’s config.xml, add the following event listening setting:

<frontend>
 <events>
  <core_block_abstract_to_html_before>
   <observers>
    <cacheBlock>
     <type>singleton</type>
     <class>{your_module}/observer</class>
     <method>customBlockCache</method>
    </cacheBlock>
   </observers>
  </core_block_abstract_to_html_before>
 </events>
</frontend>

2. Add the implementation of event handler:

class {your_company}_{your_module}_Model_Observer{
 //you can make this to be configurable at Admin Panel
 const CUSTOM_CACHE_LIFETIME = 3600;
 //the non-CMS Block you want to cache
 private $cacheableBlocks = array('Block_Class_A', 'Block_Class_B', ...);
 public function customBlockCache(Varien_Event_Observer $observer){
  try {
   $event = $observer->getEvent();
   $block = $event->getBlock();
   $class = get_class($block);
   if (('Mage_Cms_Block_Block' == $class) && $block->getBlockId()) {
    $block->setData('cache_lifetime', self::CUSTOM_CACHE_LIFETIME);
    $block->setData('cache_key', 'cms_block_' . $block->getBlockId());
    $block->setData('cache_tags', array(Mage_Core_Model_Store::CACHE_TAG, $block->getBlockId()));
   } elseif (('Mage_Cms_Block_Page' == $class) && $block->getPage()->getIdentifier()) {
    $block->setData('cache_lifetime', self::CUSTOM_CACHE_LIFETIME);
    $block->setData('cache_key', 'cms_page_' . $block->getPage()->getIdentifier());
    $block->setData('cache_tags', array(Mage_Core_Model_Store::CACHE_TAG,
                                       $block->getPage()->getIdentifier()));
   } elseif (in_array($class, $this->cacheableBlocks)) {
    $block->setData('cache_lifetime', self::CUSTOM_CACHE_LIFETIME);
    $block->setData('cache_key', 'block_' . $class);
    $block->setData('cache_tags', array(Mage_Core_Model_Store::CACHE_TAG, $class));
   }
  } catch (Exception $e) {
   Mage::logException(e);
  }
 }
}

3. Now we are done, pretty easy, right? :D

Further tuning

Soon after we rolled out the code, we met several problems that required code enhancement:

  1. For some CMS pages or static blocks, they might embed other dynamic blocks which shouldn’t be cached, so you may need use a ignore-list of CMS page id or static block id in the event handler, and skip the cache setting if the block is in the ignore-list.
  2. If your site has HTTPS enabled, you’d better use current protocol(Mage::app()->getStore()->isCurrentlySecure()) as a part of the cache_key in case the cache is mixed under certain case.

Easy way to implement Asynchronized Product Filtering(Layered Navigation) in Magento

Saturday, February 26th, 2011

This post is to introduce how we made the async product filtering in Magento when we were doing the new Diamond Search, you’ll find the solution is really simple and you can make it in minutes!

Process to Optimize Magento under AWS

Friday, March 26th, 2010

As we have been using AWS for nearly 2 years to run JewelsBoutique.com, I’ve summarized a general process to optimize Magento under AWS (or other cloud-based LAMP stack) as below:

1. Capacity planning, be clear about how many items will be loaded into the system as well as the expected traffic volume (rough) in a certain period, this can help you choose the proper hardware resource (EC instance for AWS) at the beginning.

2. Apply commonly used ways to each separate component/sub-system of your system, e.g:

> Apache/Web Server: compile/configure the right mode & module, set proper parameters such as MaxClients, process threads, enable compressing module and set a never-expire header for static files if possible

> MySQL: set proper cache size, buffer and FS mode; apply the high-performance patch provided by Percona

> PHP: compile all needed extensions to be static, use at least one OP cache extension (eAccelerator/APC/xCache/ZO) and set the right parameters (caching mode, compressing level, etc.), set proper memory limitation

> OS: tune the FS/network related parameters, shutdown all unnecessary backend services (e.g. cups, portmap, etc.)

3. Once all of these are in place, let’s dig something more with Magento’s native support:

> Enable cache for all

> Configure backend cache type to be memory based (either shm FS or OP cache’s shm function)

> Use memory FS or database to save sessions

> Enable flat category/catalog, use Magento compiler if possible

> Use separate host (even a lighter web server on same server, but different port) to serve static files (/media/*, /skin/*)

4. Now, the system should be performing reasonably well, I believe the site should be OK to launch! Actually, my post was written at that point after we finished all items listed above – step by step – we experimented using different approaches to bring better browsing experience to those who shop for diamonds and engagement ring mountings on Jewelsboutique.com.

The next step will be much more interesting… also more challenging: monitor and detect the ‘performance optimizable area’ proactively, that could be either the slowest area of the system, or the place frequently called which could be bottleneck as traffic grows, here is a list that we have done/are doing based on our experience:

- Use separate server for database, web app and static files if needed

- Optimize Magento indexing strategy (we cannot easily upgrade to Magento 1.4, so need do something by ourselves with 1.3…)

- Build our own ’summary table’ for diamond search. As we have ~40,000 items, I can not imagine how slow Magento would be if using its own schema (even using flat-catalog)

There might be more specific cases when we were building JB, but that’s all what I remember and summarize at this point, I think we can go deeper for some specific problem if you guys have specifics you can share.

At last, I can share you the main stages that we have experienced since we started to run JB on AWS:

- Starting point – EC2 + S3: one small instance to host everything, write a script to backup data from EC2 to S3 everyday

- Soon after AWS brought out EBS, we moved to use EBS, all our ‘variable data’ (htdocs/database/logs) were stored on EBS, and we started to scale out — allocated more instances to run database and web server separately

- As backend/infrastructure-level performance are not big concern to us, we started to focus more on front-end/UX area, that what we are doing now — to improve our page look and feel, optimize the page structure to bring the web user convenient and useful information when shopping for diamonds and jewelry.

Angus