open atlas
↑ К треку
Основы System Design SD · 07 · 07

Выбор хранилища: полиглот медиа-сервис

Практический проект: собери бэкенд обмена медиа, что кладёт каждую нагрузку на правильное хранилище — реляционные метаданные, байты в объектном хранилище через presigned-ссылки, поисковый индекс — и осознанно решает двойную запись между базой и индексом.

SD Senior ◷ 240 min
Уровень
ОсновыJuniorMiddleSenior

Прочитать, что надо отделить метаданные от байтов и вывести поисковый индекс из коммита БД — не то же, что собрать систему, которая правда это делает и остаётся согласованной, когда процесс падает между двумя записями. Собери небольшой бэкенд обмена медиа, что кладёт каждую нагрузку на правильное хранилище, отдаёт байты через presigned-ссылки, и затем докажи, что твой поисковый индекс не может тихо разойтись с базой, даже когда ты убиваешь релей посреди записи.

Этот проект делает весь раздел операционным: ты разместишь каждую нагрузку на хранилище, чьи гарантии ей подходят, удержишь крупные байты вне базы, а затем встретишь проблему двойной записи лоб в лоб, выведя поисковый индекс из коммита базы и доказав — осознанно уронив процесс — что он сходится.

Проект
0 из 8
Цель

Собери небольшой бэкенд обмена медиа (любой язык), что хранит метаданные файлов в реляционной базе, байты файлов в объектном хранилище с отдачей через presigned-ссылки и полнотекстовый поисковый индекс по названиям/подписям — затем сделай поисковый индекс производной, в-итоге-согласованной проекцией базы, что доказуемо не может тихо с ней разойтись.

Требования
Критерии приёмки
  • Реляционная схема, держащая только метаданные (без байтов файлов), с обоснованием каждой колонки и колонкой object_key, указывающей на байты в объектном хранилище.
  • Рабочий поток загрузки, где клиент PUT-ит байты прямо в объектное хранилище через presigned-ссылку, приложение пишет метаданные pending→complete, а уборщик сверяет провальные загрузки — продемонстрировано от начала до конца на многомегабайтном файле, что ни разу не стримится через уровень приложения.
  • Endpoint полнотекстового поиска на инвертированном индексе с короткой заметкой, показывающей, почему эквивалентный запрос LIKE полностью сканировал бы, а запрос по инвертированному индексу — нет.
  • Поисковый индекс, питаемый из коммита БД (транзакционный outbox или CDC), с убранной из обработчиков запросов двойной записью — нигде нет best-effort «проиндексировать и игнорировать провалы».
  • Воспроизводимый тест согласованности, что роняет релей/процесс посреди записи и показывает, что индекс сходится к базе после перезапуска, плюс абзац объяснения, почему он не может тихо разойтись.
  • Короткий разбор, сопоставляющий каждую нагрузку её хранилищу и модели согласованности (ACID-метаданные, в-итоге-согласованный производный поиск), изложенный на высоте композиции.
Senior-стретч
  • Добавь lifecycle-тиринг: политику (реальную или симулированную), что двигает объекты в класс нечастого доступа/архив по возрасту, и измерь изменение стоимости хранения против держания всего горячим.
  • Добавь обработку долговечность-против-доступности: настрой (или симулируй) межрегиональную репликацию объектного хранилища и опиши путь чтения с failover, что держит байты достижимыми во время сбоя региона.
  • Добавь второе производное хранилище: time-series под счётчики просмотров/метрики на элемент, питаемое тем же способом (из журнала коммитов), и покажи даунсэмплинг + retention, ограничивающие его хранение.
  • Добавь граф- или рекомендательный запрос («элементы, что также понравились тем, кому понравился этот») и обсуди, какое семейство хранилищ его обслужило бы и почему реляционный self-join не масштабировался бы — оставаясь на высоте композиции.
Вспомните перед уходом
  1. 01
    Почему отделять метаданные от байтов и как поток загрузки держит байты вне уровня приложения?
  2. 02
    Как сделать поисковый индекс согласованным с базой без двойной записи и как это доказать?
  3. 03
    Какую модель согласованности даёт каждый кусок этой системы и почему это правильная композиция?
Итог

Этот проект превращает раздел хранилищ в рабочую систему. Ты размещаешь каждую нагрузку на хранилище, чьи гарантии ей подходят: метаданные в реляционной базе (ACID, запрашиваемо, источник истины о том, что существует и кто владеет), байты в объектном хранилище под ключом (никогда в БД), отдаваемые через presigned-ссылки, чтобы клиент грузил и скачивал напрямую, а stateless-уровень приложения ни разу не проксировал байт — со статусом pending→complete и уборщиком, сверяющим разрыв метаданные↔байты. Поиск работает на производном инвертированном индексе (поисковый движок или tsvector), с подтверждением, что он избегает полного скана, что вынудил бы LIKE с ведущим wildcard. Сердце проекта — проблема двойной записи: ты убираешь best-effort запись индекса из обработчиков запросов и вместо этого выводишь индекс из коммита базы через транзакционный outbox или CDC, затем доказываешь схождение, осознанно уронив релей посреди записи и наблюдая, как индекс догоняет после перезапуска. Результат — система, что компонует сильную согласованность там, где требуется, и итоговую там, где терпимо — полиглот-обмен — и инженер, что собрал её раз, тянется к правильному хранилищу под нагрузку и никогда не доверяет двум независимым записям остаться в синхроне.

Что-то непонятно?

Задай вопрос по этому уроку. Вопросы анонимны и попадают напрямую автору — урок станет лучше.

хоткеи развернуть
поиск
K
пред. пьеса
k
след. пьеса
j
тиры
t
это меню
?
sources3
expand
  1. 01
  2. 02
  3. 03

Trademarks belong to their respective owners. Editorial reference only.