Недавно был анонс Grav CMS 2.2, который отличается от всех остальных тем, что автор системы (Andy Miller), наконец-то озаботился об её оптимизации. Мне понравилось, что он привёл цифры, которые показывают реальное положение дел. Когда я писал, что подобные CMS могут потреблять более 100Мб php-памяти, то на это мало кто отреагировал. Но цифры Grav CMS ещё более пугающие — потребление до 242Мб (!!!) на 10k записях сайта.
Меня интересуют flat-file system в первую очередь в сравнении с Albireo CMS, которая на сегодняшний день самая быстрая и экономная система, работающая на PHP. Всё дело в архитектуре и алгоритмах, которые недоступны авторам других CMS.
Дисклеймер
Хотя я пишу несколько саркастически, всё-таки я считаю, что Grav, PureBlog и другие flat-file системы — это очень хорошая тенденция. Не важно, что у них не всё гладко, авторы реализуют проекты в меру своей компетенции и опыта. Лично я за максимальную «движуху» среди CMS, поскольку для нас всех враг только один — это WordPress. Из-за огромного количества денег, он мешает нормально развиваться другим системам. Его агрессивная политика не позволяет пользователям сделать адекватный выбор других, но уже нормальных CMS. WordPress — это Internet Explorer 5, который очень негативно сказался на развитии качественных сайтов.
Основные цифры Grav
Я буду ориентироваться на цифры, приведенные в статье анонса. Они, конечно, представляют интерес только для тех, кто понимает что там вообще происходит. Поэтому я постараюсь остановится только на самых важных моментах.
Старые версии Grav даже на моём локальном тестировании показывали безумное потребление php-памяти. Скажем буквально 10 страниц потребляли 30Мб памяти. Но тут нужно оговориться: речь о т.н. «холодном запуске — это состояние системы, когда еще не построен кэш. Это же состояние происходит сразу после сброса кэша — он начинает свои построения и результат и есть оценка ресурсопотребления и скорости.
На 10к страниц потребление было 242Мб. При этом время, если я верно понял примерно 4-5 секунд.
Новые версии Grav 2.2 показывают, что память уменьшилась до 9.6Мб, а скорость увеличилась примерно в два раза до 2,3 секунд.
Теперь сравним с Albireo CMS: 4.2s/6.75Mb (тоже 10к страниц).
Но здесь есть важное различие. Автор Grav тестировал на локальном компьютере Apple M4 Max, я же на старом ноуте i5-10310U. Ради интереса я поискал сравнение производительности и M4 Max будет по самым скромным тестам как минимум в 3 раза быстрее. В отдельных случаях он будет в 7-10 раз быстрее.
Поэтому реальный цифры следует смотреть уже на сервере хостинга, но я думаю, что там скорость будет в 5-10 раз быстрее моего ноута.
В общем-то это так и есть, мой хостинг показывает многократно меньшие цифры, чем мой локальный сервер.
Поэтому, если попробовать интерполировать данные, то скорее всего Albireo CMS покажет скорость примерно на том же уровне - 1..2 секунды.
Скорость будет зависеть от кол-ва файлов
Дело в том, что для любой flat-file system работа с файловой системой будет бутылочным горлышком. Скажем так: чтение файлов в PHP в любой CMS выполняется плюс-минус на одном уровне. PHP сам оптимизирует работу с файловой системой и когда фалов много, то их скорость чтения будет зависеть уже не от PHP, а от физического диска, даже если это SSD. Здесь всё упирается именно в количество файлов.
Поэтому, если CMS не совсем криворукая, то скорость чтения множества файлов будет примерно на одном уровне. Выше прыгнуть просто невозможно из-за аппаратных ограничений.
Память
С потреблением памяти всё интересней.
Основная проблема в том, что Grav (да и другие системы) пытаются хранить все файлы в php-массиве, на что требуется много памяти. Классически это выражается в линейной зависимости потребления памяти от количества записей (точнее от суммарного объема всех записей).
Именно поэтому 242Мб — это «типовой» показатель любой подобной системы. На сервере обычно ограничение в 256Мб — поэтому просто не получится создать более 10к страниц. Например я тестировал Albireo CMS c 18к страниц — для Grav старой версии — это недостижимый результат. Тут придётся повышать лимиты PHP.
Новая версия Grav 2.2 потребляет уже 9.6Мб — резкий скачок! За счёт чего? Сюрприз: они стали использовать SQLite как «прослойку» кэша!
Для сравнения — кэш SQLite появился более 5 лет назад ещё в Albireo Framework, а в Albireo CMS — он является неотъемлемой частью системы.
Поэтому выбор SQLite правильное решение, но проблема в том, что Grav его использует только для сайтов от 1000 записей. Это уже странно. Вместо того, чтобы сделать это дефолтной опцией, он вводит искусственное ограничение на количество записей. Да, файловый кэш на небольшом количестве файлов окажется быстрей, но поддержка двух систем кэширования — это тупик в развитии.
Память после кэша
Оптимизация всё равно не снизила общее потребление памяти. Память всё равно будет зависеть от количества и размеров страниц, поэтому для 1К это всё равно 7.6Мб php-памяти.
Для сравнения в Albireo CMS это всего 2.59Mb, причём большая часть этой цифры — это модули самого PHP. То есть на моём сервере память оказывается в пределах 0.2-0.8Мб и практически не зависит от количества и размера файлов записей.
Проблема Grav в его архитектуре и некачественном php-коде.
Игры с OPCache
Вот это тоже интересно. Если я верно понял, то Grav теперь пытается управлять кэшем PHP (OPCache).
Для ясности — OPCache — это особый внутренний кэш PHP-кода. Поскольку язык интерпретируемый, то каждый запуск php-кода проходит через несколько стадий: синтаксис, проверка путей, подключений, трансляция в байт-код и только потом запуск и выполнение. С помощью OPCache можно закэшировать php-файл на уровне байт-кода, и тогда это позволит его повторно запустить быстрей.
В PHP OPCache может работать на уровне функций и файлов. Я наигрался этой возможностью много лет назад и понял, что лучше его вообще не касаться. Дело в том, что когда вручную управлять OPCache, то может возникнуть ситуация, когда вы будете всегда получать старые данные или файлы...
Более того, есть сложность в том, как именно работает PHP на сервере, особенно на простом виртуальном хостинге, где куча самых разных php-проектов. Скажем сброс OPCache приведёт к сбросу кэша всех сайтов сервера. Если система настроена на такое поведение, то оно становится непредсказуемым.
Поэтому OPCache хорош только в особых случаях и только на своём выделенном сервере, где вы сами себе хозяин.
Что сделал автор Grav? Судя по всему он понял, что построение кэша не только затратно по памяти и времени, но и по ресурсам CPU (процессоры по сути). Если идет большой трафик, скажем 10 одновременных посетителей, то они могут просто «положить» Grav-сайт, поскольку большая память (а она большая даже после построение кэша) косвенно указывает о том, что php сильно «греется».
Поэтому OPCache вроде как позволяет сгладить эту пиковую нагрузку. Проблема в том, что PHP в разных версиях ведет себя по разному, но если брать последние версии (8.3+), то такой кэш будет работать сам по себе в дефолтных настройках (как правило). На практике этот кэш будет работать в течение пары секунд. Поэтому когда идёт большая атака, то PHP и сам прекрасно справится. Особенно, если хостинг использует «лёгкие» сервера, вроде LiteSpeed.
Ну и не стоит забывать, что PHP использует JIT-компиляцию, где OPCache совсем уже не к месту. Поэтому я считаю, что серверные проблемы лучше оставить серверу.
Другие цифры
Из-за того, что админ-панель Grav теперь представляет собой некую «оболочку» над API, то очевидно, что скорость работы админ-панели сильно просядет. Если у вас будет мобильный Инет с низкой скоростью, то работать с такой панелью, напичканной аякс-запросами будет невыносимо.
Остальные цифры не представляют особого интереса, поскольку больше похожи на внутреннюю трассировку запросов, чем на реальные цифры ресурсопотребления (то есть как минимум еще и память нужно указать).
PHP-сессии
Наверное, Grav столкнулся с той же проблемой, что и я раньше. Создание сессий — это всегда было основным «классическим» приёмом для php-разработчиков. Но, поскольку сейчас трафик возрос лавинообразно, то файлы сессий создаются в огромном количестве. А это уже проблема, поскольку есть лимиты на иноды, которые пробьются за несколько дней.
Поэтому в Grav решили, что сессии будут только для авторизованных юзеров. То есть они не отказались от сессий, а просто ограничили их. И это плохое решение.
Плохо это из-за того, что сессии нужны не просто так. В первую очередь это защитные токены, которые просто обязаны быть для любого посетителя, иначе невозможно будет понять кто и как оправляет формы.
В Albireo CMS я перешел на куки, что совершенно безопасно для посетителей, а также позволяет сохранить все защитные механизмы. В Grav, похоже, ещё не осознали эту проблему.
Проблемы архитектуры
Здесь я немного затрону слабые стороны.
Когда мы говорим про кэширование, то на самом деле в Grav — это несколько уровней, которые следует разделять.
Первый уровень — это файловый кэш. Не важно как именно работает система, файлы — это узкое горлышко, а значит список файлов нужно закэшировать. Именно об этом кэше и идёт речь выше. То есть система смотрит список файлов, сверяет их со старым, делает различие, сохраняет в кэше. Когда идет второй запрос, то данные сразу берутся из кэша. Отсюда и возникает эффект скорости.
Но, если вы возьмёте не просто 10к файлов записей, а так, чтобы их суммарный размер превысил, скажем 300Мб, то скорее всего сайт «загнётся» из-за php-лимитов. Всё дело в том, что такие системы хранят в кэше все данные страниц, а значит каждая страница будет отнимать php-память примерно равную своему объему.
Поэтому, когда я тестирую 10к страниц Albireo CMS, то сразу указываю, что это примерно 471 Мбайт суммарного объемы. Это намного больше размера кэша и php-лимитов. Автор Grav не указывает какой объем файлов был в тестировании, чтобы понять насколько оптимизирован его кэш.
Второй уровень — это кэш Twig. Twig — это php-шаблонизатор, который используется как основной для Grav. То есть когда md-файл считан и обработан, то запускается Twig, который уже сам сохраняет свой кэш. Этот кэш состоит из собранных php-файлов, которые по сути и выполнятся при выводе страницы. Twig — редкое «говнище», но довольно популярное. Поэтому Grav, пока не откажется от этого, не имеет никаких шансов работать как качественное php-приложение.
Еще пару слов о Twig
В ранних версиях (до 2.0) шаблонизатор работал на всю страницу сразу. То есть в md-файле можно было использовать вставки {{ код }} и система его выполнит.
Но в новых версиях в md-файле нельзя больше использовать шаблонизатор, поскольку для Grav — это проблема безопасности.
Здесь я просто хочу показать принципиальную разницу в доверии между Grav и Albireo CMS. У меня пользователь — доверенное лицо, он может писать не только текст, но и выполнять произвольный php-код (а также html/css/js). В Grav же пользователь изначально предполагается как криворуким, безмозглым и мазохистом, желающим себе навредить. Поэтому Grav ему не просто не доверяет, но и блокирует любые его попытки написать работающий код.
Меня позабавили лозунги автора:
The problem is that page content is user-authored. (Проблема в том, что контент страниц создается пользователями.)
Grav 2.0 draws a hard line. Content is content. Code is code. (Grav 2.0 проводит четкую границу. Контент — это контент. Код — это код.) цитата
Самое забавное, что я тоже считаю, что «Content is content. Code is code», но понимаю это совсем по другому. Код, например PHP пользователь может использовать, но просто указывает это как положено в виде <?php код ?>. Контент отделяется от кода, но при этом пользователь имеет право делать что ему вздумается. С безопасностью это вообще никак не связано.
Помимо того, что это принципиально разный подход, это ещё и констатация того факта, что из-за неудачной архитектуры, Grav не может позволить выполнять код своим пользователям, прикрываясь красивыми лозунгами. 😉
Markdown как Parsedown
Parsedown — это известная php-библиотека, которую используют все, кому лень писать свой md-парсер. Parsedown сам по себе написан очень «своеобразно» и, если когда-то он и правда выполнял свою роль, то сейчас использование этой библиотеки выглядит очень странно. Ему более 10 лет и главная проблема в том, что его алгоритм обработки текста давно уже морально устарел.
Но в Grav решили сделать форк этого недоразумения и добавили пару своих отсебячек.
Лично моё мнение такое: если система использует Parsedown, то её нужно обходить стороной.
Для уточнения: Albireo CMS использует собственный парсер TextSimple.
Итого
В своём развитии и возможностях Grav сильно уступает Albireo CMS и без смены архитектуры он никогда не получит таких же результатов. Но мне нравится, что хоть какое, но развитие есть. За Grav стоят какие-то деньги, компании, предлагающие свои услуги. Так что спонсоры есть, а сейчас это важно.