Python Django ASGI - сохранение изображения в модели с помощью django-storages в ведро S3 приводит к утечке памяти

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

Проблема: размещенное веб-приложение потребляет все больше и больше памяти каждый раз, когда изображение сохраняется в Django-модель ImageField, настроенную на S3 через django-storages.

Что я пробовал или наблюдал: независимо от того, какой ASGI-сервер я использовал, результат был одинаковым (Daphne, Uvicorn, Hypercorn, Granian). Я проверил распределение памяти построчно в критических местах с помощью tracemalloc, и потребление памяти увеличилось именно после сохранения изображений.

    async def upload_image(content_img, content_thumbnail):
        lf1 = tempfile.NamedTemporaryFile()
        for chunk in content_img.chunks():
            lf1.write(chunk)

        lf2 = tempfile.NamedTemporaryFile()
        for chunk in content_thumbnail.chunks():
            lf2.write(chunk)

        uploaded_image = await UploadedImage.objects.acreate(user=request.user, type=type, app_name=app_name)

        def upload_image_sync():
            uploaded_image.image.save(content_img.name, lf1)
            uploaded_image.thumbnail.save(content_thumbnail.name, lf2)

        await sync_to_async(upload_image_sync)()

В приведенном выше коде вызов 'uploaded_image.image.save' или 'uploaded_image.thumbnail.save' показывает большее использование памяти с помощью tracemalloc, указывая 'json/decoder.py' как файл, занимающий память.

А вот и самое интересное. Сохранение изображения в модель показывает увеличение объема используемой памяти на ~10 МБ и скачки с ~45 МБ до ~55 МБ. Если я сохранял много изображений за короткий промежуток времени, то наблюдал скачки до ~400 МБ, но после этого текущее использование снижалось, и казалось, что память освобождается. Не всегда возвращаясь к 45 МБ, но после выполнения многих запросов это выглядело хорошо, как будто сборщик мусора работает как положено. Эти логи из tracemalloc были похожи на локальной машине и на хостинговом сервере.

Однако статистика использования памяти от хостера веб-сервера совершенно другая. Я понимаю, что tracemalloc, вероятно, показывает использование памяти только веб-приложением Django, а хостируемый контейнер включает в себя другие части, например, веб-сервер ASGI. Но выглядело это так (1 рабочий): начиная с ~250MB, после вызова частей, загружающих изображение в S3, использование памяти контейнера увеличилось на ~50-100MB на одно загруженное изображение. Самое неприятное, что память не освобождается (иногда казалось, что освобождается 10% от первоначального объема, который увеличивался при запросах за короткий промежуток времени). В результате я мог исчерпать 8 ГБ памяти сервера за 2 минуты, если проводил стресс-тест с большим количеством изображений.

Я использую railway.app для размещения веб-приложения, но я также проверил render.com, ведет ли он себя так же, и это так. Однако есть одно довольно большое различие, когда я использую hypercorn --workers 5 --max-requests 50 --max-requests-jitter 25 . Перезапуск рабочих на railway.app не освобождает память вообще (!), а на render.com освобождает. Перезапуск рабочих, к сожалению, приводил к отбрасыванию некоторых запросов, так что это не похоже на решение моей проблемы, даже если я выберу render.com в качестве основного хоста.

Кто-нибудь сталкивался с такой проблемой и решил ее? Может есть способ принудительно сохранять изображения в S3 кусками, если причина в этом? Или, может быть, внешние слои хостируемого контейнера не освобождают память при запросах?

Вернуться на верх