Вопрос продвинутым знатокам Cache API

Главные вкладки

Аватар пользователя marassa marassa 30 июня в 13:12

Так уж вышло, что мне нужно по одному и тому же URL генерировать немножко разные страницы в зависимости от наличия/отсутствия некоей куки. В результате пришлось отключить Internal Page Cache, так как закэшится первый запрошенный вариант страницы и будет далее выдаваться всем независимо от куки. Сайт по мере роста базы данных стал ощутимо тормозить, и очень хочется всё же найти способ задействовать страничный кэш.
Наткнулся на такую статью в которой предлагается простой способ модифицировать ядерный механизм кэширования, добавив нужную куку в Cache ID, то есть страница с кукой и страница без куки будут считаться разными страницами и соответственно кэшироваться раздельно, что мне и нужно.
Всё прочитал и понял, кроме последнего пассажа:

And the last thing is to add the cache context to your cache array

$build['#cache']['contexts' ][] = 'cookies:my_cookie_name';

Вопрос: КУДА этот код добавить-то? Наверняка ответ очевиден, но я туплю, а автор статьи не торопится отвечать.

Заранее премного.

Комментарии

Аватар пользователя OldWarrior OldWarrior 1 июля в 2:00

Ключевое слово тут $build. Т.е. этот элемент добавляется просто к любому возвращаемому из какого-то контроллера (страницы или формы) рендер-массиву.

Для контроллеров страниц возвращаемый массив обычно именуют $build, для форм - $form. В данном примере, судя по всему, контроллер страницы.

Собственно, ключ кеширования может применяться не только к верхнему уровню рендер-массива (например формы - $form), но индивидуально (и независимо от общего ключа) к отдельным её элементам, например, какому-то полю:

<?php
$form
["boxberry_address"]["#cache"] = [
      
"contexts" => [],
      
"tags" => [],
      
"max-age" => 0// Для примера отключаем кеширование только элемента "boxberry_address" в текущей форме.
  
];
?>

Т.е. тут вопрос в том, что именно хотите кешировать в выводимых данных.

Аватар пользователя marassa marassa 1 июля в 6:19

OldWarrior wrote: Т.е. тут вопрос в том, что именно хотите кешировать в выводимых данных.

Насколько я понимаю, Internal Page Cache кэширует целиком страницу. То есть в самом начале работы контроллера страницы нужно проверить нет ли уже в кэше страницы с этим адресом И ЭТОЙ КУКОЙ, и если есть, то просто отдать из кэша. А если нет, и дело дошло до рендеринга страницы, то после рендеринга нужно положить свежеотрендеренную страницу в кэш, причем с id, включающим куку.
Пока писал, осознал, что это ж у меня в БД будут храниться десятки тысяч готовых страниц. Может не зря Друпал рекомендует Internal Page Cache for small to medium-sized websites? Может всё же Varnish освоить? Там поддержка кук из коробки есть.

Аватар пользователя OldWarrior OldWarrior 1 июля в 11:05

marassa wrote: То есть в самом начале работы контроллера страницы нужно проверить нет ли уже в кэше страницы с этим адресом И ЭТОЙ КУКОЙ, и если есть, то просто отдать из кэша. А если нет, и дело дошло до рендеринга страницы, то после рендеринга нужно положить свежеотрендеренную страницу в кэш, причем с id, включающим куку.

Вот как раз для того, чтобы этим не заниматься и служит контекст кеша. Достаточно сообщить рендер-массиву зависимость контекста (в вашем случае от кук, хотя она может быть и от ID сущностей или ID термина или от ещё каких-то иных уникальных значений) и система (точнее, классы в обвязке сервиса \Drupal::renderer()) сама привяжет отображение к уникальному вхождению кеша на основании этой зависимости.

marassa wrote: Пока писал, осознал, что это ж у меня в БД будут храниться десятки тысяч готовых страниц.

На самом деле не совсем страниц, а результатов рендеринга build-массивов. Т.е., например, только отрендеренное тело ноды. Но вообще - да, каждый указанный ключ контекста (а он может быть не по одному параметру, как в вашем случае) добавляет в кеш свою группу к общему набору ключей контекста (или только их пересечениями/совокупностям). Т.е. если содержимое ноды чувствительно и к кукам и к параметрам, например, GET, и вы указали эту зависимость в кеш-контексте, то в конечном итоге будут построены вхождения для всех трёх случаев (по мере их рендеринга): для уникальных кук, для уникального параметра GET и для уникальных связок куки + GET.

Грубое объяснение, конечно, но примерно показывает картину.

Аватар пользователя OldWarrior OldWarrior 1 июля в 11:15

PS. Опять же, мы рассматриваем типичный случай, когда build-массив представляет содержимое, отдаваемое контроллером. Но всё вышенаписанное справедливо и для любых других рендер-массивов, например, блоков.

Аватар пользователя marassa marassa 1 июля в 11:46

OldWarrior wrote: Вот как раз для того, чтобы этим не заниматься и служит контекст кеша.

Есть только один нюанс: документация по cache contexts в явном виде говорит:

Note the Internal Page Cache assumes that all pages served to anonymous users will be identical, regardless of the implementation of cache contexts. If you want to use cache contexts to vary the content served to anonymous users, this module must be disabled, and the performance impact that entails incurred.

То есть Internal Page Cache (не путать с Dynamic Page Cache, который у меня включён и что-то не особо ускоряет) вообще игнорирует контексты. Тогда зачем их добавлять в билд-массив? Весь остальной код в статье модифицирует функциональность именно Internal Page Cache (page_cache).

Аватар пользователя OldWarrior OldWarrior 1 июля в 17:30

marassa wrote: То есть Internal Page Cache (не путать с Dynamic Page Cache, который у меня включён и что-то не особо ускоряет) вообще игнорирует контексты. Тогда зачем их добавлять в билд-массив?

Я сейчас смутно припоминаю: году так в 2016, когда D8 был свеж, как утренняя булочка из пекарни, мы вроде как парились на одном проекте с аналогичным вопросом. Вроде что-то кешировалось неуместным образом для гостей в форме на фронте. Не помню точно, как решилось тогда, но кажется, действительно выключили модуль Internal Page Cache.

Если вернуться к исходной причине ваших поисков:

marassa wrote: Так уж вышло, что мне нужно по одному и тому же URL генерировать немножко разные страницы в зависимости от наличия/отсутствия некоей куки. В результате пришлось отключить Internal Page Cache, так как закэшится первый запрошенный вариант страницы и будет далее выдаваться всем независимо от куки.

Для анонимов можно просто установить max-age => 0 в параметрах кеша билд-массива, это должно работать и с включенным Internal Page Cache, если не ошибаюсь. Правда, для авторизованных кеш тоже отключится. Но тогда можно попробовать сообщать разные параметры кеширования для массива в зависимости от признака: авторизован или нет. Просто как первая мысль.

Но в целом - кеширование по каким-то наиболее распространённым ключам контекста (а-ля uid) всё же более обосновано и имеет бОльшее значение именно для режима авторизованного пользователя. Как гарантия, что конфиденциальные значения/данные не попадут в общий (т.е. разделяемый) кеш. Потом, не забывайте про админку - там тоже нужен кеш на нагруженных страницах (представления, списки и т.д.). И опять же - зависимый от роли пользователя.