От переводчика: Оригинал статьи за авторством Paweł Urbanek
Обзор
В теории, вы можете запустить процессы Rails (веб приложение) и Sidekiq (отложенные задачи) на одном инстансе Heroku с 512мб памяти. Для хобби проектов с небольшим количеством трафика это сохранит 7 баксов в месяц. Однако, если пытаться запихнуть два Ruby процесса на один инстанс вы можете столкнуться с проблемой нехватки памяти. В этой статье, я объясню как вы можете ограничить количество памяти используемое Rails приложением.
Недавно, я прочитал замечательную статью от Bilal Budhani, которая объясняет как запустить Sidekiq процесс вместе с Puma на Heroku инстансе. Применив это на одном из своих хобби проектов, я столкнулся с этой ужасной ошибкой R14
.
Я начал копать и, после применения пары подходов по оптимизации использования памяти, график стал выглядеть так:
И вот, что я сделал:
Посади Gemfile на диету
Используя специальный гем для этого… В Ruby мире, оборачивать код в гем, это одно из “простейших” решений. При этом, помимо других side эффектов, легко допустить утечку памяти.
Лучший способ проверить как много памяти потребляет каждый из ваших гемов это использовать Derailed тесты (набор штук для тестирования Ruby On Rails приложений). Чтобы использовать их, просто добавьте:
в ваш Gemfile и запустите:
Мой проект использует ботов Twitter и Facebook1. Я был удивлен, что популярный гем sferik/twitter использует 13 MB памяти на старте. Для начала, я заменил его на легковесную альтернативу grackle (~1 MB), а в итоге написал собственную реализацию HTTP запросов в Twitter API. Таким же образом я избавился от гема koala (~2 MB).
Еще одной быстрой победой было заменить гем gon (~6 MB) на кастомные JavaScript дата атрибуты.
Импорт нескольких мегабайт файлов гемов, чтобы упростить один HTTP вызов или нежелание написать пару строк JavaScript кода, определенно можно легко избежать.
Используй jemalloc
jemalloc это альтернативный аллокатор памяти для Ruby. На heroku можно добавить jemalloc с помощью buildpack’а. Для моего приложения такое изменение снизило использование памяти примерно на ~20%. Только обязательно протестируйте работу вашего приложения с jemalloc на staging, перед тем как деплоить в production.
Ограничь конкурентность и количество воркеров
Скорее всего для хобби проекта с небольшим трафиком вам не нужна большая пропускная способность. Можно снизить использование памяти, уменьшением числа Sidekiq и Puma воркеров и тредов. Вот мой файл config/puma.rb
:
и config/sidekiq.yml
:
Несмотря на то, что мы указали максимальное количество тредов 1, Puma может создать до 7 тредов. С такой минимальной настройкой, Smart Wishlist все еще способен обработать около 100К Sidekiq джоб в день и сервить React фронтенд и мобильное JSON API одновременно.
Оптимизируй парсинг JSON
Все эти Sidekiq задачи нужно чтобы скачивать актуальные цены из iTunes API (оптимизация запросов в моем TODO листе) и отправлять push нотификации о скидках. Это значит, что в приложении много парсинга JSON. И у меня есть однострочный фикс, который оптимизирует как использование памяти так и производительность:
yaji-ruby предлагает API совместимый со встроенным json, он вклинивается в вызовы JSON.parse, улучшая их производительность и использование памяти.
Заключение
Работа в условиях ограничений со стороны инфраструктуры это отличный способ прокачать ваши мускулы программиста и изучить техники оптимизации. В теории, вы всегда можете решить проблемы с памятью вбрасыванием большего количества денег на сервера, но почему бы не попытаться сохранить эти доллары?
-
Соцсеть, признанная в России экстремистской ↩