• Блог
  • Ошибки кэширования лексиконов

Абсолютно каждый разработчик, использующий MODX, периодически встречает в журнале ошибок записи в стиле «Error caching lexicon topic lexicon/ru/core/default», сигнализирующие об ошибке кэширования того или иного топика лексикона. Некоторых разработчиков эти ошибки беспокоят. Вопросы об этом можно найти в разных сообществах MODX. Есть даже обсуждение данного вопроса на GitHub.

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

В MODX, как и в других системах, встроен механизм кэширования для оптимизации нагрузки на сайт. MODX кэширует практически всё. У меня много информации по данной теме. Так вот, при первом обращении к сайту MODX сохраняет всю системную информацию в кэш — системные настройки, настройки контекста, настройки пользователя, медиа-источники, информация по автопубликациям, лексиконы. Для контекста mgr список дополняется меню и картой экшенов. Таким образом, при следующих обращениях MODX уже не нужно собирать заново эту информация из БД, что положительно сказывается на скорость.

И тут надо знать одну особенность — есть данные, для которых MODX генерит кэш полностью, а есть те, для которых кэш создаётся по мере обращения к сайту. Если мы заглянем в метод refresh класса modCacheManager, то увидим какие разделы перегенерируются — "auto_publish", "system_settings" и "context_settings". Таким образом, на момент запроса для этих данных кэш уже существует. Проверить это просто. Попробуйте очистить кэш в админке в меню «Управление/Очистить кэш», а затем откройте папку core/cache и убедитесь, что практически все разделы пустые кроме context_settings, system_settings и auto_publish (если у вас есть автопубликуемые ресурсы). Остальные разделы кэша будут наполняться данными в процессе использования сайта. Такая же логика используется при изменении системных настроек, настроек контекта, установке пакетов.

Теперь давайте всё-таки разберёмся, почему именно кэш лексикона вызывает проблемы? MODX поддерживает большое количество языков. И если мы зайдём в папку core/lexicon/ru, то увидим много файлов. Это файлы словарей, сгруппированных по определённому функциональному предназначению. Все они изначально предназначены для системы управления, в простонародье админки. Для одной страницы загружается несколько лексиконов. Но не это является причиной ошибок. Количество лексиконов влияет только на количество записей в журнале ошибок. Проблема в ассинхронности.

Интерфейс админки MODX очень сложный и содержит большой объем информации. И загрузить всю эту информацию в одном потоке очень проблематично. Особенно сильную нагрузку создают деревья. Чтобы избежать этого, используется асинхронная загрузка данных.

Откройте консоль разработчика и обновите страницу. На вкладке асинхронных запросов можно увидеть сколько их. А все эти запросы отрабатывают через процесссоры (мы сейчас говорим только о системных процессорах). И в каждом из них идёт загрузка одного, а часто нескольких лексиконов. В свою очередь, лексиконы относятся к той группе объектов, для которых кэш не пересоздаётся при очистке общего кэша. Он будет генерироваться при обращении к топику. Это логично. Зачем тратить время на кэширование всех топиков (коих большое количество), если для данного запроса нужно 5?

Так вот, чтобы отрендерить страницу в случае, когда кэш лексиконов пустой, MODX'у необходимо его создать для каждого запрошенного топика. И часто они повторяются. Но как мы уже выяснили, при открытии страницы на сервер летит несколько запросов — один основной и несколько асинхронных. А сервер получает их практически одновременно. Какой из них выполнится первым вряд ли кто ответит. Но в итоге получается так, что в двух, а может и больше, запросах проверка на существование кэша срабатывает отрицательно (потому что проверяется не существование файла, а наличие в нём данных) и они начинают процесс его формирования. А это процесс небыстрый. Нужно сделать запрос в БД, получить данные из файла лексикона, потом их слить вместе и сохранить в кэш. Т.е. получается такая ситуёвина — первый запрос начал процесс создания кэша — сформировал данные, создал файл, заблокировал в эксклюзивном режиме, может даже начал писать в него данные. Но в какой-то из этих моментов другой процесс пытается сделать тоже самое. Но заблокировать файл уже не может, так как первый запрос уже получил эксклюзивный доступ к файлу. А так как у блокировки указан ключ LOCK_NB, указывающий процессу не ждать освобождения блокировки и продолжать обработку дальше, то признак создания файла $written имеет значение false, что в итоге ведёт к возникновению той самой ошибки.

// Метод set() вернёт false
if (!$this->set($cacheKey, $entries, $lifetime, $options)) {
    $this->modx->log(modX::LOG_LEVEL_ERROR, "Error caching lexicon topic " . $cacheKey);
}

Таким образом, мы видим, что с функциональной точки зрения это никакая не ошибка. Ибо в итоге файл кэша будет создан. Это техническая ошибка. И было бы правильно отличать причины ошибки записи в кэш. Ведь разработчики сами указали режим LOCK_NB. В противном случае, скрипт второго запроса дождался бы разблокировки файла предыдущим запросом и переписал его контент аналогичной информацией. Что, конечно, не имеет смысла. Ну и остаётся ещё проблема дублирующих запросов в БД. Но для админки это не очень критично.

Надеюсь, я понятно объясняю? Данная информация будет полезной и сохранит нервы, бокалы и клавиатуру некоторым разработчикам. :)

П.С. На досуге попробую решить данную проблему. Но, скорее всего, уже в новом году.

1   727

Комментарии ()

  1. Александр 07 декабря 2020, 14:30 # 0
    Если я правильно понял, то решение только тебя волнует, а другие голосуют, чтобы заменить ошибку на предупреждение?
    Жаль, что если ты найдешь решение, то его могут и не принять, а может я ошибаюсь. В любом случае хорошая работа.
    1. Сергей Шлоков 07 декабря 2020, 17:07 # +1
      другие голосуют, чтобы заменить ошибку на предупреждение?
      Я тоже сначала думал также. Но разобравшись, считаю, что никаких предупреждений не нужно.

      Жаль, что если ты найдешь решение, то его могут и не принять
      99%:(

    Вы должны авторизоваться, чтобы оставлять комментарии.

    Выделите опечатку и нажмите Ctrl + Enter, чтобы отправить сообщение об ошибке.