Блог Евгения Морозова

разработка

Для поиска утечек памяти в приложениях на Python существует масса различных модулей, в том числе, уже встроенный в Python 3 модуль tracemalloc. Недостаток всех этих инструментов в том, что утечку надо воспроизвести в локальном окружении, а потом уже, используя pdb или ipdb, ковыряться в куче (heap), в поисках утекших объектов. Однако если у нас есть огромный проект, посещаемый миллионами пользователей, то воспроизвести утечку может оказаться невозможно, так как к ней приводит какое-то сочетание взаимодействия пользователей с проектом, которое не воспроизвести наугад.

К счастью, нашёл проект meliae, позволяющий сделать дамп кучи в произвольный момент, с тем, чтобы проанализировать ее в оффлайне, уже не на боевом сервере.

Однако, в проекте оказался досадный баг: целые числа Python он пытается сериализовать как тип C long. Но в Python целое число может быть произвольной разрядности, а в C long имеет разрядность 32 или 64 бит. Где-то в нашем проекте (или одной из его зависимостей) используются большие числа, поэтому создать дамп не удавалось, потому что этот процесс очень быстро обрывался с ошибкой OverflowError.

Делать нечего, все другие варианты поиска утечки я уже попробовал без малейшего успеха. Скомпилировал Python из исходников с отладочной информацией, скомпилировал meliae, и стал вспоминать полузабытые навыки программирования на C и использования отладчика gdb. Довольно быстро нашел место возникновения ошибки, проблема была только в том, что времени на поиски у меня немного, а изучение того, как сдампить из модуля на C полноценное представление целого числа Python, потребовало бы длительного изучения устройства интерпретатора Python.

В итоге воспользовался быстро найденной функцией PyLong_AsLongLongAndOverflow, которая возвращает -1, в случае, если число Python не помещается в long long. Сомневаюсь, что в нашем приложении утекают long, поэтому точное их значение меня не интересует.

Свои правки положил здесь: https://github.com/emorozov/meliae

Если удастся найти утечку памяти, постараюсь написать об этом тоже.

#python #разработка

В больших и старых проектах на Django бывает непросто найти ошибки из-за большого количества сигналов. Сигналы затрудняют анализ кода, так как обработчик может быть создан в любом модуле проекта, например, не в том, где определена модель, и вызывается неявно.

Для облегчения поиска обработчиков нашел когда-то команду для Django, выводящую список всех обработчиков в проекте. Но она выводила только список обработчиков, без указания к какой модели они относятся. Чуть позже нашлась усовершенствованная версия, а затем я тоже приложил руки в процессе создания pull request для проекта django-extensions. Пришлось попотеть, так как django-extensions должен работать на большом количестве различных версий Python и Django (включая, например, PyPy). Но все препятствия удалось преодолеть, надеюсь, что мой код окажется в django-extensions, а пока оставляю ссылку на сам код команды, чтобы можно было пользоваться, пока рассматривается pull request: list_signals.py

#python #разработка