Testing PowerSync with Jepsen for Causal Consistency, Atomic transactions, and Strong Convergence.
PowerSync is a full featured active/active sync service for PostgreSQL and local SQLite3 clients. It offers a rich API for developers to configure and define the sync behavior.
Our primary goal is to test the sync algorithm, its core implementation, and develop best practices for
Operating under
The initial implementations of the PowerSync client and backend connector take a safety first bias:
clients do straight ahead SQL transactions:
await db.writeTransaction((tx) async {
final select = await tx.getOptional('SELECT * from lww where k = ?', [mop['k']]);
await tx.execute(
'INSERT OR REPLACE INTO lww (k,v) VALUES (?,?) ON CONFLICT (k) DO UPDATE SET v = lww.v || \' \' || ?',
[k, v, v]);
});
backend replication is transaction based:
PowerSyncDatabase.getNextCrudTransaction();
Connection.runInTransaction();
The implementation will evolve to use other data models and APIs to find the different consistency levels, performance, and usability trade-offs.
Single user, generic SQLite3 db, no PowerSync
Single user, PowerSync db, local only
Single user, PowerSync db, with replication
Multi user, generic SQLite3 shared db, no PowerSync
Multi user, PowerSync db, with replication, using example backend connector
Multi user, PowerSync db, with replication, using newly developed Causal connector
Multi user, Active/Active PostgreSQL/SQLite, with replication
The client will be a simple Dart CLI PowerSync implementation. It will also expose an http endpoint for Jepsen to post transactions to and receive the results.
Clients are expected to have total sticky availability
Observe and interact with the database
PowerSyncDatabase
driver
writeTransaction
with multiple statementsreadTransaction
\ writeTransaction
watch
query stream
SqliteDatabase
driver
LoFi and distributed systems live in a rough and tumble environment.
Successful applications, amount/duration of use, will be exposed to faults. Reality is like that.
Faults are applied
We still expect total sticky availability unless the client has been explicitly paused/killed.
close()
, connect()
, disconnect()
, disconnectAndClose()
kill stop\cont
client process(es)kill -9
client process(es)Last write wins, with last defined as the last transaction executed on the PostgreSQL backend with repeatable read
isolation.
The conflict/merge algorithm isn't important to the test. It just needs to be
Public GitHub repository
Development Logbook.
GitHub Actions.
Docker environment to run tests.
Miscellaneous notes on usage.