@fluidframework/map 2.0.0-internal.4.3.0 → 2.0.0-internal.4.4.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.
@@ -5,5 +5,5 @@
5
5
  * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY
6
6
  */
7
7
  export declare const pkgName = "@fluidframework/map";
8
- export declare const pkgVersion = "2.0.0-internal.4.3.0";
8
+ export declare const pkgVersion = "2.0.0-internal.4.4.0";
9
9
  //# sourceMappingURL=packageVersion.d.ts.map
@@ -8,5 +8,5 @@
8
8
  Object.defineProperty(exports, "__esModule", { value: true });
9
9
  exports.pkgVersion = exports.pkgName = void 0;
10
10
  exports.pkgName = "@fluidframework/map";
11
- exports.pkgVersion = "2.0.0-internal.4.3.0";
11
+ exports.pkgVersion = "2.0.0-internal.4.4.0";
12
12
  //# sourceMappingURL=packageVersion.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"packageVersion.js","sourceRoot":"","sources":["../src/packageVersion.ts"],"names":[],"mappings":";AAAA;;;;;GAKG;;;AAEU,QAAA,OAAO,GAAG,qBAAqB,CAAC;AAChC,QAAA,UAAU,GAAG,sBAAsB,CAAC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n *\n * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY\n */\n\nexport const pkgName = \"@fluidframework/map\";\nexport const pkgVersion = \"2.0.0-internal.4.3.0\";\n"]}
1
+ {"version":3,"file":"packageVersion.js","sourceRoot":"","sources":["../src/packageVersion.ts"],"names":[],"mappings":";AAAA;;;;;GAKG;;;AAEU,QAAA,OAAO,GAAG,qBAAqB,CAAC;AAChC,QAAA,UAAU,GAAG,sBAAsB,CAAC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n *\n * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY\n */\n\nexport const pkgName = \"@fluidframework/map\";\nexport const pkgVersion = \"2.0.0-internal.4.4.0\";\n"]}
package/lib/directory.js CHANGED
@@ -637,16 +637,12 @@ function isClearLocalOpMetadata(metadata) {
637
637
  }
638
638
  function isSubDirLocalOpMetadata(metadata) {
639
639
  return (metadata !== undefined &&
640
- typeof metadata.pendingMessageId === "number" &&
641
640
  (metadata.type === "createSubDir" || metadata.type === "deleteSubDir"));
642
641
  }
643
642
  function isDirectoryLocalOpMetadata(metadata) {
644
- return (metadata !== undefined &&
645
- typeof metadata.pendingMessageId === "number" &&
646
- (metadata.type === "edit" ||
647
- metadata.type === "deleteSubDir" ||
648
- (metadata.type === "clear" && typeof metadata.previousStorage === "object") ||
649
- metadata.type === "createSubDir"));
643
+ return (isKeyEditLocalOpMetadata(metadata) ||
644
+ isClearLocalOpMetadata(metadata) ||
645
+ isSubDirLocalOpMetadata(metadata));
650
646
  }
651
647
  /* eslint-enable @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-member-access */
652
648
  function assertNonNullClientId(clientId) {
@@ -691,18 +687,24 @@ class SubDirectory extends TypedEventEmitter {
691
687
  */
692
688
  this._subdirectories = new Map();
693
689
  /**
694
- * Keys that have been modified locally but not yet ack'd from the server.
690
+ * Keys that have been modified locally but not yet ack'd from the server. This is for operations on keys like
691
+ * set/delete operations on keys. The value of this map is list of pendingMessageIds at which that key
692
+ * was modified. We don't store the type of ops, and behaviour of key ops are different from behaviour of sub
693
+ * directory ops, so we have separate map from subDirectories tracker.
695
694
  */
696
695
  this.pendingKeys = new Map();
697
696
  /**
698
- * Subdirectories that have been created/deleted locally but not yet ack'd from the server.
697
+ * Subdirectories that have been deleted locally but not yet ack'd from the server. This maintains the record
698
+ * of delete op that are pending or yet to be acked from server. This is maintained just to track the locally
699
+ * deleted sub directory.
699
700
  */
700
- this.pendingSubDirectories = new Map();
701
+ this.pendingDeleteSubDirectoriesTracker = new Map();
701
702
  /**
702
- * Subdirectories that have been deleted locally but not yet ack'd from the server. This maintains the count
703
- * of delete op that are pending or yet to be acked from server.
703
+ * Subdirectories that have been created locally but not yet ack'd from the server. This maintains the record
704
+ * of create op that are pending or yet to be acked from server. This is maintained just to track the locally
705
+ * created sub directory.
704
706
  */
705
- this.pendingDeleteSubDirectoriesCount = new Map();
707
+ this.pendingCreateSubDirectoriesTracker = new Map();
706
708
  /**
707
709
  * This is used to assign a unique id to every outgoing operation and helps in tracking unack'd ops.
708
710
  */
@@ -869,8 +871,7 @@ class SubDirectory extends TypedEventEmitter {
869
871
  * @returns - true if there is pending delete.
870
872
  */
871
873
  isSubDirectoryDeletePending(subDirName) {
872
- const pendingDeleteSubDirectory = this.pendingDeleteSubDirectoriesCount.get(subDirName);
873
- if (pendingDeleteSubDirectory !== undefined && pendingDeleteSubDirectory > 0) {
874
+ if (this.pendingDeleteSubDirectoriesTracker.has(subDirName)) {
874
875
  return true;
875
876
  }
876
877
  return false;
@@ -1110,7 +1111,8 @@ class SubDirectory extends TypedEventEmitter {
1110
1111
  */
1111
1112
  processCreateSubDirectoryMessage(msg, op, local, localOpMetadata) {
1112
1113
  this.throwIfDisposed();
1113
- if (!this.needProcessSubDirectoryOperation(msg, op, local, localOpMetadata)) {
1114
+ if (!(this.isMessageForCurrentInstanceOfSubDirectory(msg) &&
1115
+ this.needProcessSubDirectoryOperation(msg, op, local, localOpMetadata))) {
1114
1116
  return;
1115
1117
  }
1116
1118
  assertNonNullClientId(msg.clientId);
@@ -1126,10 +1128,9 @@ class SubDirectory extends TypedEventEmitter {
1126
1128
  this.throwIfDisposed();
1127
1129
  // Create the sub directory locally first.
1128
1130
  this.createSubDirectoryCore(op.subdirName, true, -1, (_c = this.runtime.clientId) !== null && _c !== void 0 ? _c : "detached");
1129
- const newMessageId = this.getSubDirMessageId(op);
1131
+ this.updatePendingSubDirMessageCount(op);
1130
1132
  const localOpMetadata = {
1131
1133
  type: "createSubDir",
1132
- pendingMessageId: newMessageId,
1133
1134
  };
1134
1135
  return localOpMetadata;
1135
1136
  }
@@ -1158,10 +1159,9 @@ class SubDirectory extends TypedEventEmitter {
1158
1159
  applyStashedDeleteSubDirMessage(op) {
1159
1160
  this.throwIfDisposed();
1160
1161
  const subDir = this.deleteSubDirectoryCore(op.subdirName, true);
1161
- const newMessageId = this.getSubDirMessageId(op);
1162
+ this.updatePendingSubDirMessageCount(op);
1162
1163
  const metadata = {
1163
1164
  type: "deleteSubDir",
1164
- pendingMessageId: newMessageId,
1165
1165
  subDirectory: subDir,
1166
1166
  };
1167
1167
  return metadata;
@@ -1190,8 +1190,11 @@ class SubDirectory extends TypedEventEmitter {
1190
1190
  assert(isClearLocalOpMetadata(localOpMetadata), 0x32b /* Invalid localOpMetadata for clear */);
1191
1191
  // We don't reuse the metadata pendingMessageId but send a new one on each submit.
1192
1192
  const pendingClearMessageId = this.pendingClearMessageIds.shift();
1193
- assert(pendingClearMessageId === localOpMetadata.pendingMessageId, 0x32c /* pendingMessageId does not match */);
1194
- this.submitClearMessage(op, localOpMetadata.previousStorage);
1193
+ // Only submit the op, if we have record for it, otherwise it is possible that the older instance
1194
+ // is already deleted, in which case we don't need to submit the op.
1195
+ if (pendingClearMessageId === localOpMetadata.pendingMessageId) {
1196
+ this.submitClearMessage(op, localOpMetadata.previousStorage);
1197
+ }
1195
1198
  }
1196
1199
  /**
1197
1200
  * Get a new pending message id for the op and cache it to track the pending op
@@ -1229,33 +1232,41 @@ class SubDirectory extends TypedEventEmitter {
1229
1232
  assert(isKeyEditLocalOpMetadata(localOpMetadata), 0x32d /* Invalid localOpMetadata in submit */);
1230
1233
  // clear the old pending message id
1231
1234
  const pendingMessageIds = this.pendingKeys.get(op.key);
1232
- assert(pendingMessageIds !== undefined &&
1233
- pendingMessageIds[0] === localOpMetadata.pendingMessageId, 0x32e /* Unexpected pending message received */);
1234
- pendingMessageIds.shift();
1235
- if (pendingMessageIds.length === 0) {
1236
- this.pendingKeys.delete(op.key);
1235
+ // Only submit the op, if we have record for it, otherwise it is possible that the older instance
1236
+ // is already deleted, in which case we don't need to submit the op.
1237
+ if (pendingMessageIds !== undefined &&
1238
+ pendingMessageIds[0] === localOpMetadata.pendingMessageId) {
1239
+ pendingMessageIds.shift();
1240
+ if (pendingMessageIds.length === 0) {
1241
+ this.pendingKeys.delete(op.key);
1242
+ }
1243
+ this.submitKeyMessage(op, localOpMetadata.previousValue);
1237
1244
  }
1238
- this.submitKeyMessage(op, localOpMetadata.previousValue);
1239
1245
  }
1240
- /**
1241
- * Get a new pending message id for the op and cache it to track the pending op
1242
- */
1243
- getSubDirMessageId(op) {
1246
+ incrementPendingSubDirCount(map, subDirName) {
1244
1247
  var _c;
1245
- // We don't reuse the metadata pendingMessageId but send a new one on each submit.
1246
- const newMessageId = ++this.pendingMessageId;
1247
- const pendingMessageIds = this.pendingSubDirectories.get(op.subdirName);
1248
- if (pendingMessageIds !== undefined) {
1249
- pendingMessageIds.push(newMessageId);
1250
- }
1251
- else {
1252
- this.pendingSubDirectories.set(op.subdirName, [newMessageId]);
1248
+ const count = (_c = map.get(subDirName)) !== null && _c !== void 0 ? _c : 0;
1249
+ map.set(subDirName, count + 1);
1250
+ }
1251
+ decrementPendingSubDirCount(map, subDirName) {
1252
+ var _c;
1253
+ const count = (_c = map.get(subDirName)) !== null && _c !== void 0 ? _c : 0;
1254
+ map.set(subDirName, count - 1);
1255
+ if (count <= 1) {
1256
+ map.delete(subDirName);
1253
1257
  }
1258
+ }
1259
+ /**
1260
+ * Update the count for pending create/delete of the sub directory so that it can be validated on receiving op
1261
+ * or while resubmitting the op.
1262
+ */
1263
+ updatePendingSubDirMessageCount(op) {
1254
1264
  if (op.type === "deleteSubDirectory") {
1255
- const count = (_c = this.pendingDeleteSubDirectoriesCount.get(op.subdirName)) !== null && _c !== void 0 ? _c : 0;
1256
- this.pendingDeleteSubDirectoriesCount.set(op.subdirName, count + 1);
1265
+ this.incrementPendingSubDirCount(this.pendingDeleteSubDirectoriesTracker, op.subdirName);
1266
+ }
1267
+ else if (op.type === "createSubDirectory") {
1268
+ this.incrementPendingSubDirCount(this.pendingCreateSubDirectoriesTracker, op.subdirName);
1257
1269
  }
1258
- return newMessageId;
1259
1270
  }
1260
1271
  /**
1261
1272
  * Submit a create subdirectory operation.
@@ -1263,10 +1274,9 @@ class SubDirectory extends TypedEventEmitter {
1263
1274
  */
1264
1275
  submitCreateSubDirectoryMessage(op) {
1265
1276
  this.throwIfDisposed();
1266
- const newMessageId = this.getSubDirMessageId(op);
1277
+ this.updatePendingSubDirMessageCount(op);
1267
1278
  const localOpMetadata = {
1268
1279
  type: "createSubDir",
1269
- pendingMessageId: newMessageId,
1270
1280
  };
1271
1281
  this.directory.submitDirectoryMessage(op, localOpMetadata);
1272
1282
  }
@@ -1277,10 +1287,9 @@ class SubDirectory extends TypedEventEmitter {
1277
1287
  */
1278
1288
  submitDeleteSubDirectoryMessage(op, subDir) {
1279
1289
  this.throwIfDisposed();
1280
- const newMessageId = this.getSubDirMessageId(op);
1290
+ this.updatePendingSubDirMessageCount(op);
1281
1291
  const localOpMetadata = {
1282
1292
  type: "deleteSubDir",
1283
- pendingMessageId: newMessageId,
1284
1293
  subDirectory: subDir,
1285
1294
  };
1286
1295
  this.directory.submitDirectoryMessage(op, localOpMetadata);
@@ -1293,18 +1302,22 @@ class SubDirectory extends TypedEventEmitter {
1293
1302
  */
1294
1303
  resubmitSubDirectoryMessage(op, localOpMetadata) {
1295
1304
  assert(isSubDirLocalOpMetadata(localOpMetadata), 0x32f /* Invalid localOpMetadata for sub directory op */);
1296
- // clear the old pending message id
1297
- const pendingMessageIds = this.pendingSubDirectories.get(op.subdirName);
1298
- assert(pendingMessageIds !== undefined &&
1299
- pendingMessageIds[0] === localOpMetadata.pendingMessageId, 0x330 /* Unexpected pending message received */);
1300
- pendingMessageIds.shift();
1301
- if (pendingMessageIds.length === 0) {
1302
- this.pendingSubDirectories.delete(op.subdirName);
1305
+ // Only submit the op, if we have record for it, otherwise it is possible that the older instance
1306
+ // is already deleted, in which case we don't need to submit the op.
1307
+ if (localOpMetadata.type === "createSubDir" &&
1308
+ !this.pendingCreateSubDirectoriesTracker.has(op.subdirName)) {
1309
+ return;
1310
+ }
1311
+ else if (localOpMetadata.type === "deleteSubDir" &&
1312
+ !this.pendingDeleteSubDirectoriesTracker.has(op.subdirName)) {
1313
+ return;
1303
1314
  }
1304
1315
  if (localOpMetadata.type === "createSubDir") {
1316
+ this.decrementPendingSubDirCount(this.pendingCreateSubDirectoriesTracker, op.subdirName);
1305
1317
  this.submitCreateSubDirectoryMessage(op);
1306
1318
  }
1307
1319
  else {
1320
+ this.decrementPendingSubDirCount(this.pendingDeleteSubDirectoriesTracker, op.subdirName);
1308
1321
  this.submitDeleteSubDirectoryMessage(op, localOpMetadata.subDirectory);
1309
1322
  }
1310
1323
  }
@@ -1408,7 +1421,7 @@ class SubDirectory extends TypedEventEmitter {
1408
1421
  }
1409
1422
  else if (op.type === "createSubDirectory" && localOpMetadata.type === "createSubDir") {
1410
1423
  this.deleteSubDirectoryCore(op.subdirName, true);
1411
- this.rollbackPendingMessageId(this.pendingSubDirectories, op.subdirName, localOpMetadata.pendingMessageId);
1424
+ this.decrementPendingSubDirCount(this.pendingCreateSubDirectoriesTracker, op.subdirName);
1412
1425
  }
1413
1426
  else if (op.type === "deleteSubDirectory" && localOpMetadata.type === "deleteSubDir") {
1414
1427
  if (localOpMetadata.subDirectory !== undefined) {
@@ -1417,13 +1430,7 @@ class SubDirectory extends TypedEventEmitter {
1417
1430
  this._subdirectories.set(op.subdirName, localOpMetadata.subDirectory);
1418
1431
  this.emit("subDirectoryCreated", op.subdirName, true, this);
1419
1432
  }
1420
- this.rollbackPendingMessageId(this.pendingSubDirectories, op.subdirName, localOpMetadata.pendingMessageId);
1421
- const count = this.pendingDeleteSubDirectoriesCount.get(op.subdirName);
1422
- assert(count !== undefined && count > 0, 0x5ab /* should have record for delete op */);
1423
- this.pendingDeleteSubDirectoriesCount.set(op.subdirName, count - 1);
1424
- if (count === 1) {
1425
- this.pendingDeleteSubDirectoriesCount.delete(op.subdirName);
1426
- }
1433
+ this.decrementPendingSubDirCount(this.pendingDeleteSubDirectoriesTracker, op.subDirName);
1427
1434
  }
1428
1435
  else {
1429
1436
  throw new Error("Unsupported op for rollback");
@@ -1516,51 +1523,63 @@ class SubDirectory extends TypedEventEmitter {
1516
1523
  * @returns True if the operation should be processed, false otherwise
1517
1524
  */
1518
1525
  needProcessSubDirectoryOperation(msg, op, local, localOpMetadata) {
1519
- const pendingSubDirectoryMessageId = this.pendingSubDirectories.get(op.subdirName);
1520
1526
  assertNonNullClientId(msg.clientId);
1521
- if (pendingSubDirectoryMessageId !== undefined) {
1527
+ const pendingDeleteCount = this.pendingDeleteSubDirectoriesTracker.get(op.subdirName);
1528
+ const pendingCreateCount = this.pendingCreateSubDirectoriesTracker.get(op.subdirName);
1529
+ if ((pendingDeleteCount !== undefined && pendingDeleteCount > 0) ||
1530
+ (pendingCreateCount !== undefined && pendingCreateCount > 0)) {
1522
1531
  if (local) {
1523
1532
  assert(isSubDirLocalOpMetadata(localOpMetadata), 0x012 /* pendingMessageId is missing from the local client's operation */);
1524
- const pendingMessageIds = this.pendingSubDirectories.get(op.subdirName);
1525
- assert(pendingMessageIds !== undefined &&
1526
- pendingMessageIds[0] === localOpMetadata.pendingMessageId, 0x332 /* Unexpected pending message received */);
1527
- pendingMessageIds.shift();
1528
- if (pendingMessageIds.length === 0) {
1529
- this.pendingSubDirectories.delete(op.subdirName);
1533
+ if (localOpMetadata.type === "deleteSubDir") {
1534
+ assert(pendingDeleteCount !== undefined && pendingDeleteCount > 0, 0x6c2 /* pendingDeleteCount should exist */);
1535
+ this.decrementPendingSubDirCount(this.pendingDeleteSubDirectoriesTracker, op.subdirName);
1530
1536
  }
1531
- if (op.type === "deleteSubDirectory") {
1532
- const count = this.pendingDeleteSubDirectoriesCount.get(op.subdirName);
1533
- assert(count !== undefined && count > 0, 0x5ac /* should have record for delete op */);
1534
- this.pendingDeleteSubDirectoriesCount.set(op.subdirName, count - 1);
1535
- if (count === 1) {
1536
- this.pendingDeleteSubDirectoriesCount.delete(op.subdirName);
1537
- }
1537
+ else if (localOpMetadata.type === "createSubDir") {
1538
+ assert(pendingCreateCount !== undefined && pendingCreateCount > 0, 0x6c3 /* pendingCreateCount should exist */);
1539
+ this.decrementPendingSubDirCount(this.pendingCreateSubDirectoriesTracker, op.subdirName);
1538
1540
  }
1539
1541
  }
1540
- else if (op.type === "deleteSubDirectory") {
1541
- // If this is remote delete op and we have keys in this subDirectory, then we need to delete these
1542
- // keys except the pending ones as they will be sequenced after this delete.
1543
- const subDirectory = this._subdirectories.get(op.subdirName);
1544
- if (subDirectory) {
1545
- subDirectory.clearExceptPendingKeys(local);
1546
- // In case of remote delete op, we need to reset the creation seq number and client ids of
1542
+ if (op.type === "deleteSubDirectory") {
1543
+ const resetSubDirectoryTree = (directory) => {
1544
+ if (!directory) {
1545
+ return;
1546
+ }
1547
+ // If this is delete op and we have keys in this subDirectory, then we need to delete these
1548
+ // keys except the pending ones as they will be sequenced after this delete.
1549
+ directory.clearExceptPendingKeys(local);
1550
+ // In case of delete op, we need to reset the creation seq number and client ids of
1547
1551
  // creators as the previous directory is getting deleted and we will initialize again when
1548
1552
  // we will receive op for the create again.
1549
- subDirectory.sequenceNumber = -1;
1550
- subDirectory.clientIds.clear();
1551
- }
1553
+ directory.sequenceNumber = -1;
1554
+ directory.clientIds.clear();
1555
+ // Do the same thing for the subtree of the directory. If create is not pending for a child, then just
1556
+ // delete it.
1557
+ const subDirectories = directory.subdirectories();
1558
+ for (const [subDirName, subDir] of subDirectories) {
1559
+ if (directory.pendingCreateSubDirectoriesTracker.has(subDirName)) {
1560
+ resetSubDirectoryTree(subDir);
1561
+ continue;
1562
+ }
1563
+ directory.deleteSubDirectoryCore(subDirName, false);
1564
+ }
1565
+ };
1566
+ const subDirectory = this._subdirectories.get(op.subdirName);
1567
+ resetSubDirectoryTree(subDirectory);
1552
1568
  }
1553
1569
  if (op.type === "createSubDirectory") {
1554
1570
  const dir = this._subdirectories.get(op.subdirName);
1555
- if ((dir === null || dir === void 0 ? void 0 : dir.sequenceNumber) === -1) {
1556
- // Only set the seq on the first message, could be more
1557
- dir.sequenceNumber = msg.sequenceNumber;
1558
- }
1559
- // The client created the dir at or after the dirs seq, so list its client id as a creator.
1560
- if (dir !== undefined &&
1561
- !dir.clientIds.has(msg.clientId) &&
1562
- dir.sequenceNumber <= msg.sequenceNumber) {
1563
- dir.clientIds.add(msg.clientId);
1571
+ // Child sub directory create seq number can't be lower than the parent subdirectory.
1572
+ if (this.sequenceNumber !== -1 && this.sequenceNumber < msg.sequenceNumber) {
1573
+ if ((dir === null || dir === void 0 ? void 0 : dir.sequenceNumber) === -1) {
1574
+ // Only set the seq on the first message, could be more
1575
+ dir.sequenceNumber = msg.sequenceNumber;
1576
+ }
1577
+ // The client created the dir at or after the dirs seq, so list its client id as a creator.
1578
+ if (dir !== undefined &&
1579
+ !dir.clientIds.has(msg.clientId) &&
1580
+ dir.sequenceNumber <= msg.sequenceNumber) {
1581
+ dir.clientIds.add(msg.clientId);
1582
+ }
1564
1583
  }
1565
1584
  }
1566
1585
  return false;