Skip to content
sovereignbasePublic

About

A causality-encoding engine written in TypeScript and C++ to build high-performance, conflict-free replicated data types for the web.

Topics

Resources

Code of conduct

Contributing

Stars

1 star

Watchers

0 watching

Forks

Latest commit

 

History

879 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

npm version JSR CI codecov license

Sequencer

Sequencer provides a total order guarantee for distributed editing. It allows independently operating actors to make concurrent edits and eventually converge on the same state without relying on network arrival order or perfectly synchronized clocks.

Usage

Simple typed API

import { Projection } from '@sovereignbase/sequencer'

const projectionA = new Projection<string>(1)

const projectionB = new Projection<string>(2)

const insertDelta = projectionA.insert(['Hello World'], 0)
console.log(projectionA.value(0)) // "Hello World"

projectionB.apply(insertDelta)
console.log(projectionB.value(0)) // "Hello World"

const replaceDelta = projectionA.replace(['Hello Dude'], 0, 0)
console.log(projectionA.value(0)) // "Hello Dude"

const replaceAcknowledgement = projectionB.apply(replaceDelta)
console.log(projectionB.value(0)) // "Hello Dude"

projectionA.apply(replaceAcknowledgement)

const removeDelta = projectionA.remove(0, 0)
console.log(projectionA.value(0)) // undefined

const removeAcknowledgement = projectionB.apply(removeDelta)
console.log(projectionB.value(0)) // undefined

projectionA.apply(removeAcknowledgement)

const serializable = projectionA.sequence()

const acknowledgements = projectionB.merge(serializable)
projectionA.apply(acknowledgements)

projectionA.destroy()
projectionB.destroy()

See documentation

Errors

Code Meaning
PROJECTION_DESTROYED An operation was called after the Projection was destroyed.
NATIVE_OPERATION_FAILED The native runtime reported an initialization or operation failure.
STRING_ENCODING_FAILED Snapshot Footage concatenation failed.
UNKNOWN_ERROR All other errors.

Invalid input to apply() or merge() returns undefined instead of throwing.

Benchmarks

Moderate bundle size

Format Raw (KiB) Minified (KiB) Minified + gzip (KiB)
ESM 138.11 110.79 39.32
CommonJS 138.25 111.81 39.39

Both bundles include WASM. Terser uses three compression passes; gzip uses level 9.

Average throughput during editing to checkpoint

Arithmetic means of 3 runs, in ops/s, from each phase's start to checkpoint. Strips contain 1–100 Frames (visible entries). Headers show Strip counts and mean Frame counts.

↑ Scale up (0 → 25,000 Strips)

Method / case 1,000 Strips
50,871 Frames
5,000 Strips
252,214 Frames
10,000 Strips
504,871 Frames
15,000 Strips
758,280 Frames
20,000 Strips
1,010,749 Frames
25,000 Strips
1,264,003 Frames
apply.random 168,988 83,955 61,650 47,049 38,583 32,071
insert.head 1,206,471 1,005,683 1,120,929 1,152,814 1,182,905 1,200,605
insert.random 478,940 239,168 162,373 122,729 98,990 81,755
insert.tail 1,334,433 1,104,239 1,256,494 1,303,969 1,339,136 1,349,061
remove.head — — — — — —
remove.random 341,765 165,565 113,075 83,800 67,429 55,746
remove.tail — — — — — —
replace.head 434,921 413,737 437,844 440,051 452,138 445,770
replace.random 289,668 153,680 102,130 77,675 63,528 53,108
replace.tail 378,410 435,943 470,911 489,325 467,618 465,423
value.head 2,248,956 3,197,040 3,540,159 3,716,627 3,920,698 3,991,212
value.random 723,289 231,174 138,597 96,795 75,674 61,147
value.tail 5,010,118 5,355,455 5,275,683 5,289,080 5,356,586 5,294,392

↓ Scale down (25,000 → 0 Strips)

Method / case 20,000 Strips
1,012,742 Frames
15,000 Strips
755,860 Frames
10,000 Strips
504,103 Frames
5,000 Strips
251,203 Frames
1,000 Strips
51,057 Frames
0 Strips
0 Frames
apply.random 20,767 21,129 21,221 21,621 22,254 22,548
insert.head — — — — — —
insert.random 49,291 50,341 51,237 51,902 53,706 53,811
insert.tail — — — — — —
remove.head 609,754 609,062 614,121 617,274 622,099 624,577
remove.random 35,772 36,882 38,342 40,067 41,973 42,560
remove.tail 596,805 587,004 596,862 604,122 610,763 504,986
replace.head 423,126 383,070 388,964 374,385 371,780 311,835
replace.random 34,738 36,411 37,348 39,079 40,869 40,848
replace.tail 227,897 198,045 194,265 184,618 184,097 170,615
value.head 2,076,382 1,986,100 1,933,034 1,933,944 1,982,144 2,003,444
value.random 38,599 39,752 41,093 42,883 44,849 45,853
value.tail 2,349,312 2,266,890 2,232,286 2,259,013 2,327,296 2,364,396

API throughput at checkpoint

Checkpoint calls, in ops/s, averaged across runs.

↑ Scale up (0 → 25,000 Strips)

Method 1,000 Strips
50,871 Frames
5,000 Strips
252,214 Frames
10,000 Strips
504,871 Frames
15,000 Strips
758,280 Frames
20,000 Strips
1,010,749 Frames
25,000 Strips
1,264,003 Frames
new Projection(actorID, sequence) 4,586 948 590 417 290 240
sequence() 520 96 55 30 24 18
values() 555 226 57 79 29 54

↓ Scale down (25,000 → 0 Strips)

Method 20,000 Strips
1,012,742 Frames
15,000 Strips
755,860 Frames
10,000 Strips
504,103 Frames
5,000 Strips
251,203 Frames
1,000 Strips
51,057 Frames
0 Strips
0 Frames
new Projection(actorID, sequence) 284 414 493 1,276 5,419 21,553
sequence() 16 14 10 10 9 8
values() 63 34 79 82 209 109,439

Sequence disk size at checkpoint

Complete exported Sequences, including Footage and acknowledgements. Sizes are in KiB.

↑ Scale up (0 → 25,000 Strips)

Encoding 1,000 Strips
50,871 Frames
5,000 Strips
252,214 Frames
10,000 Strips
504,871 Frames
15,000 Strips
758,280 Frames
20,000 Strips
1,010,749 Frames
25,000 Strips
1,264,003 Frames
JSON 277.41 1,622.78 3,264.00 4,904.44 7,045.81 9,168.42
JSON + gzip 14.60 73.84 150.39 226.88 311.17 391.65
MessagePack 164.81 818.88 1,639.69 3,529.41 5,081.04 6,483.22
MessagePack + gzip 12.38 61.62 124.91 198.59 263.54 332.77

↓ Scale down (25,000 → 0 Strips)

Encoding 20,000 Strips
1,012,742 Frames
15,000 Strips
755,860 Frames
10,000 Strips
504,103 Frames
5,000 Strips
251,203 Frames
1,000 Strips
51,057 Frames
0 Strips
0 Frames
JSON 7,451.86 5,621.57 3,772.57 1,886.91 383.51 0.02
JSON + gzip 323.17 244.37 163.86 80.61 16.34 0.03
MessagePack 5,237.17 3,930.33 2,628.38 1,312.43 266.67 0.01
MessagePack + gzip 274.96 207.78 137.38 67.41 13.67 0.03

Memory usage at checkpoint

One instance: WASM memory plus retained JS array slots, in KiB (1,024 bytes).

↑ Scale up (0 → 25,000 Strips)

Measurement 1,000 Strips
50,871 Frames
5,000 Strips
252,214 Frames
10,000 Strips
504,871 Frames
15,000 Strips
758,280 Frames
20,000 Strips
1,010,749 Frames
25,000 Strips
1,264,003 Frames
Estimated memory (KiB) 5,343.67 25,100.58 50,470.89 82,701.27 98,036.35 153,462.93

↓ Scale down (25,000 → 0 Strips)

Measurement 20,000 Strips
1,012,742 Frames
15,000 Strips
755,860 Frames
10,000 Strips
504,103 Frames
5,000 Strips
251,203 Frames
1,000 Strips
51,057 Frames
0 Strips
0 Frames
Estimated memory (KiB) 163,746.93 174,034.00 202,660.72 293,941.85 302,158.29 304,203.30

Measured on Node 24.21.0, V8 13.6.233.17-node.53, linux x64, AMD EPYC 7763 64-Core Processor. Peer synchronization, encoding and JS process garbage collection are outside API timings. Full lifecycle results · Throughput results.

Tests

Behaviour guaranteed by excessive tests

The latest verification passed 249 Vitest tests and 117 compatibility checks. The configured 100% coverage threshold is met (100% statements, 100% branches, 100% functions, 100% lines). See the test suites and automated test report.

Works everywhere where JavaScript and Wasm works

Runtimes covered by the latest compatibility verification:

  • Node.js, ESM: passed
  • Node.js, CommonJS: passed
  • Deno: passed
  • Bun: passed
  • Edge Runtime: passed
  • Cloudflare Workers through workerd: passed
  • Chromium browser windows: passed
  • Firefox browser windows: passed
  • WebKit browser windows: passed

Tested realtime and offline text editing behaviour examples

  • Online and offline editors: six editors converge through delayed, reversed and shuffled delivery, including offline changes arriving during online editing.
  • Concurrent replacements: independent replacement histories retain the same surviving content across delivery orders.
  • Restart and redelivery: stored Sequences preserve visible content through hydration, duplicate delivery and further edits.
  • Peer-to-peer fanout: independently editing browser peers synchronize through BroadcastChannel.
  • WebSocket fanout: three browser peers converge through a stateless JSON Gossip relay.

Credits

This could not have been made without years of work from many great engineers of many great projects, to mention a few:

License

Apache-2.0

About

A causality-encoding engine written in TypeScript and C++ to build high-performance, conflict-free replicated data types for the web.

Topics

Resources

Code of conduct

Contributing

Stars

1 star

Watchers

0 watching

Forks

Releases

Used by

Contributors

Languages