2

I've just installed Memcached and i'm trying to use it to cache results of various queries done with Doctrine ORM (Doctrine 2.4.8+, Symfony 2.8+).
My app/config/config_prod.yml have this :

doctrine:
    orm:
        metadata_cache_driver: memcached
        result_cache_driver: memcached
        query_cache_driver: memcached  

And i tried to useResultCache() on 2 queries like that (i just replaced the cache id here for the example) : return $query->useResultCache(true, 300, "my_cache_id")->getArrayResult();. Special queries here because they're native queries (SQL) due to their complexity, but the method is available for any query (class AbstractQuery) so i assume it should work.
Unfortunately, it doesn't. Everytime i refresh the page, if i just did a change in the database, the change is displayed. I've checked the stats of memcached and it seems there're still some cache hits but i don't really know how, cf. what i just said.

Does anyone has an idea on why the cache doesn't seem to be used here to get the supposedly cached results ? Did i misunderstand something and the TTL is ignored somehow ?
There's no error generated, memcached log is empty.

As requested by @nifr, here is the layout of my code to create the 2 native queries i put a Memcached test on :

    $rsm = new ResultSetMapping;
    $rsm->addEntityResult('my_entity_user', 'u');
    // some $rsm->addFieldResult('u', 'column', 'field');
    // some $rsm->addScalarResult('column', 'alias');

    $sqlQuery = 'SELECT
        ... 
        FROM ...
        INNER JOIN ... ON ...
        INNER JOIN ... ON ...
        // some more join
        WHERE condition1
            AND condition2';
    // some conditions added depending on params passed to this function
    $sqlQuery .= '
            AND (fieldX = (subrequest1))
            AND (fieldY = (subrequest2))
            AND condition3
            AND condition4
        GROUP BY ...
        ORDER BY ...
        LIMIT :nbPerPage
        OFFSET :offset
        ';

    $query =
        $this->_em->createNativeQuery($sqlQuery, $rsm)
        // some ->setParameter('param', value)
        ;

    return $query->useResultCache(true, 300, "my_cache_id")->getArrayResult();
Harest
  • 31
  • 7
  • Interesting question. Does switching (back) to apc as cache driver make any difference? – mblaettermann Mar 26 '16 at 02:24
  • @mblaettermann By default it's commentated with apc indeed. And as expected if i set apc i directly get this exception : `Symfony\Component\Debug\Exception\UndefinedFunctionException Attempted to call function "apc_fetch" from namespace "Doctrine\Common\Cache".`. – Harest Mar 26 '16 at 02:30
  • Ah, okay. Seems the APCu PHP Extension is missing. I was just curious if it would give you other results. If you can change the system, you may install it with `apt-get install php5-apcu` But this is not really helping the question asked. – mblaettermann Mar 26 '16 at 02:33
  • @mblaettermann APC is deprecated for a while now. I'd rather prefer to not install it. That's also why i chose Memcached, even if i don't know if it's still maintained either. I read some stuff about the various existing caching drivers and it seemed Memcached was still a good choice. – Harest Mar 26 '16 at 02:39
  • APC as an Opcode cache is deprecated, yes. But the "userland" cache APCu is still valid I think. If you really want the best and most maintained, use redis: http://olegpuzanov.com/2015/02/01/using-redis-for-doctrine-caching-in-symfony2/ and http://stackoverflow.com/questions/10558465/memcached-vs-redis?rq=1 – mblaettermann Mar 26 '16 at 03:29
  • @mblaettermann Not that i wanted the best and most maintained. Just something stable and not too hard to implement as i'm kinda new to caching stuff. I think i saw this SO thread while i searched some weeks ago for the sql cache i'd use ^^. I'll keep that in a corner if i don't get any other info about my issue, thanks. But i've the feeling i'll get the same issue if i switch to Redis. – Harest Mar 26 '16 at 04:00
  • How do you create your query? Can you share the code please? – Nicolai Fröhlich Mar 26 '16 at 16:16
  • @nifr I just did. Added it in the OP. – Harest Mar 26 '16 at 18:45

1 Answers1

1

So, it seems for some reason Doctrine doesn't succeed to get the ResultCacheDriver. I tried to set it before the useResultCache() but i had an exception from Memcached : Error: Call to a member function get() on null.

I've decided to do it more directly by calling Memcached(). I guess i'll do this kind of stuff in controllers and repositories, depending on my needs. After some tests it works perfectly.
Here is what i basically do :

    $cacheHit = false;
    $cacheId = md5("my_cache_id"); // Generate an hash for your cache id
    // We check if Memcached exists, if it's not installed in dev environment for instance
    if (class_exists('Memcached'))
    {
        $cache = new \Memcached();
        $cache->addServer('localhost', 11211);
        $cacheContent = $cache->get($cacheId);
        // We check if the content is already cached
        if ($cacheContent != false)
        {
            // Content cached, that's a cache hit
            $content = $cacheContent;
            $cacheHit = true;
        }
    }
    // No cache hit ? We do our stuff and set the cache content for future requests
    if ($cacheHit == false)
    {
        // Do the stuff you want to cache here and put it in a variable, $content for instance
        if (class_exists('Memcached') and $cacheHit == false) $cache->set($cacheId, $content, time() + 600); // Here cache will expire in 600 seconds
    }  

I'll probably put this in a Service. Not sure yet what's the "best practice" for this kind of stuff.

Edit : I did a service. But it only works with native sql... So the problem stays unsolved.
Edit² : I've found a working solution about this null issue (meaning it couldn't find Memcached). The bit of code :

        $memcached = new \Memcached();
        $memcached->addServer('localhost', 11211);
        $doctrineMemcached = new \Doctrine\Common\Cache\MemcachedCache();
        $doctrineMemcached->setMemcached($memcached);
        $query
            ->setResultCacheDriver($doctrineMemcached)
            ->useResultCache(true, 300);  

Now, i would like to know which stuff should i put in the config_prod.yml to just use the useResultCache() function.

Harest
  • 31
  • 7