@sovereignbase/sequencer 0.0.1 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/INTERNALS.md +1530 -0
- package/LICENSE +201 -201
- package/README.md +128 -90
- package/dist/index.cjs +264 -160
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +118 -12
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.ts +118 -12
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +265 -160
- package/dist/index.js.map +1 -1
- package/package.json +27 -27
package/README.md
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
|
+
[](https://www.npmjs.com/package/@sovereignbase/sequencer)
|
|
2
|
+
[](https://jsr.io/@sovereignbase/sequencer)
|
|
3
|
+
[](https://github.com/sovereignbase/sequencer/actions/workflows/ci.yaml)
|
|
4
|
+
[](https://codecov.io/gh/sovereignbase/sequencer)
|
|
5
|
+
[](LICENSE)
|
|
6
|
+
|
|
1
7
|
# Sequencer
|
|
2
8
|
|
|
3
|
-
Sequencer provides a deterministic total ordering for distributed data. It allows independently operating
|
|
9
|
+
Sequencer provides a deterministic total ordering for distributed data. It allows independently operating actors to make concurrent changes and eventually converge on the same state without relying on network arrival order or perfectly synchronized clocks.
|
|
4
10
|
|
|
5
11
|
## Usage
|
|
6
12
|
|
|
@@ -10,128 +16,160 @@ Sequencer provides a deterministic total ordering for distributed data. It allow
|
|
|
10
16
|
import { Projection } from '@sovereignbase/sequencer'
|
|
11
17
|
|
|
12
18
|
const projection = new Projection<string>(1)
|
|
13
|
-
|
|
19
|
+
|
|
20
|
+
const gossip0 = projection.insert(['Hello World'], 0)
|
|
14
21
|
console.log(projection.value(0)) // "Hello World"
|
|
22
|
+
const gossip1 = projection.replace(['Hello Dude'], 0, 0)
|
|
23
|
+
console.log(projection.value(0)) // "Hello Dude"
|
|
24
|
+
const gossip2 = projection.remove(0, 0)
|
|
25
|
+
console.log(projection.value(0)) // undefined
|
|
15
26
|
|
|
16
27
|
projection.destroy()
|
|
17
28
|
```
|
|
18
29
|
|
|
30
|
+
### See documentation
|
|
31
|
+
|
|
32
|
+
- [How sequencer works?](/INTERNALS.md)
|
|
33
|
+
- [Typescript](https://sovereignbase.dev/sequencer/)
|
|
34
|
+
- [C++](https://sovereignbase.dev/sequencer/c++)
|
|
35
|
+
|
|
19
36
|
## Benchmarks
|
|
20
37
|
|
|
21
|
-
###
|
|
38
|
+
### Moderate bundle size
|
|
22
39
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
40
|
+
| Format | Raw (KiB) | Minified (KiB) | Minified + gzip (KiB) |
|
|
41
|
+
| -------- | --------: | -------------: | --------------------: |
|
|
42
|
+
| ESM | 123.83 | 100.04 | 36.07 |
|
|
43
|
+
| CommonJS | 123.94 | 100.86 | 36.28 |
|
|
27
44
|
|
|
28
|
-
|
|
29
|
-
public API call, weighted by call count across five runs.
|
|
45
|
+
Both bundles include WASM. Terser uses three compression passes; gzip uses level 9.
|
|
30
46
|
|
|
31
|
-
|
|
32
|
-
| ----------------------------------- | ---------------------: | -----------------------: | ------------------------: |
|
|
33
|
-
| `new Projection(actorID, sequence)` | 1.732 | 1.725 | 1.648 |
|
|
34
|
-
| `insert`, random position | 1.043 | 1.036 | 1.081 |
|
|
35
|
-
| `remove`, middle | 1.091 | 1.269 | 1.433 |
|
|
36
|
-
| `replace`, middle | 1.145 | 1.369 | 1.430 |
|
|
37
|
-
| `sequence()` | 0.353 | 0.390 | 0.363 |
|
|
38
|
-
| `value`, random position | 0.065 | 0.053 | 0.115 |
|
|
39
|
-
| `values()` | 4.352 | 147.965 | 1156.844 |
|
|
47
|
+
### Average throughput during editing to checkpoint
|
|
40
48
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
64 calls, then measures a burst of approximately 100 ms or at most 10,000 calls.
|
|
49
|
+
Arithmetic means of 3 runs, in ops/s, from each phase's start to checkpoint.
|
|
50
|
+
Strips contain 1–100 Frames (visible entries). Headers show Strip counts and mean Frame counts.
|
|
44
51
|
|
|
45
|
-
|
|
52
|
+
**↑ Scale up (0 → 25,000 Strips)**
|
|
46
53
|
|
|
47
|
-
|
|
48
|
-
|
|
54
|
+
| Method / case | 1,000 Strips<br>50,871 Frames | 5,000 Strips<br>252,214 Frames | 10,000 Strips<br>504,871 Frames | 15,000 Strips<br>758,280 Frames | 20,000 Strips<br>1,010,749 Frames | 25,000 Strips<br>1,264,003 Frames |
|
|
55
|
+
| -------------- | ----------------------------: | -----------------------------: | ------------------------------: | ------------------------------: | --------------------------------: | --------------------------------: |
|
|
56
|
+
| apply.random | 146,512 | 50,162 | 35,425 | 25,372 | 17,992 | 13,401 |
|
|
57
|
+
| insert.head | 1,103,240 | 627,952 | 723,841 | 741,193 | 692,500 | 629,581 |
|
|
58
|
+
| insert.random | 423,106 | 138,442 | 91,906 | 64,213 | 44,063 | 33,092 |
|
|
59
|
+
| insert.tail | 1,247,342 | 779,594 | 895,027 | 892,070 | 830,660 | 765,706 |
|
|
60
|
+
| remove.head | — | — | — | — | — | — |
|
|
61
|
+
| remove.random | 258,681 | 95,210 | 65,861 | 46,668 | 31,655 | 23,556 |
|
|
62
|
+
| remove.tail | — | — | — | — | — | — |
|
|
63
|
+
| replace.head | 380,869 | 309,212 | 303,788 | 291,544 | 270,032 | 245,792 |
|
|
64
|
+
| replace.random | 253,774 | 94,640 | 63,352 | 45,272 | 31,633 | 23,736 |
|
|
65
|
+
| replace.tail | 392,296 | 315,718 | 313,645 | 310,052 | 268,791 | 247,351 |
|
|
66
|
+
| value.head | 2,283,059 | 2,095,440 | 1,999,974 | 2,016,602 | 1,907,994 | 1,721,581 |
|
|
67
|
+
| value.random | 569,971 | 133,693 | 78,867 | 51,725 | 34,540 | 25,316 |
|
|
68
|
+
| value.tail | 4,749,438 | 3,636,984 | 3,461,656 | 3,227,876 | 2,914,996 | 2,408,625 |
|
|
49
69
|
|
|
50
|
-
|
|
51
|
-
| --------------------------- | --------------------------: | -------: |
|
|
52
|
-
| Additional JS heap after GC | 2.038 MB | 0.520 MB |
|
|
53
|
-
| Shared WASM memory capacity | 7.406 MB | 1.049 MB |
|
|
70
|
+
**↓ Scale down (25,000 → 0 Strips)**
|
|
54
71
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
72
|
+
| Method / case | 20,000 Strips<br>1,012,742 Frames | 15,000 Strips<br>755,860 Frames | 10,000 Strips<br>504,103 Frames | 5,000 Strips<br>251,203 Frames | 1,000 Strips<br>51,057 Frames | 0 Strips<br>0 Frames |
|
|
73
|
+
| -------------- | --------------------------------: | ------------------------------: | ------------------------------: | -----------------------------: | ----------------------------: | -------------------: |
|
|
74
|
+
| apply.random | 7,268 | 7,592 | 8,263 | 8,783 | 9,083 | 9,219 |
|
|
75
|
+
| insert.head | — | — | — | — | — | — |
|
|
76
|
+
| insert.random | 17,296 | 18,458 | 20,290 | 21,279 | 22,052 | 22,459 |
|
|
77
|
+
| insert.tail | — | — | — | — | — | — |
|
|
78
|
+
| remove.head | 157,981 | 165,917 | 180,514 | 178,894 | 181,826 | 183,709 |
|
|
79
|
+
| remove.random | 13,036 | 13,992 | 15,527 | 16,535 | 17,163 | 17,459 |
|
|
80
|
+
| remove.tail | 192,199 | 192,220 | 203,528 | 209,615 | 209,694 | 210,394 |
|
|
81
|
+
| replace.head | 176,953 | 178,240 | 186,921 | 185,389 | 181,259 | 161,071 |
|
|
82
|
+
| replace.random | 13,038 | 14,084 | 15,647 | 16,708 | 17,397 | 17,697 |
|
|
83
|
+
| replace.tail | 190,938 | 184,284 | 193,773 | 187,715 | 182,304 | 182,496 |
|
|
84
|
+
| value.head | 794,128 | 838,606 | 885,880 | 922,502 | 940,112 | 948,944 |
|
|
85
|
+
| value.random | 13,677 | 14,768 | 16,600 | 18,037 | 18,894 | 19,271 |
|
|
86
|
+
| value.tail | 1,070,897 | 1,038,948 | 1,093,604 | 1,140,368 | 1,178,006 | 1,192,164 |
|
|
59
87
|
|
|
60
|
-
###
|
|
88
|
+
### API throughput at checkpoint
|
|
61
89
|
|
|
62
|
-
|
|
63
|
-
| -------- | -------: | -------: | --------------: |
|
|
64
|
-
| ESM | 117.9 kB | 99.7 kB | 36.4 kB |
|
|
65
|
-
| CommonJS | 118.0 kB | 100.6 kB | 36.6 kB |
|
|
90
|
+
Checkpoint calls, in ops/s, averaged across runs.
|
|
66
91
|
|
|
67
|
-
|
|
68
|
-
compression passes; source maps and type declarations are excluded. kB and MB
|
|
69
|
-
use decimal units.
|
|
92
|
+
**↑ Scale up (0 → 25,000 Strips)**
|
|
70
93
|
|
|
71
|
-
|
|
94
|
+
| Method | 1,000 Strips<br>50,871 Frames | 5,000 Strips<br>252,214 Frames | 10,000 Strips<br>504,871 Frames | 15,000 Strips<br>758,280 Frames | 20,000 Strips<br>1,010,749 Frames | 25,000 Strips<br>1,264,003 Frames |
|
|
95
|
+
| --------------------------------- | ----------------------------: | -----------------------------: | ------------------------------: | ------------------------------: | --------------------------------: | --------------------------------: |
|
|
96
|
+
| new Projection(actorID, sequence) | 2,513 | 795 | 408 | 215 | 154 | 107 |
|
|
97
|
+
| sequence() | 375 | 66 | 33 | 18 | 11 | 6 |
|
|
98
|
+
| values() | 480 | 163 | 108 | 70 | 49 | 32 |
|
|
72
99
|
|
|
73
|
-
|
|
74
|
-
framing or content. Numeric metadata accepts `Uint32Array` and JSON numeric
|
|
75
|
-
arrays. Generic content, called Footage, stays in JavaScript; only numeric
|
|
76
|
-
metadata enters WASM.
|
|
100
|
+
**↓ Scale down (25,000 → 0 Strips)**
|
|
77
101
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
arrays or `Uint32Array` metadata; exported numeric arrays can be sent as JSON.
|
|
102
|
+
| Method | 20,000 Strips<br>1,012,742 Frames | 15,000 Strips<br>755,860 Frames | 10,000 Strips<br>504,103 Frames | 5,000 Strips<br>251,203 Frames | 1,000 Strips<br>51,057 Frames | 0 Strips<br>0 Frames |
|
|
103
|
+
| --------------------------------- | --------------------------------: | ------------------------------: | ------------------------------: | -----------------------------: | ----------------------------: | -------------------: |
|
|
104
|
+
| new Projection(actorID, sequence) | 116 | 212 | 273 | 586 | 1,881 | 16,098 |
|
|
105
|
+
| sequence() | 7 | 6 | 5 | 4 | 4 | 38,383 |
|
|
106
|
+
| values() | 44 | 51 | 58 | 62 | 199 | 68,912 |
|
|
84
107
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
```
|
|
108
|
+
### Sequence disk size at checkpoint
|
|
109
|
+
|
|
110
|
+
Complete exported Sequences, including Footage and acknowledgements. Sizes are in KiB.
|
|
89
111
|
|
|
90
|
-
|
|
91
|
-
without reserving another zero in its logical clock. Gossip contains only the
|
|
92
|
-
new suffix, still using six structural words. A removal or fragmentation ends
|
|
93
|
-
the local extension chain. Retained snapshots remain unchanged by later edits.
|
|
112
|
+
**↑ Scale up (0 → 25,000 Strips)**
|
|
94
113
|
|
|
95
|
-
|
|
96
|
-
|
|
114
|
+
| Encoding | 1,000 Strips<br>50,871 Frames | 5,000 Strips<br>252,214 Frames | 10,000 Strips<br>504,871 Frames | 15,000 Strips<br>758,280 Frames | 20,000 Strips<br>1,010,749 Frames | 25,000 Strips<br>1,264,003 Frames |
|
|
115
|
+
| ------------------ | ----------------------------: | -----------------------------: | ------------------------------: | ------------------------------: | --------------------------------: | --------------------------------: |
|
|
116
|
+
| JSON | 277.17 | 1,621.65 | 3,261.72 | 4,901.02 | 7,041.25 | 9,162.75 |
|
|
117
|
+
| JSON + gzip | 14.59 | 73.84 | 150.38 | 226.94 | 311.22 | 391.69 |
|
|
118
|
+
| MessagePack | 164.81 | 818.88 | 1,639.69 | 3,529.41 | 5,081.04 | 6,483.22 |
|
|
119
|
+
| MessagePack + gzip | 12.38 | 61.62 | 124.91 | 198.59 | 263.54 | 332.77 |
|
|
97
120
|
|
|
98
|
-
|
|
99
|
-
| ---------------------------------------------- | ---------------------------------------: | ------: | ----------: | -------------: |
|
|
100
|
-
| 1,000 one-frame insertions | 44.8 B | 45.8 kB | 5.1 kB | 88.9% |
|
|
101
|
-
| 1,000 one-frame removals | 67.8 B | 68.8 kB | 7.7 kB | 88.9% |
|
|
102
|
-
| Snapshot containing 1,000 one-frame insertions | 4.0 B | 4.0 kB | 0.1 kB | 98.3% |
|
|
121
|
+
**↓ Scale down (25,000 → 0 Strips)**
|
|
103
122
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
123
|
+
| Encoding | 20,000 Strips<br>1,012,742 Frames | 15,000 Strips<br>755,860 Frames | 10,000 Strips<br>504,103 Frames | 5,000 Strips<br>251,203 Frames | 1,000 Strips<br>51,057 Frames | 0 Strips<br>0 Frames |
|
|
124
|
+
| ------------------ | --------------------------------: | ------------------------------: | ------------------------------: | -----------------------------: | ----------------------------: | -------------------: |
|
|
125
|
+
| JSON | 7,446.44 | 5,617.36 | 3,769.76 | 1,885.51 | 383.23 | 0.02 |
|
|
126
|
+
| JSON + gzip | 323.23 | 244.45 | 163.91 | 80.61 | 16.33 | 0.03 |
|
|
127
|
+
| MessagePack | 5,237.17 | 3,930.33 | 2,628.38 | 1,312.43 | 266.67 | 0.01 |
|
|
128
|
+
| MessagePack + gzip | 274.96 | 207.78 | 137.38 | 67.41 | 13.67 | 0.03 |
|
|
129
|
+
|
|
130
|
+
### Memory usage at checkpoint
|
|
131
|
+
|
|
132
|
+
One instance: WASM memory plus retained JS array slots, in KiB (1,024 bytes).
|
|
133
|
+
|
|
134
|
+
**↑ Scale up (0 → 25,000 Strips)**
|
|
135
|
+
|
|
136
|
+
| Measurement | 1,000 Strips<br>50,871 Frames | 5,000 Strips<br>252,214 Frames | 10,000 Strips<br>504,871 Frames | 15,000 Strips<br>758,280 Frames | 20,000 Strips<br>1,010,749 Frames | 25,000 Strips<br>1,264,003 Frames |
|
|
137
|
+
| ---------------------- | ----------------------------: | -----------------------------: | ------------------------------: | ------------------------------: | --------------------------------: | --------------------------------: |
|
|
138
|
+
| Estimated memory (KiB) | 5,343.67 | 25,100.58 | 50,470.89 | 82,701.27 | 98,036.35 | 153,462.93 |
|
|
139
|
+
|
|
140
|
+
**↓ Scale down (25,000 → 0 Strips)**
|
|
141
|
+
|
|
142
|
+
| Measurement | 20,000 Strips<br>1,012,742 Frames | 15,000 Strips<br>755,860 Frames | 10,000 Strips<br>504,103 Frames | 5,000 Strips<br>251,203 Frames | 1,000 Strips<br>51,057 Frames | 0 Strips<br>0 Frames |
|
|
143
|
+
| ---------------------- | --------------------------------: | ------------------------------: | ------------------------------: | -----------------------------: | ----------------------------: | -------------------: |
|
|
144
|
+
| Estimated memory (KiB) | 163,746.93 | 174,034.00 | 202,660.72 | 293,941.85 | 302,158.29 | 304,203.30 |
|
|
145
|
+
|
|
146
|
+
Measured on Node 24.16.0, V8 13.6.233.17-node.49,
|
|
147
|
+
win32 x64, Intel(R) Core(TM) i5-10210U CPU @ 1.60GHz.
|
|
148
|
+
Peer synchronization, encoding and JS process garbage collection are outside API timings.
|
|
149
|
+
[Full lifecycle results](benchmark/.results/lifecycle.md) · [Throughput results](benchmark/.results/throughput.md).
|
|
108
150
|
|
|
109
151
|
## Tests
|
|
110
152
|
|
|
111
153
|
### Behaviour guaranteed by excessive tests
|
|
112
154
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
The latest verification passed 167 Vitest tests and 88 compatibility checks.
|
|
118
|
-
Convergence tests include 1,024 generated editing histories for each compaction
|
|
119
|
-
mode. See the [test suites](test/README.md) and
|
|
155
|
+
The latest verification passed 178 Vitest tests and 88 compatibility checks (1 failed).
|
|
156
|
+
The configured 100% coverage threshold is met (100% statements, 100% branches, 100% functions, 100% lines).
|
|
157
|
+
See the [test suites](test/README.md) and
|
|
120
158
|
[automated test report](https://sovereignbase.dev/sequencer/tests/).
|
|
121
159
|
|
|
122
160
|
### Works everywhere where JavaScript and Wasm works
|
|
123
161
|
|
|
124
162
|
Runtimes covered by the latest compatibility verification:
|
|
125
163
|
|
|
126
|
-
- Node.js, ESM
|
|
127
|
-
-
|
|
128
|
-
-
|
|
129
|
-
-
|
|
130
|
-
-
|
|
131
|
-
-
|
|
132
|
-
|
|
133
|
-
Firefox
|
|
134
|
-
|
|
164
|
+
- Node.js, ESM: passed
|
|
165
|
+
- Node.js, CommonJS: passed
|
|
166
|
+
- Deno: passed
|
|
167
|
+
- Bun: passed
|
|
168
|
+
- Edge Runtime: passed
|
|
169
|
+
- Cloudflare Workers through workerd: passed
|
|
170
|
+
- Chromium browser windows: passed
|
|
171
|
+
- Firefox browser windows: failed (matching Playwright browser binary unavailable)
|
|
172
|
+
- WebKit browser windows: passed
|
|
135
173
|
|
|
136
174
|
### Tested realtime and offline text editing behaviour examples
|
|
137
175
|
|
|
@@ -143,13 +181,13 @@ binary was unavailable in the latest run.
|
|
|
143
181
|
|
|
144
182
|
## Credits
|
|
145
183
|
|
|
146
|
-
|
|
184
|
+
This could not have been made without years of work from many great engineers of many great projects, to mention a few:
|
|
147
185
|
|
|
148
|
-
- AUTOMERGE
|
|
186
|
+
- [AUTOMERGE](https://github.com/automerge)
|
|
149
187
|
|
|
150
|
-
- YJS
|
|
188
|
+
- [YJS](https://github.com/yjs)
|
|
151
189
|
|
|
152
|
-
- DIAMOND TYPES
|
|
190
|
+
- [DIAMOND TYPES](https://github.com/josephg/diamond-types)
|
|
153
191
|
|
|
154
192
|
## License
|
|
155
193
|
|