System Design Space
Граф знанийНастройки

Обновлено: 23 июня 2026 г. в 01:21

Синхронизация файлов (Dropbox / Google Drive)

средний

Системный кейс сервиса синхронизации файлов: разбиение на блоки и content-defined chunking, дедупликация, дельта-синхронизация, разделение метаданных и данных, уведомление об изменениях по курсору и разрешение конфликтов через конфликтные копии. Граница с кейсом объектного хранилища; ссылки на инженерные разборы движка синхронизации Dropbox и статью LBFS.

Синхронизация файлов — это не «загрузить файл в облако», а поддержание одной и той же картины дерева файлов сразу на нескольких устройствах: ноутбук, телефон, веб. Каждое из них может править файлы офлайн, а потом появиться в сети — и движок синхронизации обязан аккуратно слить изменения, передав по сети как можно меньше данных и ничего не потеряв молча.

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

Самое тонкое — метаданные и конфликты. Дерево, версии и журнал изменений живут в строго консистентном сервисе метаданных, отдельном от блочного хранилища, и операции идут в порядке «сначала блоки, потом commit». При одновременной правке с разных устройств система не теряет данные молча, а сохраняет конфликтную копию; автоматическое слияние правок — это уже область CRDT и совместного редактирования.

Долговечность данных

Проектируйте репликацию и восстановление так, чтобы выдерживать отказы узлов, стоек и зон доступности.

Контроль метаданных

Разделение метаданных и данных, а также политики размещения определяют управляемость кластера.

Риск горячих точек

Нужны стратегии против перекоса нагрузки: перебалансировка шардов, уплотнение и маршрутизация с учётом локальности.

Эксплуатационная стоимость

Сравнивайте уровни хранения, исходящий сетевой трафик и цену отказоустойчивости.

Граница

Объектное хранилище — рядом

Этот кейс — про синхронизацию файлов между устройствами. Само хранение блоков уходит в объектное хранилище, которое разбирается отдельно.

Читать обзор

файлов — это не «загрузить файл в облако». Это поддержание согласованной картины одного и того же дерева файлов сразу на нескольких устройствах: ноутбук, телефон, веб. Каждое устройство может править файлы , а потом появиться в сети — и система обязана аккуратно слить изменения, передав по сети как можно меньше данных и ничего молча не потеряв.

Важно сразу провести границу с соседним кейсом. Хранение байтов — задача : иммутабельные объекты, адресация по содержимому, дешёвое масштабирование. Здесь же мы проектируем движок синхронизации: что считать единицей передачи, как сравнивать состояния устройств, как уведомлять об изменениях и что делать при одновременной правке. Само блочное хранилище мы используем как бэкенд, а сами решаем задачу «привести N устройств к одному состоянию».

Что синхронизируем и какие сценарии важны

  • Многоустройственность: одно состояние на ноутбуке, телефоне и в вебе; новое устройство должно докачать актуальную картину.
  • Офлайн-правки: файл редактируется без сети, изменения накапливаются локально и применяются при возвращении в онлайн.
  • Большие файлы с мелкими правками: поправили один абзац в документе на сотни мегабайт — слать целиком расточительно.
  • Шаринг: общая папка у нескольких пользователей с разными правами доступа.

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

Загрузить и скачать — это ещё не синхронизация. Система обязана видеть, что изменилось на каждом устройстве, отдавать поток изменений и держать общий доступ. Ниже — рабочий контур API и то, что он должен гарантировать по надёжности.

Базовый API

  • POST /files/upload — загрузка новых или изменённых блоков
  • POST /metadata/commit — фиксация нового состояния файла (список блоков и версия)
  • GET /changes?cursor=… — поток изменений с указанного курсора
  • GET /blocks/:hash — скачивание блока по его
  • POST /share — выдача доступа к папке другому пользователю

Надёжность синхронизации

  • Каждое устройство ведёт локальный журнал изменений и докатывается до сервера
  • Прерванная загрузка докачивается, а не начинается заново
  • Конфликт сохраняет обе версии вместо молчаливой потери
  • Состояние сходится: после паузы все устройства приходят к одной картине

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

Для движка синхронизации важны не пиковые как таковые, а минимум трафика, корректность слияния и предсказуемая метаданных. Числа ниже — порядковые ориентиры для прикидки, а не реальные данные конкретного продукта; на интервью их полезно проговаривать как допущения.

ТребованиеОриентирПочему это важно
Минимум трафикаПередавать только изменённые блокиБольшинство правок касаются малой части файла — слать его целиком неэкономно
НадёжностьПодтверждённое состояние не теряетсяПадение клиента или сети между загрузкой блоков и фиксацией метаданных не должно ломать дерево
СогласованностьСходимость всех устройствПосле паузы устройства обязаны прийти к одинаковому представлению дерева
Задержка уведомленияСекунды от правки до пушаПользователь ждёт, что правка на одном устройстве быстро появится на другом

Прикидка экономии на дельтах (порядковая оценка)

  • Пусть у пользователя в среднем несколько устройств, у каждого — десятки тысяч файлов; ежедневно меняется лишь малая доля.
  • Если правка типично затрагивает 1–2 блока из десятков, передача срезает трафик на один-два порядка против полной перезаливки.
  • Сверху накладывается : одинаковые блоки (вложения, копии шаблонов) хранятся и передаются один раз.
  • Числа здесь — иллюстрация метода прикидки, а не измеренные показатели конкретного сервиса.

Разбиение на блоки и дедупликация

Единица передачи и хранения — не файл, а блок. Файл представляется как упорядоченный список хэшей блоков (blocklist). Тогда «что изменилось» — это разница двух списков, а одинаковые блоки переиспользуются между версиями, файлами и даже пользователями.

Бэкенд

Блоки живут в объектном хранилище

Блок, адресуемый по хэшу содержимого, — это иммутабельный объект. Дедупликация естественно ложится на content-addressable хранилище.

Читать обзор
Версия 1 файлаB1hash aB2hash bB3hash cB4hash dВерсия 2 (правка во втором блоке)B1hash aB2'hash xB3hash cB4hash dхеш изменилсяПо сети уходит только новый блокB2' (hash x)Блоки B1, B3, B4 уже есть на сервере —дедупликация по хэшу, повторная передача не нужна.

Фиксированные блоки

  • Файл режется на куски фиксированного размера (например, 4 МБ у Dropbox).
  • Просто и быстро; хорошо ложится на загрузку и докачку крупных файлов.
  • Слабое место — сдвиг данных: вставка байта в начало смещает все границы, и почти все блоки получают новые хэши.

Блоки переменной длины (content-defined chunking)

  • Границы блоков определяются по содержимому: скользящее окно и хэш Рабина (Rabin fingerprint) отмечают точку разреза там, где значение отпечатка удовлетворяет условию.
  • Вставка байта меняет границы только локально — соседние блоки сохраняют хэши, дедупликация остаётся высокой. Этот приём предложен в LBFS (SOSP 2001).
  • Цена — больше вычислений на клиенте и менее предсказуемый размер блока.

Приватность дедупликации между пользователями

Глобальная дедупликация (один блок на всех) экономит хранение, но создаёт канал утечки: по факту «блок уже есть» можно проверить, владеет ли кто-то конкретным файлом. Поэтому дедупликацию часто ограничивают рамками пользователя/папки, а проверку наличия не делают наблюдаемой со стороны клиента.

Метаданные против данных

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

Сервис метаданных

  • Дерево папок и файлов, пути, имена, размеры, время правки.
  • Версии файла и его blocklist; стабильные идентификаторы, переживающие переименование и перемещение.
  • на пользователя и курсор, по которому устройство докатывается до актуального состояния.
  • Требует : дерево не должно разъезжаться между клиентом и сервером.

Блочное хранилище

  • Иммутабельные блоки, адресуемые по хэшу содержимого.
  • Опирается на — отдельный соседний кейс — с дешёвым масштабированием и репликацией.
  • важнее задержки: блок записан и реплицирован — его уже не теряем.
  • Удаление блоков ленивое: сборка мусора по подсчёту ссылок, а не сразу.

Такое разделение даёт важный инвариант: сначала загружаем блоки, потом фиксируем метаданные. Если фиксация не дошла, лишние блоки просто соберёт сборщик мусора; обратный порядок привёл бы к ссылкам на ещё не загруженные данные. Dropbox в рассказе про потоковую синхронизацию (streaming sync) как раз использует временное промежуточное состояние метаданных, чтобы скачивающий клиент мог начать тянуть блоки ещё до окончательной фиксации.

Дельта-синхронизация: что передаём при правке

При сохранении файла клиент заново считает его blocklist и сравнивает с предыдущей версией. Передаются только блоки, которых ещё нет на сервере; остальное — это запись новой версии в метаданные. Так выглядит путь записи.

  1. Клиент режет файл на блоки и считает их хэши (фиксированные 4 МБ или content-defined).
  2. Спрашивает сервер, какие хэши из списка уже есть, и загружает только недостающие блоки.
  3. Загрузку можно сжимать и докачивать: оборвалась — продолжаем с непереданных блоков.
  4. После приёма блоков клиент делает commit новой версии в метаданные — атомарно меняется указатель файла на новый blocklist.
  5. Старая версия остаётся доступной по истории; блоки переиспользуются между версиями.

Скачивающая сторона действует симметрично: получив новый blocklist, она тянет только те блоки, которых нет в её . Часто это и есть те же блоки, что уже лежат в других файлах, — повторная передача не нужна.

Уведомление об изменениях и сходимость

Клиент не должен опрашивать сервер в плотном цикле. Обычно держат лёгкий notification-канал (например, или постоянное соединение): сервер отвечает, когда у пользователя появилось новое изменение. Получив сигнал, клиент идёт за дельтой по курсору.

Курсор изменений

  • Каждое устройство хранит позицию в журнале — курсор. Запрос /changes?cursor=… возвращает всё новое и сдвигает курсор.
  • Дёшево возобновляется после простоя: устройство просто докатывает хвост журнала.
  • Notification-сервис лишь будит клиента; саму дельту тот забирает отдельным запросом.

Сходимость (convergence)

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

Конфликты при одновременной правке

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

Конфликтные копии (синхронизация файлов)

  • Первая дошедшая правка фиксируется; вторая, чей предок устарел, сохраняется как отдельный файл «… (conflicted copy)».
  • Это сознательный отказ от автоматического слияния произвольных бинарных файлов — слить их корректно в общем случае нельзя.
  • Стратегия проще, но молча теряет одну из правок — для файлов это плохой дефолт.

Когда нужно слияние (collaborative)

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

Глубже

CRDT и совместное редактирование

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

Читать обзор

Глубокие разборы

Согласованность метаданных

Стабильные идентификаторы файлов важнее путей: перемещение папки не должно выглядеть как «удалил и создал заново». Dropbox в рассказе про переписанный движок (Nucleus) выделяет именно глобально уникальные идентификаторы и строгую консистентность представления клиента и сервера как основу корректной синхронизации.

Клиентский кэш

Локальный кэш блоков и метаданных позволяет открывать файлы офлайн и не качать заново то, что уже есть. Кэш ограничен по размеру и вытесняет редко используемые блоки; «выборочная синхронизация» держит локально лишь часть дерева.

Шифрование

Блоки шифруются при передаче и на диске сервера. Сквозное шифрование (ключи только у пользователя) усиливает приватность, но почти исключает межпользовательскую дедупликацию и серверное слияние — это явный компромисс между приватностью и экономией.

Шаринг и права

Общая папка — это запись метаданных с : участники, роли (чтение/запись), наследование прав. Журнал изменений общей папки доставляется всем участникам, а отзыв доступа должен немедленно отражаться в их представлении дерева.

Компромиссы и типовые ошибки

  • Файл как единица передачи: слать целый файл при любой правке — мгновенная потеря главного преимущества дельта-синхронизации.
  • Молчаливая потеря при конфликте: применять стратегию «последняя запись побеждает» к файлам и не сохранять вторую версию.
  • Путаница метаданных и данных: хранить дерево и байты в одном сервисе с одними требованиями к консистентности.
  • Неверный порядок: фиксировать метаданные до загрузки блоков и получать ссылки на отсутствующие данные.
  • Опрос вместо уведомлений: плотный polling вместо notification-канала — лишний трафик и задержки.
  • Игнор приватности дедупликации: глобальный дедуп без оглядки на утечку факта владения файлом.

Что важно проговорить на интервью

  • Что считаем единицей передачи: файл, фиксированный блок или content-defined блок — и почему.
  • Как разделены метаданные и данные и какой порядок операций (сначала блоки, потом фиксация).
  • Как устройство узнаёт об изменениях: notification-канал плюс курсор по журналу.
  • Как разрешаются конфликты офлайн-правок и где проходит граница с совместным редактированием.
  • Где идёт дедупликация и как при этом не создать канал утечки факта владения файлом.

Источники и материалы

Карта источников: Dropbox 2014 описывает streaming sync, блоки и blocklist; Dropbox Nucleus — модель глобальных идентификаторов и строгой консистентности sync engine; LBFS — content-defined chunking и дедупликацию; ByteByteGo — интервью-рамку. Размеры блоков, порядок операций и конфликтные политики ниже не переносятся автоматически на любой file-sync продукт.

Связанные главы

  • Объектное хранилище - Соседний кейс: бэкенд для блоков файлов — иммутабельные объекты, адресация по содержимому, дешёвое масштабирование хранения.
  • CRDT и совместное редактирование - Куда уходит разрешение конфликтов, когда правки нужно сливать автоматически, а не сохранять конфликтные копии.
  • CDN - Как раздавать скачивание блоков ближе к пользователю и снимать нагрузку с блочного хранилища на чтении.
  • Key-Value база данных - Под капотом сервиса метаданных: индексы дерева файлов, версии и курсор изменений на устройство.

Чтобы отмечать прохождение, включи трекинг в Настройки