Real-Time Backend
Collaborative Whiteboard
100 дизайнеров открывают один Figma-файл и начинают двигать объекты одновременно. Никто не видит ошибок синхронизации, нет "кто последний тот и прав" блокировок. Как это возможно без единой точки координации?
- **Figma** поддерживает 1000+ одновременных пользователей в одном файле через WebSocket + CRDT-подобную архитектуру. В 2020 году Figma опубликовала детали: каждый объект имеет уникальный ID, операции атомарны, LWW с серверным lamport clock разрешает конфликты.
- **Miro** обслуживает 50M+ пользователей на canvas-инфраструктуре. Для sticky notes используется Yjs (CRDT) для текстового контента, для позиций объектов - LWW. Архитектура позволяет работать офлайн с последующим автоматическим мержем.
- **Excalidraw** (open source) реализует полностью децентрализованный коллаборативный холст через WebRTC p2p + Yjs CRDT. Нет центрального сервера для координации - каждый клиент синхронизируется напрямую с peers.
- **Google Jamboard** (до закрытия в 2024) использовал Firebase Realtime Database как транспорт для canvas операций. Firebase обеспечивал ordering через server timestamp, конфликты разрешались через транзакции.
Canvas Sync: архитектура общего холста
Коллаборативный холст - это распределённая структура данных: сотни курсоров, тысячи объектов, каждый может быть изменён одновременно. Наивный подход - lock-based редактирование - убивает UX. Figma решила эту задачу иначе: каждый клиент имеет локальную копию состояния, изменения передаются как операции, конфликты разрешаются детерминированно.
Figma поддерживает 1000+ одновременных пользователей в одном файле. Курсоры обновляются через WebSocket с throttling 60 fps -> 15 fps при высокой нагрузке. Объектные операции идут отдельным каналом с higher priority. Miro обслуживает 50M пользователей на canvas-инфраструктуре с аналогичной архитектурой.
Разделение каналов критично: cursor awareness - lossy (потеря позиции курсора не страшна), object operations - lossless (потеря изменения объекта - катастрофа). WebSocket обеспечивает надёжность для операций, но курсоры можно отправлять с throttling и не retransmit-ить при потере.
Почему в Figma cursor awareness и object operations идут по разным каналам с разными приоритетами?
Shape Operations: типы и атомарность
Каждое изменение на холсте должно быть выражено как **атомарная операция** с определённой семантикой. Не "нарисовать прямоугольник", а "insert shape с id X в позицию Y с размерами Z". Это позволяет применять, отменять и мержить операции детерминированно.
- **INSERT**: добавить объект с уникальным id (UUID), начальными свойствами и позицией в z-order
- **UPDATE**: изменить свойства существующего объекта (position, size, style) - только delta, не full replace
- **DELETE**: пометить объект удалённым (soft delete для undo/redo и conflict resolution)
- **MOVE**: изменить позицию и/или z-order объекта - отдельная операция для оптимизации частых перемещений
- **GROUP/UNGROUP**: создать/разбить группу объектов - compound операция с гарантией атомарности
Зачем DELETE реализуется как soft delete (пометка удалённым) вместо физического удаления?
Vector Graphics Sync: пути Безье и трансформации
Векторная графика добавляет сложность: кривые Безье, трансформационные матрицы, вложенные группы. Синхронизация пути из 100 точек при редактировании одной точки не должна пересылать весь путь. Figma решает это через **path delta encoding**: передаётся только изменённая точка с индексом.
Figma использует protobuf для сериализации операций вместо JSON: бинарный формат в 3-5 раз компактнее. При 100 одновременных пользователях, активно рисующих, экономия на сериализации даёт заметное снижение bandwidth и задержки обработки.
Почему при редактировании одной точки пути Безье нужно передавать только delta (индекс + новые координаты), а не весь путь?
Whiteboard CRDT: конфликты без координатора
При одновременном редактировании одного объекта разными пользователями нужна стратегия разрешения конфликтов. Figma использует **Last-Write-Wins** (LWW) с серверным timestamp для большинства свойств: побеждает последняя запись. Для текстового контента в фреймах - Yjs (CRDT), где каждый символ имеет уникальный идентификатор.
CRDT (Conflict-free Replicated Data Type) для whiteboard: каждый объект имеет уникальный ID (клиент-id + lamport clock), операции коммутативны и идемпотентны. Два клиента, применившие один и тот же набор операций в разном порядке, придут к одному состоянию. Excalidraw (open source) использует эту модель без центрального сервера.
Miro использует operational transformation (OT) для совместного редактирования sticky notes (текст), но LWW для позиций и размеров объектов. Гибридный подход: CRDT/LWW для структурных свойств, OT для текстового контента, где символьная precision важна.
Для коллаборативного холста достаточно WebSocket broadcast - каждый клиент применяет все операции в порядке получения
Порядок получения операций у разных клиентов может отличаться. Без CRDT или OT diverged state гарантирован при concurrent edit
Клиент A отправляет op1 (move X to 100), клиент B отправляет op2 (move X to 200) одновременно. A получает [op1, op2] -> X=200. B получает [op2, op1] -> X=100. State diverged. CRDT с LWW даёт детерминированный результат независимо от порядка применения.
Почему LWW (Last-Write-Wins) подходит для позиций объектов, но не для текстового контента?
Итоги
- **Двухканальная архитектура**: cursor awareness (lossy, throttled 60->15 fps) и object operations (lossless) требуют разных QoS гарантий и должны идти по разным каналам
- **Атомарные операции** (INSERT/UPDATE/DELETE/MOVE) с уникальными ID объектов - единственный способ детерминированно мержить concurrent changes без координатора
- **LWW для структурных свойств + CRDT/OT для текста** - гибридная стратегия Figma и Miro: позиции разрешаются по timestamp, текст мержится посимвольно через Yjs
Связанные темы
Collaborative whiteboard - прикладная область для фундаментальных концепций realtime-систем:
- CRDT Structures — Математическая основа conflict-free репликации, которую использует Figma для объектных операций
- Operational Transformation — Альтернативный подход к конфликт-резолюции, используемый для текстового контента в Miro и Google Docs
- Live Location Tracking — Схожая задача: синхронизация позиций множества объектов в реальном времени с разными QoS требованиями
Вопросы для размышления
- Как бы изменилась архитектура Figma, если бы нужно было поддерживать офлайн-редактирование с синхронизацией при восстановлении соединения?
- При каком количестве одновременных пользователей LWW-стратегия разрешения конфликтов начинает создавать заметный UX-дискомфорт? Как это зависит от типа контента?
- Почему Excalidraw выбрал p2p WebRTC вместо центрального сервера? Какие архитектурные компромиссы это создаёт?