@fluidframework/container-runtime 2.0.0-internal.3.0.5 → 2.0.0-internal.3.1.1
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 +11 -1
- package/dist/containerRuntime.d.ts.map +1 -1
- package/dist/containerRuntime.js +116 -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 -13
- package/dist/dataStoreContext.d.ts.map +1 -1
- package/dist/dataStoreContext.js +68 -55
- 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 +33 -14
- package/dist/garbageCollection.d.ts.map +1 -1
- package/dist/garbageCollection.js +178 -92
- package/dist/garbageCollection.js.map +1 -1
- package/dist/garbageCollectionConstants.d.ts +1 -0
- package/dist/garbageCollectionConstants.d.ts.map +1 -1
- package/dist/garbageCollectionConstants.js +4 -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 +0 -4
- package/dist/opLifecycle/opDecompressor.d.ts.map +1 -1
- package/dist/opLifecycle/opDecompressor.js +7 -43
- 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 +20 -19
- 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 +5 -2
- 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 +35 -16
- 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 +11 -1
- package/lib/containerRuntime.d.ts.map +1 -1
- package/lib/containerRuntime.js +122 -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 -13
- package/lib/dataStoreContext.d.ts.map +1 -1
- package/lib/dataStoreContext.js +71 -58
- 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 +33 -14
- package/lib/garbageCollection.d.ts.map +1 -1
- package/lib/garbageCollection.js +180 -94
- package/lib/garbageCollection.js.map +1 -1
- package/lib/garbageCollectionConstants.d.ts +1 -0
- package/lib/garbageCollectionConstants.d.ts.map +1 -1
- package/lib/garbageCollectionConstants.js +3 -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 +0 -4
- package/lib/opLifecycle/opDecompressor.d.ts.map +1 -1
- package/lib/opLifecycle/opDecompressor.js +7 -43
- 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 +20 -19
- 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 +5 -2
- 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 +35 -16
- 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 -115
- 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 -2988
- package/src/dataStore.ts +172 -159
- package/src/dataStoreContext.ts +1098 -1055
- 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 +1860 -1688
- package/src/garbageCollectionConstants.ts +3 -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 -128
- package/src/opLifecycle/opSplitter.ts +214 -188
- package/src/opLifecycle/outbox.ts +204 -195
- 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 +165 -143
- package/src/summaryGenerator.ts +465 -410
- 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, gcTombstoneGenerationOptionName } 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 { getGCVersion, metadataBlobName, dataStoreAttributesBlobName, } from "./summaryFormat";
|
|
28
28
|
/** The types of GC nodes in the GC reference graph. */
|
|
29
29
|
export const GCNodeType = {
|
|
@@ -123,15 +123,15 @@ export class UnreferencedStateTracker {
|
|
|
123
123
|
* Graph - all nodes with their respective routes
|
|
124
124
|
*
|
|
125
125
|
* ```
|
|
126
|
-
*
|
|
126
|
+
* GC Graph
|
|
127
127
|
*
|
|
128
|
-
*
|
|
129
|
-
*
|
|
130
|
-
*
|
|
131
|
-
*
|
|
132
|
-
*
|
|
133
|
-
*
|
|
134
|
-
* NodeId = "dds1"
|
|
128
|
+
* Node
|
|
129
|
+
* NodeId = "datastore1"
|
|
130
|
+
* / \\
|
|
131
|
+
* OutboundRoute OutboundRoute
|
|
132
|
+
* / \\
|
|
133
|
+
* Node Node
|
|
134
|
+
* NodeId = "dds1" NodeId = "dds2"
|
|
135
135
|
* ```
|
|
136
136
|
*/
|
|
137
137
|
export class GarbageCollector {
|
|
@@ -163,10 +163,14 @@ export class GarbageCollector {
|
|
|
163
163
|
const baseSnapshot = createParams.baseSnapshot;
|
|
164
164
|
const metadata = createParams.metadata;
|
|
165
165
|
const readAndParseBlob = createParams.readAndParseBlob;
|
|
166
|
-
this.mc = loggerToMonitoringContext(ChildLogger.create(createParams.baseLogger, "GarbageCollector", {
|
|
166
|
+
this.mc = loggerToMonitoringContext(ChildLogger.create(createParams.baseLogger, "GarbageCollector", {
|
|
167
|
+
all: { completedGCRuns: () => this.completedRuns },
|
|
168
|
+
}));
|
|
167
169
|
// If version upgrade is not enabled, fall back to the stable GC version.
|
|
168
170
|
this.currentGCVersion =
|
|
169
|
-
this.mc.config.getBoolean(gcVersionUpgradeToV2Key) === true
|
|
171
|
+
this.mc.config.getBoolean(gcVersionUpgradeToV2Key) === true
|
|
172
|
+
? currentGCVersion
|
|
173
|
+
: stableGCVersion;
|
|
170
174
|
let prevSummaryGCVersion;
|
|
171
175
|
/**
|
|
172
176
|
* Sweep timeout is the time after which unreferenced content can be swept.
|
|
@@ -179,8 +183,8 @@ export class GarbageCollector {
|
|
|
179
183
|
function computeSweepTimeout(sessionExpiryTimeoutMs) {
|
|
180
184
|
const maxSnapshotCacheExpiryMs = 5 * oneDayMs;
|
|
181
185
|
const bufferMs = oneDayMs;
|
|
182
|
-
return sessionExpiryTimeoutMs &&
|
|
183
|
-
|
|
186
|
+
return (sessionExpiryTimeoutMs &&
|
|
187
|
+
sessionExpiryTimeoutMs + maxSnapshotCacheExpiryMs + bufferMs);
|
|
184
188
|
}
|
|
185
189
|
/**
|
|
186
190
|
* The following GC state is enabled during container creation and cannot be changed throughout its lifetime:
|
|
@@ -215,7 +219,8 @@ export class GarbageCollector {
|
|
|
215
219
|
this.sweepEnabled = this.gcOptions.sweepAllowed === true;
|
|
216
220
|
// Set the Session Expiry only if the flag is enabled and GC is enabled.
|
|
217
221
|
if (this.mc.config.getBoolean(runSessionExpiryKey) && this.gcEnabled) {
|
|
218
|
-
this.sessionExpiryTimeoutMs =
|
|
222
|
+
this.sessionExpiryTimeoutMs =
|
|
223
|
+
(_c = this.gcOptions.sessionExpiryTimeoutMs) !== null && _c !== void 0 ? _c : defaultSessionExpiryDurationMs;
|
|
219
224
|
}
|
|
220
225
|
this.sweepTimeoutMs =
|
|
221
226
|
testOverrideSweepTimeoutMs !== null && testOverrideSweepTimeoutMs !== void 0 ? testOverrideSweepTimeoutMs : computeSweepTimeout(this.sessionExpiryTimeoutMs);
|
|
@@ -230,7 +235,9 @@ export class GarbageCollector {
|
|
|
230
235
|
// If Test Override config is set, override Session Expiry timeout.
|
|
231
236
|
const overrideSessionExpiryTimeoutMs = this.mc.config.getNumber("Fluid.GarbageCollection.TestOverride.SessionExpiryMs");
|
|
232
237
|
const timeoutMs = overrideSessionExpiryTimeoutMs !== null && overrideSessionExpiryTimeoutMs !== void 0 ? overrideSessionExpiryTimeoutMs : this.sessionExpiryTimeoutMs;
|
|
233
|
-
this.sessionExpiryTimer = new Timer(timeoutMs, () => {
|
|
238
|
+
this.sessionExpiryTimer = new Timer(timeoutMs, () => {
|
|
239
|
+
this.runtime.closeFn(new ClientSessionExpiredError(`Client session expired.`, timeoutMs));
|
|
240
|
+
});
|
|
234
241
|
this.sessionExpiryTimer.start();
|
|
235
242
|
}
|
|
236
243
|
// For existing document, the latest summary is the one that we loaded from. So, use its GC version as the
|
|
@@ -245,11 +252,12 @@ export class GarbageCollector {
|
|
|
245
252
|
*
|
|
246
253
|
* These conditions can be overridden via runGCKey feature flag.
|
|
247
254
|
*/
|
|
248
|
-
this.shouldRunGC =
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
255
|
+
this.shouldRunGC =
|
|
256
|
+
(_d = this.mc.config.getBoolean(runGCKey)) !== null && _d !== void 0 ? _d :
|
|
257
|
+
// GC must be enabled for the document.
|
|
258
|
+
(this.gcEnabled &&
|
|
259
|
+
// GC must not be disabled via GC options.
|
|
260
|
+
!this.gcOptions.disableGC);
|
|
253
261
|
/**
|
|
254
262
|
* Whether sweep should run or not. The following conditions have to be met to run sweep:
|
|
255
263
|
*
|
|
@@ -261,21 +269,24 @@ export class GarbageCollector {
|
|
|
261
269
|
* feature flag.
|
|
262
270
|
*/
|
|
263
271
|
this.shouldRunSweep =
|
|
264
|
-
this.shouldRunGC
|
|
265
|
-
|
|
266
|
-
|
|
272
|
+
this.shouldRunGC &&
|
|
273
|
+
this.sweepTimeoutMs !== undefined &&
|
|
274
|
+
((_e = this.mc.config.getBoolean(runSweepKey)) !== null && _e !== void 0 ? _e : this.sweepEnabled);
|
|
267
275
|
this.trackGCState = this.mc.config.getBoolean(trackGCStateKey) === true;
|
|
268
276
|
// Override inactive timeout if test config or gc options to override it is set.
|
|
269
|
-
this.inactiveTimeoutMs =
|
|
277
|
+
this.inactiveTimeoutMs =
|
|
278
|
+
(_g = (_f = this.mc.config.getNumber("Fluid.GarbageCollection.TestOverride.InactiveTimeoutMs")) !== null && _f !== void 0 ? _f : this.gcOptions.inactiveTimeoutMs) !== null && _g !== void 0 ? _g : defaultInactiveTimeoutMs;
|
|
270
279
|
// Inactive timeout must be greater than sweep timeout since a node goes from active -> inactive -> sweep ready.
|
|
271
280
|
if (this.sweepTimeoutMs !== undefined && this.inactiveTimeoutMs > this.sweepTimeoutMs) {
|
|
272
281
|
throw new UsageError("inactive timeout should not be greater than the sweep timeout");
|
|
273
282
|
}
|
|
274
283
|
// Whether we are running in test mode. In this mode, unreferenced nodes are immediately deleted.
|
|
275
|
-
this.testMode =
|
|
284
|
+
this.testMode =
|
|
285
|
+
(_h = this.mc.config.getBoolean(gcTestModeKey)) !== null && _h !== void 0 ? _h : this.gcOptions.runGCInTestMode === true;
|
|
276
286
|
// Whether we are running in tombstone mode. This is enabled by default if sweep won't run. It can be disabled
|
|
277
287
|
// via feature flags.
|
|
278
|
-
this.tombstoneMode =
|
|
288
|
+
this.tombstoneMode =
|
|
289
|
+
!this.shouldRunSweep && this.mc.config.getBoolean(disableTombstoneKey) !== true;
|
|
279
290
|
// If GC ran in the container that generated the base snapshot, it will have a GC tree.
|
|
280
291
|
this.wasGCRunInLatestSummary = (baseSnapshot === null || baseSnapshot === void 0 ? void 0 : baseSnapshot.trees[gcTreeKey]) !== undefined;
|
|
281
292
|
// Get the GC data from the base snapshot. Use LazyPromise because we only want to do this once since it
|
|
@@ -294,7 +305,9 @@ export class GarbageCollector {
|
|
|
294
305
|
// back-compat - Older documents will have the GC blobs in each data store's summary tree. Get them and
|
|
295
306
|
// consolidate into IGarbageCollectionState format.
|
|
296
307
|
// Add a node for the root node that is not present in older snapshot format.
|
|
297
|
-
const gcState = {
|
|
308
|
+
const gcState = {
|
|
309
|
+
gcNodes: { "/": { outboundRoutes: [] } },
|
|
310
|
+
};
|
|
298
311
|
const dataStoreSnapshotTree = getSummaryForDatastores(baseSnapshot, metadata);
|
|
299
312
|
assert(dataStoreSnapshotTree !== undefined, 0x2a8 /* "Expected data store snapshot tree in base snapshot" */);
|
|
300
313
|
for (const [dsId, dsSnapshotTree] of Object.entries(dataStoreSnapshotTree.trees)) {
|
|
@@ -318,10 +331,13 @@ export class GarbageCollector {
|
|
|
318
331
|
// Prefix the data store id to the GC node ids to make them relative to the root from being
|
|
319
332
|
// relative to the data store. Similar to how its done in DataStore::getGCData.
|
|
320
333
|
const rootId = id === "/" ? dsRootId : `${dsRootId}${id}`;
|
|
321
|
-
gcState.gcNodes[rootId] = {
|
|
334
|
+
gcState.gcNodes[rootId] = {
|
|
335
|
+
outboundRoutes: Array.from(outboundRoutes),
|
|
336
|
+
};
|
|
322
337
|
}
|
|
323
338
|
assert(gcState.gcNodes[dsRootId] !== undefined, 0x2a9 /* GC nodes for data store not in GC blob */);
|
|
324
|
-
gcState.gcNodes[dsRootId].unreferencedTimestampMs =
|
|
339
|
+
gcState.gcNodes[dsRootId].unreferencedTimestampMs =
|
|
340
|
+
gcSummaryDetails.unrefTimestamp;
|
|
325
341
|
}
|
|
326
342
|
// If there is only one node (root node just added above), either GC is disabled or we are loading from
|
|
327
343
|
// the first summary generated by detached container. In both cases, GC was not run - return undefined.
|
|
@@ -415,8 +431,8 @@ export class GarbageCollector {
|
|
|
415
431
|
* 4.2. This client's latest summary was updated from a snapshot that has a different GC version.
|
|
416
432
|
*/
|
|
417
433
|
get summaryStateNeedsReset() {
|
|
418
|
-
return this.gcStateNeedsReset ||
|
|
419
|
-
(this.shouldRunGC && this.latestSummaryGCVersion !== this.currentGCVersion);
|
|
434
|
+
return (this.gcStateNeedsReset ||
|
|
435
|
+
(this.shouldRunGC && this.latestSummaryGCVersion !== this.currentGCVersion));
|
|
420
436
|
}
|
|
421
437
|
/**
|
|
422
438
|
* Tells whether the GC state needs to be reset. This can happen under 3 conditions:
|
|
@@ -431,7 +447,7 @@ export class GarbageCollector {
|
|
|
431
447
|
*
|
|
432
448
|
* Note that the state will be reset only once for the first summary generated after this returns true. After that,
|
|
433
449
|
* this will return false.
|
|
434
|
-
|
|
450
|
+
*/
|
|
435
451
|
get gcStateNeedsReset() {
|
|
436
452
|
return this.wasGCRunInLatestSummary !== this.shouldRunGC;
|
|
437
453
|
}
|
|
@@ -462,6 +478,7 @@ export class GarbageCollector {
|
|
|
462
478
|
// If running in tombstone mode, initialize the tombstone state from the snapshot. Also, notify the runtime of
|
|
463
479
|
// tombstone routes.
|
|
464
480
|
if (this.tombstoneMode && baseSnapshotData.tombstones !== undefined) {
|
|
481
|
+
// Create a copy since we are writing from a source we don't control
|
|
465
482
|
this.tombstones = Array.from(baseSnapshotData.tombstones);
|
|
466
483
|
this.runtime.updateTombstonedRoutes(this.tombstones);
|
|
467
484
|
}
|
|
@@ -489,7 +506,6 @@ export class GarbageCollector {
|
|
|
489
506
|
for (const [, nodeStateTracker] of this.unreferencedNodesState) {
|
|
490
507
|
nodeStateTracker.stopTracking();
|
|
491
508
|
}
|
|
492
|
-
;
|
|
493
509
|
this.unreferencedNodesState.clear();
|
|
494
510
|
// If running sweep, the tombstone state represents the list of nodes that have been deleted during sweep.
|
|
495
511
|
// If running in tombstone mode, the tombstone state represents the list of nodes that have been marked as
|
|
@@ -497,7 +513,9 @@ export class GarbageCollector {
|
|
|
497
513
|
// If this call is because we are refreshing from a snapshot due to an ack, it is likely that the GC state
|
|
498
514
|
// in the snapshot is newer than this client's. And so, the deleted / tombstone nodes need to be updated.
|
|
499
515
|
if (this.shouldRunSweep) {
|
|
500
|
-
const snapshotDeletedNodes = (snapshotData === null || snapshotData === void 0 ? void 0 : snapshotData.
|
|
516
|
+
const snapshotDeletedNodes = (snapshotData === null || snapshotData === void 0 ? void 0 : snapshotData.deletedNodes)
|
|
517
|
+
? new Set(snapshotData.deletedNodes)
|
|
518
|
+
: undefined;
|
|
501
519
|
// If the snapshot contains deleted nodes that are not yet deleted by this client, ask the runtime to
|
|
502
520
|
// delete them.
|
|
503
521
|
if (snapshotDeletedNodes !== undefined) {
|
|
@@ -571,11 +589,13 @@ export class GarbageCollector {
|
|
|
571
589
|
* Runs garbage collection and updates the reference / used state of the nodes in the container.
|
|
572
590
|
* @returns stats of the GC run or undefined if GC did not run.
|
|
573
591
|
*/
|
|
574
|
-
async collectGarbage(options
|
|
592
|
+
async collectGarbage(options) {
|
|
575
593
|
var _a;
|
|
576
594
|
const fullGC = (_a = options.fullGC) !== null && _a !== void 0 ? _a : (this.gcOptions.runFullGC === true || this.summaryStateNeedsReset);
|
|
577
595
|
const logger = options.logger
|
|
578
|
-
? ChildLogger.create(options.logger, undefined, {
|
|
596
|
+
? ChildLogger.create(options.logger, undefined, {
|
|
597
|
+
all: { completedGCRuns: () => this.completedRuns },
|
|
598
|
+
})
|
|
579
599
|
: this.mc.logger;
|
|
580
600
|
/**
|
|
581
601
|
* If there is no current reference timestamp, skip running GC. We need the current timestamp to track
|
|
@@ -594,8 +614,6 @@ export class GarbageCollector {
|
|
|
594
614
|
});
|
|
595
615
|
return undefined;
|
|
596
616
|
}
|
|
597
|
-
// Add the options that are used to run GC to the telemetry context.
|
|
598
|
-
telemetryContext === null || telemetryContext === void 0 ? void 0 : telemetryContext.setAll("fluid_GC", "Options", { fullGC, runSweep: options.runSweep });
|
|
599
617
|
return PerformanceEvent.timedExecAsync(logger, { eventName: "GarbageCollection" }, async (event) => {
|
|
600
618
|
await this.runPreGCSteps();
|
|
601
619
|
// Get the runtime's GC data and run GC on the reference graph in it.
|
|
@@ -617,26 +635,29 @@ export class GarbageCollector {
|
|
|
617
635
|
// Generate statistics from the current run. This is done before updating the current state because it
|
|
618
636
|
// generates some of its data based on previous state of the system.
|
|
619
637
|
const gcStats = this.generateStats(gcResult);
|
|
620
|
-
// Update the state
|
|
621
|
-
|
|
622
|
-
this.updateStateSinceLastRun(gcData, logger);
|
|
623
|
-
// Update the current state and update the runtime of all routes or ids that used as per the GC run.
|
|
624
|
-
this.updateCurrentState(gcData, gcResult, currentReferenceTimestampMs);
|
|
638
|
+
// Update the current mark state and update the runtime of all used routes or ids that used as per the GC run.
|
|
639
|
+
const sweepReadyNodes = this.updateMarkPhase(gcData, gcResult, currentReferenceTimestampMs, logger);
|
|
625
640
|
this.runtime.updateUsedRoutes(gcResult.referencedNodeIds);
|
|
626
641
|
// Log events for objects that are ready to be deleted by sweep. When we have sweep enabled, we will
|
|
627
642
|
// delete these objects here instead.
|
|
628
643
|
this.logSweepEvents(logger, currentReferenceTimestampMs);
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
644
|
+
let updatedGCData = gcData;
|
|
645
|
+
if (this.shouldRunSweep) {
|
|
646
|
+
updatedGCData = this.runSweepPhase(sweepReadyNodes, gcData);
|
|
647
|
+
}
|
|
648
|
+
else if (this.testMode) {
|
|
649
|
+
// If we are running in GC test mode, delete objects for unused routes. This enables testing scenarios
|
|
650
|
+
// involving access to deleted data.
|
|
632
651
|
this.runtime.updateUnusedRoutes(gcResult.deletedNodeIds);
|
|
633
652
|
}
|
|
634
653
|
else if (this.tombstoneMode) {
|
|
654
|
+
this.tombstones = sweepReadyNodes;
|
|
635
655
|
// If we are running in GC tombstone mode, update tombstoned routes. This enables testing scenarios
|
|
636
656
|
// involving access to "deleted" data without actually deleting the data from summaries.
|
|
637
657
|
// Note: we will not tombstone in test mode.
|
|
638
658
|
this.runtime.updateTombstonedRoutes(this.tombstones);
|
|
639
659
|
}
|
|
660
|
+
this.gcDataFromLastRun = cloneGCData(updatedGCData);
|
|
640
661
|
// Log pending unreferenced events such as a node being used after inactive. This is done after GC runs and
|
|
641
662
|
// updates its state so that we don't send false positives based on intermediate state. For example, we may get
|
|
642
663
|
// reference to an unreferenced node from another unreferenced node which means the node wasn't revived.
|
|
@@ -668,7 +689,9 @@ export class GarbageCollector {
|
|
|
668
689
|
: undefined;
|
|
669
690
|
// If running in tombstone mode, serialize and write tombstones, if any.
|
|
670
691
|
const serializedTombstones = this.tombstoneMode
|
|
671
|
-
?
|
|
692
|
+
? this.tombstones.length > 0
|
|
693
|
+
? JSON.stringify(this.tombstones.sort())
|
|
694
|
+
: undefined
|
|
672
695
|
: undefined;
|
|
673
696
|
/**
|
|
674
697
|
* Incremental summary of GC data - If none of GC state, deleted nodes or tombstones changed since last summary,
|
|
@@ -677,11 +700,15 @@ export class GarbageCollector {
|
|
|
677
700
|
* for each of these that did not change, write a summary handle.
|
|
678
701
|
*/
|
|
679
702
|
if (this.trackGCState) {
|
|
680
|
-
this.pendingSummaryData = {
|
|
703
|
+
this.pendingSummaryData = {
|
|
704
|
+
serializedGCState,
|
|
705
|
+
serializedTombstones,
|
|
706
|
+
serializedDeletedNodes,
|
|
707
|
+
};
|
|
681
708
|
if (trackState && !fullTree && this.latestSummaryData !== undefined) {
|
|
682
709
|
// If nothing changed since last summary, send a summary handle for the entire GC data.
|
|
683
|
-
if (this.latestSummaryData.serializedGCState === serializedGCState
|
|
684
|
-
|
|
710
|
+
if (this.latestSummaryData.serializedGCState === serializedGCState &&
|
|
711
|
+
this.latestSummaryData.serializedTombstones === serializedTombstones) {
|
|
685
712
|
const stats = mergeStats();
|
|
686
713
|
stats.handleNodeCount++;
|
|
687
714
|
return {
|
|
@@ -724,7 +751,8 @@ export class GarbageCollector {
|
|
|
724
751
|
// If tombstones exist, write a summary handle if it hasn't changed. If it has changed, write a
|
|
725
752
|
// summary blob.
|
|
726
753
|
if (serializedTombstones !== undefined) {
|
|
727
|
-
if (((_b = this.latestSummaryData) === null || _b === void 0 ? void 0 : _b.serializedTombstones) === serializedTombstones &&
|
|
754
|
+
if (((_b = this.latestSummaryData) === null || _b === void 0 ? void 0 : _b.serializedTombstones) === serializedTombstones &&
|
|
755
|
+
trackState) {
|
|
728
756
|
builder.addHandle(gcTombstoneBlobKey, SummaryType.Blob, `/${gcTreeKey}/${gcTombstoneBlobKey}`);
|
|
729
757
|
}
|
|
730
758
|
else {
|
|
@@ -736,7 +764,8 @@ export class GarbageCollector {
|
|
|
736
764
|
return builder.getSummaryTree();
|
|
737
765
|
}
|
|
738
766
|
// If the deleted nodes hasn't changed, write a summary handle, else write a summary blob for it.
|
|
739
|
-
if (((_c = this.latestSummaryData) === null || _c === void 0 ? void 0 : _c.serializedDeletedNodes) === serializedDeletedNodes &&
|
|
767
|
+
if (((_c = this.latestSummaryData) === null || _c === void 0 ? void 0 : _c.serializedDeletedNodes) === serializedDeletedNodes &&
|
|
768
|
+
trackState) {
|
|
740
769
|
builder.addHandle(gcDeletedBlobKey, SummaryType.Blob, `/${gcTreeKey}/${gcDeletedBlobKey}`);
|
|
741
770
|
}
|
|
742
771
|
else {
|
|
@@ -800,7 +829,11 @@ export class GarbageCollector {
|
|
|
800
829
|
// to be at least one op (summary op / ack, if nothing else) if a snapshot was taken.
|
|
801
830
|
const currentReferenceTimestampMs = this.runtime.getCurrentReferenceTimestampMs();
|
|
802
831
|
if (currentReferenceTimestampMs === undefined) {
|
|
803
|
-
throw DataProcessingError.create("No reference timestamp when updating GC state from snapshot", "refreshLatestSummary", undefined, {
|
|
832
|
+
throw DataProcessingError.create("No reference timestamp when updating GC state from snapshot", "refreshLatestSummary", undefined, {
|
|
833
|
+
proposalHandle,
|
|
834
|
+
summaryRefSeq: result.summaryRefSeq,
|
|
835
|
+
details: JSON.stringify(this.configs),
|
|
836
|
+
});
|
|
804
837
|
}
|
|
805
838
|
const gcSnapshotTree = snapshotTree.trees[gcTreeKey];
|
|
806
839
|
// If GC ran in the container that generated this snapshot, it will have a GC tree.
|
|
@@ -857,12 +890,12 @@ export class GarbageCollector {
|
|
|
857
890
|
else if (nodeType === GCNodeType.Blob) {
|
|
858
891
|
eventName = "GC_Tombstone_Blob_Revived";
|
|
859
892
|
}
|
|
860
|
-
|
|
893
|
+
sendGCUnexpectedUsageEvent(this.mc, {
|
|
861
894
|
eventName,
|
|
862
895
|
category: "generic",
|
|
863
|
-
isSummarizerClient: this.isSummarizerClient,
|
|
864
896
|
url: trimLeadingSlashes(toNodePath),
|
|
865
897
|
nodeType,
|
|
898
|
+
gcTombstoneEnforcementAllowed: this.runtime.gcTombstoneEnforcementAllowed,
|
|
866
899
|
}, undefined /* packagePath */);
|
|
867
900
|
}
|
|
868
901
|
}
|
|
@@ -886,13 +919,16 @@ export class GarbageCollector {
|
|
|
886
919
|
* @param gcData - The data representing the reference graph on which GC is run.
|
|
887
920
|
* @param gcResult - The result of the GC run on the gcData.
|
|
888
921
|
* @param currentReferenceTimestampMs - The timestamp to be used for unreferenced nodes' timestamp.
|
|
922
|
+
* @returns - A list of sweep ready nodes. (Nodes ready to be deleted)
|
|
889
923
|
*/
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
924
|
+
updateMarkPhase(gcData, gcResult, currentReferenceTimestampMs, logger) {
|
|
925
|
+
var _a;
|
|
926
|
+
// Get references from the current GC run + references between previous and current run and then update each
|
|
927
|
+
// node's state
|
|
928
|
+
const allNodesReferencedBetweenGCs = (_a = this.findAllNodesReferencedBetweenGCs(gcData, this.gcDataFromLastRun, logger)) !== null && _a !== void 0 ? _a : gcResult.referencedNodeIds;
|
|
893
929
|
this.newReferencesSinceLastRun.clear();
|
|
894
930
|
// Iterate through the referenced nodes and stop tracking if they were unreferenced before.
|
|
895
|
-
for (const nodeId of
|
|
931
|
+
for (const nodeId of allNodesReferencedBetweenGCs) {
|
|
896
932
|
const nodeStateTracker = this.unreferencedNodesState.get(nodeId);
|
|
897
933
|
if (nodeStateTracker !== undefined) {
|
|
898
934
|
// Stop tracking so as to clear out any running timers.
|
|
@@ -905,7 +941,10 @@ export class GarbageCollector {
|
|
|
905
941
|
* If a node became unreferenced in this run, start tracking it.
|
|
906
942
|
* If a node was already unreferenced, update its tracking information. Since the current reference time is
|
|
907
943
|
* from the ops seen, this will ensure that we keep updating the unreferenced state as time moves forward.
|
|
944
|
+
*
|
|
945
|
+
* If a node is sweep ready, store and then return it.
|
|
908
946
|
*/
|
|
947
|
+
const sweepReadyNodes = [];
|
|
909
948
|
for (const nodeId of gcResult.deletedNodeIds) {
|
|
910
949
|
const nodeStateTracker = this.unreferencedNodesState.get(nodeId);
|
|
911
950
|
if (nodeStateTracker === undefined) {
|
|
@@ -913,14 +952,51 @@ export class GarbageCollector {
|
|
|
913
952
|
}
|
|
914
953
|
else {
|
|
915
954
|
nodeStateTracker.updateTracking(currentReferenceTimestampMs);
|
|
916
|
-
if (
|
|
917
|
-
|
|
918
|
-
if (nodeType === GCNodeType.DataStore || nodeType === GCNodeType.Blob) {
|
|
919
|
-
this.tombstones.push(nodeId);
|
|
920
|
-
}
|
|
955
|
+
if (nodeStateTracker.state === UnreferencedState.SweepReady) {
|
|
956
|
+
sweepReadyNodes.push(nodeId);
|
|
921
957
|
}
|
|
922
958
|
}
|
|
923
959
|
}
|
|
960
|
+
return sweepReadyNodes;
|
|
961
|
+
}
|
|
962
|
+
/**
|
|
963
|
+
* Deletes nodes from both the runtime and garbage collection
|
|
964
|
+
* @param sweepReadyNodes - nodes that are ready to be deleted
|
|
965
|
+
*/
|
|
966
|
+
runSweepPhase(sweepReadyNodes, gcData) {
|
|
967
|
+
// TODO: GC:Validation - validate that removed routes are not double deleted
|
|
968
|
+
// TODO: GC:Validation - validate that the child routes of removed routes are deleted as well
|
|
969
|
+
const sweptRoutes = this.runtime.deleteUnusedNodes(sweepReadyNodes);
|
|
970
|
+
const updatedGCData = this.deleteSweptRoutes(sweptRoutes, gcData);
|
|
971
|
+
for (const nodeId of sweptRoutes) {
|
|
972
|
+
const nodeStateTracker = this.unreferencedNodesState.get(nodeId);
|
|
973
|
+
// TODO: GC:Validation - assert that the nodeStateTracker is defined
|
|
974
|
+
if (nodeStateTracker !== undefined) {
|
|
975
|
+
// Stop tracking so as to clear out any running timers.
|
|
976
|
+
nodeStateTracker.stopTracking();
|
|
977
|
+
// Delete the node as we don't need to track it any more.
|
|
978
|
+
this.unreferencedNodesState.delete(nodeId);
|
|
979
|
+
}
|
|
980
|
+
// TODO: GC:Validation - assert that the deleted node is not a duplicate
|
|
981
|
+
this.deletedNodes.add(nodeId);
|
|
982
|
+
}
|
|
983
|
+
return updatedGCData;
|
|
984
|
+
}
|
|
985
|
+
/**
|
|
986
|
+
* @returns IGarbageCollectionData after deleting the sweptRoutes from the gcData
|
|
987
|
+
*/
|
|
988
|
+
deleteSweptRoutes(sweptRoutes, gcData) {
|
|
989
|
+
const sweptRoutesSet = new Set(sweptRoutes);
|
|
990
|
+
const gcNodes = {};
|
|
991
|
+
for (const [id, outboundRoutes] of Object.entries(gcData.gcNodes)) {
|
|
992
|
+
if (!sweptRoutesSet.has(id)) {
|
|
993
|
+
gcNodes[id] = Array.from(outboundRoutes);
|
|
994
|
+
}
|
|
995
|
+
}
|
|
996
|
+
// TODO: GC:Validation - assert that the nodeId is in gcData
|
|
997
|
+
return {
|
|
998
|
+
gcNodes,
|
|
999
|
+
};
|
|
924
1000
|
}
|
|
925
1001
|
/**
|
|
926
1002
|
* Since GC runs periodically, the GC data that is generated only tells us the state of the world at that point in
|
|
@@ -932,16 +1008,19 @@ export class GarbageCollector {
|
|
|
932
1008
|
* 2. A reference is added from one unreferenced node to one or more unreferenced nodes. Even though the node[s] were
|
|
933
1009
|
* unreferenced, they could have been accessed and in-memory reference to them added.
|
|
934
1010
|
*
|
|
935
|
-
* This function identifies nodes that were referenced since last run
|
|
1011
|
+
* This function identifies nodes that were referenced since the last run.
|
|
936
1012
|
* If these nodes are currently unreferenced, they will be assigned new unreferenced state by the current run.
|
|
1013
|
+
*
|
|
1014
|
+
* @returns - a list of all nodes referenced from the last local summary until now.
|
|
937
1015
|
*/
|
|
938
|
-
|
|
1016
|
+
findAllNodesReferencedBetweenGCs(currentGCData, previousGCData, logger) {
|
|
939
1017
|
// If we haven't run GC before there is nothing to do.
|
|
940
|
-
|
|
941
|
-
|
|
1018
|
+
// No previousGCData, means nothing is unreferenced, and there are no reference state trackers to clear
|
|
1019
|
+
if (previousGCData === undefined) {
|
|
1020
|
+
return undefined;
|
|
942
1021
|
}
|
|
943
1022
|
// Find any references that haven't been identified correctly.
|
|
944
|
-
const missingExplicitReferences = this.findMissingExplicitReferences(currentGCData,
|
|
1023
|
+
const missingExplicitReferences = this.findMissingExplicitReferences(currentGCData, previousGCData, this.newReferencesSinceLastRun);
|
|
945
1024
|
if (missingExplicitReferences.length > 0) {
|
|
946
1025
|
missingExplicitReferences.forEach((missingExplicitReference) => {
|
|
947
1026
|
logger.sendErrorEvent({
|
|
@@ -952,9 +1031,9 @@ export class GarbageCollector {
|
|
|
952
1031
|
});
|
|
953
1032
|
}
|
|
954
1033
|
// No references were added since the last run so we don't have to update reference states of any unreferenced
|
|
955
|
-
// nodes
|
|
1034
|
+
// nodes. There is no in between state at this point.
|
|
956
1035
|
if (this.newReferencesSinceLastRun.size === 0) {
|
|
957
|
-
return;
|
|
1036
|
+
return undefined;
|
|
958
1037
|
}
|
|
959
1038
|
/**
|
|
960
1039
|
* Generate a super set of the GC data that contains the nodes and edges from last run, plus any new node and
|
|
@@ -972,7 +1051,7 @@ export class GarbageCollector {
|
|
|
972
1051
|
* - We don't require DDSes handles to be stored in a referenced DDS.
|
|
973
1052
|
* - A new data store may have "root" DDSes already created and we don't detect them today.
|
|
974
1053
|
*/
|
|
975
|
-
const gcDataSuperSet = concatGarbageCollectionData(
|
|
1054
|
+
const gcDataSuperSet = concatGarbageCollectionData(previousGCData, currentGCData);
|
|
976
1055
|
const newOutboundRoutesSinceLastRun = [];
|
|
977
1056
|
this.newReferencesSinceLastRun.forEach((outboundRoutes, sourceNodeId) => {
|
|
978
1057
|
if (gcDataSuperSet.gcNodes[sourceNodeId] === undefined) {
|
|
@@ -990,16 +1069,11 @@ export class GarbageCollector {
|
|
|
990
1069
|
* Note that some of these nodes may be unreferenced now and if so, the current run will mark them as
|
|
991
1070
|
* unreferenced and add unreferenced state.
|
|
992
1071
|
*/
|
|
993
|
-
const gcResult = runGarbageCollection(gcDataSuperSet.gcNodes, [
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
nodeStateTracker.stopTracking();
|
|
999
|
-
// Delete the unreferenced state as we don't need to track it any more.
|
|
1000
|
-
this.unreferencedNodesState.delete(nodeId);
|
|
1001
|
-
}
|
|
1002
|
-
}
|
|
1072
|
+
const gcResult = runGarbageCollection(gcDataSuperSet.gcNodes, [
|
|
1073
|
+
"/",
|
|
1074
|
+
...newOutboundRoutesSinceLastRun,
|
|
1075
|
+
]);
|
|
1076
|
+
return gcResult.referencedNodeIds;
|
|
1003
1077
|
}
|
|
1004
1078
|
/**
|
|
1005
1079
|
* Finds all new references or outbound routes in the current graph that haven't been explicitly notified to GC.
|
|
@@ -1015,7 +1089,7 @@ export class GarbageCollector {
|
|
|
1015
1089
|
* @returns - a list of missing explicit references
|
|
1016
1090
|
*/
|
|
1017
1091
|
findMissingExplicitReferences(currentGCData, previousGCData, explicitReferences) {
|
|
1018
|
-
assert(previousGCData !== undefined, 0x2b7);
|
|
1092
|
+
assert(previousGCData !== undefined, 0x2b7 /* "Can't validate correctness without GC data from last run" */);
|
|
1019
1093
|
const currentGraph = Object.entries(currentGCData.gcNodes);
|
|
1020
1094
|
const missingExplicitReferences = [];
|
|
1021
1095
|
currentGraph.forEach(([nodeId, currentOutboundRoutes]) => {
|
|
@@ -1033,9 +1107,10 @@ export class GarbageCollector {
|
|
|
1033
1107
|
*/
|
|
1034
1108
|
currentOutboundRoutes.forEach((route) => {
|
|
1035
1109
|
const nodeType = this.runtime.getNodeType(route);
|
|
1036
|
-
if ((nodeType === GCNodeType.DataStore || nodeType === GCNodeType.Blob)
|
|
1037
|
-
|
|
1038
|
-
|
|
1110
|
+
if ((nodeType === GCNodeType.DataStore || nodeType === GCNodeType.Blob) &&
|
|
1111
|
+
!nodeId.startsWith(route) &&
|
|
1112
|
+
!previousRoutes.includes(route) &&
|
|
1113
|
+
!explicitRoutes.includes(route)) {
|
|
1039
1114
|
missingExplicitRoutes.push(route);
|
|
1040
1115
|
}
|
|
1041
1116
|
});
|
|
@@ -1107,7 +1182,8 @@ export class GarbageCollector {
|
|
|
1107
1182
|
* this will give us a view into how much deleted content a container has.
|
|
1108
1183
|
*/
|
|
1109
1184
|
logSweepEvents(logger, currentReferenceTimestampMs) {
|
|
1110
|
-
if (this.mc.config.getBoolean(disableSweepLogKey) === true ||
|
|
1185
|
+
if (this.mc.config.getBoolean(disableSweepLogKey) === true ||
|
|
1186
|
+
this.sweepTimeoutMs === undefined) {
|
|
1111
1187
|
return;
|
|
1112
1188
|
}
|
|
1113
1189
|
this.unreferencedNodesState.forEach((nodeStateTracker, nodeId) => {
|
|
@@ -1142,7 +1218,8 @@ export class GarbageCollector {
|
|
|
1142
1218
|
// If there is no reference timestamp to work with, no ops have been processed after creation. If so, skip
|
|
1143
1219
|
// logging as nothing interesting would have happened worth logging.
|
|
1144
1220
|
// If the node is active, skip logging.
|
|
1145
|
-
if (currentReferenceTimestampMs === undefined ||
|
|
1221
|
+
if (currentReferenceTimestampMs === undefined ||
|
|
1222
|
+
nodeStateTracker.state === UnreferencedState.Active) {
|
|
1146
1223
|
return;
|
|
1147
1224
|
}
|
|
1148
1225
|
// We only care about data stores and attachment blobs for this telemetry since GC only marks these objects
|
|
@@ -1201,11 +1278,18 @@ export class GarbageCollector {
|
|
|
1201
1278
|
* revived and a Revived event will be logged for it.
|
|
1202
1279
|
*/
|
|
1203
1280
|
const nodeStateTracker = this.unreferencedNodesState.get(eventProps.id);
|
|
1204
|
-
const active = nodeStateTracker === undefined ||
|
|
1281
|
+
const active = nodeStateTracker === undefined ||
|
|
1282
|
+
nodeStateTracker.state === UnreferencedState.Active;
|
|
1205
1283
|
if ((usageType === "Revived") === active) {
|
|
1206
1284
|
const pkg = await this.getNodePackagePath(eventProps.id);
|
|
1207
|
-
const fromPkg = eventProps.fromId
|
|
1208
|
-
|
|
1285
|
+
const fromPkg = eventProps.fromId
|
|
1286
|
+
? await this.getNodePackagePath(eventProps.fromId)
|
|
1287
|
+
: undefined;
|
|
1288
|
+
const event = Object.assign(Object.assign({}, propsToLog), { eventName: `${state}Object_${usageType}`, pkg: pkg
|
|
1289
|
+
? { value: pkg.join("/"), tag: TelemetryDataTag.CodeArtifact }
|
|
1290
|
+
: undefined, fromPkg: fromPkg
|
|
1291
|
+
? { value: fromPkg.join("/"), tag: TelemetryDataTag.CodeArtifact }
|
|
1292
|
+
: undefined });
|
|
1209
1293
|
if (state === UnreferencedState.Inactive) {
|
|
1210
1294
|
logger.sendTelemetryEvent(event);
|
|
1211
1295
|
}
|
|
@@ -1231,7 +1315,9 @@ function generateSortedGCState(gcState) {
|
|
|
1231
1315
|
class TimerWithNoDefaultTimeout extends Timer {
|
|
1232
1316
|
constructor(callback) {
|
|
1233
1317
|
// The default timeout/handlers will never be used since start/restart pass overrides below
|
|
1234
|
-
super(0, () => {
|
|
1318
|
+
super(0, () => {
|
|
1319
|
+
throw new Error("DefaultHandler should not be used");
|
|
1320
|
+
});
|
|
1235
1321
|
this.callback = callback;
|
|
1236
1322
|
}
|
|
1237
1323
|
start(timeoutMs) {
|