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

887 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 137.99 110.79 39.32
CommonJS 138.13 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,895 86,573 61,605 48,732 40,398 34,057
insert.head 1,238,365 995,027 1,117,145 1,167,131 1,206,064 1,229,409
insert.random 511,821 242,276 163,479 127,816 104,243 86,916
insert.tail 1,408,741 1,123,363 1,267,235 1,331,483 1,372,341 1,392,934
remove.head — — — — — —
remove.random 308,323 164,586 112,774 86,224 69,134 57,629
remove.tail — — — — — —
replace.head 443,410 418,001 438,874 441,873 453,131 460,214
replace.random 268,050 154,252 107,365 82,055 66,782 55,835
replace.tail 403,298 443,262 472,039 488,178 465,759 464,021
value.head 2,434,965 3,112,412 3,421,309 3,684,089 3,867,804 3,922,585
value.random 717,920 235,540 139,919 99,843 78,819 64,134
value.tail 4,950,895 5,171,163 5,231,274 5,312,859 5,383,719 5,302,240

↓ 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 22,907 23,870 24,257 25,022 26,128 26,553
insert.head — — — — — —
insert.random 54,792 57,896 60,350 62,369 65,804 66,158
insert.tail — — — — — —
remove.head 612,390 604,294 605,519 607,193 564,707 568,840
remove.random 37,925 40,879 42,768 45,400 48,220 48,909
remove.tail 588,179 477,651 507,563 525,757 539,235 462,091
replace.head 424,399 413,396 409,634 375,567 360,573 315,196
replace.random 37,856 40,437 42,506 44,831 47,392 47,461
replace.tail 226,464 196,561 193,482 185,451 185,532 173,026
value.head 2,194,434 2,119,802 2,076,342 2,063,441 2,105,685 2,134,027
value.random 41,819 44,489 46,971 49,757 53,023 54,257
value.tail 2,569,701 2,499,505 2,461,820 2,467,249 2,563,195 2,606,245

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,435 960 599 421 290 244
sequence() 609 108 54 36 26 19
values() 640 246 84 51 55 33

↓ 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 420 504 1,289 5,550 23,860
sequence() 17 16 11 13 11 10
values() 62 36 88 89 219 115,201

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.48 7,045.77 9,168.43
JSON + gzip 14.59 73.84 150.40 226.90 311.15 391.67
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.92 5,621.59 3,772.60 1,886.91 383.52 0.02
JSON + gzip 323.18 244.43 163.92 80.65 16.35 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