Rinat Shaykhutdinov

Ruby developer, DJ, musician from St. Petersburg, Russia

Размораживаем ActiveRecord или «170k запросов на страницу — не предел»

Несколько дней назад я вылил на тестовый сервер новые пользовательские интерфейсы, а именно — статистику пользователей; перезапустил passenger и обновил страницу. Результата отрисовки я ждал не меньше 10 минут, в то время как на локальной машине на тех же тестовых данных страница генерировалась 1 секунду. Магия? Нет, просто ActiveRecord подложила мне очередную свинью.

Проработав 10 минут и съев кучу ресурсов сервера рельсы всё-таки соизволили отдать страницу, а я стал разбираться в причинах такого поведения. Включив логирование всех MySQL-запросов на сервере и сгенерировав ещё раз страницу со статистикой я получил 170 000 однотипных DESCRIBE table и других вызовов, результаты которых по-хорошему неплохо бы кешировать хотя бы в пределах одной транзакции. Боюсь представить как громко смеялся наш сисадмин когда узнал про такой оверхед на генерацию одной страницы :)

Не буду вдаваться в долгий и мучительный процесс поиска ошибки и курения мануалов и исходников рельсов, в частности ActiveRecord и mysql2 гема, а просто расскажу в чём вся соль. Таблица модели, данные из которой я запрашивал, не имеет стандартного PRIMARY KEY `id` и из-за этого при создании каждого объекта такой модели ActiveRecord делал DESCRIBE для этой таблицы и SHOW TABLES. Понимать составные ключи рельсы не умеют, поэтому для решения подобной проблемы просто явно укажите в модели primary key в виде string. Нет, это не поможет нам, допустим, успешно выполнять object.delete, но поможет заметно сократить время генерации страницы из-за того, что ActiveRecord не будет постоянно опрашивать таблицы.

ORM — это хорошо, но не стоит слепо ей верить и полностью на неё полагаться, особенно если разрабатываете высоконагруженный проект.

8 октября 2011 г.