Реалізація CRDT для безконфліктної синхронізації в мобільному додатку

TRUETECH займається розробкою, підтримкою та обслуговуванням мобільних додатків iOS, Android, PWA. Маємо великий досвід та експертизу для публікації мобільних додатків до популярних маркетів Google Play, App Store, Amazon, AppGallery та інші.

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

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

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

Послуги, які ми пропонуємо
Показано 1 з 1Усі 1735 послуг
Реалізація CRDT для безконфліктної синхронізації в мобільному додатку
Складний
від 2 тижнів до 3 місяців
Часті запитання

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

Етапи розробки

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

  • image_mobile-applications_feedme_467_0.webp
    Розробка мобільного додатка для компанії FEEDME
    792
  • image_mobile-applications_xoomer_471_0.webp
    Розробка мобільного додатку для компанії XOOMER
    671
  • image_mobile-applications_rhl_428_0.webp
    Розробка мобільного додатку для компанії RHL
    1097
  • image_mobile-applications_zippy_411_0.webp
    Розробка мобільного додатку для компанії ZIPPY
    969
  • image_mobile-applications_affhome_429_0.webp
    Розробка мобільного додатку для компанії Affhome
    914
  • image_mobile-applications_flavors_409_0.webp
    Розробка мобільного додатку для компанії FLAVORS
    495

Реалізація CRDT для конфлікт-свободної синхронізації в мобільному додатку

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

Для мобільних додатків це особливо цінно: користувач редагує в метро (offline), синхронізується дома (online), партнер робив то ж саме — 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 сталась offline на одному пристрої, а інший одночасно видалив текст навколо — insert застосується, може опинитися в «порожньому» місці, але не втратиться.

Провайдери синхронізації у Y.js

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

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

Для мобільного додатка: y-websocket для online-синхронізації + кастомний провайдер для 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 активніше підтримується, більше провайдерів синхронізації.

Векторні години та detection конфліктів

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

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

Ефективна синхронізація без передачі всього документа. При переподключенні після offline: відправляємо свій 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 поточного стану без історії. Для offline-додатків: зберігати snapshot + delta після snapshot.

Оцінка

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 тижнів.