Перейти к содержимому

Тегированный кэш - сброс по изменению данных, а не по расписанию

Сбрасываем кэш по изменению данных, а не по расписанию: управляемый кэш, теги, регистрация тега в своём коде и привязка к таблице.

Что нужно знать заранее

Обычный кэш живёт до конца своего срока и о правке данных ничего не знает. Поэтому редактор видит старую страницу до истечения срока, а короткий срок жизни кэша обесценивает саму идею кэширования.

Тегированный кэш решает это связью «кэш - источник данных». Кэш регистрирует тег инфоблока или своей таблицы, а изменение данных сбрасывает только те записи, где этот тег зарегистрирован.

Теги работают лишь при включённом управляемом кэше. Без объявленной константы регистрация тега и сброс по тегу выполняются молча и не дают никакого эффекта.

Шаги

  1. Включить управляемый кэш константой в файле подключения к базе данных.
  2. Обернуть кэшируемый участок кода началом и завершением работы с тегами.
  3. Зарегистрировать теги источников, от которых зависит содержимое участка.
  4. Сбросить кэш по тегу там, где данные меняются своим кодом.
  5. Проверить связь: правка элемента должна очищать только связанные страницы.

Решение

Включаем управляемый кэш:

/bitrix/php_interface/dbconn.php
define('BX_COMP_MANAGED_CACHE', true); // без этой строки теги не работают
// после правки файла кэш сбрасывают целиком один раз

Константа включает управляемый режим для компонентов и своего кода сразу. Проверить её проще всего выводом на служебной странице: строка «объявлено» означает, что дальше можно рассчитывать на теги.

Регистрируем тег вокруг своего кэша:

$app = \Bitrix\Main\Application::getInstance();
$taggedCache = $app->getTaggedCache();
$taggedCache->startTagCache('/vendor/promo'); // каталог кэша участка
$taggedCache->registerTag('iblock_id_' . $iblockId); // источник данных участка
$taggedCache->endTagCache();
// теги регистрируют внутри участка, между началом и завершением работы с кэшем

Регистрация тега говорит платформе, от чего зависит этот кусок кэша. Тегов может быть несколько: страница с товарами и баннерами регистрирует оба инфоблока и очищается при правке любого из них.

Сбрасываем кэш по тегу из своего кода:

$taggedCache->clearByTag('iblock_id_' . $iblockId); // очистка связанных записей
// вызывают там, где данные меняются мимо штатных методов платформы

Сброс по тегу нужен своему коду, который пишет данные напрямую. Штатные методы инфоблоков и торгового каталога делают это сами, а прямой запрос к базе - нет.

Связываем управляемый кэш со своей таблицей:

$managed = $app->getManagedCache();
if ($managed->read(86400, 'vendor_rates', 'vendor_rates_table')) { // третий аргумент - таблица
$rates = $managed->get('vendor_rates');
} else {
$rates = VendorRatesTable::getList()->fetchAll(); // тяжёлая выборка справочника
$managed->set('vendor_rates', $rates); // запись только после чтения
}

Третий аргумент связывает запись кэша с таблицей и включает автоматическую очистку при изменении её данных. Без него запись живёт своей жизнью и чистится только вручную или по сроку.

Сбрасываем тег из обработчика события:

$em = \Bitrix\Main\EventManager::getInstance();
$em->addEventHandler('vendor', 'onAfterRateSave', static function () {
\Bitrix\Main\Application::getInstance()->getTaggedCache()
->clearByTag('vendor_rates_table'); // сброс сразу после записи данных
});

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

Прерываем запись кэша при ошибке:

if ($response->getStatus() !== 200) {
$cache->abortDataCache(); // в кэш не должен попасть неполный ответ
return [];
}

Прерывание защищает от худшего сценария: пустой ответ чужого сервиса, сохранённый на сутки. Завершать запись кэша после ошибки нельзя, даже если данные выглядят почти полными.

Типичные проблемы

Теги зарегистрированы, а кэш по правке не сбрасывается.

Управляемый кэш не включён константой в файле подключения к базе. Без неё регистрация тега и сброс по тегу выполняются вхолостую.

Запись управляемого кэша не очищается при правке таблицы.

При чтении не передана таблица-источник третьим аргументом вызова. Тогда запись не связана с таблицей и живёт до ручной очистки.

В кэше оказался пустой ответ внешнего сервиса.

Запись кэша завершена штатно, несмотря на ошибку запроса. При любой ошибке запись прерывают, а не сохраняют неполные данные.

Каталог кэша меню разрастается до гигабайтов.

Кэш меню хранится отдельным файлом на страницу, тип меню и группу прав. Тяжёлые запросы выносят из шаблона меню, а лишние элементы не кэшируют.

Кэш перегенерируется на каждом запросе.

В ключ кэша попали данные, разные у каждого посетителя: сеанс, адрес или браузер. Ключ собирают только из параметров, влияющих на содержимое участка.

Частые вопросы

Чем тегированный кэш отличается от обычного?

Обычный живёт до конца срока, тегированный сбрасывается при изменении источника. Второй позволяет ставить длинный срок жизни без риска показать старые данные.

Нужна ли константа на всех проектах?

Она нужна везде, где рассчитывают на сброс по тегам, включая штатные компоненты каталога. Без неё длинный срок жизни кэша становится опасным.

Какие теги регистрируют для инфоблока?

Тег инфоблока по его идентификатору, а при зависимости от одного элемента - тег элемента. Штатные компоненты регистрируют такие теги сами.

Почему кэш не сбросился после прямого запроса к базе?

Прямой запрос не проходит через методы платформы и событий не поднимает. Сброс по тегу в таком коде вызывают руками, сразу после изменения данных.

Что делать со старым кодом на прежних классах?

Он продолжает работать, но в новом коде берут классы ядра для кэша, управляемого кэша и тегов. Смешивать подходы в одном участке не стоит.

Смежное

Первоисточник