@fluidframework/container-runtime 2.0.0-internal.3.0.1 → 2.0.0-internal.3.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/.eslintrc.js +19 -19
- package/.mocharc.js +2 -2
- package/api-extractor.json +2 -2
- package/dist/batchTracker.d.ts.map +1 -1
- package/dist/batchTracker.js +2 -1
- package/dist/batchTracker.js.map +1 -1
- package/dist/blobManager.d.ts +9 -2
- package/dist/blobManager.d.ts.map +1 -1
- package/dist/blobManager.js +80 -33
- package/dist/blobManager.js.map +1 -1
- package/dist/connectionTelemetry.d.ts.map +1 -1
- package/dist/connectionTelemetry.js +11 -9
- package/dist/connectionTelemetry.js.map +1 -1
- package/dist/containerHandleContext.d.ts.map +1 -1
- package/dist/containerHandleContext.js +3 -1
- package/dist/containerHandleContext.js.map +1 -1
- package/dist/containerRuntime.d.ts +10 -0
- package/dist/containerRuntime.d.ts.map +1 -1
- package/dist/containerRuntime.js +140 -72
- package/dist/containerRuntime.js.map +1 -1
- package/dist/dataStore.d.ts.map +1 -1
- package/dist/dataStore.js +11 -9
- package/dist/dataStore.js.map +1 -1
- package/dist/dataStoreContext.d.ts +18 -1
- package/dist/dataStoreContext.d.ts.map +1 -1
- package/dist/dataStoreContext.js +66 -15
- package/dist/dataStoreContext.js.map +1 -1
- package/dist/dataStoreContexts.d.ts.map +1 -1
- package/dist/dataStoreContexts.js +7 -3
- package/dist/dataStoreContexts.js.map +1 -1
- package/dist/dataStoreRegistry.d.ts.map +1 -1
- package/dist/dataStoreRegistry.js +3 -1
- package/dist/dataStoreRegistry.js.map +1 -1
- package/dist/dataStores.d.ts +26 -1
- package/dist/dataStores.d.ts.map +1 -1
- package/dist/dataStores.js +103 -18
- package/dist/dataStores.js.map +1 -1
- package/dist/deltaScheduler.d.ts.map +1 -1
- package/dist/deltaScheduler.js +8 -3
- package/dist/deltaScheduler.js.map +1 -1
- package/dist/garbageCollection.d.ts +34 -14
- package/dist/garbageCollection.d.ts.map +1 -1
- package/dist/garbageCollection.js +188 -93
- package/dist/garbageCollection.js.map +1 -1
- package/dist/garbageCollectionConstants.d.ts +3 -0
- package/dist/garbageCollectionConstants.d.ts.map +1 -1
- package/dist/garbageCollectionConstants.js +6 -1
- package/dist/garbageCollectionConstants.js.map +1 -1
- package/dist/garbageCollectionHelpers.d.ts +26 -0
- package/dist/garbageCollectionHelpers.d.ts.map +1 -0
- package/dist/garbageCollectionHelpers.js +45 -0
- package/dist/garbageCollectionHelpers.js.map +1 -0
- package/dist/gcSweepReadyUsageDetection.d.ts +5 -5
- package/dist/gcSweepReadyUsageDetection.d.ts.map +1 -1
- package/dist/gcSweepReadyUsageDetection.js +14 -10
- package/dist/gcSweepReadyUsageDetection.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/opLifecycle/batchManager.d.ts +5 -5
- package/dist/opLifecycle/batchManager.d.ts.map +1 -1
- package/dist/opLifecycle/batchManager.js +19 -12
- package/dist/opLifecycle/batchManager.js.map +1 -1
- package/dist/opLifecycle/definitions.d.ts.map +1 -1
- package/dist/opLifecycle/definitions.js.map +1 -1
- package/dist/opLifecycle/index.d.ts.map +1 -1
- package/dist/opLifecycle/index.js.map +1 -1
- package/dist/opLifecycle/opCompressor.d.ts.map +1 -1
- package/dist/opLifecycle/opCompressor.js.map +1 -1
- package/dist/opLifecycle/opDecompressor.d.ts.map +1 -1
- package/dist/opLifecycle/opDecompressor.js +5 -2
- package/dist/opLifecycle/opDecompressor.js.map +1 -1
- package/dist/opLifecycle/opSplitter.d.ts.map +1 -1
- package/dist/opLifecycle/opSplitter.js +4 -1
- package/dist/opLifecycle/opSplitter.js.map +1 -1
- package/dist/opLifecycle/outbox.d.ts.map +1 -1
- package/dist/opLifecycle/outbox.js +19 -17
- package/dist/opLifecycle/outbox.js.map +1 -1
- package/dist/opLifecycle/remoteMessageProcessor.d.ts.map +1 -1
- package/dist/opLifecycle/remoteMessageProcessor.js.map +1 -1
- package/dist/opProperties.d.ts.map +1 -1
- package/dist/opProperties.js +1 -3
- package/dist/opProperties.js.map +1 -1
- package/dist/orderedClientElection.d.ts.map +1 -1
- package/dist/orderedClientElection.js +10 -4
- package/dist/orderedClientElection.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/pendingStateManager.d.ts +7 -0
- package/dist/pendingStateManager.d.ts.map +1 -1
- package/dist/pendingStateManager.js +7 -4
- package/dist/pendingStateManager.js.map +1 -1
- package/dist/runWhileConnectedCoordinator.d.ts.map +1 -1
- package/dist/runWhileConnectedCoordinator.js.map +1 -1
- package/dist/runningSummarizer.d.ts.map +1 -1
- package/dist/runningSummarizer.js +34 -21
- package/dist/runningSummarizer.js.map +1 -1
- package/dist/scheduleManager.d.ts.map +1 -1
- package/dist/scheduleManager.js +3 -2
- package/dist/scheduleManager.js.map +1 -1
- package/dist/serializedSnapshotStorage.d.ts +2 -2
- package/dist/serializedSnapshotStorage.d.ts.map +1 -1
- package/dist/serializedSnapshotStorage.js +5 -3
- package/dist/serializedSnapshotStorage.js.map +1 -1
- package/dist/summarizer.d.ts +2 -2
- package/dist/summarizer.d.ts.map +1 -1
- package/dist/summarizer.js +37 -17
- package/dist/summarizer.js.map +1 -1
- package/dist/summarizerClientElection.d.ts.map +1 -1
- package/dist/summarizerClientElection.js.map +1 -1
- package/dist/summarizerHandle.d.ts.map +1 -1
- package/dist/summarizerHandle.js.map +1 -1
- package/dist/summarizerHeuristics.d.ts.map +1 -1
- package/dist/summarizerHeuristics.js +6 -9
- package/dist/summarizerHeuristics.js.map +1 -1
- package/dist/summarizerTypes.d.ts +21 -21
- package/dist/summarizerTypes.d.ts.map +1 -1
- package/dist/summarizerTypes.js.map +1 -1
- package/dist/summaryCollection.d.ts.map +1 -1
- package/dist/summaryCollection.js +18 -8
- package/dist/summaryCollection.js.map +1 -1
- package/dist/summaryFormat.d.ts +22 -0
- package/dist/summaryFormat.d.ts.map +1 -1
- package/dist/summaryFormat.js +18 -10
- package/dist/summaryFormat.js.map +1 -1
- package/dist/summaryGenerator.d.ts.map +1 -1
- package/dist/summaryGenerator.js +34 -15
- package/dist/summaryGenerator.js.map +1 -1
- package/dist/summaryManager.d.ts.map +1 -1
- package/dist/summaryManager.js +21 -9
- package/dist/summaryManager.js.map +1 -1
- package/dist/throttler.d.ts +2 -2
- package/dist/throttler.d.ts.map +1 -1
- package/dist/throttler.js +4 -4
- package/dist/throttler.js.map +1 -1
- package/garbageCollection.md +15 -2
- package/lib/batchTracker.d.ts.map +1 -1
- package/lib/batchTracker.js +2 -1
- package/lib/batchTracker.js.map +1 -1
- package/lib/blobManager.d.ts +9 -2
- package/lib/blobManager.d.ts.map +1 -1
- package/lib/blobManager.js +82 -35
- package/lib/blobManager.js.map +1 -1
- package/lib/connectionTelemetry.d.ts.map +1 -1
- package/lib/connectionTelemetry.js +11 -9
- package/lib/connectionTelemetry.js.map +1 -1
- package/lib/containerHandleContext.d.ts.map +1 -1
- package/lib/containerHandleContext.js +3 -1
- package/lib/containerHandleContext.js.map +1 -1
- package/lib/containerRuntime.d.ts +10 -0
- package/lib/containerRuntime.d.ts.map +1 -1
- package/lib/containerRuntime.js +146 -78
- package/lib/containerRuntime.js.map +1 -1
- package/lib/dataStore.d.ts.map +1 -1
- package/lib/dataStore.js +11 -9
- package/lib/dataStore.js.map +1 -1
- package/lib/dataStoreContext.d.ts +18 -1
- package/lib/dataStoreContext.d.ts.map +1 -1
- package/lib/dataStoreContext.js +68 -17
- package/lib/dataStoreContext.js.map +1 -1
- package/lib/dataStoreContexts.d.ts.map +1 -1
- package/lib/dataStoreContexts.js +7 -3
- package/lib/dataStoreContexts.js.map +1 -1
- package/lib/dataStoreRegistry.d.ts.map +1 -1
- package/lib/dataStoreRegistry.js +3 -1
- package/lib/dataStoreRegistry.js.map +1 -1
- package/lib/dataStores.d.ts +26 -1
- package/lib/dataStores.d.ts.map +1 -1
- package/lib/dataStores.js +109 -24
- package/lib/dataStores.js.map +1 -1
- package/lib/deltaScheduler.d.ts.map +1 -1
- package/lib/deltaScheduler.js +9 -4
- package/lib/deltaScheduler.js.map +1 -1
- package/lib/garbageCollection.d.ts +34 -14
- package/lib/garbageCollection.d.ts.map +1 -1
- package/lib/garbageCollection.js +190 -95
- package/lib/garbageCollection.js.map +1 -1
- package/lib/garbageCollectionConstants.d.ts +3 -0
- package/lib/garbageCollectionConstants.d.ts.map +1 -1
- package/lib/garbageCollectionConstants.js +5 -0
- package/lib/garbageCollectionConstants.js.map +1 -1
- package/lib/garbageCollectionHelpers.d.ts +26 -0
- package/lib/garbageCollectionHelpers.d.ts.map +1 -0
- package/lib/garbageCollectionHelpers.js +40 -0
- package/lib/garbageCollectionHelpers.js.map +1 -0
- package/lib/gcSweepReadyUsageDetection.d.ts +5 -5
- package/lib/gcSweepReadyUsageDetection.d.ts.map +1 -1
- package/lib/gcSweepReadyUsageDetection.js +14 -10
- package/lib/gcSweepReadyUsageDetection.js.map +1 -1
- package/lib/index.d.ts +2 -2
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +1 -1
- package/lib/index.js.map +1 -1
- package/lib/opLifecycle/batchManager.d.ts +5 -5
- package/lib/opLifecycle/batchManager.d.ts.map +1 -1
- package/lib/opLifecycle/batchManager.js +19 -12
- package/lib/opLifecycle/batchManager.js.map +1 -1
- package/lib/opLifecycle/definitions.d.ts.map +1 -1
- package/lib/opLifecycle/definitions.js.map +1 -1
- package/lib/opLifecycle/index.d.ts.map +1 -1
- package/lib/opLifecycle/index.js.map +1 -1
- package/lib/opLifecycle/opCompressor.d.ts.map +1 -1
- package/lib/opLifecycle/opCompressor.js.map +1 -1
- package/lib/opLifecycle/opDecompressor.d.ts.map +1 -1
- package/lib/opLifecycle/opDecompressor.js +5 -2
- package/lib/opLifecycle/opDecompressor.js.map +1 -1
- package/lib/opLifecycle/opSplitter.d.ts.map +1 -1
- package/lib/opLifecycle/opSplitter.js +5 -2
- package/lib/opLifecycle/opSplitter.js.map +1 -1
- package/lib/opLifecycle/outbox.d.ts.map +1 -1
- package/lib/opLifecycle/outbox.js +19 -17
- package/lib/opLifecycle/outbox.js.map +1 -1
- package/lib/opLifecycle/remoteMessageProcessor.d.ts.map +1 -1
- package/lib/opLifecycle/remoteMessageProcessor.js.map +1 -1
- package/lib/opProperties.d.ts.map +1 -1
- package/lib/opProperties.js +1 -3
- package/lib/opProperties.js.map +1 -1
- package/lib/orderedClientElection.d.ts.map +1 -1
- package/lib/orderedClientElection.js +10 -4
- package/lib/orderedClientElection.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/pendingStateManager.d.ts +7 -0
- package/lib/pendingStateManager.d.ts.map +1 -1
- package/lib/pendingStateManager.js +7 -4
- package/lib/pendingStateManager.js.map +1 -1
- package/lib/runWhileConnectedCoordinator.d.ts.map +1 -1
- package/lib/runWhileConnectedCoordinator.js.map +1 -1
- package/lib/runningSummarizer.d.ts.map +1 -1
- package/lib/runningSummarizer.js +35 -22
- package/lib/runningSummarizer.js.map +1 -1
- package/lib/scheduleManager.d.ts.map +1 -1
- package/lib/scheduleManager.js +3 -2
- package/lib/scheduleManager.js.map +1 -1
- package/lib/serializedSnapshotStorage.d.ts +2 -2
- package/lib/serializedSnapshotStorage.d.ts.map +1 -1
- package/lib/serializedSnapshotStorage.js +5 -3
- package/lib/serializedSnapshotStorage.js.map +1 -1
- package/lib/summarizer.d.ts +2 -2
- package/lib/summarizer.d.ts.map +1 -1
- package/lib/summarizer.js +37 -17
- package/lib/summarizer.js.map +1 -1
- package/lib/summarizerClientElection.d.ts.map +1 -1
- package/lib/summarizerClientElection.js.map +1 -1
- package/lib/summarizerHandle.d.ts.map +1 -1
- package/lib/summarizerHandle.js.map +1 -1
- package/lib/summarizerHeuristics.d.ts.map +1 -1
- package/lib/summarizerHeuristics.js +6 -9
- package/lib/summarizerHeuristics.js.map +1 -1
- package/lib/summarizerTypes.d.ts +21 -21
- package/lib/summarizerTypes.d.ts.map +1 -1
- package/lib/summarizerTypes.js.map +1 -1
- package/lib/summaryCollection.d.ts.map +1 -1
- package/lib/summaryCollection.js +18 -8
- package/lib/summaryCollection.js.map +1 -1
- package/lib/summaryFormat.d.ts +22 -0
- package/lib/summaryFormat.d.ts.map +1 -1
- package/lib/summaryFormat.js +20 -12
- package/lib/summaryFormat.js.map +1 -1
- package/lib/summaryGenerator.d.ts.map +1 -1
- package/lib/summaryGenerator.js +34 -15
- package/lib/summaryGenerator.js.map +1 -1
- package/lib/summaryManager.d.ts.map +1 -1
- package/lib/summaryManager.js +21 -9
- package/lib/summaryManager.js.map +1 -1
- package/lib/throttler.d.ts +2 -2
- package/lib/throttler.d.ts.map +1 -1
- package/lib/throttler.js +4 -4
- package/lib/throttler.js.map +1 -1
- package/package.json +121 -149
- package/prettier.config.cjs +1 -1
- package/src/batchTracker.ts +54 -49
- package/src/blobManager.ts +793 -672
- package/src/connectionTelemetry.ts +280 -249
- package/src/containerHandleContext.ts +27 -29
- package/src/containerRuntime.ts +3168 -2940
- package/src/dataStore.ts +172 -159
- package/src/dataStoreContext.ts +1098 -996
- package/src/dataStoreContexts.ts +178 -161
- package/src/dataStoreRegistry.ts +25 -20
- package/src/dataStores.ts +884 -728
- package/src/deltaScheduler.ts +158 -150
- package/src/garbageCollection.ts +1883 -1692
- package/src/garbageCollectionConstants.ts +6 -0
- package/src/garbageCollectionHelpers.ts +61 -0
- package/src/gcSweepReadyUsageDetection.ts +89 -83
- package/src/index.ts +67 -66
- package/src/opLifecycle/README.md +152 -0
- package/src/opLifecycle/batchManager.ts +145 -141
- package/src/opLifecycle/definitions.ts +29 -29
- package/src/opLifecycle/index.ts +5 -5
- package/src/opLifecycle/opCompressor.ts +54 -53
- package/src/opLifecycle/opDecompressor.ts +100 -81
- package/src/opLifecycle/opSplitter.ts +214 -188
- package/src/opLifecycle/outbox.ts +204 -194
- package/src/opLifecycle/remoteMessageProcessor.ts +62 -62
- package/src/opProperties.ts +11 -9
- package/src/orderedClientElection.ts +489 -457
- package/src/packageVersion.ts +1 -1
- package/src/pendingStateManager.ts +384 -338
- package/src/runWhileConnectedCoordinator.ts +78 -71
- package/src/runningSummarizer.ts +619 -581
- package/src/scheduleManager.ts +299 -269
- package/src/serializedSnapshotStorage.ts +126 -112
- package/src/summarizer.ts +417 -381
- package/src/summarizerClientElection.ts +107 -100
- package/src/summarizerHandle.ts +11 -9
- package/src/summarizerHeuristics.ts +183 -186
- package/src/summarizerTypes.ts +344 -330
- package/src/summaryCollection.ts +378 -349
- package/src/summaryFormat.ts +170 -126
- package/src/summaryGenerator.ts +465 -406
- package/src/summaryManager.ts +377 -348
- package/src/throttler.ts +131 -122
- package/tsconfig.esnext.json +6 -6
- package/tsconfig.json +9 -13
- package/dist/garbageCollectionTombstoneUtils.d.ts +0 -14
- package/dist/garbageCollectionTombstoneUtils.d.ts.map +0 -1
- package/dist/garbageCollectionTombstoneUtils.js +0 -23
- package/dist/garbageCollectionTombstoneUtils.js.map +0 -1
- package/lib/garbageCollectionTombstoneUtils.d.ts +0 -14
- package/lib/garbageCollectionTombstoneUtils.d.ts.map +0 -1
- package/lib/garbageCollectionTombstoneUtils.js +0 -19
- package/lib/garbageCollectionTombstoneUtils.js.map +0 -1
- package/src/garbageCollectionTombstoneUtils.ts +0 -28
|
@@ -26,7 +26,7 @@ const telemetry_utils_1 = require("@fluidframework/telemetry-utils");
|
|
|
26
26
|
const containerRuntime_1 = require("./containerRuntime");
|
|
27
27
|
const dataStores_1 = require("./dataStores");
|
|
28
28
|
const garbageCollectionConstants_1 = require("./garbageCollectionConstants");
|
|
29
|
-
const
|
|
29
|
+
const garbageCollectionHelpers_1 = require("./garbageCollectionHelpers");
|
|
30
30
|
const gcSweepReadyUsageDetection_1 = require("./gcSweepReadyUsageDetection");
|
|
31
31
|
const summaryFormat_1 = require("./summaryFormat");
|
|
32
32
|
/** The types of GC nodes in the GC reference graph. */
|
|
@@ -128,15 +128,15 @@ exports.UnreferencedStateTracker = UnreferencedStateTracker;
|
|
|
128
128
|
* Graph - all nodes with their respective routes
|
|
129
129
|
*
|
|
130
130
|
* ```
|
|
131
|
-
*
|
|
131
|
+
* GC Graph
|
|
132
132
|
*
|
|
133
|
-
*
|
|
134
|
-
*
|
|
135
|
-
*
|
|
136
|
-
*
|
|
137
|
-
*
|
|
138
|
-
*
|
|
139
|
-
* NodeId = "dds1"
|
|
133
|
+
* Node
|
|
134
|
+
* NodeId = "datastore1"
|
|
135
|
+
* / \\
|
|
136
|
+
* OutboundRoute OutboundRoute
|
|
137
|
+
* / \\
|
|
138
|
+
* Node Node
|
|
139
|
+
* NodeId = "dds1" NodeId = "dds2"
|
|
140
140
|
* ```
|
|
141
141
|
*/
|
|
142
142
|
class GarbageCollector {
|
|
@@ -168,10 +168,14 @@ class GarbageCollector {
|
|
|
168
168
|
const baseSnapshot = createParams.baseSnapshot;
|
|
169
169
|
const metadata = createParams.metadata;
|
|
170
170
|
const readAndParseBlob = createParams.readAndParseBlob;
|
|
171
|
-
this.mc = (0, telemetry_utils_1.loggerToMonitoringContext)(telemetry_utils_1.ChildLogger.create(createParams.baseLogger, "GarbageCollector", {
|
|
171
|
+
this.mc = (0, telemetry_utils_1.loggerToMonitoringContext)(telemetry_utils_1.ChildLogger.create(createParams.baseLogger, "GarbageCollector", {
|
|
172
|
+
all: { completedGCRuns: () => this.completedRuns },
|
|
173
|
+
}));
|
|
172
174
|
// If version upgrade is not enabled, fall back to the stable GC version.
|
|
173
175
|
this.currentGCVersion =
|
|
174
|
-
this.mc.config.getBoolean(garbageCollectionConstants_1.gcVersionUpgradeToV2Key) === true
|
|
176
|
+
this.mc.config.getBoolean(garbageCollectionConstants_1.gcVersionUpgradeToV2Key) === true
|
|
177
|
+
? garbageCollectionConstants_1.currentGCVersion
|
|
178
|
+
: garbageCollectionConstants_1.stableGCVersion;
|
|
175
179
|
this.sweepReadyUsageHandler = new gcSweepReadyUsageDetection_1.SweepReadyUsageDetectionHandler(createParams.getContainerDiagnosticId(), this.mc, this.runtime.closeFn);
|
|
176
180
|
let prevSummaryGCVersion;
|
|
177
181
|
/**
|
|
@@ -185,8 +189,8 @@ class GarbageCollector {
|
|
|
185
189
|
function computeSweepTimeout(sessionExpiryTimeoutMs) {
|
|
186
190
|
const maxSnapshotCacheExpiryMs = 5 * garbageCollectionConstants_1.oneDayMs;
|
|
187
191
|
const bufferMs = garbageCollectionConstants_1.oneDayMs;
|
|
188
|
-
return sessionExpiryTimeoutMs &&
|
|
189
|
-
|
|
192
|
+
return (sessionExpiryTimeoutMs &&
|
|
193
|
+
sessionExpiryTimeoutMs + maxSnapshotCacheExpiryMs + bufferMs);
|
|
190
194
|
}
|
|
191
195
|
/**
|
|
192
196
|
* The following GC state is enabled during container creation and cannot be changed throughout its lifetime:
|
|
@@ -204,6 +208,7 @@ class GarbageCollector {
|
|
|
204
208
|
this.sessionExpiryTimeoutMs = metadata === null || metadata === void 0 ? void 0 : metadata.sessionExpiryTimeoutMs;
|
|
205
209
|
this.sweepTimeoutMs =
|
|
206
210
|
(_b = metadata === null || metadata === void 0 ? void 0 : metadata.sweepTimeoutMs) !== null && _b !== void 0 ? _b : computeSweepTimeout(this.sessionExpiryTimeoutMs); // Backfill old documents that didn't persist this
|
|
211
|
+
this.persistedGcFeatureMatrix = metadata === null || metadata === void 0 ? void 0 : metadata.gcFeatureMatrix;
|
|
207
212
|
}
|
|
208
213
|
else {
|
|
209
214
|
// Sweep should not be enabled without enabling GC mark phase. We could silently disable sweep in this
|
|
@@ -220,17 +225,25 @@ class GarbageCollector {
|
|
|
220
225
|
this.sweepEnabled = this.gcOptions.sweepAllowed === true;
|
|
221
226
|
// Set the Session Expiry only if the flag is enabled and GC is enabled.
|
|
222
227
|
if (this.mc.config.getBoolean(garbageCollectionConstants_1.runSessionExpiryKey) && this.gcEnabled) {
|
|
223
|
-
this.sessionExpiryTimeoutMs =
|
|
228
|
+
this.sessionExpiryTimeoutMs =
|
|
229
|
+
(_c = this.gcOptions.sessionExpiryTimeoutMs) !== null && _c !== void 0 ? _c : garbageCollectionConstants_1.defaultSessionExpiryDurationMs;
|
|
224
230
|
}
|
|
225
231
|
this.sweepTimeoutMs =
|
|
226
232
|
testOverrideSweepTimeoutMs !== null && testOverrideSweepTimeoutMs !== void 0 ? testOverrideSweepTimeoutMs : computeSweepTimeout(this.sessionExpiryTimeoutMs);
|
|
233
|
+
if (this.gcOptions[garbageCollectionConstants_1.gcTombstoneGenerationOptionName] !== undefined) {
|
|
234
|
+
this.persistedGcFeatureMatrix = {
|
|
235
|
+
tombstoneGeneration: this.gcOptions[garbageCollectionConstants_1.gcTombstoneGenerationOptionName],
|
|
236
|
+
};
|
|
237
|
+
}
|
|
227
238
|
}
|
|
228
239
|
// If session expiry is enabled, we need to close the container when the session expiry timeout expires.
|
|
229
240
|
if (this.sessionExpiryTimeoutMs !== undefined) {
|
|
230
241
|
// If Test Override config is set, override Session Expiry timeout.
|
|
231
242
|
const overrideSessionExpiryTimeoutMs = this.mc.config.getNumber("Fluid.GarbageCollection.TestOverride.SessionExpiryMs");
|
|
232
243
|
const timeoutMs = overrideSessionExpiryTimeoutMs !== null && overrideSessionExpiryTimeoutMs !== void 0 ? overrideSessionExpiryTimeoutMs : this.sessionExpiryTimeoutMs;
|
|
233
|
-
this.sessionExpiryTimer = new common_utils_1.Timer(timeoutMs, () => {
|
|
244
|
+
this.sessionExpiryTimer = new common_utils_1.Timer(timeoutMs, () => {
|
|
245
|
+
this.runtime.closeFn(new container_utils_1.ClientSessionExpiredError(`Client session expired.`, timeoutMs));
|
|
246
|
+
});
|
|
234
247
|
this.sessionExpiryTimer.start();
|
|
235
248
|
}
|
|
236
249
|
// For existing document, the latest summary is the one that we loaded from. So, use its GC version as the
|
|
@@ -245,11 +258,12 @@ class GarbageCollector {
|
|
|
245
258
|
*
|
|
246
259
|
* These conditions can be overridden via runGCKey feature flag.
|
|
247
260
|
*/
|
|
248
|
-
this.shouldRunGC =
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
261
|
+
this.shouldRunGC =
|
|
262
|
+
(_d = this.mc.config.getBoolean(garbageCollectionConstants_1.runGCKey)) !== null && _d !== void 0 ? _d :
|
|
263
|
+
// GC must be enabled for the document.
|
|
264
|
+
(this.gcEnabled &&
|
|
265
|
+
// GC must not be disabled via GC options.
|
|
266
|
+
!this.gcOptions.disableGC);
|
|
253
267
|
/**
|
|
254
268
|
* Whether sweep should run or not. The following conditions have to be met to run sweep:
|
|
255
269
|
*
|
|
@@ -261,21 +275,24 @@ class GarbageCollector {
|
|
|
261
275
|
* feature flag.
|
|
262
276
|
*/
|
|
263
277
|
this.shouldRunSweep =
|
|
264
|
-
this.shouldRunGC
|
|
265
|
-
|
|
266
|
-
|
|
278
|
+
this.shouldRunGC &&
|
|
279
|
+
this.sweepTimeoutMs !== undefined &&
|
|
280
|
+
((_e = this.mc.config.getBoolean(garbageCollectionConstants_1.runSweepKey)) !== null && _e !== void 0 ? _e : this.sweepEnabled);
|
|
267
281
|
this.trackGCState = this.mc.config.getBoolean(garbageCollectionConstants_1.trackGCStateKey) === true;
|
|
268
282
|
// Override inactive timeout if test config or gc options to override it is set.
|
|
269
|
-
this.inactiveTimeoutMs =
|
|
283
|
+
this.inactiveTimeoutMs =
|
|
284
|
+
(_g = (_f = this.mc.config.getNumber("Fluid.GarbageCollection.TestOverride.InactiveTimeoutMs")) !== null && _f !== void 0 ? _f : this.gcOptions.inactiveTimeoutMs) !== null && _g !== void 0 ? _g : garbageCollectionConstants_1.defaultInactiveTimeoutMs;
|
|
270
285
|
// Inactive timeout must be greater than sweep timeout since a node goes from active -> inactive -> sweep ready.
|
|
271
286
|
if (this.sweepTimeoutMs !== undefined && this.inactiveTimeoutMs > this.sweepTimeoutMs) {
|
|
272
287
|
throw new container_utils_1.UsageError("inactive timeout should not be greater than the sweep timeout");
|
|
273
288
|
}
|
|
274
289
|
// Whether we are running in test mode. In this mode, unreferenced nodes are immediately deleted.
|
|
275
|
-
this.testMode =
|
|
290
|
+
this.testMode =
|
|
291
|
+
(_h = this.mc.config.getBoolean(garbageCollectionConstants_1.gcTestModeKey)) !== null && _h !== void 0 ? _h : this.gcOptions.runGCInTestMode === true;
|
|
276
292
|
// Whether we are running in tombstone mode. This is enabled by default if sweep won't run. It can be disabled
|
|
277
293
|
// via feature flags.
|
|
278
|
-
this.tombstoneMode =
|
|
294
|
+
this.tombstoneMode =
|
|
295
|
+
!this.shouldRunSweep && this.mc.config.getBoolean(garbageCollectionConstants_1.disableTombstoneKey) !== true;
|
|
279
296
|
// If GC ran in the container that generated the base snapshot, it will have a GC tree.
|
|
280
297
|
this.wasGCRunInLatestSummary = (baseSnapshot === null || baseSnapshot === void 0 ? void 0 : baseSnapshot.trees[runtime_definitions_1.gcTreeKey]) !== undefined;
|
|
281
298
|
// Get the GC data from the base snapshot. Use LazyPromise because we only want to do this once since it
|
|
@@ -294,7 +311,9 @@ class GarbageCollector {
|
|
|
294
311
|
// back-compat - Older documents will have the GC blobs in each data store's summary tree. Get them and
|
|
295
312
|
// consolidate into IGarbageCollectionState format.
|
|
296
313
|
// Add a node for the root node that is not present in older snapshot format.
|
|
297
|
-
const gcState = {
|
|
314
|
+
const gcState = {
|
|
315
|
+
gcNodes: { "/": { outboundRoutes: [] } },
|
|
316
|
+
};
|
|
298
317
|
const dataStoreSnapshotTree = (0, dataStores_1.getSummaryForDatastores)(baseSnapshot, metadata);
|
|
299
318
|
(0, common_utils_1.assert)(dataStoreSnapshotTree !== undefined, 0x2a8 /* "Expected data store snapshot tree in base snapshot" */);
|
|
300
319
|
for (const [dsId, dsSnapshotTree] of Object.entries(dataStoreSnapshotTree.trees)) {
|
|
@@ -318,10 +337,13 @@ class GarbageCollector {
|
|
|
318
337
|
// Prefix the data store id to the GC node ids to make them relative to the root from being
|
|
319
338
|
// relative to the data store. Similar to how its done in DataStore::getGCData.
|
|
320
339
|
const rootId = id === "/" ? dsRootId : `${dsRootId}${id}`;
|
|
321
|
-
gcState.gcNodes[rootId] = {
|
|
340
|
+
gcState.gcNodes[rootId] = {
|
|
341
|
+
outboundRoutes: Array.from(outboundRoutes),
|
|
342
|
+
};
|
|
322
343
|
}
|
|
323
344
|
(0, common_utils_1.assert)(gcState.gcNodes[dsRootId] !== undefined, 0x2a9 /* GC nodes for data store not in GC blob */);
|
|
324
|
-
gcState.gcNodes[dsRootId].unreferencedTimestampMs =
|
|
345
|
+
gcState.gcNodes[dsRootId].unreferencedTimestampMs =
|
|
346
|
+
gcSummaryDetails.unrefTimestamp;
|
|
325
347
|
}
|
|
326
348
|
// If there is only one node (root node just added above), either GC is disabled or we are loading from
|
|
327
349
|
// the first summary generated by detached container. In both cases, GC was not run - return undefined.
|
|
@@ -415,8 +437,8 @@ class GarbageCollector {
|
|
|
415
437
|
* 4.2. This client's latest summary was updated from a snapshot that has a different GC version.
|
|
416
438
|
*/
|
|
417
439
|
get summaryStateNeedsReset() {
|
|
418
|
-
return this.gcStateNeedsReset ||
|
|
419
|
-
(this.shouldRunGC && this.latestSummaryGCVersion !== this.currentGCVersion);
|
|
440
|
+
return (this.gcStateNeedsReset ||
|
|
441
|
+
(this.shouldRunGC && this.latestSummaryGCVersion !== this.currentGCVersion));
|
|
420
442
|
}
|
|
421
443
|
/**
|
|
422
444
|
* Tells whether the GC state needs to be reset. This can happen under 3 conditions:
|
|
@@ -431,7 +453,7 @@ class GarbageCollector {
|
|
|
431
453
|
*
|
|
432
454
|
* Note that the state will be reset only once for the first summary generated after this returns true. After that,
|
|
433
455
|
* this will return false.
|
|
434
|
-
|
|
456
|
+
*/
|
|
435
457
|
get gcStateNeedsReset() {
|
|
436
458
|
return this.wasGCRunInLatestSummary !== this.shouldRunGC;
|
|
437
459
|
}
|
|
@@ -462,6 +484,7 @@ class GarbageCollector {
|
|
|
462
484
|
// If running in tombstone mode, initialize the tombstone state from the snapshot. Also, notify the runtime of
|
|
463
485
|
// tombstone routes.
|
|
464
486
|
if (this.tombstoneMode && baseSnapshotData.tombstones !== undefined) {
|
|
487
|
+
// Create a copy since we are writing from a source we don't control
|
|
465
488
|
this.tombstones = Array.from(baseSnapshotData.tombstones);
|
|
466
489
|
this.runtime.updateTombstonedRoutes(this.tombstones);
|
|
467
490
|
}
|
|
@@ -489,7 +512,6 @@ class GarbageCollector {
|
|
|
489
512
|
for (const [, nodeStateTracker] of this.unreferencedNodesState) {
|
|
490
513
|
nodeStateTracker.stopTracking();
|
|
491
514
|
}
|
|
492
|
-
;
|
|
493
515
|
this.unreferencedNodesState.clear();
|
|
494
516
|
// If running sweep, the tombstone state represents the list of nodes that have been deleted during sweep.
|
|
495
517
|
// If running in tombstone mode, the tombstone state represents the list of nodes that have been marked as
|
|
@@ -497,7 +519,9 @@ class GarbageCollector {
|
|
|
497
519
|
// If this call is because we are refreshing from a snapshot due to an ack, it is likely that the GC state
|
|
498
520
|
// in the snapshot is newer than this client's. And so, the deleted / tombstone nodes need to be updated.
|
|
499
521
|
if (this.shouldRunSweep) {
|
|
500
|
-
const snapshotDeletedNodes = (snapshotData === null || snapshotData === void 0 ? void 0 : snapshotData.
|
|
522
|
+
const snapshotDeletedNodes = (snapshotData === null || snapshotData === void 0 ? void 0 : snapshotData.deletedNodes)
|
|
523
|
+
? new Set(snapshotData.deletedNodes)
|
|
524
|
+
: undefined;
|
|
501
525
|
// If the snapshot contains deleted nodes that are not yet deleted by this client, ask the runtime to
|
|
502
526
|
// delete them.
|
|
503
527
|
if (snapshotDeletedNodes !== undefined) {
|
|
@@ -575,7 +599,9 @@ class GarbageCollector {
|
|
|
575
599
|
var _a;
|
|
576
600
|
const fullGC = (_a = options.fullGC) !== null && _a !== void 0 ? _a : (this.gcOptions.runFullGC === true || this.summaryStateNeedsReset);
|
|
577
601
|
const logger = options.logger
|
|
578
|
-
? telemetry_utils_1.ChildLogger.create(options.logger, undefined, {
|
|
602
|
+
? telemetry_utils_1.ChildLogger.create(options.logger, undefined, {
|
|
603
|
+
all: { completedGCRuns: () => this.completedRuns },
|
|
604
|
+
})
|
|
579
605
|
: this.mc.logger;
|
|
580
606
|
/**
|
|
581
607
|
* If there is no current reference timestamp, skip running GC. We need the current timestamp to track
|
|
@@ -615,26 +641,29 @@ class GarbageCollector {
|
|
|
615
641
|
// Generate statistics from the current run. This is done before updating the current state because it
|
|
616
642
|
// generates some of its data based on previous state of the system.
|
|
617
643
|
const gcStats = this.generateStats(gcResult);
|
|
618
|
-
// Update the state
|
|
619
|
-
|
|
620
|
-
this.updateStateSinceLastRun(gcData, logger);
|
|
621
|
-
// Update the current state and update the runtime of all routes or ids that used as per the GC run.
|
|
622
|
-
this.updateCurrentState(gcData, gcResult, currentReferenceTimestampMs);
|
|
644
|
+
// Update the current mark state and update the runtime of all used routes or ids that used as per the GC run.
|
|
645
|
+
const sweepReadyNodes = this.updateMarkPhase(gcData, gcResult, currentReferenceTimestampMs, logger);
|
|
623
646
|
this.runtime.updateUsedRoutes(gcResult.referencedNodeIds);
|
|
624
647
|
// Log events for objects that are ready to be deleted by sweep. When we have sweep enabled, we will
|
|
625
648
|
// delete these objects here instead.
|
|
626
649
|
this.logSweepEvents(logger, currentReferenceTimestampMs);
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
650
|
+
let updatedGCData = gcData;
|
|
651
|
+
if (this.shouldRunSweep) {
|
|
652
|
+
updatedGCData = this.runSweepPhase(sweepReadyNodes, gcData);
|
|
653
|
+
}
|
|
654
|
+
else if (this.testMode) {
|
|
655
|
+
// If we are running in GC test mode, delete objects for unused routes. This enables testing scenarios
|
|
656
|
+
// involving access to deleted data.
|
|
630
657
|
this.runtime.updateUnusedRoutes(gcResult.deletedNodeIds);
|
|
631
658
|
}
|
|
632
659
|
else if (this.tombstoneMode) {
|
|
660
|
+
this.tombstones = sweepReadyNodes;
|
|
633
661
|
// If we are running in GC tombstone mode, update tombstoned routes. This enables testing scenarios
|
|
634
662
|
// involving access to "deleted" data without actually deleting the data from summaries.
|
|
635
663
|
// Note: we will not tombstone in test mode.
|
|
636
664
|
this.runtime.updateTombstonedRoutes(this.tombstones);
|
|
637
665
|
}
|
|
666
|
+
this.gcDataFromLastRun = (0, garbage_collector_1.cloneGCData)(updatedGCData);
|
|
638
667
|
// Log pending unreferenced events such as a node being used after inactive. This is done after GC runs and
|
|
639
668
|
// updates its state so that we don't send false positives based on intermediate state. For example, we may get
|
|
640
669
|
// reference to an unreferenced node from another unreferenced node which means the node wasn't revived.
|
|
@@ -666,7 +695,9 @@ class GarbageCollector {
|
|
|
666
695
|
: undefined;
|
|
667
696
|
// If running in tombstone mode, serialize and write tombstones, if any.
|
|
668
697
|
const serializedTombstones = this.tombstoneMode
|
|
669
|
-
?
|
|
698
|
+
? this.tombstones.length > 0
|
|
699
|
+
? JSON.stringify(this.tombstones.sort())
|
|
700
|
+
: undefined
|
|
670
701
|
: undefined;
|
|
671
702
|
/**
|
|
672
703
|
* Incremental summary of GC data - If none of GC state, deleted nodes or tombstones changed since last summary,
|
|
@@ -675,11 +706,15 @@ class GarbageCollector {
|
|
|
675
706
|
* for each of these that did not change, write a summary handle.
|
|
676
707
|
*/
|
|
677
708
|
if (this.trackGCState) {
|
|
678
|
-
this.pendingSummaryData = {
|
|
709
|
+
this.pendingSummaryData = {
|
|
710
|
+
serializedGCState,
|
|
711
|
+
serializedTombstones,
|
|
712
|
+
serializedDeletedNodes,
|
|
713
|
+
};
|
|
679
714
|
if (trackState && !fullTree && this.latestSummaryData !== undefined) {
|
|
680
715
|
// If nothing changed since last summary, send a summary handle for the entire GC data.
|
|
681
|
-
if (this.latestSummaryData.serializedGCState === serializedGCState
|
|
682
|
-
|
|
716
|
+
if (this.latestSummaryData.serializedGCState === serializedGCState &&
|
|
717
|
+
this.latestSummaryData.serializedTombstones === serializedTombstones) {
|
|
683
718
|
const stats = (0, runtime_utils_1.mergeStats)();
|
|
684
719
|
stats.handleNodeCount++;
|
|
685
720
|
return {
|
|
@@ -722,7 +757,8 @@ class GarbageCollector {
|
|
|
722
757
|
// If tombstones exist, write a summary handle if it hasn't changed. If it has changed, write a
|
|
723
758
|
// summary blob.
|
|
724
759
|
if (serializedTombstones !== undefined) {
|
|
725
|
-
if (((_b = this.latestSummaryData) === null || _b === void 0 ? void 0 : _b.serializedTombstones) === serializedTombstones &&
|
|
760
|
+
if (((_b = this.latestSummaryData) === null || _b === void 0 ? void 0 : _b.serializedTombstones) === serializedTombstones &&
|
|
761
|
+
trackState) {
|
|
726
762
|
builder.addHandle(runtime_definitions_1.gcTombstoneBlobKey, protocol_definitions_1.SummaryType.Blob, `/${runtime_definitions_1.gcTreeKey}/${runtime_definitions_1.gcTombstoneBlobKey}`);
|
|
727
763
|
}
|
|
728
764
|
else {
|
|
@@ -734,7 +770,8 @@ class GarbageCollector {
|
|
|
734
770
|
return builder.getSummaryTree();
|
|
735
771
|
}
|
|
736
772
|
// If the deleted nodes hasn't changed, write a summary handle, else write a summary blob for it.
|
|
737
|
-
if (((_c = this.latestSummaryData) === null || _c === void 0 ? void 0 : _c.serializedDeletedNodes) === serializedDeletedNodes &&
|
|
773
|
+
if (((_c = this.latestSummaryData) === null || _c === void 0 ? void 0 : _c.serializedDeletedNodes) === serializedDeletedNodes &&
|
|
774
|
+
trackState) {
|
|
738
775
|
builder.addHandle(runtime_definitions_1.gcDeletedBlobKey, protocol_definitions_1.SummaryType.Blob, `/${runtime_definitions_1.gcTreeKey}/${runtime_definitions_1.gcDeletedBlobKey}`);
|
|
739
776
|
}
|
|
740
777
|
else {
|
|
@@ -749,6 +786,7 @@ class GarbageCollector {
|
|
|
749
786
|
* into the metadata blob. If GC is disabled, the gcFeature is 0.
|
|
750
787
|
*/
|
|
751
788
|
gcFeature: this.gcEnabled ? this.currentGCVersion : 0,
|
|
789
|
+
gcFeatureMatrix: this.persistedGcFeatureMatrix,
|
|
752
790
|
sessionExpiryTimeoutMs: this.sessionExpiryTimeoutMs,
|
|
753
791
|
sweepEnabled: this.sweepEnabled,
|
|
754
792
|
sweepTimeoutMs: this.sweepTimeoutMs,
|
|
@@ -765,7 +803,7 @@ class GarbageCollector {
|
|
|
765
803
|
* Called to refresh the latest summary state. This happens when either a pending summary is acked or a snapshot
|
|
766
804
|
* is downloaded and should be used to update the state.
|
|
767
805
|
*/
|
|
768
|
-
async refreshLatestSummary(
|
|
806
|
+
async refreshLatestSummary(proposalHandle, result, readAndParseBlob) {
|
|
769
807
|
// If the latest summary was updated and the summary was tracked, this client is the one that generated this
|
|
770
808
|
// summary. So, update wasGCRunInLatestSummary.
|
|
771
809
|
// Note that this has to be updated if GC did not run too. Otherwise, `gcStateNeedsReset` will always return
|
|
@@ -787,8 +825,8 @@ class GarbageCollector {
|
|
|
787
825
|
return;
|
|
788
826
|
}
|
|
789
827
|
// If the summary was not tracked by this client, the state should be updated from the downloaded snapshot.
|
|
790
|
-
const
|
|
791
|
-
const metadataBlobId =
|
|
828
|
+
const snapshotTree = result.snapshotTree;
|
|
829
|
+
const metadataBlobId = snapshotTree.blobs[summaryFormat_1.metadataBlobName];
|
|
792
830
|
if (metadataBlobId) {
|
|
793
831
|
const metadata = await readAndParseBlob(metadataBlobId);
|
|
794
832
|
this.latestSummaryGCVersion = (0, summaryFormat_1.getGCVersion)(metadata);
|
|
@@ -797,9 +835,13 @@ class GarbageCollector {
|
|
|
797
835
|
// to be at least one op (summary op / ack, if nothing else) if a snapshot was taken.
|
|
798
836
|
const currentReferenceTimestampMs = this.runtime.getCurrentReferenceTimestampMs();
|
|
799
837
|
if (currentReferenceTimestampMs === undefined) {
|
|
800
|
-
throw container_utils_1.DataProcessingError.create("No reference timestamp when updating GC state from snapshot", "refreshLatestSummary", undefined, {
|
|
838
|
+
throw container_utils_1.DataProcessingError.create("No reference timestamp when updating GC state from snapshot", "refreshLatestSummary", undefined, {
|
|
839
|
+
proposalHandle,
|
|
840
|
+
summaryRefSeq: result.summaryRefSeq,
|
|
841
|
+
details: JSON.stringify(this.configs),
|
|
842
|
+
});
|
|
801
843
|
}
|
|
802
|
-
const gcSnapshotTree =
|
|
844
|
+
const gcSnapshotTree = snapshotTree.trees[runtime_definitions_1.gcTreeKey];
|
|
803
845
|
// If GC ran in the container that generated this snapshot, it will have a GC tree.
|
|
804
846
|
this.wasGCRunInLatestSummary = gcSnapshotTree !== undefined;
|
|
805
847
|
let latestGCData;
|
|
@@ -854,12 +896,12 @@ class GarbageCollector {
|
|
|
854
896
|
else if (nodeType === exports.GCNodeType.Blob) {
|
|
855
897
|
eventName = "GC_Tombstone_Blob_Revived";
|
|
856
898
|
}
|
|
857
|
-
(0,
|
|
899
|
+
(0, garbageCollectionHelpers_1.sendGCUnexpectedUsageEvent)(this.mc, {
|
|
858
900
|
eventName,
|
|
859
901
|
category: "generic",
|
|
860
|
-
isSummarizerClient: this.isSummarizerClient,
|
|
861
902
|
url: (0, garbage_collector_1.trimLeadingSlashes)(toNodePath),
|
|
862
903
|
nodeType,
|
|
904
|
+
gcTombstoneEnforcementAllowed: this.runtime.gcTombstoneEnforcementAllowed,
|
|
863
905
|
}, undefined /* packagePath */);
|
|
864
906
|
}
|
|
865
907
|
}
|
|
@@ -883,13 +925,16 @@ class GarbageCollector {
|
|
|
883
925
|
* @param gcData - The data representing the reference graph on which GC is run.
|
|
884
926
|
* @param gcResult - The result of the GC run on the gcData.
|
|
885
927
|
* @param currentReferenceTimestampMs - The timestamp to be used for unreferenced nodes' timestamp.
|
|
928
|
+
* @returns - A list of sweep ready nodes. (Nodes ready to be deleted)
|
|
886
929
|
*/
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
930
|
+
updateMarkPhase(gcData, gcResult, currentReferenceTimestampMs, logger) {
|
|
931
|
+
var _a;
|
|
932
|
+
// Get references from the current GC run + references between previous and current run and then update each
|
|
933
|
+
// node's state
|
|
934
|
+
const allNodesReferencedBetweenGCs = (_a = this.findAllNodesReferencedBetweenGCs(gcData, this.gcDataFromLastRun, logger)) !== null && _a !== void 0 ? _a : gcResult.referencedNodeIds;
|
|
890
935
|
this.newReferencesSinceLastRun.clear();
|
|
891
936
|
// Iterate through the referenced nodes and stop tracking if they were unreferenced before.
|
|
892
|
-
for (const nodeId of
|
|
937
|
+
for (const nodeId of allNodesReferencedBetweenGCs) {
|
|
893
938
|
const nodeStateTracker = this.unreferencedNodesState.get(nodeId);
|
|
894
939
|
if (nodeStateTracker !== undefined) {
|
|
895
940
|
// Stop tracking so as to clear out any running timers.
|
|
@@ -902,7 +947,10 @@ class GarbageCollector {
|
|
|
902
947
|
* If a node became unreferenced in this run, start tracking it.
|
|
903
948
|
* If a node was already unreferenced, update its tracking information. Since the current reference time is
|
|
904
949
|
* from the ops seen, this will ensure that we keep updating the unreferenced state as time moves forward.
|
|
950
|
+
*
|
|
951
|
+
* If a node is sweep ready, store and then return it.
|
|
905
952
|
*/
|
|
953
|
+
const sweepReadyNodes = [];
|
|
906
954
|
for (const nodeId of gcResult.deletedNodeIds) {
|
|
907
955
|
const nodeStateTracker = this.unreferencedNodesState.get(nodeId);
|
|
908
956
|
if (nodeStateTracker === undefined) {
|
|
@@ -910,14 +958,51 @@ class GarbageCollector {
|
|
|
910
958
|
}
|
|
911
959
|
else {
|
|
912
960
|
nodeStateTracker.updateTracking(currentReferenceTimestampMs);
|
|
913
|
-
if (
|
|
914
|
-
|
|
915
|
-
if (nodeType === exports.GCNodeType.DataStore || nodeType === exports.GCNodeType.Blob) {
|
|
916
|
-
this.tombstones.push(nodeId);
|
|
917
|
-
}
|
|
961
|
+
if (nodeStateTracker.state === exports.UnreferencedState.SweepReady) {
|
|
962
|
+
sweepReadyNodes.push(nodeId);
|
|
918
963
|
}
|
|
919
964
|
}
|
|
920
965
|
}
|
|
966
|
+
return sweepReadyNodes;
|
|
967
|
+
}
|
|
968
|
+
/**
|
|
969
|
+
* Deletes nodes from both the runtime and garbage collection
|
|
970
|
+
* @param sweepReadyNodes - nodes that are ready to be deleted
|
|
971
|
+
*/
|
|
972
|
+
runSweepPhase(sweepReadyNodes, gcData) {
|
|
973
|
+
// TODO: GC:Validation - validate that removed routes are not double deleted
|
|
974
|
+
// TODO: GC:Validation - validate that the child routes of removed routes are deleted as well
|
|
975
|
+
const sweptRoutes = this.runtime.deleteUnusedNodes(sweepReadyNodes);
|
|
976
|
+
const updatedGCData = this.deleteSweptRoutes(sweptRoutes, gcData);
|
|
977
|
+
for (const nodeId of sweptRoutes) {
|
|
978
|
+
const nodeStateTracker = this.unreferencedNodesState.get(nodeId);
|
|
979
|
+
// TODO: GC:Validation - assert that the nodeStateTracker is defined
|
|
980
|
+
if (nodeStateTracker !== undefined) {
|
|
981
|
+
// Stop tracking so as to clear out any running timers.
|
|
982
|
+
nodeStateTracker.stopTracking();
|
|
983
|
+
// Delete the node as we don't need to track it any more.
|
|
984
|
+
this.unreferencedNodesState.delete(nodeId);
|
|
985
|
+
}
|
|
986
|
+
// TODO: GC:Validation - assert that the deleted node is not a duplicate
|
|
987
|
+
this.deletedNodes.add(nodeId);
|
|
988
|
+
}
|
|
989
|
+
return updatedGCData;
|
|
990
|
+
}
|
|
991
|
+
/**
|
|
992
|
+
* @returns IGarbageCollectionData after deleting the sweptRoutes from the gcData
|
|
993
|
+
*/
|
|
994
|
+
deleteSweptRoutes(sweptRoutes, gcData) {
|
|
995
|
+
const sweptRoutesSet = new Set(sweptRoutes);
|
|
996
|
+
const gcNodes = {};
|
|
997
|
+
for (const [id, outboundRoutes] of Object.entries(gcData.gcNodes)) {
|
|
998
|
+
if (!sweptRoutesSet.has(id)) {
|
|
999
|
+
gcNodes[id] = Array.from(outboundRoutes);
|
|
1000
|
+
}
|
|
1001
|
+
}
|
|
1002
|
+
// TODO: GC:Validation - assert that the nodeId is in gcData
|
|
1003
|
+
return {
|
|
1004
|
+
gcNodes,
|
|
1005
|
+
};
|
|
921
1006
|
}
|
|
922
1007
|
/**
|
|
923
1008
|
* Since GC runs periodically, the GC data that is generated only tells us the state of the world at that point in
|
|
@@ -929,16 +1014,19 @@ class GarbageCollector {
|
|
|
929
1014
|
* 2. A reference is added from one unreferenced node to one or more unreferenced nodes. Even though the node[s] were
|
|
930
1015
|
* unreferenced, they could have been accessed and in-memory reference to them added.
|
|
931
1016
|
*
|
|
932
|
-
* This function identifies nodes that were referenced since last run
|
|
1017
|
+
* This function identifies nodes that were referenced since the last run.
|
|
933
1018
|
* If these nodes are currently unreferenced, they will be assigned new unreferenced state by the current run.
|
|
1019
|
+
*
|
|
1020
|
+
* @returns - a list of all nodes referenced from the last local summary until now.
|
|
934
1021
|
*/
|
|
935
|
-
|
|
1022
|
+
findAllNodesReferencedBetweenGCs(currentGCData, previousGCData, logger) {
|
|
936
1023
|
// If we haven't run GC before there is nothing to do.
|
|
937
|
-
|
|
938
|
-
|
|
1024
|
+
// No previousGCData, means nothing is unreferenced, and there are no reference state trackers to clear
|
|
1025
|
+
if (previousGCData === undefined) {
|
|
1026
|
+
return undefined;
|
|
939
1027
|
}
|
|
940
1028
|
// Find any references that haven't been identified correctly.
|
|
941
|
-
const missingExplicitReferences = this.findMissingExplicitReferences(currentGCData,
|
|
1029
|
+
const missingExplicitReferences = this.findMissingExplicitReferences(currentGCData, previousGCData, this.newReferencesSinceLastRun);
|
|
942
1030
|
if (missingExplicitReferences.length > 0) {
|
|
943
1031
|
missingExplicitReferences.forEach((missingExplicitReference) => {
|
|
944
1032
|
logger.sendErrorEvent({
|
|
@@ -949,9 +1037,9 @@ class GarbageCollector {
|
|
|
949
1037
|
});
|
|
950
1038
|
}
|
|
951
1039
|
// No references were added since the last run so we don't have to update reference states of any unreferenced
|
|
952
|
-
// nodes
|
|
1040
|
+
// nodes. There is no in between state at this point.
|
|
953
1041
|
if (this.newReferencesSinceLastRun.size === 0) {
|
|
954
|
-
return;
|
|
1042
|
+
return undefined;
|
|
955
1043
|
}
|
|
956
1044
|
/**
|
|
957
1045
|
* Generate a super set of the GC data that contains the nodes and edges from last run, plus any new node and
|
|
@@ -969,7 +1057,7 @@ class GarbageCollector {
|
|
|
969
1057
|
* - We don't require DDSes handles to be stored in a referenced DDS.
|
|
970
1058
|
* - A new data store may have "root" DDSes already created and we don't detect them today.
|
|
971
1059
|
*/
|
|
972
|
-
const gcDataSuperSet = (0, garbage_collector_1.concatGarbageCollectionData)(
|
|
1060
|
+
const gcDataSuperSet = (0, garbage_collector_1.concatGarbageCollectionData)(previousGCData, currentGCData);
|
|
973
1061
|
const newOutboundRoutesSinceLastRun = [];
|
|
974
1062
|
this.newReferencesSinceLastRun.forEach((outboundRoutes, sourceNodeId) => {
|
|
975
1063
|
if (gcDataSuperSet.gcNodes[sourceNodeId] === undefined) {
|
|
@@ -987,16 +1075,11 @@ class GarbageCollector {
|
|
|
987
1075
|
* Note that some of these nodes may be unreferenced now and if so, the current run will mark them as
|
|
988
1076
|
* unreferenced and add unreferenced state.
|
|
989
1077
|
*/
|
|
990
|
-
const gcResult = (0, garbage_collector_1.runGarbageCollection)(gcDataSuperSet.gcNodes, [
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
nodeStateTracker.stopTracking();
|
|
996
|
-
// Delete the unreferenced state as we don't need to track it any more.
|
|
997
|
-
this.unreferencedNodesState.delete(nodeId);
|
|
998
|
-
}
|
|
999
|
-
}
|
|
1078
|
+
const gcResult = (0, garbage_collector_1.runGarbageCollection)(gcDataSuperSet.gcNodes, [
|
|
1079
|
+
"/",
|
|
1080
|
+
...newOutboundRoutesSinceLastRun,
|
|
1081
|
+
]);
|
|
1082
|
+
return gcResult.referencedNodeIds;
|
|
1000
1083
|
}
|
|
1001
1084
|
/**
|
|
1002
1085
|
* Finds all new references or outbound routes in the current graph that haven't been explicitly notified to GC.
|
|
@@ -1012,7 +1095,7 @@ class GarbageCollector {
|
|
|
1012
1095
|
* @returns - a list of missing explicit references
|
|
1013
1096
|
*/
|
|
1014
1097
|
findMissingExplicitReferences(currentGCData, previousGCData, explicitReferences) {
|
|
1015
|
-
(0, common_utils_1.assert)(previousGCData !== undefined, 0x2b7);
|
|
1098
|
+
(0, common_utils_1.assert)(previousGCData !== undefined, 0x2b7 /* "Can't validate correctness without GC data from last run" */);
|
|
1016
1099
|
const currentGraph = Object.entries(currentGCData.gcNodes);
|
|
1017
1100
|
const missingExplicitReferences = [];
|
|
1018
1101
|
currentGraph.forEach(([nodeId, currentOutboundRoutes]) => {
|
|
@@ -1030,9 +1113,10 @@ class GarbageCollector {
|
|
|
1030
1113
|
*/
|
|
1031
1114
|
currentOutboundRoutes.forEach((route) => {
|
|
1032
1115
|
const nodeType = this.runtime.getNodeType(route);
|
|
1033
|
-
if ((nodeType === exports.GCNodeType.DataStore || nodeType === exports.GCNodeType.Blob)
|
|
1034
|
-
|
|
1035
|
-
|
|
1116
|
+
if ((nodeType === exports.GCNodeType.DataStore || nodeType === exports.GCNodeType.Blob) &&
|
|
1117
|
+
!nodeId.startsWith(route) &&
|
|
1118
|
+
!previousRoutes.includes(route) &&
|
|
1119
|
+
!explicitRoutes.includes(route)) {
|
|
1036
1120
|
missingExplicitRoutes.push(route);
|
|
1037
1121
|
}
|
|
1038
1122
|
});
|
|
@@ -1104,7 +1188,8 @@ class GarbageCollector {
|
|
|
1104
1188
|
* this will give us a view into how much deleted content a container has.
|
|
1105
1189
|
*/
|
|
1106
1190
|
logSweepEvents(logger, currentReferenceTimestampMs) {
|
|
1107
|
-
if (this.mc.config.getBoolean(garbageCollectionConstants_1.disableSweepLogKey) === true ||
|
|
1191
|
+
if (this.mc.config.getBoolean(garbageCollectionConstants_1.disableSweepLogKey) === true ||
|
|
1192
|
+
this.sweepTimeoutMs === undefined) {
|
|
1108
1193
|
return;
|
|
1109
1194
|
}
|
|
1110
1195
|
this.unreferencedNodesState.forEach((nodeStateTracker, nodeId) => {
|
|
@@ -1139,7 +1224,8 @@ class GarbageCollector {
|
|
|
1139
1224
|
// If there is no reference timestamp to work with, no ops have been processed after creation. If so, skip
|
|
1140
1225
|
// logging as nothing interesting would have happened worth logging.
|
|
1141
1226
|
// If the node is active, skip logging.
|
|
1142
|
-
if (currentReferenceTimestampMs === undefined ||
|
|
1227
|
+
if (currentReferenceTimestampMs === undefined ||
|
|
1228
|
+
nodeStateTracker.state === exports.UnreferencedState.Active) {
|
|
1143
1229
|
return;
|
|
1144
1230
|
}
|
|
1145
1231
|
// We only care about data stores and attachment blobs for this telemetry since GC only marks these objects
|
|
@@ -1204,11 +1290,18 @@ class GarbageCollector {
|
|
|
1204
1290
|
* revived and a Revived event will be logged for it.
|
|
1205
1291
|
*/
|
|
1206
1292
|
const nodeStateTracker = this.unreferencedNodesState.get(eventProps.id);
|
|
1207
|
-
const active = nodeStateTracker === undefined ||
|
|
1293
|
+
const active = nodeStateTracker === undefined ||
|
|
1294
|
+
nodeStateTracker.state === exports.UnreferencedState.Active;
|
|
1208
1295
|
if ((usageType === "Revived") === active) {
|
|
1209
1296
|
const pkg = await this.getNodePackagePath(eventProps.id);
|
|
1210
|
-
const fromPkg = eventProps.fromId
|
|
1211
|
-
|
|
1297
|
+
const fromPkg = eventProps.fromId
|
|
1298
|
+
? await this.getNodePackagePath(eventProps.fromId)
|
|
1299
|
+
: undefined;
|
|
1300
|
+
const event = Object.assign(Object.assign({}, propsToLog), { eventName: `${state}Object_${usageType}`, pkg: pkg
|
|
1301
|
+
? { value: pkg.join("/"), tag: telemetry_utils_1.TelemetryDataTag.CodeArtifact }
|
|
1302
|
+
: undefined, fromPkg: fromPkg
|
|
1303
|
+
? { value: fromPkg.join("/"), tag: telemetry_utils_1.TelemetryDataTag.CodeArtifact }
|
|
1304
|
+
: undefined });
|
|
1212
1305
|
if (state === exports.UnreferencedState.Inactive) {
|
|
1213
1306
|
logger.sendTelemetryEvent(event);
|
|
1214
1307
|
}
|
|
@@ -1235,7 +1328,9 @@ function generateSortedGCState(gcState) {
|
|
|
1235
1328
|
class TimerWithNoDefaultTimeout extends common_utils_1.Timer {
|
|
1236
1329
|
constructor(callback) {
|
|
1237
1330
|
// The default timeout/handlers will never be used since start/restart pass overrides below
|
|
1238
|
-
super(0, () => {
|
|
1331
|
+
super(0, () => {
|
|
1332
|
+
throw new Error("DefaultHandler should not be used");
|
|
1333
|
+
});
|
|
1239
1334
|
this.callback = callback;
|
|
1240
1335
|
}
|
|
1241
1336
|
start(timeoutMs) {
|