Решение: оптимизация Django при работе с S3
Оптимизация затронула все четыре выявленные точки в цепочке обработки.
Убрали избыточное скачивание при валидации. Переработали логику проверки загруженных файлов: теперь валидация использует метаданные, уже полученные при первоначальной загрузке, без повторного обращения к S3.
Заменили «скачать-загрузить» на копирование внутри S3. Вместо того чтобы гонять файлы через сервер приложения, мы используем API Yandex Cloud S3 для копирования объектов напрямую внутри хранилища. Сервер отправляет только команду с путями источника и назначения. Сами данные по сети не передаются.
Для реализации потребовалось расширить стандартный класс хранилища Django и переопределить метод копирования файлов. Также создали собственное поле ImageField, которое работает с новым хранилищем вместо стандартного.
Кэшировали оригинал при создании превью. Теперь оригинальное изображение скачивается один раз и хранится в памяти, пока из него последовательно создаются все пять превью. Это сократило количество сетевых запросов в пять раз для каждой фотографии.
Сгруппировали фоновые задачи. Вместо создания отдельной Celery-задачи на каждое изображение, мы группируем обработку. Теперь при публикации объекта создаются только две задачи: одна обрабатывает все оригинальные превью, вторая — все превью с водяными знаками. Каждая задача работает со своим набором файлов параллельно, но без накладных расходов на управление десятками мелких задач.