АНАТОМИЯ ФИЧИ: На каком транспорте на самом деле работает Google Docs
Спасибо Юрию Морозову – в посте про совместное редактирование он указал на нашу ошибку. Мы написали, что топология держится на "WebSocket-канале к серверу", и подверстали под это Google Docs.
Это неверно: Google Docs WebSocket не использует. Поговорим о том, как там всё устроено на самом деле.
🔬 Поверхностный слой – что в DevTools
Открой документ, Network → фильтр WS. WebSocket-кадров нет. Зато стабильно видно два типа XHR: POST на /save на каждое изменение и долгоживущий GET на /bind, который висит открытым. Печатаешь ты – уходят POST-ы. Печатает сосед – по тому же /bind приезжают его правки.
⚙️ Средний слой – как это работает
Это исторический протокол Google – BrowserChannel (он же лежал в основе чата Gmail). Два виртуальных канала поверх голого HTTP:
🔹 Forward-канал: правки уходят пачками (bundles) обычным POST на /save. Каждая операция – insert/delete с позицией, к ней привязаны sid и requestId.
🔹 Back-канал: чужие правки и презенс прилетают через hanging GET на /bind. Сервер держит ответ открытым и дописывает в него события через chunked-стриминг, переоткрывая соединение примерно раз в 4 минуты.
Ключевая деталь: перед стартом клиент тестирует сеть на буферизующий прокси. Если прокси копит ответ – канал деградирует до серии коротких GET-ов (классический long-poll). Протокол сам подстраивается под враждебную сеть.
🧬 Глубокий слой – почему так
🔸 История. Docs появился в 2006, а WebSocket стандартизировали только в 2011 (RFC 6455). Real-time запустили за 5 лет до того, как WS стал реальной опцией.
🔸 Инфраструктура. Корпоративные прокси и firewall резали WebSocket-хендшейк. HTTP проходит везде, масштабируется stateless-серверами, кешируется, проксируется и виден в логах. Фейлы явные, не "тихие".
И вот главный урок, в котором была суть нашей ошибки. Выбор транспорта (POST vs WebSocket) ортогонален выбору алгоритма сходимости (OT vs CRDT). Это два независимых слоя. Сходимость отвечает "как смержить конкурентные правки". Транспорт – "как доставить байты". Жёсткой связки между ними нет. В прошлом посте мы склеили эти слои в один – вот в чём ошибка по сути.
🤝 Совместное редактирование в других системах
Комбинации транспорта и алгоритма сходимости бывают разные:
🔸 Google Docs – OT (серверный арбитр Jupiter) поверх HTTP POST на /save и долгого GET на /bind (BrowserChannel).
🔸 Figma – CRDT-вдохновлённый мерж (LWW-register, серверный порядок) поверх WebSocket. Эталон WS-со-редактирования.
🔸 Notion – гибрид: правки уходят POST на /saveTransactions, а нотификации о чужих правках прилетают через WebSocket pub/sub.
🔸 Firebase Firestore – real-time-листенеры на WebChannel: XHR/Fetch-стриминг с фолбэком в long-poll.
🔸 Yjs-приложения – CRDT (YATA) поверх любого провайдера: y-websocket, y-webrtc, даже HTTP. Транспорт подключаемый.
Вывод: транспорт и алгоритм сходимости – независимые оси. Их выбирают под продукт, сеть и историю; жёсткой связки "OT → POST" или "CRDT → WebSocket" нет.
И не путай термины: long-poll завершает ответ на каждом событии, HTTP streaming держит и дописывает, SSE – отдельный стандарт с text/event-stream. Docs формально делает streaming с long-poll-фолбэком.
Честно про источник: /save, /bind, sid – это реверс-инжиниринг и наблюдения в DevTools (2014–2025), не официальная документация. И это не публичный Google Docs API (documents.batchUpdate) – тот про батч-интеграции, не про живой редактор.
📚 Что почитать:
"What technology does Google Drive use to get real-time updates?" – разбор Max Heiber на StackOverflow
"How Figma's multiplayer technology works" – Figma Blog (контраст: эталон WebSocket-со-редактирования)
Еще раз спасибо Юрию! Для этого и нужно наше сообщество – обмениваться знаниями и вместе узнавать новое.