Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to approach caching in ZF2

I am just starting to get my head into caching as a whole. I have a simple indexAction() that fetches all given Datasets. My approach is:

  • check for existing key 'controllername-index-index'
  • if existing: return the value of the key
  • if not existing, do the normal action and add the key

The value inside the key should be the ViewModel that will be generated and populated with my data.

Here's what i have done so far:

<?php
public function indexAction()
{
    $sl = $this->getServiceLocator();
//  $cache = $sl->get('cache');
//  $key = 'kennzahlen-index-index';
//
//  if ($cache->hasItem($key)) {
//      return $cache->getItem($key);
//  }

    $viewModel = new ViewModel();
    $viewModel->setTemplate('kennzahlen/index/index');
    $entityService = $sl->get('kennzahlen_referenzwert_service');
    $viewModel->setVariable('entities', $entityService->findAll());

//  $cache->setItem($key, $viewModel);

    return $viewModel;
}

The Caching parts are commented out for testing purposes, but basically this is all that i am doing. The Caching config/service looks like the following:

<?php
'cache' => function () {
    return \Zend\Cache\StorageFactory::factory(array(
        'adapter' => array(
            'name' => 'filesystem',
            'options' => array(
                'cache_dir' => __DIR__ . '/../../data/cache',
                'ttl' => 100
            ),
        ),
        'plugins' => array(
            array(
                'name' => 'serializer',
                'options' => array(

                )
            )
        )
    ));
},

The serialization and caching works quite well, but i am surprised by the missing results. Going by what the ZendDevelopersToolbar tells me, the times WITHOUT caching range between 1.8s to 2.5s. Having the caching parts uncommented (enabled) doesn't really improve the loading time of my page at all.

So my question is: Is this approach completely wrong? Are there different, more speedy parts, that can be saved with some neat configuration tricks?

I Feel that a 2 second load time of a page is DEFINITELY too slow. 1s to me is the maximum given a huge amount of data, but certainly not anything more than that :S

All help/hints/suggestions will be greatly appreciated. Thanks in advance!

like image 437
Sam Avatar asked Dec 07 '12 14:12

Sam


1 Answers

One option would be to cache the complete output of your page, for example based on the route match. You need to listen between routing and dispatching which route has been found as match and then act accordingly:

namespace MyModule;

use Zend\Mvc\MvcEvent;

class Module
{
    public function onBootstrap(MvcEvent $e)
    {
        // A list of routes to be cached
        $routes = array('foo/bar', 'foo/baz');

        $app = $e->getApplication();
        $em  = $app->getEventManager();
        $sm  = $app->getServiceManager();

        $em->attach(MvcEvent::EVENT_ROUTE, function($e) use ($sm) {
            $route = $e->getRouteMatch()->getMatchedRouteName();
            $cache = $sm->get('cache-service');
            $key   = 'route-cache-' . $route;

            if ($cache->hasItem($key)) {
                // Handle response
                $content  = $cache->getItem($key);

                $response = $e->getResponse();
                $response->setContent($content);

                return $response;
            }
        }, -1000); // Low, then routing has happened

        $em->attach(MvcEvent::EVENT_RENDER, function($e) use ($sm, $routes) {
            $route = $e->getRouteMatch()->getMatchedRouteName();
            if (!in_array($route, $routes)) {
                return;
            }

            $response = $e->getResponse();
            $content  = $response->getContent();

            $cache = $sm->get('cache-service');
            $key   = 'route-cache-' . $route;
            $cache->setItem($key, $content);
        }, -1000); // Late, then rendering has happened
    }
}

The second listener checks at the render event. If that happens, the result of the response will be cached.

This system (perhaps not with 100% copy/paste, but the concept) works because if you return a Response during the route or dispatch event, the application will short circuit the application flow and stop further triggering listeners. It will then serve this response as it is.

Bear in mind it will be the complete page (including layout). If you don't want that (only the controller), move the logic to the controller. The first event (now route) will be dispatch of the controller. Listen to that early, so the normal execution of the action will be omitted. To cache the result, check the render event for the view layer to listen to.

/update: I wrote a small module to use this DRY in your app: SlmCache

like image 168
Jurian Sluiman Avatar answered Oct 23 '22 11:10

Jurian Sluiman