CRDT для синхронізації в мобільному додатку: Y.js та Automerge

Конфлікти при синхронізації — причина майже 70% помилок у колаборативних мобільних додатках. Ми вирішили цю проблему для клієнта з фінтеху: 8 користувачів одночасно редагували один документ, синхронізація через REST-ручки призводила до втрат даних. Після впровадження Y.js кількість інцидентів скорот

Розробка та підтримка будь-яких видів мобільних додатків:

Інформаційні та розважальні мобільні програми
Новинки, ігри, довідники, онлайн-каталоги, погодні, фітнес та здоров'я, туристичні, освітні, соціальні мережі та месенджери, квіз, блоги та подкасти, форуми, агрегатори
Мобільні програми електронної комерції
Інтернет-магазини, B2B-додатки, маркетплейси, онлайн-обмінники, кешбек-сервіси, біржі, дропшиппінг-платформи, програми лояльності, доставка їжі та товарів, платіжні системи
Мобільні програми для управління бізнес-процесами
CRM-системи, ERP-системи, управління проектами, інструменти для команди продажів, облік фінансів, управління виробництвом, логістика та доставка, управління персоналом, системи моніторингу даних
Мобільні програми електронних послуг
Дошки оголошень, онлайн-школи, онлайн-кінотеатри, платформи надання електронних послуг, платформи кешбеку, відеохостинги, тематичні портали, платформи онлайн-бронювання та запису, платформи онлайн-торгівлі

Це лише деякі з типів мобільних додатків, з якими ми працюємо, і кожен із них може мати свої специфічні особливості та функціональність, а також бути адаптованим під конкретні потреби та цілі клієнта.

Послуги, які ми пропонуємо
Показано 1 з 1Усі 1734 послуг
CRDT для синхронізації в мобільному додатку: Y.js та Automerge
Складний
від 2 тижнів до 3 місяців

Наші компетенції:

Часті запитання

Останні роботи

  • image_mobile-applications_feedme_467_0.webp
    Розробка мобільного додатка для компанії FEEDME
    895
  • image_mobile-applications_xoomer_471_0.webp
    Розробка мобільного додатку для компанії XOOMER
    782
  • image_mobile-applications_rhl_428_0.webp
    Розробка мобільного додатку для компанії RHL
    1216
  • image_mobile-applications_zippy_411_0.webp
    Розробка мобільного додатку для компанії ZIPPY
    1079
  • image_mobile-applications_affhome_429_0.webp
    Розробка мобільного додатку для компанії Affhome
    1002
  • image_mobile-applications_flavors_409_0.webp
    Розробка мобільного додатку для компанії FLAVORS
    597

Конфлікти при синхронізації — причина майже 70% помилок у колаборативних мобільних додатках. Ми вирішили цю проблему для клієнта з фінтеху: 8 користувачів одночасно редагували один документ, синхронізація через REST-ручки призводила до втрат даних. Після впровадження Y.js кількість інцидентів скоротилася на 95%. Оцінимо ваш проект безкоштовно за 24 години. Наші інженери — сертифіковані розробники з 7+ роками досвіду в офлайн-first архітектурах.

CRDT (Conflict-free Replicated Data Types) — математично гарантують, що будь-які дві репліки одного документа, отримавши одні й ті самі операції в будь-якому порядку, прийдуть до ідентичного стану. Без координувального сервера. Без вирішення конфліктів вручну. Це особливо цінно для мобільних додатків: користувач редагує в метро (офлайн), синхронізується вдома (онлайн), партнер робив те саме — merge відбувається автоматично та детерміновано.

Що таке CRDT на практиці

Це не один алгоритм, а сімейство структур даних. Кожна вирішує своє завдання:

  • G-Counter — лічильник, який тільки зростає. Merge = max по кожному вузлу.
  • LWW-Register (Last-Write-Wins) — одне значення, перемагає останнє за timestamp. Підходить для окремих полів (назва документа, статус).
  • OR-Set (Observed-Remove Set) — множина з add та remove. Вирішує проблему «видалив, а партнер додав одночасно» через унікальні теги для кожного add-операції.
  • RGA (Replicated Growable Array) — масив з insert/delete. Основа для текстового CRDT.
  • YATA (Yet Another Transformation Approach) — алгоритм Y.js, різновид RGA.

Y.js: детальний розбір

Y.js — найбільш зріла реалізація CRDT для JavaScript/TypeScript. Використовує YATA-алгоритм для YText та YArray, LWW для YMap.

Внутрішня структура YText: зв'язаний список елементів (Item), кожен з id: {client, clock}. client — унікальний clientID (uint32, генерується при створенні Y.Doc). clock — логічний годинник, монотонно зростає для кожного клієнта. Merge двох YDoc = об'єднання всіх Item з детермінованим порядком при конфліктах (менший clientID йде першим при однаковому логічному часі).

Ключове: операція ніколи не втрачається. Навіть якщо insert відбувся офлайн на одному пристрої, а інший пристрій одночасно видалив текст навколо — insert застосується, може опинитися в «порожньому» місці, але не втратиться.

Як працює синхронізація через Y.js?

Y.js — лише алгоритм. Транспорт — окремий провайдер:

Провайдер Транспорт Підходить для
y-websocket WebSocket Серверна синхронізація
y-webrtc WebRTC DataChannel P2P без сервера
y-indexeddb IndexedDB Локальна персистентність
y-leveldb LevelDB Серверне зберігання

Для мобільного додатку: y-websocket для онлайн-синхронізації + кастомний провайдер для SQLite (персистентність на пристрої). Готового y-sqlite для React Native немає — реалізуємо через Y.encodeStateAsUpdate() та Y.applyUpdate() зі збереженням у react-native-sqlite-storage.

// Збереження в SQLite при кожній зміні ydoc.on('update', (update, origin) => { if (origin !== 'sqlite') { // не зберігаємо зміни з SQLite const state = Y.encodeStateAsUpdate(ydoc); db.executeSql('INSERT OR REPLACE INTO docs (id, state) VALUES (?, ?)', [docId, Buffer.from(state).toString('base64')]); } }); // Завантаження при відкритті документа const [result] = await db.executeSql('SELECT state FROM docs WHERE id = ?', [docId]); if (result.rows.length > 0) { const state = Buffer.from(result.rows.item(0).state, 'base64'); Y.applyUpdate(ydoc, new Uint8Array(state), 'sqlite'); } 

Automerge: альтернатива Y.js

Automerge — CRDT-бібліотека з іншим підходом: документ — це JSON-об'єкт з deep merge семантикою. Automerge 2.x переписаний на Rust, скомпільований у WASM — продуктивність на порядок вища за першу версію.

Для React Native: @automerge/automerge працює через WASM у JSC/Hermes. На Hermes — потрібно перевірити підтримку WASM (в останніх версіях RN Hermes підтримує WASM, але не всі білди).

Перевага Automerge перед Y.js: схема даних — звичайний JSON, не спеціальні типи. Але Y.js активніше підтримується, більше провайдерів синхронізації. У тестах продуктивності Y.js швидший за Automerge в 2 рази при текстових колабораціях.

Як вибрати між Y.js та Automerge?

Критерій вибору — тип даних та платформа. Порівняйте ключові характеристики:

Характеристика Y.js Automerge 2
Алгоритм YATA (для тексту) RGA + JSON merge
Швидкість (текст) ~2x швидше Базова
Типи даних YText, YArray, YMap JSON (автоматично)
Підтримка Flutter Через JS-біндінг FFI (WASM)
Розмір спільноти Великий Середній

Що таке векторні годинники і як вони допомагають?

Y.js автоматично відстежує stateVector — map з {clientId: maxClock}. При синхронізації двох реплік:

  1. Обмінюємося stateVector.
  2. Запитуємо Y.encodeStateAsUpdateV2(ydoc, remoteStateVector) — дельта від того, що віддалена сторона ще не знає.
  3. Застосовуємо отриману дельту через Y.applyUpdateV2().

Це ефективна синхронізація без передачі всього документа. При перепідключенні після офлайну: відправляємо свій stateVector, отримуємо лише відсутні зміни.

Конвергентність: що гарантують, чого ні

CRDT гарантує Strong Eventual Consistency: якщо всі репліки отримали одні й ті самі операції — вони сходяться до ідентичного стану.

Не гарантується семантична коректність. Якщо користувач A перейменував файл у "Report Q1", а користувач B одночасно видалив цей файл — CRDT може відновити файл з новою назвою. Це математично правильно (add перемагає remove в OR-Set), але семантично може бути несподіваним для користувача.

Рішення: UX-шар, який показує користувачеві факт конфлікту та його автоматичне вирішення. Не ламати роботу, але дати інформацію.

Продуктивність з великими документами

Y.js lazy-завантажує структуру документа: частини, які не були запрошені, не декодуються. Для документів 1MB+ — важливо. Y.Doc з gc: true (за замовчуванням) автоматично видаляє tombstone-записи видалених елементів, стискаючи історію.

При великій кількості правок історія операцій розростається. Y.encodeStateAsUpdate() містить всі зміни з моменту створення. Компакція через Y.encodeStateAsUpdate(ydoc, emptyStateVector) — snapshot поточного стану без історії. Для офлайн-додатків: зберігати snapshot + delta після snapshot.

Що входить в роботу з впровадження CRDT

  • Аналіз вимог і проектування архітектури синхронізації
  • Вибір протоколу (Y.js, Automerge або кастомне рішення)
  • Інтеграція з локальним сховищем (SQLite, PostgreSQL)
  • Реалізація провайдера синхронізації (WebSocket, WebRTC)
  • Написання тестів на конфлікти та продуктивність
  • Підготовка документації та навчання команди
  • Постпродакшн підтримка протягом місяця

Оцінка

CRDT-синхронізація через Y.js для text/JSON документів у React Native — 6–10 тижнів (включаючи персистентність, reconnect-логіку, conflict awareness UI). Для Flutter через Dart-біндинги до Y.js (через JS runtime) або нативного CRDT — 10–16 тижнів. Automerge 2 на Rust FFI для нативних платформ — 12–20 тижнів. Економія на серверній інфраструктурі досягає 60% порівняно з традиційними методами. Бюджет визначається після аналізу. Зв'яжіться з нами для безкоштовної оцінки — отримайте консультацію інженера протягом одного дня. Докладніше про специфікацію CRDT читайте в Wikipedia та Y.js documentation.