@fluidframework/matrix 2.0.0-internal.7.3.0 → 2.0.0-internal.8.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/CHANGELOG.md +32 -0
- package/README.md +39 -0
- package/api-extractor-lint.json +13 -0
- package/api-extractor.json +8 -3
- package/api-report/matrix.api.md +22 -12
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/matrix-alpha.d.ts +74 -14
- package/dist/matrix-beta.d.ts +41 -134
- package/dist/matrix-public.d.ts +41 -134
- package/dist/matrix-untrimmed.d.ts +74 -14
- package/dist/matrix.cjs +178 -48
- package/dist/matrix.cjs.map +1 -1
- package/dist/matrix.d.ts +67 -9
- package/dist/matrix.d.ts.map +1 -1
- package/dist/ops.cjs +1 -0
- package/dist/ops.cjs.map +1 -1
- package/dist/ops.d.ts +6 -2
- package/dist/ops.d.ts.map +1 -1
- package/dist/packageVersion.cjs +1 -1
- package/dist/packageVersion.cjs.map +1 -1
- package/dist/packageVersion.d.ts +1 -1
- package/dist/permutationvector.cjs +1 -1
- package/dist/permutationvector.cjs.map +1 -1
- package/dist/permutationvector.d.ts.map +1 -1
- package/dist/runtime.cjs +1 -1
- package/dist/runtime.cjs.map +1 -1
- package/dist/runtime.d.ts +1 -1
- package/dist/serialization.cjs.map +1 -1
- package/dist/serialization.d.ts +1 -1
- package/dist/serialization.d.ts.map +1 -1
- package/dist/types.cjs.map +1 -1
- package/dist/types.d.ts +5 -5
- package/dist/types.d.ts.map +1 -1
- package/lib/handlecache.d.ts +2 -2
- package/lib/handlecache.d.ts.map +1 -1
- package/lib/index.d.ts +3 -3
- package/lib/index.d.ts.map +1 -1
- package/lib/index.mjs.map +1 -1
- package/lib/matrix-alpha.d.ts +74 -14
- package/lib/matrix-beta.d.ts +41 -134
- package/lib/matrix-public.d.ts +41 -134
- package/lib/matrix-untrimmed.d.ts +74 -14
- package/lib/matrix.d.ts +69 -11
- package/lib/matrix.d.ts.map +1 -1
- package/lib/matrix.mjs +178 -47
- package/lib/matrix.mjs.map +1 -1
- package/lib/ops.d.ts +6 -2
- package/lib/ops.d.ts.map +1 -1
- package/lib/ops.mjs +1 -0
- package/lib/ops.mjs.map +1 -1
- package/lib/packageVersion.d.ts +1 -1
- package/lib/packageVersion.mjs +1 -1
- package/lib/packageVersion.mjs.map +1 -1
- package/lib/permutationvector.d.ts +3 -3
- package/lib/permutationvector.d.ts.map +1 -1
- package/lib/permutationvector.mjs +4 -1
- package/lib/permutationvector.mjs.map +1 -1
- package/lib/runtime.d.ts +1 -1
- package/lib/runtime.d.ts.map +1 -1
- package/lib/runtime.mjs +1 -1
- package/lib/runtime.mjs.map +1 -1
- package/lib/serialization.d.ts +1 -1
- package/lib/serialization.d.ts.map +1 -1
- package/lib/serialization.mjs.map +1 -1
- package/lib/sparsearray2d.d.ts.map +1 -1
- package/lib/types.d.ts +5 -5
- package/lib/types.d.ts.map +1 -1
- package/lib/types.mjs.map +1 -1
- package/lib/undoprovider.d.ts +4 -4
- package/lib/undoprovider.d.ts.map +1 -1
- package/matrix.test-files.tar +0 -0
- package/package.json +26 -31
- package/src/index.ts +1 -1
- package/src/matrix.ts +284 -62
- package/src/ops.ts +6 -1
- package/src/packageVersion.ts +1 -1
- package/src/permutationvector.ts +2 -2
- package/src/runtime.ts +1 -1
- package/src/serialization.ts +2 -2
- package/src/types.ts +5 -5
- package/tsconfig.json +1 -0
package/lib/matrix.d.ts
CHANGED
|
@@ -2,18 +2,45 @@
|
|
|
2
2
|
* Copyright (c) Microsoft Corporation and contributors. All rights reserved.
|
|
3
3
|
* Licensed under the MIT License.
|
|
4
4
|
*/
|
|
5
|
+
import { IEventThisPlaceHolder } from "@fluidframework/core-interfaces";
|
|
5
6
|
import { ISequencedDocumentMessage } from "@fluidframework/protocol-definitions";
|
|
6
7
|
import { IFluidDataStoreRuntime, IChannelStorageService, Serializable, IChannelAttributes } from "@fluidframework/datastore-definitions";
|
|
7
|
-
import { IFluidSerializer, SharedObject, SummarySerializer } from "@fluidframework/shared-object-base";
|
|
8
|
+
import { IFluidSerializer, ISharedObjectEvents, SharedObject, SummarySerializer } from "@fluidframework/shared-object-base";
|
|
8
9
|
import { ISummaryTreeWithStats } from "@fluidframework/runtime-definitions";
|
|
9
10
|
import { IMatrixProducer, IMatrixConsumer, IMatrixReader, IMatrixWriter } from "@tiny-calc/nano";
|
|
10
11
|
import { IJSONSegment } from "@fluidframework/merge-tree";
|
|
11
|
-
import { SharedMatrixFactory } from "./runtime";
|
|
12
|
-
import { IUndoConsumer } from "./types";
|
|
12
|
+
import { SharedMatrixFactory } from "./runtime.mjs";
|
|
13
|
+
import { IUndoConsumer } from "./types.mjs";
|
|
14
|
+
/**
|
|
15
|
+
* Events emitted by Shared Matrix.
|
|
16
|
+
* @alpha
|
|
17
|
+
*/
|
|
18
|
+
export interface ISharedMatrixEvents<T> extends ISharedObjectEvents {
|
|
19
|
+
/**
|
|
20
|
+
* This event is only emitted when the SetCell Resolution Policy is First Write Win(FWW).
|
|
21
|
+
* This is emitted when two clients race and send changes without observing each other changes,
|
|
22
|
+
* the changes that gets sequenced last would be rejected, and only client who's changes rejected
|
|
23
|
+
* would be notified via this event, with expectation that it will merge its changes back by
|
|
24
|
+
* accounting new information (state from winner of the race).
|
|
25
|
+
*
|
|
26
|
+
* @remarks Listener parameters:
|
|
27
|
+
*
|
|
28
|
+
* - `row` - Row number at which conflict happened.
|
|
29
|
+
*
|
|
30
|
+
* - `col` - Col number at which conflict happened.
|
|
31
|
+
*
|
|
32
|
+
* - `currentValue` - The current value of the cell.
|
|
33
|
+
*
|
|
34
|
+
* - `conflictingValue` - The value that this client tried to set in the cell and got ignored due to conflict.
|
|
35
|
+
*
|
|
36
|
+
* - `target` - The {@link SharedMatrix} itself.
|
|
37
|
+
*/
|
|
38
|
+
(event: "conflict", listener: (row: number, col: number, currentValue: MatrixItem<T>, conflictingValue: MatrixItem<T>, target: IEventThisPlaceHolder) => void): void;
|
|
39
|
+
}
|
|
13
40
|
/**
|
|
14
41
|
* A matrix cell value may be undefined (indicating an empty cell) or any serializable type,
|
|
15
42
|
* excluding null. (However, nulls may be embedded inside objects and arrays.)
|
|
16
|
-
* @
|
|
43
|
+
* @alpha
|
|
17
44
|
*/
|
|
18
45
|
export type MatrixItem<T> = Serializable<Exclude<T, null>> | undefined;
|
|
19
46
|
/**
|
|
@@ -27,18 +54,29 @@ export type MatrixItem<T> = Serializable<Exclude<T, null>> | undefined;
|
|
|
27
54
|
* matrix data and physically stores data in Z-order to leverage CPU caches and
|
|
28
55
|
* prefetching when reading in either row or column major order. (See README.md
|
|
29
56
|
* for more details.)
|
|
30
|
-
*
|
|
31
|
-
* @public
|
|
57
|
+
* @alpha
|
|
32
58
|
*/
|
|
33
|
-
export declare class SharedMatrix<T = any> extends SharedObject implements IMatrixProducer<MatrixItem<T>>, IMatrixReader<MatrixItem<T>>, IMatrixWriter<MatrixItem<T>> {
|
|
59
|
+
export declare class SharedMatrix<T = any> extends SharedObject<ISharedMatrixEvents<T>> implements IMatrixProducer<MatrixItem<T>>, IMatrixReader<MatrixItem<T>>, IMatrixWriter<MatrixItem<T>> {
|
|
34
60
|
id: string;
|
|
35
61
|
private readonly consumers;
|
|
36
62
|
static getFactory(): SharedMatrixFactory;
|
|
37
63
|
private readonly rows;
|
|
38
64
|
private readonly cols;
|
|
39
65
|
private cells;
|
|
40
|
-
private pending;
|
|
41
|
-
|
|
66
|
+
private readonly pending;
|
|
67
|
+
private cellLastWriteTracker;
|
|
68
|
+
private setCellLwwToFwwPolicySwitchOpSeqNumber;
|
|
69
|
+
private userSwitchedSetCellPolicy;
|
|
70
|
+
private reentrantCount;
|
|
71
|
+
/**
|
|
72
|
+
* Constructor for the Shared Matrix
|
|
73
|
+
* @param runtime - DataStore runtime.
|
|
74
|
+
* @param id - id of the dds
|
|
75
|
+
* @param attributes - channel attributes
|
|
76
|
+
* @param _isSetCellConflictResolutionPolicyFWW - Conflict resolution for Matrix set op is First Writer Win in case of
|
|
77
|
+
* race condition. Client can still overwrite values in case of no race.
|
|
78
|
+
*/
|
|
79
|
+
constructor(runtime: IFluidDataStoreRuntime, id: string, attributes: IChannelAttributes, _isSetCellConflictResolutionPolicyFWW?: boolean);
|
|
42
80
|
private undo?;
|
|
43
81
|
/**
|
|
44
82
|
* Subscribes the given IUndoConsumer to the matrix.
|
|
@@ -54,12 +92,22 @@ export declare class SharedMatrix<T = any> extends SharedObject implements IMatr
|
|
|
54
92
|
closeMatrix(consumer: IMatrixConsumer<MatrixItem<T>>): void;
|
|
55
93
|
get rowCount(): number;
|
|
56
94
|
get colCount(): number;
|
|
95
|
+
isSetCellConflictResolutionPolicyFWW(): boolean;
|
|
57
96
|
getCell(row: number, col: number): MatrixItem<T>;
|
|
58
97
|
get matrixProducer(): IMatrixProducer<MatrixItem<T>>;
|
|
59
98
|
setCell(row: number, col: number, value: MatrixItem<T>): void;
|
|
60
99
|
setCells(rowStart: number, colStart: number, colCount: number, values: readonly MatrixItem<T>[]): void;
|
|
61
100
|
private setCellCore;
|
|
62
101
|
private sendSetCellOp;
|
|
102
|
+
/**
|
|
103
|
+
* This makes sure that the code inside the callback is not reentrant. We need to do that because we raise notifications
|
|
104
|
+
* to the consumers telling about these changes and they can try to change the matrix while listening to those notifications
|
|
105
|
+
* which can make the shared matrix to be in bad state. For example, we are raising notification for a setCell changes and
|
|
106
|
+
* a consumer tries to delete that row/col on receiving that notification which can lead to this matrix trying to setCell in
|
|
107
|
+
* a deleted row/col.
|
|
108
|
+
* @param callback - code that needs to protected against reentrancy.
|
|
109
|
+
*/
|
|
110
|
+
private protectAgainstReentrancy;
|
|
63
111
|
private submitVectorMessage;
|
|
64
112
|
private submitColMessage;
|
|
65
113
|
insertCols(colStart: number, count: number): void;
|
|
@@ -67,8 +115,8 @@ export declare class SharedMatrix<T = any> extends SharedObject implements IMatr
|
|
|
67
115
|
private submitRowMessage;
|
|
68
116
|
insertRows(rowStart: number, count: number): void;
|
|
69
117
|
removeRows(rowStart: number, count: number): void;
|
|
70
|
-
|
|
71
|
-
|
|
118
|
+
/***/ _undoRemoveRows(rowStart: number, spec: IJSONSegment): void;
|
|
119
|
+
/***/ _undoRemoveCols(colStart: number, spec: IJSONSegment): void;
|
|
72
120
|
protected summarizeCore(serializer: IFluidSerializer): ISummaryTreeWithStats;
|
|
73
121
|
/**
|
|
74
122
|
* Runs serializer on the GC data for this SharedMatrix.
|
|
@@ -92,11 +140,21 @@ export declare class SharedMatrix<T = any> extends SharedObject implements IMatr
|
|
|
92
140
|
* {@inheritDoc @fluidframework/shared-object-base#SharedObject.loadCore}
|
|
93
141
|
*/
|
|
94
142
|
protected loadCore(storage: IChannelStorageService): Promise<void>;
|
|
143
|
+
/**
|
|
144
|
+
* Tells whether the setCell op should be applied or not based on First Write Win policy. It assumes
|
|
145
|
+
* we are in FWW mode.
|
|
146
|
+
*/
|
|
147
|
+
private shouldSetCellBasedOnFWW;
|
|
95
148
|
protected processCore(rawMessage: ISequencedDocumentMessage, local: boolean, localOpMetadata: unknown): void;
|
|
96
149
|
private readonly onRowDelta;
|
|
97
150
|
private readonly onColDelta;
|
|
98
151
|
private readonly onRowHandlesRecycled;
|
|
99
152
|
private readonly onColHandlesRecycled;
|
|
153
|
+
/**
|
|
154
|
+
* Api to switch Set Op policy from Last Writer Win to First Writer Win. It only switches from LWW to FWW
|
|
155
|
+
* and not from FWW to LWW. The next SetOp which is sent will communicate this policy to other clients.
|
|
156
|
+
*/
|
|
157
|
+
switchSetCellPolicy(): void;
|
|
100
158
|
/**
|
|
101
159
|
* Returns true if the latest pending write to the cell indicated by the given row/col handles
|
|
102
160
|
* matches the given 'localSeq'.
|
package/lib/matrix.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"matrix.d.ts","sourceRoot":"","sources":["../src/matrix.ts"],"names":[],"mappings":"AAAA;;;GAGG;
|
|
1
|
+
{"version":3,"file":"matrix.d.ts","sourceRoot":"","sources":["../src/matrix.ts"],"names":[],"mappings":"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"}
|
package/lib/matrix.mjs
CHANGED
|
@@ -25,19 +25,30 @@ import { MatrixUndoProvider } from "./undoprovider.mjs";
|
|
|
25
25
|
* matrix data and physically stores data in Z-order to leverage CPU caches and
|
|
26
26
|
* prefetching when reading in either row or column major order. (See README.md
|
|
27
27
|
* for more details.)
|
|
28
|
-
*
|
|
29
|
-
* @public
|
|
28
|
+
* @alpha
|
|
30
29
|
*/
|
|
31
30
|
export class SharedMatrix extends SharedObject {
|
|
32
31
|
static getFactory() {
|
|
33
32
|
return new SharedMatrixFactory();
|
|
34
33
|
}
|
|
35
|
-
|
|
34
|
+
/**
|
|
35
|
+
* Constructor for the Shared Matrix
|
|
36
|
+
* @param runtime - DataStore runtime.
|
|
37
|
+
* @param id - id of the dds
|
|
38
|
+
* @param attributes - channel attributes
|
|
39
|
+
* @param _isSetCellConflictResolutionPolicyFWW - Conflict resolution for Matrix set op is First Writer Win in case of
|
|
40
|
+
* race condition. Client can still overwrite values in case of no race.
|
|
41
|
+
*/
|
|
42
|
+
constructor(runtime, id, attributes, _isSetCellConflictResolutionPolicyFWW) {
|
|
36
43
|
super(id, runtime, attributes, "fluid_matrix_");
|
|
37
44
|
this.id = id;
|
|
38
45
|
this.consumers = new Set();
|
|
39
46
|
this.cells = new SparseArray2D(); // Stores cell values.
|
|
40
47
|
this.pending = new SparseArray2D(); // Tracks pending writes.
|
|
48
|
+
this.cellLastWriteTracker = new SparseArray2D(); // Tracks last writes sequence number and clientId in a cell.
|
|
49
|
+
this.userSwitchedSetCellPolicy = false; // Set to true when the user calls switchPolicy.
|
|
50
|
+
// Used to track if there is any reentrancy in setCell code.
|
|
51
|
+
this.reentrantCount = 0;
|
|
41
52
|
// Invoked by PermutationVector to notify IMatrixConsumers of row insertion/deletions.
|
|
42
53
|
this.onRowDelta = (position, removedCount, insertedCount) => {
|
|
43
54
|
for (const consumer of this.consumers) {
|
|
@@ -54,14 +65,18 @@ export class SharedMatrix extends SharedObject {
|
|
|
54
65
|
for (const rowHandle of rowHandles) {
|
|
55
66
|
this.cells.clearRows(/* rowStart: */ rowHandle, /* rowCount: */ 1);
|
|
56
67
|
this.pending.clearRows(/* rowStart: */ rowHandle, /* rowCount: */ 1);
|
|
68
|
+
this.cellLastWriteTracker.clearRows(/* rowStart: */ rowHandle, /* rowCount: */ 1);
|
|
57
69
|
}
|
|
58
70
|
};
|
|
59
71
|
this.onColHandlesRecycled = (colHandles) => {
|
|
60
72
|
for (const colHandle of colHandles) {
|
|
61
73
|
this.cells.clearCols(/* colStart: */ colHandle, /* colCount: */ 1);
|
|
62
74
|
this.pending.clearCols(/* colStart: */ colHandle, /* colCount: */ 1);
|
|
75
|
+
this.cellLastWriteTracker.clearCols(/* colStart: */ colHandle, /* colCount: */ 1);
|
|
63
76
|
}
|
|
64
77
|
};
|
|
78
|
+
this.setCellLwwToFwwPolicySwitchOpSeqNumber =
|
|
79
|
+
_isSetCellConflictResolutionPolicyFWW === true ? 0 : -1;
|
|
65
80
|
this.rows = new PermutationVector("rows" /* SnapshotPath.rows */, this.logger, runtime, this.onRowDelta, this.onRowHandlesRecycled);
|
|
66
81
|
this.cols = new PermutationVector("cols" /* SnapshotPath.cols */, this.logger, runtime, this.onColDelta, this.onColHandlesRecycled);
|
|
67
82
|
}
|
|
@@ -101,6 +116,9 @@ export class SharedMatrix extends SharedObject {
|
|
|
101
116
|
get colCount() {
|
|
102
117
|
return this.cols.getLength();
|
|
103
118
|
}
|
|
119
|
+
isSetCellConflictResolutionPolicyFWW() {
|
|
120
|
+
return this.setCellLwwToFwwPolicySwitchOpSeqNumber > -1 || this.userSwitchedSetCellPolicy;
|
|
121
|
+
}
|
|
104
122
|
getCell(row, col) {
|
|
105
123
|
// Perf: When possible, bounds checking is performed inside the implementation for
|
|
106
124
|
// 'getHandle()' so that it can be elided in the case of a cache hit. This
|
|
@@ -127,10 +145,6 @@ export class SharedMatrix extends SharedObject {
|
|
|
127
145
|
setCell(row, col, value) {
|
|
128
146
|
assert(0 <= row && row < this.rowCount && 0 <= col && col < this.colCount, 0x01a /* "Trying to set out-of-bounds cell!" */);
|
|
129
147
|
this.setCellCore(row, col, value);
|
|
130
|
-
// Avoid reentrancy by raising change notifications after the op is queued.
|
|
131
|
-
for (const consumer of this.consumers.values()) {
|
|
132
|
-
consumer.cellsChanged(row, col, 1, 1, this);
|
|
133
|
-
}
|
|
134
148
|
}
|
|
135
149
|
setCells(rowStart, colStart, colCount, values) {
|
|
136
150
|
const rowCount = Math.ceil(values.length / colCount);
|
|
@@ -151,23 +165,25 @@ export class SharedMatrix extends SharedObject {
|
|
|
151
165
|
r++;
|
|
152
166
|
}
|
|
153
167
|
}
|
|
154
|
-
// Avoid reentrancy by raising change notifications after the op is queued.
|
|
155
|
-
for (const consumer of this.consumers.values()) {
|
|
156
|
-
consumer.cellsChanged(rowStart, colStart, rowCount, colCount, this);
|
|
157
|
-
}
|
|
158
168
|
}
|
|
159
169
|
setCellCore(row, col, value, rowHandle = this.rows.getAllocatedHandle(row), colHandle = this.cols.getAllocatedHandle(col)) {
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
oldValue
|
|
170
|
+
this.protectAgainstReentrancy(() => {
|
|
171
|
+
if (this.undo !== undefined) {
|
|
172
|
+
let oldValue = this.cells.getCell(rowHandle, colHandle);
|
|
173
|
+
if (oldValue === null) {
|
|
174
|
+
oldValue = undefined;
|
|
175
|
+
}
|
|
176
|
+
this.undo.cellSet(rowHandle, colHandle, oldValue);
|
|
164
177
|
}
|
|
165
|
-
this.
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
178
|
+
this.cells.setCell(rowHandle, colHandle, value);
|
|
179
|
+
if (this.isAttached()) {
|
|
180
|
+
this.sendSetCellOp(row, col, value, rowHandle, colHandle);
|
|
181
|
+
}
|
|
182
|
+
// Avoid reentrancy by raising change notifications after the op is queued.
|
|
183
|
+
for (const consumer of this.consumers.values()) {
|
|
184
|
+
consumer.cellsChanged(row, col, 1, 1, this);
|
|
185
|
+
}
|
|
186
|
+
});
|
|
171
187
|
}
|
|
172
188
|
sendSetCellOp(row, col, value, rowHandle, colHandle, localSeq = this.nextLocalSeq(), rowsRefSeq = this.rows.getCollabWindow().currentSeq, colsRefSeq = this.cols.getCollabWindow().currentSeq) {
|
|
173
189
|
assert(this.isAttached(), 0x1e2 /* "Caller must ensure 'isAttached()' before calling 'sendSetCellOp'." */);
|
|
@@ -176,6 +192,7 @@ export class SharedMatrix extends SharedObject {
|
|
|
176
192
|
row,
|
|
177
193
|
col,
|
|
178
194
|
value,
|
|
195
|
+
fwwMode: this.userSwitchedSetCellPolicy || this.setCellLwwToFwwPolicySwitchOpSeqNumber > -1,
|
|
179
196
|
};
|
|
180
197
|
const metadata = {
|
|
181
198
|
rowHandle,
|
|
@@ -183,10 +200,26 @@ export class SharedMatrix extends SharedObject {
|
|
|
183
200
|
localSeq,
|
|
184
201
|
rowsRefSeq,
|
|
185
202
|
colsRefSeq,
|
|
203
|
+
referenceSeqNumber: this.runtime.deltaManager.lastSequenceNumber,
|
|
186
204
|
};
|
|
187
205
|
this.submitLocalMessage(op, metadata);
|
|
188
206
|
this.pending.setCell(rowHandle, colHandle, localSeq);
|
|
189
207
|
}
|
|
208
|
+
/**
|
|
209
|
+
* This makes sure that the code inside the callback is not reentrant. We need to do that because we raise notifications
|
|
210
|
+
* to the consumers telling about these changes and they can try to change the matrix while listening to those notifications
|
|
211
|
+
* which can make the shared matrix to be in bad state. For example, we are raising notification for a setCell changes and
|
|
212
|
+
* a consumer tries to delete that row/col on receiving that notification which can lead to this matrix trying to setCell in
|
|
213
|
+
* a deleted row/col.
|
|
214
|
+
* @param callback - code that needs to protected against reentrancy.
|
|
215
|
+
*/
|
|
216
|
+
protectAgainstReentrancy(callback) {
|
|
217
|
+
assert(this.reentrantCount === 0, 0x85d /* reentrant code */);
|
|
218
|
+
this.reentrantCount++;
|
|
219
|
+
callback();
|
|
220
|
+
this.reentrantCount--;
|
|
221
|
+
assert(this.reentrantCount === 0, 0x85e /* reentrant code on exit */);
|
|
222
|
+
}
|
|
190
223
|
submitVectorMessage(currentVector, oppositeVector, dimension, message) {
|
|
191
224
|
// Ideally, we would have a single 'localSeq' counter that is shared between both PermutationVectors
|
|
192
225
|
// and the SharedMatrix's cell data. Instead, we externally advance each MergeTree's 'localSeq' counter
|
|
@@ -209,21 +242,21 @@ export class SharedMatrix extends SharedObject {
|
|
|
209
242
|
this.submitVectorMessage(this.cols, this.rows, "cols" /* SnapshotPath.cols */, message);
|
|
210
243
|
}
|
|
211
244
|
insertCols(colStart, count) {
|
|
212
|
-
this.submitColMessage(this.cols.insert(colStart, count));
|
|
245
|
+
this.protectAgainstReentrancy(() => this.submitColMessage(this.cols.insert(colStart, count)));
|
|
213
246
|
}
|
|
214
247
|
removeCols(colStart, count) {
|
|
215
|
-
this.submitColMessage(this.cols.remove(colStart, count));
|
|
248
|
+
this.protectAgainstReentrancy(() => this.submitColMessage(this.cols.remove(colStart, count)));
|
|
216
249
|
}
|
|
217
250
|
submitRowMessage(message) {
|
|
218
251
|
this.submitVectorMessage(this.rows, this.cols, "rows" /* SnapshotPath.rows */, message);
|
|
219
252
|
}
|
|
220
253
|
insertRows(rowStart, count) {
|
|
221
|
-
this.submitRowMessage(this.rows.insert(rowStart, count));
|
|
254
|
+
this.protectAgainstReentrancy(() => this.submitRowMessage(this.rows.insert(rowStart, count)));
|
|
222
255
|
}
|
|
223
256
|
removeRows(rowStart, count) {
|
|
224
|
-
this.submitRowMessage(this.rows.remove(rowStart, count));
|
|
257
|
+
this.protectAgainstReentrancy(() => this.submitRowMessage(this.rows.remove(rowStart, count)));
|
|
225
258
|
}
|
|
226
|
-
|
|
259
|
+
/***/ _undoRemoveRows(rowStart, spec) {
|
|
227
260
|
const { op, inserted } = reinsertSegmentIntoVector(this.rows, rowStart, spec);
|
|
228
261
|
this.submitRowMessage(op);
|
|
229
262
|
// Generate setCell ops for each populated cell in the reinserted rows.
|
|
@@ -243,7 +276,7 @@ export class SharedMatrix extends SharedObject {
|
|
|
243
276
|
consumer.cellsChanged(rowStart, /* colStart: */ 0, rowCount, this.colCount, this);
|
|
244
277
|
}
|
|
245
278
|
}
|
|
246
|
-
|
|
279
|
+
/***/ _undoRemoveCols(colStart, spec) {
|
|
247
280
|
const { op, inserted } = reinsertSegmentIntoVector(this.cols, colStart, spec);
|
|
248
281
|
this.submitColMessage(op);
|
|
249
282
|
// Generate setCell ops for each populated cell in the reinserted cols.
|
|
@@ -267,7 +300,16 @@ export class SharedMatrix extends SharedObject {
|
|
|
267
300
|
const builder = new SummaryTreeBuilder();
|
|
268
301
|
builder.addWithStats("rows" /* SnapshotPath.rows */, this.rows.summarize(this.runtime, this.handle, serializer));
|
|
269
302
|
builder.addWithStats("cols" /* SnapshotPath.cols */, this.cols.summarize(this.runtime, this.handle, serializer));
|
|
270
|
-
|
|
303
|
+
const artifactsToSummarize = [
|
|
304
|
+
this.cells.snapshot(),
|
|
305
|
+
this.pending.snapshot(),
|
|
306
|
+
this.setCellLwwToFwwPolicySwitchOpSeqNumber,
|
|
307
|
+
];
|
|
308
|
+
// Only need to store it in the snapshot if we have switched the policy already.
|
|
309
|
+
if (this.setCellLwwToFwwPolicySwitchOpSeqNumber > -1) {
|
|
310
|
+
artifactsToSummarize.push(this.cellLastWriteTracker.snapshot());
|
|
311
|
+
}
|
|
312
|
+
builder.addBlob("cells" /* SnapshotPath.cells */, serializer.stringify(artifactsToSummarize, this.handle));
|
|
271
313
|
return builder.getSummaryTree();
|
|
272
314
|
}
|
|
273
315
|
/**
|
|
@@ -317,7 +359,9 @@ export class SharedMatrix extends SharedObject {
|
|
|
317
359
|
this.rows.startOrUpdateCollaboration(this.runtime.clientId);
|
|
318
360
|
this.cols.startOrUpdateCollaboration(this.runtime.clientId);
|
|
319
361
|
}
|
|
320
|
-
rebasePosition(
|
|
362
|
+
rebasePosition(
|
|
363
|
+
// eslint-disable-next-line import/no-deprecated
|
|
364
|
+
client, pos, referenceSequenceNumber, localSeq) {
|
|
321
365
|
const { clientId } = client.getCollabWindow();
|
|
322
366
|
const { segment, offset } = client.getContainingSegment(pos, { referenceSequenceNumber, clientId: client.getLongClientId(clientId) }, localSeq);
|
|
323
367
|
if (segment === undefined || offset === undefined) {
|
|
@@ -336,16 +380,27 @@ export class SharedMatrix extends SharedObject {
|
|
|
336
380
|
default: {
|
|
337
381
|
assert(content.type === MatrixOp.set, 0x020 /* "Unknown SharedMatrix 'op' type." */);
|
|
338
382
|
const setOp = content;
|
|
339
|
-
const { rowHandle, colHandle, localSeq, rowsRefSeq, colsRefSeq } = localOpMetadata;
|
|
340
|
-
// If
|
|
341
|
-
//
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
const
|
|
346
|
-
|
|
383
|
+
const { rowHandle, colHandle, localSeq, rowsRefSeq, colsRefSeq, referenceSeqNumber, } = localOpMetadata;
|
|
384
|
+
// If after rebasing the op, we get a valid row/col number, that means the row/col
|
|
385
|
+
// handles have not been recycled and we can safely use them.
|
|
386
|
+
const row = this.rebasePosition(this.rows, setOp.row, rowsRefSeq, localSeq);
|
|
387
|
+
const col = this.rebasePosition(this.cols, setOp.col, colsRefSeq, localSeq);
|
|
388
|
+
if (row !== undefined && col !== undefined && row >= 0 && col >= 0) {
|
|
389
|
+
const lastCellModificationDetails = this.cellLastWriteTracker.getCell(rowHandle, colHandle);
|
|
390
|
+
// If the mode is LWW, then send the op.
|
|
391
|
+
// Otherwise if the current mode is FWW and if we generated this op, after seeing the
|
|
392
|
+
// last set op, or it is the first set op for the cell, then regenerate the op,
|
|
393
|
+
// otherwise raise conflict. We want to check the current mode here and not that
|
|
394
|
+
// whether op was made in FWW or not.
|
|
395
|
+
if (this.setCellLwwToFwwPolicySwitchOpSeqNumber === -1 ||
|
|
396
|
+
lastCellModificationDetails === undefined ||
|
|
397
|
+
referenceSeqNumber >= lastCellModificationDetails.seqNum) {
|
|
347
398
|
this.sendSetCellOp(row, col, setOp.value, rowHandle, colHandle, localSeq, rowsRefSeq, colsRefSeq);
|
|
348
399
|
}
|
|
400
|
+
else if (this.pending.getCell(rowHandle, colHandle) !== undefined) {
|
|
401
|
+
// Clear the pending changes if any as we are not sending the op.
|
|
402
|
+
this.pending.setCell(rowHandle, colHandle, undefined);
|
|
403
|
+
}
|
|
349
404
|
}
|
|
350
405
|
break;
|
|
351
406
|
}
|
|
@@ -359,14 +414,32 @@ export class SharedMatrix extends SharedObject {
|
|
|
359
414
|
try {
|
|
360
415
|
await this.rows.load(this.runtime, new ObjectStoragePartition(storage, "rows" /* SnapshotPath.rows */), this.serializer);
|
|
361
416
|
await this.cols.load(this.runtime, new ObjectStoragePartition(storage, "cols" /* SnapshotPath.cols */), this.serializer);
|
|
362
|
-
const [cellData,
|
|
417
|
+
const [cellData, _pendingCliSeqData, setCellLwwToFwwPolicySwitchOpSeqNumber, cellLastWriteTracker,] = await deserializeBlob(storage, "cells" /* SnapshotPath.cells */, this.serializer);
|
|
363
418
|
this.cells = SparseArray2D.load(cellData);
|
|
364
|
-
this.
|
|
419
|
+
this.setCellLwwToFwwPolicySwitchOpSeqNumber =
|
|
420
|
+
setCellLwwToFwwPolicySwitchOpSeqNumber ?? -1;
|
|
421
|
+
if (cellLastWriteTracker !== undefined) {
|
|
422
|
+
this.cellLastWriteTracker = SparseArray2D.load(cellLastWriteTracker);
|
|
423
|
+
}
|
|
365
424
|
}
|
|
366
425
|
catch (error) {
|
|
367
426
|
this.logger.sendErrorEvent({ eventName: "MatrixLoadFailed" }, error);
|
|
368
427
|
}
|
|
369
428
|
}
|
|
429
|
+
/**
|
|
430
|
+
* Tells whether the setCell op should be applied or not based on First Write Win policy. It assumes
|
|
431
|
+
* we are in FWW mode.
|
|
432
|
+
*/
|
|
433
|
+
shouldSetCellBasedOnFWW(rowHandle, colHandle, message) {
|
|
434
|
+
assert(this.setCellLwwToFwwPolicySwitchOpSeqNumber > -1, 0x85f /* should be in Fww mode when calling this method */);
|
|
435
|
+
assert(message.clientId !== null, 0x860 /* clientId should not be null */);
|
|
436
|
+
const lastCellModificationDetails = this.cellLastWriteTracker.getCell(rowHandle, colHandle);
|
|
437
|
+
// If someone tried to Overwrite the cell value or first write on this cell or
|
|
438
|
+
// same client tried to modify the cell.
|
|
439
|
+
return (lastCellModificationDetails === undefined ||
|
|
440
|
+
lastCellModificationDetails.clientId === message.clientId ||
|
|
441
|
+
message.referenceSequenceNumber >= lastCellModificationDetails.seqNum);
|
|
442
|
+
}
|
|
370
443
|
processCore(rawMessage, local, localOpMetadata) {
|
|
371
444
|
const msg = parseHandles(rawMessage, this.serializer);
|
|
372
445
|
const contents = msg.contents;
|
|
@@ -379,13 +452,28 @@ export class SharedMatrix extends SharedObject {
|
|
|
379
452
|
break;
|
|
380
453
|
default: {
|
|
381
454
|
assert(contents.type === MatrixOp.set, 0x021 /* "SharedMatrix message contents have unexpected type!" */);
|
|
382
|
-
const { row, col } = contents;
|
|
455
|
+
const { row, col, value, fwwMode } = contents;
|
|
456
|
+
const isPreviousSetCellPolicyModeFWW = this.setCellLwwToFwwPolicySwitchOpSeqNumber > -1;
|
|
457
|
+
// If this is the first op notifying us of the policy change, then set the policy change seq number.
|
|
458
|
+
if (this.setCellLwwToFwwPolicySwitchOpSeqNumber === -1 && fwwMode === true) {
|
|
459
|
+
this.setCellLwwToFwwPolicySwitchOpSeqNumber = rawMessage.sequenceNumber;
|
|
460
|
+
}
|
|
461
|
+
assert(rawMessage.clientId !== null, 0x861 /* clientId should not be null!! */);
|
|
383
462
|
if (local) {
|
|
384
463
|
// We are receiving the ACK for a local pending set operation.
|
|
385
464
|
const { rowHandle, colHandle, localSeq } = localOpMetadata;
|
|
386
|
-
|
|
387
|
-
//
|
|
388
|
-
|
|
465
|
+
const isLatestPendingOp = this.isLatestPendingWrite(rowHandle, colHandle, localSeq);
|
|
466
|
+
// If policy is switched and cell should be modified too based on policy, then update the tracker.
|
|
467
|
+
// If policy is not switched, then also update the tracker in case it is the latest.
|
|
468
|
+
if ((this.setCellLwwToFwwPolicySwitchOpSeqNumber > -1 &&
|
|
469
|
+
this.shouldSetCellBasedOnFWW(rowHandle, colHandle, rawMessage)) ||
|
|
470
|
+
(this.setCellLwwToFwwPolicySwitchOpSeqNumber === -1 && isLatestPendingOp)) {
|
|
471
|
+
this.cellLastWriteTracker.setCell(rowHandle, colHandle, {
|
|
472
|
+
seqNum: rawMessage.sequenceNumber,
|
|
473
|
+
clientId: rawMessage.clientId,
|
|
474
|
+
});
|
|
475
|
+
}
|
|
476
|
+
if (isLatestPendingOp) {
|
|
389
477
|
this.pending.setCell(rowHandle, colHandle, undefined);
|
|
390
478
|
}
|
|
391
479
|
}
|
|
@@ -397,11 +485,39 @@ export class SharedMatrix extends SharedObject {
|
|
|
397
485
|
const rowHandle = this.rows.getAllocatedHandle(adjustedRow);
|
|
398
486
|
const colHandle = this.cols.getAllocatedHandle(adjustedCol);
|
|
399
487
|
assert(isHandleValid(rowHandle) && isHandleValid(colHandle), 0x022 /* "SharedMatrix row and/or col handles are invalid!" */);
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
488
|
+
if (this.setCellLwwToFwwPolicySwitchOpSeqNumber > -1) {
|
|
489
|
+
// If someone tried to Overwrite the cell value or first write on this cell or
|
|
490
|
+
// same client tried to modify the cell or if the previous mode was LWW, then we need to still
|
|
491
|
+
// overwrite the cell and raise conflict if we have pending changes as our change is going to be lost.
|
|
492
|
+
if (!isPreviousSetCellPolicyModeFWW ||
|
|
493
|
+
this.shouldSetCellBasedOnFWW(rowHandle, colHandle, rawMessage)) {
|
|
494
|
+
const previousValue = this.cells.getCell(rowHandle, colHandle);
|
|
495
|
+
this.cells.setCell(rowHandle, colHandle, value);
|
|
496
|
+
this.cellLastWriteTracker.setCell(rowHandle, colHandle, {
|
|
497
|
+
seqNum: rawMessage.sequenceNumber,
|
|
498
|
+
clientId: rawMessage.clientId,
|
|
499
|
+
});
|
|
500
|
+
for (const consumer of this.consumers.values()) {
|
|
501
|
+
consumer.cellsChanged(adjustedRow, adjustedCol, 1, 1, this);
|
|
502
|
+
}
|
|
503
|
+
// Check is there are any pending changes, which will be rejected. If so raise conflict.
|
|
504
|
+
if (this.pending.getCell(rowHandle, colHandle) !== undefined) {
|
|
505
|
+
// Don't reset the pending value yet, as there maybe more fww op from same client, so we want
|
|
506
|
+
// to raise conflict event for that op also.
|
|
507
|
+
this.emit("conflict", row, col, value, // Current value
|
|
508
|
+
previousValue, // Ignored local value
|
|
509
|
+
this);
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
else if (this.pending.getCell(rowHandle, colHandle) === undefined) {
|
|
514
|
+
// If there is a pending (unACKed) local write to the same cell, skip the current op
|
|
515
|
+
// since it "happened before" the pending write.
|
|
404
516
|
this.cells.setCell(rowHandle, colHandle, value);
|
|
517
|
+
this.cellLastWriteTracker.setCell(rowHandle, colHandle, {
|
|
518
|
+
seqNum: rawMessage.sequenceNumber,
|
|
519
|
+
clientId: rawMessage.clientId,
|
|
520
|
+
});
|
|
405
521
|
for (const consumer of this.consumers.values()) {
|
|
406
522
|
consumer.cellsChanged(adjustedRow, adjustedCol, 1, 1, this);
|
|
407
523
|
}
|
|
@@ -412,6 +528,20 @@ export class SharedMatrix extends SharedObject {
|
|
|
412
528
|
}
|
|
413
529
|
}
|
|
414
530
|
}
|
|
531
|
+
/**
|
|
532
|
+
* Api to switch Set Op policy from Last Writer Win to First Writer Win. It only switches from LWW to FWW
|
|
533
|
+
* and not from FWW to LWW. The next SetOp which is sent will communicate this policy to other clients.
|
|
534
|
+
*/
|
|
535
|
+
switchSetCellPolicy() {
|
|
536
|
+
if (this.setCellLwwToFwwPolicySwitchOpSeqNumber === -1) {
|
|
537
|
+
if (this.isAttached()) {
|
|
538
|
+
this.userSwitchedSetCellPolicy = true;
|
|
539
|
+
}
|
|
540
|
+
else {
|
|
541
|
+
this.setCellLwwToFwwPolicySwitchOpSeqNumber = 0;
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
}
|
|
415
545
|
/**
|
|
416
546
|
* Returns true if the latest pending write to the cell indicated by the given row/col handles
|
|
417
547
|
* matches the given 'localSeq'.
|
|
@@ -484,6 +614,7 @@ export class SharedMatrix extends SharedObject {
|
|
|
484
614
|
localSeq,
|
|
485
615
|
rowsRefSeq,
|
|
486
616
|
colsRefSeq,
|
|
617
|
+
referenceSeqNumber: this.runtime.deltaManager.lastSequenceNumber,
|
|
487
618
|
};
|
|
488
619
|
this.pending.setCell(rowHandle, colHandle, localSeq);
|
|
489
620
|
return metadata;
|