Обновление кеша

Общие вопросы по использованию второй версии фреймворка. Если не знаете как что-то сделать и это про Yii 2, вам сюда.
Ответить
yujin1st
Сообщения: 192
Зарегистрирован: 2012.03.26, 12:03

Обновление кеша

Сообщение yujin1st »

Здравствуйте,
В проекте запустили кеш на redis'е на запрашиваемые из бд данные, и вслед за ним встала закономерная задача с его обновлением.
Очевидно, что надо кеш обновлять при изменении данных, вопрос в том, как правильно и в каком месте где это делать?

Есть есть ряд ситуаций на которые и возникают вопросы:
* afterSave единичной модели – а что если мы обновляем данные большим скопом и тогда проще
* данные обновляются через updateAttributes()
* запросы напрямую к БД без AR
- как не забыть и учесть про все эти обновления?

Если ранее пришли к практике использования репозитория для получения данных - туда же и вставили кеширование данные, то правильно ли будет использовать те же репозитории для сохранения данных, или есть другие подходы?
Опять таки где-то есть обработки, где-то есть другой код который имеет место к этим данным - как не пропустить все это и не забыть?
zxczxc12
Сообщения: 161
Зарегистрирован: 2013.01.24, 21:16

Re: Обновление кеша

Сообщение zxczxc12 »

Только закончил покрытие проекта кешем

Сразу скажу , если проект большой то вероятность того что где то кеш забудешь обновить - очень большая
Тут надо аккуратно и ответ на вопрос :
>как не пропустить все это и не забыть?

или записывать где то или помнить :-)

я использую вот такое поведение которое прикручиваю к нужной модели кеш которой нужно обновлять + обновяль кеши которые связанные :

Код: Выделить всё

<?php
/**
 * Eugine Terentev <[email protected]>
 */

namespace common\behaviors;

use Yii;
use yii\base\Behavior;
use yii\caching\TagDependency;
use yii\db\ActiveRecord;

/**
 * CacheInvalidateBehavior automatically invalidates cache by specified keys or tags
 *  public function behaviors()
 * {
 *     return [
 *         [
 *             'class' => CacheInvalidateBehavior::class,
 *             'tags' => [
 *                  'awesomeTag',
 *                   function($model){
 *                      return "tag-{$model->id}";
 *                  }
 *              ],
 *             'keys' => [
 *                  'awesomeKey',
 *                  function($model){
 *                      return "key-{$model->id}";
 *                  }
 *              ],
 *             'fragments' => [
 *                  'awesomeKey',
 *                  function($model){
 *                      return "key-{$model->id}";
 *                  }
 *              ]
 *         ],
 *     ];
 * }
 * ```
 * @package common\behaviors
 */
class CacheInvalidateBehavior extends Behavior
{
    /**
     * @var string Name of cache componentj
     */

    public $cacheComponent = 'cache';
    /**
     * @var array List of tags to invalidate
     */
    public $tags = [];

    /**
     * @var array List of keys to invalidate
     */
    public $keys = [];

    /**
     * @var array List of pages fragments to invalidate
     */
    public $fragments = [];

    /**
     * @var
     */
    private $cache;


    /**
     * Get events list.
     * @return array
     */
    public function events()
    {
        return [
            ActiveRecord::EVENT_AFTER_DELETE => 'invalidateCache',
            ActiveRecord::EVENT_AFTER_INSERT => 'invalidateCache',
            ActiveRecord::EVENT_AFTER_UPDATE => 'invalidateCache',
        ];
    }

    /**
     * Invalidate cache connected to model.
     * @return bool
     */
    public function invalidateCache()
    {
        if (!empty($this->keys)) {
            $this->invalidateKeys();
        }

        if (!empty($this->tags)) {
            $this->invalidateTags();
        }

        if (!empty($this->fragments)) {
            $this->invalidateFragments();
        }

        return true;
    }

    /**
     * Invalidates
     */
    protected function invalidateKeys()
    {
        foreach ($this->keys as $key) {
            if (is_callable($key)) {
                $key = call_user_func($key, $this->owner);
            }
            $this->getCache()->delete($key);
        }
    }

    /**
     * @return \yii\caching\Cache
     */
    protected function getCache()
    {
        return $this->cache ?: Yii::$app->{$this->cacheComponent};
    }

    /**
     *
     */
    protected function invalidateTags()
    {
        TagDependency::invalidate(
            $this->getCache(),
            array_map(function ($tag) {
                if (is_callable($tag)) {
                    $tag = call_user_func($tag, $this->owner);
                }
                return $tag;
            }, $this->tags)
        );
    }

    /**
     * Invalidate pages fragments
     */
    protected function invalidateFragments()
    {
        foreach ($this->fragments as $key) {
            if (is_callable($key)) {
                $key = call_user_func($key, $this->owner);
            }

            $buldKey = ['yii\widgets\FragmentCache', $key];
            $this->getCache()->delete($buldKey);
        }
    }
}
код доработал что бы можно было фрагмент страницы удалять из кеша так как он не как простой ключ хранится



вот так использую если надо обновить кеш :

Код: Выделить всё

class ForumTopic extends \yii\db\ActiveRecord
{

... 

    /**
     * @inheritdoc
     */
    public static function tableName()
    {
        return 'forum_topic';
    }

    /**
     * @inheritdoc
     */
    public function behaviors()
    {
        return [
            'blameable' => [
                'class' => BlameableBehavior::className(),
            ],
            [
                'class' => SluggableBehavior::className(),
                'attribute' => 'name'
            ],
            [
                'class' => TimestampBehavior::className(),
                'updatedAtAttribute' => false,
                'value' => Time::getNowTime(),
            ],
            [
                'class' => CacheInvalidateBehavior::class,
                'tags' => [
                    'table.forum',
                    'table.forum_topic'
//                    function ($model,$test) {
//                        return "table.forum_topic-{$model->id}";
//                    }
                ],
                'fragments' => [
                    'index_page.new-topics-block'
                ]
            ],
        ];
        
        ...

вот так кеширую :

Код: Выделить всё


class ForumMessage extends \yii\db\ActiveRecord
{

...

/**
     * @return \yii\db\ActiveQuery
     */
    public function getTopic()
    {
        return $this->hasOne(ForumTopic::className(), ['id' => 'topic_id'])
            ->cache(2 * Time::SECONDS_IN_A_DAY, new TagDependency(['tags' => 'table.forum_topic']));
    }

...

и повторю
все нужно делать крайне осторожно и последовательно ибо легко потеряться в погоне кешировать всего и побольше
zxczxc12
Сообщения: 161
Зарегистрирован: 2013.01.24, 21:16

Re: Обновление кеша

Сообщение zxczxc12 »

А !

Забыл ответить на
>данные обновляются через updateAttributes()

ответ только один - переписывать на $model->update(false)
я после такого опыта с кешированием очень сильно пожалел что везде использовал этот сахарок :-)


>запросы напрямую к БД без AR

это не важно

Код: Выделить всё


$result = $db->cache(function ($db) {

    // ... выполнять SQL запросы здесь ...

    return $result;

}, $duration, $dependency);

Ответить