@sovereignbase/sequencer 1.0.0 → 1.0.2
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 +330 -951
- package/README.md +60 -41
- package/dist/index.cjs +61 -17
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +41 -4
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.ts +41 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +61 -18
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/INTERNALS.md
CHANGED
|
@@ -1,50 +1,34 @@
|
|
|
1
|
-
# How
|
|
1
|
+
# How sequencer works
|
|
2
2
|
|
|
3
|
-
Sequencer
|
|
3
|
+
Sequencer keeps an ordered sequence synchronized between replicas.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Applications see a normal indexed sequence:
|
|
6
6
|
|
|
7
7
|
```text
|
|
8
8
|
A B C D
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
with positions:
|
|
12
12
|
|
|
13
13
|
```text
|
|
14
|
-
|
|
15
|
-
remove 1..3
|
|
16
|
-
replace 4..6
|
|
14
|
+
0 1 2 3
|
|
17
15
|
```
|
|
18
16
|
|
|
19
|
-
|
|
17
|
+
Internally Sequencer cannot use those positions to describe replicated edits.
|
|
20
18
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
Sequencer solves this by translating every local edit from a temporary visible position into a stable relationship with existing history.
|
|
24
|
-
|
|
25
|
-
The basic model is:
|
|
19
|
+
If another Frame is inserted before `B`:
|
|
26
20
|
|
|
27
21
|
```text
|
|
28
|
-
|
|
29
|
-
↓
|
|
30
|
-
historical logical point
|
|
31
|
-
↓
|
|
32
|
-
canonical Insertion
|
|
33
|
-
↓
|
|
34
|
-
deterministic Structural Order
|
|
35
|
-
↓
|
|
36
|
-
visible Projection
|
|
22
|
+
A X B C D
|
|
37
23
|
```
|
|
38
24
|
|
|
39
|
-
|
|
25
|
+
`B` moves from position `1` to position `2`.
|
|
40
26
|
|
|
41
|
-
|
|
27
|
+
Instead, when an edit is made, Sequencer finds the existing history around that position and records the edit relative to it.
|
|
42
28
|
|
|
43
|
-
|
|
29
|
+
## Projection and Structural Order
|
|
44
30
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
The **Projection** is what the application sees.
|
|
31
|
+
The **Projection** is the sequence the application currently sees.
|
|
48
32
|
|
|
49
33
|
For example:
|
|
50
34
|
|
|
@@ -52,15 +36,13 @@ For example:
|
|
|
52
36
|
A B C D
|
|
53
37
|
```
|
|
54
38
|
|
|
55
|
-
|
|
39
|
+
has four visible Frames:
|
|
56
40
|
|
|
57
41
|
```text
|
|
58
42
|
A B C D
|
|
59
43
|
0 1 2 3
|
|
60
44
|
```
|
|
61
45
|
|
|
62
|
-
Applications read and edit these visible positions.
|
|
63
|
-
|
|
64
46
|
Insertion positions are boundaries between Frames:
|
|
65
47
|
|
|
66
48
|
```text
|
|
@@ -68,80 +50,37 @@ Insertion positions are boundaries between Frames:
|
|
|
68
50
|
0 1 2 3 4
|
|
69
51
|
```
|
|
70
52
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
A Frame position identifies visible content.
|
|
74
|
-
|
|
75
|
-
A boundary identifies a place where new history can be attached.
|
|
76
|
-
|
|
77
|
-
Projection positions are deliberately temporary. They describe the current visible result, not the permanent identity of anything inside it.
|
|
78
|
-
|
|
79
|
-
---
|
|
80
|
-
|
|
81
|
-
## History must use stable coordinates
|
|
82
|
-
|
|
83
|
-
Suppose two replicas begin with:
|
|
84
|
-
|
|
85
|
-
```text
|
|
86
|
-
A B
|
|
87
|
-
```
|
|
88
|
-
|
|
89
|
-
Replica 1 inserts:
|
|
53
|
+
Behind the Projection is **Structural Order**.
|
|
90
54
|
|
|
91
|
-
|
|
92
|
-
X
|
|
93
|
-
```
|
|
55
|
+
Structural Order contains the Insertions that produced the sequence, including history that may no longer be visible.
|
|
94
56
|
|
|
95
|
-
|
|
57
|
+
A simple sequence might start as:
|
|
96
58
|
|
|
97
59
|
```text
|
|
98
|
-
|
|
99
|
-
```
|
|
100
|
-
|
|
101
|
-
At the same time, Replica 2 inserts:
|
|
60
|
+
Structural Order
|
|
102
61
|
|
|
103
|
-
|
|
104
|
-
Y
|
|
105
|
-
```
|
|
62
|
+
[A B C D]
|
|
106
63
|
|
|
107
|
-
|
|
64
|
+
Projection
|
|
108
65
|
|
|
109
|
-
|
|
110
|
-
A Y B
|
|
66
|
+
A B C D
|
|
111
67
|
```
|
|
112
68
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
If Sequencer replicated only:
|
|
69
|
+
After `B C` are removed, the Projection becomes:
|
|
116
70
|
|
|
117
71
|
```text
|
|
118
|
-
|
|
119
|
-
insert Y at 1
|
|
72
|
+
A D
|
|
120
73
|
```
|
|
121
74
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
Instead, both edits are translated into a relationship with the same historical boundary between `A` and `B`.
|
|
125
|
-
|
|
126
|
-
That boundary remains meaningful even after the visible sequence changes.
|
|
75
|
+
but Structural Order still contains the original positive Insertion and the history describing the removal.
|
|
127
76
|
|
|
128
|
-
|
|
77
|
+
More history can later attach to those original coordinates, so hidden history may remain useful long after its Frames disappear from the Projection.
|
|
129
78
|
|
|
130
|
-
|
|
79
|
+
## Insertions
|
|
131
80
|
|
|
132
|
-
|
|
81
|
+
History is made of **Insertions**.
|
|
133
82
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
Conceptually, every Insertion contains:
|
|
137
|
-
|
|
138
|
-
```text
|
|
139
|
-
where it belongs
|
|
140
|
-
+
|
|
141
|
-
what history it introduces
|
|
142
|
-
```
|
|
143
|
-
|
|
144
|
-
Its canonical coordinates are:
|
|
83
|
+
An Insertion has six coordinates:
|
|
145
84
|
|
|
146
85
|
```text
|
|
147
86
|
anchorSession
|
|
@@ -153,399 +92,325 @@ insertionStart
|
|
|
153
92
|
insertionDiff
|
|
154
93
|
```
|
|
155
94
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
The anchor:
|
|
95
|
+
The first three describe where the Insertion belongs:
|
|
159
96
|
|
|
160
97
|
```text
|
|
161
98
|
(anchorSession, anchorStart, anchorDiff)
|
|
162
99
|
```
|
|
163
100
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
The identity:
|
|
101
|
+
The next two identify the Insertion:
|
|
167
102
|
|
|
168
103
|
```text
|
|
169
104
|
(insertionSession, insertionStart)
|
|
170
105
|
```
|
|
171
106
|
|
|
172
|
-
describes
|
|
107
|
+
and `insertionDiff` describes its original Frame length and whether it adds or removes Frames.
|
|
173
108
|
|
|
174
|
-
|
|
109
|
+
A positive Insertion adds Frames:
|
|
175
110
|
|
|
176
111
|
```text
|
|
177
|
-
insertionDiff
|
|
112
|
+
insertionDiff = 4
|
|
178
113
|
```
|
|
179
114
|
|
|
180
|
-
|
|
115
|
+
A reducing Insertion masks Frames:
|
|
181
116
|
|
|
182
|
-
|
|
117
|
+
```text
|
|
118
|
+
insertionDiff = -4
|
|
119
|
+
```
|
|
183
120
|
|
|
184
|
-
|
|
121
|
+
Positive Insertions can also carry **Footage**, which contains the actual values.
|
|
185
122
|
|
|
186
|
-
|
|
123
|
+
Sequencer orders the Insertion. Footage is what eventually appears in the Projection.
|
|
187
124
|
|
|
188
|
-
##
|
|
125
|
+
## Frames and logical points
|
|
189
126
|
|
|
190
|
-
|
|
127
|
+
Suppose one Insertion contains:
|
|
191
128
|
|
|
192
129
|
```text
|
|
193
|
-
|
|
130
|
+
A B C
|
|
194
131
|
```
|
|
195
132
|
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
This means:
|
|
133
|
+
It has three Frames:
|
|
199
134
|
|
|
200
135
|
```text
|
|
201
|
-
|
|
136
|
+
A B C
|
|
137
|
+
0 1 2
|
|
202
138
|
```
|
|
203
139
|
|
|
204
|
-
and:
|
|
140
|
+
and four logical points:
|
|
205
141
|
|
|
206
142
|
```text
|
|
207
|
-
|
|
143
|
+
| A | B | C |
|
|
144
|
+
0 1 2 3
|
|
208
145
|
```
|
|
209
146
|
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
That separation is useful for several reasons.
|
|
213
|
-
|
|
214
|
-
The same operation can be recognized after retransmission.
|
|
215
|
-
|
|
216
|
-
Fragments of one Insertion still belong to the same canonical identity.
|
|
217
|
-
|
|
218
|
-
An Insertion can be found independently from whichever Strip currently represents its anchor.
|
|
219
|
-
|
|
220
|
-
And duplicate delivery does not create duplicate visible content.
|
|
221
|
-
|
|
222
|
-
---
|
|
223
|
-
|
|
224
|
-
# Logical time
|
|
225
|
-
|
|
226
|
-
Every sequencing Session owns its own logical coordinate space.
|
|
227
|
-
|
|
228
|
-
A newly authored Insertion receives a start position inside that Session.
|
|
147
|
+
An anchor points to one of these boundaries.
|
|
229
148
|
|
|
230
149
|
For example:
|
|
231
150
|
|
|
232
151
|
```text
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
Insertion A starts at 0
|
|
236
|
-
Insertion B starts at 6
|
|
237
|
-
Insertion C starts at 11
|
|
152
|
+
anchorDiff = 2
|
|
238
153
|
```
|
|
239
154
|
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
They are logical coordinates used only to identify history.
|
|
243
|
-
|
|
244
|
-
A canonical Insertion normally reserves both:
|
|
155
|
+
refers to:
|
|
245
156
|
|
|
246
157
|
```text
|
|
247
|
-
|
|
248
|
-
+
|
|
249
|
-
a following logical boundary
|
|
158
|
+
A B | C
|
|
250
159
|
```
|
|
251
160
|
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
That following boundary gives later history a stable point to reference.
|
|
161
|
+
inside that original Insertion.
|
|
255
162
|
|
|
256
|
-
|
|
163
|
+
If later edits move `C` somewhere else in the Projection, this point is still point `2` of the same original Insertion.
|
|
257
164
|
|
|
258
|
-
##
|
|
165
|
+
## Anchoring an edit
|
|
259
166
|
|
|
260
|
-
|
|
167
|
+
Suppose the Projection contains:
|
|
261
168
|
|
|
262
169
|
```text
|
|
263
|
-
|
|
264
|
-
```
|
|
265
|
-
|
|
266
|
-
It contains three Frames:
|
|
267
|
-
|
|
268
|
-
```text
|
|
269
|
-
0 1 2
|
|
170
|
+
Hello world
|
|
270
171
|
```
|
|
271
172
|
|
|
272
|
-
|
|
173
|
+
and `big ` is inserted here:
|
|
273
174
|
|
|
274
175
|
```text
|
|
275
|
-
|
|
276
|
-
0 1 2 3
|
|
176
|
+
Hello | world
|
|
277
177
|
```
|
|
278
178
|
|
|
279
|
-
|
|
179
|
+
Sequencer first finds which original Insertion owns that boundary.
|
|
280
180
|
|
|
281
|
-
|
|
181
|
+
If the boundary is point `6` of an Insertion identified by:
|
|
282
182
|
|
|
283
183
|
```text
|
|
284
|
-
|
|
184
|
+
(session 4, start 20)
|
|
285
185
|
```
|
|
286
186
|
|
|
287
|
-
|
|
187
|
+
the new Insertion can anchor to:
|
|
288
188
|
|
|
289
189
|
```text
|
|
290
|
-
|
|
190
|
+
anchorSession = 4
|
|
191
|
+
anchorStart = 20
|
|
192
|
+
anchorDiff = 6
|
|
291
193
|
```
|
|
292
194
|
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
It does **not** mean:
|
|
195
|
+
Another replica might currently see:
|
|
296
196
|
|
|
297
197
|
```text
|
|
298
|
-
|
|
198
|
+
Well, Hello world
|
|
299
199
|
```
|
|
300
200
|
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
Visible positions move.
|
|
304
|
-
|
|
305
|
-
Historical logical points do not.
|
|
201
|
+
The same anchor still refers to the point after `Hello ` in the original Insertion.
|
|
306
202
|
|
|
307
|
-
|
|
203
|
+
The Projection position is different, but the relationship is the same.
|
|
308
204
|
|
|
309
|
-
|
|
205
|
+
## Insertion identity
|
|
310
206
|
|
|
311
|
-
|
|
207
|
+
An Insertion is identified by:
|
|
312
208
|
|
|
313
209
|
```text
|
|
314
|
-
|
|
210
|
+
(insertionSession, insertionStart)
|
|
315
211
|
```
|
|
316
212
|
|
|
317
|
-
|
|
213
|
+
The anchor is not part of its identity.
|
|
318
214
|
|
|
319
|
-
|
|
320
|
-
big
|
|
321
|
-
```
|
|
215
|
+
If the same Insertion arrives twice, both copies have the same identity.
|
|
322
216
|
|
|
323
|
-
|
|
217
|
+
Sequencer can keep the first and ignore the duplicate instead of inserting the same content twice.
|
|
324
218
|
|
|
325
|
-
|
|
326
|
-
Hello | world
|
|
327
|
-
```
|
|
219
|
+
Fragments of an Insertion also keep this identity. Splitting an Insertion at runtime does not create new replicated history.
|
|
328
220
|
|
|
329
|
-
|
|
221
|
+
## Sessions
|
|
330
222
|
|
|
331
|
-
|
|
223
|
+
Insertions are authored inside **Sessions**.
|
|
332
224
|
|
|
333
|
-
|
|
225
|
+
A Session has its own logical time.
|
|
334
226
|
|
|
335
|
-
|
|
336
|
-
specific historical Insertion
|
|
337
|
-
+
|
|
338
|
-
specific logical point inside it
|
|
339
|
-
```
|
|
227
|
+
Suppose a Session creates a three-Frame Insertion starting at `0`.
|
|
340
228
|
|
|
341
|
-
|
|
229
|
+
Its Frame coordinates occupy:
|
|
342
230
|
|
|
343
231
|
```text
|
|
344
|
-
|
|
232
|
+
0 1 2
|
|
345
233
|
```
|
|
346
234
|
|
|
347
|
-
|
|
235
|
+
and its endpoint is:
|
|
348
236
|
|
|
349
237
|
```text
|
|
350
|
-
|
|
238
|
+
3
|
|
351
239
|
```
|
|
352
240
|
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
The numeric Projection position may differ.
|
|
356
|
-
|
|
357
|
-
The historical relationship does not.
|
|
241
|
+
A new independent Insertion normally starts after another reserved logical point:
|
|
358
242
|
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
243
|
+
```text
|
|
244
|
+
0 1 2 | 3
|
|
245
|
+
↑
|
|
246
|
+
boundary
|
|
362
247
|
|
|
363
|
-
|
|
248
|
+
next start = 4
|
|
249
|
+
```
|
|
364
250
|
|
|
365
|
-
|
|
251
|
+
The gap gives the previous Insertion its own endpoint without making that endpoint the identity of the next independent Insertion.
|
|
366
252
|
|
|
367
|
-
|
|
253
|
+
Positive and reducing history use separate Sessions.
|
|
368
254
|
|
|
369
|
-
|
|
255
|
+
Session identifiers are also separate from Actor identifiers. Two replicas belonging to the same Actor can author their own Sessions without sharing one sequencing clock.
|
|
370
256
|
|
|
371
|
-
|
|
372
|
-
positive Insertions
|
|
373
|
-
removal history
|
|
374
|
-
fragments
|
|
375
|
-
concurrent siblings
|
|
376
|
-
causal descendants
|
|
377
|
-
hidden historical boundaries
|
|
378
|
-
```
|
|
257
|
+
## Structural Order
|
|
379
258
|
|
|
380
|
-
|
|
259
|
+
Every materialized Insertion is placed into one Structural Order.
|
|
381
260
|
|
|
382
|
-
|
|
261
|
+
Suppose the original history is:
|
|
383
262
|
|
|
384
263
|
```text
|
|
385
|
-
|
|
386
|
-
↓
|
|
387
|
-
surviving positive Frames
|
|
388
|
-
↓
|
|
389
|
-
Projection
|
|
264
|
+
A B
|
|
390
265
|
```
|
|
391
266
|
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
The Projection answers:
|
|
267
|
+
and a new Insertion anchors between them:
|
|
395
268
|
|
|
396
269
|
```text
|
|
397
|
-
|
|
270
|
+
A | B
|
|
398
271
|
```
|
|
399
272
|
|
|
400
|
-
Structural Order
|
|
273
|
+
Structural Order can now represent:
|
|
401
274
|
|
|
402
275
|
```text
|
|
403
|
-
|
|
276
|
+
A X B
|
|
404
277
|
```
|
|
405
278
|
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
# Concurrent Insertions
|
|
409
|
-
|
|
410
|
-
Two Insertions directly compete when they use exactly the same canonical anchor.
|
|
411
|
-
|
|
412
|
-
For example, both of these may attach to:
|
|
279
|
+
If another replica concurrently inserts `Y` at the same anchor, both new Insertions describe the same historical point:
|
|
413
280
|
|
|
414
281
|
```text
|
|
415
282
|
A | B
|
|
283
|
+
X
|
|
284
|
+
Y
|
|
416
285
|
```
|
|
417
286
|
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
```text
|
|
421
|
-
X
|
|
422
|
-
Y
|
|
423
|
-
```
|
|
424
|
-
|
|
425
|
-
The order between them cannot depend on which replica received which packet first.
|
|
426
|
-
|
|
427
|
-
Sequencer therefore defines a deterministic competitor ordering.
|
|
428
|
-
|
|
429
|
-
Closer to the shared anchor:
|
|
287
|
+
They must get the same order on every replica even when they arrive in different orders.
|
|
430
288
|
|
|
431
|
-
|
|
432
|
-
2. within the same sign, larger Session identifiers come first;
|
|
433
|
-
3. within the same Session, larger start coordinates come first.
|
|
289
|
+
Sequencer orders competing Insertions from the anchor outward by:
|
|
434
290
|
|
|
435
|
-
|
|
291
|
+
1. positive Insertions before reducing Insertions;
|
|
292
|
+
2. greater Session first;
|
|
293
|
+
3. within the same Session, greater start first.
|
|
436
294
|
|
|
437
|
-
So
|
|
295
|
+
So receiving:
|
|
438
296
|
|
|
439
297
|
```text
|
|
440
298
|
X
|
|
441
299
|
Y
|
|
442
300
|
```
|
|
443
301
|
|
|
444
|
-
|
|
302
|
+
or:
|
|
445
303
|
|
|
446
304
|
```text
|
|
447
305
|
Y
|
|
448
306
|
X
|
|
449
307
|
```
|
|
450
308
|
|
|
451
|
-
|
|
309
|
+
produces the same Structural Order once both are known.
|
|
452
310
|
|
|
453
|
-
|
|
311
|
+
## Causal subtrees
|
|
454
312
|
|
|
455
|
-
|
|
313
|
+
An Insertion can have more history attached to it.
|
|
456
314
|
|
|
457
|
-
|
|
315
|
+
Suppose two replicas start from an empty sequence.
|
|
458
316
|
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
Consider two replicas that independently create root content:
|
|
317
|
+
One creates:
|
|
462
318
|
|
|
463
319
|
```text
|
|
464
|
-
|
|
320
|
+
Project notes
|
|
465
321
|
```
|
|
466
322
|
|
|
467
|
-
and
|
|
323
|
+
and continues editing it.
|
|
324
|
+
|
|
325
|
+
The other independently creates:
|
|
468
326
|
|
|
469
327
|
```text
|
|
470
|
-
|
|
328
|
+
Shopping list
|
|
471
329
|
```
|
|
472
330
|
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
The two roots compete at the same virtual root anchor.
|
|
476
|
-
|
|
477
|
-
Sequencer deterministically decides which root comes first.
|
|
331
|
+
and continues editing that.
|
|
478
332
|
|
|
479
|
-
|
|
333
|
+
The two initial Insertions compete at the root.
|
|
480
334
|
|
|
481
|
-
|
|
335
|
+
Their later edits are descendants of those roots:
|
|
482
336
|
|
|
483
337
|
```text
|
|
484
|
-
|
|
338
|
+
Project notes
|
|
339
|
+
├─ child
|
|
340
|
+
└─ child
|
|
485
341
|
|
|
486
|
-
|
|
342
|
+
Shopping list
|
|
343
|
+
├─ child
|
|
344
|
+
└─ child
|
|
487
345
|
```
|
|
488
346
|
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
It must not become:
|
|
347
|
+
When the roots are ordered, their descendants stay with them:
|
|
492
348
|
|
|
493
349
|
```text
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
rest of A
|
|
497
|
-
rest of B
|
|
350
|
+
[Project notes subtree]
|
|
351
|
+
[Shopping list subtree]
|
|
498
352
|
```
|
|
499
353
|
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
This is what lets independently developed branches of history remain coherent when they meet.
|
|
354
|
+
rather than becoming interleaved:
|
|
503
355
|
|
|
504
|
-
|
|
356
|
+
```text
|
|
357
|
+
part of Project notes
|
|
358
|
+
part of Shopping list
|
|
359
|
+
rest of Project notes
|
|
360
|
+
rest of Shopping list
|
|
361
|
+
```
|
|
505
362
|
|
|
506
|
-
|
|
363
|
+
When a competitor is inserted into Structural Order, its place is determined relative to the complete subtree of the competitors already there.
|
|
507
364
|
|
|
508
|
-
|
|
365
|
+
## History can arrive before its parent
|
|
509
366
|
|
|
510
|
-
Suppose one replica
|
|
367
|
+
Suppose one replica creates:
|
|
511
368
|
|
|
512
369
|
```text
|
|
513
370
|
Hello
|
|
514
371
|
```
|
|
515
372
|
|
|
516
|
-
and
|
|
373
|
+
and then inserts:
|
|
517
374
|
|
|
518
375
|
```text
|
|
519
|
-
|
|
376
|
+
world
|
|
520
377
|
```
|
|
521
378
|
|
|
522
|
-
|
|
379
|
+
after it.
|
|
380
|
+
|
|
381
|
+
The second Insertion anchors to the first.
|
|
382
|
+
|
|
383
|
+
Another replica may receive:
|
|
523
384
|
|
|
524
385
|
```text
|
|
525
386
|
world
|
|
526
387
|
```
|
|
527
388
|
|
|
528
|
-
before
|
|
389
|
+
before:
|
|
529
390
|
|
|
530
391
|
```text
|
|
531
392
|
Hello
|
|
532
393
|
```
|
|
533
394
|
|
|
534
|
-
|
|
395
|
+
There is not yet anything local that can resolve the anchor.
|
|
535
396
|
|
|
536
|
-
|
|
397
|
+
The Insertion waits.
|
|
537
398
|
|
|
538
|
-
When
|
|
399
|
+
When `Hello` arrives, its identity becomes available and the waiting Insertion can be placed:
|
|
539
400
|
|
|
540
|
-
|
|
401
|
+
```text
|
|
402
|
+
Hello world
|
|
403
|
+
```
|
|
541
404
|
|
|
542
|
-
The same
|
|
405
|
+
The same thing can happen when the parent exists but has not yet grown far enough to contain the referenced logical point.
|
|
543
406
|
|
|
544
|
-
|
|
407
|
+
The relationship itself does not change while it waits.
|
|
545
408
|
|
|
546
|
-
|
|
409
|
+
## Author-time relationships
|
|
547
410
|
|
|
548
|
-
|
|
411
|
+
This allows edits to describe the sequence as it existed when they were authored without requiring another replica to pass through the same intermediate states.
|
|
412
|
+
|
|
413
|
+
For example, one replica may produce:
|
|
549
414
|
|
|
550
415
|
```text
|
|
551
416
|
world
|
|
@@ -556,7 +421,7 @@ Now, Well, actually Hello world
|
|
|
556
421
|
Okay, Now, Well, actually Hello world
|
|
557
422
|
```
|
|
558
423
|
|
|
559
|
-
Another replica might receive the
|
|
424
|
+
Another replica might receive the Insertions in this order:
|
|
560
425
|
|
|
561
426
|
```text
|
|
562
427
|
world
|
|
@@ -567,128 +432,66 @@ Well
|
|
|
567
432
|
Okay
|
|
568
433
|
```
|
|
569
434
|
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
Each operation describes where it belonged relative to existing canonical history at author time.
|
|
435
|
+
`actually` is still attached to the historical Insertion it followed when it was authored.
|
|
573
436
|
|
|
574
|
-
Once
|
|
437
|
+
Once the required history has arrived, the relationships can be resolved into:
|
|
575
438
|
|
|
576
439
|
```text
|
|
577
440
|
Okay, Now, Well, actually Hello world
|
|
578
441
|
```
|
|
579
442
|
|
|
580
|
-
|
|
443
|
+
## Strips
|
|
581
444
|
|
|
582
|
-
|
|
583
|
-
author-time relationships
|
|
584
|
-
```
|
|
445
|
+
An Insertion does not always stay in one piece inside a running Projection.
|
|
585
446
|
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
```text
|
|
589
|
-
author-time numeric indexes
|
|
590
|
-
```
|
|
591
|
-
|
|
592
|
-
---
|
|
593
|
-
|
|
594
|
-
# Canonical history and runtime fragmentation
|
|
595
|
-
|
|
596
|
-
A canonical Insertion is a logical piece of history.
|
|
597
|
-
|
|
598
|
-
It does not need to remain one physical runtime object forever.
|
|
599
|
-
|
|
600
|
-
Suppose one Insertion originally contains:
|
|
447
|
+
Suppose an original Insertion contains:
|
|
601
448
|
|
|
602
449
|
```text
|
|
603
450
|
A B C D
|
|
604
451
|
```
|
|
605
452
|
|
|
606
|
-
|
|
453
|
+
An edit at the middle may split its runtime representation into:
|
|
607
454
|
|
|
608
455
|
```text
|
|
609
456
|
[A B] [C D]
|
|
610
457
|
```
|
|
611
458
|
|
|
612
|
-
These pieces are
|
|
613
|
-
|
|
614
|
-
The important point is that they are not new canonical Insertions.
|
|
615
|
-
|
|
616
|
-
Both still belong to the same original history:
|
|
617
|
-
|
|
618
|
-
```text
|
|
619
|
-
same anchor
|
|
620
|
-
same insertion identity
|
|
621
|
-
same original coordinate space
|
|
622
|
-
```
|
|
459
|
+
These pieces are **Strips**.
|
|
623
460
|
|
|
624
|
-
|
|
461
|
+
Both Strips still belong to the same original Insertion.
|
|
625
462
|
|
|
626
|
-
|
|
463
|
+
The original coordinate space is still:
|
|
627
464
|
|
|
628
465
|
```text
|
|
629
|
-
canonical Insertion
|
|
630
|
-
|
|
631
466
|
| A | B | C | D |
|
|
632
467
|
0 1 2 3 4
|
|
633
|
-
|
|
634
|
-
↓
|
|
635
|
-
|
|
636
|
-
runtime materialization
|
|
637
|
-
|
|
638
|
-
[A B] [C D]
|
|
639
468
|
```
|
|
640
469
|
|
|
641
|
-
The
|
|
470
|
+
The left Strip represents one part of that space and the right Strip represents another.
|
|
642
471
|
|
|
643
|
-
|
|
644
|
-
0 1 2 3 4
|
|
645
|
-
```
|
|
472
|
+
An operation authored earlier against point `3` can still find point `3` even though the original Insertion is no longer represented by one Strip.
|
|
646
473
|
|
|
647
|
-
|
|
474
|
+
Different replicas can also have different fragmentation.
|
|
648
475
|
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
---
|
|
652
|
-
|
|
653
|
-
# Canonical coordinates survive fragmentation
|
|
654
|
-
|
|
655
|
-
This separation between canonical identity and runtime representation is essential.
|
|
656
|
-
|
|
657
|
-
A replica may represent one Insertion as:
|
|
476
|
+
One might currently have:
|
|
658
477
|
|
|
659
478
|
```text
|
|
660
479
|
[A B C D]
|
|
661
480
|
```
|
|
662
481
|
|
|
663
|
-
while another
|
|
482
|
+
while another has:
|
|
664
483
|
|
|
665
484
|
```text
|
|
666
485
|
[A] [B C] [D]
|
|
667
486
|
```
|
|
668
487
|
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
```text
|
|
672
|
-
which Insertion it is
|
|
673
|
-
```
|
|
674
|
-
|
|
675
|
-
and:
|
|
676
|
-
|
|
677
|
-
```text
|
|
678
|
-
which original logical point an anchor refers to
|
|
679
|
-
```
|
|
680
|
-
|
|
681
|
-
They do not need identical runtime layouts.
|
|
682
|
-
|
|
683
|
-
The receiver resolves canonical coordinates through whatever fragmentation exists locally.
|
|
488
|
+
They still agree on the original Insertion and its coordinates.
|
|
684
489
|
|
|
685
|
-
|
|
490
|
+
## Extending an Insertion
|
|
686
491
|
|
|
687
|
-
|
|
492
|
+
Consecutive editing often continues the Insertion that was just created.
|
|
688
493
|
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
Without any coalescing, typing:
|
|
494
|
+
Typing:
|
|
692
495
|
|
|
693
496
|
```text
|
|
694
497
|
H
|
|
@@ -698,13 +501,9 @@ Hell
|
|
|
698
501
|
Hello
|
|
699
502
|
```
|
|
700
503
|
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
That would preserve correctness, but history would grow unnecessarily quickly.
|
|
704
|
-
|
|
705
|
-
Sequencer can instead extend a recent positive Insertion when the new edit is a genuine continuation of that same history.
|
|
504
|
+
does not need five unrelated canonical Insertions.
|
|
706
505
|
|
|
707
|
-
|
|
506
|
+
When the new content is a clean continuation of the latest local positive Insertion, its original extent can grow:
|
|
708
507
|
|
|
709
508
|
```text
|
|
710
509
|
H
|
|
@@ -718,35 +517,19 @@ Hell
|
|
|
718
517
|
Hello
|
|
719
518
|
```
|
|
720
519
|
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
The key requirement is that extending it must not change previously established structural relationships.
|
|
724
|
-
|
|
725
|
-
If history has already attached to the old endpoint in a way that requires that boundary to remain distinct, a new independent Insertion is created instead.
|
|
726
|
-
|
|
727
|
-
If the canonical Insertion has fragmented at runtime, a valid extension can still become another fragment of the same grown canonical identity.
|
|
520
|
+
The continuation starts at the old endpoint of the same Session, so another replica can recognize it as an extension of the existing Insertion.
|
|
728
521
|
|
|
729
|
-
|
|
522
|
+
This is only done while the old endpoint can safely grow.
|
|
730
523
|
|
|
731
|
-
|
|
524
|
+
If other history already depends on that endpoint in a way that would change its ordering, the new content becomes a normal separate Insertion instead.
|
|
732
525
|
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
Removing visible content does not immediately erase its original Insertion.
|
|
736
|
-
|
|
737
|
-
Instead, removal is represented as new history.
|
|
738
|
-
|
|
739
|
-
A removal is a reducing Insertion:
|
|
740
|
-
|
|
741
|
-
```text
|
|
742
|
-
insertionDiff < 0
|
|
743
|
-
```
|
|
526
|
+
An Insertion can also be extended after it has fragmented. The new suffix may need its own Strip, but it still belongs to the same canonical Insertion.
|
|
744
527
|
|
|
745
|
-
|
|
528
|
+
## Removal
|
|
746
529
|
|
|
747
|
-
|
|
530
|
+
Removal is represented by reducing Insertions.
|
|
748
531
|
|
|
749
|
-
Suppose
|
|
532
|
+
Suppose one positive Insertion contains:
|
|
750
533
|
|
|
751
534
|
```text
|
|
752
535
|
A B C D
|
|
@@ -754,27 +537,27 @@ A B C D
|
|
|
754
537
|
|
|
755
538
|
and `B C` are removed.
|
|
756
539
|
|
|
757
|
-
The
|
|
758
|
-
|
|
759
|
-
A reducing Insertion records:
|
|
540
|
+
The removal anchors to the original positive Insertion and covers those original Frames:
|
|
760
541
|
|
|
761
542
|
```text
|
|
762
|
-
|
|
543
|
+
original
|
|
544
|
+
|
|
545
|
+
A B C D
|
|
546
|
+
└─┬─┘
|
|
547
|
+
removed
|
|
763
548
|
```
|
|
764
549
|
|
|
765
|
-
The Projection
|
|
550
|
+
The Projection becomes:
|
|
766
551
|
|
|
767
552
|
```text
|
|
768
553
|
A D
|
|
769
554
|
```
|
|
770
555
|
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
---
|
|
556
|
+
The original coordinates still exist in Structural Order.
|
|
774
557
|
|
|
775
|
-
|
|
558
|
+
This becomes especially useful when insertion and removal happen concurrently.
|
|
776
559
|
|
|
777
|
-
|
|
560
|
+
Suppose both replicas start with:
|
|
778
561
|
|
|
779
562
|
```text
|
|
780
563
|
The old house
|
|
@@ -786,135 +569,103 @@ Replica A removes:
|
|
|
786
569
|
old
|
|
787
570
|
```
|
|
788
571
|
|
|
789
|
-
|
|
572
|
+
and sees:
|
|
790
573
|
|
|
791
574
|
```text
|
|
792
|
-
|
|
575
|
+
The house
|
|
793
576
|
```
|
|
794
577
|
|
|
795
|
-
|
|
578
|
+
At the same time Replica B inserts:
|
|
796
579
|
|
|
797
580
|
```text
|
|
798
|
-
|
|
581
|
+
small
|
|
799
582
|
```
|
|
800
583
|
|
|
801
|
-
|
|
584
|
+
after `old `:
|
|
802
585
|
|
|
803
586
|
```text
|
|
804
|
-
small
|
|
587
|
+
The old small house
|
|
805
588
|
```
|
|
806
589
|
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
Because removal targets original Frames rather than a mutable visible interval, it masks only:
|
|
590
|
+
The removal authored by A refers to the original Frames of `old `.
|
|
810
591
|
|
|
811
|
-
|
|
812
|
-
old
|
|
813
|
-
```
|
|
592
|
+
It never referred to `small ` because those Frames did not exist in A's history.
|
|
814
593
|
|
|
815
|
-
After synchronization
|
|
594
|
+
After synchronization:
|
|
816
595
|
|
|
817
596
|
```text
|
|
818
597
|
The small house
|
|
819
598
|
```
|
|
820
599
|
|
|
821
|
-
|
|
600
|
+
The concurrent insertion survives.
|
|
822
601
|
|
|
823
|
-
|
|
602
|
+
## A removal can cross several Insertions
|
|
824
603
|
|
|
825
|
-
|
|
604
|
+
A visible range can contain Frames from several original Insertions.
|
|
826
605
|
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
What looks like one contiguous visible range may internally contain Frames from several canonical Insertions.
|
|
830
|
-
|
|
831
|
-
For example:
|
|
606
|
+
For example, the Projection:
|
|
832
607
|
|
|
833
608
|
```text
|
|
834
609
|
Hello world
|
|
835
610
|
```
|
|
836
611
|
|
|
837
|
-
might
|
|
612
|
+
might have been built from:
|
|
838
613
|
|
|
839
614
|
```text
|
|
840
|
-
[Hello]
|
|
841
|
-
[ ]
|
|
842
|
-
[world]
|
|
615
|
+
[Hello] [ ] [world]
|
|
843
616
|
```
|
|
844
617
|
|
|
845
|
-
from three different historical Insertions.
|
|
846
|
-
|
|
847
618
|
Removing:
|
|
848
619
|
|
|
849
620
|
```text
|
|
850
621
|
lo wo
|
|
851
622
|
```
|
|
852
623
|
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
Sequencer expresses the removal against the actual original Insertions whose Frames are being consumed.
|
|
856
|
-
|
|
857
|
-
Each reducing Insertion targets only Frames belonging to its own historical parent.
|
|
624
|
+
crosses all three.
|
|
858
625
|
|
|
859
|
-
|
|
626
|
+
Sequencer describes the removal against the original Frames of each Insertion it crosses.
|
|
860
627
|
|
|
861
|
-
|
|
628
|
+
The visible edit is one range, but its historical description can contain several reducing Insertions.
|
|
862
629
|
|
|
863
|
-
|
|
630
|
+
## Removal fragments
|
|
864
631
|
|
|
865
|
-
A
|
|
632
|
+
A reducing Insertion can also become fragmented.
|
|
866
633
|
|
|
867
|
-
Suppose original
|
|
634
|
+
Suppose an original positive Insertion contains:
|
|
868
635
|
|
|
869
636
|
```text
|
|
870
637
|
A B C D E F
|
|
871
638
|
```
|
|
872
639
|
|
|
873
|
-
|
|
640
|
+
and its current structural representation has already become:
|
|
874
641
|
|
|
875
642
|
```text
|
|
876
643
|
[A B] [C D] [E F]
|
|
877
644
|
```
|
|
878
645
|
|
|
879
|
-
A removal
|
|
646
|
+
A removal covering:
|
|
880
647
|
|
|
881
648
|
```text
|
|
882
649
|
B C D E
|
|
883
650
|
```
|
|
884
651
|
|
|
885
|
-
may need
|
|
652
|
+
may need mask fragments at several places in Structural Order.
|
|
886
653
|
|
|
887
|
-
Those
|
|
654
|
+
Those mask Strips still belong to one canonical reducing Insertion.
|
|
888
655
|
|
|
889
|
-
|
|
656
|
+
Its identity and original target coordinates do not change.
|
|
890
657
|
|
|
891
|
-
|
|
658
|
+
## Overlapping removals
|
|
892
659
|
|
|
893
|
-
|
|
894
|
-
canonical history
|
|
895
|
-
```
|
|
660
|
+
Two replicas can remove some of the same original Frames.
|
|
896
661
|
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
```text
|
|
900
|
-
runtime representation
|
|
901
|
-
```
|
|
902
|
-
|
|
903
|
-
remain separate.
|
|
904
|
-
|
|
905
|
-
---
|
|
906
|
-
|
|
907
|
-
# Overlapping removals
|
|
908
|
-
|
|
909
|
-
Concurrent replicas may independently remove overlapping Frames.
|
|
910
|
-
|
|
911
|
-
For example, from:
|
|
662
|
+
Starting from:
|
|
912
663
|
|
|
913
664
|
```text
|
|
914
665
|
the brown fox jumps
|
|
915
666
|
```
|
|
916
667
|
|
|
917
|
-
one replica
|
|
668
|
+
one replica might remove:
|
|
918
669
|
|
|
919
670
|
```text
|
|
920
671
|
brown fox
|
|
@@ -926,605 +677,233 @@ while another removes:
|
|
|
926
677
|
fox jumps
|
|
927
678
|
```
|
|
928
679
|
|
|
929
|
-
Both
|
|
680
|
+
Both target the original Frames of `fox`.
|
|
930
681
|
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
```text
|
|
934
|
-
fox
|
|
935
|
-
```
|
|
682
|
+
When the second removal is integrated, those Frames may already be masked.
|
|
936
683
|
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
They must still disappear only once from the Projection.
|
|
940
|
-
|
|
941
|
-
A reducing Insertion therefore retains its canonical authored meaning while its current runtime effect is resolved against Frames that remain available to consume.
|
|
942
|
-
|
|
943
|
-
This guarantees:
|
|
944
|
-
|
|
945
|
-
```text
|
|
946
|
-
one original Frame cannot reduce the Projection twice
|
|
947
|
-
```
|
|
684
|
+
They cannot reduce the Projection again.
|
|
948
685
|
|
|
949
|
-
|
|
686
|
+
The reducing Insertion still describes the range it originally targeted, while its runtime effect is limited to original Frames that remain available for it to consume.
|
|
950
687
|
|
|
951
|
-
|
|
688
|
+
This lets overlapping removals arrive in either order without changing the result.
|
|
952
689
|
|
|
953
|
-
|
|
690
|
+
## Replacement
|
|
954
691
|
|
|
955
|
-
Replacement
|
|
692
|
+
Replacement is a removal followed by a positive Insertion.
|
|
956
693
|
|
|
957
|
-
|
|
694
|
+
Changing:
|
|
958
695
|
|
|
959
696
|
```text
|
|
960
697
|
Draft
|
|
961
698
|
```
|
|
962
699
|
|
|
963
|
-
|
|
700
|
+
to:
|
|
964
701
|
|
|
965
702
|
```text
|
|
966
703
|
Final
|
|
967
704
|
```
|
|
968
705
|
|
|
969
|
-
|
|
706
|
+
first masks the Frames of:
|
|
970
707
|
|
|
971
708
|
```text
|
|
972
|
-
|
|
973
|
-
insert Final
|
|
974
|
-
```
|
|
975
|
-
|
|
976
|
-
The important part is the relationship between those two operations.
|
|
977
|
-
|
|
978
|
-
The new content is anchored against the history that exists after the removal relationship has been established.
|
|
979
|
-
|
|
980
|
-
This keeps replacement semantics consistent with ordinary insertion and removal instead of introducing another independent ordering model.
|
|
981
|
-
|
|
982
|
-
---
|
|
983
|
-
|
|
984
|
-
# Visible content and historical content are deliberately different
|
|
985
|
-
|
|
986
|
-
After enough editing, Structural Order may contain much more history than the Projection shows.
|
|
987
|
-
|
|
988
|
-
For example:
|
|
989
|
-
|
|
990
|
-
```text
|
|
991
|
-
Structural Order
|
|
992
|
-
|
|
993
|
-
[A B C]
|
|
994
|
-
[mask B]
|
|
995
|
-
[X]
|
|
996
|
-
[mask C]
|
|
997
|
-
[Y]
|
|
998
|
-
```
|
|
999
|
-
|
|
1000
|
-
could produce a Projection such as:
|
|
1001
|
-
|
|
1002
|
-
```text
|
|
1003
|
-
A X Y
|
|
1004
|
-
```
|
|
1005
|
-
|
|
1006
|
-
The hidden history is not accidental garbage.
|
|
1007
|
-
|
|
1008
|
-
It may still be required to understand:
|
|
1009
|
-
|
|
1010
|
-
```text
|
|
1011
|
-
future delayed operations
|
|
1012
|
-
historical anchor points
|
|
1013
|
-
concurrent relationships
|
|
1014
|
-
removal ownership
|
|
1015
|
-
ordering of descendants
|
|
1016
|
-
```
|
|
1017
|
-
|
|
1018
|
-
Sequencer therefore cannot simply delete every hidden node as soon as it stops being visible.
|
|
1019
|
-
|
|
1020
|
-
---
|
|
1021
|
-
|
|
1022
|
-
# Replication transports canonical history
|
|
1023
|
-
|
|
1024
|
-
Replicas exchange canonical Insertions and acknowledgement information.
|
|
1025
|
-
|
|
1026
|
-
The transport itself is not part of the sequencing model.
|
|
1027
|
-
|
|
1028
|
-
History may be delivered:
|
|
1029
|
-
|
|
1030
|
-
```text
|
|
1031
|
-
immediately
|
|
1032
|
-
later
|
|
1033
|
-
out of order
|
|
1034
|
-
more than once
|
|
1035
|
-
through a central server
|
|
1036
|
-
peer to peer
|
|
1037
|
-
after being persisted
|
|
1038
|
-
```
|
|
1039
|
-
|
|
1040
|
-
None of these should affect the eventual ordering.
|
|
1041
|
-
|
|
1042
|
-
The important property is that the same canonical history eventually reaches the replicas that need it.
|
|
1043
|
-
|
|
1044
|
-
---
|
|
1045
|
-
|
|
1046
|
-
# Duplicate delivery is harmless
|
|
1047
|
-
|
|
1048
|
-
Because canonical Insertions have stable identities, receiving the same history multiple times does not duplicate its visible effect.
|
|
1049
|
-
|
|
1050
|
-
This allows replication systems to favor reliability over exactly-once transport.
|
|
1051
|
-
|
|
1052
|
-
A sender can retransmit uncertain history.
|
|
1053
|
-
|
|
1054
|
-
A relay can replay stored history.
|
|
1055
|
-
|
|
1056
|
-
A reconnecting replica can receive data it already has.
|
|
1057
|
-
|
|
1058
|
-
Sequencer recognizes the canonical identity and keeps the resulting history idempotent.
|
|
1059
|
-
|
|
1060
|
-
---
|
|
1061
|
-
|
|
1062
|
-
# Actors and Sessions have different roles
|
|
1063
|
-
|
|
1064
|
-
A **Session** identifies authored sequencing history.
|
|
1065
|
-
|
|
1066
|
-
An **Actor** participates in acknowledgement of removal history.
|
|
1067
|
-
|
|
1068
|
-
These concepts are intentionally separate.
|
|
1069
|
-
|
|
1070
|
-
One Actor may have several independent Sessions.
|
|
1071
|
-
|
|
1072
|
-
Different replicas belonging to the same logical Actor can therefore create history without sharing one mutable sequencing clock.
|
|
1073
|
-
|
|
1074
|
-
Session identities answer:
|
|
1075
|
-
|
|
1076
|
-
```text
|
|
1077
|
-
who authored this canonical history stream?
|
|
1078
|
-
```
|
|
1079
|
-
|
|
1080
|
-
Actor identities answer:
|
|
1081
|
-
|
|
1082
|
-
```text
|
|
1083
|
-
whose acknowledgement is required before old removal history can be forgotten?
|
|
1084
|
-
```
|
|
1085
|
-
|
|
1086
|
-
---
|
|
1087
|
-
|
|
1088
|
-
# Why acknowledgements are needed
|
|
1089
|
-
|
|
1090
|
-
Suppose Replica A removes some content.
|
|
1091
|
-
|
|
1092
|
-
Replica B is currently offline.
|
|
1093
|
-
|
|
1094
|
-
Replica B may still contain unsent history anchored to the content that A removed.
|
|
1095
|
-
|
|
1096
|
-
If A permanently discarded every trace of that historical content immediately, B's delayed operation could later refer to a point that no longer exists anywhere in A's retained state.
|
|
1097
|
-
|
|
1098
|
-
Sequencer therefore separates:
|
|
1099
|
-
|
|
1100
|
-
```text
|
|
1101
|
-
content is no longer visible
|
|
1102
|
-
```
|
|
1103
|
-
|
|
1104
|
-
from:
|
|
1105
|
-
|
|
1106
|
-
```text
|
|
1107
|
-
history is safe to forget
|
|
1108
|
-
```
|
|
1109
|
-
|
|
1110
|
-
Those are not the same event.
|
|
1111
|
-
|
|
1112
|
-
---
|
|
1113
|
-
|
|
1114
|
-
# Removal frontiers
|
|
1115
|
-
|
|
1116
|
-
Actors acknowledge received prefixes of removal Sessions.
|
|
1117
|
-
|
|
1118
|
-
Conceptually an acknowledgement says:
|
|
1119
|
-
|
|
1120
|
-
```text
|
|
1121
|
-
Actor A has observed this removal Session
|
|
1122
|
-
through logical point N
|
|
1123
|
-
```
|
|
1124
|
-
|
|
1125
|
-
A frontier summarizes a whole contiguous prefix rather than listing every individual removal.
|
|
1126
|
-
|
|
1127
|
-
This keeps acknowledgement state small while still allowing Sequencer to determine which removal history is globally known among the active Actors.
|
|
1128
|
-
|
|
1129
|
-
---
|
|
1130
|
-
|
|
1131
|
-
# Compaction
|
|
1132
|
-
|
|
1133
|
-
Once all active Actors agree on a removal frontier, the acknowledged removal history becomes eligible for compaction.
|
|
1134
|
-
|
|
1135
|
-
Compaction does not simply delete mask nodes.
|
|
1136
|
-
|
|
1137
|
-
It constructs an equivalent retained history in which obsolete removed Frames and the historical machinery surrounding them can be omitted.
|
|
1138
|
-
|
|
1139
|
-
This may involve:
|
|
1140
|
-
|
|
1141
|
-
```text
|
|
1142
|
-
removing acknowledged masks
|
|
1143
|
-
removing Footage that can no longer reappear
|
|
1144
|
-
shortening positive Insertions
|
|
1145
|
-
adjusting surviving mask coordinates
|
|
1146
|
-
collapsing obsolete logical regions
|
|
1147
|
-
re-anchoring surviving history
|
|
1148
|
-
```
|
|
1149
|
-
|
|
1150
|
-
The required invariant is simple:
|
|
1151
|
-
|
|
1152
|
-
```text
|
|
1153
|
-
Projection before compaction
|
|
1154
|
-
==
|
|
1155
|
-
Projection reconstructed from compacted history
|
|
1156
|
-
```
|
|
1157
|
-
|
|
1158
|
-
But preserving that invariant under future editing is the harder part.
|
|
1159
|
-
|
|
1160
|
-
The compacted history must not only look the same now.
|
|
1161
|
-
|
|
1162
|
-
It must also preserve the ordering relationships that remain relevant later.
|
|
1163
|
-
|
|
1164
|
-
---
|
|
1165
|
-
|
|
1166
|
-
# Compaction can collapse historical space
|
|
1167
|
-
|
|
1168
|
-
Consider surviving children originally attached to two different logical points:
|
|
1169
|
-
|
|
1170
|
-
```text
|
|
1171
|
-
point 1
|
|
1172
|
-
↓
|
|
1173
|
-
removed historical region
|
|
1174
|
-
↓
|
|
1175
|
-
point 2
|
|
1176
|
-
```
|
|
1177
|
-
|
|
1178
|
-
After the region between them becomes safe to forget, both historical points may collapse onto the same retained boundary.
|
|
1179
|
-
|
|
1180
|
-
At that point the original geometric distinction no longer exists.
|
|
1181
|
-
|
|
1182
|
-
But the children may already have a resolved order.
|
|
1183
|
-
|
|
1184
|
-
Compaction must preserve that order.
|
|
1185
|
-
|
|
1186
|
-
Sequencer can therefore re-anchor surviving positive history according to its already resolved retained Structural Order.
|
|
1187
|
-
|
|
1188
|
-
In other words, once obsolete history is discarded:
|
|
1189
|
-
|
|
1190
|
-
```text
|
|
1191
|
-
resolved order
|
|
1192
|
-
```
|
|
1193
|
-
|
|
1194
|
-
can become more important than:
|
|
1195
|
-
|
|
1196
|
-
```text
|
|
1197
|
-
the exact historical path that originally produced it
|
|
709
|
+
Draft
|
|
1198
710
|
```
|
|
1199
711
|
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
---
|
|
1203
|
-
|
|
1204
|
-
# Compaction does not rewrite live history
|
|
1205
|
-
|
|
1206
|
-
Compaction operates on retained state.
|
|
1207
|
-
|
|
1208
|
-
A live Projection does not need to restructure itself every time a snapshot is produced.
|
|
1209
|
-
|
|
1210
|
-
Instead:
|
|
712
|
+
and then anchors:
|
|
1211
713
|
|
|
1212
714
|
```text
|
|
1213
|
-
|
|
1214
|
-
↓
|
|
1215
|
-
derive equivalent compacted history
|
|
1216
|
-
↓
|
|
1217
|
-
persist / transfer snapshot
|
|
715
|
+
Final
|
|
1218
716
|
```
|
|
1219
717
|
|
|
1220
|
-
|
|
718
|
+
into the resulting history.
|
|
1221
719
|
|
|
1222
|
-
|
|
720
|
+
Both parts use the same insertion and removal rules as every other edit.
|
|
1223
721
|
|
|
1224
|
-
|
|
722
|
+
## Actors and acknowledgements
|
|
1225
723
|
|
|
1226
|
-
|
|
724
|
+
Removal history cannot always be discarded when it disappears from the Projection.
|
|
1227
725
|
|
|
1228
|
-
|
|
726
|
+
Suppose one replica removes some Frames while another replica is offline.
|
|
1229
727
|
|
|
1230
|
-
|
|
728
|
+
The offline replica may still have unsent history anchored to those Frames.
|
|
1231
729
|
|
|
1232
|
-
|
|
730
|
+
The removed Frames must remain addressable until that old history no longer needs to be supported.
|
|
1233
731
|
|
|
1234
|
-
|
|
1235
|
-
this Actor is no longer expected to acknowledge future compaction
|
|
1236
|
-
```
|
|
732
|
+
Actors report how far they have observed each reducing Session.
|
|
1237
733
|
|
|
1238
|
-
|
|
734
|
+
For example:
|
|
1239
735
|
|
|
1240
736
|
```text
|
|
1241
|
-
|
|
1242
|
-
```
|
|
1243
|
-
|
|
1244
|
-
Those are unrelated concepts.
|
|
1245
|
-
|
|
1246
|
-
Retirement is an application-level decision because Sequencer cannot know whether a disconnected replica is temporarily offline or permanently abandoned.
|
|
1247
|
-
|
|
1248
|
-
---
|
|
1249
|
-
|
|
1250
|
-
# Persistence retains history, not runtime state
|
|
1251
|
-
|
|
1252
|
-
A persisted Sequence contains the history needed to reconstruct the Projection.
|
|
737
|
+
Actor 3
|
|
1253
738
|
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
For example, persistence needs the equivalent of:
|
|
1257
|
-
|
|
1258
|
-
```text
|
|
1259
|
-
canonical Insertions
|
|
1260
|
-
retained acknowledgement state
|
|
1261
|
-
positive Footage
|
|
739
|
+
removal Session 17
|
|
740
|
+
observed through logical time 42
|
|
1262
741
|
```
|
|
1263
742
|
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
```text
|
|
1267
|
-
current fragmentation layout
|
|
1268
|
-
temporary traversal indexes
|
|
1269
|
-
recent lookup positions
|
|
1270
|
-
allocation addresses
|
|
1271
|
-
```
|
|
743
|
+
The reported point is a **frontier**.
|
|
1272
744
|
|
|
1273
|
-
|
|
745
|
+
A single frontier summarizes a received prefix of a Session rather than acknowledging every reducing Insertion separately.
|
|
1274
746
|
|
|
1275
|
-
|
|
747
|
+
Every active Actor, including an offline Actor, must be registered before removals. An Actor-only acknowledgement such as `[3]` establishes that membership. Constructing another Projection does not register its Actor in the other replicas.
|
|
1276
748
|
|
|
1277
|
-
|
|
1278
|
-
stable history is durable
|
|
1279
|
-
runtime materialization is disposable
|
|
1280
|
-
```
|
|
749
|
+
The application must exchange pending history before treating an acknowledgement as a complete Session frontier. The library reports receipt of reducing Insertions; it does not prove that missing earlier operations have arrived.
|
|
1281
750
|
|
|
1282
|
-
|
|
751
|
+
## Compaction
|
|
1283
752
|
|
|
1284
|
-
|
|
753
|
+
When all active Actors agree on the same frontier for a reducing Session, the acknowledged part can be removed from future retained Sequences.
|
|
1285
754
|
|
|
1286
|
-
|
|
755
|
+
This is whole-Session eligibility: every active Actor must report the same complete Session frontier. A missing claim or a different endpoint blocks that Session. Taking the minimum of unequal frontiers is not this compaction rule.
|
|
1287
756
|
|
|
1288
|
-
|
|
757
|
+
Eligible masks must disappear from the exported Sequence. Wholly removed positive Insertions and deleted Footage disappear as well. Claims for compacted Sessions are dropped; Actor membership is retained.
|
|
1289
758
|
|
|
1290
|
-
|
|
759
|
+
Suppose history contains:
|
|
1291
760
|
|
|
1292
761
|
```text
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
762
|
+
positive Frames
|
|
763
|
+
removal mask
|
|
764
|
+
surviving descendants
|
|
1296
765
|
```
|
|
1297
766
|
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
They are not part of the distributed identity of the sequence.
|
|
1301
|
-
|
|
1302
|
-
---
|
|
767
|
+
and every active Actor has seen the removal.
|
|
1303
768
|
|
|
1304
|
-
|
|
769
|
+
A new retained Sequence no longer needs to preserve all of the machinery that represented that deletion.
|
|
1305
770
|
|
|
1306
|
-
|
|
771
|
+
Compaction can remove the acknowledged masks and remove deleted Frames from retained Footage.
|
|
1307
772
|
|
|
1308
|
-
|
|
773
|
+
If only part of a positive Insertion was deleted:
|
|
1309
774
|
|
|
1310
775
|
```text
|
|
1311
|
-
|
|
776
|
+
A B C D
|
|
1312
777
|
```
|
|
1313
778
|
|
|
1314
|
-
|
|
779
|
+
with:
|
|
1315
780
|
|
|
1316
781
|
```text
|
|
1317
|
-
|
|
782
|
+
B C
|
|
1318
783
|
```
|
|
1319
784
|
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
As long as all of them agree on:
|
|
785
|
+
gone, the retained positive history can shrink to the surviving content:
|
|
1323
786
|
|
|
1324
787
|
```text
|
|
1325
|
-
|
|
1326
|
-
canonical anchors
|
|
1327
|
-
original Frame coordinates
|
|
1328
|
-
deterministic Structural Order
|
|
788
|
+
A D
|
|
1329
789
|
```
|
|
1330
790
|
|
|
1331
|
-
|
|
791
|
+
Anchors and remaining removals that referred to the old coordinates are adjusted to the smaller retained coordinate space.
|
|
1332
792
|
|
|
1333
|
-
|
|
793
|
+
The running Projection does not need to be rewritten when this happens. Compaction produces another retained description of the same history.
|
|
1334
794
|
|
|
1335
|
-
|
|
795
|
+
The exported Sequence is a retained checkpoint. Its shorter Insertions and translated anchors can use different coordinates from the live graph before export. Continued editors must use compatible retained state; a compacted checkpoint does not preserve a permanent mapping to discarded history.
|
|
1336
796
|
|
|
1337
|
-
|
|
797
|
+
An offline Actor's unacknowledged history blocks compaction only if that Actor was registered. A test that omits its membership, allows compaction, then demands that its discarded anchors still work is outside this contract. Tests must not retain masks to satisfy that demand.
|
|
1338
798
|
|
|
1339
|
-
|
|
799
|
+
## Collapsed historical points
|
|
1340
800
|
|
|
1341
|
-
|
|
801
|
+
Compaction can make two old logical points collapse onto the same retained point.
|
|
1342
802
|
|
|
1343
|
-
|
|
803
|
+
Imagine:
|
|
1344
804
|
|
|
1345
805
|
```text
|
|
1346
|
-
|
|
806
|
+
A | removed Frames | B
|
|
1347
807
|
```
|
|
1348
808
|
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
They are temporary.
|
|
1352
|
-
|
|
1353
|
-
---
|
|
809
|
+
with surviving descendants attached on both sides of the removed region.
|
|
1354
810
|
|
|
1355
|
-
|
|
811
|
+
Before compaction those descendants were separated by different historical coordinates.
|
|
1356
812
|
|
|
1357
|
-
|
|
813
|
+
After the deleted Frames disappear from retained history, that space no longer exists.
|
|
1358
814
|
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
```text
|
|
1362
|
-
concurrent edits
|
|
1363
|
-
fragmentation
|
|
1364
|
-
removal
|
|
1365
|
-
reordered delivery
|
|
1366
|
-
restart
|
|
1367
|
-
```
|
|
815
|
+
Their order has already been resolved in Structural Order, so the compacted history keeps that order when it re-anchors the survivors around the remaining boundary.
|
|
1368
816
|
|
|
1369
|
-
|
|
817
|
+
The discarded history is no longer needed to make the same ordering decision again.
|
|
1370
818
|
|
|
1371
|
-
|
|
819
|
+
## Actor retirement
|
|
1372
820
|
|
|
1373
|
-
|
|
821
|
+
An Actor that will never return would otherwise remain part of acknowledgement consensus forever.
|
|
1374
822
|
|
|
1375
|
-
|
|
823
|
+
Retiring an Actor removes it from future compaction requirements.
|
|
1376
824
|
|
|
1377
|
-
|
|
825
|
+
It does not remove Insertions authored by that Actor.
|
|
1378
826
|
|
|
1379
|
-
|
|
827
|
+
It only changes which Actors must agree before old removal history can be discarded.
|
|
1380
828
|
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
So:
|
|
1384
|
-
|
|
1385
|
-
```text
|
|
1386
|
-
Projection position
|
|
1387
|
-
!=
|
|
1388
|
-
canonical logical coordinate
|
|
1389
|
-
!=
|
|
1390
|
-
runtime structural position
|
|
1391
|
-
```
|
|
1392
|
-
|
|
1393
|
-
Keeping those concepts separate is the easiest way to reason about Sequencer.
|
|
1394
|
-
|
|
1395
|
-
---
|
|
1396
|
-
|
|
1397
|
-
# The model in one example
|
|
1398
|
-
|
|
1399
|
-
Start with an empty Projection.
|
|
1400
|
-
|
|
1401
|
-
Replica A inserts:
|
|
1402
|
-
|
|
1403
|
-
```text
|
|
1404
|
-
Hello
|
|
1405
|
-
```
|
|
829
|
+
Whether an Actor is permanently gone is something the surrounding application has to decide.
|
|
1406
830
|
|
|
1407
|
-
|
|
831
|
+
## Sequence
|
|
1408
832
|
|
|
1409
|
-
|
|
833
|
+
A retained **Sequence** stores enough history to reconstruct the Projection later.
|
|
1410
834
|
|
|
1411
|
-
|
|
835
|
+
It contains canonical Insertion history, acknowledgement frontiers and the Footage belonging to surviving positive history.
|
|
1412
836
|
|
|
1413
|
-
|
|
1414
|
-
Hello
|
|
1415
|
-
```
|
|
837
|
+
Runtime fragmentation is not persisted as separate Insertions.
|
|
1416
838
|
|
|
1417
|
-
|
|
839
|
+
If one canonical Insertion currently exists as:
|
|
1418
840
|
|
|
1419
841
|
```text
|
|
1420
|
-
|
|
842
|
+
[A] [B C] [D]
|
|
1421
843
|
```
|
|
1422
844
|
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
If eligible, the existing positive history may simply extend.
|
|
1426
|
-
|
|
1427
|
-
Meanwhile Replica B inserts:
|
|
845
|
+
the retained history still describes the one original Insertion:
|
|
1428
846
|
|
|
1429
847
|
```text
|
|
1430
|
-
|
|
848
|
+
A B C D
|
|
1431
849
|
```
|
|
1432
850
|
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
Now the replicas contain concurrent history.
|
|
851
|
+
along with the other canonical history that caused its current result.
|
|
1436
852
|
|
|
1437
|
-
|
|
853
|
+
The next runtime can fragment it again as needed.
|
|
1438
854
|
|
|
1439
|
-
|
|
855
|
+
## Hydration
|
|
1440
856
|
|
|
1441
|
-
|
|
857
|
+
Loading a Sequence rebuilds Structural Order and derives the Projection from it.
|
|
1442
858
|
|
|
1443
|
-
|
|
1444
|
-
Hello beautiful world
|
|
1445
|
-
```
|
|
859
|
+
A hydrated runtime does not need to reproduce the exact Strip layout of the runtime that saved the Sequence.
|
|
1446
860
|
|
|
1447
|
-
|
|
861
|
+
For example, the old runtime may have ended with:
|
|
1448
862
|
|
|
1449
863
|
```text
|
|
1450
|
-
|
|
864
|
+
[A] [B C] [D]
|
|
1451
865
|
```
|
|
1452
866
|
|
|
1453
|
-
while
|
|
867
|
+
while the new one initially materializes:
|
|
1454
868
|
|
|
1455
869
|
```text
|
|
1456
|
-
|
|
870
|
+
[A B C D]
|
|
1457
871
|
```
|
|
1458
872
|
|
|
1459
|
-
inside
|
|
1460
|
-
|
|
1461
|
-
The removal masks only Frames actually observed by its author.
|
|
873
|
+
If later history needs a boundary inside it, it can split again.
|
|
1462
874
|
|
|
1463
|
-
|
|
875
|
+
Both represent the same canonical Insertion.
|
|
1464
876
|
|
|
1465
|
-
|
|
877
|
+
The visible sequence and the relationships available to future edits remain the same.
|
|
1466
878
|
|
|
1467
|
-
|
|
879
|
+
## Projection and history keep changing at different rates
|
|
1468
880
|
|
|
1469
|
-
A
|
|
881
|
+
A Projection can change dramatically while very little canonical history is added.
|
|
1470
882
|
|
|
1471
|
-
A
|
|
883
|
+
A long contiguous typing session may extend one positive Insertion.
|
|
1472
884
|
|
|
1473
|
-
|
|
1474
|
-
Hello wide world
|
|
1475
|
-
```
|
|
885
|
+
A removal can hide many Frames while leaving the positive Insertion itself in history.
|
|
1476
886
|
|
|
1477
|
-
|
|
887
|
+
Compaction can later make retained history smaller without changing the Projection at all.
|
|
1478
888
|
|
|
1479
|
-
|
|
889
|
+
So these are separate views of the same sequence:
|
|
1480
890
|
|
|
1481
891
|
```text
|
|
1482
|
-
|
|
1483
|
-
→ canonical anchors
|
|
1484
|
-
→ deterministic history
|
|
1485
|
-
→ concurrency
|
|
1486
|
-
→ fragmentation
|
|
1487
|
-
→ masking
|
|
1488
|
-
→ acknowledgement
|
|
1489
|
-
→ compaction
|
|
1490
|
-
→ equivalent reconstruction
|
|
892
|
+
Projection
|
|
1491
893
|
```
|
|
1492
894
|
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
# The central idea
|
|
1496
|
-
|
|
1497
|
-
Sequencer works because temporary visible positions are never treated as permanent distributed identities.
|
|
1498
|
-
|
|
1499
|
-
Every edit is translated into stable historical relationships.
|
|
1500
|
-
|
|
1501
|
-
Those relationships can survive:
|
|
895
|
+
is the content visible now.
|
|
1502
896
|
|
|
1503
897
|
```text
|
|
1504
|
-
|
|
1505
|
-
different packet orders
|
|
1506
|
-
duplicate delivery
|
|
1507
|
-
offline replicas
|
|
1508
|
-
fragmentation
|
|
1509
|
-
removal
|
|
1510
|
-
restart
|
|
1511
|
-
compaction
|
|
898
|
+
Structural Order
|
|
1512
899
|
```
|
|
1513
900
|
|
|
1514
|
-
|
|
901
|
+
is the history currently needed to resolve ordering and removals.
|
|
1515
902
|
|
|
1516
903
|
```text
|
|
1517
|
-
|
|
1518
|
-
↓
|
|
1519
|
-
canonical historical relationship
|
|
1520
|
-
↓
|
|
1521
|
-
deterministic Structural Order
|
|
1522
|
-
↓
|
|
1523
|
-
local Projection
|
|
904
|
+
Sequence
|
|
1524
905
|
```
|
|
1525
906
|
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
Once they know the same canonical history, they can independently derive the same result.
|
|
907
|
+
is the retained form of that history, with acknowledged obsolete history compacted away when possible.
|
|
1529
908
|
|
|
1530
|
-
|
|
909
|
+
An edit starts from the Projection, becomes a relationship in Structural Order, and can eventually survive in a much smaller retained Sequence after the history around it is no longer needed.
|