Течет сильно - 7 гигов за 3 дня. Хотя раньше в рабочем режиме больше 1 гига не требовал (был запущен и по 2 недели без перезапусков).
Подумалось, что либо циклические ссылки, либо данные с какого то контейнера не удаляются.
Ну что же решил заюзать старый добрый google perf tools, что бы сделать снимок памяти в момент работы. Хм. Под ubuntu amd64 почему то только старая версия. И она виснет когда приложение разпаралеливается (хотя google perf tools - специально сделано для многопоточных приложений).
В дебагере видно, что все потоки висят на спинлоках.
Решил обновиться до последней версии - но компились версию с сайта не хотелось. Нашел пакеты на packages.debian.org - и на удивление всё установилось без проблем.
Но при входе в многопоточный режим работы демона - опять зависание.
Да и сам демон загружается под 15-20 минут - так как взял базу с продакшена, а там данных очень много.
Поискал какие то хорошие аллокаторы для отладки - того что мне надо было (google perf tools - heap peofiler, который бы не вис) не было.
Ну а может в valgrind.memcheck есть какие то ключи, что бы дампить состояние памяти не в конце работы программы, а в середине? Нет. Но... Есть valgrind.massif - который этим и занимается. Ура!
Безжалостно очистив базу от данных (так что бы хоть нагрузочные тесты могли работать, а они требуют настоящую игру и хоть немного валидных данных), стал ковырять valgrind.massif.
Запустил - прогнал нагрузочные тесты - всё работает, ничего не течет. Очень интерестно.
В общем тулзы мне никакого ответа не дали - наверно синтетическая нагрузка не провоцировала ошибку, влекущую утечку памяти. Надо включить моск;)
К сожалению с последней стабильной выливки очнь много кода поменялось и я тому виной: делал рефакторинг затронув почти половину всего кода;)
Думал задампить память и посмотреть что там, но разбираться в 7 гиговом файлике не хотелось. И тут - пришла безумная идея - анализировать статистику по демонам (благо текущих демона у нас 3 в кластере) которую мог собрать как с операционки, так и с внутреннего мониторинга демонов.
Совершенно случайно увидел пропорцию между количеством утекшей памяти и количеством выполненных команд одного типа. Команда была старой, но я вспомнил, что в эту выливку мы её стали намного активнее использовать. Проверил пропорцию на калькуляторе (питон в шеле;) - всё совпадает. Эта твать жрет память.
Вся проблема в том, что схожая команда использовалась только в биллинге, на неё нагрузочного теста не было. А недавно сделали схожую команду, которая использует схожие фичи что и первая команда, но выполняется в разы больше раз. Используется она сугубо в соц сетях и в нагрузочный тест тоже добавлена не была. Вот такая нехорошая ситуация.
Добавив в нагрузочный тест эту команду - за 10 секунд отожрал 50% памяти своего ноута. А дальше дело техники - даже не стал использовать massif - почитал код - нашел место где может не создаваться циклическая ссылка - сменил policy с Strong на Weak - сбилдил - запустил тест - ура, жучек умер.
Ну и какие я сделал выводы из убитого дня на дебаг? Собственно вот:
- Надо чаще прогонять тесты на утечки памяти
- Даже если утечка маленькая - в будущем код может начать использоваться активнее - тогда маленькая утечка превратится в сильную головную боль
- Надо иметь репрезентативную базу по функционалу аналогичную продакшену, но с меньшим объемом данных. Без этого отладка под дебагерами памяти будет практически невозможна
- Ещё раз убедился - отсутствие голых указателей не гарантирует отсутствие утечек (это касается как java и c#, так и если использовать умные указатели в С++)
- Когда ничего не помогает - случайность может решить всё
- Случайности не случайны (c) =) - не ища какие то закономерности в статистиках я бы не нашел их, хотя я и не надеялся что то найти
No comments:
Post a Comment