Плагин быстрой генерации постов KAGG Fast Post Generator — новый инструмент, позволяющий создать 1 миллион рандомных постов за 2 минуты. К написанию его подтолкнула необходимость проводить тестирование продукта под WordPress на больших базах данных, содержащих сотни тысяч и миллионы постов/страниц.
Существующие средства не позволяют создать нужное количество рандомных постов за приемлемое время. Например, плагин FakerPress создаёт примерно 1,000 постов в час. Пакет wpastronaut/wp-cli-seeder работает существенно быстрее, 1000 постов за 35 секунд. Один миллион постов это средство создаст примерно за 10 часов. Если вам нужна база, содержащая миллионы и десятки миллионов постов, то у вас проблемы.
Чем достигается такая скорость? В плагине использована методика быстрой загрузки .csv файлов через команду LOAD DATA INFILE. Это основная идея кода. Плагин создаёт временный .csv файл, записывая в него пачку постов (размер по умолчанию — 50,000 постов) и затем загружает их в базу одной командой MySQL. На создание и запись 50,000 постов средствами PHP во временный файл уходит примерно 1 секунда, и примерно 5 секунд — на загрузку этих 50,000 постов в базу одной командой MySQL. При задании сгенерировать 1,000,000 постов, плагин разбивает это задание на куски и выполняет по умолчанию 20 ajax-запросов.
Так выглядит страница настроек и результат работы плагина.
Видим, что плагин создал 1 миллион постов за 3 минуты. На моей тестовой машине скорость несколько ниже, чем на Digital Ocean сервере начального класса. Там аналогичная процедура занимает 2 минуты. Размер базы до генерации — 250 MB, после генерации — 2.9 GB.
Плагин быстрой генерации постов — оптимизация
Для своих тестов я создавал базу с 2 миллионами постов и 200 тысяч страниц. Её общий размер превышал 5 GB. Здесь надо отметить, что при наличии в базе 200,000 страниц WordPress начинает безжалостно тупить, что связано с ошибками как на фронте, так и в админке. Тема 2022 выполняет на главной странице запрос в базу, пытаясь загрузить все 200 тысяч страниц. В админке на странице всех постов ядро пытается загрузить все страницы для создания выпадающего списка страниц. Так что для создания больших баз лучше использовать генерацию постов.
Для достижения максимальной скорости работы, плагин в вызовах ajax использует константу WordPress SHORTINIT, чтобы произвести минимальную загрузку ядра и обойти загрузку всех плагинов. Это позволяет сэкономить 1-2 секунды на каждый шаг генерации.
Во время разработки выяснилось, что плагин не работает на некоторых хостингах и серверах. Возникала ошибка при выполнении LOAD DATA INFILE.
Дело в том, что LOAD DATA INFILE может читать только из определённой директории, которая задана read-only системной переменной secure_file_priv
. Обычно на равна /var/lib/mysql-files
. На Windows она пустая и поэтому мне удавалось грузить файл из любого места.
На Linux, понятно, проблемы. PHP не может записать в эту папку, а MySQL не может прочитать из временного файла PHP. Выход заключается в использовании LOAD DATA LOCAL INFILE. В этом случае MySQL устанавливает соединение с PHP, переписывает файл в свою папку и затем загружает его.
Для того, чтобы это работало, нужно установить SET GLOBAL local_infile = 'ON'
. Это легко сделать на лету.
Второе условие — в php.ini
(и только там) должно быть mysqli.allow_local_infile = On
. С версии PHP 7.1 эта опция по умолчанию отключена.
При активации плагин выдаёт нотис, что нужна такая опция в php.ini
, если она отключена. Девелопер справится, а домохозяйкам этот плагин не нужен.
Удаление сгенерированных постов
Быстрое удаление постов — отдельная тема. Для того, чтобы сгенерированные посты можно было легко удалить, плагин пишет в поле guid поста (которое практически никогда не используется) начальный слаг поста в таком виде: https://generator.kagg.eu/post_name
.
Чтобы удалить миллион постов запросом DELETE FROM wp_posts p WHERE p.guid like 'https://generator.kagg.eu/%';
надо 4 минуты. Это долго.
Оказывается, есть более быстрое и элегантное решение. Чтобы сделать то же самое через
START TRANSACTION;
CREATE TABLE wp_posts_copy LIKE wp_posts;
INSERT INTO wp_posts_copy
SELECT * FROM wp_posts p
WHERE p.guid not like 'https://generator.kagg.eu/%';
DROP TABLE wp_posts;
RENAME TABLE wp_posts_copy TO wp_posts;
COMMIT;
требуется всего 5 секунд.
Такой прирост производительности при удалении обусловлен тем, что INSERT выполняется в MySQL разы быстрее, чем DELETE. Проще переписать сохраняемые посты в другую таблицу, старую удалить и потом новую переименовать. После чего, кстати, база работает намного быстрее. Если просто удалять строки, то размер таблицы wp_posts
, где почти пусто, сохраняется в 2.9 GB. База начинает подтупливать. А при фокусах с таблицами всё ок.
Плагин доступен для загрузки на GitHub и в официальном репозитории WordPress.
Круто. Молодец!