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

Поясню: менеджер заводит скидку на товары, которая должна активироваться в 20:00 вечера, а деактивироваться в 3 часа ночи следующего дня. Если у компонента, который выводит товары каталога, в параметрах время жизни кеша большое, то цены со скидками в каталоге не появятся пока вы не сбросите кеш. Мой агент эту проблему решает. Можно заводить отложенные скидки и идти спокойно спать :)

Решил поделиться, так как этот кейс нередко просят добавить.

Так как алгоритм агента довольно прост, есть некоторые особенности для корректной работы агента (с настройками по умолчанию):

  • Агенты сайта должны работать по крону, с периодичностью в 1 минуту
  • Возможен оверхед на одно лишнее сбрасывание кеша, при активации/деактивации скидки

Если агенты сайта не на кроне, то агент сброса кеша будет срабатывать только в момент посещения пользователями сайта, что теряет смысл в его целесообразности.

Код агента

<?php
/**
 * Created by olegpro.ru.
 * User: Oleg Maksimenko <oleg.39style@gmail.com>
 * Date: 24.01.2016
 */
 
namespace Olegpro\Agents;
 
use Bitrix\Main\Config\Option;
use Bitrix\Main\Type;
use Bitrix\Main\Diag\Debug;
 
class CatalogDiscountAgent
{
 
    /**
     * @param int $minutes
     * @return string
     * @throws \Bitrix\Main\LoaderException
     */
    public static function clearExpiredCache($minutes = 1)
    {
 
        $minutes = abs($minutes > 0 ? $minutes : 1);
 
        $useLog = (Option::get('main', '~use_log_agent_catalog_discount_clear_cache', 'N') === 'Y');
 
        // Ids iblocks at which you need to clear the cache
        $iblockIds = array(
            1,
        );
 
        $needClear = false;
 
        if (\Bitrix\Main\Loader::includeModule('catalog')) {
 
            $fromDate = new Type\DateTime();
 
            $fromDate
                ->add(sprintf('-%s minutes', $minutes))
                ->add(sprintf('-%d seconds', $fromDate->format('s')))
            ;
 
            $toDate = new Type\DateTime();
 
            $toDate
                ->add(sprintf('+%s minutes', $minutes))
                ->add(sprintf('-%d seconds', $toDate->format('s')))
            ;
 
            // started
            /** @noinspection PhpDynamicAsStaticMethodCallInspection */
            $catalogDiscountIterator = \CCatalogDiscount::GetList(
                array('ID' => 'DESC',),
                array(
                    '>=ACTIVE_TO' => $fromDate,
                    '<=ACTIVE_TO' => $toDate,
                ),
                false,
                false,
                array('ID', 'NAME', 'ACTIVE_FROM', 'ACTIVE_TO')
            );
 
            if ($catalogDiscount = $catalogDiscountIterator->Fetch()) {
                $needClear = true;
 
                if ($useLog) {
                    Debug::writeToFile(
                        sprintf(
                            '%s',
                            print_r($catalogDiscount, 1) . PHP_EOL
                            . print_r(
                                array(
                                    '>=ACTIVE_TO' => $fromDate,
                                    '<=ACTIVE_TO' => $toDate,
                                ),
                                1
                            )
                        ),
                        sprintf('%s, %s - started "to"', __METHOD__, date('d-m-Y H:i:s')),
                        'agent_catalog_discount_clear_cache.log'
                    );
 
                    Debug::writeToFile(
                        sprintf(
                            '$needClear=%s, $catalogDiscount=%s',
                            var_export($needClear, true),
                            var_export($catalogDiscount, true)
                        ),
                        sprintf('%s, %s - started "to"', __METHOD__, date('d-m-Y H:i:s')),
                        'agent_catalog_discount_clear_cache.log'
                    );
                }
            }
 
            // finished
            /** @noinspection PhpDynamicAsStaticMethodCallInspection */
            $catalogDiscountIterator = \CCatalogDiscount::GetList(
                array('ID' => 'DESC',),
                array(
                    '>=ACTIVE_FROM' => $fromDate,
                    '<=ACTIVE_FROM' => $toDate,
                ),
                false,
                false,
                array('ID', 'NAME', 'ACTIVE_FROM', 'ACTIVE_TO')
            );
 
            if ($catalogDiscount = $catalogDiscountIterator->Fetch()) {
                $needClear = true;
 
                if ($useLog) {
                    Debug::writeToFile(
                        sprintf(
                            '%s',
                            print_r($catalogDiscount, 1) . PHP_EOL
                            . print_r(
                                array(
                                    '>=ACTIVE_FROM' => $fromDate,
                                    '<=ACTIVE_FROM' => $toDate,
                                ),
                                1
                            )
                        ),
                        sprintf('%s, %s - finished "from"', __METHOD__, date('d-m-Y H:i:s')),
                        'agent_catalog_discount_clear_cache.log'
                    );
 
                    Debug::writeToFile(
                        sprintf(
                            '$needClear=%s, $catalogDiscount=%s',
                            var_export($needClear, true),
                            var_export($catalogDiscount, true)
                        ),
                        sprintf('%s, %s - finished "from"', __METHOD__, date('d-m-Y H:i:s')),
                        'agent_catalog_discount_clear_cache.log'
                    );
                }
            }
 
        }
 
        if ($needClear) {
 
            // >= iblock 15.0.7
            if (method_exists('\CIBlock', 'clearIblockTagCache')) {
 
                \CIBlock::enableClearTagCache();
 
                foreach ($iblockIds as $iblockId) {
                    \CIBlock::clearIblockTagCache($iblockId);
                }
 
                \CIBlock::DisableClearTagCache();
            } else {
 
                BXClearCache(true);
 
                (new \Bitrix\Main\Data\ManagedCache())->cleanAll();
 
                (new \CStackCacheManager())->CleanAll();
            }
 
            if (
                method_exists('\CHTMLPagesCache', 'IsCompositeEnabled')
                && \CHTMLPagesCache::IsCompositeEnabled()
            ) {
                \CHTMLPagesCache::CleanAll();
            }
 
        }
 
        return '\\' . __METHOD__ . '();';
 
    }
 
}

Как обычно, копируем код класса, размещаем в удобном месте и добавляем в автозагрузку.

Если ваша версия модуля инфоблоков >= 15.0.7, то нужно в массив $iblockIds добавить ID инфоблоков, для которых вы хотите сбрасывать кеш. Иначе будет сброшен кеш всего сайта.

В коде класса агента оставлена возможность отладки. Активируется опцией (можно через коммандную php-строку битрикса):

\Bitrix\Main\Config\Option::set('main', '~use_log_agent_catalog_discount_clear_cache', 'Y');

После этого добавляем агент в систему (можно через коммандную php-строку битрикса):

\CAgent::AddAgent('\Olegpro\Agents\CatalogDiscountAgent::clearExpiredCache();', '', 'Y', 60, '', 'N', '');