@fluidframework/merge-tree 0.59.4002 → 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/.eslintrc.js +1 -1
- package/README.md +1 -1
- package/REFERENCEPOSITIONS.md +127 -0
- package/dist/client.d.ts +17 -0
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +47 -41
- package/dist/client.js.map +1 -1
- package/dist/collections.d.ts +5 -4
- package/dist/collections.d.ts.map +1 -1
- package/dist/collections.js +17 -18
- package/dist/collections.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -1
- package/dist/index.js.map +1 -1
- package/dist/localReference.d.ts +4 -0
- package/dist/localReference.d.ts.map +1 -1
- package/dist/localReference.js +24 -3
- package/dist/localReference.js.map +1 -1
- package/dist/mergeTree.d.ts +20 -0
- package/dist/mergeTree.d.ts.map +1 -1
- package/dist/mergeTree.js +139 -48
- package/dist/mergeTree.js.map +1 -1
- package/dist/mergeTreeDeltaCallback.d.ts +8 -10
- package/dist/mergeTreeDeltaCallback.d.ts.map +1 -1
- package/dist/mergeTreeDeltaCallback.js +6 -10
- package/dist/mergeTreeDeltaCallback.js.map +1 -1
- package/dist/opBuilder.js +6 -5
- package/dist/opBuilder.js.map +1 -1
- package/dist/ops.d.ts +12 -10
- package/dist/ops.d.ts.map +1 -1
- package/dist/ops.js +7 -7
- package/dist/ops.js.map +1 -1
- package/dist/referencePositions.d.ts +1 -1
- package/dist/referencePositions.d.ts.map +1 -1
- package/dist/referencePositions.js +3 -2
- package/dist/referencePositions.js.map +1 -1
- package/lib/client.d.ts +17 -0
- package/lib/client.d.ts.map +1 -1
- package/lib/client.js +47 -41
- package/lib/client.js.map +1 -1
- package/lib/collections.d.ts +5 -4
- package/lib/collections.d.ts.map +1 -1
- package/lib/collections.js +17 -18
- package/lib/collections.js.map +1 -1
- package/lib/index.d.ts +1 -1
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +1 -1
- package/lib/index.js.map +1 -1
- package/lib/localReference.d.ts +4 -0
- package/lib/localReference.d.ts.map +1 -1
- package/lib/localReference.js +22 -2
- package/lib/localReference.js.map +1 -1
- package/lib/mergeTree.d.ts +20 -0
- package/lib/mergeTree.d.ts.map +1 -1
- package/lib/mergeTree.js +140 -49
- package/lib/mergeTree.js.map +1 -1
- package/lib/mergeTreeDeltaCallback.d.ts +8 -10
- package/lib/mergeTreeDeltaCallback.d.ts.map +1 -1
- package/lib/mergeTreeDeltaCallback.js +6 -10
- package/lib/mergeTreeDeltaCallback.js.map +1 -1
- package/lib/opBuilder.js +6 -5
- package/lib/opBuilder.js.map +1 -1
- package/lib/ops.d.ts +12 -10
- package/lib/ops.d.ts.map +1 -1
- package/lib/ops.js +7 -7
- package/lib/ops.js.map +1 -1
- package/lib/referencePositions.d.ts +1 -1
- package/lib/referencePositions.d.ts.map +1 -1
- package/lib/referencePositions.js +3 -2
- package/lib/referencePositions.js.map +1 -1
- package/package.json +69 -74
- package/src/client.ts +27 -18
- package/src/collections.ts +5 -4
- package/src/index.ts +1 -1
- package/src/localReference.ts +24 -2
- package/src/mergeTree.ts +133 -39
- package/src/mergeTreeDeltaCallback.ts +8 -10
- package/src/ops.ts +13 -10
- package/src/referencePositions.ts +3 -2
package/.eslintrc.js
CHANGED
package/README.md
CHANGED
|
@@ -89,7 +89,7 @@ A segment was inserted and/or removed on the remote client at the time client se
|
|
|
89
89
|
|
|
90
90
|
* The referenced sequence number is greater than or equal the server-assigned sequence number of the operation
|
|
91
91
|
that inserted/removed the segment.
|
|
92
|
-
* The client sent the operation that resulted in insertion/removal. (In which case, the client hadn't yet
|
|
92
|
+
* The client sent the operation that resulted in insertion/removal. (In which case, the client hadn't yet received
|
|
93
93
|
their sequenced op from the server but was aware of the insertion/removal because the client produced it locally.)
|
|
94
94
|
|
|
95
95
|
If both above conditions are false, then the insertion/removal happened "after" the remote operation, and
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
# ReferencePosition Documentation
|
|
2
|
+
|
|
3
|
+
ReferencePositions are used to indicates a MergeTree position which is stable as operations are performed. There are two
|
|
4
|
+
types:
|
|
5
|
+
|
|
6
|
+
1. LocalReferences refer to a segment and offset within that segment
|
|
7
|
+
2. Markers are actual segments in the Merge Tree
|
|
8
|
+
|
|
9
|
+
The function `Client.localReferencePositionToPosition` returns the numerical position of a reference in the client's
|
|
10
|
+
current view.
|
|
11
|
+
|
|
12
|
+
## LocalReference behavior on Remove
|
|
13
|
+
|
|
14
|
+
By default, LocalReferences become detached when the segment they reference is removed.
|
|
15
|
+
The ReferenceTypes SlideOnRemove, StayOnRemove, and Transient change this behavior.
|
|
16
|
+
They are only valid for LocalReferences.
|
|
17
|
+
They are exclusive - a reference may be at most one of these types.
|
|
18
|
+
|
|
19
|
+
### SlideOnRemove
|
|
20
|
+
|
|
21
|
+
The reference will slide to the next farthest segment when the segment is removed and the remove has been acknowledged.
|
|
22
|
+
Sliding will look for the next valid segment.
|
|
23
|
+
A valid segment is one whose creation has been acknowledged and either hasn't been removed
|
|
24
|
+
or the remove is pending (not acknowledged).
|
|
25
|
+
If a farther segment is found, then the LocalReference will be changed to refer to that segment and have offset 0.
|
|
26
|
+
In the event that the slide is happening on the acknowledgement of a remove, the slide to a farther segment will not
|
|
27
|
+
change the numerical position of the reference.
|
|
28
|
+
If there is no there is no valid segment farther in the tree, then the slide will place the reference on the last valid segment.
|
|
29
|
+
The offset will be set to the last position in that segment.
|
|
30
|
+
In the event that the slide is happening on the acknowledgement of a remove, the reference would have been on the removed
|
|
31
|
+
segment. This slide from the removed segment to a nearer segment does change the numerical position of the reference.
|
|
32
|
+
If there is no valid position (all segments removed and acknowledged) then the reference is detached.
|
|
33
|
+
|
|
34
|
+
### StayOnRemove
|
|
35
|
+
|
|
36
|
+
The reference will stay on removed segments.
|
|
37
|
+
This behavior is only defined until the removed segment is cleaned up by Zamboni.
|
|
38
|
+
This is intended to be used only while collaborating (see below) while waiting for an acknowledgement.
|
|
39
|
+
|
|
40
|
+
### Transient
|
|
41
|
+
|
|
42
|
+
The reference is not tracked by the MergeTree.
|
|
43
|
+
It will continue to reference removed segments.
|
|
44
|
+
This behavior is only defined until the removed segment is cleaned up by Zamboni.
|
|
45
|
+
This is intended to be used to create transient references which may be compared with other references.
|
|
46
|
+
|
|
47
|
+
### Detached LocalReferences
|
|
48
|
+
|
|
49
|
+
A detached LocalReference does not reference a segment in the MergeTree.
|
|
50
|
+
It's position is defined to be `LocalReference.DetachedPosition` (-1).
|
|
51
|
+
|
|
52
|
+
### LocalReferences on Removed Segments
|
|
53
|
+
|
|
54
|
+
LocalReferences may reference removed segments:
|
|
55
|
+
|
|
56
|
+
* SlideOnRemove references may reference a removed segment which is pending (not acknoweldged)
|
|
57
|
+
* StayOnRemove references may reference removed segments
|
|
58
|
+
* Transient references may reference removed segments
|
|
59
|
+
|
|
60
|
+
The numerical position of a reference which is on a removed segment will be one more than the previous (nearer) segment.
|
|
61
|
+
If there is a farther segment that is not removed, this will be the same as the position of the start of that segment.
|
|
62
|
+
If there is no farther segment, then the reference position will be the length of the tree (one more than the last valid
|
|
63
|
+
position in the tree).
|
|
64
|
+
|
|
65
|
+
## Eventually Consistent References
|
|
66
|
+
|
|
67
|
+
Markers are segments in the MergeTree and are eventually consistent.
|
|
68
|
+
LocalReferences may be used as part of an eventually consistent feature.
|
|
69
|
+
For example, SharedIntervals are built using LocalReferences.
|
|
70
|
+
|
|
71
|
+
### Implementing Eventually Consistent LocalReferences
|
|
72
|
+
|
|
73
|
+
To implement an operation which creates LocalReferences which will be have an eventually consistent position:
|
|
74
|
+
|
|
75
|
+
1. Locally create the reference as StayOnRemove
|
|
76
|
+
2. Send the reference numerical position in an op
|
|
77
|
+
3. On acknowledgement of the local create:
|
|
78
|
+
1. set the `refType` of the reference to include `SlideOnRemove`
|
|
79
|
+
2. call `Client.getSlideToSegment` with the references current segment and offset to get the proper new location
|
|
80
|
+
3. Delete the old reference and create a new one with the returned values
|
|
81
|
+
4. Remote clients, on receiving the op, call `Client.getContainingSegment` followed by `Client.getSlideToSegment`
|
|
82
|
+
on the result. Call `Client.createLocalReferencePosition` with the result to create a `SlideOnRemove` reference.
|
|
83
|
+
|
|
84
|
+
### Implementation Notes
|
|
85
|
+
|
|
86
|
+
This algorithm works because it ensures that slid reference slide to the same segment.
|
|
87
|
+
The slide only happens when both the creation of the reference and removal of the segment have been acknowledged.
|
|
88
|
+
When sliding we do not consider any local (unacknowledged) ops.
|
|
89
|
+
|
|
90
|
+
Keeping references on removed segments until they can be slid works well in most cases because of these properties:
|
|
91
|
+
|
|
92
|
+
1. Interval positions on removed segments appear as if they were on the following position in the string.
|
|
93
|
+
If the removed segment is between positions 5 and 6, the interval positions on the removed segment appear to be at
|
|
94
|
+
position 6. This matches where they will eventually slide, so slide will not cause a change in position as long as
|
|
95
|
+
segments are not slid over and it is not necessary to slide to the near end of the string.
|
|
96
|
+
2. Text inserted at the same location as the removed segment is inserted before the removed segment.
|
|
97
|
+
So if the removed segment is between 0 and 1 (“A[removed]B”), insertText(1, “X”) inserts before the removed segment
|
|
98
|
+
(“AX[removed]B”). This makes it hard to end up with local only segments to be slid over, which will mean it is rare
|
|
99
|
+
that slide visibly changes the interval position. It can still happen if there is a conflicting remove, but that is
|
|
100
|
+
much less likely.
|
|
101
|
+
|
|
102
|
+
#### Conflict Scenarios
|
|
103
|
+
|
|
104
|
+
Considering Create Interval / Remove Range conflicts, here are the scenarios
|
|
105
|
+
(before indicates the relative sequence order):
|
|
106
|
+
|
|
107
|
+
1. Local create before local remove. Interval position needs to slide on ack of the local remove.
|
|
108
|
+
2. Remote create before remote remove. Slide on receiving the remove.
|
|
109
|
+
3. Local remove before local create. This is impossible – once the segment is removed locally an
|
|
110
|
+
interval position can’t be created on it.
|
|
111
|
+
4. Remote remove before remote create. (Possible if ops are from different remote clients).
|
|
112
|
+
Slide on receiving the remote create.
|
|
113
|
+
5. Remote create before local remove. Slide on the ack of the local remove.
|
|
114
|
+
6. Local create before remote remove. Slide on receiving the remove.
|
|
115
|
+
7. Local remove before remote create. Slide on receiving the create.
|
|
116
|
+
8. Remote remove before local create. Slide on receiving the ack of the create.
|
|
117
|
+
|
|
118
|
+
## Tests
|
|
119
|
+
|
|
120
|
+
* `packages\dds\merge-tree\src\test\client.localReference.spec.ts`
|
|
121
|
+
unit tests for LocalReferences
|
|
122
|
+
* `packages\dds\sequence\src\test\intervalCollection.spec.ts`
|
|
123
|
+
test LocalReferences as used in interval collections (including eventual consistency)
|
|
124
|
+
* `packages\test\test-end-to-end-tests\src\test\sharedInterval.spec.ts`
|
|
125
|
+
end-to-end tests using LocalReferences for interval collections.
|
|
126
|
+
These tests have only been minimally updated to reflect this implementation,
|
|
127
|
+
so they do not comprehensively test LocalReferences.
|
package/dist/client.d.ts
CHANGED
|
@@ -152,6 +152,11 @@ export declare class Client {
|
|
|
152
152
|
* @param clientArgs - The client args for the op
|
|
153
153
|
*/
|
|
154
154
|
private getValidOpRange;
|
|
155
|
+
/**
|
|
156
|
+
* Gets the client args from the op if remote, otherwise uses the local clients info
|
|
157
|
+
* @param sequencedMessage - The sequencedMessage to get the client sequence args for
|
|
158
|
+
*/
|
|
159
|
+
private getClientSequenceArgsForMessage;
|
|
155
160
|
/**
|
|
156
161
|
* Gets the client args from the op if remote, otherwise uses the local clients info
|
|
157
162
|
* @param opArgs - The op arg to get the client sequence args for
|
|
@@ -210,6 +215,18 @@ export declare class Client {
|
|
|
210
215
|
segment: T | undefined;
|
|
211
216
|
offset: number | undefined;
|
|
212
217
|
};
|
|
218
|
+
/**
|
|
219
|
+
* Returns the position to slide a reference to if a slide is required.
|
|
220
|
+
* @param segoff - The segment and offset to slide from
|
|
221
|
+
* @returns - segment and offset to slide the reference to
|
|
222
|
+
*/
|
|
223
|
+
getSlideToSegment(segoff: {
|
|
224
|
+
segment: ISegment | undefined;
|
|
225
|
+
offset: number | undefined;
|
|
226
|
+
}): {
|
|
227
|
+
segment: ISegment | undefined;
|
|
228
|
+
offset: number | undefined;
|
|
229
|
+
};
|
|
213
230
|
getPropertiesAtPosition(pos: number): PropertySet | undefined;
|
|
214
231
|
getRangeExtentsOfPosition(pos: number): {
|
|
215
232
|
posStart: number | undefined;
|
package/dist/client.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,OAAO,EAAE,YAAY,EAAE,MAAM,iCAAiC,CAAC;AAC/D,OAAO,EAAE,gBAAgB,EAAE,MAAM,oCAAoC,CAAC;AACtE,OAAO,EAAE,yBAAyB,EAAe,MAAM,sCAAsC,CAAC;AAC9F,OAAO,EAAE,sBAAsB,EAAE,sBAAsB,EAAE,MAAM,uCAAuC,CAAC;AACvG,OAAO,EAAE,qBAAqB,EAAE,MAAM,qCAAqC,CAAC;AAC5E,OAAO,EAAE,gBAAgB,EAAE,MAAM,oCAAoC,CAAC;AAMtE,OAAO,EAAE,cAAc,
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
|
package/dist/client.js
CHANGED
|
@@ -13,6 +13,7 @@ const constants_1 = require("./constants");
|
|
|
13
13
|
const localReference_1 = require("./localReference");
|
|
14
14
|
const mergeTree_1 = require("./mergeTree");
|
|
15
15
|
const opBuilder_1 = require("./opBuilder");
|
|
16
|
+
const ops_1 = require("./ops");
|
|
16
17
|
const snapshotlegacy_1 = require("./snapshotlegacy");
|
|
17
18
|
const snapshotLoader_1 = require("./snapshotLoader");
|
|
18
19
|
const textSegment_1 = require("./textSegment");
|
|
@@ -247,7 +248,7 @@ class Client {
|
|
|
247
248
|
* @returns True if the remove was applied. False if it could not be.
|
|
248
249
|
*/
|
|
249
250
|
applyRemoveRangeOp(opArgs) {
|
|
250
|
-
(0, common_utils_1.assert)(opArgs.op.type ===
|
|
251
|
+
(0, common_utils_1.assert)(opArgs.op.type === ops_1.MergeTreeDeltaType.REMOVE, 0x02d /* "Unexpected op type on range remove!" */);
|
|
251
252
|
const op = opArgs.op;
|
|
252
253
|
const clientArgs = this.getClientSequenceArgs(opArgs);
|
|
253
254
|
const range = this.getValidOpRange(op, clientArgs);
|
|
@@ -268,7 +269,7 @@ class Client {
|
|
|
268
269
|
* @returns True if the annotate was applied. False if it could not be.
|
|
269
270
|
*/
|
|
270
271
|
applyAnnotateRangeOp(opArgs) {
|
|
271
|
-
(0, common_utils_1.assert)(opArgs.op.type ===
|
|
272
|
+
(0, common_utils_1.assert)(opArgs.op.type === ops_1.MergeTreeDeltaType.ANNOTATE, 0x02e /* "Unexpected op type on range annotate!" */);
|
|
272
273
|
const op = opArgs.op;
|
|
273
274
|
const clientArgs = this.getClientSequenceArgs(opArgs);
|
|
274
275
|
const range = this.getValidOpRange(op, clientArgs);
|
|
@@ -289,7 +290,7 @@ class Client {
|
|
|
289
290
|
* @returns True if the insert was applied. False if it could not be.
|
|
290
291
|
*/
|
|
291
292
|
applyInsertOp(opArgs) {
|
|
292
|
-
(0, common_utils_1.assert)(opArgs.op.type ===
|
|
293
|
+
(0, common_utils_1.assert)(opArgs.op.type === ops_1.MergeTreeDeltaType.INSERT, 0x02f /* "Unexpected op type on range insert!" */);
|
|
293
294
|
const op = opArgs.op;
|
|
294
295
|
const clientArgs = this.getClientSequenceArgs(opArgs);
|
|
295
296
|
const range = this.getValidOpRange(op, clientArgs);
|
|
@@ -358,12 +359,12 @@ class Client {
|
|
|
358
359
|
if (start === undefined
|
|
359
360
|
|| start < 0
|
|
360
361
|
|| start > length
|
|
361
|
-
|| start === length && op.type !==
|
|
362
|
+
|| start === length && op.type !== ops_1.MergeTreeDeltaType.INSERT) {
|
|
362
363
|
invalidPositions.push("start");
|
|
363
364
|
}
|
|
364
365
|
// Validate end if not insert, or insert has end
|
|
365
366
|
//
|
|
366
|
-
if (op.type !==
|
|
367
|
+
if (op.type !== ops_1.MergeTreeDeltaType.INSERT || end !== undefined) {
|
|
367
368
|
if (end === undefined || end <= start) {
|
|
368
369
|
invalidPositions.push("end");
|
|
369
370
|
}
|
|
@@ -389,13 +390,13 @@ class Client {
|
|
|
389
390
|
}
|
|
390
391
|
/**
|
|
391
392
|
* Gets the client args from the op if remote, otherwise uses the local clients info
|
|
392
|
-
* @param
|
|
393
|
+
* @param sequencedMessage - The sequencedMessage to get the client sequence args for
|
|
393
394
|
*/
|
|
394
|
-
|
|
395
|
+
getClientSequenceArgsForMessage(sequencedMessage) {
|
|
395
396
|
// If there this no sequenced message, then the op is local
|
|
396
397
|
// and unacked, so use this clients sequenced args
|
|
397
398
|
//
|
|
398
|
-
if (!
|
|
399
|
+
if (!sequencedMessage) {
|
|
399
400
|
const segWindow = this.getCollabWindow();
|
|
400
401
|
return {
|
|
401
402
|
clientId: segWindow.clientId,
|
|
@@ -405,12 +406,19 @@ class Client {
|
|
|
405
406
|
}
|
|
406
407
|
else {
|
|
407
408
|
return {
|
|
408
|
-
clientId: this.
|
|
409
|
-
referenceSequenceNumber:
|
|
410
|
-
sequenceNumber:
|
|
409
|
+
clientId: this.getOrAddShortClientId(sequencedMessage.clientId),
|
|
410
|
+
referenceSequenceNumber: sequencedMessage.referenceSequenceNumber,
|
|
411
|
+
sequenceNumber: sequencedMessage.sequenceNumber,
|
|
411
412
|
};
|
|
412
413
|
}
|
|
413
414
|
}
|
|
415
|
+
/**
|
|
416
|
+
* Gets the client args from the op if remote, otherwise uses the local clients info
|
|
417
|
+
* @param opArgs - The op arg to get the client sequence args for
|
|
418
|
+
*/
|
|
419
|
+
getClientSequenceArgs(opArgs) {
|
|
420
|
+
return this.getClientSequenceArgsForMessage(opArgs.sequencedMessage);
|
|
421
|
+
}
|
|
414
422
|
ackPendingSegment(opArgs) {
|
|
415
423
|
const ackOp = (deltaOpArgs) => {
|
|
416
424
|
let trace;
|
|
@@ -418,7 +426,7 @@ class Client {
|
|
|
418
426
|
trace = common_utils_1.Trace.start();
|
|
419
427
|
}
|
|
420
428
|
this.mergeTree.ackPendingSegment(deltaOpArgs);
|
|
421
|
-
if (deltaOpArgs.op.type ===
|
|
429
|
+
if (deltaOpArgs.op.type === ops_1.MergeTreeDeltaType.ANNOTATE) {
|
|
422
430
|
if (deltaOpArgs.op.combiningOp && (deltaOpArgs.op.combiningOp.name === "consensus")) {
|
|
423
431
|
this.updateConsensusProperty(deltaOpArgs.op, deltaOpArgs.sequencedMessage);
|
|
424
432
|
}
|
|
@@ -429,7 +437,7 @@ class Client {
|
|
|
429
437
|
this.accumWindow += (this.getCurrentSeq() - this.getCollabWindow().minSeq);
|
|
430
438
|
}
|
|
431
439
|
};
|
|
432
|
-
if (opArgs.op.type ===
|
|
440
|
+
if (opArgs.op.type === ops_1.MergeTreeDeltaType.GROUP) {
|
|
433
441
|
for (const memberOp of opArgs.op.ops) {
|
|
434
442
|
ackOp({
|
|
435
443
|
groupOp: opArgs.op,
|
|
@@ -527,7 +535,7 @@ class Client {
|
|
|
527
535
|
const segmentPosition = this.findReconnectionPosition(segment, segmentGroup.localSeq);
|
|
528
536
|
let newOp;
|
|
529
537
|
switch (resetOp.type) {
|
|
530
|
-
case
|
|
538
|
+
case ops_1.MergeTreeDeltaType.ANNOTATE:
|
|
531
539
|
(0, common_utils_1.assert)(((_b = segment.propertyManager) === null || _b === void 0 ? void 0 : _b.hasPendingProperties()) === true, 0x036 /* "Segment has no pending properties" */);
|
|
532
540
|
// if the segment has been removed, there's no need to send the annotate op
|
|
533
541
|
// unless the remove was local, in which case the annotate must have come
|
|
@@ -536,11 +544,11 @@ class Client {
|
|
|
536
544
|
newOp = (0, opBuilder_1.createAnnotateRangeOp)(segmentPosition, segmentPosition + segment.cachedLength, resetOp.props, resetOp.combiningOp);
|
|
537
545
|
}
|
|
538
546
|
break;
|
|
539
|
-
case
|
|
547
|
+
case ops_1.MergeTreeDeltaType.INSERT:
|
|
540
548
|
(0, common_utils_1.assert)(segment.seq === constants_1.UnassignedSequenceNumber, 0x037 /* "Segment already has assigned sequence number" */);
|
|
541
549
|
newOp = (0, opBuilder_1.createInsertSegmentOp)(segmentPosition, segment);
|
|
542
550
|
break;
|
|
543
|
-
case
|
|
551
|
+
case ops_1.MergeTreeDeltaType.REMOVE:
|
|
544
552
|
if (segment.localRemovedSeq !== undefined) {
|
|
545
553
|
newOp = (0, opBuilder_1.createRemoveRangeOp)(segmentPosition, segmentPosition + segment.cachedLength);
|
|
546
554
|
}
|
|
@@ -562,16 +570,16 @@ class Client {
|
|
|
562
570
|
const msg = opArgs.sequencedMessage;
|
|
563
571
|
this.getOrAddShortClientId(msg.clientId);
|
|
564
572
|
switch (op.type) {
|
|
565
|
-
case
|
|
573
|
+
case ops_1.MergeTreeDeltaType.INSERT:
|
|
566
574
|
this.applyInsertOp(opArgs);
|
|
567
575
|
break;
|
|
568
|
-
case
|
|
576
|
+
case ops_1.MergeTreeDeltaType.REMOVE:
|
|
569
577
|
this.applyRemoveRangeOp(opArgs);
|
|
570
578
|
break;
|
|
571
|
-
case
|
|
579
|
+
case ops_1.MergeTreeDeltaType.ANNOTATE:
|
|
572
580
|
this.applyAnnotateRangeOp(opArgs);
|
|
573
581
|
break;
|
|
574
|
-
case
|
|
582
|
+
case ops_1.MergeTreeDeltaType.GROUP: {
|
|
575
583
|
for (const memberOp of op.ops) {
|
|
576
584
|
this.applyRemoteOp({
|
|
577
585
|
op: memberOp,
|
|
@@ -588,19 +596,19 @@ class Client {
|
|
|
588
596
|
applyStashedOp(op) {
|
|
589
597
|
let metadata;
|
|
590
598
|
switch (op.type) {
|
|
591
|
-
case
|
|
599
|
+
case ops_1.MergeTreeDeltaType.INSERT:
|
|
592
600
|
this.applyInsertOp({ op });
|
|
593
601
|
metadata = this.peekPendingSegmentGroups();
|
|
594
602
|
break;
|
|
595
|
-
case
|
|
603
|
+
case ops_1.MergeTreeDeltaType.REMOVE:
|
|
596
604
|
this.applyRemoveRangeOp({ op });
|
|
597
605
|
metadata = this.peekPendingSegmentGroups();
|
|
598
606
|
break;
|
|
599
|
-
case
|
|
607
|
+
case ops_1.MergeTreeDeltaType.ANNOTATE:
|
|
600
608
|
this.applyAnnotateRangeOp({ op });
|
|
601
609
|
metadata = this.peekPendingSegmentGroups();
|
|
602
610
|
break;
|
|
603
|
-
case
|
|
611
|
+
case ops_1.MergeTreeDeltaType.GROUP:
|
|
604
612
|
return op.ops.map((o) => this.applyStashedOp(o));
|
|
605
613
|
default:
|
|
606
614
|
(0, common_utils_1.unreachableCase)(op, "unrecognized op type");
|
|
@@ -655,7 +663,7 @@ class Client {
|
|
|
655
663
|
*/
|
|
656
664
|
regeneratePendingOp(resetOp, segmentGroup) {
|
|
657
665
|
const opList = [];
|
|
658
|
-
if (resetOp.type ===
|
|
666
|
+
if (resetOp.type === ops_1.MergeTreeDeltaType.GROUP) {
|
|
659
667
|
if (Array.isArray(segmentGroup)) {
|
|
660
668
|
(0, common_utils_1.assert)(resetOp.ops.length === segmentGroup.length, 0x03a /* "Number of ops in 'resetOp' must match the number of segment groups provided." */);
|
|
661
669
|
for (let i = 0; i < resetOp.ops.length; i++) {
|
|
@@ -670,7 +678,7 @@ class Client {
|
|
|
670
678
|
}
|
|
671
679
|
}
|
|
672
680
|
else {
|
|
673
|
-
(0, common_utils_1.assert)(resetOp.type !==
|
|
681
|
+
(0, common_utils_1.assert)(resetOp.type !== ops_1.MergeTreeDeltaType.GROUP, 0x03c /* "Reset op has 'group' delta type!" */);
|
|
674
682
|
(0, common_utils_1.assert)(!Array.isArray(segmentGroup), 0x03d /* "segmentGroup is array rather than singleton!" */);
|
|
675
683
|
opList.push(...this.resetPendingDeltaToOps(resetOp, segmentGroup));
|
|
676
684
|
}
|
|
@@ -727,13 +735,13 @@ class Client {
|
|
|
727
735
|
groupOp,
|
|
728
736
|
};
|
|
729
737
|
switch (op.type) {
|
|
730
|
-
case
|
|
738
|
+
case ops_1.MergeTreeDeltaType.INSERT:
|
|
731
739
|
this.applyInsertOp(opArgs);
|
|
732
740
|
break;
|
|
733
|
-
case
|
|
741
|
+
case ops_1.MergeTreeDeltaType.ANNOTATE:
|
|
734
742
|
this.applyAnnotateRangeOp(opArgs);
|
|
735
743
|
break;
|
|
736
|
-
case
|
|
744
|
+
case ops_1.MergeTreeDeltaType.REMOVE:
|
|
737
745
|
this.applyRemoveRangeOp(opArgs);
|
|
738
746
|
break;
|
|
739
747
|
default:
|
|
@@ -764,18 +772,16 @@ class Client {
|
|
|
764
772
|
}
|
|
765
773
|
}
|
|
766
774
|
getContainingSegment(pos, op) {
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
}
|
|
778
|
-
return this.mergeTree.getContainingSegment(pos, seq, clientId);
|
|
775
|
+
const args = this.getClientSequenceArgsForMessage(op);
|
|
776
|
+
return this.mergeTree.getContainingSegment(pos, args.referenceSequenceNumber, args.clientId);
|
|
777
|
+
}
|
|
778
|
+
/**
|
|
779
|
+
* Returns the position to slide a reference to if a slide is required.
|
|
780
|
+
* @param segoff - The segment and offset to slide from
|
|
781
|
+
* @returns - segment and offset to slide the reference to
|
|
782
|
+
*/
|
|
783
|
+
getSlideToSegment(segoff) {
|
|
784
|
+
return this.mergeTree._getSlideToSegment(segoff);
|
|
779
785
|
}
|
|
780
786
|
getPropertiesAtPosition(pos) {
|
|
781
787
|
let propertiesAtPosition;
|