← все задачи

Задача 2 из 10

Хранилище и раздача картинок

Пользователи загружают фотографии, сервис хранит их и быстро отдаёт в нескольких размерах — как аватарки и картинки товаров в маркетплейсе.

Уровень: Начальный На собеседовании: 35–45 минут объектное хранилищеCDNфоновая обработка

Функциональные требования

  • Загрузка изображения размером до 20 МБ
  • Автоматические превью в трёх размерах
  • Быстрая отдача картинки по постоянной ссылке
  • Удаление изображения вместе со всеми превью

Нефункциональные требования

  • 50 ТБ новых файлов в год
  • Пик 500 загрузок/с, отдача 20 000 запросов/с
  • Пользователи по всему миру, картинка открывается за 100 мс
  • Превью готово в течение 10 секунд после загрузки
  • Потеря оригинала недопустима

Нарисуйте архитектуру

Не гонитесь за красотой: на собеседовании от схемы нужно, чтобы по ней было видно путь запроса и где лежат данные. Сначала нарисуйте сами — эталон и разбор ниже.

Блок из палитры — добавить. Тащите мышкой или пальцем. Двойной клик по названию — переименовать. «Связь» — щёлкнуть по одному блоку, потом по другому: получится стрелка.

Схема сохраняется в этом браузере — вкладку можно закрыть и вернуться позже.

1 С чего начать, если ступор
  • Не пропускайте файл через приложение: подпишите ссылку и пусть клиент грузит байты прямо в хранилище.
  • Отдача картинок — работа CDN, а не вашего сервиса.
  • Ресайз асинхронный: ответ на загрузку не должен ждать обработки.
  • Метаданные (кто, когда, какие размеры готовы) — в БД, сами байты — в объектном хранилище.
  • Продумайте «сирот»: загрузка началась и не завершилась, запись в БД есть, а файла нет.
2 Эталонная схема

Байты не ходят через приложение: клиент грузит файл прямо в хранилище по подписанной ссылке, а раздаёт картинки CDN. API держит только метаданные и ставит задачу на превью.

Это один из рабочих вариантов, а не единственно верный. Если у вас иначе, но вы можете объяснить почему — на собеседовании это ровно то, что нужно.

3 Разбор: что должно прозвучать

Загрузка без прокси через приложение

  • Pre-signed URL: клиент кладёт файл прямо в хранилище, приложение только подписывает
  • Ограничения зашиваются в подпись: тип, максимальный размер, срок действия
  • Почему прогонять 20 МБ через gunicorn с одним воркером — плохая идея: воркер занят на всё время загрузки

Отдача и CDN

  • CDN перед хранилищем закрывает и географию, и 20k RPS
  • Длинный Cache-Control: файлы неизменяемы, имя меняется вместе с содержимым
  • Инвалидация через новое имя файла, а не через purge всего кэша

Асинхронная обработка

  • Очередь и воркеры: загрузка отвечает сразу, превью появляются позже
  • Обработчик идемпотентен — повторная задача из очереди не должна ломать результат
  • Что показывать, пока превью не готово: оригинал, заглушку или ресайз на лету

Метаданные и «сироты»

  • Запись в БД со статусом (загружается / готово) и отметками готовых размеров
  • Незавершённые загрузки убирает фоновая уборка по возрасту записи
  • Событие о завершении загрузки от хранилища надёжнее, чем доверие клиенту

Удаление

  • Мягкое удаление в БД + отложенная физическая уборка файлов
  • Удалять нужно все производные, а не только оригинал
  • Картинка может остаться в кэше CDN — с этим либо живут, либо инвалидируют явно

Куда копать дальше, если спросят

  • Проверка содержимого, а не расширения файла
  • Дедупликация по хешу: одна и та же картинка от тысячи пользователей
  • Приватные картинки: подписанные ссылки на чтение и почему CDN тут мешает
  • Стоимость хранения: холодные классы, ретеншен старых превью

Следующая задача: Ограничение частоты запросов

Маленькая задача с большим количеством граблей: алгоритм окна, общее состояние на 20 инстансов и атомарность.