@fluidframework/map 2.0.0-dev.4.4.0.162574 → 2.0.0-dev.5.3.2.178189
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 +20 -0
- package/dist/directory.d.ts.map +1 -1
- package/dist/directory.js +120 -97
- package/dist/directory.js.map +1 -1
- package/dist/interfaces.d.ts +2 -1
- package/dist/interfaces.d.ts.map +1 -1
- package/dist/interfaces.js.map +1 -1
- package/dist/mapKernel.d.ts.map +1 -1
- package/dist/mapKernel.js +13 -5
- package/dist/mapKernel.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/dist/tsdoc-metadata.json +11 -0
- package/lib/directory.d.ts.map +1 -1
- package/lib/directory.js +120 -97
- package/lib/directory.js.map +1 -1
- package/lib/interfaces.d.ts +2 -1
- package/lib/interfaces.d.ts.map +1 -1
- package/lib/interfaces.js.map +1 -1
- package/lib/mapKernel.d.ts.map +1 -1
- package/lib/mapKernel.js +13 -5
- package/lib/mapKernel.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 +21 -23
- package/src/directory.ts +155 -117
- package/src/interfaces.ts +2 -6
- package/src/mapKernel.ts +17 -8
- package/src/packageVersion.ts +1 -1
package/src/directory.ts
CHANGED
|
@@ -654,7 +654,10 @@ export class SharedDirectory
|
|
|
654
654
|
if (!newSubDir) {
|
|
655
655
|
const createInfo = subdirObject.ci;
|
|
656
656
|
newSubDir = new SubDirectory(
|
|
657
|
-
|
|
657
|
+
// If csn is -1, then initialize it with 0, otherwise we will never process ops for this
|
|
658
|
+
// sub directory. This could be done at serialization time too, but we need to maintain
|
|
659
|
+
// back compat too and also we will actually know the state when it was serialized.
|
|
660
|
+
createInfo !== undefined && createInfo.csn > -1 ? createInfo.csn : 0,
|
|
658
661
|
createInfo !== undefined
|
|
659
662
|
? new Set<string>(createInfo.ccIds)
|
|
660
663
|
: new Set(),
|
|
@@ -1015,12 +1018,10 @@ interface IClearLocalOpMetadata {
|
|
|
1015
1018
|
|
|
1016
1019
|
interface ICreateSubDirLocalOpMetadata {
|
|
1017
1020
|
type: "createSubDir";
|
|
1018
|
-
pendingMessageId: number;
|
|
1019
1021
|
}
|
|
1020
1022
|
|
|
1021
1023
|
interface IDeleteSubDirLocalOpMetadata {
|
|
1022
1024
|
type: "deleteSubDir";
|
|
1023
|
-
pendingMessageId: number;
|
|
1024
1025
|
subDirectory: SubDirectory | undefined;
|
|
1025
1026
|
}
|
|
1026
1027
|
|
|
@@ -1052,19 +1053,15 @@ function isClearLocalOpMetadata(metadata: any): metadata is IClearLocalOpMetadat
|
|
|
1052
1053
|
function isSubDirLocalOpMetadata(metadata: any): metadata is SubDirLocalOpMetadata {
|
|
1053
1054
|
return (
|
|
1054
1055
|
metadata !== undefined &&
|
|
1055
|
-
typeof metadata.pendingMessageId === "number" &&
|
|
1056
1056
|
(metadata.type === "createSubDir" || metadata.type === "deleteSubDir")
|
|
1057
1057
|
);
|
|
1058
1058
|
}
|
|
1059
1059
|
|
|
1060
1060
|
function isDirectoryLocalOpMetadata(metadata: any): metadata is DirectoryLocalOpMetadata {
|
|
1061
1061
|
return (
|
|
1062
|
-
metadata
|
|
1063
|
-
|
|
1064
|
-
(metadata
|
|
1065
|
-
metadata.type === "deleteSubDir" ||
|
|
1066
|
-
(metadata.type === "clear" && typeof metadata.previousStorage === "object") ||
|
|
1067
|
-
metadata.type === "createSubDir")
|
|
1062
|
+
isKeyEditLocalOpMetadata(metadata) ||
|
|
1063
|
+
isClearLocalOpMetadata(metadata) ||
|
|
1064
|
+
isSubDirLocalOpMetadata(metadata)
|
|
1068
1065
|
);
|
|
1069
1066
|
}
|
|
1070
1067
|
|
|
@@ -1100,20 +1097,26 @@ class SubDirectory extends TypedEventEmitter<IDirectoryEvents> implements IDirec
|
|
|
1100
1097
|
private readonly _subdirectories: Map<string, SubDirectory> = new Map();
|
|
1101
1098
|
|
|
1102
1099
|
/**
|
|
1103
|
-
* Keys that have been modified locally but not yet ack'd from the server.
|
|
1100
|
+
* Keys that have been modified locally but not yet ack'd from the server. This is for operations on keys like
|
|
1101
|
+
* set/delete operations on keys. The value of this map is list of pendingMessageIds at which that key
|
|
1102
|
+
* was modified. We don't store the type of ops, and behaviour of key ops are different from behaviour of sub
|
|
1103
|
+
* directory ops, so we have separate map from subDirectories tracker.
|
|
1104
1104
|
*/
|
|
1105
1105
|
private readonly pendingKeys: Map<string, number[]> = new Map();
|
|
1106
1106
|
|
|
1107
1107
|
/**
|
|
1108
|
-
* Subdirectories that have been
|
|
1108
|
+
* Subdirectories that have been deleted locally but not yet ack'd from the server. This maintains the record
|
|
1109
|
+
* of delete op that are pending or yet to be acked from server. This is maintained just to track the locally
|
|
1110
|
+
* deleted sub directory.
|
|
1109
1111
|
*/
|
|
1110
|
-
private readonly
|
|
1112
|
+
private readonly pendingDeleteSubDirectoriesTracker: Map<string, number> = new Map();
|
|
1111
1113
|
|
|
1112
1114
|
/**
|
|
1113
|
-
* Subdirectories that have been
|
|
1114
|
-
* of
|
|
1115
|
+
* Subdirectories that have been created locally but not yet ack'd from the server. This maintains the record
|
|
1116
|
+
* of create op that are pending or yet to be acked from server. This is maintained just to track the locally
|
|
1117
|
+
* created sub directory.
|
|
1115
1118
|
*/
|
|
1116
|
-
private readonly
|
|
1119
|
+
private readonly pendingCreateSubDirectoriesTracker: Map<string, number> = new Map();
|
|
1117
1120
|
|
|
1118
1121
|
/**
|
|
1119
1122
|
* This is used to assign a unique id to every outgoing operation and helps in tracking unack'd ops.
|
|
@@ -1335,8 +1338,7 @@ class SubDirectory extends TypedEventEmitter<IDirectoryEvents> implements IDirec
|
|
|
1335
1338
|
* @returns - true if there is pending delete.
|
|
1336
1339
|
*/
|
|
1337
1340
|
public isSubDirectoryDeletePending(subDirName: string): boolean {
|
|
1338
|
-
|
|
1339
|
-
if (pendingDeleteSubDirectory !== undefined && pendingDeleteSubDirectory > 0) {
|
|
1341
|
+
if (this.pendingDeleteSubDirectoriesTracker.has(subDirName)) {
|
|
1340
1342
|
return true;
|
|
1341
1343
|
}
|
|
1342
1344
|
return false;
|
|
@@ -1637,7 +1639,12 @@ class SubDirectory extends TypedEventEmitter<IDirectoryEvents> implements IDirec
|
|
|
1637
1639
|
localOpMetadata: unknown,
|
|
1638
1640
|
): void {
|
|
1639
1641
|
this.throwIfDisposed();
|
|
1640
|
-
if (
|
|
1642
|
+
if (
|
|
1643
|
+
!(
|
|
1644
|
+
this.isMessageForCurrentInstanceOfSubDirectory(msg) &&
|
|
1645
|
+
this.needProcessSubDirectoryOperation(msg, op, local, localOpMetadata)
|
|
1646
|
+
)
|
|
1647
|
+
) {
|
|
1641
1648
|
return;
|
|
1642
1649
|
}
|
|
1643
1650
|
assertNonNullClientId(msg.clientId);
|
|
@@ -1655,11 +1662,10 @@ class SubDirectory extends TypedEventEmitter<IDirectoryEvents> implements IDirec
|
|
|
1655
1662
|
this.throwIfDisposed();
|
|
1656
1663
|
// Create the sub directory locally first.
|
|
1657
1664
|
this.createSubDirectoryCore(op.subdirName, true, -1, this.runtime.clientId ?? "detached");
|
|
1658
|
-
|
|
1665
|
+
this.updatePendingSubDirMessageCount(op);
|
|
1659
1666
|
|
|
1660
1667
|
const localOpMetadata: ICreateSubDirLocalOpMetadata = {
|
|
1661
1668
|
type: "createSubDir",
|
|
1662
|
-
pendingMessageId: newMessageId,
|
|
1663
1669
|
};
|
|
1664
1670
|
return localOpMetadata;
|
|
1665
1671
|
}
|
|
@@ -1701,10 +1707,9 @@ class SubDirectory extends TypedEventEmitter<IDirectoryEvents> implements IDirec
|
|
|
1701
1707
|
): IDeleteSubDirLocalOpMetadata {
|
|
1702
1708
|
this.throwIfDisposed();
|
|
1703
1709
|
const subDir = this.deleteSubDirectoryCore(op.subdirName, true);
|
|
1704
|
-
|
|
1710
|
+
this.updatePendingSubDirMessageCount(op);
|
|
1705
1711
|
const metadata: IDeleteSubDirLocalOpMetadata = {
|
|
1706
1712
|
type: "deleteSubDir",
|
|
1707
|
-
pendingMessageId: newMessageId,
|
|
1708
1713
|
subDirectory: subDir,
|
|
1709
1714
|
};
|
|
1710
1715
|
return metadata;
|
|
@@ -1741,11 +1746,11 @@ class SubDirectory extends TypedEventEmitter<IDirectoryEvents> implements IDirec
|
|
|
1741
1746
|
);
|
|
1742
1747
|
// We don't reuse the metadata pendingMessageId but send a new one on each submit.
|
|
1743
1748
|
const pendingClearMessageId = this.pendingClearMessageIds.shift();
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
+
// Only submit the op, if we have record for it, otherwise it is possible that the older instance
|
|
1750
|
+
// is already deleted, in which case we don't need to submit the op.
|
|
1751
|
+
if (pendingClearMessageId === localOpMetadata.pendingMessageId) {
|
|
1752
|
+
this.submitClearMessage(op, localOpMetadata.previousStorage);
|
|
1753
|
+
}
|
|
1749
1754
|
}
|
|
1750
1755
|
|
|
1751
1756
|
/**
|
|
@@ -1789,36 +1794,49 @@ class SubDirectory extends TypedEventEmitter<IDirectoryEvents> implements IDirec
|
|
|
1789
1794
|
|
|
1790
1795
|
// clear the old pending message id
|
|
1791
1796
|
const pendingMessageIds = this.pendingKeys.get(op.key);
|
|
1792
|
-
|
|
1797
|
+
// Only submit the op, if we have record for it, otherwise it is possible that the older instance
|
|
1798
|
+
// is already deleted, in which case we don't need to submit the op.
|
|
1799
|
+
if (
|
|
1793
1800
|
pendingMessageIds !== undefined &&
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1801
|
+
pendingMessageIds[0] === localOpMetadata.pendingMessageId
|
|
1802
|
+
) {
|
|
1803
|
+
pendingMessageIds.shift();
|
|
1804
|
+
if (pendingMessageIds.length === 0) {
|
|
1805
|
+
this.pendingKeys.delete(op.key);
|
|
1806
|
+
}
|
|
1807
|
+
this.submitKeyMessage(op, localOpMetadata.previousValue);
|
|
1800
1808
|
}
|
|
1809
|
+
}
|
|
1801
1810
|
|
|
1802
|
-
|
|
1811
|
+
private incrementPendingSubDirCount(map: Map<string, number>, subDirName: string) {
|
|
1812
|
+
const count = map.get(subDirName) ?? 0;
|
|
1813
|
+
map.set(subDirName, count + 1);
|
|
1814
|
+
}
|
|
1815
|
+
|
|
1816
|
+
private decrementPendingSubDirCount(map: Map<string, number>, subDirName: string) {
|
|
1817
|
+
const count = map.get(subDirName) ?? 0;
|
|
1818
|
+
map.set(subDirName, count - 1);
|
|
1819
|
+
if (count <= 1) {
|
|
1820
|
+
map.delete(subDirName);
|
|
1821
|
+
}
|
|
1803
1822
|
}
|
|
1804
1823
|
|
|
1805
1824
|
/**
|
|
1806
|
-
*
|
|
1825
|
+
* Update the count for pending create/delete of the sub directory so that it can be validated on receiving op
|
|
1826
|
+
* or while resubmitting the op.
|
|
1807
1827
|
*/
|
|
1808
|
-
private
|
|
1809
|
-
// We don't reuse the metadata pendingMessageId but send a new one on each submit.
|
|
1810
|
-
const newMessageId = ++this.pendingMessageId;
|
|
1811
|
-
const pendingMessageIds = this.pendingSubDirectories.get(op.subdirName);
|
|
1812
|
-
if (pendingMessageIds !== undefined) {
|
|
1813
|
-
pendingMessageIds.push(newMessageId);
|
|
1814
|
-
} else {
|
|
1815
|
-
this.pendingSubDirectories.set(op.subdirName, [newMessageId]);
|
|
1816
|
-
}
|
|
1828
|
+
private updatePendingSubDirMessageCount(op: IDirectorySubDirectoryOperation) {
|
|
1817
1829
|
if (op.type === "deleteSubDirectory") {
|
|
1818
|
-
|
|
1819
|
-
|
|
1830
|
+
this.incrementPendingSubDirCount(
|
|
1831
|
+
this.pendingDeleteSubDirectoriesTracker,
|
|
1832
|
+
op.subdirName,
|
|
1833
|
+
);
|
|
1834
|
+
} else if (op.type === "createSubDirectory") {
|
|
1835
|
+
this.incrementPendingSubDirCount(
|
|
1836
|
+
this.pendingCreateSubDirectoriesTracker,
|
|
1837
|
+
op.subdirName,
|
|
1838
|
+
);
|
|
1820
1839
|
}
|
|
1821
|
-
return newMessageId;
|
|
1822
1840
|
}
|
|
1823
1841
|
|
|
1824
1842
|
/**
|
|
@@ -1827,11 +1845,10 @@ class SubDirectory extends TypedEventEmitter<IDirectoryEvents> implements IDirec
|
|
|
1827
1845
|
*/
|
|
1828
1846
|
private submitCreateSubDirectoryMessage(op: IDirectorySubDirectoryOperation): void {
|
|
1829
1847
|
this.throwIfDisposed();
|
|
1830
|
-
|
|
1848
|
+
this.updatePendingSubDirMessageCount(op);
|
|
1831
1849
|
|
|
1832
1850
|
const localOpMetadata: ICreateSubDirLocalOpMetadata = {
|
|
1833
1851
|
type: "createSubDir",
|
|
1834
|
-
pendingMessageId: newMessageId,
|
|
1835
1852
|
};
|
|
1836
1853
|
this.directory.submitDirectoryMessage(op, localOpMetadata);
|
|
1837
1854
|
}
|
|
@@ -1846,11 +1863,10 @@ class SubDirectory extends TypedEventEmitter<IDirectoryEvents> implements IDirec
|
|
|
1846
1863
|
subDir: SubDirectory | undefined,
|
|
1847
1864
|
): void {
|
|
1848
1865
|
this.throwIfDisposed();
|
|
1849
|
-
|
|
1866
|
+
this.updatePendingSubDirMessageCount(op);
|
|
1850
1867
|
|
|
1851
1868
|
const localOpMetadata: IDeleteSubDirLocalOpMetadata = {
|
|
1852
1869
|
type: "deleteSubDir",
|
|
1853
|
-
pendingMessageId: newMessageId,
|
|
1854
1870
|
subDirectory: subDir,
|
|
1855
1871
|
};
|
|
1856
1872
|
this.directory.submitDirectoryMessage(op, localOpMetadata);
|
|
@@ -1871,21 +1887,31 @@ class SubDirectory extends TypedEventEmitter<IDirectoryEvents> implements IDirec
|
|
|
1871
1887
|
0x32f /* Invalid localOpMetadata for sub directory op */,
|
|
1872
1888
|
);
|
|
1873
1889
|
|
|
1874
|
-
//
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
this.
|
|
1890
|
+
// Only submit the op, if we have record for it, otherwise it is possible that the older instance
|
|
1891
|
+
// is already deleted, in which case we don't need to submit the op.
|
|
1892
|
+
if (
|
|
1893
|
+
localOpMetadata.type === "createSubDir" &&
|
|
1894
|
+
!this.pendingCreateSubDirectoriesTracker.has(op.subdirName)
|
|
1895
|
+
) {
|
|
1896
|
+
return;
|
|
1897
|
+
} else if (
|
|
1898
|
+
localOpMetadata.type === "deleteSubDir" &&
|
|
1899
|
+
!this.pendingDeleteSubDirectoriesTracker.has(op.subdirName)
|
|
1900
|
+
) {
|
|
1901
|
+
return;
|
|
1884
1902
|
}
|
|
1885
1903
|
|
|
1886
1904
|
if (localOpMetadata.type === "createSubDir") {
|
|
1905
|
+
this.decrementPendingSubDirCount(
|
|
1906
|
+
this.pendingCreateSubDirectoriesTracker,
|
|
1907
|
+
op.subdirName,
|
|
1908
|
+
);
|
|
1887
1909
|
this.submitCreateSubDirectoryMessage(op);
|
|
1888
1910
|
} else {
|
|
1911
|
+
this.decrementPendingSubDirCount(
|
|
1912
|
+
this.pendingDeleteSubDirectoriesTracker,
|
|
1913
|
+
op.subdirName,
|
|
1914
|
+
);
|
|
1889
1915
|
this.submitDeleteSubDirectoryMessage(op, localOpMetadata.subDirectory);
|
|
1890
1916
|
}
|
|
1891
1917
|
}
|
|
@@ -2010,10 +2036,9 @@ class SubDirectory extends TypedEventEmitter<IDirectoryEvents> implements IDirec
|
|
|
2010
2036
|
} else if (op.type === "createSubDirectory" && localOpMetadata.type === "createSubDir") {
|
|
2011
2037
|
this.deleteSubDirectoryCore(op.subdirName as string, true);
|
|
2012
2038
|
|
|
2013
|
-
this.
|
|
2014
|
-
this.
|
|
2039
|
+
this.decrementPendingSubDirCount(
|
|
2040
|
+
this.pendingCreateSubDirectoriesTracker,
|
|
2015
2041
|
op.subdirName as string,
|
|
2016
|
-
localOpMetadata.pendingMessageId,
|
|
2017
2042
|
);
|
|
2018
2043
|
} else if (op.type === "deleteSubDirectory" && localOpMetadata.type === "deleteSubDir") {
|
|
2019
2044
|
if (localOpMetadata.subDirectory !== undefined) {
|
|
@@ -2023,17 +2048,10 @@ class SubDirectory extends TypedEventEmitter<IDirectoryEvents> implements IDirec
|
|
|
2023
2048
|
this.emit("subDirectoryCreated", op.subdirName, true, this);
|
|
2024
2049
|
}
|
|
2025
2050
|
|
|
2026
|
-
this.
|
|
2027
|
-
this.
|
|
2028
|
-
op.
|
|
2029
|
-
localOpMetadata.pendingMessageId,
|
|
2051
|
+
this.decrementPendingSubDirCount(
|
|
2052
|
+
this.pendingDeleteSubDirectoriesTracker,
|
|
2053
|
+
op.subDirName as string,
|
|
2030
2054
|
);
|
|
2031
|
-
const count = this.pendingDeleteSubDirectoriesCount.get(op.subdirName);
|
|
2032
|
-
assert(count !== undefined && count > 0, 0x5ab /* should have record for delete op */);
|
|
2033
|
-
this.pendingDeleteSubDirectoriesCount.set(op.subdirName, count - 1);
|
|
2034
|
-
if (count === 1) {
|
|
2035
|
-
this.pendingDeleteSubDirectoriesCount.delete(op.subdirName);
|
|
2036
|
-
}
|
|
2037
2055
|
} else {
|
|
2038
2056
|
throw new Error("Unsupported op for rollback");
|
|
2039
2057
|
}
|
|
@@ -2152,61 +2170,81 @@ class SubDirectory extends TypedEventEmitter<IDirectoryEvents> implements IDirec
|
|
|
2152
2170
|
local: boolean,
|
|
2153
2171
|
localOpMetadata: unknown,
|
|
2154
2172
|
): boolean {
|
|
2155
|
-
const pendingSubDirectoryMessageId = this.pendingSubDirectories.get(op.subdirName);
|
|
2156
2173
|
assertNonNullClientId(msg.clientId);
|
|
2157
|
-
|
|
2174
|
+
const pendingDeleteCount = this.pendingDeleteSubDirectoriesTracker.get(op.subdirName);
|
|
2175
|
+
const pendingCreateCount = this.pendingCreateSubDirectoriesTracker.get(op.subdirName);
|
|
2176
|
+
if (
|
|
2177
|
+
(pendingDeleteCount !== undefined && pendingDeleteCount > 0) ||
|
|
2178
|
+
(pendingCreateCount !== undefined && pendingCreateCount > 0)
|
|
2179
|
+
) {
|
|
2158
2180
|
if (local) {
|
|
2159
2181
|
assert(
|
|
2160
2182
|
isSubDirLocalOpMetadata(localOpMetadata),
|
|
2161
2183
|
0x012 /* pendingMessageId is missing from the local client's operation */,
|
|
2162
2184
|
);
|
|
2163
|
-
|
|
2164
|
-
assert(
|
|
2165
|
-
pendingMessageIds !== undefined &&
|
|
2166
|
-
pendingMessageIds[0] === localOpMetadata.pendingMessageId,
|
|
2167
|
-
0x332 /* Unexpected pending message received */,
|
|
2168
|
-
);
|
|
2169
|
-
pendingMessageIds.shift();
|
|
2170
|
-
if (pendingMessageIds.length === 0) {
|
|
2171
|
-
this.pendingSubDirectories.delete(op.subdirName);
|
|
2172
|
-
}
|
|
2173
|
-
if (op.type === "deleteSubDirectory") {
|
|
2174
|
-
const count = this.pendingDeleteSubDirectoriesCount.get(op.subdirName);
|
|
2185
|
+
if (localOpMetadata.type === "deleteSubDir") {
|
|
2175
2186
|
assert(
|
|
2176
|
-
|
|
2177
|
-
|
|
2187
|
+
pendingDeleteCount !== undefined && pendingDeleteCount > 0,
|
|
2188
|
+
0x6c2 /* pendingDeleteCount should exist */,
|
|
2189
|
+
);
|
|
2190
|
+
this.decrementPendingSubDirCount(
|
|
2191
|
+
this.pendingDeleteSubDirectoriesTracker,
|
|
2192
|
+
op.subdirName,
|
|
2193
|
+
);
|
|
2194
|
+
} else if (localOpMetadata.type === "createSubDir") {
|
|
2195
|
+
assert(
|
|
2196
|
+
pendingCreateCount !== undefined && pendingCreateCount > 0,
|
|
2197
|
+
0x6c3 /* pendingCreateCount should exist */,
|
|
2198
|
+
);
|
|
2199
|
+
this.decrementPendingSubDirCount(
|
|
2200
|
+
this.pendingCreateSubDirectoriesTracker,
|
|
2201
|
+
op.subdirName,
|
|
2178
2202
|
);
|
|
2179
|
-
this.pendingDeleteSubDirectoriesCount.set(op.subdirName, count - 1);
|
|
2180
|
-
if (count === 1) {
|
|
2181
|
-
this.pendingDeleteSubDirectoriesCount.delete(op.subdirName);
|
|
2182
|
-
}
|
|
2183
2203
|
}
|
|
2184
|
-
}
|
|
2185
|
-
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
//
|
|
2204
|
+
}
|
|
2205
|
+
if (op.type === "deleteSubDirectory") {
|
|
2206
|
+
const resetSubDirectoryTree = (directory: SubDirectory | undefined): void => {
|
|
2207
|
+
if (!directory) {
|
|
2208
|
+
return;
|
|
2209
|
+
}
|
|
2210
|
+
// If this is delete op and we have keys in this subDirectory, then we need to delete these
|
|
2211
|
+
// keys except the pending ones as they will be sequenced after this delete.
|
|
2212
|
+
directory.clearExceptPendingKeys(local);
|
|
2213
|
+
// In case of delete op, we need to reset the creation seq number and client ids of
|
|
2191
2214
|
// creators as the previous directory is getting deleted and we will initialize again when
|
|
2192
2215
|
// we will receive op for the create again.
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
|
|
2216
|
+
directory.sequenceNumber = -1;
|
|
2217
|
+
directory.clientIds.clear();
|
|
2218
|
+
// Do the same thing for the subtree of the directory. If create is not pending for a child, then just
|
|
2219
|
+
// delete it.
|
|
2220
|
+
const subDirectories = directory.subdirectories();
|
|
2221
|
+
for (const [subDirName, subDir] of subDirectories) {
|
|
2222
|
+
if (directory.pendingCreateSubDirectoriesTracker.has(subDirName)) {
|
|
2223
|
+
resetSubDirectoryTree(subDir as SubDirectory);
|
|
2224
|
+
continue;
|
|
2225
|
+
}
|
|
2226
|
+
directory.deleteSubDirectoryCore(subDirName, false);
|
|
2227
|
+
}
|
|
2228
|
+
};
|
|
2229
|
+
const subDirectory = this._subdirectories.get(op.subdirName);
|
|
2230
|
+
resetSubDirectoryTree(subDirectory);
|
|
2196
2231
|
}
|
|
2197
2232
|
if (op.type === "createSubDirectory") {
|
|
2198
2233
|
const dir = this._subdirectories.get(op.subdirName);
|
|
2199
|
-
|
|
2200
|
-
|
|
2201
|
-
dir
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
dir
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2234
|
+
// Child sub directory create seq number can't be lower than the parent subdirectory.
|
|
2235
|
+
if (this.sequenceNumber !== -1 && this.sequenceNumber < msg.sequenceNumber) {
|
|
2236
|
+
if (dir?.sequenceNumber === -1) {
|
|
2237
|
+
// Only set the seq on the first message, could be more
|
|
2238
|
+
dir.sequenceNumber = msg.sequenceNumber;
|
|
2239
|
+
}
|
|
2240
|
+
// The client created the dir at or after the dirs seq, so list its client id as a creator.
|
|
2241
|
+
if (
|
|
2242
|
+
dir !== undefined &&
|
|
2243
|
+
!dir.clientIds.has(msg.clientId) &&
|
|
2244
|
+
dir.sequenceNumber <= msg.sequenceNumber
|
|
2245
|
+
) {
|
|
2246
|
+
dir.clientIds.add(msg.clientId);
|
|
2247
|
+
}
|
|
2210
2248
|
}
|
|
2211
2249
|
}
|
|
2212
2250
|
return false;
|
package/src/interfaces.ts
CHANGED
|
@@ -4,12 +4,8 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import { ISharedObject, ISharedObjectEvents } from "@fluidframework/shared-object-base";
|
|
7
|
-
import {
|
|
8
|
-
|
|
9
|
-
IEvent,
|
|
10
|
-
IEventProvider,
|
|
11
|
-
IEventThisPlaceHolder,
|
|
12
|
-
} from "@fluidframework/common-definitions";
|
|
7
|
+
import { IEvent, IEventProvider, IEventThisPlaceHolder } from "@fluidframework/common-definitions";
|
|
8
|
+
import { IDisposable } from "@fluidframework/core-interfaces";
|
|
13
9
|
|
|
14
10
|
/**
|
|
15
11
|
* Type of "valueChanged" event parameter.
|
package/src/mapKernel.ts
CHANGED
|
@@ -342,6 +342,9 @@ export class MapKernel {
|
|
|
342
342
|
// Clear the data locally first.
|
|
343
343
|
this.clearCore(true);
|
|
344
344
|
|
|
345
|
+
// Clear the pendingKeys immediately, the local unack'd operations are aborted
|
|
346
|
+
this.pendingKeys.clear();
|
|
347
|
+
|
|
345
348
|
// If we are not attached, don't submit the op.
|
|
346
349
|
if (!this.isAttached()) {
|
|
347
350
|
return;
|
|
@@ -547,8 +550,11 @@ export class MapKernel {
|
|
|
547
550
|
// we will get the value for the pendingKeys and clear the map
|
|
548
551
|
const temp = new Map<string, ILocalValue>();
|
|
549
552
|
for (const key of this.pendingKeys.keys()) {
|
|
550
|
-
//
|
|
551
|
-
|
|
553
|
+
// Verify if the most recent pending operation is a delete op, no need to retain it if so.
|
|
554
|
+
// This ensures the map size remains consistent.
|
|
555
|
+
if (this.data.has(key)) {
|
|
556
|
+
temp.set(key, this.data.get(key) as ILocalValue);
|
|
557
|
+
}
|
|
552
558
|
}
|
|
553
559
|
this.clearCore(false);
|
|
554
560
|
for (const [key, value] of temp.entries()) {
|
|
@@ -771,13 +777,16 @@ export class MapKernel {
|
|
|
771
777
|
0x2fe /* Invalid localOpMetadata in submit */,
|
|
772
778
|
);
|
|
773
779
|
|
|
774
|
-
//
|
|
780
|
+
// no need to submit messages for op's that have been aborted
|
|
775
781
|
const pendingMessageIds = this.pendingKeys.get(op.key);
|
|
776
|
-
|
|
777
|
-
pendingMessageIds
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
782
|
+
if (
|
|
783
|
+
pendingMessageIds === undefined ||
|
|
784
|
+
pendingMessageIds[0] !== localOpMetadata.pendingMessageId
|
|
785
|
+
) {
|
|
786
|
+
return;
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
// clear the old pending message id
|
|
781
790
|
pendingMessageIds.shift();
|
|
782
791
|
if (pendingMessageIds.length === 0) {
|
|
783
792
|
this.pendingKeys.delete(op.key);
|
package/src/packageVersion.ts
CHANGED