Combined cache

Hello,

I have number of almost same websites, and for all them I use redis caching to cache queries.

But after some time even if all is cached, asking cache took some amount of time.

Keeping all that in mind, I tried to make some cuper-cache which will use file cache for caching redis cache.

So it first query file cache, if it is there then use it.

Otherwise query redis cache, and after value is found store it in file-cache also.

But somewhere there is an error, after redis cache is cached locally, it got returned as json string instead of array for example.

Does anybody has idea where is the error, or maybe advice how to improve this caching solutiuon. Code is below:


class Cache extends \yii\caching\Cache

{

    /** @var  \yii\redis\Cache */

    public $redisCache;

    /** @var  FileCache */

    public $fileCache;


    public function init()

    {

        parent::init();

        $this->redisCache = Yii::$app->redisCache;

        $this->fileCache = Yii::$app->fileCache;

    }




    /**

     * Retrieves a value from cache with a specified key.

     * First try file cache and if it fails try redis cache and store to file cache

     *

     * @param string $key a unique key identifying the cached value

     *

     * @return string|boolean the value stored in cache, false if the value is not in the cache or expired.

     */

    protected function getValue($key)

    {

        $value = $this->fileCache->getValue($key);

        if ($value !== false) {

            return $value;

        }

        $value = $this->redisCache->getValue($key);


        $this->fileCache->set($key, $value);

        return $value;


    }


    /**

     * Stores a value identified by a key in cache.

     * Save ion both caches

     *

     * @param string  $key      the key identifying the value to be cached

     * @param string  $value    the value to be cached

     * @param integer $duration the number of seconds in which the cached value will expire. 0 means never expire.

     *

     * @return boolean true if the value is successfully stored into cache, false otherwise

     */

    protected function setValue($key, $value, $duration)

    {

        $this->fileCache->setValue($key, $value, $duration);

        return $this->redisCache->setValue($key, $value, $duration);

    }


    /**

     * Stores a value identified by a key into cache if the cache does not contain this key.

     *Store in both caches

     *

     * @param string  $key      the key identifying the value to be cached

     * @param string  $value    the value to be cached

     * @param integer $duration the number of seconds in which the cached value will expire. 0 means never expire.

     *

     * @return boolean true if the value is successfully stored into cache, false otherwise

     */

    protected function addValue($key, $value, $duration)

    {

        $this->fileCache->addValue($key, $value, $duration);

        return $this->redisCache->addValue($key, $value, $duration);

    }


    /**

     * Deletes a value with the specified key from cache

     * Delete from both

     *

     * @return boolean if no error happens during deletion

     */

    protected function deleteValue($key)

    {

        $this->fileCache->deleteValue($key);

        return $this->redisCache->deleteValue($key);

    }


    /**

     * Deletes all values from cache.

     * Flush both.

     *

     * @return boolean whether the flush operation was successful.

     */

    protected function flushValues()

    {

        $this->fileCache->flushValues();

        return $this->redisCache->flushValues();

    }

}

[color="#006400"]/* Moved from "Tips" to "General Discussions" */[/color]

Frankly speaking I don’t think it’s a good idea to cascade the different cache like this. Since both the file cache and the redis cache have exactly the same contents, it’s just adding unnecessary overhead to the caching solution as a whole. Using only the file cache will be much faster and more effective than your solution, because we don’t need to check and update the redis cache.

Hi,

thank you for your answer, but here it makes sense.

Consider this one: accessing file cache 0.001s, accessing redis cache 0.03s

If there are 10 cached objects, then using redis it may take 0.3s (in practice there are more then 10 and can eat much more time)

On the other hand some database queries are much longer, most complex can took more then 1s. And as there are 50 or 100 sites with same queries (each site on different subnet) it makes sense to cache those queries.

What I am trying to do is to write and read redis cache as least as possible.

(In my scenario I do not care if redis cache change as long as I have it in file cache I will not check it again)

Hmm …

So you say that each app on different sites has a file cache dedicated for it, and all the apps across many sites share a single instance of redis cache?

It’s interesting.

Does this really work? Here you are calling FileCache::getValue() and RedisCache::getValue(). But they are protected methods and are not allowed to be called from outside.

Probably you have to implement your Cache using public methods of FileCache and RedisCache.

And I think you should note that the keys and the values are not the same between public methods and protected ones. For example, get() method will receive the user defined key, and it will call getValue() with a normalized key. And when getValue() returns a cached content, get() will usually return an unserialized data. set() also does the same conversion of the key and the serialization of the value before it calls setValue().

@softark thank you for your answer, it really should not work…

But does not throw any error, probably the methods are never called, I should stick to public methods only.

Anyway, it works when page is accessed first time. Your suggestion will help me to investigate smaller part of code for possible errors.

In fact, I’m not sure if it should work or not. And it looks like working, anyway.




protected function getValue($key)

{

    $value = $this->fileCache->getValue($key);

    if ($value !== false) {

        return $value;

    }

    $value = $this->redisCache->getValue($key);


    $this->fileCache->set($key, $value); // <- this is problematic

    return $value;

}



I think that $this->fileCache->set() should not be called here, because set() expects a key that is not yet processed by buildKey(), and also a value that is not yet serialized. Note that getValue() accepts a key that is processed by buildKey(), and the retrieved value is a string (i.e., already serialized).

[EDIT]

http://php.net/manual/en/language.oop5.visibility.php#language.oop5.visibility-other-objects

I didn’t know that …