@fluidframework/matrix 2.41.0-338401 → 2.42.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/lib/matrix.d.ts CHANGED
@@ -140,9 +140,7 @@ export declare class SharedMatrix<T = any> extends SharedObject<ISharedMatrixEve
140
140
  private readonly cols;
141
141
  private cells;
142
142
  private readonly pending;
143
- private cellLastWriteTracker;
144
- private setCellLwwToFwwPolicySwitchOpSeqNumber;
145
- private userSwitchedSetCellPolicy;
143
+ private fwwPolicy;
146
144
  private reentrantCount;
147
145
  /**
148
146
  * Constructor for the Shared Matrix
@@ -208,6 +206,7 @@ export declare class SharedMatrix<T = any> extends SharedObject<ISharedMatrixEve
208
206
  protected onConnect(): void;
209
207
  private rebasePosition;
210
208
  protected reSubmitCore(incoming: unknown, localOpMetadata: unknown): void;
209
+ protected rollback(content: unknown, localOpMetadata: unknown): void;
211
210
  protected onDisconnect(): void;
212
211
  /**
213
212
  * {@inheritDoc @fluidframework/shared-object-base#SharedObject.loadCore}
@@ -224,15 +223,6 @@ export declare class SharedMatrix<T = any> extends SharedObject<ISharedMatrixEve
224
223
  private readonly onRowHandlesRecycled;
225
224
  private readonly onColHandlesRecycled;
226
225
  switchSetCellPolicy(): void;
227
- /**
228
- * Returns true if the latest pending write to the cell indicated by the given row/col handles
229
- * matches the given 'localSeq'.
230
- *
231
- * A return value of `true` indicates that there are no later local operations queued that will
232
- * clobber the write op at the given 'localSeq'. This includes later ops that overwrite the cell
233
- * with a different value as well as row/col removals that might recycled the given row/col handles.
234
- */
235
- private isLatestPendingWrite;
236
226
  toString(): string;
237
227
  /**
238
228
  * {@inheritDoc @fluidframework/shared-object-base#SharedObjectCore.applyStashedOp}
@@ -1 +1 @@
1
- {"version":3,"file":"matrix.d.ts","sourceRoot":"","sources":["../src/matrix.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"matrix.d.ts","sourceRoot":"","sources":["../src/matrix.ts"],"names":[],"mappings":"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"}
package/lib/matrix.js CHANGED
@@ -58,8 +58,9 @@ export class SharedMatrix extends SharedObject {
58
58
  this.getMinInFlightRefSeq = () => this.inFlightRefSeqs.get(0);
59
59
  this.cells = new SparseArray2D(); // Stores cell values.
60
60
  this.pending = new SparseArray2D(); // Tracks pending writes.
61
- this.cellLastWriteTracker = new SparseArray2D(); // Tracks last writes sequence number and clientId in a cell.
62
- this.userSwitchedSetCellPolicy = false; // Set to true when the user calls switchPolicy.
61
+ this.fwwPolicy = {
62
+ state: "off",
63
+ };
63
64
  // Used to track if there is any reentrancy in setCell code.
64
65
  this.reentrantCount = 0;
65
66
  // Invoked by PermutationVector to notify IMatrixConsumers of row insertion/deletions.
@@ -78,17 +79,24 @@ export class SharedMatrix extends SharedObject {
78
79
  for (const rowHandle of rowHandles) {
79
80
  this.cells.clearRows(/* rowStart: */ rowHandle, /* rowCount: */ 1);
80
81
  this.pending.clearRows(/* rowStart: */ rowHandle, /* rowCount: */ 1);
81
- this.cellLastWriteTracker.clearRows(/* rowStart: */ rowHandle, /* rowCount: */ 1);
82
+ if (this.fwwPolicy.state === "on") {
83
+ this.fwwPolicy.cellLastWriteTracker?.clearRows(
84
+ /* rowStart: */ rowHandle,
85
+ /* rowCount: */ 1);
86
+ }
82
87
  }
83
88
  };
84
89
  this.onColHandlesRecycled = (colHandles) => {
85
90
  for (const colHandle of colHandles) {
86
91
  this.cells.clearCols(/* colStart: */ colHandle, /* colCount: */ 1);
87
92
  this.pending.clearCols(/* colStart: */ colHandle, /* colCount: */ 1);
88
- this.cellLastWriteTracker.clearCols(/* colStart: */ colHandle, /* colCount: */ 1);
93
+ if (this.fwwPolicy.state === "on") {
94
+ this.fwwPolicy.cellLastWriteTracker?.clearCols(
95
+ /* colStart: */ colHandle,
96
+ /* colCount: */ 1);
97
+ }
89
98
  }
90
99
  };
91
- this.setCellLwwToFwwPolicySwitchOpSeqNumber = -1;
92
100
  this.rows = new PermutationVector(SnapshotPath.rows, this.logger, runtime, this.onRowDelta, this.onRowHandlesRecycled, this.getMinInFlightRefSeq);
93
101
  this.cols = new PermutationVector(SnapshotPath.cols, this.logger, runtime, this.onColDelta, this.onColHandlesRecycled, this.getMinInFlightRefSeq);
94
102
  }
@@ -123,7 +131,7 @@ export class SharedMatrix extends SharedObject {
123
131
  return this.cols.getLength();
124
132
  }
125
133
  isSetCellConflictResolutionPolicyFWW() {
126
- return this.setCellLwwToFwwPolicySwitchOpSeqNumber > -1 || this.userSwitchedSetCellPolicy;
134
+ return this.fwwPolicy.state !== "off";
127
135
  }
128
136
  getCell(row, col) {
129
137
  // Perf: When possible, bounds checking is performed inside the implementation for
@@ -174,18 +182,18 @@ export class SharedMatrix extends SharedObject {
174
182
  }
175
183
  }
176
184
  }
177
- setCellCore(row, col, value, rowHandle = this.rows.getAllocatedHandle(row), colHandle = this.cols.getAllocatedHandle(col)) {
185
+ setCellCore(row, col, value, rowHandle = this.rows.getAllocatedHandle(row), colHandle = this.cols.getAllocatedHandle(col), rollback) {
178
186
  this.protectAgainstReentrancy(() => {
187
+ const oldValue = this.cells.getCell(rowHandle, colHandle) ?? undefined;
179
188
  if (this.undo !== undefined) {
180
- let oldValue = this.cells.getCell(rowHandle, colHandle);
181
- if (oldValue === null) {
182
- oldValue = undefined;
183
- }
184
189
  this.undo.cellSet(rowHandle, colHandle, oldValue);
185
190
  }
186
191
  this.cells.setCell(rowHandle, colHandle, value);
187
- if (this.isAttached()) {
188
- this.sendSetCellOp(row, col, value, rowHandle, colHandle);
192
+ if (this.isAttached() && rollback !== true) {
193
+ const pending = this.sendSetCellOp(row, col, value, rowHandle, colHandle);
194
+ if (pending.local.length === 1) {
195
+ pending.consensus ??= oldValue;
196
+ }
189
197
  }
190
198
  // Avoid reentrancy by raising change notifications after the op is queued.
191
199
  for (const consumer of this.consumers.values()) {
@@ -205,7 +213,7 @@ export class SharedMatrix extends SharedObject {
205
213
  row,
206
214
  col,
207
215
  value,
208
- fwwMode: this.userSwitchedSetCellPolicy || this.setCellLwwToFwwPolicySwitchOpSeqNumber > -1,
216
+ fwwMode: this.fwwPolicy.state !== "off",
209
217
  };
210
218
  const rowsRef = this.createOpMetadataLocalRef(this.rows, row, localSeq);
211
219
  const colsRef = this.createOpMetadataLocalRef(this.cols, col, localSeq);
@@ -218,7 +226,12 @@ export class SharedMatrix extends SharedObject {
218
226
  referenceSeqNumber: this.deltaManager.lastSequenceNumber,
219
227
  };
220
228
  this.submitLocalMessage(op, metadata);
221
- this.pending.setCell(rowHandle, colHandle, localSeq);
229
+ const pendingCell = this.pending.getCell(rowHandle, colHandle) ?? {
230
+ local: [],
231
+ };
232
+ pendingCell.local.push({ localSeq, value });
233
+ this.pending.setCell(rowHandle, colHandle, pendingCell);
234
+ return pendingCell;
222
235
  }
223
236
  /**
224
237
  * This makes sure that the code inside the callback is not reentrant. We need to do that because we raise notifications
@@ -358,12 +371,21 @@ export class SharedMatrix extends SharedObject {
358
371
  builder.addWithStats(SnapshotPath.cols, this.cols.summarize(this.runtime, this.handle, serializer));
359
372
  const artifactsToSummarize = [
360
373
  this.cells.snapshot(),
361
- this.pending.snapshot(),
362
- this.setCellLwwToFwwPolicySwitchOpSeqNumber,
374
+ /**
375
+ * we used to write this.pending.snapshot(). this should have never been done, as pending is only for local
376
+ * changes, and there should never be local changes in the summarizer. This was also never used on load
377
+ * as there is no way to understand a previous clients pending changes. so we just set this to a constant
378
+ * which matches an empty this.pending.snapshot() for back-compat in terms of the array length
379
+ */
380
+ [undefined],
363
381
  ];
364
382
  // Only need to store it in the snapshot if we have switched the policy already.
365
- if (this.setCellLwwToFwwPolicySwitchOpSeqNumber > -1) {
366
- artifactsToSummarize.push(this.cellLastWriteTracker.snapshot());
383
+ if (this.fwwPolicy.state === "on") {
384
+ artifactsToSummarize.push(this.fwwPolicy.switchOpSeqNumber, this.fwwPolicy.cellLastWriteTracker.snapshot());
385
+ }
386
+ else {
387
+ // back-compat: used -1 for disabled
388
+ artifactsToSummarize.push(-1);
367
389
  }
368
390
  builder.addBlob(SnapshotPath.cells, serializer.stringify(artifactsToSummarize, this.handle));
369
391
  return builder.getSummaryTree();
@@ -438,32 +460,36 @@ export class SharedMatrix extends SharedObject {
438
460
  const col = this.rebasePosition(this.cols, colsRef, localSeq);
439
461
  this.rows.removeLocalReferencePosition(rowsRef);
440
462
  this.cols.removeLocalReferencePosition(colsRef);
441
- if (row !== undefined && col !== undefined && row >= 0 && col >= 0) {
442
- const lastCellModificationDetails = this.cellLastWriteTracker.getCell(rowHandle, colHandle);
443
- // If the mode is LWW, then send the op.
463
+ const pendingCell = this.pending.getCell(rowHandle, colHandle);
464
+ assert(pendingCell !== undefined, 0xba4 /* local operation must have a pending array */);
465
+ const { local } = pendingCell;
466
+ assert(local !== undefined, 0xba5 /* local operation must have a pending array */);
467
+ const localSeqIndex = local.findIndex((p) => p.localSeq === localSeq);
468
+ assert(localSeqIndex >= 0, 0xba6 /* local operation must have a pending entry */);
469
+ const [change] = local.splice(localSeqIndex, 1);
470
+ assert(change.localSeq === localSeq, 0xba7 /* must match */);
471
+ if (row !== undefined &&
472
+ col !== undefined &&
473
+ row >= 0 &&
474
+ col >= 0 && // If the mode is LWW, then send the op.
444
475
  // Otherwise if the current mode is FWW and if we generated this op, after seeing the
445
476
  // last set op, or it is the first set op for the cell, then regenerate the op,
446
477
  // otherwise raise conflict. We want to check the current mode here and not that
447
478
  // whether op was made in FWW or not.
448
- if (this.setCellLwwToFwwPolicySwitchOpSeqNumber === -1 ||
449
- lastCellModificationDetails === undefined ||
450
- referenceSeqNumber >= lastCellModificationDetails.seqNum) {
451
- this.sendSetCellOp(row, col, setOp.value, rowHandle, colHandle, localSeq);
452
- }
453
- else if (this.pending.getCell(rowHandle, colHandle) !== undefined) {
454
- // Clear the pending changes if any as we are not sending the op.
455
- this.pending.setCell(rowHandle, colHandle, undefined);
456
- }
479
+ (this.fwwPolicy.state !== "on" ||
480
+ referenceSeqNumber >=
481
+ (this.fwwPolicy.cellLastWriteTracker.getCell(rowHandle, colHandle)?.seqNum ?? 0))) {
482
+ this.sendSetCellOp(row, col, setOp.value, rowHandle, colHandle, localSeq);
457
483
  }
458
484
  }
459
485
  else {
460
486
  switch (content.target) {
461
487
  case SnapshotPath.cols: {
462
- this.submitColMessage(this.cols.regeneratePendingOp(content, localOpMetadata));
488
+ this.submitColMessage(this.cols.regeneratePendingOp(content, localOpMetadata, false));
463
489
  break;
464
490
  }
465
491
  case SnapshotPath.rows: {
466
- this.submitRowMessage(this.rows.regeneratePendingOp(content, localOpMetadata));
492
+ this.submitRowMessage(this.rows.regeneratePendingOp(content, localOpMetadata, false));
467
493
  break;
468
494
  }
469
495
  default: {
@@ -472,6 +498,33 @@ export class SharedMatrix extends SharedObject {
472
498
  }
473
499
  }
474
500
  }
501
+ rollback(content, localOpMetadata) {
502
+ const contents = content;
503
+ const target = contents.target;
504
+ switch (target) {
505
+ case SnapshotPath.cols: {
506
+ this.cols.rollback(content, localOpMetadata);
507
+ break;
508
+ }
509
+ case SnapshotPath.rows: {
510
+ this.rows.rollback(content, localOpMetadata);
511
+ break;
512
+ }
513
+ case undefined: {
514
+ assert(contents.type === MatrixOp.set, 0xba8 /* only sets supported */);
515
+ const setMetadata = localOpMetadata;
516
+ const pendingCell = this.pending.getCell(setMetadata.rowHandle, setMetadata.colHandle);
517
+ assert(pendingCell !== undefined, 0xba9 /* must have pending */);
518
+ const change = pendingCell.local.pop();
519
+ assert(change?.localSeq === setMetadata.localSeq, 0xbaa /* must have change */);
520
+ const previous = pendingCell.local.length > 0
521
+ ? pendingCell.local[pendingCell.local.length - 1].value
522
+ : pendingCell.consensus;
523
+ this.setCellCore(contents.row, contents.col, previous, setMetadata.rowHandle, setMetadata.colHandle, true);
524
+ }
525
+ default:
526
+ }
527
+ }
475
528
  onDisconnect() { }
476
529
  /**
477
530
  * {@inheritDoc @fluidframework/shared-object-base#SharedObject.loadCore}
@@ -484,11 +537,20 @@ export class SharedMatrix extends SharedObject {
484
537
  // Cast is needed since the (de)serializer returns content of type `any`.
485
538
  ] = (await deserializeBlob(storage, SnapshotPath.cells, this.serializer));
486
539
  this.cells = SparseArray2D.load(cellData);
487
- this.setCellLwwToFwwPolicySwitchOpSeqNumber =
488
- setCellLwwToFwwPolicySwitchOpSeqNumber ?? -1;
489
- if (cellLastWriteTracker !== undefined) {
490
- this.cellLastWriteTracker = SparseArray2D.load(cellLastWriteTracker);
491
- }
540
+ // back-compat: used -1 for disabled, also may not exist
541
+ const switchOpSeqNumber = setCellLwwToFwwPolicySwitchOpSeqNumber === -1
542
+ ? undefined
543
+ : (setCellLwwToFwwPolicySwitchOpSeqNumber ?? undefined);
544
+ this.fwwPolicy =
545
+ switchOpSeqNumber === undefined
546
+ ? {
547
+ state: "off",
548
+ }
549
+ : {
550
+ state: "on",
551
+ switchOpSeqNumber,
552
+ cellLastWriteTracker: SparseArray2D.load(cellLastWriteTracker),
553
+ };
492
554
  }
493
555
  catch (error) {
494
556
  this.logger.sendErrorEvent({ eventName: "MatrixLoadFailed" }, error);
@@ -499,9 +561,9 @@ export class SharedMatrix extends SharedObject {
499
561
  * we are in FWW mode.
500
562
  */
501
563
  shouldSetCellBasedOnFWW(rowHandle, colHandle, message) {
502
- assert(this.setCellLwwToFwwPolicySwitchOpSeqNumber > -1, 0x85f /* should be in Fww mode when calling this method */);
564
+ assert(this.fwwPolicy.state === "on", 0x85f /* should be in Fww mode when calling this method */);
503
565
  assert(message.clientId !== null, 0x860 /* clientId should not be null */);
504
- const lastCellModificationDetails = this.cellLastWriteTracker.getCell(rowHandle, colHandle);
566
+ const lastCellModificationDetails = this.fwwPolicy.cellLastWriteTracker.getCell(rowHandle, colHandle);
505
567
  // If someone tried to Overwrite the cell value or first write on this cell or
506
568
  // same client tried to modify the cell.
507
569
  return (lastCellModificationDetails === undefined ||
@@ -534,78 +596,88 @@ export class SharedMatrix extends SharedObject {
534
596
  case undefined: {
535
597
  assert(contents.type === MatrixOp.set, 0x021 /* "SharedMatrix message contents have unexpected type!" */);
536
598
  const { row, col, value, fwwMode } = contents;
537
- const isPreviousSetCellPolicyModeFWW = this.setCellLwwToFwwPolicySwitchOpSeqNumber > -1;
538
599
  // If this is the first op notifying us of the policy change, then set the policy change seq number.
539
- if (this.setCellLwwToFwwPolicySwitchOpSeqNumber === -1 && fwwMode === true) {
540
- this.setCellLwwToFwwPolicySwitchOpSeqNumber = msg.sequenceNumber;
600
+ if (fwwMode === true && this.fwwPolicy.state !== "on") {
601
+ this.fwwPolicy = {
602
+ state: "on",
603
+ switchOpSeqNumber: msg.sequenceNumber,
604
+ cellLastWriteTracker: new SparseArray2D(),
605
+ };
541
606
  }
542
607
  assert(msg.clientId !== null, 0x861 /* clientId should not be null!! */);
543
608
  if (local) {
544
609
  // We are receiving the ACK for a local pending set operation.
545
610
  const { rowHandle, colHandle, localSeq, rowsRef, colsRef } = localOpMetadata;
546
- const isLatestPendingOp = this.isLatestPendingWrite(rowHandle, colHandle, localSeq);
547
611
  this.rows.removeLocalReferencePosition(rowsRef);
548
612
  this.cols.removeLocalReferencePosition(colsRef);
613
+ const pendingCell = this.pending.getCell(rowHandle, colHandle);
614
+ const ackedChange = pendingCell?.local.shift();
615
+ assert(ackedChange?.localSeq === localSeq, 0xbab /* must match */);
616
+ if (pendingCell?.local.length === 0) {
617
+ this.pending.setCell(rowHandle, colHandle, undefined);
618
+ }
549
619
  // If policy is switched and cell should be modified too based on policy, then update the tracker.
550
620
  // If policy is not switched, then also update the tracker in case it is the latest.
551
- if ((this.setCellLwwToFwwPolicySwitchOpSeqNumber > -1 &&
552
- this.shouldSetCellBasedOnFWW(rowHandle, colHandle, msg)) ||
553
- (this.setCellLwwToFwwPolicySwitchOpSeqNumber === -1 && isLatestPendingOp)) {
554
- this.cellLastWriteTracker.setCell(rowHandle, colHandle, {
621
+ if (this.fwwPolicy.state === "on" &&
622
+ this.shouldSetCellBasedOnFWW(rowHandle, colHandle, msg)) {
623
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
624
+ pendingCell.consensus = ackedChange.value;
625
+ this.fwwPolicy.cellLastWriteTracker.setCell(rowHandle, colHandle, {
555
626
  seqNum: msg.sequenceNumber,
556
627
  clientId: msg.clientId,
557
628
  });
558
629
  }
559
- if (isLatestPendingOp) {
560
- this.pending.setCell(rowHandle, colHandle, undefined);
561
- }
562
630
  }
563
631
  else {
564
632
  const adjustedRow = this.rows.adjustPosition(row, msg);
565
- if (adjustedRow !== undefined) {
566
- const adjustedCol = this.cols.adjustPosition(col, msg);
567
- if (adjustedCol !== undefined) {
568
- const rowHandle = this.rows.getAllocatedHandle(adjustedRow);
569
- const colHandle = this.cols.getAllocatedHandle(adjustedCol);
570
- assert(isHandleValid(rowHandle) && isHandleValid(colHandle), 0x022 /* "SharedMatrix row and/or col handles are invalid!" */);
571
- if (this.setCellLwwToFwwPolicySwitchOpSeqNumber > -1) {
572
- // If someone tried to Overwrite the cell value or first write on this cell or
573
- // same client tried to modify the cell or if the previous mode was LWW, then we need to still
574
- // overwrite the cell and raise conflict if we have pending changes as our change is going to be lost.
575
- if (!isPreviousSetCellPolicyModeFWW ||
576
- this.shouldSetCellBasedOnFWW(rowHandle, colHandle, msg)) {
577
- const previousValue = this.cells.getCell(rowHandle, colHandle);
578
- this.cells.setCell(rowHandle, colHandle, value);
579
- this.cellLastWriteTracker.setCell(rowHandle, colHandle, {
580
- seqNum: msg.sequenceNumber,
581
- clientId: msg.clientId,
582
- });
583
- for (const consumer of this.consumers.values()) {
584
- consumer.cellsChanged(adjustedRow, adjustedCol, 1, 1, this);
585
- }
586
- // Check is there are any pending changes, which will be rejected. If so raise conflict.
587
- if (this.pending.getCell(rowHandle, colHandle) !== undefined) {
588
- // Don't reset the pending value yet, as there maybe more fww op from same client, so we want
589
- // to raise conflict event for that op also.
590
- this.emit("conflict", row, col, value, // Current value
591
- previousValue, // Ignored local value
592
- this);
593
- }
633
+ const adjustedCol = this.cols.adjustPosition(col, msg);
634
+ const rowHandle = adjustedRow.handle;
635
+ const colHandle = adjustedCol.handle;
636
+ assert(isHandleValid(rowHandle) && isHandleValid(colHandle), 0x022 /* "SharedMatrix row and/or col handles are invalid!" */);
637
+ const pendingCell = this.pending.getCell(rowHandle, colHandle);
638
+ if (this.fwwPolicy.state === "on") {
639
+ // If someone tried to Overwrite the cell value or first write on this cell or
640
+ // same client tried to modify the cell or if the previous mode was LWW, then we need to still
641
+ // overwrite the cell and raise conflict if we have pending changes as our change is going to be lost.
642
+ if (this.shouldSetCellBasedOnFWW(rowHandle, colHandle, msg)) {
643
+ const previousValue = this.cells.getCell(rowHandle, colHandle);
644
+ this.cells.setCell(rowHandle, colHandle, value);
645
+ this.fwwPolicy.cellLastWriteTracker.setCell(rowHandle, colHandle, {
646
+ seqNum: msg.sequenceNumber,
647
+ clientId: msg.clientId,
648
+ });
649
+ if (pendingCell !== undefined) {
650
+ pendingCell.consensus = value;
651
+ }
652
+ if (adjustedRow.pos !== undefined && adjustedCol.pos !== undefined) {
653
+ for (const consumer of this.consumers.values()) {
654
+ consumer.cellsChanged(adjustedRow.pos, adjustedCol.pos, 1, 1, this);
655
+ }
656
+ // Check is there are any pending changes, which will be rejected. If so raise conflict.
657
+ if (pendingCell !== undefined && pendingCell.local.length > 0) {
658
+ // Don't reset the pending value yet, as there maybe more fww op from same client, so we want
659
+ // to raise conflict event for that op also.
660
+ this.emit("conflict", row, col, value, // Current value
661
+ previousValue, // Ignored local value
662
+ this);
594
663
  }
595
664
  }
596
- else if (this.pending.getCell(rowHandle, colHandle) === undefined) {
597
- // If there is a pending (unACKed) local write to the same cell, skip the current op
598
- // since it "happened before" the pending write.
599
- this.cells.setCell(rowHandle, colHandle, value);
600
- this.cellLastWriteTracker.setCell(rowHandle, colHandle, {
601
- seqNum: msg.sequenceNumber,
602
- clientId: msg.clientId,
603
- });
665
+ }
666
+ }
667
+ else {
668
+ if (pendingCell === undefined || pendingCell.local.length === 0) {
669
+ // If there is a pending (unACKed) local write to the same cell, skip the current op
670
+ // since it "happened before" the pending write.
671
+ this.cells.setCell(rowHandle, colHandle, value);
672
+ if (adjustedRow.pos !== undefined && adjustedCol.pos !== undefined) {
604
673
  for (const consumer of this.consumers.values()) {
605
- consumer.cellsChanged(adjustedRow, adjustedCol, 1, 1, this);
674
+ consumer.cellsChanged(adjustedRow.pos, adjustedCol.pos, 1, 1, this);
606
675
  }
607
676
  }
608
677
  }
678
+ else {
679
+ pendingCell.consensus = value;
680
+ }
609
681
  }
610
682
  }
611
683
  break;
@@ -616,34 +688,16 @@ export class SharedMatrix extends SharedObject {
616
688
  }
617
689
  }
618
690
  switchSetCellPolicy() {
619
- if (this.setCellLwwToFwwPolicySwitchOpSeqNumber === -1) {
620
- if (this.isAttached()) {
621
- this.userSwitchedSetCellPolicy = true;
622
- }
623
- else {
624
- this.setCellLwwToFwwPolicySwitchOpSeqNumber = 0;
625
- }
691
+ if (this.fwwPolicy.state === "off") {
692
+ this.fwwPolicy = this.isAttached()
693
+ ? { state: "local" }
694
+ : {
695
+ state: "on",
696
+ switchOpSeqNumber: 0,
697
+ cellLastWriteTracker: new SparseArray2D(),
698
+ };
626
699
  }
627
700
  }
628
- /**
629
- * Returns true if the latest pending write to the cell indicated by the given row/col handles
630
- * matches the given 'localSeq'.
631
- *
632
- * A return value of `true` indicates that there are no later local operations queued that will
633
- * clobber the write op at the given 'localSeq'. This includes later ops that overwrite the cell
634
- * with a different value as well as row/col removals that might recycled the given row/col handles.
635
- */
636
- isLatestPendingWrite(rowHandle, colHandle, localSeq) {
637
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
638
- const pendingLocalSeq = this.pending.getCell(rowHandle, colHandle);
639
- // Note while we're awaiting the ACK for a local set, it's possible for the row/col to be
640
- // locally removed and the row/col handles recycled. If this happens, the pendingLocalSeq will
641
- // be 'undefined' or > 'localSeq'.
642
- assert(!(pendingLocalSeq < localSeq), 0x023 /* "The 'localSeq' of pending write (if any) must be <= the localSeq of the currently processed op." */);
643
- // If this is the most recent write to the cell by the local client, the stored localSeq
644
- // will be an exact match for the given 'localSeq'.
645
- return pendingLocalSeq === localSeq;
646
- }
647
701
  toString() {
648
702
  let s = `client:${this.runtime.clientId}\nrows: ${this.rows.toString()}\ncols: ${this.cols.toString()}\n\n`;
649
703
  for (let r = 0; r < this.rowCount; r++) {