@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.
- package/CHANGELOG.md +4 -0
- package/dist/directory.js +115 -96
- package/dist/directory.js.map +1 -1
- package/dist/packageVersion.d.ts +1 -1
- package/dist/packageVersion.js +1 -1
- package/dist/packageVersion.js.map +1 -1
- package/lib/directory.js +115 -96
- package/lib/directory.js.map +1 -1
- package/lib/packageVersion.d.ts +1 -1
- package/lib/packageVersion.js +1 -1
- package/lib/packageVersion.js.map +1 -1
- package/package.json +12 -12
- package/src/directory.ts +151 -116
- package/src/packageVersion.ts +1 -1
package/CHANGELOG.md
CHANGED
package/dist/directory.js
CHANGED
|
@@ -661,16 +661,12 @@ function isClearLocalOpMetadata(metadata) {
|
|
|
661
661
|
}
|
|
662
662
|
function isSubDirLocalOpMetadata(metadata) {
|
|
663
663
|
return (metadata !== undefined &&
|
|
664
|
-
typeof metadata.pendingMessageId === "number" &&
|
|
665
664
|
(metadata.type === "createSubDir" || metadata.type === "deleteSubDir"));
|
|
666
665
|
}
|
|
667
666
|
function isDirectoryLocalOpMetadata(metadata) {
|
|
668
|
-
return (metadata
|
|
669
|
-
|
|
670
|
-
(metadata
|
|
671
|
-
metadata.type === "deleteSubDir" ||
|
|
672
|
-
(metadata.type === "clear" && typeof metadata.previousStorage === "object") ||
|
|
673
|
-
metadata.type === "createSubDir"));
|
|
667
|
+
return (isKeyEditLocalOpMetadata(metadata) ||
|
|
668
|
+
isClearLocalOpMetadata(metadata) ||
|
|
669
|
+
isSubDirLocalOpMetadata(metadata));
|
|
674
670
|
}
|
|
675
671
|
/* eslint-enable @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-member-access */
|
|
676
672
|
function assertNonNullClientId(clientId) {
|
|
@@ -715,18 +711,24 @@ class SubDirectory extends common_utils_1.TypedEventEmitter {
|
|
|
715
711
|
*/
|
|
716
712
|
this._subdirectories = new Map();
|
|
717
713
|
/**
|
|
718
|
-
* Keys that have been modified locally but not yet ack'd from the server.
|
|
714
|
+
* Keys that have been modified locally but not yet ack'd from the server. This is for operations on keys like
|
|
715
|
+
* set/delete operations on keys. The value of this map is list of pendingMessageIds at which that key
|
|
716
|
+
* was modified. We don't store the type of ops, and behaviour of key ops are different from behaviour of sub
|
|
717
|
+
* directory ops, so we have separate map from subDirectories tracker.
|
|
719
718
|
*/
|
|
720
719
|
this.pendingKeys = new Map();
|
|
721
720
|
/**
|
|
722
|
-
* Subdirectories that have been
|
|
721
|
+
* Subdirectories that have been deleted locally but not yet ack'd from the server. This maintains the record
|
|
722
|
+
* of delete op that are pending or yet to be acked from server. This is maintained just to track the locally
|
|
723
|
+
* deleted sub directory.
|
|
723
724
|
*/
|
|
724
|
-
this.
|
|
725
|
+
this.pendingDeleteSubDirectoriesTracker = new Map();
|
|
725
726
|
/**
|
|
726
|
-
* Subdirectories that have been
|
|
727
|
-
* of
|
|
727
|
+
* Subdirectories that have been created locally but not yet ack'd from the server. This maintains the record
|
|
728
|
+
* of create op that are pending or yet to be acked from server. This is maintained just to track the locally
|
|
729
|
+
* created sub directory.
|
|
728
730
|
*/
|
|
729
|
-
this.
|
|
731
|
+
this.pendingCreateSubDirectoriesTracker = new Map();
|
|
730
732
|
/**
|
|
731
733
|
* This is used to assign a unique id to every outgoing operation and helps in tracking unack'd ops.
|
|
732
734
|
*/
|
|
@@ -893,8 +895,7 @@ class SubDirectory extends common_utils_1.TypedEventEmitter {
|
|
|
893
895
|
* @returns - true if there is pending delete.
|
|
894
896
|
*/
|
|
895
897
|
isSubDirectoryDeletePending(subDirName) {
|
|
896
|
-
|
|
897
|
-
if (pendingDeleteSubDirectory !== undefined && pendingDeleteSubDirectory > 0) {
|
|
898
|
+
if (this.pendingDeleteSubDirectoriesTracker.has(subDirName)) {
|
|
898
899
|
return true;
|
|
899
900
|
}
|
|
900
901
|
return false;
|
|
@@ -1134,7 +1135,8 @@ class SubDirectory extends common_utils_1.TypedEventEmitter {
|
|
|
1134
1135
|
*/
|
|
1135
1136
|
processCreateSubDirectoryMessage(msg, op, local, localOpMetadata) {
|
|
1136
1137
|
this.throwIfDisposed();
|
|
1137
|
-
if (!this.
|
|
1138
|
+
if (!(this.isMessageForCurrentInstanceOfSubDirectory(msg) &&
|
|
1139
|
+
this.needProcessSubDirectoryOperation(msg, op, local, localOpMetadata))) {
|
|
1138
1140
|
return;
|
|
1139
1141
|
}
|
|
1140
1142
|
assertNonNullClientId(msg.clientId);
|
|
@@ -1150,10 +1152,9 @@ class SubDirectory extends common_utils_1.TypedEventEmitter {
|
|
|
1150
1152
|
this.throwIfDisposed();
|
|
1151
1153
|
// Create the sub directory locally first.
|
|
1152
1154
|
this.createSubDirectoryCore(op.subdirName, true, -1, (_c = this.runtime.clientId) !== null && _c !== void 0 ? _c : "detached");
|
|
1153
|
-
|
|
1155
|
+
this.updatePendingSubDirMessageCount(op);
|
|
1154
1156
|
const localOpMetadata = {
|
|
1155
1157
|
type: "createSubDir",
|
|
1156
|
-
pendingMessageId: newMessageId,
|
|
1157
1158
|
};
|
|
1158
1159
|
return localOpMetadata;
|
|
1159
1160
|
}
|
|
@@ -1182,10 +1183,9 @@ class SubDirectory extends common_utils_1.TypedEventEmitter {
|
|
|
1182
1183
|
applyStashedDeleteSubDirMessage(op) {
|
|
1183
1184
|
this.throwIfDisposed();
|
|
1184
1185
|
const subDir = this.deleteSubDirectoryCore(op.subdirName, true);
|
|
1185
|
-
|
|
1186
|
+
this.updatePendingSubDirMessageCount(op);
|
|
1186
1187
|
const metadata = {
|
|
1187
1188
|
type: "deleteSubDir",
|
|
1188
|
-
pendingMessageId: newMessageId,
|
|
1189
1189
|
subDirectory: subDir,
|
|
1190
1190
|
};
|
|
1191
1191
|
return metadata;
|
|
@@ -1214,8 +1214,11 @@ class SubDirectory extends common_utils_1.TypedEventEmitter {
|
|
|
1214
1214
|
(0, common_utils_1.assert)(isClearLocalOpMetadata(localOpMetadata), 0x32b /* Invalid localOpMetadata for clear */);
|
|
1215
1215
|
// We don't reuse the metadata pendingMessageId but send a new one on each submit.
|
|
1216
1216
|
const pendingClearMessageId = this.pendingClearMessageIds.shift();
|
|
1217
|
-
|
|
1218
|
-
|
|
1217
|
+
// Only submit the op, if we have record for it, otherwise it is possible that the older instance
|
|
1218
|
+
// is already deleted, in which case we don't need to submit the op.
|
|
1219
|
+
if (pendingClearMessageId === localOpMetadata.pendingMessageId) {
|
|
1220
|
+
this.submitClearMessage(op, localOpMetadata.previousStorage);
|
|
1221
|
+
}
|
|
1219
1222
|
}
|
|
1220
1223
|
/**
|
|
1221
1224
|
* Get a new pending message id for the op and cache it to track the pending op
|
|
@@ -1253,33 +1256,41 @@ class SubDirectory extends common_utils_1.TypedEventEmitter {
|
|
|
1253
1256
|
(0, common_utils_1.assert)(isKeyEditLocalOpMetadata(localOpMetadata), 0x32d /* Invalid localOpMetadata in submit */);
|
|
1254
1257
|
// clear the old pending message id
|
|
1255
1258
|
const pendingMessageIds = this.pendingKeys.get(op.key);
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
pendingMessageIds
|
|
1259
|
-
|
|
1260
|
-
|
|
1259
|
+
// Only submit the op, if we have record for it, otherwise it is possible that the older instance
|
|
1260
|
+
// is already deleted, in which case we don't need to submit the op.
|
|
1261
|
+
if (pendingMessageIds !== undefined &&
|
|
1262
|
+
pendingMessageIds[0] === localOpMetadata.pendingMessageId) {
|
|
1263
|
+
pendingMessageIds.shift();
|
|
1264
|
+
if (pendingMessageIds.length === 0) {
|
|
1265
|
+
this.pendingKeys.delete(op.key);
|
|
1266
|
+
}
|
|
1267
|
+
this.submitKeyMessage(op, localOpMetadata.previousValue);
|
|
1261
1268
|
}
|
|
1262
|
-
this.submitKeyMessage(op, localOpMetadata.previousValue);
|
|
1263
1269
|
}
|
|
1264
|
-
|
|
1265
|
-
* Get a new pending message id for the op and cache it to track the pending op
|
|
1266
|
-
*/
|
|
1267
|
-
getSubDirMessageId(op) {
|
|
1270
|
+
incrementPendingSubDirCount(map, subDirName) {
|
|
1268
1271
|
var _c;
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1272
|
+
const count = (_c = map.get(subDirName)) !== null && _c !== void 0 ? _c : 0;
|
|
1273
|
+
map.set(subDirName, count + 1);
|
|
1274
|
+
}
|
|
1275
|
+
decrementPendingSubDirCount(map, subDirName) {
|
|
1276
|
+
var _c;
|
|
1277
|
+
const count = (_c = map.get(subDirName)) !== null && _c !== void 0 ? _c : 0;
|
|
1278
|
+
map.set(subDirName, count - 1);
|
|
1279
|
+
if (count <= 1) {
|
|
1280
|
+
map.delete(subDirName);
|
|
1277
1281
|
}
|
|
1282
|
+
}
|
|
1283
|
+
/**
|
|
1284
|
+
* Update the count for pending create/delete of the sub directory so that it can be validated on receiving op
|
|
1285
|
+
* or while resubmitting the op.
|
|
1286
|
+
*/
|
|
1287
|
+
updatePendingSubDirMessageCount(op) {
|
|
1278
1288
|
if (op.type === "deleteSubDirectory") {
|
|
1279
|
-
|
|
1280
|
-
|
|
1289
|
+
this.incrementPendingSubDirCount(this.pendingDeleteSubDirectoriesTracker, op.subdirName);
|
|
1290
|
+
}
|
|
1291
|
+
else if (op.type === "createSubDirectory") {
|
|
1292
|
+
this.incrementPendingSubDirCount(this.pendingCreateSubDirectoriesTracker, op.subdirName);
|
|
1281
1293
|
}
|
|
1282
|
-
return newMessageId;
|
|
1283
1294
|
}
|
|
1284
1295
|
/**
|
|
1285
1296
|
* Submit a create subdirectory operation.
|
|
@@ -1287,10 +1298,9 @@ class SubDirectory extends common_utils_1.TypedEventEmitter {
|
|
|
1287
1298
|
*/
|
|
1288
1299
|
submitCreateSubDirectoryMessage(op) {
|
|
1289
1300
|
this.throwIfDisposed();
|
|
1290
|
-
|
|
1301
|
+
this.updatePendingSubDirMessageCount(op);
|
|
1291
1302
|
const localOpMetadata = {
|
|
1292
1303
|
type: "createSubDir",
|
|
1293
|
-
pendingMessageId: newMessageId,
|
|
1294
1304
|
};
|
|
1295
1305
|
this.directory.submitDirectoryMessage(op, localOpMetadata);
|
|
1296
1306
|
}
|
|
@@ -1301,10 +1311,9 @@ class SubDirectory extends common_utils_1.TypedEventEmitter {
|
|
|
1301
1311
|
*/
|
|
1302
1312
|
submitDeleteSubDirectoryMessage(op, subDir) {
|
|
1303
1313
|
this.throwIfDisposed();
|
|
1304
|
-
|
|
1314
|
+
this.updatePendingSubDirMessageCount(op);
|
|
1305
1315
|
const localOpMetadata = {
|
|
1306
1316
|
type: "deleteSubDir",
|
|
1307
|
-
pendingMessageId: newMessageId,
|
|
1308
1317
|
subDirectory: subDir,
|
|
1309
1318
|
};
|
|
1310
1319
|
this.directory.submitDirectoryMessage(op, localOpMetadata);
|
|
@@ -1317,18 +1326,22 @@ class SubDirectory extends common_utils_1.TypedEventEmitter {
|
|
|
1317
1326
|
*/
|
|
1318
1327
|
resubmitSubDirectoryMessage(op, localOpMetadata) {
|
|
1319
1328
|
(0, common_utils_1.assert)(isSubDirLocalOpMetadata(localOpMetadata), 0x32f /* Invalid localOpMetadata for sub directory op */);
|
|
1320
|
-
//
|
|
1321
|
-
|
|
1322
|
-
(
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1329
|
+
// Only submit the op, if we have record for it, otherwise it is possible that the older instance
|
|
1330
|
+
// is already deleted, in which case we don't need to submit the op.
|
|
1331
|
+
if (localOpMetadata.type === "createSubDir" &&
|
|
1332
|
+
!this.pendingCreateSubDirectoriesTracker.has(op.subdirName)) {
|
|
1333
|
+
return;
|
|
1334
|
+
}
|
|
1335
|
+
else if (localOpMetadata.type === "deleteSubDir" &&
|
|
1336
|
+
!this.pendingDeleteSubDirectoriesTracker.has(op.subdirName)) {
|
|
1337
|
+
return;
|
|
1327
1338
|
}
|
|
1328
1339
|
if (localOpMetadata.type === "createSubDir") {
|
|
1340
|
+
this.decrementPendingSubDirCount(this.pendingCreateSubDirectoriesTracker, op.subdirName);
|
|
1329
1341
|
this.submitCreateSubDirectoryMessage(op);
|
|
1330
1342
|
}
|
|
1331
1343
|
else {
|
|
1344
|
+
this.decrementPendingSubDirCount(this.pendingDeleteSubDirectoriesTracker, op.subdirName);
|
|
1332
1345
|
this.submitDeleteSubDirectoryMessage(op, localOpMetadata.subDirectory);
|
|
1333
1346
|
}
|
|
1334
1347
|
}
|
|
@@ -1432,7 +1445,7 @@ class SubDirectory extends common_utils_1.TypedEventEmitter {
|
|
|
1432
1445
|
}
|
|
1433
1446
|
else if (op.type === "createSubDirectory" && localOpMetadata.type === "createSubDir") {
|
|
1434
1447
|
this.deleteSubDirectoryCore(op.subdirName, true);
|
|
1435
|
-
this.
|
|
1448
|
+
this.decrementPendingSubDirCount(this.pendingCreateSubDirectoriesTracker, op.subdirName);
|
|
1436
1449
|
}
|
|
1437
1450
|
else if (op.type === "deleteSubDirectory" && localOpMetadata.type === "deleteSubDir") {
|
|
1438
1451
|
if (localOpMetadata.subDirectory !== undefined) {
|
|
@@ -1441,13 +1454,7 @@ class SubDirectory extends common_utils_1.TypedEventEmitter {
|
|
|
1441
1454
|
this._subdirectories.set(op.subdirName, localOpMetadata.subDirectory);
|
|
1442
1455
|
this.emit("subDirectoryCreated", op.subdirName, true, this);
|
|
1443
1456
|
}
|
|
1444
|
-
this.
|
|
1445
|
-
const count = this.pendingDeleteSubDirectoriesCount.get(op.subdirName);
|
|
1446
|
-
(0, common_utils_1.assert)(count !== undefined && count > 0, 0x5ab /* should have record for delete op */);
|
|
1447
|
-
this.pendingDeleteSubDirectoriesCount.set(op.subdirName, count - 1);
|
|
1448
|
-
if (count === 1) {
|
|
1449
|
-
this.pendingDeleteSubDirectoriesCount.delete(op.subdirName);
|
|
1450
|
-
}
|
|
1457
|
+
this.decrementPendingSubDirCount(this.pendingDeleteSubDirectoriesTracker, op.subDirName);
|
|
1451
1458
|
}
|
|
1452
1459
|
else {
|
|
1453
1460
|
throw new Error("Unsupported op for rollback");
|
|
@@ -1540,51 +1547,63 @@ class SubDirectory extends common_utils_1.TypedEventEmitter {
|
|
|
1540
1547
|
* @returns True if the operation should be processed, false otherwise
|
|
1541
1548
|
*/
|
|
1542
1549
|
needProcessSubDirectoryOperation(msg, op, local, localOpMetadata) {
|
|
1543
|
-
const pendingSubDirectoryMessageId = this.pendingSubDirectories.get(op.subdirName);
|
|
1544
1550
|
assertNonNullClientId(msg.clientId);
|
|
1545
|
-
|
|
1551
|
+
const pendingDeleteCount = this.pendingDeleteSubDirectoriesTracker.get(op.subdirName);
|
|
1552
|
+
const pendingCreateCount = this.pendingCreateSubDirectoriesTracker.get(op.subdirName);
|
|
1553
|
+
if ((pendingDeleteCount !== undefined && pendingDeleteCount > 0) ||
|
|
1554
|
+
(pendingCreateCount !== undefined && pendingCreateCount > 0)) {
|
|
1546
1555
|
if (local) {
|
|
1547
1556
|
(0, common_utils_1.assert)(isSubDirLocalOpMetadata(localOpMetadata), 0x012 /* pendingMessageId is missing from the local client's operation */);
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
pendingMessageIds.shift();
|
|
1552
|
-
if (pendingMessageIds.length === 0) {
|
|
1553
|
-
this.pendingSubDirectories.delete(op.subdirName);
|
|
1557
|
+
if (localOpMetadata.type === "deleteSubDir") {
|
|
1558
|
+
(0, common_utils_1.assert)(pendingDeleteCount !== undefined && pendingDeleteCount > 0, 0x6c2 /* pendingDeleteCount should exist */);
|
|
1559
|
+
this.decrementPendingSubDirCount(this.pendingDeleteSubDirectoriesTracker, op.subdirName);
|
|
1554
1560
|
}
|
|
1555
|
-
if (
|
|
1556
|
-
|
|
1557
|
-
(
|
|
1558
|
-
this.pendingDeleteSubDirectoriesCount.set(op.subdirName, count - 1);
|
|
1559
|
-
if (count === 1) {
|
|
1560
|
-
this.pendingDeleteSubDirectoriesCount.delete(op.subdirName);
|
|
1561
|
-
}
|
|
1561
|
+
else if (localOpMetadata.type === "createSubDir") {
|
|
1562
|
+
(0, common_utils_1.assert)(pendingCreateCount !== undefined && pendingCreateCount > 0, 0x6c3 /* pendingCreateCount should exist */);
|
|
1563
|
+
this.decrementPendingSubDirCount(this.pendingCreateSubDirectoriesTracker, op.subdirName);
|
|
1562
1564
|
}
|
|
1563
1565
|
}
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
subDirectory
|
|
1570
|
-
//
|
|
1566
|
+
if (op.type === "deleteSubDirectory") {
|
|
1567
|
+
const resetSubDirectoryTree = (directory) => {
|
|
1568
|
+
if (!directory) {
|
|
1569
|
+
return;
|
|
1570
|
+
}
|
|
1571
|
+
// If this is delete op and we have keys in this subDirectory, then we need to delete these
|
|
1572
|
+
// keys except the pending ones as they will be sequenced after this delete.
|
|
1573
|
+
directory.clearExceptPendingKeys(local);
|
|
1574
|
+
// In case of delete op, we need to reset the creation seq number and client ids of
|
|
1571
1575
|
// creators as the previous directory is getting deleted and we will initialize again when
|
|
1572
1576
|
// we will receive op for the create again.
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1577
|
+
directory.sequenceNumber = -1;
|
|
1578
|
+
directory.clientIds.clear();
|
|
1579
|
+
// Do the same thing for the subtree of the directory. If create is not pending for a child, then just
|
|
1580
|
+
// delete it.
|
|
1581
|
+
const subDirectories = directory.subdirectories();
|
|
1582
|
+
for (const [subDirName, subDir] of subDirectories) {
|
|
1583
|
+
if (directory.pendingCreateSubDirectoriesTracker.has(subDirName)) {
|
|
1584
|
+
resetSubDirectoryTree(subDir);
|
|
1585
|
+
continue;
|
|
1586
|
+
}
|
|
1587
|
+
directory.deleteSubDirectoryCore(subDirName, false);
|
|
1588
|
+
}
|
|
1589
|
+
};
|
|
1590
|
+
const subDirectory = this._subdirectories.get(op.subdirName);
|
|
1591
|
+
resetSubDirectoryTree(subDirectory);
|
|
1576
1592
|
}
|
|
1577
1593
|
if (op.type === "createSubDirectory") {
|
|
1578
1594
|
const dir = this._subdirectories.get(op.subdirName);
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
dir.sequenceNumber
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
dir
|
|
1587
|
-
|
|
1595
|
+
// Child sub directory create seq number can't be lower than the parent subdirectory.
|
|
1596
|
+
if (this.sequenceNumber !== -1 && this.sequenceNumber < msg.sequenceNumber) {
|
|
1597
|
+
if ((dir === null || dir === void 0 ? void 0 : dir.sequenceNumber) === -1) {
|
|
1598
|
+
// Only set the seq on the first message, could be more
|
|
1599
|
+
dir.sequenceNumber = msg.sequenceNumber;
|
|
1600
|
+
}
|
|
1601
|
+
// The client created the dir at or after the dirs seq, so list its client id as a creator.
|
|
1602
|
+
if (dir !== undefined &&
|
|
1603
|
+
!dir.clientIds.has(msg.clientId) &&
|
|
1604
|
+
dir.sequenceNumber <= msg.sequenceNumber) {
|
|
1605
|
+
dir.clientIds.add(msg.clientId);
|
|
1606
|
+
}
|
|
1588
1607
|
}
|
|
1589
1608
|
}
|
|
1590
1609
|
return false;
|