@fluid-experimental/tree 0.59.3003 → 0.59.4000
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/dist/Forest.js +1 -1
- package/dist/Forest.js.map +1 -1
- package/dist/id-compressor/IdCompressor.d.ts +19 -45
- package/dist/id-compressor/IdCompressor.d.ts.map +1 -1
- package/dist/id-compressor/IdCompressor.js +151 -151
- package/dist/id-compressor/IdCompressor.js.map +1 -1
- package/dist/id-compressor/SessionIdNormalizer.d.ts +1 -16
- package/dist/id-compressor/SessionIdNormalizer.d.ts.map +1 -1
- package/dist/id-compressor/SessionIdNormalizer.js +23 -21
- package/dist/id-compressor/SessionIdNormalizer.js.map +1 -1
- package/dist/id-compressor/persisted-types/0.0.1.d.ts +23 -4
- package/dist/id-compressor/persisted-types/0.0.1.d.ts.map +1 -1
- package/dist/id-compressor/persisted-types/0.0.1.js.map +1 -1
- package/lib/Forest.js +1 -1
- package/lib/Forest.js.map +1 -1
- package/lib/id-compressor/IdCompressor.d.ts +19 -45
- package/lib/id-compressor/IdCompressor.d.ts.map +1 -1
- package/lib/id-compressor/IdCompressor.js +152 -152
- package/lib/id-compressor/IdCompressor.js.map +1 -1
- package/lib/id-compressor/SessionIdNormalizer.d.ts +1 -16
- package/lib/id-compressor/SessionIdNormalizer.d.ts.map +1 -1
- package/lib/id-compressor/SessionIdNormalizer.js +23 -21
- package/lib/id-compressor/SessionIdNormalizer.js.map +1 -1
- package/lib/id-compressor/persisted-types/0.0.1.d.ts +23 -4
- package/lib/id-compressor/persisted-types/0.0.1.d.ts.map +1 -1
- package/lib/id-compressor/persisted-types/0.0.1.js.map +1 -1
- package/lib/test/IdCompressor.perf.tests.js +47 -67
- package/lib/test/IdCompressor.perf.tests.js.map +1 -1
- package/lib/test/IdCompressor.tests.js +196 -101
- package/lib/test/IdCompressor.tests.js.map +1 -1
- package/lib/test/Summary.tests.d.ts +0 -1
- package/lib/test/Summary.tests.d.ts.map +1 -1
- package/lib/test/Summary.tests.js +0 -3
- package/lib/test/Summary.tests.js.map +1 -1
- package/lib/test/Virtualization.tests.js +0 -4
- package/lib/test/Virtualization.tests.js.map +1 -1
- package/lib/test/utilities/IdCompressorTestUtilities.d.ts +14 -7
- package/lib/test/utilities/IdCompressorTestUtilities.d.ts.map +1 -1
- package/lib/test/utilities/IdCompressorTestUtilities.js +40 -20
- package/lib/test/utilities/IdCompressorTestUtilities.js.map +1 -1
- package/lib/test/utilities/SharedTreeTests.d.ts.map +1 -1
- package/lib/test/utilities/SharedTreeTests.js +0 -1
- package/lib/test/utilities/SharedTreeTests.js.map +1 -1
- package/lib/test/utilities/SummaryLoadPerfTests.js +1 -1
- package/lib/test/utilities/SummaryLoadPerfTests.js.map +1 -1
- package/lib/test/utilities/TestCommon.d.ts +4 -0
- package/lib/test/utilities/TestCommon.d.ts.map +1 -1
- package/lib/test/utilities/TestCommon.js +6 -0
- package/lib/test/utilities/TestCommon.js.map +1 -1
- package/package.json +24 -19
- package/src/Forest.ts +1 -1
- package/src/id-compressor/IdCompressor.ts +171 -198
- package/src/id-compressor/SessionIdNormalizer.ts +29 -41
- package/src/id-compressor/persisted-types/0.0.1.ts +25 -4
|
@@ -15,6 +15,7 @@ const UuidUtilities_1 = require("../UuidUtilities");
|
|
|
15
15
|
const AppendOnlySortedMap_1 = require("./AppendOnlySortedMap");
|
|
16
16
|
const IdRange_1 = require("./IdRange");
|
|
17
17
|
const NumericUuid_1 = require("./NumericUuid");
|
|
18
|
+
const SessionIdNormalizer_1 = require("./SessionIdNormalizer");
|
|
18
19
|
/**
|
|
19
20
|
* Roughly equates to a minimum of 1M sessions before we start allocating 64 bit IDs.
|
|
20
21
|
* This value must *NOT* change without careful consideration to compatibility.
|
|
@@ -144,11 +145,21 @@ class IdCompressor {
|
|
|
144
145
|
*/
|
|
145
146
|
this.localOverrides = new AppendOnlySortedMap_1.AppendOnlySortedMap(Common_1.compareFiniteNumbersReversed);
|
|
146
147
|
/**
|
|
147
|
-
* Maps local IDs to the
|
|
148
|
-
*
|
|
149
|
-
*
|
|
148
|
+
* Maps local IDs to the final ID they are associated with (if any), and maps final IDs to the corresponding local ID (if any).
|
|
149
|
+
* This is used to efficiently compute normalization. This map can be thought of as mapping ranges of "optimistic uncertainty"
|
|
150
|
+
* (local IDs) to the result of consensus (reserved ranges of final IDs, a.k.a. clusters). Any given range of local IDs
|
|
151
|
+
* does not necessarily span an entire cluster, as some session-space IDs may be allocated *after* a cluster has been allocated
|
|
152
|
+
* but before it is full. In this case, there is no uncertainty, as the range of final IDs was reserved when the cluster was created.
|
|
153
|
+
* However, there is always a range of local IDs with size \>= 1 associated with the beginning of every cluster, as clusters are only
|
|
154
|
+
* created *after* they are needed and thus there is some period of uncertainty after local IDs have been handed out but before the
|
|
155
|
+
* range containing them has been finalized. There may also be ranges of local IDs that do not start at the beginning of a
|
|
156
|
+
* cluster; this happens when a cluster is expanded instead of allocating a new one.
|
|
157
|
+
* Additionally, session space IDs associated with an override string will also always be local IDs, because there is uncertainty as
|
|
158
|
+
* to whether another client simultaneously allocated the same override and could get sequenced first (a.k.a. unification) and its
|
|
159
|
+
* final ID would be associated with that override.
|
|
160
|
+
* See `SessionIdNormalizer` for more.
|
|
150
161
|
*/
|
|
151
|
-
this.
|
|
162
|
+
this.sessionIdNormalizer = new SessionIdNormalizer_1.SessionIdNormalizer();
|
|
152
163
|
/**
|
|
153
164
|
* Contains entries for cluster base UUIDs and override strings (both local and final).
|
|
154
165
|
* As a performance optimization, entries for finalized strings also include the containing cluster object.
|
|
@@ -204,12 +215,6 @@ class IdCompressor {
|
|
|
204
215
|
get attributionId() {
|
|
205
216
|
return this.localSession.attributionId;
|
|
206
217
|
}
|
|
207
|
-
/**
|
|
208
|
-
* Helper comparator for searching append-only sorted maps.
|
|
209
|
-
*/
|
|
210
|
-
static overrideComparator(search, element) {
|
|
211
|
-
return (0, Common_1.compareFiniteNumbers)(search, element[0]);
|
|
212
|
-
}
|
|
213
218
|
/**
|
|
214
219
|
* Creates a session object for the supplied ID.
|
|
215
220
|
* Must only be called once per ID.
|
|
@@ -247,77 +252,28 @@ class IdCompressor {
|
|
|
247
252
|
/**
|
|
248
253
|
* Returns an iterable of all IDs created by this compressor.
|
|
249
254
|
*/
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
for (let i = 1; i <= this.localIdCount; i++) {
|
|
253
|
-
yield -i;
|
|
254
|
-
}
|
|
255
|
+
getAllIdsFromLocalSession() {
|
|
256
|
+
return this.sessionIdNormalizer[Symbol.iterator]();
|
|
255
257
|
}
|
|
256
258
|
/**
|
|
257
259
|
* Returns the attribution ID associated with the compressor that created the ID
|
|
258
260
|
*/
|
|
259
261
|
attributeId(id) {
|
|
260
|
-
var _a;
|
|
261
262
|
const opSpaceNormalizedId = this.normalizeToOpSpace(id);
|
|
262
263
|
if (isLocalId(opSpaceNormalizedId)) {
|
|
263
264
|
return this.attributionId;
|
|
264
265
|
}
|
|
265
|
-
const
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
getIdsFromRange(rangeDescriptor, sessionId) {
|
|
273
|
-
var _a, _b, _c;
|
|
274
|
-
const { first, count } = rangeDescriptor;
|
|
275
|
-
if (sessionId === this.localSessionId) {
|
|
276
|
-
return {
|
|
277
|
-
length: count,
|
|
278
|
-
get: (index) => {
|
|
279
|
-
if (index < 0 || index >= count) {
|
|
280
|
-
(0, Common_1.fail)('Index out of bounds of range.');
|
|
281
|
-
}
|
|
282
|
-
return (first - index);
|
|
283
|
-
},
|
|
284
|
-
};
|
|
285
|
-
}
|
|
286
|
-
else {
|
|
287
|
-
const session = (_a = this.sessions.get(sessionId)) !== null && _a !== void 0 ? _a : (0, Common_1.fail)('Unknown session, range may not be finalized.');
|
|
288
|
-
const firstNumericUuid = (0, NumericUuid_1.incrementUuid)(session.sessionUuid, -first - 1);
|
|
289
|
-
const firstFinal = (_b = this.compressNumericUuid(firstNumericUuid)) !== null && _b !== void 0 ? _b : (0, Common_1.fail)('Remote range must be finalized before getting IDs.');
|
|
290
|
-
(0, Common_1.assert)(isFinalId(firstFinal), 'ID from a remote session ID must have final form, as overrides are impossible by definition.');
|
|
291
|
-
const [baseFinalId, cluster] = (_c = this.getClusterForFinalId(firstFinal)) !== null && _c !== void 0 ? _c : (0, Common_1.fail)();
|
|
292
|
-
const numIdsRemainingInFirstCluster = cluster.capacity - (firstFinal - baseFinalId);
|
|
293
|
-
let pivotFinal;
|
|
294
|
-
if (count > numIdsRemainingInFirstCluster) {
|
|
295
|
-
const compressedPivot = this.compressNumericUuid((0, NumericUuid_1.incrementUuid)(firstNumericUuid, numIdsRemainingInFirstCluster));
|
|
296
|
-
// Looking up the actual cluster can be avoided, as it is guaranteed that at most one new cluster will be
|
|
297
|
-
// created when finalizing a range (regardless of size) due to the expansion optimization.
|
|
298
|
-
if (compressedPivot === undefined || isLocalId(compressedPivot)) {
|
|
299
|
-
(0, Common_1.fail)('ID from a remote session ID must have final form, as overrides are impossible by definition.');
|
|
300
|
-
}
|
|
301
|
-
else {
|
|
302
|
-
pivotFinal = compressedPivot;
|
|
303
|
-
}
|
|
266
|
+
const closestCluster = this.getClusterForFinalId(opSpaceNormalizedId);
|
|
267
|
+
if (closestCluster === undefined) {
|
|
268
|
+
if (this.sessionIdNormalizer.getCreationIndex(opSpaceNormalizedId) !== undefined) {
|
|
269
|
+
return this.attributionId;
|
|
270
|
+
}
|
|
271
|
+
else {
|
|
272
|
+
(0, Common_1.fail)('Cluster does not exist for final ID');
|
|
304
273
|
}
|
|
305
|
-
return {
|
|
306
|
-
length: count,
|
|
307
|
-
get: (index) => {
|
|
308
|
-
if (index < 0 || index >= count) {
|
|
309
|
-
(0, Common_1.fail)('Index out of bounds of range.');
|
|
310
|
-
}
|
|
311
|
-
if (index < numIdsRemainingInFirstCluster) {
|
|
312
|
-
return (firstFinal + index);
|
|
313
|
-
}
|
|
314
|
-
else {
|
|
315
|
-
return ((pivotFinal !== null && pivotFinal !== void 0 ? pivotFinal : (0, Common_1.fail)('Pivot must exist if range spans clusters.')) +
|
|
316
|
-
(index - numIdsRemainingInFirstCluster));
|
|
317
|
-
}
|
|
318
|
-
},
|
|
319
|
-
};
|
|
320
274
|
}
|
|
275
|
+
const [_, cluster] = closestCluster;
|
|
276
|
+
return cluster.session.attributionId;
|
|
321
277
|
}
|
|
322
278
|
/**
|
|
323
279
|
* Returns a range of local IDs created by this session in a format for sending to the server for finalizing.
|
|
@@ -373,7 +329,7 @@ class IdCompressor {
|
|
|
373
329
|
* @param range - the range of session-local IDs to finalize.
|
|
374
330
|
*/
|
|
375
331
|
finalizeCreationRange(range) {
|
|
376
|
-
var _a, _b, _c;
|
|
332
|
+
var _a, _b, _c, _d;
|
|
377
333
|
const { sessionId, attributionId } = range;
|
|
378
334
|
const isLocal = sessionId === this.localSessionId;
|
|
379
335
|
const session = (_a = this.sessions.get(sessionId)) !== null && _a !== void 0 ? _a : this.createSession(sessionId, attributionId);
|
|
@@ -387,16 +343,26 @@ class IdCompressor {
|
|
|
387
343
|
cluster: undefined,
|
|
388
344
|
clusterBase: undefined,
|
|
389
345
|
};
|
|
390
|
-
const
|
|
391
|
-
const
|
|
392
|
-
|
|
346
|
+
const currentClusterExists = currentCluster !== undefined && currentBaseFinalId !== undefined;
|
|
347
|
+
const normalizedLastFinalizedLocal = (_b = session.lastFinalizedLocalId) !== null && _b !== void 0 ? _b : 0;
|
|
348
|
+
const { first: newFirstFinalizedLocal, last: newLastFinalizedLocal } = ids;
|
|
349
|
+
(0, Common_1.assert)(newFirstFinalizedLocal === normalizedLastFinalizedLocal - 1, 'Ranges finalized out of order.');
|
|
393
350
|
// The total number of session-local IDs to finalize
|
|
394
|
-
const finalizeCount =
|
|
351
|
+
const finalizeCount = normalizedLastFinalizedLocal - newLastFinalizedLocal;
|
|
395
352
|
(0, Common_1.assert)(finalizeCount >= 1, 'Cannot finalize an empty range.');
|
|
396
353
|
let initialClusterCount = 0;
|
|
397
354
|
let remainingCount = finalizeCount;
|
|
398
355
|
let newBaseUuid;
|
|
399
|
-
if (
|
|
356
|
+
if (currentClusterExists) {
|
|
357
|
+
if (isLocal) {
|
|
358
|
+
const lastKnownFinal = (_c = this.sessionIdNormalizer.getLastFinalId()) !== null && _c !== void 0 ? _c : (0, Common_1.fail)('Cluster exists but normalizer does not have an entry for it.');
|
|
359
|
+
const lastFinalInCluster = (currentBaseFinalId +
|
|
360
|
+
Math.min(currentCluster.count + finalizeCount, currentCluster.capacity) -
|
|
361
|
+
1);
|
|
362
|
+
if (lastFinalInCluster > lastKnownFinal) {
|
|
363
|
+
this.sessionIdNormalizer.addFinalIds((lastKnownFinal + 1), lastFinalInCluster, currentCluster);
|
|
364
|
+
}
|
|
365
|
+
}
|
|
400
366
|
initialClusterCount = currentCluster.count;
|
|
401
367
|
const remainingCapacity = currentCluster.capacity - initialClusterCount;
|
|
402
368
|
const overflow = remainingCount - remainingCapacity;
|
|
@@ -413,6 +379,21 @@ class IdCompressor {
|
|
|
413
379
|
this.nextClusterBaseFinalId = (this.nextClusterBaseFinalId + expansionAmount);
|
|
414
380
|
(0, Common_1.assert)(this.nextClusterBaseFinalId < Number.MAX_SAFE_INTEGER, 'The number of allocated final IDs must not exceed the JS maximum safe integer.');
|
|
415
381
|
this.checkClusterForCollision(currentCluster);
|
|
382
|
+
if (isLocal) {
|
|
383
|
+
// Example with cluster size of 3:
|
|
384
|
+
// Ids generated so far: -1 1 2 -4 -5 <-- note positive numbers are eager finals
|
|
385
|
+
// Cluster: [ 0 1 2 ]
|
|
386
|
+
// ~ finalizing happens, causing expansion ~
|
|
387
|
+
// Cluster: [ 0 1 2 3 4 5 ]
|
|
388
|
+
// corresponding locals: -1 -4
|
|
389
|
+
// lastFinalizedLocalId^ ^newLastFinalizedLocalId = -6
|
|
390
|
+
// overflow = 2: ----
|
|
391
|
+
// localIdPivot^
|
|
392
|
+
// lastFinalizedFinal^
|
|
393
|
+
const lastFinalizedFinal = (currentBaseFinalId + currentCluster.count - 1);
|
|
394
|
+
const finalPivot = (lastFinalizedFinal - overflow + 1);
|
|
395
|
+
this.sessionIdNormalizer.addFinalIds(finalPivot, lastFinalizedFinal, currentCluster);
|
|
396
|
+
}
|
|
416
397
|
}
|
|
417
398
|
}
|
|
418
399
|
else {
|
|
@@ -453,9 +434,10 @@ class IdCompressor {
|
|
|
453
434
|
session,
|
|
454
435
|
};
|
|
455
436
|
const usedCapacity = finalizeCount - remainingCount;
|
|
456
|
-
localIdPivot = (
|
|
437
|
+
localIdPivot = (newFirstFinalizedLocal - usedCapacity);
|
|
457
438
|
if (isLocal) {
|
|
458
|
-
|
|
439
|
+
const lastFinalizedFinal = (newBaseFinalId + newCluster.count - 1);
|
|
440
|
+
this.sessionIdNormalizer.addFinalIds(newBaseFinalId, lastFinalizedFinal, newCluster);
|
|
459
441
|
}
|
|
460
442
|
this.checkClusterForCollision(newCluster);
|
|
461
443
|
this.clustersAndOverridesInversion.set((0, NumericUuid_1.stableIdFromNumericUuid)(newCluster.baseUuid), {
|
|
@@ -474,8 +456,8 @@ class IdCompressor {
|
|
|
474
456
|
const [overriddenLocal, override] = overrides[i];
|
|
475
457
|
// Note: recall that local IDs are negative
|
|
476
458
|
(0, Common_1.assert)(i === 0 || overriddenLocal < overrides[i - 1][0], 'Override IDs must be in sorted order.');
|
|
477
|
-
(0, Common_1.assert)(overriddenLocal <
|
|
478
|
-
(0, Common_1.assert)(overriddenLocal >=
|
|
459
|
+
(0, Common_1.assert)(overriddenLocal < normalizedLastFinalizedLocal, 'Ranges finalized out of order.');
|
|
460
|
+
(0, Common_1.assert)(overriddenLocal >= newLastFinalizedLocal, 'Malformed range: override ID ahead of range start.');
|
|
479
461
|
let cluster;
|
|
480
462
|
let overriddenFinal;
|
|
481
463
|
if (localIdPivot !== undefined && overriddenLocal <= localIdPivot) {
|
|
@@ -490,10 +472,10 @@ class IdCompressor {
|
|
|
490
472
|
cluster = currentCluster;
|
|
491
473
|
overriddenFinal = (currentBaseFinalId +
|
|
492
474
|
initialClusterCount +
|
|
493
|
-
(
|
|
475
|
+
(normalizedLastFinalizedLocal - overriddenLocal) -
|
|
494
476
|
1);
|
|
495
477
|
}
|
|
496
|
-
(
|
|
478
|
+
(_d = cluster.overrides) !== null && _d !== void 0 ? _d : (cluster.overrides = new Map());
|
|
497
479
|
const inversionKey = IdCompressor.createInversionKey(override);
|
|
498
480
|
const existingIds = this.getExistingIdsForNewOverride(inversionKey, true);
|
|
499
481
|
let overrideForCluster;
|
|
@@ -559,7 +541,7 @@ class IdCompressor {
|
|
|
559
541
|
}
|
|
560
542
|
}
|
|
561
543
|
}
|
|
562
|
-
session.lastFinalizedLocalId =
|
|
544
|
+
session.lastFinalizedLocalId = newLastFinalizedLocal;
|
|
563
545
|
}
|
|
564
546
|
checkClusterForCollision(cluster) {
|
|
565
547
|
const maxClusterUuid = (0, NumericUuid_1.incrementUuid)(cluster.baseUuid, cluster.capacity - 1);
|
|
@@ -639,9 +621,9 @@ class IdCompressor {
|
|
|
639
621
|
}
|
|
640
622
|
const override = (_a = numericOverride !== null && numericOverride !== void 0 ? numericOverride : stableOverride) !== null && _a !== void 0 ? _a : (IdCompressor.isStableInversionKey(inversionKey) ? inversionKey : undefined);
|
|
641
623
|
if (override !== undefined) {
|
|
642
|
-
const
|
|
643
|
-
if (
|
|
644
|
-
return
|
|
624
|
+
const sessionSpaceId = this.getCompressedIdForStableId(override);
|
|
625
|
+
if (sessionSpaceId !== undefined) {
|
|
626
|
+
return sessionSpaceId;
|
|
645
627
|
}
|
|
646
628
|
}
|
|
647
629
|
return undefined;
|
|
@@ -683,41 +665,50 @@ class IdCompressor {
|
|
|
683
665
|
* @returns an existing ID if one already exists for `override`, and a new local ID otherwise. The returned ID is in session space.
|
|
684
666
|
*/
|
|
685
667
|
generateCompressedId(override) {
|
|
686
|
-
|
|
668
|
+
let overrideInversionKey;
|
|
687
669
|
if (override !== undefined) {
|
|
688
|
-
|
|
689
|
-
const existingIds = this.getExistingIdsForNewOverride(
|
|
670
|
+
overrideInversionKey = IdCompressor.createInversionKey(override);
|
|
671
|
+
const existingIds = this.getExistingIdsForNewOverride(overrideInversionKey, false);
|
|
690
672
|
if (existingIds !== undefined) {
|
|
691
673
|
return typeof existingIds === 'number' ? existingIds : existingIds[0];
|
|
692
674
|
}
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
675
|
+
}
|
|
676
|
+
// Bump local counter regardless, then attempt to optimistically return a final ID.
|
|
677
|
+
// If the local session has reserved a cluster range via consensus, it is safe to hand out final IDs prior to
|
|
678
|
+
// finalizing the range that includes these locals.
|
|
679
|
+
const newLocalId = -++this.localIdCount;
|
|
680
|
+
const { currentClusterDetails } = this.localSession;
|
|
681
|
+
const { sessionIdNormalizer } = this;
|
|
682
|
+
let eagerFinalId;
|
|
683
|
+
let cluster;
|
|
684
|
+
if (currentClusterDetails !== undefined) {
|
|
685
|
+
const { clusterBase } = currentClusterDetails;
|
|
686
|
+
cluster = currentClusterDetails.cluster;
|
|
687
|
+
const lastFinalKnown = sessionIdNormalizer.getLastFinalId();
|
|
688
|
+
if (lastFinalKnown !== undefined && lastFinalKnown - clusterBase + 1 < cluster.capacity) {
|
|
689
|
+
eagerFinalId = (lastFinalKnown + 1);
|
|
700
690
|
}
|
|
701
691
|
}
|
|
692
|
+
if (overrideInversionKey !== undefined) {
|
|
693
|
+
const registeredLocal = sessionIdNormalizer.addLocalId();
|
|
694
|
+
(0, Common_1.assert)(registeredLocal === newLocalId, 'TODO');
|
|
695
|
+
if (eagerFinalId !== undefined) {
|
|
696
|
+
sessionIdNormalizer.addFinalIds(eagerFinalId, eagerFinalId, cluster !== null && cluster !== void 0 ? cluster : (0, Common_1.fail)());
|
|
697
|
+
}
|
|
698
|
+
this.localOverrides.append(newLocalId, override !== null && override !== void 0 ? override : (0, Common_1.fail)());
|
|
699
|
+
// Since the local ID was just created, it is in both session and op space
|
|
700
|
+
const compressionMapping = newLocalId;
|
|
701
|
+
this.clustersAndOverridesInversion.set(overrideInversionKey, compressionMapping);
|
|
702
|
+
}
|
|
703
|
+
else if (eagerFinalId !== undefined) {
|
|
704
|
+
sessionIdNormalizer.addFinalIds(eagerFinalId, eagerFinalId, cluster !== null && cluster !== void 0 ? cluster : (0, Common_1.fail)());
|
|
705
|
+
return eagerFinalId;
|
|
706
|
+
}
|
|
702
707
|
else {
|
|
703
|
-
|
|
708
|
+
const registeredLocal = sessionIdNormalizer.addLocalId();
|
|
709
|
+
(0, Common_1.assert)(registeredLocal === newLocalId, 'TODO');
|
|
704
710
|
}
|
|
705
|
-
|
|
706
|
-
/**
|
|
707
|
-
* Generates a range of compressed IDs.
|
|
708
|
-
* This should ONLY be called to generate IDs for local operations.
|
|
709
|
-
* @param count - the number of IDs to generate, must be \> 0.
|
|
710
|
-
* @returns a persistable descriptor of the ID range.
|
|
711
|
-
*/
|
|
712
|
-
generateCompressedIdRange(count) {
|
|
713
|
-
(0, Common_1.assert)(count > 0, 'Must generate a nonzero number of IDs.');
|
|
714
|
-
(0, Common_1.assert)(count <= Number.MAX_SAFE_INTEGER, 'The number of allocated local IDs must not exceed the JS maximum safe integer.');
|
|
715
|
-
const first = this.generateNextLocalId();
|
|
716
|
-
this.localIdCount += count - 1;
|
|
717
|
-
return { first, count };
|
|
718
|
-
}
|
|
719
|
-
generateNextLocalId() {
|
|
720
|
-
return -++this.localIdCount;
|
|
711
|
+
return newLocalId;
|
|
721
712
|
}
|
|
722
713
|
/**
|
|
723
714
|
* Decompresses a previously compressed ID into a UUID or override string.
|
|
@@ -738,6 +729,11 @@ class IdCompressor {
|
|
|
738
729
|
if (isFinalId(id)) {
|
|
739
730
|
const possibleCluster = this.getClusterForFinalId(id);
|
|
740
731
|
if (possibleCluster === undefined) {
|
|
732
|
+
// It may be an unfinalized eager final ID, so check with normalizer to get the offset from the session UUID
|
|
733
|
+
const creationIndex = this.sessionIdNormalizer.getCreationIndex(id);
|
|
734
|
+
if (creationIndex !== undefined) {
|
|
735
|
+
return (0, NumericUuid_1.stableIdFromNumericUuid)(this.localSession.sessionUuid, creationIndex);
|
|
736
|
+
}
|
|
741
737
|
return undefined;
|
|
742
738
|
}
|
|
743
739
|
else {
|
|
@@ -805,9 +801,6 @@ class IdCompressor {
|
|
|
805
801
|
return compressionMapping;
|
|
806
802
|
}
|
|
807
803
|
else {
|
|
808
|
-
const cluster = compressionMapping.cluster;
|
|
809
|
-
(0, Common_1.assert)(IdCompressor.tryGetOverride(cluster, compressionMapping.originalOverridingFinal) !==
|
|
810
|
-
undefined, 'No override for cluster marked as having one.');
|
|
811
804
|
return ((_a = compressionMapping.associatedLocalId) !== null && _a !== void 0 ? _a : compressionMapping.originalOverridingFinal);
|
|
812
805
|
}
|
|
813
806
|
}
|
|
@@ -836,9 +829,9 @@ class IdCompressor {
|
|
|
836
829
|
}
|
|
837
830
|
if (isStable) {
|
|
838
831
|
// May have already computed the numeric UUID, so avoid recomputing if possible
|
|
839
|
-
const
|
|
840
|
-
if (
|
|
841
|
-
return
|
|
832
|
+
const sessionSpaceId = this.getCompressedIdForStableId(numericUuid !== null && numericUuid !== void 0 ? numericUuid : inversionKey);
|
|
833
|
+
if (sessionSpaceId !== undefined) {
|
|
834
|
+
return sessionSpaceId;
|
|
842
835
|
}
|
|
843
836
|
}
|
|
844
837
|
return undefined;
|
|
@@ -857,9 +850,14 @@ class IdCompressor {
|
|
|
857
850
|
if (-id > this.localIdCount) {
|
|
858
851
|
(0, Common_1.fail)('Supplied local ID was not created by this compressor.');
|
|
859
852
|
}
|
|
860
|
-
// Check if this local ID has not been finalized yet
|
|
853
|
+
// Check if this local ID has not been finalized yet.
|
|
854
|
+
// Comparing lastFinalizedLocalId is a safe check for eager final IDs because the local IDs corresponding to them
|
|
855
|
+
// are never handed out to a consumer, and thus could not be passed into this method.
|
|
861
856
|
const { lastFinalizedLocalId } = this.localSession;
|
|
862
857
|
if (lastFinalizedLocalId === undefined || id < lastFinalizedLocalId) {
|
|
858
|
+
// Eager final IDs do not have overrides in the cluster until finalizing
|
|
859
|
+
// This means that using the normalizer to get the final/cluster associated would succeed but would not have the override,
|
|
860
|
+
// so checking localOverrides first is necessary.
|
|
863
861
|
const override = this.localOverrides.get(id);
|
|
864
862
|
if (override !== undefined) {
|
|
865
863
|
const inversionKey = IdCompressor.createInversionKey(override);
|
|
@@ -872,8 +870,7 @@ class IdCompressor {
|
|
|
872
870
|
}
|
|
873
871
|
return id;
|
|
874
872
|
}
|
|
875
|
-
const [
|
|
876
|
-
const correspondingFinal = (finalBase + (localBase - id));
|
|
873
|
+
const [correspondingFinal, cluster] = (_b = this.sessionIdNormalizer.getFinalId(id)) !== null && _b !== void 0 ? _b : (0, Common_1.fail)('Locally created cluster should be added to the map when allocated');
|
|
877
874
|
if (cluster.overrides) {
|
|
878
875
|
const override = cluster.overrides.get(correspondingFinal);
|
|
879
876
|
if (typeof override === 'object' && override.originalOverridingFinal !== undefined) {
|
|
@@ -883,11 +880,10 @@ class IdCompressor {
|
|
|
883
880
|
}
|
|
884
881
|
return correspondingFinal;
|
|
885
882
|
}
|
|
886
|
-
normalizeToSessionSpace(id,
|
|
887
|
-
var _a, _b, _c;
|
|
888
|
-
const isLocalSession = originSessionId === this.localSessionId;
|
|
883
|
+
normalizeToSessionSpace(id, sessionIdIfLocal) {
|
|
884
|
+
var _a, _b, _c, _d;
|
|
889
885
|
if (isLocalId(id)) {
|
|
890
|
-
if (
|
|
886
|
+
if (sessionIdIfLocal === undefined || sessionIdIfLocal === this.localSessionId) {
|
|
891
887
|
const localIndex = -id;
|
|
892
888
|
if (localIndex > this.localIdCount) {
|
|
893
889
|
(0, Common_1.fail)('Supplied local ID was not created by this compressor.');
|
|
@@ -895,27 +891,20 @@ class IdCompressor {
|
|
|
895
891
|
return id;
|
|
896
892
|
}
|
|
897
893
|
else {
|
|
898
|
-
const session = this.sessions.get(
|
|
899
|
-
if (session === undefined) {
|
|
900
|
-
(0, Common_1.fail)('No IDs have ever been finalized by the supplied session.');
|
|
901
|
-
}
|
|
894
|
+
const session = (_a = this.sessions.get(sessionIdIfLocal)) !== null && _a !== void 0 ? _a : (0, Common_1.fail)('No IDs have ever been finalized by the supplied session.');
|
|
902
895
|
const localCount = -id;
|
|
903
896
|
const numericUuid = (0, NumericUuid_1.incrementUuid)(session.sessionUuid, localCount - 1);
|
|
904
|
-
return (
|
|
897
|
+
return (_b = this.compressNumericUuid(numericUuid)) !== null && _b !== void 0 ? _b : (0, Common_1.fail)('ID is not known to this compressor.');
|
|
905
898
|
}
|
|
906
899
|
}
|
|
907
|
-
const
|
|
908
|
-
if (
|
|
909
|
-
|
|
910
|
-
const indexInCluster = id - finalBase;
|
|
911
|
-
if (indexInCluster < cluster.count) {
|
|
912
|
-
return (localBase - indexInCluster);
|
|
913
|
-
}
|
|
900
|
+
const normalizedId = this.sessionIdNormalizer.getSessionSpaceId(id);
|
|
901
|
+
if (normalizedId !== undefined) {
|
|
902
|
+
return normalizedId;
|
|
914
903
|
}
|
|
915
904
|
// Check for a unified override finalized first by another session but to which the local session
|
|
916
905
|
// still has an associated local ID.
|
|
917
|
-
const [_, cluster] = (
|
|
918
|
-
const override = (
|
|
906
|
+
const [_, cluster] = (_c = this.getClusterForFinalId(id)) !== null && _c !== void 0 ? _c : (0, Common_1.fail)('Supplied final ID was not finalized by this compressor.');
|
|
907
|
+
const override = (_d = cluster.overrides) === null || _d === void 0 ? void 0 : _d.get(id);
|
|
919
908
|
if (typeof override === 'object' && override.associatedLocalId !== undefined) {
|
|
920
909
|
return override.associatedLocalId;
|
|
921
910
|
}
|
|
@@ -949,13 +938,19 @@ class IdCompressor {
|
|
|
949
938
|
}
|
|
950
939
|
return sessionSpaceId;
|
|
951
940
|
}
|
|
952
|
-
|
|
941
|
+
/**
|
|
942
|
+
* Returns a compressed ID for the supplied stable ID if it was created by the local session, and undefined otherwise.
|
|
943
|
+
*/
|
|
944
|
+
getCompressedIdForStableId(stableId) {
|
|
953
945
|
const numericUuid = typeof stableId === 'string' ? (0, NumericUuid_1.numericUuidFromStableId)(stableId) : stableId;
|
|
954
|
-
const
|
|
955
|
-
if (
|
|
956
|
-
|
|
946
|
+
const creationIndex = (0, NumericUuid_1.getPositiveDelta)(numericUuid, this.localSession.sessionUuid, this.localIdCount - 1);
|
|
947
|
+
if (creationIndex !== undefined) {
|
|
948
|
+
const sessionSpaceId = this.sessionIdNormalizer.getIdByCreationIndex(creationIndex);
|
|
949
|
+
if (sessionSpaceId !== undefined) {
|
|
950
|
+
return sessionSpaceId;
|
|
951
|
+
}
|
|
957
952
|
}
|
|
958
|
-
return
|
|
953
|
+
return undefined;
|
|
959
954
|
}
|
|
960
955
|
getClusterForFinalId(finalId) {
|
|
961
956
|
const possibleCluster = this.finalIdToCluster.getPairOrNextLower(finalId);
|
|
@@ -986,6 +981,9 @@ class IdCompressor {
|
|
|
986
981
|
if (!(0, Common_1.compareMaps)(this.sessions, other.sessions, (a, b) => IdCompressor.sessionDataEqual(a, b, true, compareLocalState))) {
|
|
987
982
|
return false;
|
|
988
983
|
}
|
|
984
|
+
if (!this.sessionIdNormalizer.equals(other.sessionIdNormalizer, (a, b) => IdCompressor.idClustersEqual(a, b, false, compareLocalState))) {
|
|
985
|
+
return false;
|
|
986
|
+
}
|
|
989
987
|
}
|
|
990
988
|
else {
|
|
991
989
|
for (const [keyA, valueA] of this.sessions) {
|
|
@@ -1176,6 +1174,7 @@ class IdCompressor {
|
|
|
1176
1174
|
localIdCount: this.localIdCount,
|
|
1177
1175
|
overrides: [...this.localOverrides.entries()].map((entry) => [...entry]),
|
|
1178
1176
|
lastTakenLocalId: this.lastTakenLocalId,
|
|
1177
|
+
sessionNormalizer: this.sessionIdNormalizer.serialize(),
|
|
1179
1178
|
};
|
|
1180
1179
|
}
|
|
1181
1180
|
return serializedWithSession;
|
|
@@ -1249,12 +1248,6 @@ class IdCompressor {
|
|
|
1249
1248
|
};
|
|
1250
1249
|
const lastFinalizedNormalized = lastFinalizedLocalId !== null && lastFinalizedLocalId !== void 0 ? lastFinalizedLocalId : 0;
|
|
1251
1250
|
const clusterBase = compressor.nextClusterBaseFinalId;
|
|
1252
|
-
if (serializedLocalState !== undefined && sessionId === compressor.localSessionId) {
|
|
1253
|
-
compressor.localIdToCluster.append((lastFinalizedNormalized - 1), [
|
|
1254
|
-
clusterBase,
|
|
1255
|
-
cluster,
|
|
1256
|
-
]);
|
|
1257
|
-
}
|
|
1258
1251
|
session.lastFinalizedLocalId = (lastFinalizedNormalized - count);
|
|
1259
1252
|
session.currentClusterDetails = { clusterBase, cluster };
|
|
1260
1253
|
compressor.nextClusterBaseFinalId = (compressor.nextClusterBaseFinalId + capacity);
|
|
@@ -1304,6 +1297,13 @@ class IdCompressor {
|
|
|
1304
1297
|
}
|
|
1305
1298
|
}
|
|
1306
1299
|
}
|
|
1300
|
+
if (serializedLocalState !== undefined) {
|
|
1301
|
+
compressor.sessionIdNormalizer = SessionIdNormalizer_1.SessionIdNormalizer.deserialize(serializedLocalState.sessionNormalizer, (finalId) => {
|
|
1302
|
+
var _a;
|
|
1303
|
+
const [_, cluster] = (_a = compressor.finalIdToCluster.getPairOrNextLower(finalId)) !== null && _a !== void 0 ? _a : (0, Common_1.fail)('Final in serialized normalizer was never created.');
|
|
1304
|
+
return cluster;
|
|
1305
|
+
});
|
|
1306
|
+
}
|
|
1307
1307
|
(0, Common_1.assert)(compressor.localSession.lastFinalizedLocalId === undefined ||
|
|
1308
1308
|
compressor.localIdCount >= -compressor.localSession.lastFinalizedLocalId);
|
|
1309
1309
|
return compressor;
|