@fluidframework/container-runtime 2.0.0-internal.2.1.1 → 2.0.0-internal.2.2.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 +1 -1
- package/dist/blobManager.d.ts +20 -5
- package/dist/blobManager.d.ts.map +1 -1
- package/dist/blobManager.js +57 -15
- package/dist/blobManager.js.map +1 -1
- package/dist/containerRuntime.d.ts +39 -40
- package/dist/containerRuntime.d.ts.map +1 -1
- package/dist/containerRuntime.js +115 -278
- package/dist/containerRuntime.js.map +1 -1
- package/dist/dataStoreContext.d.ts +3 -1
- package/dist/dataStoreContext.d.ts.map +1 -1
- package/dist/dataStoreContext.js +21 -3
- package/dist/dataStoreContext.js.map +1 -1
- package/dist/dataStores.d.ts +8 -5
- package/dist/dataStores.d.ts.map +1 -1
- package/dist/dataStores.js +26 -13
- package/dist/dataStores.js.map +1 -1
- package/dist/garbageCollection.d.ts +15 -17
- package/dist/garbageCollection.d.ts.map +1 -1
- package/dist/garbageCollection.js +92 -106
- package/dist/garbageCollection.js.map +1 -1
- package/dist/garbageCollectionConstants.d.ts +19 -0
- package/dist/garbageCollectionConstants.d.ts.map +1 -0
- package/dist/garbageCollectionConstants.js +34 -0
- package/dist/garbageCollectionConstants.js.map +1 -0
- package/dist/gcSweepReadyUsageDetection.js +2 -2
- package/dist/gcSweepReadyUsageDetection.js.map +1 -1
- package/dist/index.d.ts +4 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -6
- package/dist/index.js.map +1 -1
- package/dist/opLifecycle/batchManager.d.ts +30 -0
- package/dist/opLifecycle/batchManager.d.ts.map +1 -0
- package/dist/{batchManager.js → opLifecycle/batchManager.js} +25 -10
- package/dist/opLifecycle/batchManager.js.map +1 -0
- package/dist/opLifecycle/definitions.d.ts +40 -0
- package/dist/opLifecycle/definitions.d.ts.map +1 -0
- package/dist/opLifecycle/definitions.js +7 -0
- package/dist/opLifecycle/definitions.js.map +1 -0
- package/dist/opLifecycle/index.d.ts +12 -0
- package/dist/opLifecycle/index.d.ts.map +1 -0
- package/dist/opLifecycle/index.js +21 -0
- package/dist/opLifecycle/index.js.map +1 -0
- package/dist/opLifecycle/opCompressor.d.ts +18 -0
- package/dist/opLifecycle/opCompressor.d.ts.map +1 -0
- package/dist/opLifecycle/opCompressor.js +53 -0
- package/dist/opLifecycle/opCompressor.js.map +1 -0
- package/dist/opLifecycle/opDecompressor.d.ts +20 -0
- package/dist/opLifecycle/opDecompressor.d.ts.map +1 -0
- package/dist/opLifecycle/opDecompressor.js +72 -0
- package/dist/opLifecycle/opDecompressor.js.map +1 -0
- package/dist/opLifecycle/opSplitter.d.ts +17 -0
- package/dist/opLifecycle/opSplitter.d.ts.map +1 -0
- package/dist/opLifecycle/opSplitter.js +61 -0
- package/dist/opLifecycle/opSplitter.js.map +1 -0
- package/dist/opLifecycle/outbox.d.ts +47 -0
- package/dist/opLifecycle/outbox.d.ts.map +1 -0
- package/dist/opLifecycle/outbox.js +153 -0
- package/dist/opLifecycle/outbox.js.map +1 -0
- package/dist/opLifecycle/remoteMessageProcessor.d.ts +26 -0
- package/dist/opLifecycle/remoteMessageProcessor.d.ts.map +1 -0
- package/dist/opLifecycle/remoteMessageProcessor.js +81 -0
- package/dist/opLifecycle/remoteMessageProcessor.js.map +1 -0
- package/dist/packageVersion.d.ts +1 -1
- package/dist/packageVersion.js +1 -1
- package/dist/packageVersion.js.map +1 -1
- package/dist/summaryFormat.js +2 -2
- package/dist/summaryFormat.js.map +1 -1
- package/lib/blobManager.d.ts +20 -5
- package/lib/blobManager.d.ts.map +1 -1
- package/lib/blobManager.js +59 -17
- package/lib/blobManager.js.map +1 -1
- package/lib/containerRuntime.d.ts +39 -40
- package/lib/containerRuntime.d.ts.map +1 -1
- package/lib/containerRuntime.js +113 -275
- package/lib/containerRuntime.js.map +1 -1
- package/lib/dataStoreContext.d.ts +3 -1
- package/lib/dataStoreContext.d.ts.map +1 -1
- package/lib/dataStoreContext.js +23 -5
- package/lib/dataStoreContext.js.map +1 -1
- package/lib/dataStores.d.ts +8 -5
- package/lib/dataStores.d.ts.map +1 -1
- package/lib/dataStores.js +28 -15
- package/lib/dataStores.js.map +1 -1
- package/lib/garbageCollection.d.ts +15 -17
- package/lib/garbageCollection.d.ts.map +1 -1
- package/lib/garbageCollection.js +72 -86
- package/lib/garbageCollection.js.map +1 -1
- package/lib/garbageCollectionConstants.d.ts +19 -0
- package/lib/garbageCollectionConstants.d.ts.map +1 -0
- package/lib/garbageCollectionConstants.js +31 -0
- package/lib/garbageCollectionConstants.js.map +1 -0
- package/lib/gcSweepReadyUsageDetection.js +1 -1
- package/lib/gcSweepReadyUsageDetection.js.map +1 -1
- package/lib/index.d.ts +4 -2
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +3 -2
- package/lib/index.js.map +1 -1
- package/lib/opLifecycle/batchManager.d.ts +30 -0
- package/lib/opLifecycle/batchManager.d.ts.map +1 -0
- package/lib/{batchManager.js → opLifecycle/batchManager.js} +25 -10
- package/lib/opLifecycle/batchManager.js.map +1 -0
- package/lib/opLifecycle/definitions.d.ts +40 -0
- package/lib/opLifecycle/definitions.d.ts.map +1 -0
- package/lib/opLifecycle/definitions.js +6 -0
- package/lib/opLifecycle/definitions.js.map +1 -0
- package/lib/opLifecycle/index.d.ts +12 -0
- package/lib/opLifecycle/index.d.ts.map +1 -0
- package/lib/opLifecycle/index.js +11 -0
- package/lib/opLifecycle/index.js.map +1 -0
- package/lib/opLifecycle/opCompressor.d.ts +18 -0
- package/lib/opLifecycle/opCompressor.d.ts.map +1 -0
- package/lib/opLifecycle/opCompressor.js +49 -0
- package/lib/opLifecycle/opCompressor.js.map +1 -0
- package/lib/opLifecycle/opDecompressor.d.ts +20 -0
- package/lib/opLifecycle/opDecompressor.d.ts.map +1 -0
- package/lib/opLifecycle/opDecompressor.js +68 -0
- package/lib/opLifecycle/opDecompressor.js.map +1 -0
- package/lib/opLifecycle/opSplitter.d.ts +17 -0
- package/lib/opLifecycle/opSplitter.d.ts.map +1 -0
- package/lib/opLifecycle/opSplitter.js +57 -0
- package/lib/opLifecycle/opSplitter.js.map +1 -0
- package/lib/opLifecycle/outbox.d.ts +47 -0
- package/lib/opLifecycle/outbox.d.ts.map +1 -0
- package/lib/opLifecycle/outbox.js +149 -0
- package/lib/opLifecycle/outbox.js.map +1 -0
- package/lib/opLifecycle/remoteMessageProcessor.d.ts +26 -0
- package/lib/opLifecycle/remoteMessageProcessor.d.ts.map +1 -0
- package/lib/opLifecycle/remoteMessageProcessor.js +76 -0
- package/lib/opLifecycle/remoteMessageProcessor.js.map +1 -0
- package/lib/packageVersion.d.ts +1 -1
- package/lib/packageVersion.js +1 -1
- package/lib/packageVersion.js.map +1 -1
- package/lib/summaryFormat.js +1 -1
- package/lib/summaryFormat.js.map +1 -1
- package/package.json +35 -21
- package/prettier.config.cjs +8 -0
- package/src/blobManager.ts +74 -19
- package/src/containerRuntime.ts +144 -341
- package/src/dataStoreContext.ts +33 -5
- package/src/dataStores.ts +32 -16
- package/src/garbageCollection.ts +106 -82
- package/src/garbageCollectionConstants.ts +35 -0
- package/src/gcSweepReadyUsageDetection.ts +1 -1
- package/src/index.ts +6 -4
- package/src/{batchManager.ts → opLifecycle/batchManager.ts} +41 -23
- package/src/opLifecycle/definitions.ts +44 -0
- package/src/opLifecycle/index.ts +17 -0
- package/src/opLifecycle/opCompressor.ts +64 -0
- package/src/opLifecycle/opDecompressor.ts +84 -0
- package/src/opLifecycle/opSplitter.ts +78 -0
- package/src/opLifecycle/outbox.ts +204 -0
- package/src/opLifecycle/remoteMessageProcessor.ts +90 -0
- package/src/packageVersion.ts +1 -1
- package/src/summaryFormat.ts +1 -1
- package/dist/batchManager.d.ts +0 -36
- package/dist/batchManager.d.ts.map +0 -1
- package/dist/batchManager.js.map +0 -1
- package/lib/batchManager.d.ts +0 -36
- package/lib/batchManager.d.ts.map +0 -1
- package/lib/batchManager.js.map +0 -1
package/lib/garbageCollection.js
CHANGED
|
@@ -18,38 +18,15 @@ import { ClientSessionExpiredError, DataProcessingError, UsageError } from "@flu
|
|
|
18
18
|
import { cloneGCData, concatGarbageCollectionStates, concatGarbageCollectionData, runGarbageCollection, unpackChildNodesGCDetails, } from "@fluidframework/garbage-collector";
|
|
19
19
|
import { SummaryType } from "@fluidframework/protocol-definitions";
|
|
20
20
|
import { gcBlobKey, } from "@fluidframework/runtime-definitions";
|
|
21
|
-
import { mergeStats, SummaryTreeBuilder, } from "@fluidframework/runtime-utils";
|
|
21
|
+
import { mergeStats, packagePathToTelemetryProperty, SummaryTreeBuilder, } from "@fluidframework/runtime-utils";
|
|
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 { defaultInactiveTimeoutMs, defaultSessionExpiryDurationMs, disableSweepLogKey, disableTombstoneKey, gcBlobPrefix, gcTestModeKey, gcTombstoneBlobKey, gcTreeKey, oneDayMs, runGCKey, runSessionExpiryKey, runSweepKey, trackGCStateKey } from "./garbageCollectionConstants";
|
|
25
26
|
import { SweepReadyUsageDetectionHandler } from "./gcSweepReadyUsageDetection";
|
|
26
27
|
import { getGCVersion, metadataBlobName, dataStoreAttributesBlobName, } from "./summaryFormat";
|
|
27
28
|
/** This is the current version of garbage collection. */
|
|
28
29
|
const GCVersion = 1;
|
|
29
|
-
// The key for the GC tree in summary.
|
|
30
|
-
export const gcTreeKey = "gc";
|
|
31
|
-
// They prefix for GC blobs in the GC tree in summary.
|
|
32
|
-
export const gcBlobPrefix = "__gc";
|
|
33
|
-
// The key for tombstone blob in the GC tree in summary.
|
|
34
|
-
export const gcTombstoneBlobKey = "__tombstones";
|
|
35
|
-
// Feature gate key to turn GC on / off.
|
|
36
|
-
export const runGCKey = "Fluid.GarbageCollection.RunGC";
|
|
37
|
-
// Feature gate key to turn GC sweep on / off.
|
|
38
|
-
export const runSweepKey = "Fluid.GarbageCollection.RunSweep";
|
|
39
|
-
// Feature gate key to turn GC test mode on / off.
|
|
40
|
-
export const gcTestModeKey = "Fluid.GarbageCollection.GCTestMode";
|
|
41
|
-
// Feature gate key to expire a session after a set period of time.
|
|
42
|
-
export const runSessionExpiryKey = "Fluid.GarbageCollection.RunSessionExpiry";
|
|
43
|
-
// Feature gate key to write the gc blob as a handle if the data is the same.
|
|
44
|
-
export const trackGCStateKey = "Fluid.GarbageCollection.TrackGCState";
|
|
45
|
-
// Feature gate key to turn GC sweep log off.
|
|
46
|
-
export const disableSweepLogKey = "Fluid.GarbageCollection.DisableSweepLog";
|
|
47
|
-
// Feature gate key to tombstone datastores.
|
|
48
|
-
export const testTombstoneKey = "Fluid.GarbageCollection.Test.Tombstone";
|
|
49
|
-
// One day in milliseconds.
|
|
50
|
-
export const oneDayMs = 1 * 24 * 60 * 60 * 1000;
|
|
51
|
-
export const defaultInactiveTimeoutMs = 7 * oneDayMs; // 7 days
|
|
52
|
-
export const defaultSessionExpiryDurationMs = 30 * oneDayMs; // 30 days
|
|
53
30
|
/** The types of GC nodes in the GC reference graph. */
|
|
54
31
|
export const GCNodeType = {
|
|
55
32
|
// Nodes that are for data stores.
|
|
@@ -161,7 +138,7 @@ export class UnreferencedStateTracker {
|
|
|
161
138
|
*/
|
|
162
139
|
export class GarbageCollector {
|
|
163
140
|
constructor(createParams) {
|
|
164
|
-
var _a, _b, _c, _d, _e, _f, _g, _h
|
|
141
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
165
142
|
/**
|
|
166
143
|
* Tells whether the initial GC state needs to be reset. This can happen under 2 conditions:
|
|
167
144
|
*
|
|
@@ -193,6 +170,7 @@ export class GarbageCollector {
|
|
|
193
170
|
this.runtime = createParams.runtime;
|
|
194
171
|
this.isSummarizerClient = createParams.isSummarizerClient;
|
|
195
172
|
this.gcOptions = createParams.gcOptions;
|
|
173
|
+
this.createContainerMetadata = createParams.createContainerMetadata;
|
|
196
174
|
this.getNodePackagePath = createParams.getNodePackagePath;
|
|
197
175
|
this.getLastSummaryTimestampMs = createParams.getLastSummaryTimestampMs;
|
|
198
176
|
this.activeConnection = createParams.activeConnection;
|
|
@@ -302,14 +280,15 @@ export class GarbageCollector {
|
|
|
302
280
|
}
|
|
303
281
|
// Whether we are running in test mode. In this mode, unreferenced nodes are immediately deleted.
|
|
304
282
|
this.testMode = (_h = this.mc.config.getBoolean(gcTestModeKey)) !== null && _h !== void 0 ? _h : this.gcOptions.runGCInTestMode === true;
|
|
305
|
-
|
|
283
|
+
// Whether we are running in tombstone mode. This is true by default unless disabled via feature flags.
|
|
284
|
+
this.tombstoneMode = this.mc.config.getBoolean(disableTombstoneKey) !== true;
|
|
306
285
|
// The GC state needs to be reset if the base snapshot contains GC tree and GC is disabled or it doesn't
|
|
307
286
|
// contain GC tree and GC is enabled.
|
|
308
287
|
const gcTreePresent = (baseSnapshot === null || baseSnapshot === void 0 ? void 0 : baseSnapshot.trees[gcTreeKey]) !== undefined;
|
|
309
288
|
this.initialStateNeedsReset = gcTreePresent !== this.shouldRunGC;
|
|
310
|
-
// Get the GC
|
|
311
|
-
//
|
|
312
|
-
|
|
289
|
+
// Get the GC data from the base snapshot. Use LazyPromise because we only want to do this once since it
|
|
290
|
+
// it involves fetching blobs from storage which is expensive.
|
|
291
|
+
this.baseSnapshotDataP = new LazyPromise(async () => {
|
|
313
292
|
var _a;
|
|
314
293
|
if (baseSnapshot === undefined) {
|
|
315
294
|
return undefined;
|
|
@@ -318,17 +297,7 @@ export class GarbageCollector {
|
|
|
318
297
|
// For newer documents, GC data should be present in the GC tree in the root of the snapshot.
|
|
319
298
|
const gcSnapshotTree = baseSnapshot.trees[gcTreeKey];
|
|
320
299
|
if (gcSnapshotTree !== undefined) {
|
|
321
|
-
|
|
322
|
-
if (baseGCData.tombstones !== undefined && this.tombstoneMode) {
|
|
323
|
-
this.tombstones = baseGCData.tombstones;
|
|
324
|
-
}
|
|
325
|
-
if (this.trackGCState) {
|
|
326
|
-
this.latestSummaryData = {
|
|
327
|
-
serializedGCState: JSON.stringify(generateSortedGCState(baseGCData.gcState)),
|
|
328
|
-
serializedTombstones: JSON.stringify(baseGCData.tombstones),
|
|
329
|
-
};
|
|
330
|
-
}
|
|
331
|
-
return baseGCData.gcState;
|
|
300
|
+
return getGCDataFromSnapshot(gcSnapshotTree, readAndParseBlob);
|
|
332
301
|
}
|
|
333
302
|
// back-compat - Older documents will have the GC blobs in each data store's summary tree. Get them and
|
|
334
303
|
// consolidate into IGarbageCollectionState format.
|
|
@@ -364,7 +333,7 @@ export class GarbageCollector {
|
|
|
364
333
|
}
|
|
365
334
|
// If there is only one node (root node just added above), either GC is disabled or we are loading from
|
|
366
335
|
// the first summary generated by detached container. In both cases, GC was not run - return undefined.
|
|
367
|
-
return Object.keys(gcState.gcNodes).length === 1 ? undefined : gcState;
|
|
336
|
+
return Object.keys(gcState.gcNodes).length === 1 ? undefined : { gcState, tombstones: undefined };
|
|
368
337
|
}
|
|
369
338
|
catch (error) {
|
|
370
339
|
const dpe = DataProcessingError.wrapIfUnrecognized(error, "FailedToInitializeGC");
|
|
@@ -373,11 +342,11 @@ export class GarbageCollector {
|
|
|
373
342
|
}
|
|
374
343
|
});
|
|
375
344
|
/**
|
|
376
|
-
* Set up the initializer which initializes the
|
|
377
|
-
*
|
|
378
|
-
*
|
|
345
|
+
* Set up the initializer which initializes the GC state from the data in base snapshot. This is done when
|
|
346
|
+
* connected in write mode or when GC runs the first time. It sets up all unreferenced nodes from the base
|
|
347
|
+
* GC state and updates their inactive or sweep ready state.
|
|
379
348
|
*/
|
|
380
|
-
this.
|
|
349
|
+
this.initializeGCStateFromBaseSnapshotP = new LazyPromise(async () => {
|
|
381
350
|
const currentReferenceTimestampMs = this.runtime.getCurrentReferenceTimestampMs();
|
|
382
351
|
/**
|
|
383
352
|
* If there is no current reference timestamp, skip initialization. We need the current timestamp to track
|
|
@@ -395,34 +364,41 @@ export class GarbageCollector {
|
|
|
395
364
|
});
|
|
396
365
|
return;
|
|
397
366
|
}
|
|
398
|
-
const
|
|
367
|
+
const baseSnapshotData = await this.baseSnapshotDataP;
|
|
399
368
|
/**
|
|
400
|
-
* The base
|
|
369
|
+
* The base snapshot data will not be present if the container is loaded from:
|
|
401
370
|
* 1. The first summary created by the detached container.
|
|
402
371
|
* 2. A summary that was generated with GC disabled.
|
|
403
372
|
* 3. A summary that was generated before GC even existed.
|
|
404
373
|
*/
|
|
405
|
-
if (
|
|
374
|
+
if (baseSnapshotData === undefined) {
|
|
406
375
|
return;
|
|
407
376
|
}
|
|
408
377
|
const gcNodes = {};
|
|
409
|
-
for (const [nodeId, nodeData] of Object.entries(
|
|
378
|
+
for (const [nodeId, nodeData] of Object.entries(baseSnapshotData.gcState.gcNodes)) {
|
|
410
379
|
if (nodeData.unreferencedTimestampMs !== undefined) {
|
|
411
380
|
this.unreferencedNodesState.set(nodeId, new UnreferencedStateTracker(nodeData.unreferencedTimestampMs, this.inactiveTimeoutMs, currentReferenceTimestampMs, this.sweepTimeoutMs));
|
|
412
381
|
}
|
|
413
382
|
gcNodes[nodeId] = Array.from(nodeData.outboundRoutes);
|
|
414
383
|
}
|
|
415
384
|
this.previousGCDataFromLastRun = { gcNodes };
|
|
385
|
+
// If tracking state across summaries, update latest summary data from the base snapshot's GC data.
|
|
386
|
+
if (this.trackGCState) {
|
|
387
|
+
this.latestSummaryData = {
|
|
388
|
+
serializedGCState: JSON.stringify(generateSortedGCState(baseSnapshotData.gcState)),
|
|
389
|
+
serializedTombstones: JSON.stringify(baseSnapshotData.tombstones),
|
|
390
|
+
};
|
|
391
|
+
}
|
|
416
392
|
});
|
|
417
393
|
// Get the GC details for each node from the GC state in the base summary. This is returned in getBaseGCDetails
|
|
418
394
|
// which the caller uses to initialize each node's GC state.
|
|
419
395
|
this.baseGCDetailsP = new LazyPromise(async () => {
|
|
420
|
-
const
|
|
421
|
-
if (
|
|
396
|
+
const baseSnapshotData = await this.baseSnapshotDataP;
|
|
397
|
+
if (baseSnapshotData === undefined) {
|
|
422
398
|
return new Map();
|
|
423
399
|
}
|
|
424
400
|
const gcNodes = {};
|
|
425
|
-
for (const [nodeId, nodeData] of Object.entries(
|
|
401
|
+
for (const [nodeId, nodeData] of Object.entries(baseSnapshotData.gcState.gcNodes)) {
|
|
426
402
|
gcNodes[nodeId] = Array.from(nodeData.outboundRoutes);
|
|
427
403
|
}
|
|
428
404
|
// Run GC on the nodes in the base summary to get the routes used in each node in the container.
|
|
@@ -432,7 +408,7 @@ export class GarbageCollector {
|
|
|
432
408
|
const baseGCDetailsMap = unpackChildNodesGCDetails({ gcData: { gcNodes }, usedRoutes });
|
|
433
409
|
// Currently, the nodes may write the GC data. So, we need to update its base GC details with the
|
|
434
410
|
// unreferenced timestamp. Once we start writing the GC data here, we won't need to do this anymore.
|
|
435
|
-
for (const [nodeId, nodeData] of Object.entries(
|
|
411
|
+
for (const [nodeId, nodeData] of Object.entries(baseSnapshotData.gcState.gcNodes)) {
|
|
436
412
|
if (nodeData.unreferencedTimestampMs !== undefined) {
|
|
437
413
|
const dataStoreGCDetails = baseGCDetailsMap.get(nodeId.slice(1));
|
|
438
414
|
if (dataStoreGCDetails !== undefined) {
|
|
@@ -475,6 +451,26 @@ export class GarbageCollector {
|
|
|
475
451
|
get configs() {
|
|
476
452
|
return Object.assign({ gcEnabled: this.gcEnabled, sweepEnabled: this.sweepEnabled, runGC: this.shouldRunGC, runSweep: this.shouldRunSweep, testMode: this.testMode, tombstoneMode: this.tombstoneMode, sessionExpiry: this.sessionExpiryTimeoutMs, sweepTimeout: this.sweepTimeoutMs, inactiveTimeout: this.inactiveTimeoutMs, trackGCState: this.trackGCState }, this.gcOptions);
|
|
477
453
|
}
|
|
454
|
+
/**
|
|
455
|
+
* Called during container initialization. Initialize the tombstone state so that object are marked as tombstones
|
|
456
|
+
* before they are loaded or used. This is important to get accurate information of whether tombstoned object are
|
|
457
|
+
* in use or not.
|
|
458
|
+
*/
|
|
459
|
+
async initializeBaseState() {
|
|
460
|
+
const baseSnapshotData = await this.baseSnapshotDataP;
|
|
461
|
+
/**
|
|
462
|
+
* The base snapshot data or tombstone state will not be present if the container is loaded from:
|
|
463
|
+
* 1. The first summary created by the detached container.
|
|
464
|
+
* 2. A summary that was generated with GC disabled.
|
|
465
|
+
* 3. A summary that was generated before GC even existed.
|
|
466
|
+
* 4. A summary that was generated with tombstone feature disabled.
|
|
467
|
+
*/
|
|
468
|
+
if (!this.tombstoneMode || (baseSnapshotData === null || baseSnapshotData === void 0 ? void 0 : baseSnapshotData.tombstones) === undefined) {
|
|
469
|
+
return;
|
|
470
|
+
}
|
|
471
|
+
this.tombstones = baseSnapshotData.tombstones;
|
|
472
|
+
this.runtime.updateUnusedRoutes(this.tombstones, true /* tombstone */);
|
|
473
|
+
}
|
|
478
474
|
/**
|
|
479
475
|
* Called when the connection state of the runtime changes, i.e., it connects or disconnects. GC subscribes to this
|
|
480
476
|
* to initialize the base state for non-summarizer clients so that they can track inactive / sweep ready nodes.
|
|
@@ -496,7 +492,7 @@ export class GarbageCollector {
|
|
|
496
492
|
* sweep in phases and we want to track when inactive and sweep ready objects are used in any client.
|
|
497
493
|
*/
|
|
498
494
|
if (this.activeConnection() && this.shouldRunGC) {
|
|
499
|
-
this.
|
|
495
|
+
this.initializeGCStateFromBaseSnapshotP.catch((error) => { });
|
|
500
496
|
}
|
|
501
497
|
}
|
|
502
498
|
/**
|
|
@@ -538,8 +534,8 @@ export class GarbageCollector {
|
|
|
538
534
|
}, { end: true, cancel: "error" });
|
|
539
535
|
}
|
|
540
536
|
async runPreGCSteps() {
|
|
541
|
-
// Ensure that
|
|
542
|
-
await this.
|
|
537
|
+
// Ensure that state has been initialized from the base snapshot data.
|
|
538
|
+
await this.initializeGCStateFromBaseSnapshotP;
|
|
543
539
|
// Let the runtime update its pending state before GC runs.
|
|
544
540
|
await this.runtime.updateStateBeforeGC();
|
|
545
541
|
}
|
|
@@ -559,23 +555,13 @@ export class GarbageCollector {
|
|
|
559
555
|
// If we are running in GC test mode, delete objects for unused routes. This enables testing scenarios
|
|
560
556
|
// involving access to deleted data.
|
|
561
557
|
if (this.testMode) {
|
|
562
|
-
this.runtime.
|
|
558
|
+
this.runtime.updateUnusedRoutes(gcResult.deletedNodeIds, false /* tombstone */);
|
|
563
559
|
}
|
|
564
|
-
else {
|
|
560
|
+
else if (this.tombstoneMode) {
|
|
565
561
|
// If we are running in GC tombstone mode, tombstone objects for unused routes. This enables testing
|
|
566
562
|
// scenarios involving access to "deleted" data without actually deleting the data from summaries.
|
|
567
563
|
// Note: we will not tombstone in test mode
|
|
568
|
-
|
|
569
|
-
const tombstoneRoutes = [];
|
|
570
|
-
// Currently only tombstone datastores
|
|
571
|
-
for (const [key, value] of this.unreferencedNodesState.entries()) {
|
|
572
|
-
if (value.state === UnreferencedState.SweepReady &&
|
|
573
|
-
this.runtime.getNodeType(key) === GCNodeType.DataStore) {
|
|
574
|
-
tombstoneRoutes.push(key);
|
|
575
|
-
}
|
|
576
|
-
}
|
|
577
|
-
this.runtime.deleteUnusedRoutes(tombstoneRoutes);
|
|
578
|
-
}
|
|
564
|
+
this.runtime.updateUnusedRoutes(this.tombstones, true /* tombstone */);
|
|
579
565
|
}
|
|
580
566
|
// Log pending unreferenced events such as a node being used after inactive. This is done after GC runs and
|
|
581
567
|
// updates its state so that we don't send false positives based on intermediate state. For example, we may get
|
|
@@ -601,7 +587,9 @@ export class GarbageCollector {
|
|
|
601
587
|
};
|
|
602
588
|
}
|
|
603
589
|
const serializedGCState = JSON.stringify(generateSortedGCState(gcState));
|
|
604
|
-
const serializedTombstones = this.
|
|
590
|
+
const serializedTombstones = this.tombstoneMode
|
|
591
|
+
? (this.tombstones.length > 0 ? JSON.stringify(this.tombstones.sort()) : undefined)
|
|
592
|
+
: undefined;
|
|
605
593
|
/**
|
|
606
594
|
* Incremental summary of GC data - If any of the GC state or tombstone state hasn't changed since the last
|
|
607
595
|
* summary, send summary handles for them. Otherwise, send the data in summary blobs.
|
|
@@ -1034,31 +1022,24 @@ export class GarbageCollector {
|
|
|
1034
1022
|
return;
|
|
1035
1023
|
}
|
|
1036
1024
|
this.loggedUnreferencedEvents.add(uniqueEventId);
|
|
1037
|
-
const propsToLog = {
|
|
1038
|
-
id: nodeId,
|
|
1039
|
-
type: nodeType,
|
|
1040
|
-
unrefTime: nodeStateTracker.unreferencedTimestampMs,
|
|
1041
|
-
age: currentReferenceTimestampMs - nodeStateTracker.unreferencedTimestampMs,
|
|
1042
|
-
timeout: nodeStateTracker.state === UnreferencedState.Inactive
|
|
1025
|
+
const propsToLog = Object.assign(Object.assign({ id: nodeId, type: nodeType, unrefTime: nodeStateTracker.unreferencedTimestampMs, age: currentReferenceTimestampMs - nodeStateTracker.unreferencedTimestampMs, timeout: nodeStateTracker.state === UnreferencedState.Inactive
|
|
1043
1026
|
? this.inactiveTimeoutMs
|
|
1044
|
-
: this.sweepTimeoutMs,
|
|
1045
|
-
completedGCRuns: this.completedRuns,
|
|
1046
|
-
lastSummaryTime: this.getLastSummaryTimestampMs(),
|
|
1047
|
-
externalRequest: requestHeaders === null || requestHeaders === void 0 ? void 0 : requestHeaders[RuntimeHeaders.externalRequest],
|
|
1048
|
-
viaHandle: requestHeaders === null || requestHeaders === void 0 ? void 0 : requestHeaders[RuntimeHeaders.viaHandle],
|
|
1049
|
-
fromId: fromNodeId,
|
|
1050
|
-
};
|
|
1027
|
+
: this.sweepTimeoutMs, completedGCRuns: this.completedRuns, lastSummaryTime: this.getLastSummaryTimestampMs() }, this.createContainerMetadata), { externalRequest: requestHeaders === null || requestHeaders === void 0 ? void 0 : requestHeaders[RuntimeHeaders.externalRequest], viaHandle: requestHeaders === null || requestHeaders === void 0 ? void 0 : requestHeaders[RuntimeHeaders.viaHandle], fromId: fromNodeId });
|
|
1051
1028
|
// For summarizer client, queue the event so it is logged the next time GC runs if the event is still valid.
|
|
1052
1029
|
// For non-summarizer client, log the event now since GC won't run on it. This may result in false positives
|
|
1053
1030
|
// but it's a good signal nonetheless and we can consume it with a grain of salt.
|
|
1031
|
+
// Inactive errors are usages of Objects that are unreferenced for at least a period of 7 days.
|
|
1032
|
+
// SweepReady errors are usages of Objects that will be deleted by GC Sweep!
|
|
1054
1033
|
if (this.isSummarizerClient) {
|
|
1055
1034
|
this.pendingEventsQueue.push(Object.assign(Object.assign({}, propsToLog), { usageType, state }));
|
|
1056
1035
|
}
|
|
1057
1036
|
else {
|
|
1058
1037
|
// For non-summarizer clients, only log "Loaded" type events since these objects may not be loaded in the
|
|
1059
1038
|
// summarizer clients if they are based off of user actions (such as scrolling to content for these objects)
|
|
1039
|
+
// Events generated:
|
|
1040
|
+
// InactiveObject_Loaded, SweepReadyObject_Loaded
|
|
1060
1041
|
if (usageType === "Loaded") {
|
|
1061
|
-
this.mc.logger.sendErrorEvent(Object.assign(Object.assign({}, propsToLog), { eventName: `${state}Object_${usageType}`, pkg: packagePath
|
|
1042
|
+
this.mc.logger.sendErrorEvent(Object.assign(Object.assign({}, propsToLog), { eventName: `${state}Object_${usageType}`, pkg: packagePathToTelemetryProperty(packagePath), stack: generateStack() }));
|
|
1062
1043
|
}
|
|
1063
1044
|
// If SweepReady Usage Detection is enabed, the handler may close the interactive container.
|
|
1064
1045
|
// Once Sweep is fully implemented, this will be removed since the objects will be gone
|
|
@@ -1069,6 +1050,11 @@ export class GarbageCollector {
|
|
|
1069
1050
|
}
|
|
1070
1051
|
}
|
|
1071
1052
|
async logUnreferencedEvents(logger) {
|
|
1053
|
+
// Events sent come only from the summarizer client. In between summaries, events are pushed to a queue and at
|
|
1054
|
+
// summary time they are then logged.
|
|
1055
|
+
// Events generated:
|
|
1056
|
+
// InactiveObject_Loaded, InactiveObject_Changed, InactiveObject_Revived
|
|
1057
|
+
// SweepReadyObject_Loaded, SweepReadyObject_Changed, SweepReadyObject_Revived
|
|
1072
1058
|
for (const eventProps of this.pendingEventsQueue) {
|
|
1073
1059
|
const { usageType, state } = eventProps, propsToLog = __rest(eventProps, ["usageType", "state"]);
|
|
1074
1060
|
/**
|
|
@@ -1089,7 +1075,7 @@ export class GarbageCollector {
|
|
|
1089
1075
|
}
|
|
1090
1076
|
}
|
|
1091
1077
|
/**
|
|
1092
|
-
* Gets the garbage collection
|
|
1078
|
+
* Gets the base garbage collection state from the given snapshot tree. It contains GC state and tombstone state.
|
|
1093
1079
|
* The GC state may be written into multiple blobs. Merge the GC state from all such blobs into one.
|
|
1094
1080
|
*/
|
|
1095
1081
|
async function getGCDataFromSnapshot(gcSnapshotTree, readAndParseBlob) {
|