[spoiler]
В первую очередь решалась задача сброса кеша компонентов инфоблоков при изменении информации в них. Важным условием являлось отсутствие модификации самих компонент. И эта задача была решена.
Сначала посмотрим "базовый" код тегирования кеша.
01: $cache_id = md5(serialize($arParams)); 02: $cache_dir = "/tagged_getlist"; 03: 04: $obCache = new CPHPCache; 05: if($obCache->InitCache(36000, $cache_id, $cache_dir)) 06: { 07: $arElements = $obCache->GetVars(); 08: } 09: elseif(CModule::IncludeModule("iblock") && $obCache->StartDataCache()) 10: { 11: $arElements = array(); 12: $rsElements = CIBlockElement::GetList($arParams["order"], $arParams["filter"]); 13: 14: global $CACHE_MANAGER; 15: $CACHE_MANAGER->StartTagCache($cache_dir); 16: while($arElement = $rsElements->Fetch()) 17: { 18: $CACHE_MANAGER->RegisterTag("iblock_id_".$arElement["ID"]); 19: $arElements[] = $arElement; 20: } 21: $CACHE_MANAGER->RegisterTag("iblock_id_new"); 22: $CACHE_MANAGER->EndTagCache(); 23: 24: $obCache->EndDataCache($arElements); 25: } 26: else 27: { 28: $arElements = array(); 29: } |
В строке 01 ининциализируется уникальный идентификатор файла кеша. Далее определяется каталог относительно /bitrix/cache в котором будут сохранятся файлы кеша с разными значениями $arParams. Важно, что этот путь начинается со слеша и им не заканчивается. При использовании в качестве кеша memcached или APC это будет критичным при сбросе кеша.
В строках 04-05 инициализируется объект кеша. Если время кеширования не истекло, то будет выполнена строка 07 и мы получим данные из файла кеша.
Условие в строке 09 фактически всегда будет true. Это подключение модуля и начало кеширования.
В строке 12 происходит обращение к базе данных. Важно, чтобы все параметры от которых зависит результат выборки "поучаствовали" в идентификаторе кеша ($cache_id).
В 14-й строчке объявляется доступ к переменной $CACHE_MANAGER. Этот объект будет управлять тегами.
15 - Все последующие назначаемые теги будут привязаны к каталогу $cache_dir. и при сбросе кеша по одному из них содержимое этого каталога будет удалено. StartTagCache - может использоваться рекурсивно. Например:
$CACHE_MANAGER->StartTagCache($cache_dir1); $CACHE_MANAGER->StartTagCache($cache_dir2); $CACHE_MANAGER->StartTagCache($cache_dir3); $CACHE_MANAGER->EndTagCache(); $CACHE_MANAGER->EndTagCache(); $CACHE_MANAGER->EndTagCache(); |
Важно чтобы вызовы StartTagCache и EndTagCache были сбалансированы. Объект $CACHE_MANAGER создает и отслеживает стек каталогов кеша.
При этом теги назначенные на каталог $cache_dir3 будут также связаны и с $cache_dir2 и $cache_dir1. Теги назначенные на $cache_dir2 будут связаны и с $cache_dir1.
В строке 18 происходит отметка кеша тегом с помощью метода RegisterTag. Длина тела может быть до 100 символов. В методе RegisterTag автоматически удаляются дубликаты тегов.
22 - запись тегов каталога в таблицу базы данных. Можно считать оп одному insert'у на тег.
Вот так можно сбросить кеш:
$CACHE_MANAGER->ClearByTag("iblock_id_7"); |
Это были технические детали реализации. Вернемся к компонентам инфоблоков.
Для запуска механизма необходимо определить константу в файле dbconn.php
define("BX_COMP_MANAGED_CACHE", true); |
При этом в методе StartResultCache компонента будет вызываться StartTagCache с путем к кешу компонента (с учетом страницы).
А в методе EndResultCache (который в свою очередь вызывается из IncludeComponentTemplate) - EndTagCache.
В модуле инфоблоков CIBlockElement::GetList и CIBlockSection::GetList возвращают объект класса CIBlockResult.
В методе Fetch/GetNext этого объекта будут вызываться $CACHE_MANAGER->RegisterTag("iblock_id_".$res["IBLOCK_ID"]);.
Если выборка не содержит элементов, то значение идентификатора инфоблока будет взято из фильтра.
Сброс кеша вызывается из методов Add/Update/Delete для элементов, разделов и инфоблоков.
На данный момент у нас нет данных по всем плюсам и минусам использования этого механизма. Можно предположить, что не стоит использовать этот механизм при частом обновлении элементов или разделов. С другой стороны это должно быть удобным для редакторов сайтов. Добавил новость - вот она. Еще один плюс - можно задавать практически бесконечное время кеширования.
Ждем ваших пожеланий и отзывов.
Я следил за обсуждениями в группе, но мне кажется итоговое решение получилось немного нагроможденным.
Основную сложность я вижу в StartTagCache с $cache_dir.
Как программисту мне должно быть все-равно что там и где хранится
В том смысле, что это CMS, а не с++ программа где нужно следить за памятью и т.п.
В идеале хотелось бы видеть $obCache->setTag('iblock_id'.$res["IBLOCK_ID"]); и всё.
А для компонентов $this->setCacheTag('iblock_id'.$res["IBLOCK_ID"]).
Проставление тегов из CIBlockElement::GetList я бы наверное вообще убрал, т.к. это не всегда нужно.
В данном случае, необходимо четко представлять где будут храниться файлы кеша. Т.к. сброс происходит директориями, а файлами. Это сделано для снижения накладных расходов собственно тегирования.
Про "не всегда нужно" - может быть попробуем разделить инфоблоки на тегирующие и нет.
Вопрос:
Если ее не устанавливать, то механизм тегов не будет работать вообще, или только не будет работать в стандартных методах инфоблоков, а вручную можно будет использовать?
Другими словами, без данной константы код в посте будет работать?
Так как там работает кеш с тегами?
Что не учитывается, но в идеале не помешало бы:
- при выборке полей типа PROPERTY_<PROPERTY_CODE>.<FIELD> не добавляется кэш-тег для инфоблока связанного элемента;(Максим ответил, что поправят)- не сбрасывается кэш, при изменении цен и свойств отдельно от элемента. При импорте, например, это не помешало бы.
Еще не помешал бы дополнительный параметр для Fetch(), а также GetNext() и GetNextElement(), запрещающий добавление кэш-тега. Наличие константы это хорошо, но нужен менее глобальный выключатель.
Еще будет не лишней возможность отложенного сброса кэша в прологе и контрольным на register_shutdown_function или деструкторе (в зависимости от реализации). При массовых операциях сэкономит немножко времени.
В остальном - все супер!
пожалуйста.
все они отображаются на сайте компонентом catalog.section на разных страницах.
При добавлении нового учащегося кэш на всех этих страницах потрется или только на странице где выводятся учащиеся?
Правильно я понял что кэш теперь будет храниться не в папках для каждого компонента:
/catalog.section/сериализованный_массив1/
/catalog.section/сериализованный_массив2/
/catalog.section/сериализованный_массив3/
...
/catalog.element/сериализованный_массив4/
/catalog.element/сериализованный_массив5/
...
а от разных компонентов в одних папках (тегах):
/$cache_dir1/сериализованный_массив1(catalog.section)/
/$cache_dir1/сериализованный_массив2(catalog.element)/
/$cache_dir1/сериализованный_массив3(catalog.section)/
...
/$cache_dir2/сериализованный_массив5(catalog.section)/
/$cache_dir2/сериализованный_массив6(catalog.element)/
/$cache_dir2/сериализованный_массив7(catalog.section)/
...
?
А как теперь(цитирую дословно)
Может, проще было бы добавить в классы CPHPCache и CPageCache убивалку кэша по его ID??? Зачем вообще эти тэги? У кэша есть ID! Код (не важно, отдельный он или внутри компонента) создает кэш и его ID нам известен. У инфоблока методы Add/Update/Delete по известному имени кэша убивают его, если данные меняются.
Проблема названия/передачи/формирования ID кэша в этом случае, конечно, вопрос открытый. Можно, например, в настройках инфоблока сделать поле ввода списка ID, которые, скажем, при изменении хотя бы одного элемента в инфоблоке, приказано уничтожить, т.к. они отражают кэш выборки элементов.
В Add/Update/Delete сбрасывается кеш, если вы используете встроенное кеширование ($this->StartResultCache )
Если вы используете CPHPCache, то создание кеша (в том числе тэгированного) и его чистка, полностью ваша задача.
Установите только константу в dbconn.php:
define('BX_COMP_MANAGED_CACHE', true);
Тег устанавливается по ID инфоблока "iblock_id_".[код инфоблока], а не по коду элемента "iblock_id_".$arElement["ID"];
$CACHE_MANAGER->RegisterTag("iblock_id_new");
А еще, если я возьму и сотру каталог /bitrix/cache что будет? Записи в таблице ведь останутся. И она не чистится пока, насколько я могу понять.
У нас используется на сайте в боковой колонке список последних обновлённых тем форума (сквозное размещение). Ранее обработчиком легко обновляли эту информацию на всех страницах единовременно - кеш-то был единый. Теперь этой возможности нет, на каждой странице сайта отдельный кеш компонента. Тысячи кешей одного компонента, можете представить себе "рост" производительности, ведь теперь нужно генерировать кеш на каждой странице. Можете также представить себе объём кеша!
Жаль, что вместе с "лекарством" Битрикс забывает выпускать инструкцию, содержащую информацию о "противопоказания". А противопоказания в этой ситуации для многих сайтов значительно превышают лечебный эффект.
Жесть, одним словом, просто жесть!
А то уж два месяца, как выпущена эта технология, а её до сих пор нельзя ставить на рабочий сайт.
А именно, в cache_id используется $arNavigation, в котором, в свою очередь, используется текущий URL, поэтому если компонент помещен в шаблоне сайта, то на каждой странице будет свой кэш. Подскажите, не планируется ли это поведение исправить в ближайших обновлениях?
вот содержимое этого массива:
Текущий url не используется.
Антон ещё полгода назад об этом написал. Хотелось бы как-то форсировать исправления!
/bitrix/modules/main/classes/general/database.php
$sNavID не передается, а вот $APPLICATION->GetCurPage() (замечу что это не url) можно упростить до:
Это и будет постраничка на странице. Резюмируя скажу: на данный момент код не поддерживает ЧПУ.
Будем думать и исправлять. Расчитывать можно на main 9.5.1
Там содержится исправление?
Соответственно получить:
- Fetch по блоку даст тэг только блока
- Fetch по секции даст тэг только секции
- Fetch по элементу даст тэг только элемента
- аналагичные разграниченные обработки Add/Update/Delete по очистке кэша только тэга элемента, или только секции, или только блока.
А то убивать кэш всего инфоблока (читай на всем сайте всех компонентов по работе с данным инфоблоком) из-за правки одного элемента совсем печально при больших объемах.
От него отказались из-за огромного количества возникающих зависимостей.
При больших объемах фича должна быть однозначно отключена.
Но при этом остается АПИ при грамотном использовании которого вы все сможете реализовать сами в нужных вам объемах.
Те чтобы отключить фичу, но использовать свой тэгированный кэш, нужно в админке выключить управляемый кэш и убрать определение константы BX_COMP_MANAGED_CACHE из db_conn.php
Но при этом:
CSiteStore::ShowPanel() принудительно включает @define("BX_COMP_MANAGED_CACHE", true);
CCacheManager::CleanAll() не дает чистить тэгированный кэш без обьявленной константы if(defined("BX_COMP_MANAGED_CACHE")) $this->ClearByTag(true);
Ну а после нам останется переписать стандартные компоненты под свои нужды, используя что-то типа
StartTagCacheComponent($this->__cachePath)
AbortTagCacheComponent()
EndTagCacheComponent()
RegisterTagIBlockElement($arItem)
ClearTagIBlockElement($arItem)
А приведенный еще ниже план действий прекрасен. Именно так я бы и поступил (только имена функций не распознаю).
Следовательно при сбросе кэша тэга, образованного из любого компонента, происходит сброс кэшей обоих вызовов компонетов, а не того, в котором был зарегистрирован тэг.
Привязываем тэги элементов не к директории кэша, а непосредственно к файлу кэша. Благо можно вытащить из объекта компонента экземпляр объекта кэширования. А из него (или прямо из него, если идет простое CPHPCache) получить файл кэша можно.
Очистка кэша происходит все равно в итоге по DeleteDirFilesEx, те файлы чистятся (хотя директории остаются).
В итоге всякие показатели голосований, рейтингов, комментариев на "простых" списковых компонентах тех же новостей показываются адекватно.
CCacheManager::Get($uniqid)
CCacheManager::GetCompCachePath($relativePath)
$CACHE_MANAGER->RegisterTag("iblock_id_".$arElement["ID"] ) ;
а должно быть
$CACHE_MANAGER->RegisterTag("iblock_id_".$arElement["IBLOCK_ID" ] );
Вот есть у меня набор элементов инфоблока.
Значения свойств элемента инфоблока в определенный момент времени могут быть обновлены (программно с помощью апи).
Вот как сбросить тегированный кеш только! для данного элемента? Не сбрасывать же кеш инфоблока ради одного только элемента (точнее из-за каждого элемента) - иначе смыла в кешировании не вижу....