@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
package/lib/garbageCollection.js
CHANGED
|
@@ -14,7 +14,7 @@ var __rest = (this && this.__rest) || function (s, e) {
|
|
|
14
14
|
return t;
|
|
15
15
|
};
|
|
16
16
|
import { assert, LazyPromise, Timer } from "@fluidframework/common-utils";
|
|
17
|
-
import { ClientSessionExpiredError, DataProcessingError, UsageError } from "@fluidframework/container-utils";
|
|
17
|
+
import { ClientSessionExpiredError, DataProcessingError, UsageError, } from "@fluidframework/container-utils";
|
|
18
18
|
import { cloneGCData, concatGarbageCollectionData, getGCDataFromSnapshot, runGarbageCollection, trimLeadingSlashes, } from "@fluidframework/garbage-collector";
|
|
19
19
|
import { SummaryType } from "@fluidframework/protocol-definitions";
|
|
20
20
|
import { gcTreeKey, gcBlobPrefix, gcTombstoneBlobKey, gcDeletedBlobKey, } from "@fluidframework/runtime-definitions";
|
|
@@ -22,8 +22,8 @@ import { mergeStats, packagePathToTelemetryProperty, SummaryTreeBuilder, } from
|
|
|
22
22
|
import { ChildLogger, generateStack, loggerToMonitoringContext, PerformanceEvent, TelemetryDataTag, } from "@fluidframework/telemetry-utils";
|
|
23
23
|
import { RuntimeHeaders } from "./containerRuntime";
|
|
24
24
|
import { getSummaryForDatastores } from "./dataStores";
|
|
25
|
-
import { currentGCVersion, defaultInactiveTimeoutMs, defaultSessionExpiryDurationMs, disableSweepLogKey, disableTombstoneKey, gcVersionUpgradeToV2Key, gcTestModeKey, oneDayMs, runGCKey, runSessionExpiryKey, runSweepKey, stableGCVersion, trackGCStateKey } from "./garbageCollectionConstants";
|
|
26
|
-
import {
|
|
25
|
+
import { currentGCVersion, defaultInactiveTimeoutMs, defaultSessionExpiryDurationMs, disableSweepLogKey, disableTombstoneKey, gcVersionUpgradeToV2Key, gcTestModeKey, oneDayMs, runGCKey, runSessionExpiryKey, runSweepKey, stableGCVersion, trackGCStateKey, gcTombstoneGenerationOptionName, } from "./garbageCollectionConstants";
|
|
26
|
+
import { sendGCUnexpectedUsageEvent } from "./garbageCollectionHelpers";
|
|
27
27
|
import { SweepReadyUsageDetectionHandler } from "./gcSweepReadyUsageDetection";
|
|
28
28
|
import { getGCVersion, metadataBlobName, dataStoreAttributesBlobName, } from "./summaryFormat";
|
|
29
29
|
/** The types of GC nodes in the GC reference graph. */
|
|
@@ -124,15 +124,15 @@ export class UnreferencedStateTracker {
|
|
|
124
124
|
* Graph - all nodes with their respective routes
|
|
125
125
|
*
|
|
126
126
|
* ```
|
|
127
|
-
*
|
|
127
|
+
* GC Graph
|
|
128
128
|
*
|
|
129
|
-
*
|
|
130
|
-
*
|
|
131
|
-
*
|
|
132
|
-
*
|
|
133
|
-
*
|
|
134
|
-
*
|
|
135
|
-
* NodeId = "dds1"
|
|
129
|
+
* Node
|
|
130
|
+
* NodeId = "datastore1"
|
|
131
|
+
* / \\
|
|
132
|
+
* OutboundRoute OutboundRoute
|
|
133
|
+
* / \\
|
|
134
|
+
* Node Node
|
|
135
|
+
* NodeId = "dds1" NodeId = "dds2"
|
|
136
136
|
* ```
|
|
137
137
|
*/
|
|
138
138
|
export class GarbageCollector {
|
|
@@ -164,10 +164,14 @@ export class GarbageCollector {
|
|
|
164
164
|
const baseSnapshot = createParams.baseSnapshot;
|
|
165
165
|
const metadata = createParams.metadata;
|
|
166
166
|
const readAndParseBlob = createParams.readAndParseBlob;
|
|
167
|
-
this.mc = loggerToMonitoringContext(ChildLogger.create(createParams.baseLogger, "GarbageCollector", {
|
|
167
|
+
this.mc = loggerToMonitoringContext(ChildLogger.create(createParams.baseLogger, "GarbageCollector", {
|
|
168
|
+
all: { completedGCRuns: () => this.completedRuns },
|
|
169
|
+
}));
|
|
168
170
|
// If version upgrade is not enabled, fall back to the stable GC version.
|
|
169
171
|
this.currentGCVersion =
|
|
170
|
-
this.mc.config.getBoolean(gcVersionUpgradeToV2Key) === true
|
|
172
|
+
this.mc.config.getBoolean(gcVersionUpgradeToV2Key) === true
|
|
173
|
+
? currentGCVersion
|
|
174
|
+
: stableGCVersion;
|
|
171
175
|
this.sweepReadyUsageHandler = new SweepReadyUsageDetectionHandler(createParams.getContainerDiagnosticId(), this.mc, this.runtime.closeFn);
|
|
172
176
|
let prevSummaryGCVersion;
|
|
173
177
|
/**
|
|
@@ -181,8 +185,8 @@ export class GarbageCollector {
|
|
|
181
185
|
function computeSweepTimeout(sessionExpiryTimeoutMs) {
|
|
182
186
|
const maxSnapshotCacheExpiryMs = 5 * oneDayMs;
|
|
183
187
|
const bufferMs = oneDayMs;
|
|
184
|
-
return sessionExpiryTimeoutMs &&
|
|
185
|
-
|
|
188
|
+
return (sessionExpiryTimeoutMs &&
|
|
189
|
+
sessionExpiryTimeoutMs + maxSnapshotCacheExpiryMs + bufferMs);
|
|
186
190
|
}
|
|
187
191
|
/**
|
|
188
192
|
* The following GC state is enabled during container creation and cannot be changed throughout its lifetime:
|
|
@@ -200,6 +204,7 @@ export class GarbageCollector {
|
|
|
200
204
|
this.sessionExpiryTimeoutMs = metadata === null || metadata === void 0 ? void 0 : metadata.sessionExpiryTimeoutMs;
|
|
201
205
|
this.sweepTimeoutMs =
|
|
202
206
|
(_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
|
|
207
|
+
this.persistedGcFeatureMatrix = metadata === null || metadata === void 0 ? void 0 : metadata.gcFeatureMatrix;
|
|
203
208
|
}
|
|
204
209
|
else {
|
|
205
210
|
// Sweep should not be enabled without enabling GC mark phase. We could silently disable sweep in this
|
|
@@ -216,17 +221,25 @@ export class GarbageCollector {
|
|
|
216
221
|
this.sweepEnabled = this.gcOptions.sweepAllowed === true;
|
|
217
222
|
// Set the Session Expiry only if the flag is enabled and GC is enabled.
|
|
218
223
|
if (this.mc.config.getBoolean(runSessionExpiryKey) && this.gcEnabled) {
|
|
219
|
-
this.sessionExpiryTimeoutMs =
|
|
224
|
+
this.sessionExpiryTimeoutMs =
|
|
225
|
+
(_c = this.gcOptions.sessionExpiryTimeoutMs) !== null && _c !== void 0 ? _c : defaultSessionExpiryDurationMs;
|
|
220
226
|
}
|
|
221
227
|
this.sweepTimeoutMs =
|
|
222
228
|
testOverrideSweepTimeoutMs !== null && testOverrideSweepTimeoutMs !== void 0 ? testOverrideSweepTimeoutMs : computeSweepTimeout(this.sessionExpiryTimeoutMs);
|
|
229
|
+
if (this.gcOptions[gcTombstoneGenerationOptionName] !== undefined) {
|
|
230
|
+
this.persistedGcFeatureMatrix = {
|
|
231
|
+
tombstoneGeneration: this.gcOptions[gcTombstoneGenerationOptionName],
|
|
232
|
+
};
|
|
233
|
+
}
|
|
223
234
|
}
|
|
224
235
|
// If session expiry is enabled, we need to close the container when the session expiry timeout expires.
|
|
225
236
|
if (this.sessionExpiryTimeoutMs !== undefined) {
|
|
226
237
|
// If Test Override config is set, override Session Expiry timeout.
|
|
227
238
|
const overrideSessionExpiryTimeoutMs = this.mc.config.getNumber("Fluid.GarbageCollection.TestOverride.SessionExpiryMs");
|
|
228
239
|
const timeoutMs = overrideSessionExpiryTimeoutMs !== null && overrideSessionExpiryTimeoutMs !== void 0 ? overrideSessionExpiryTimeoutMs : this.sessionExpiryTimeoutMs;
|
|
229
|
-
this.sessionExpiryTimer = new Timer(timeoutMs, () => {
|
|
240
|
+
this.sessionExpiryTimer = new Timer(timeoutMs, () => {
|
|
241
|
+
this.runtime.closeFn(new ClientSessionExpiredError(`Client session expired.`, timeoutMs));
|
|
242
|
+
});
|
|
230
243
|
this.sessionExpiryTimer.start();
|
|
231
244
|
}
|
|
232
245
|
// For existing document, the latest summary is the one that we loaded from. So, use its GC version as the
|
|
@@ -241,11 +254,12 @@ export class GarbageCollector {
|
|
|
241
254
|
*
|
|
242
255
|
* These conditions can be overridden via runGCKey feature flag.
|
|
243
256
|
*/
|
|
244
|
-
this.shouldRunGC =
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
257
|
+
this.shouldRunGC =
|
|
258
|
+
(_d = this.mc.config.getBoolean(runGCKey)) !== null && _d !== void 0 ? _d :
|
|
259
|
+
// GC must be enabled for the document.
|
|
260
|
+
(this.gcEnabled &&
|
|
261
|
+
// GC must not be disabled via GC options.
|
|
262
|
+
!this.gcOptions.disableGC);
|
|
249
263
|
/**
|
|
250
264
|
* Whether sweep should run or not. The following conditions have to be met to run sweep:
|
|
251
265
|
*
|
|
@@ -257,21 +271,24 @@ export class GarbageCollector {
|
|
|
257
271
|
* feature flag.
|
|
258
272
|
*/
|
|
259
273
|
this.shouldRunSweep =
|
|
260
|
-
this.shouldRunGC
|
|
261
|
-
|
|
262
|
-
|
|
274
|
+
this.shouldRunGC &&
|
|
275
|
+
this.sweepTimeoutMs !== undefined &&
|
|
276
|
+
((_e = this.mc.config.getBoolean(runSweepKey)) !== null && _e !== void 0 ? _e : this.sweepEnabled);
|
|
263
277
|
this.trackGCState = this.mc.config.getBoolean(trackGCStateKey) === true;
|
|
264
278
|
// Override inactive timeout if test config or gc options to override it is set.
|
|
265
|
-
this.inactiveTimeoutMs =
|
|
279
|
+
this.inactiveTimeoutMs =
|
|
280
|
+
(_g = (_f = this.mc.config.getNumber("Fluid.GarbageCollection.TestOverride.InactiveTimeoutMs")) !== null && _f !== void 0 ? _f : this.gcOptions.inactiveTimeoutMs) !== null && _g !== void 0 ? _g : defaultInactiveTimeoutMs;
|
|
266
281
|
// Inactive timeout must be greater than sweep timeout since a node goes from active -> inactive -> sweep ready.
|
|
267
282
|
if (this.sweepTimeoutMs !== undefined && this.inactiveTimeoutMs > this.sweepTimeoutMs) {
|
|
268
283
|
throw new UsageError("inactive timeout should not be greater than the sweep timeout");
|
|
269
284
|
}
|
|
270
285
|
// Whether we are running in test mode. In this mode, unreferenced nodes are immediately deleted.
|
|
271
|
-
this.testMode =
|
|
286
|
+
this.testMode =
|
|
287
|
+
(_h = this.mc.config.getBoolean(gcTestModeKey)) !== null && _h !== void 0 ? _h : this.gcOptions.runGCInTestMode === true;
|
|
272
288
|
// Whether we are running in tombstone mode. This is enabled by default if sweep won't run. It can be disabled
|
|
273
289
|
// via feature flags.
|
|
274
|
-
this.tombstoneMode =
|
|
290
|
+
this.tombstoneMode =
|
|
291
|
+
!this.shouldRunSweep && this.mc.config.getBoolean(disableTombstoneKey) !== true;
|
|
275
292
|
// If GC ran in the container that generated the base snapshot, it will have a GC tree.
|
|
276
293
|
this.wasGCRunInLatestSummary = (baseSnapshot === null || baseSnapshot === void 0 ? void 0 : baseSnapshot.trees[gcTreeKey]) !== undefined;
|
|
277
294
|
// Get the GC data from the base snapshot. Use LazyPromise because we only want to do this once since it
|
|
@@ -290,7 +307,9 @@ export class GarbageCollector {
|
|
|
290
307
|
// back-compat - Older documents will have the GC blobs in each data store's summary tree. Get them and
|
|
291
308
|
// consolidate into IGarbageCollectionState format.
|
|
292
309
|
// Add a node for the root node that is not present in older snapshot format.
|
|
293
|
-
const gcState = {
|
|
310
|
+
const gcState = {
|
|
311
|
+
gcNodes: { "/": { outboundRoutes: [] } },
|
|
312
|
+
};
|
|
294
313
|
const dataStoreSnapshotTree = getSummaryForDatastores(baseSnapshot, metadata);
|
|
295
314
|
assert(dataStoreSnapshotTree !== undefined, 0x2a8 /* "Expected data store snapshot tree in base snapshot" */);
|
|
296
315
|
for (const [dsId, dsSnapshotTree] of Object.entries(dataStoreSnapshotTree.trees)) {
|
|
@@ -314,10 +333,13 @@ export class GarbageCollector {
|
|
|
314
333
|
// Prefix the data store id to the GC node ids to make them relative to the root from being
|
|
315
334
|
// relative to the data store. Similar to how its done in DataStore::getGCData.
|
|
316
335
|
const rootId = id === "/" ? dsRootId : `${dsRootId}${id}`;
|
|
317
|
-
gcState.gcNodes[rootId] = {
|
|
336
|
+
gcState.gcNodes[rootId] = {
|
|
337
|
+
outboundRoutes: Array.from(outboundRoutes),
|
|
338
|
+
};
|
|
318
339
|
}
|
|
319
340
|
assert(gcState.gcNodes[dsRootId] !== undefined, 0x2a9 /* GC nodes for data store not in GC blob */);
|
|
320
|
-
gcState.gcNodes[dsRootId].unreferencedTimestampMs =
|
|
341
|
+
gcState.gcNodes[dsRootId].unreferencedTimestampMs =
|
|
342
|
+
gcSummaryDetails.unrefTimestamp;
|
|
321
343
|
}
|
|
322
344
|
// If there is only one node (root node just added above), either GC is disabled or we are loading from
|
|
323
345
|
// the first summary generated by detached container. In both cases, GC was not run - return undefined.
|
|
@@ -411,8 +433,8 @@ export class GarbageCollector {
|
|
|
411
433
|
* 4.2. This client's latest summary was updated from a snapshot that has a different GC version.
|
|
412
434
|
*/
|
|
413
435
|
get summaryStateNeedsReset() {
|
|
414
|
-
return this.gcStateNeedsReset ||
|
|
415
|
-
(this.shouldRunGC && this.latestSummaryGCVersion !== this.currentGCVersion);
|
|
436
|
+
return (this.gcStateNeedsReset ||
|
|
437
|
+
(this.shouldRunGC && this.latestSummaryGCVersion !== this.currentGCVersion));
|
|
416
438
|
}
|
|
417
439
|
/**
|
|
418
440
|
* Tells whether the GC state needs to be reset. This can happen under 3 conditions:
|
|
@@ -427,7 +449,7 @@ export class GarbageCollector {
|
|
|
427
449
|
*
|
|
428
450
|
* Note that the state will be reset only once for the first summary generated after this returns true. After that,
|
|
429
451
|
* this will return false.
|
|
430
|
-
|
|
452
|
+
*/
|
|
431
453
|
get gcStateNeedsReset() {
|
|
432
454
|
return this.wasGCRunInLatestSummary !== this.shouldRunGC;
|
|
433
455
|
}
|
|
@@ -458,6 +480,7 @@ export class GarbageCollector {
|
|
|
458
480
|
// If running in tombstone mode, initialize the tombstone state from the snapshot. Also, notify the runtime of
|
|
459
481
|
// tombstone routes.
|
|
460
482
|
if (this.tombstoneMode && baseSnapshotData.tombstones !== undefined) {
|
|
483
|
+
// Create a copy since we are writing from a source we don't control
|
|
461
484
|
this.tombstones = Array.from(baseSnapshotData.tombstones);
|
|
462
485
|
this.runtime.updateTombstonedRoutes(this.tombstones);
|
|
463
486
|
}
|
|
@@ -485,7 +508,6 @@ export class GarbageCollector {
|
|
|
485
508
|
for (const [, nodeStateTracker] of this.unreferencedNodesState) {
|
|
486
509
|
nodeStateTracker.stopTracking();
|
|
487
510
|
}
|
|
488
|
-
;
|
|
489
511
|
this.unreferencedNodesState.clear();
|
|
490
512
|
// If running sweep, the tombstone state represents the list of nodes that have been deleted during sweep.
|
|
491
513
|
// If running in tombstone mode, the tombstone state represents the list of nodes that have been marked as
|
|
@@ -493,7 +515,9 @@ export class GarbageCollector {
|
|
|
493
515
|
// If this call is because we are refreshing from a snapshot due to an ack, it is likely that the GC state
|
|
494
516
|
// in the snapshot is newer than this client's. And so, the deleted / tombstone nodes need to be updated.
|
|
495
517
|
if (this.shouldRunSweep) {
|
|
496
|
-
const snapshotDeletedNodes = (snapshotData === null || snapshotData === void 0 ? void 0 : snapshotData.
|
|
518
|
+
const snapshotDeletedNodes = (snapshotData === null || snapshotData === void 0 ? void 0 : snapshotData.deletedNodes)
|
|
519
|
+
? new Set(snapshotData.deletedNodes)
|
|
520
|
+
: undefined;
|
|
497
521
|
// If the snapshot contains deleted nodes that are not yet deleted by this client, ask the runtime to
|
|
498
522
|
// delete them.
|
|
499
523
|
if (snapshotDeletedNodes !== undefined) {
|
|
@@ -571,7 +595,9 @@ export class GarbageCollector {
|
|
|
571
595
|
var _a;
|
|
572
596
|
const fullGC = (_a = options.fullGC) !== null && _a !== void 0 ? _a : (this.gcOptions.runFullGC === true || this.summaryStateNeedsReset);
|
|
573
597
|
const logger = options.logger
|
|
574
|
-
? ChildLogger.create(options.logger, undefined, {
|
|
598
|
+
? ChildLogger.create(options.logger, undefined, {
|
|
599
|
+
all: { completedGCRuns: () => this.completedRuns },
|
|
600
|
+
})
|
|
575
601
|
: this.mc.logger;
|
|
576
602
|
/**
|
|
577
603
|
* If there is no current reference timestamp, skip running GC. We need the current timestamp to track
|
|
@@ -611,26 +637,29 @@ export class GarbageCollector {
|
|
|
611
637
|
// Generate statistics from the current run. This is done before updating the current state because it
|
|
612
638
|
// generates some of its data based on previous state of the system.
|
|
613
639
|
const gcStats = this.generateStats(gcResult);
|
|
614
|
-
// Update the state
|
|
615
|
-
|
|
616
|
-
this.updateStateSinceLastRun(gcData, logger);
|
|
617
|
-
// Update the current state and update the runtime of all routes or ids that used as per the GC run.
|
|
618
|
-
this.updateCurrentState(gcData, gcResult, currentReferenceTimestampMs);
|
|
640
|
+
// Update the current mark state and update the runtime of all used routes or ids that used as per the GC run.
|
|
641
|
+
const sweepReadyNodes = this.updateMarkPhase(gcData, gcResult, currentReferenceTimestampMs, logger);
|
|
619
642
|
this.runtime.updateUsedRoutes(gcResult.referencedNodeIds);
|
|
620
643
|
// Log events for objects that are ready to be deleted by sweep. When we have sweep enabled, we will
|
|
621
644
|
// delete these objects here instead.
|
|
622
645
|
this.logSweepEvents(logger, currentReferenceTimestampMs);
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
646
|
+
let updatedGCData = gcData;
|
|
647
|
+
if (this.shouldRunSweep) {
|
|
648
|
+
updatedGCData = this.runSweepPhase(sweepReadyNodes, gcData);
|
|
649
|
+
}
|
|
650
|
+
else if (this.testMode) {
|
|
651
|
+
// If we are running in GC test mode, delete objects for unused routes. This enables testing scenarios
|
|
652
|
+
// involving access to deleted data.
|
|
626
653
|
this.runtime.updateUnusedRoutes(gcResult.deletedNodeIds);
|
|
627
654
|
}
|
|
628
655
|
else if (this.tombstoneMode) {
|
|
656
|
+
this.tombstones = sweepReadyNodes;
|
|
629
657
|
// If we are running in GC tombstone mode, update tombstoned routes. This enables testing scenarios
|
|
630
658
|
// involving access to "deleted" data without actually deleting the data from summaries.
|
|
631
659
|
// Note: we will not tombstone in test mode.
|
|
632
660
|
this.runtime.updateTombstonedRoutes(this.tombstones);
|
|
633
661
|
}
|
|
662
|
+
this.gcDataFromLastRun = cloneGCData(updatedGCData);
|
|
634
663
|
// Log pending unreferenced events such as a node being used after inactive. This is done after GC runs and
|
|
635
664
|
// updates its state so that we don't send false positives based on intermediate state. For example, we may get
|
|
636
665
|
// reference to an unreferenced node from another unreferenced node which means the node wasn't revived.
|
|
@@ -662,7 +691,9 @@ export class GarbageCollector {
|
|
|
662
691
|
: undefined;
|
|
663
692
|
// If running in tombstone mode, serialize and write tombstones, if any.
|
|
664
693
|
const serializedTombstones = this.tombstoneMode
|
|
665
|
-
?
|
|
694
|
+
? this.tombstones.length > 0
|
|
695
|
+
? JSON.stringify(this.tombstones.sort())
|
|
696
|
+
: undefined
|
|
666
697
|
: undefined;
|
|
667
698
|
/**
|
|
668
699
|
* Incremental summary of GC data - If none of GC state, deleted nodes or tombstones changed since last summary,
|
|
@@ -671,11 +702,15 @@ export class GarbageCollector {
|
|
|
671
702
|
* for each of these that did not change, write a summary handle.
|
|
672
703
|
*/
|
|
673
704
|
if (this.trackGCState) {
|
|
674
|
-
this.pendingSummaryData = {
|
|
705
|
+
this.pendingSummaryData = {
|
|
706
|
+
serializedGCState,
|
|
707
|
+
serializedTombstones,
|
|
708
|
+
serializedDeletedNodes,
|
|
709
|
+
};
|
|
675
710
|
if (trackState && !fullTree && this.latestSummaryData !== undefined) {
|
|
676
711
|
// If nothing changed since last summary, send a summary handle for the entire GC data.
|
|
677
|
-
if (this.latestSummaryData.serializedGCState === serializedGCState
|
|
678
|
-
|
|
712
|
+
if (this.latestSummaryData.serializedGCState === serializedGCState &&
|
|
713
|
+
this.latestSummaryData.serializedTombstones === serializedTombstones) {
|
|
679
714
|
const stats = mergeStats();
|
|
680
715
|
stats.handleNodeCount++;
|
|
681
716
|
return {
|
|
@@ -718,7 +753,8 @@ export class GarbageCollector {
|
|
|
718
753
|
// If tombstones exist, write a summary handle if it hasn't changed. If it has changed, write a
|
|
719
754
|
// summary blob.
|
|
720
755
|
if (serializedTombstones !== undefined) {
|
|
721
|
-
if (((_b = this.latestSummaryData) === null || _b === void 0 ? void 0 : _b.serializedTombstones) === serializedTombstones &&
|
|
756
|
+
if (((_b = this.latestSummaryData) === null || _b === void 0 ? void 0 : _b.serializedTombstones) === serializedTombstones &&
|
|
757
|
+
trackState) {
|
|
722
758
|
builder.addHandle(gcTombstoneBlobKey, SummaryType.Blob, `/${gcTreeKey}/${gcTombstoneBlobKey}`);
|
|
723
759
|
}
|
|
724
760
|
else {
|
|
@@ -730,7 +766,8 @@ export class GarbageCollector {
|
|
|
730
766
|
return builder.getSummaryTree();
|
|
731
767
|
}
|
|
732
768
|
// If the deleted nodes hasn't changed, write a summary handle, else write a summary blob for it.
|
|
733
|
-
if (((_c = this.latestSummaryData) === null || _c === void 0 ? void 0 : _c.serializedDeletedNodes) === serializedDeletedNodes &&
|
|
769
|
+
if (((_c = this.latestSummaryData) === null || _c === void 0 ? void 0 : _c.serializedDeletedNodes) === serializedDeletedNodes &&
|
|
770
|
+
trackState) {
|
|
734
771
|
builder.addHandle(gcDeletedBlobKey, SummaryType.Blob, `/${gcTreeKey}/${gcDeletedBlobKey}`);
|
|
735
772
|
}
|
|
736
773
|
else {
|
|
@@ -745,6 +782,7 @@ export class GarbageCollector {
|
|
|
745
782
|
* into the metadata blob. If GC is disabled, the gcFeature is 0.
|
|
746
783
|
*/
|
|
747
784
|
gcFeature: this.gcEnabled ? this.currentGCVersion : 0,
|
|
785
|
+
gcFeatureMatrix: this.persistedGcFeatureMatrix,
|
|
748
786
|
sessionExpiryTimeoutMs: this.sessionExpiryTimeoutMs,
|
|
749
787
|
sweepEnabled: this.sweepEnabled,
|
|
750
788
|
sweepTimeoutMs: this.sweepTimeoutMs,
|
|
@@ -761,7 +799,7 @@ export class GarbageCollector {
|
|
|
761
799
|
* Called to refresh the latest summary state. This happens when either a pending summary is acked or a snapshot
|
|
762
800
|
* is downloaded and should be used to update the state.
|
|
763
801
|
*/
|
|
764
|
-
async refreshLatestSummary(
|
|
802
|
+
async refreshLatestSummary(proposalHandle, result, readAndParseBlob) {
|
|
765
803
|
// If the latest summary was updated and the summary was tracked, this client is the one that generated this
|
|
766
804
|
// summary. So, update wasGCRunInLatestSummary.
|
|
767
805
|
// Note that this has to be updated if GC did not run too. Otherwise, `gcStateNeedsReset` will always return
|
|
@@ -783,8 +821,8 @@ export class GarbageCollector {
|
|
|
783
821
|
return;
|
|
784
822
|
}
|
|
785
823
|
// If the summary was not tracked by this client, the state should be updated from the downloaded snapshot.
|
|
786
|
-
const
|
|
787
|
-
const metadataBlobId =
|
|
824
|
+
const snapshotTree = result.snapshotTree;
|
|
825
|
+
const metadataBlobId = snapshotTree.blobs[metadataBlobName];
|
|
788
826
|
if (metadataBlobId) {
|
|
789
827
|
const metadata = await readAndParseBlob(metadataBlobId);
|
|
790
828
|
this.latestSummaryGCVersion = getGCVersion(metadata);
|
|
@@ -793,9 +831,13 @@ export class GarbageCollector {
|
|
|
793
831
|
// to be at least one op (summary op / ack, if nothing else) if a snapshot was taken.
|
|
794
832
|
const currentReferenceTimestampMs = this.runtime.getCurrentReferenceTimestampMs();
|
|
795
833
|
if (currentReferenceTimestampMs === undefined) {
|
|
796
|
-
throw DataProcessingError.create("No reference timestamp when updating GC state from snapshot", "refreshLatestSummary", undefined, {
|
|
834
|
+
throw DataProcessingError.create("No reference timestamp when updating GC state from snapshot", "refreshLatestSummary", undefined, {
|
|
835
|
+
proposalHandle,
|
|
836
|
+
summaryRefSeq: result.summaryRefSeq,
|
|
837
|
+
details: JSON.stringify(this.configs),
|
|
838
|
+
});
|
|
797
839
|
}
|
|
798
|
-
const gcSnapshotTree =
|
|
840
|
+
const gcSnapshotTree = snapshotTree.trees[gcTreeKey];
|
|
799
841
|
// If GC ran in the container that generated this snapshot, it will have a GC tree.
|
|
800
842
|
this.wasGCRunInLatestSummary = gcSnapshotTree !== undefined;
|
|
801
843
|
let latestGCData;
|
|
@@ -850,12 +892,12 @@ export class GarbageCollector {
|
|
|
850
892
|
else if (nodeType === GCNodeType.Blob) {
|
|
851
893
|
eventName = "GC_Tombstone_Blob_Revived";
|
|
852
894
|
}
|
|
853
|
-
|
|
895
|
+
sendGCUnexpectedUsageEvent(this.mc, {
|
|
854
896
|
eventName,
|
|
855
897
|
category: "generic",
|
|
856
|
-
isSummarizerClient: this.isSummarizerClient,
|
|
857
898
|
url: trimLeadingSlashes(toNodePath),
|
|
858
899
|
nodeType,
|
|
900
|
+
gcTombstoneEnforcementAllowed: this.runtime.gcTombstoneEnforcementAllowed,
|
|
859
901
|
}, undefined /* packagePath */);
|
|
860
902
|
}
|
|
861
903
|
}
|
|
@@ -879,13 +921,16 @@ export class GarbageCollector {
|
|
|
879
921
|
* @param gcData - The data representing the reference graph on which GC is run.
|
|
880
922
|
* @param gcResult - The result of the GC run on the gcData.
|
|
881
923
|
* @param currentReferenceTimestampMs - The timestamp to be used for unreferenced nodes' timestamp.
|
|
924
|
+
* @returns - A list of sweep ready nodes. (Nodes ready to be deleted)
|
|
882
925
|
*/
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
926
|
+
updateMarkPhase(gcData, gcResult, currentReferenceTimestampMs, logger) {
|
|
927
|
+
var _a;
|
|
928
|
+
// Get references from the current GC run + references between previous and current run and then update each
|
|
929
|
+
// node's state
|
|
930
|
+
const allNodesReferencedBetweenGCs = (_a = this.findAllNodesReferencedBetweenGCs(gcData, this.gcDataFromLastRun, logger)) !== null && _a !== void 0 ? _a : gcResult.referencedNodeIds;
|
|
886
931
|
this.newReferencesSinceLastRun.clear();
|
|
887
932
|
// Iterate through the referenced nodes and stop tracking if they were unreferenced before.
|
|
888
|
-
for (const nodeId of
|
|
933
|
+
for (const nodeId of allNodesReferencedBetweenGCs) {
|
|
889
934
|
const nodeStateTracker = this.unreferencedNodesState.get(nodeId);
|
|
890
935
|
if (nodeStateTracker !== undefined) {
|
|
891
936
|
// Stop tracking so as to clear out any running timers.
|
|
@@ -898,7 +943,10 @@ export class GarbageCollector {
|
|
|
898
943
|
* If a node became unreferenced in this run, start tracking it.
|
|
899
944
|
* If a node was already unreferenced, update its tracking information. Since the current reference time is
|
|
900
945
|
* from the ops seen, this will ensure that we keep updating the unreferenced state as time moves forward.
|
|
946
|
+
*
|
|
947
|
+
* If a node is sweep ready, store and then return it.
|
|
901
948
|
*/
|
|
949
|
+
const sweepReadyNodes = [];
|
|
902
950
|
for (const nodeId of gcResult.deletedNodeIds) {
|
|
903
951
|
const nodeStateTracker = this.unreferencedNodesState.get(nodeId);
|
|
904
952
|
if (nodeStateTracker === undefined) {
|
|
@@ -906,14 +954,51 @@ export class GarbageCollector {
|
|
|
906
954
|
}
|
|
907
955
|
else {
|
|
908
956
|
nodeStateTracker.updateTracking(currentReferenceTimestampMs);
|
|
909
|
-
if (
|
|
910
|
-
|
|
911
|
-
if (nodeType === GCNodeType.DataStore || nodeType === GCNodeType.Blob) {
|
|
912
|
-
this.tombstones.push(nodeId);
|
|
913
|
-
}
|
|
957
|
+
if (nodeStateTracker.state === UnreferencedState.SweepReady) {
|
|
958
|
+
sweepReadyNodes.push(nodeId);
|
|
914
959
|
}
|
|
915
960
|
}
|
|
916
961
|
}
|
|
962
|
+
return sweepReadyNodes;
|
|
963
|
+
}
|
|
964
|
+
/**
|
|
965
|
+
* Deletes nodes from both the runtime and garbage collection
|
|
966
|
+
* @param sweepReadyNodes - nodes that are ready to be deleted
|
|
967
|
+
*/
|
|
968
|
+
runSweepPhase(sweepReadyNodes, gcData) {
|
|
969
|
+
// TODO: GC:Validation - validate that removed routes are not double deleted
|
|
970
|
+
// TODO: GC:Validation - validate that the child routes of removed routes are deleted as well
|
|
971
|
+
const sweptRoutes = this.runtime.deleteUnusedNodes(sweepReadyNodes);
|
|
972
|
+
const updatedGCData = this.deleteSweptRoutes(sweptRoutes, gcData);
|
|
973
|
+
for (const nodeId of sweptRoutes) {
|
|
974
|
+
const nodeStateTracker = this.unreferencedNodesState.get(nodeId);
|
|
975
|
+
// TODO: GC:Validation - assert that the nodeStateTracker is defined
|
|
976
|
+
if (nodeStateTracker !== undefined) {
|
|
977
|
+
// Stop tracking so as to clear out any running timers.
|
|
978
|
+
nodeStateTracker.stopTracking();
|
|
979
|
+
// Delete the node as we don't need to track it any more.
|
|
980
|
+
this.unreferencedNodesState.delete(nodeId);
|
|
981
|
+
}
|
|
982
|
+
// TODO: GC:Validation - assert that the deleted node is not a duplicate
|
|
983
|
+
this.deletedNodes.add(nodeId);
|
|
984
|
+
}
|
|
985
|
+
return updatedGCData;
|
|
986
|
+
}
|
|
987
|
+
/**
|
|
988
|
+
* @returns IGarbageCollectionData after deleting the sweptRoutes from the gcData
|
|
989
|
+
*/
|
|
990
|
+
deleteSweptRoutes(sweptRoutes, gcData) {
|
|
991
|
+
const sweptRoutesSet = new Set(sweptRoutes);
|
|
992
|
+
const gcNodes = {};
|
|
993
|
+
for (const [id, outboundRoutes] of Object.entries(gcData.gcNodes)) {
|
|
994
|
+
if (!sweptRoutesSet.has(id)) {
|
|
995
|
+
gcNodes[id] = Array.from(outboundRoutes);
|
|
996
|
+
}
|
|
997
|
+
}
|
|
998
|
+
// TODO: GC:Validation - assert that the nodeId is in gcData
|
|
999
|
+
return {
|
|
1000
|
+
gcNodes,
|
|
1001
|
+
};
|
|
917
1002
|
}
|
|
918
1003
|
/**
|
|
919
1004
|
* Since GC runs periodically, the GC data that is generated only tells us the state of the world at that point in
|
|
@@ -925,16 +1010,19 @@ export class GarbageCollector {
|
|
|
925
1010
|
* 2. A reference is added from one unreferenced node to one or more unreferenced nodes. Even though the node[s] were
|
|
926
1011
|
* unreferenced, they could have been accessed and in-memory reference to them added.
|
|
927
1012
|
*
|
|
928
|
-
* This function identifies nodes that were referenced since last run
|
|
1013
|
+
* This function identifies nodes that were referenced since the last run.
|
|
929
1014
|
* If these nodes are currently unreferenced, they will be assigned new unreferenced state by the current run.
|
|
1015
|
+
*
|
|
1016
|
+
* @returns - a list of all nodes referenced from the last local summary until now.
|
|
930
1017
|
*/
|
|
931
|
-
|
|
1018
|
+
findAllNodesReferencedBetweenGCs(currentGCData, previousGCData, logger) {
|
|
932
1019
|
// If we haven't run GC before there is nothing to do.
|
|
933
|
-
|
|
934
|
-
|
|
1020
|
+
// No previousGCData, means nothing is unreferenced, and there are no reference state trackers to clear
|
|
1021
|
+
if (previousGCData === undefined) {
|
|
1022
|
+
return undefined;
|
|
935
1023
|
}
|
|
936
1024
|
// Find any references that haven't been identified correctly.
|
|
937
|
-
const missingExplicitReferences = this.findMissingExplicitReferences(currentGCData,
|
|
1025
|
+
const missingExplicitReferences = this.findMissingExplicitReferences(currentGCData, previousGCData, this.newReferencesSinceLastRun);
|
|
938
1026
|
if (missingExplicitReferences.length > 0) {
|
|
939
1027
|
missingExplicitReferences.forEach((missingExplicitReference) => {
|
|
940
1028
|
logger.sendErrorEvent({
|
|
@@ -945,9 +1033,9 @@ export class GarbageCollector {
|
|
|
945
1033
|
});
|
|
946
1034
|
}
|
|
947
1035
|
// No references were added since the last run so we don't have to update reference states of any unreferenced
|
|
948
|
-
// nodes
|
|
1036
|
+
// nodes. There is no in between state at this point.
|
|
949
1037
|
if (this.newReferencesSinceLastRun.size === 0) {
|
|
950
|
-
return;
|
|
1038
|
+
return undefined;
|
|
951
1039
|
}
|
|
952
1040
|
/**
|
|
953
1041
|
* Generate a super set of the GC data that contains the nodes and edges from last run, plus any new node and
|
|
@@ -965,7 +1053,7 @@ export class GarbageCollector {
|
|
|
965
1053
|
* - We don't require DDSes handles to be stored in a referenced DDS.
|
|
966
1054
|
* - A new data store may have "root" DDSes already created and we don't detect them today.
|
|
967
1055
|
*/
|
|
968
|
-
const gcDataSuperSet = concatGarbageCollectionData(
|
|
1056
|
+
const gcDataSuperSet = concatGarbageCollectionData(previousGCData, currentGCData);
|
|
969
1057
|
const newOutboundRoutesSinceLastRun = [];
|
|
970
1058
|
this.newReferencesSinceLastRun.forEach((outboundRoutes, sourceNodeId) => {
|
|
971
1059
|
if (gcDataSuperSet.gcNodes[sourceNodeId] === undefined) {
|
|
@@ -983,16 +1071,11 @@ export class GarbageCollector {
|
|
|
983
1071
|
* Note that some of these nodes may be unreferenced now and if so, the current run will mark them as
|
|
984
1072
|
* unreferenced and add unreferenced state.
|
|
985
1073
|
*/
|
|
986
|
-
const gcResult = runGarbageCollection(gcDataSuperSet.gcNodes, [
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
nodeStateTracker.stopTracking();
|
|
992
|
-
// Delete the unreferenced state as we don't need to track it any more.
|
|
993
|
-
this.unreferencedNodesState.delete(nodeId);
|
|
994
|
-
}
|
|
995
|
-
}
|
|
1074
|
+
const gcResult = runGarbageCollection(gcDataSuperSet.gcNodes, [
|
|
1075
|
+
"/",
|
|
1076
|
+
...newOutboundRoutesSinceLastRun,
|
|
1077
|
+
]);
|
|
1078
|
+
return gcResult.referencedNodeIds;
|
|
996
1079
|
}
|
|
997
1080
|
/**
|
|
998
1081
|
* Finds all new references or outbound routes in the current graph that haven't been explicitly notified to GC.
|
|
@@ -1008,7 +1091,7 @@ export class GarbageCollector {
|
|
|
1008
1091
|
* @returns - a list of missing explicit references
|
|
1009
1092
|
*/
|
|
1010
1093
|
findMissingExplicitReferences(currentGCData, previousGCData, explicitReferences) {
|
|
1011
|
-
assert(previousGCData !== undefined, 0x2b7);
|
|
1094
|
+
assert(previousGCData !== undefined, 0x2b7 /* "Can't validate correctness without GC data from last run" */);
|
|
1012
1095
|
const currentGraph = Object.entries(currentGCData.gcNodes);
|
|
1013
1096
|
const missingExplicitReferences = [];
|
|
1014
1097
|
currentGraph.forEach(([nodeId, currentOutboundRoutes]) => {
|
|
@@ -1026,9 +1109,10 @@ export class GarbageCollector {
|
|
|
1026
1109
|
*/
|
|
1027
1110
|
currentOutboundRoutes.forEach((route) => {
|
|
1028
1111
|
const nodeType = this.runtime.getNodeType(route);
|
|
1029
|
-
if ((nodeType === GCNodeType.DataStore || nodeType === GCNodeType.Blob)
|
|
1030
|
-
|
|
1031
|
-
|
|
1112
|
+
if ((nodeType === GCNodeType.DataStore || nodeType === GCNodeType.Blob) &&
|
|
1113
|
+
!nodeId.startsWith(route) &&
|
|
1114
|
+
!previousRoutes.includes(route) &&
|
|
1115
|
+
!explicitRoutes.includes(route)) {
|
|
1032
1116
|
missingExplicitRoutes.push(route);
|
|
1033
1117
|
}
|
|
1034
1118
|
});
|
|
@@ -1100,7 +1184,8 @@ export class GarbageCollector {
|
|
|
1100
1184
|
* this will give us a view into how much deleted content a container has.
|
|
1101
1185
|
*/
|
|
1102
1186
|
logSweepEvents(logger, currentReferenceTimestampMs) {
|
|
1103
|
-
if (this.mc.config.getBoolean(disableSweepLogKey) === true ||
|
|
1187
|
+
if (this.mc.config.getBoolean(disableSweepLogKey) === true ||
|
|
1188
|
+
this.sweepTimeoutMs === undefined) {
|
|
1104
1189
|
return;
|
|
1105
1190
|
}
|
|
1106
1191
|
this.unreferencedNodesState.forEach((nodeStateTracker, nodeId) => {
|
|
@@ -1135,7 +1220,8 @@ export class GarbageCollector {
|
|
|
1135
1220
|
// If there is no reference timestamp to work with, no ops have been processed after creation. If so, skip
|
|
1136
1221
|
// logging as nothing interesting would have happened worth logging.
|
|
1137
1222
|
// If the node is active, skip logging.
|
|
1138
|
-
if (currentReferenceTimestampMs === undefined ||
|
|
1223
|
+
if (currentReferenceTimestampMs === undefined ||
|
|
1224
|
+
nodeStateTracker.state === UnreferencedState.Active) {
|
|
1139
1225
|
return;
|
|
1140
1226
|
}
|
|
1141
1227
|
// We only care about data stores and attachment blobs for this telemetry since GC only marks these objects
|
|
@@ -1200,11 +1286,18 @@ export class GarbageCollector {
|
|
|
1200
1286
|
* revived and a Revived event will be logged for it.
|
|
1201
1287
|
*/
|
|
1202
1288
|
const nodeStateTracker = this.unreferencedNodesState.get(eventProps.id);
|
|
1203
|
-
const active = nodeStateTracker === undefined ||
|
|
1289
|
+
const active = nodeStateTracker === undefined ||
|
|
1290
|
+
nodeStateTracker.state === UnreferencedState.Active;
|
|
1204
1291
|
if ((usageType === "Revived") === active) {
|
|
1205
1292
|
const pkg = await this.getNodePackagePath(eventProps.id);
|
|
1206
|
-
const fromPkg = eventProps.fromId
|
|
1207
|
-
|
|
1293
|
+
const fromPkg = eventProps.fromId
|
|
1294
|
+
? await this.getNodePackagePath(eventProps.fromId)
|
|
1295
|
+
: undefined;
|
|
1296
|
+
const event = Object.assign(Object.assign({}, propsToLog), { eventName: `${state}Object_${usageType}`, pkg: pkg
|
|
1297
|
+
? { value: pkg.join("/"), tag: TelemetryDataTag.CodeArtifact }
|
|
1298
|
+
: undefined, fromPkg: fromPkg
|
|
1299
|
+
? { value: fromPkg.join("/"), tag: TelemetryDataTag.CodeArtifact }
|
|
1300
|
+
: undefined });
|
|
1208
1301
|
if (state === UnreferencedState.Inactive) {
|
|
1209
1302
|
logger.sendTelemetryEvent(event);
|
|
1210
1303
|
}
|
|
@@ -1230,7 +1323,9 @@ function generateSortedGCState(gcState) {
|
|
|
1230
1323
|
class TimerWithNoDefaultTimeout extends Timer {
|
|
1231
1324
|
constructor(callback) {
|
|
1232
1325
|
// The default timeout/handlers will never be used since start/restart pass overrides below
|
|
1233
|
-
super(0, () => {
|
|
1326
|
+
super(0, () => {
|
|
1327
|
+
throw new Error("DefaultHandler should not be used");
|
|
1328
|
+
});
|
|
1234
1329
|
this.callback = callback;
|
|
1235
1330
|
}
|
|
1236
1331
|
start(timeoutMs) {
|