Розробити веб-сайт, який функціонує як синхронний текстовий редактор. Документ заданого формату можуть редагувати одночасно кілька користувачів, а зміни видно в режимі реального часу.
- Користувач відкриває документ → клієнт робить REST запит, отримує snapshot стану.
- Клієнт підключається по WebSocket → починає отримувати всі нові зміни.
- Коли користувач вносить зміни → вони відправляються на сервер (WebSocket повідомлення).
- Сервер застосовує зміни до CRDT-моделі і:
- розсилає їх всім іншим учасникам,
- зберігає update в
document_updates.
- Періодично або за подією (наприклад, закриття документа) сервер робить snapshot в
documents.state.
POST /documents→ створити документ.GET /documents/{id}→ повернути snapshot документа.GET /documents/{id}/updates?since=...→ повернути лог змін (для відновлення при reconnect).
[! NOTE] snapshot зберігається в
documents.state(серіалізований CRDT). логи зберігаються вdocument_updates. при reconnect клієнт може отримати всі зміни, яких у нього немає.
- Підняти WebSocket endpoint
/ws/{document_id}. - Клієнт відправляє операції у форматі CRDT update.
- Сервер застосовує update до документа в базі даних, розсилає іншим.
[! NOTE] CRDT забезпечує унікальні ідентифікатори для кожного символу. При вставці двох символів в одне місце → порядок визначається їх ID (за часом + випадковий компонент). При видаленні сервер просто позначає символ як «видалений» (але зберігає ID для консистентності). Таким чином, конфліктів «чий символ залишився» не виникає.
- Підтримувати документ у базі даних (модель CRDT).
- Логувати зміни в
document_updates. - Періодично зберігати snapshot в
documents.state.
Таблиця documents для зберігання стану (snapshot).
CREATE TABLE documents (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
title TEXT NOT NULL,
state BYTEA NOT NULL,
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
Таблиця document_updates для зберігання логів змін.
CREATE TABLE document_updates (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
document_id UUID NOT NULL REFERENCES documents(id) ON DELETE CASCADE,
update BYTEA NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
[! NOTE] При запуску редагування сервер піднімає модель CRDT зі snapshot + логів. Всі нові зміни записуються в базу даних. Snapshot потрібен для швидкого завантаження (щоб не накочувати 10k змін кожен раз).
- Якщо клієнт відключився → при reconnect він відправляє
last_known_update_id. - Сервер повертає всі зміни після цього ID.
- Якщо сервер падає → після рестарту CRDT відновлюється з snapshot + останніх змін.
[! NOTE] Такий підхід гарантує, що ніхто не втратить дані, навіть якщо сервер «впав» прямо в момент редагування.
-
Базовий бекенд
- Налаштувати сервер за допомогою
actix_web. - Додати необхідні ендпоінти.
- Налаштувати сервер за допомогою
-
База даних
- Створити базу даних psql.
- Створити міграції з таблицями
documents,document_updates. - Додати необхідні CRUD-операції для документів та відповідні ендпоінти.
-
WebSocket шар
- Реалізувати підключення до
/ws/{document_id}.
- Реалізувати підключення до
-
CRDT модель
- Підключити бібліотеку CRDT.
- Налаштувати серіалізацію/десеріалізацію стану.
- Додати необхідні методи для застосування змін і їх відправки.
-
Frontend
- Інтерфейс з редактором (наприклад, CodeMirror з CRDT адаптером).
- Підключення по WebSocket і оновлення стану редактора.
-
Збереження стану
- При кожній зміні → лог в
document_updates. - Snapshot в
documents.stateраз в N секунд або при закритті документа.
- При кожній зміні → лог в
Rostyslav Kashper and Mariia Kaduk
GitHub: FantRS and MashaKaduk