@3ton

Какова последовательность поиска утечек памяти на Ruby?

Имеется приложение написанное не мною и до меня.
Оно отвечает за бизнес-логику телефонии, а так же за предоставление API пользования этой бизнес-логикой.

Само приложение использует библиотеки Adhearsion(для общения с Asterisk) и Sinatra для API

В определенный момент виртуальная машина подвисла на столько что даже по SSH не возможно было подключиться к ней и пришлось делать Hard Reset.

В процессе разбирательств было определено проблемное место - приложение на Ruby.
Оно в момент старта отъедало 100 метров оперативы, через пару недель оно уже отжирает более гига оперативы.

Хотелось бы понять каким образом ищутся утечки памяти, либо не закрытые/удаленые объекты в памяти

UPD:22-07-2021

на сегодняшний день ситуация такова, написал свой логер и пытаюсь в критичные для меня моменты скидывать в логи данные

по сути кидаю 2(иногда 3) параметра
1 - класс и метод откуда идет сброс в логер
2 - строка для логера
3 - необязательный атрибут - отдельный файл лога

отбросил 2 не особо важных для меня логирования отдельными файлами, это delayed job библиотека запуска задач в руби по расписанию(использую для старта звонков в астериске в нужный момент) и отдельно кидаю лог по синатре(обращения к методам и атрибуты)

теперь в основном логе собираются данные по стартам сессий, их отработке, а так же билингу

строка в логе принимает следующий вид
Время :: виртуальная память процесса|оперативная память проеесса :: данные из GC(malloc_increase_bytes-malloc_increase_bytes_limit-total_allocated_objects) :: Класс->метод вызова лога :: строка сообщения в лог

2021-07-22 09:34:19 +0200 :: 4545776|172832 :: 14869832-16777216-14064439 :: AdviserCall-> :: 17715: Start to call to adviser
2021-07-22 09:34:22 +0200 :: 4545776|172940 :: 16298832-16777216-14074552 :: Direct->run :: 17715: on end call
2021-07-22 09:34:22 +0200 :: 4545776|171628 :: 433496-16777216-14078617 :: Direct->run :: 17715: finished call: hungup (16)
2021-07-22 09:34:22 +0200 :: 4545776|171676 :: 1625480-16777216-14081883 :: Direct->run :: 17715: session.update_attributes: stop 2021-07-22 09:34:22 +0200, duration 31.0
2021-07-22 09:34:22 +0200 :: 4545776|171676 :: 2307360-16777216-14090011 :: Direct->run :: 17715: on customer.calculate_duration
2021-07-22 09:34:22 +0200 :: 4545776|171676 :: 2391720-16777216-14092322 :: AdviserCall-> :: : End callcontroller
2021-07-22 09:34:22 +0200 :: 4545776|171676 :: 2568720-16777216-14092986 :: AdviserCall-> :: 17715: after call to adviser
2021-07-22 09:34:22 +0200 :: 4545776|171684 :: 3171688-16777216-14094233 :: Direct->run :: 17715: on adv.calculate_duration
2021-07-22 09:34:22 +0200 :: 4545776|171684 :: 3224400-16777216-14095530 :: Direct->run :: 17715: unlock adviser
2021-07-22 09:34:22 +0200 :: 4545776|171704 :: 3962392-16777216-14101560 :: Direct->run :: 17715: on session.calculate_billing
2021-07-22 09:34:22 +0200 :: 4545776|171732 :: 4271248-16777216-14110094 :: Direct->run :: 17715: before EXECUTED status was: EXECUTING
2021-07-22 09:34:22 +0200 :: 4545776|171756 :: 6101592-16777216-14120962 :: Direct-> :: 7cf84f4b-7178-4c55-b6bd-3aa42019c6b6: End callcontroller
2021-07-22 09:34:22 +0200 :: 4545776|171812 :: 7845656-16777216-14122863 :: AdviserCall->run :: 17715: call.on_end: hangup-command ()
2021-07-22 09:34:22 +0200 :: 4545776|171836 :: 8181216-16777216-14124630 :: AdviserCall->run :: 17715: before calculate duration
2021-07-22 09:34:22 +0200 :: 4545776|171852 :: 8196080-16777216-14124849 :: AdviserCall->run :: 17715: before unlock adviser
2021-07-22 09:34:22 +0200 :: 4545776|171852 :: 8224112-16777216-14125307 :: AdviserCall->run :: 17715: before calculate billing


но по факту пока ничего серьезного не нарыл.
не могу найти момент где идет серьезная утечка в памяти, но на замерах получил на 10 сутках почти полтора гига в оперативе под процесс, а один раз даже обнаружил 13 гигов в виртуальной памяти

UPD(2021-08-23): обнаружил направление поиска утечки, а так же интересный баг
баг выражается в следующем - при работе с моделью в монго в определенный момент я произвожу изменение атрибута status c EXECUTING на EXECUTED, а в методе after_save этой модели сбрасываю объект в лог. и примерно в 1% случаев у меня в логе статус EXECUTED у объекта был, но при этом в базе я вижу лишь EXECUTING

что касается утечки - она образуется из-за потоков. в момент запуска приложения у меня 38 потоков, по истечению 5 суток у меня их уже под 4 сотни, пропорционально с ними растет и память. Одну из потенциальных проблем обнаружил. В сложной сессии когда клиент создает входящий вызов, я создаю ногу оператора и соединяю их, после чего оператор пином во время разговора определяет какому эксперту пойдет дозвон и произойдет подмена ноги оператора на ногу эксперта. в процессе происходит кучу еще действий в том числе и проигрывание приветствий/музычки каждому свое. В определенных ситуациях я получаю в лог сообщение об ошибке вида
[2021-08-23 18:27:37.909] ERROR Adhearsion::OutboundCall: 1741366f-01ca-48ae-8640-c53c037576d2@10.0.0.106: <Adhearsion::Call::Hangup> Adhearsion::Call::Hangup
	/usr/local/rvm/gems/ruby-2.3.0/gems/celluloid-0.15.2/lib/celluloid/responses.rb:29:in `value'
	/usr/local/rvm/gems/ruby-2.3.0/gems/celluloid-0.15.2/lib/celluloid/calls.rb:92:in `value'
	/usr/local/rvm/gems/ruby-2.3.0/gems/celluloid-0.15.2/lib/celluloid/proxies/sync_proxy.rb:33:in `method_missing'
	/usr/local/rvm/gems/ruby-2.3.0/gems/adhearsion-2.6.3/lib/adhearsion/call.rb:22:in `method_missing'
	/usr/local/rvm/gems/ruby-2.3.0/gems/matrioska-0.3.0/lib/matrioska/app_runner.rb:27:in `start'
	/usr/local/rvm/gems/ruby-2.3.0/gems/matrioska-0.3.0/lib/matrioska/app_runner.rb:80:in `block in match_and_run'
	/usr/local/rvm/gems/ruby-2.3.0/gems/adhearsion-2.6.3/lib/adhearsion/call_controller.rb:112:in `exec_with_callback'
	/usr/local/rvm/gems/ruby-2.3.0/gems/adhearsion-2.6.3/lib/adhearsion/call_controller.rb:104:in `block (2 levels) in bg_exec'
	/usr/local/rvm/gems/ruby-2.3.0/gems/adhearsion-2.6.3/lib/adhearsion/foundation/exception_handler.rb:5:in `catching_standard_errors'
	/usr/local/rvm/gems/ruby-2.3.0/gems/adhearsion-2.6.3/lib/adhearsion/call_controller.rb:103:in `block in bg_exec'
	/usr/local/rvm/gems/ruby-2.3.0/gems/celluloid-0.15.2/lib/celluloid/thread_handle.rb:13:in `block in initialize'
	/usr/local/rvm/gems/ruby-2.3.0/gems/celluloid-0.15.2/lib/celluloid/internal_pool.rb:100:in `block in create'
	/usr/local/rvm/gems/ruby-2.3.0/gems/logging-1.8.2/lib/logging/diagnostic_context.rb:323:in `block in create_with_logging_context'

при этом нет упоминания самого файла приложения как источника ошибки, как следствие лишь понятие что какой-то поток в целюлоиде дал сбой и не смог выйти
  • Вопрос задан
  • 136 просмотров
Пригласить эксперта
Ответы на вопрос 3
@AVKor
Здесь, например.
Ответ написан
Комментировать
2ord
@2ord
Обычно это не утечки памяти, а раздувание памяти из-за бездумного программирования: чтение огромного JSON целиком в память, чтение всех записей с БД в память (Product.all) и пр.

Есть gem, с которого предлагаю начать расследование: derailed_benchmarks. С его помощью можно определить сколько памяти занимает приложение при старте, включая фреймворк RoR. См. как подключать к Sinatra.

bundle exec derailed bundle:mem

bundle exec derailed bundle:objects

Так можно проследить за потреблением памяти во времени:
bundle exec derailed exec perf:mem_over_time

scout_apm - с ним можно определять какие операции ведут к увеличению потребления памяти приложения.

При расследовании на локальной машине стоит учесть, что если работать с локальными тестовыми выборками данных, то и потребление памяти может оказаться существенно меньше из-за разницы в объеме данных.

Добавлено:
Нашел еще хорошее сравнение инструментов для определения производительности, в нагрузку:
Rails Performance Monitoring Tools Compared

Добавлено:
Еще простой способ уменьшить потребление памяти:
Добавить гем
gem 'jemalloc'
Ответ написан
@3ton Автор вопроса
Произвел оптимизацию кода и уменьшил кол-во записей в один и тот же документ.

Теперь кол-во Thread'ов продолжает расти, но память держит себя в норме

Правда случаются непонятки и код завершает Thread не давая отработать всему коду в call.on_end
При этом стали появляться в логи adhearsion.log ошибки вида
[2021-08-31 18:15:31.879] ERROR Adhearsion::OutboundCall: 2f4a0aa4-8582-4cde-819b-367aa24ad4b2@10.0.0.106: <Celluloid::Task::TerminatedError> task was terminated
	/usr/local/rvm/gems/ruby-2.3.0/gems/celluloid-0.15.2/lib/celluloid/tasks/task_fiber.rb:32:in `terminate'
	/usr/local/rvm/gems/ruby-2.3.0/gems/celluloid-0.15.2/lib/celluloid/actor.rb:404:in `block in cleanup'
--
[2021-08-31 18:16:32.133] ERROR Adhearsion::Call: f10e279b-bf13-433f-b98f-b04b673e24fe@10.0.0.106: <Celluloid::Task::TerminatedError> task was terminated
	/usr/local/rvm/gems/ruby-2.3.0/gems/celluloid-0.15.2/lib/celluloid/tasks/task_fiber.rb:32:in `terminate'
	/usr/local/rvm/gems/ruby-2.3.0/gems/celluloid-0.15.2/lib/celluloid/actor.rb:404:in `block in cleanup'
--
[2021-08-31 18:17:38.997] ERROR Adhearsion::Call: 688a99ec-3871-4a40-85d5-088949f4d4a5@10.0.0.106: <Celluloid::Task::TerminatedError> task was terminated
	/usr/local/rvm/gems/ruby-2.3.0/gems/celluloid-0.15.2/lib/celluloid/tasks/task_fiber.rb:32:in `terminate'
	/usr/local/rvm/gems/ruby-2.3.0/gems/celluloid-0.15.2/lib/celluloid/actor.rb:404:in `block in cleanup'
--
[2021-08-31 18:18:58.485] ERROR Adhearsion::Call: b418acac-d3e9-41ff-85fc-6c6c54e577b7@10.0.0.106: <Celluloid::Task::TerminatedError> task was terminated
	/usr/local/rvm/gems/ruby-2.3.0/gems/celluloid-0.15.2/lib/celluloid/tasks/task_fiber.rb:32:in `terminate'
	/usr/local/rvm/gems/ruby-2.3.0/gems/celluloid-0.15.2/lib/celluloid/actor.rb:404:in `block in cleanup'
--
[2021-08-31 18:20:57.680] ERROR Adhearsion::OutboundCall: 3b189dd2-4dc5-4680-8c4b-97a3ad71d18e@10.0.0.106: <Celluloid::Task::TerminatedError> task was terminated
	/usr/local/rvm/gems/ruby-2.3.0/gems/celluloid-0.15.2/lib/celluloid/tasks/task_fiber.rb:32:in `terminate'
	/usr/local/rvm/gems/ruby-2.3.0/gems/celluloid-0.15.2/lib/celluloid/actor.rb:404:in `block in cleanup'
--
[2021-08-31 18:22:23.770] ERROR Adhearsion::OutboundCall: d4d7049e-8913-486e-8a16-e50edce4a4bd@10.0.0.106: <Celluloid::Task::TerminatedError> task was terminated
	/usr/local/rvm/gems/ruby-2.3.0/gems/celluloid-0.15.2/lib/celluloid/tasks/task_fiber.rb:32:in `terminate'
	/usr/local/rvm/gems/ruby-2.3.0/gems/celluloid-0.15.2/lib/celluloid/actor.rb:404:in `block in cleanup'
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы