Ошибки кэширования лексиконов
Абсолютно каждый разработчик, использующий 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. В противном случае, скрипт второго запроса дождался бы разблокировки файла предыдущим запросом и переписал его контент аналогичной информацией. Что, конечно, не имеет смысла. Ну и остаётся ещё проблема дублирующих запросов в БД. Но для админки это не очень критично.
Надеюсь, я понятно объясняю? Данная информация будет полезной и сохранит нервы, бокалы и клавиатуру некоторым разработчикам. :)
П.С. На досуге попробую решить данную проблему. Но, скорее всего, уже в новом году.
Комментарии ()
Вы должны авторизоваться, чтобы оставлять комментарии.
Жаль, что если ты найдешь решение, то его могут и не принять, а может я ошибаюсь. В любом случае хорошая работа.
99%