CRDT LF SQLite

crdt_lf_sqlite_badge pub points pub likes codecov ci_badge License: MIT pub publisher

docs_badge

A sqlite3 storage implementation for CRDT LF objects, providing efficient persistence for Change and Snapshot objects with document-scoped organization in a single SQLite database.

Features

  • Compact Binary Storage: Change and Snapshot are persisted as the self-describing binary blobs produced by crdt_lf's native toBytes() / fromBytes() methods
  • Single Database, Many Documents: one database holds changes and snapshots tables
  • Document-Scoped Storage: utilities that organize data by document ID for better isolation and querying
  • Synchronous API: sqlite3 is synchronous (FFI), so the storage API is synchronous too

Quick Start

1. Open a database

import 'package:crdt_lf_sqlite/crdt_lf_sqlite.dart';

void main() {
  // Open (or create) a database file...
  final storage = CRDTSqlite.open('./my_app_data.db');

  // ...or an in-memory database (useful for tests):
  final memory = CRDTSqlite.memory();

  // Your app code here...

  storage.close();
}

2. Document-scoped storage

import 'package:crdt_lf/crdt_lf.dart';
import 'package:crdt_lf_sqlite/crdt_lf_sqlite.dart';

const documentId = 'my-document-id';

final storage = CRDTSqlite.open('./my_app_data.db');

// Open storage for a specific document
final changeStorage = storage.changeStorageForDocument(documentId);
final snapshotStorage = storage.snapshotStorageForDocument(documentId);

// Or open both at once
final documentStorage = storage.storageForDocument(documentId);

Document-Scoped Storage

Data for different documents lives in the same tables and is isolated through the document_id column.

CRDTSqliteChangeStorage

Manages Change objects for a specific document:

final changeStorage = storage.changeStorageForDocument('doc-123');

// Save individual changes
changeStorage.saveChange(change);

// Batch save multiple changes
changeStorage.saveChanges([change1, change2, change3]);

// Load all changes for the document
final changes = changeStorage.getChanges();

// Delete changes
changeStorage.deleteChange(change);
changeStorage.deleteChanges([change1, change2]);

// Storage info
print('Total changes: ${changeStorage.count}');
print('Is empty: ${changeStorage.isEmpty}');

CRDTSqliteSnapshotStorage

Manages Snapshot objects for a specific document:

final snapshotStorage = storage.snapshotStorageForDocument('doc-123');

// Save snapshots
snapshotStorage.saveSnapshot(snapshot);
snapshotStorage.saveSnapshots([snapshot1, snapshot2]);

// Retrieve snapshots
final snapshot = snapshotStorage.getSnapshot('snapshot-id');
final allSnapshots = snapshotStorage.getSnapshots();

// Check existence
if (snapshotStorage.containsSnapshot('snapshot-id')) {
  // Snapshot exists
}

How Data Is Stored

Both Change and Snapshot are stored as opaque binary blobs using the self-describing format provided by crdt_lf (toBytes() / fromBytes()). The schema is two tables:

CREATE TABLE changes (
  document_id TEXT NOT NULL,
  change_id   TEXT NOT NULL,
  bytes       BLOB NOT NULL,
  PRIMARY KEY (document_id, change_id)
);

CREATE TABLE snapshots (
  document_id TEXT NOT NULL,
  snapshot_id TEXT NOT NULL,
  bytes       BLOB NOT NULL,
  PRIMARY KEY (document_id, snapshot_id)
);

Changes are keyed by change.id, snapshots by snapshot.id.

Examples

A complete example is available here.

Storage Management

// Delete all data for a specific document
storage.deleteDocumentData('doc-123');

// Close the database and release resources
storage.close();

Roadmap

A roadmap is available in the project page. The roadmap provides a high-level overview of the project's goals and the current status of the project.

Apps

Packages

Other bricks of the crdt "system" are:

Libraries

crdt_lf_sqlite
A sqlite3 storage implementation for CRDT (Conflict-free Replicated Data Type) objects.