@fluidframework/container-runtime 2.0.0-internal.2.0.3 → 2.0.0-internal.2.1.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/dist/containerRuntime.d.ts +2 -1
- package/dist/containerRuntime.d.ts.map +1 -1
- package/dist/containerRuntime.js +63 -23
- package/dist/containerRuntime.js.map +1 -1
- package/dist/dataStore.d.ts.map +1 -1
- package/dist/dataStore.js +6 -0
- package/dist/dataStore.js.map +1 -1
- package/dist/dataStoreContext.d.ts +7 -0
- package/dist/dataStoreContext.d.ts.map +1 -1
- package/dist/dataStoreContext.js +34 -8
- package/dist/dataStoreContext.js.map +1 -1
- package/dist/dataStoreContexts.js +1 -1
- package/dist/dataStoreContexts.js.map +1 -1
- package/dist/dataStores.d.ts +3 -2
- package/dist/dataStores.d.ts.map +1 -1
- package/dist/dataStores.js +29 -3
- package/dist/dataStores.js.map +1 -1
- package/dist/garbageCollection.d.ts +19 -5
- package/dist/garbageCollection.d.ts.map +1 -1
- package/dist/garbageCollection.js +120 -37
- package/dist/garbageCollection.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.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/summarizerClientElection.js +1 -1
- package/dist/summarizerClientElection.js.map +1 -1
- package/dist/summaryGenerator.d.ts.map +1 -1
- package/dist/summaryGenerator.js +3 -2
- package/dist/summaryGenerator.js.map +1 -1
- package/garbageCollection.md +27 -22
- package/lib/containerRuntime.d.ts +2 -1
- package/lib/containerRuntime.d.ts.map +1 -1
- package/lib/containerRuntime.js +64 -24
- package/lib/containerRuntime.js.map +1 -1
- package/lib/dataStore.d.ts.map +1 -1
- package/lib/dataStore.js +6 -0
- package/lib/dataStore.js.map +1 -1
- package/lib/dataStoreContext.d.ts +7 -0
- package/lib/dataStoreContext.d.ts.map +1 -1
- package/lib/dataStoreContext.js +35 -9
- package/lib/dataStoreContext.js.map +1 -1
- package/lib/dataStoreContexts.js +1 -1
- package/lib/dataStoreContexts.js.map +1 -1
- package/lib/dataStores.d.ts +3 -2
- package/lib/dataStores.d.ts.map +1 -1
- package/lib/dataStores.js +30 -4
- package/lib/dataStores.js.map +1 -1
- package/lib/garbageCollection.d.ts +19 -5
- package/lib/garbageCollection.d.ts.map +1 -1
- package/lib/garbageCollection.js +119 -36
- package/lib/garbageCollection.js.map +1 -1
- package/lib/index.d.ts +1 -1
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +1 -1
- package/lib/index.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/lib/summarizerClientElection.js +1 -1
- package/lib/summarizerClientElection.js.map +1 -1
- package/lib/summaryGenerator.d.ts.map +1 -1
- package/lib/summaryGenerator.js +3 -2
- package/lib/summaryGenerator.js.map +1 -1
- package/package.json +26 -23
- package/src/containerRuntime.ts +77 -26
- package/src/dataStore.ts +13 -1
- package/src/dataStoreContext.ts +48 -10
- package/src/dataStoreContexts.ts +1 -1
- package/src/dataStores.ts +34 -3
- package/src/garbageCollection.ts +144 -44
- package/src/index.ts +1 -1
- package/src/packageVersion.ts +1 -1
- package/src/summarizerClientElection.ts +1 -1
- package/src/summaryGenerator.ts +3 -2
|
@@ -15,7 +15,7 @@ var __rest = (this && this.__rest) || function (s, e) {
|
|
|
15
15
|
return t;
|
|
16
16
|
};
|
|
17
17
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
18
|
-
exports.GarbageCollector = exports.UnreferencedStateTracker = exports.UnreferencedState = exports.GCNodeType = exports.defaultSessionExpiryDurationMs = exports.defaultInactiveTimeoutMs = exports.oneDayMs = exports.disableSweepLogKey = exports.trackGCStateKey = exports.runSessionExpiryKey = exports.gcTestModeKey = exports.runSweepKey = exports.runGCKey = exports.gcBlobPrefix = exports.gcTreeKey = void 0;
|
|
18
|
+
exports.GarbageCollector = exports.UnreferencedStateTracker = exports.UnreferencedState = exports.GCNodeType = exports.defaultSessionExpiryDurationMs = exports.defaultInactiveTimeoutMs = exports.oneDayMs = exports.testTombstoneKey = exports.disableSweepLogKey = exports.trackGCStateKey = exports.runSessionExpiryKey = exports.gcTestModeKey = exports.runSweepKey = exports.runGCKey = exports.gcTombstoneBlobKey = exports.gcBlobPrefix = exports.gcTreeKey = void 0;
|
|
19
19
|
const common_utils_1 = require("@fluidframework/common-utils");
|
|
20
20
|
const container_utils_1 = require("@fluidframework/container-utils");
|
|
21
21
|
const garbage_collector_1 = require("@fluidframework/garbage-collector");
|
|
@@ -33,6 +33,8 @@ const GCVersion = 1;
|
|
|
33
33
|
exports.gcTreeKey = "gc";
|
|
34
34
|
// They prefix for GC blobs in the GC tree in summary.
|
|
35
35
|
exports.gcBlobPrefix = "__gc";
|
|
36
|
+
// The key for tombstone blob in the GC tree in summary.
|
|
37
|
+
exports.gcTombstoneBlobKey = "__tombstones";
|
|
36
38
|
// Feature gate key to turn GC on / off.
|
|
37
39
|
exports.runGCKey = "Fluid.GarbageCollection.RunGC";
|
|
38
40
|
// Feature gate key to turn GC sweep on / off.
|
|
@@ -45,6 +47,8 @@ exports.runSessionExpiryKey = "Fluid.GarbageCollection.RunSessionExpiry";
|
|
|
45
47
|
exports.trackGCStateKey = "Fluid.GarbageCollection.TrackGCState";
|
|
46
48
|
// Feature gate key to turn GC sweep log off.
|
|
47
49
|
exports.disableSweepLogKey = "Fluid.GarbageCollection.DisableSweepLog";
|
|
50
|
+
// Feature gate key to tombstone datastores.
|
|
51
|
+
exports.testTombstoneKey = "Fluid.GarbageCollection.Test.Tombstone";
|
|
48
52
|
// One day in milliseconds.
|
|
49
53
|
exports.oneDayMs = 1 * 24 * 60 * 60 * 1000;
|
|
50
54
|
exports.defaultInactiveTimeoutMs = 7 * exports.oneDayMs; // 7 days
|
|
@@ -161,7 +165,7 @@ exports.UnreferencedStateTracker = UnreferencedStateTracker;
|
|
|
161
165
|
*/
|
|
162
166
|
class GarbageCollector {
|
|
163
167
|
constructor(createParams) {
|
|
164
|
-
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
168
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j;
|
|
165
169
|
/**
|
|
166
170
|
* Tells whether the initial GC state needs to be reset. This can happen under 2 conditions:
|
|
167
171
|
*
|
|
@@ -180,6 +184,7 @@ class GarbageCollector {
|
|
|
180
184
|
// Keeps a list of references (edges in the GC graph) between GC runs. Each entry has a node id and a list of
|
|
181
185
|
// outbound routes from that node.
|
|
182
186
|
this.newReferencesSinceLastRun = new Map();
|
|
187
|
+
this.tombstones = [];
|
|
183
188
|
// Map of node ids to their unreferenced state tracker.
|
|
184
189
|
this.unreferencedNodesState = new Map();
|
|
185
190
|
// Keeps track of unreferenced events that are logged for a node. This is used to limit the log generation to one
|
|
@@ -301,6 +306,7 @@ class GarbageCollector {
|
|
|
301
306
|
}
|
|
302
307
|
// Whether we are running in test mode. In this mode, unreferenced nodes are immediately deleted.
|
|
303
308
|
this.testMode = (_h = this.mc.config.getBoolean(exports.gcTestModeKey)) !== null && _h !== void 0 ? _h : this.gcOptions.runGCInTestMode === true;
|
|
309
|
+
this.tombstoneMode = (_j = this.mc.config.getBoolean(exports.testTombstoneKey)) !== null && _j !== void 0 ? _j : false;
|
|
304
310
|
// The GC state needs to be reset if the base snapshot contains GC tree and GC is disabled or it doesn't
|
|
305
311
|
// contain GC tree and GC is enabled.
|
|
306
312
|
const gcTreePresent = (baseSnapshot === null || baseSnapshot === void 0 ? void 0 : baseSnapshot.trees[exports.gcTreeKey]) !== undefined;
|
|
@@ -316,11 +322,17 @@ class GarbageCollector {
|
|
|
316
322
|
// For newer documents, GC data should be present in the GC tree in the root of the snapshot.
|
|
317
323
|
const gcSnapshotTree = baseSnapshot.trees[exports.gcTreeKey];
|
|
318
324
|
if (gcSnapshotTree !== undefined) {
|
|
319
|
-
const
|
|
325
|
+
const baseGCData = await getGCDataFromSnapshot(gcSnapshotTree, readAndParseBlob);
|
|
326
|
+
if (baseGCData.tombstones !== undefined && this.tombstoneMode) {
|
|
327
|
+
this.tombstones = baseGCData.tombstones;
|
|
328
|
+
}
|
|
320
329
|
if (this.trackGCState) {
|
|
321
|
-
this.
|
|
330
|
+
this.latestSummaryData = {
|
|
331
|
+
serializedGCState: JSON.stringify(generateSortedGCState(baseGCData.gcState)),
|
|
332
|
+
serializedTombstones: JSON.stringify(baseGCData.tombstones),
|
|
333
|
+
};
|
|
322
334
|
}
|
|
323
|
-
return
|
|
335
|
+
return baseGCData.gcState;
|
|
324
336
|
}
|
|
325
337
|
// back-compat - Older documents will have the GC blobs in each data store's summary tree. Get them and
|
|
326
338
|
// consolidate into IGarbageCollectionState format.
|
|
@@ -465,7 +477,7 @@ class GarbageCollector {
|
|
|
465
477
|
}
|
|
466
478
|
/** Returns a list of all the configurations for garbage collection. */
|
|
467
479
|
get configs() {
|
|
468
|
-
return Object.assign({ gcEnabled: this.gcEnabled, sweepEnabled: this.sweepEnabled, runGC: this.shouldRunGC, runSweep: this.shouldRunSweep, testMode: this.testMode, sessionExpiry: this.sessionExpiryTimeoutMs, sweepTimeout: this.sweepTimeoutMs, inactiveTimeout: this.inactiveTimeoutMs, trackGCState: this.trackGCState }, this.gcOptions);
|
|
480
|
+
return Object.assign({ gcEnabled: this.gcEnabled, sweepEnabled: this.sweepEnabled, runGC: this.shouldRunGC, runSweep: this.shouldRunSweep, testMode: this.testMode, tombstoneMode: this.tombstoneMode, sessionExpiry: this.sessionExpiryTimeoutMs, sweepTimeout: this.sweepTimeoutMs, inactiveTimeout: this.inactiveTimeoutMs, trackGCState: this.trackGCState }, this.gcOptions);
|
|
469
481
|
}
|
|
470
482
|
/**
|
|
471
483
|
* Called when the connection state of the runtime changes, i.e., it connects or disconnects. GC subscribes to this
|
|
@@ -475,15 +487,19 @@ class GarbageCollector {
|
|
|
475
487
|
*/
|
|
476
488
|
setConnectionState(connected, clientId) {
|
|
477
489
|
/**
|
|
478
|
-
* For
|
|
490
|
+
* For all clients, initialize the base state when the container becomes active, i.e., it transitions
|
|
479
491
|
* to "write" mode. This will ensure that the container's own join op is processed and there is a recent
|
|
480
492
|
* reference timestamp that will be used to update the state of unreferenced nodes. Also, all trailing ops which
|
|
481
493
|
* could affect the GC state will have been processed.
|
|
482
494
|
*
|
|
495
|
+
* If GC is up-to-date for the client and the summarizing client, there will be an doubling of both
|
|
496
|
+
* InactiveObject_Loaded and SweepReady_Loaded errors, as there will be one from the sending client and one from
|
|
497
|
+
* the receiving summarizer client.
|
|
498
|
+
*
|
|
483
499
|
* Ideally, this initialization should only be done for summarizer client. However, we are currently rolling out
|
|
484
500
|
* sweep in phases and we want to track when inactive and sweep ready objects are used in any client.
|
|
485
501
|
*/
|
|
486
|
-
if (this.activeConnection() &&
|
|
502
|
+
if (this.activeConnection() && this.shouldRunGC) {
|
|
487
503
|
this.initializeBaseStateP.catch((error) => { });
|
|
488
504
|
}
|
|
489
505
|
}
|
|
@@ -549,6 +565,22 @@ class GarbageCollector {
|
|
|
549
565
|
if (this.testMode) {
|
|
550
566
|
this.runtime.deleteUnusedRoutes(gcResult.deletedNodeIds);
|
|
551
567
|
}
|
|
568
|
+
else {
|
|
569
|
+
// If we are running in GC tombstone mode, tombstone objects for unused routes. This enables testing
|
|
570
|
+
// scenarios involving access to "deleted" data without actually deleting the data from summaries.
|
|
571
|
+
// Note: we will not tombstone in test mode
|
|
572
|
+
if (this.tombstoneMode) {
|
|
573
|
+
const tombstoneRoutes = [];
|
|
574
|
+
// Currently only tombstone datastores
|
|
575
|
+
for (const [key, value] of this.unreferencedNodesState.entries()) {
|
|
576
|
+
if (value.state === exports.UnreferencedState.SweepReady &&
|
|
577
|
+
this.runtime.getNodeType(key) === exports.GCNodeType.DataStore) {
|
|
578
|
+
tombstoneRoutes.push(key);
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
this.runtime.deleteUnusedRoutes(tombstoneRoutes);
|
|
582
|
+
}
|
|
583
|
+
}
|
|
552
584
|
// Log pending unreferenced events such as a node being used after inactive. This is done after GC runs and
|
|
553
585
|
// updates its state so that we don't send false positives based on intermediate state. For example, we may get
|
|
554
586
|
// reference to an unreferenced node from another unreferenced node which means the node wasn't revived.
|
|
@@ -572,31 +604,67 @@ class GarbageCollector {
|
|
|
572
604
|
unreferencedTimestampMs: (_a = this.unreferencedNodesState.get(nodeId)) === null || _a === void 0 ? void 0 : _a.unreferencedTimestampMs,
|
|
573
605
|
};
|
|
574
606
|
}
|
|
575
|
-
const
|
|
607
|
+
const serializedGCState = JSON.stringify(generateSortedGCState(gcState));
|
|
608
|
+
const serializedTombstones = this.tombstones.length > 0 ? JSON.stringify(this.tombstones.sort()) : undefined;
|
|
576
609
|
/**
|
|
577
|
-
*
|
|
578
|
-
*
|
|
610
|
+
* Incremental summary of GC data - If any of the GC state or tombstone state hasn't changed since the last
|
|
611
|
+
* summary, send summary handles for them. Otherwise, send the data in summary blobs.
|
|
579
612
|
*/
|
|
580
613
|
if (this.trackGCState) {
|
|
581
|
-
this.
|
|
582
|
-
if (this.
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
614
|
+
this.pendingSummaryData = { serializedGCState, serializedTombstones };
|
|
615
|
+
if (trackState && !fullTree && this.latestSummaryData !== undefined) {
|
|
616
|
+
// If neither GC state or tombstone state changed, send a summary handle for the entire GC data.
|
|
617
|
+
if (this.latestSummaryData.serializedGCState === serializedGCState
|
|
618
|
+
&& this.latestSummaryData.serializedTombstones === serializedTombstones) {
|
|
619
|
+
const stats = (0, runtime_utils_1.mergeStats)();
|
|
620
|
+
stats.handleNodeCount++;
|
|
621
|
+
return {
|
|
622
|
+
summary: {
|
|
623
|
+
type: protocol_definitions_1.SummaryType.Handle,
|
|
624
|
+
handle: `/${exports.gcTreeKey}`,
|
|
625
|
+
handleType: protocol_definitions_1.SummaryType.Tree,
|
|
626
|
+
},
|
|
627
|
+
stats,
|
|
628
|
+
};
|
|
629
|
+
}
|
|
630
|
+
// If either or both of GC state or tombstone state changed, build a GC summary tree.
|
|
631
|
+
return this.buildGCSummaryTree(serializedGCState, serializedTombstones, true /* trackState */);
|
|
596
632
|
}
|
|
597
633
|
}
|
|
634
|
+
// If not tracking GC state, build a GC summary tree without any summary handles.
|
|
635
|
+
return this.buildGCSummaryTree(serializedGCState, serializedTombstones, false /* trackState */);
|
|
636
|
+
}
|
|
637
|
+
/**
|
|
638
|
+
* Builds the GC summary tree which contains GC state and tombstone state.
|
|
639
|
+
* If trackState is false, both GC state and tombstone state are written as summary blobs.
|
|
640
|
+
* If trackState is true, summary blob is written for GC state or tombstone state if they changed.
|
|
641
|
+
* @param serializedGCState - The GC state serialized as string.
|
|
642
|
+
* @param serializedTombstones - THe tombstone state serialized as string.
|
|
643
|
+
* @param trackState - Whether we are tracking GC state across summaries.
|
|
644
|
+
* @returns the GC summary tree.
|
|
645
|
+
*/
|
|
646
|
+
buildGCSummaryTree(serializedGCState, serializedTombstones, trackState) {
|
|
647
|
+
var _a, _b;
|
|
648
|
+
const gcStateBlobKey = `${exports.gcBlobPrefix}_root`;
|
|
598
649
|
const builder = new runtime_utils_1.SummaryTreeBuilder();
|
|
599
|
-
|
|
650
|
+
// If the GC state hasn't changed, write a summary handle, else write a summary blob for it.
|
|
651
|
+
if (((_a = this.latestSummaryData) === null || _a === void 0 ? void 0 : _a.serializedGCState) === serializedGCState && trackState) {
|
|
652
|
+
builder.addHandle(gcStateBlobKey, protocol_definitions_1.SummaryType.Blob, `/${exports.gcTreeKey}/${gcStateBlobKey}`);
|
|
653
|
+
}
|
|
654
|
+
else {
|
|
655
|
+
builder.addBlob(gcStateBlobKey, serializedGCState);
|
|
656
|
+
}
|
|
657
|
+
// If there is no tombstone data, return only the GC state.
|
|
658
|
+
if (serializedTombstones === undefined) {
|
|
659
|
+
return builder.getSummaryTree();
|
|
660
|
+
}
|
|
661
|
+
// If the tombstone state hasn't changed, write a summary handle, else write a summary blob for it.
|
|
662
|
+
if (((_b = this.latestSummaryData) === null || _b === void 0 ? void 0 : _b.serializedTombstones) === serializedTombstones && trackState) {
|
|
663
|
+
builder.addHandle(exports.gcTombstoneBlobKey, protocol_definitions_1.SummaryType.Blob, `/${exports.gcTreeKey}/${exports.gcTombstoneBlobKey}`);
|
|
664
|
+
}
|
|
665
|
+
else {
|
|
666
|
+
builder.addBlob(exports.gcTombstoneBlobKey, serializedTombstones);
|
|
667
|
+
}
|
|
600
668
|
return builder.getSummaryTree();
|
|
601
669
|
}
|
|
602
670
|
getMetadata() {
|
|
@@ -632,8 +700,8 @@ class GarbageCollector {
|
|
|
632
700
|
this.latestSummaryGCVersion = this.currentGCVersion;
|
|
633
701
|
this.initialStateNeedsReset = false;
|
|
634
702
|
if (this.trackGCState) {
|
|
635
|
-
this.
|
|
636
|
-
this.
|
|
703
|
+
this.latestSummaryData = this.pendingSummaryData;
|
|
704
|
+
this.pendingSummaryData = undefined;
|
|
637
705
|
}
|
|
638
706
|
return;
|
|
639
707
|
}
|
|
@@ -647,13 +715,16 @@ class GarbageCollector {
|
|
|
647
715
|
}
|
|
648
716
|
const gcSnapshotTree = snapshot.trees[exports.gcTreeKey];
|
|
649
717
|
if (gcSnapshotTree !== undefined && this.trackGCState) {
|
|
650
|
-
const
|
|
651
|
-
this.
|
|
718
|
+
const latestGCData = await getGCDataFromSnapshot(gcSnapshotTree, readAndParseBlob);
|
|
719
|
+
this.latestSummaryData = {
|
|
720
|
+
serializedGCState: JSON.stringify(generateSortedGCState(latestGCData.gcState)),
|
|
721
|
+
serializedTombstones: JSON.stringify(latestGCData.tombstones),
|
|
722
|
+
};
|
|
652
723
|
}
|
|
653
724
|
else {
|
|
654
|
-
this.
|
|
725
|
+
this.latestSummaryData = undefined;
|
|
655
726
|
}
|
|
656
|
-
this.
|
|
727
|
+
this.pendingSummaryData = undefined;
|
|
657
728
|
}
|
|
658
729
|
/**
|
|
659
730
|
* Called when a node with the given id is updated. If the node is inactive, log an error.
|
|
@@ -708,6 +779,7 @@ class GarbageCollector {
|
|
|
708
779
|
*/
|
|
709
780
|
updateCurrentState(gcData, gcResult, currentReferenceTimestampMs) {
|
|
710
781
|
this.previousGCDataFromLastRun = (0, garbage_collector_1.cloneGCData)(gcData);
|
|
782
|
+
this.tombstones = [];
|
|
711
783
|
this.newReferencesSinceLastRun.clear();
|
|
712
784
|
// Iterate through the referenced nodes and stop tracking if they were unreferenced before.
|
|
713
785
|
for (const nodeId of gcResult.referencedNodeIds) {
|
|
@@ -731,6 +803,12 @@ class GarbageCollector {
|
|
|
731
803
|
}
|
|
732
804
|
else {
|
|
733
805
|
nodeStateTracker.updateTracking(currentReferenceTimestampMs);
|
|
806
|
+
if (this.tombstoneMode && nodeStateTracker.state === exports.UnreferencedState.SweepReady) {
|
|
807
|
+
const nodeType = this.runtime.getNodeType(nodeId);
|
|
808
|
+
if (nodeType === exports.GCNodeType.DataStore || nodeType === exports.GCNodeType.Blob) {
|
|
809
|
+
this.tombstones.push(nodeId);
|
|
810
|
+
}
|
|
811
|
+
}
|
|
734
812
|
}
|
|
735
813
|
}
|
|
736
814
|
}
|
|
@@ -1016,12 +1094,17 @@ class GarbageCollector {
|
|
|
1016
1094
|
}
|
|
1017
1095
|
exports.GarbageCollector = GarbageCollector;
|
|
1018
1096
|
/**
|
|
1019
|
-
* Gets the garbage collection
|
|
1020
|
-
* Merge the GC state from all such blobs
|
|
1097
|
+
* Gets the garbage collection data from the given snapshot tree. It contains GC state and tombstone state.
|
|
1098
|
+
* The GC state may be written into multiple blobs. Merge the GC state from all such blobs into one.
|
|
1021
1099
|
*/
|
|
1022
|
-
async function
|
|
1100
|
+
async function getGCDataFromSnapshot(gcSnapshotTree, readAndParseBlob) {
|
|
1023
1101
|
let rootGCState = { gcNodes: {} };
|
|
1102
|
+
let tombstones;
|
|
1024
1103
|
for (const key of Object.keys(gcSnapshotTree.blobs)) {
|
|
1104
|
+
if (key === exports.gcTombstoneBlobKey) {
|
|
1105
|
+
tombstones = await readAndParseBlob(gcSnapshotTree.blobs[key]);
|
|
1106
|
+
continue;
|
|
1107
|
+
}
|
|
1025
1108
|
// Skip blobs that do not start with the GC prefix.
|
|
1026
1109
|
if (!key.startsWith(exports.gcBlobPrefix)) {
|
|
1027
1110
|
continue;
|
|
@@ -1035,7 +1118,7 @@ async function getGCStateFromSnapshot(gcSnapshotTree, readAndParseBlob) {
|
|
|
1035
1118
|
// Merge the GC state of this blob into the root GC state.
|
|
1036
1119
|
rootGCState = (0, garbage_collector_1.concatGarbageCollectionStates)(rootGCState, gcState);
|
|
1037
1120
|
}
|
|
1038
|
-
return rootGCState;
|
|
1121
|
+
return { gcState: rootGCState, tombstones };
|
|
1039
1122
|
}
|
|
1040
1123
|
function generateSortedGCState(gcState) {
|
|
1041
1124
|
const sortableArray = Object.entries(gcState.gcNodes);
|