@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/src/dataStoreContext.ts
CHANGED
|
@@ -58,10 +58,16 @@ import {
|
|
|
58
58
|
SummarizeInternalFn,
|
|
59
59
|
ITelemetryContext,
|
|
60
60
|
} from "@fluidframework/runtime-definitions";
|
|
61
|
-
import {
|
|
61
|
+
import {
|
|
62
|
+
addBlobToSummary,
|
|
63
|
+
convertSummaryTreeToITree,
|
|
64
|
+
packagePathToTelemetryProperty,
|
|
65
|
+
} from "@fluidframework/runtime-utils";
|
|
62
66
|
import {
|
|
63
67
|
ChildLogger,
|
|
68
|
+
loggerToMonitoringContext,
|
|
64
69
|
LoggingError,
|
|
70
|
+
MonitoringContext,
|
|
65
71
|
TelemetryDataTag,
|
|
66
72
|
ThresholdCounter,
|
|
67
73
|
} from "@fluidframework/telemetry-utils";
|
|
@@ -81,6 +87,8 @@ import {
|
|
|
81
87
|
getAttributesFormatVersion,
|
|
82
88
|
getFluidDataStoreAttributes,
|
|
83
89
|
} from "./summaryFormat";
|
|
90
|
+
import { throwOnTombstoneUsageKey } from "./garbageCollectionConstants";
|
|
91
|
+
import { summarizerClientType } from "./summarizerClientElection";
|
|
84
92
|
|
|
85
93
|
function createAttributes(
|
|
86
94
|
pkg: readonly string[],
|
|
@@ -200,6 +208,8 @@ export abstract class FluidDataStoreContext extends TypedEventEmitter<IFluidData
|
|
|
200
208
|
*/
|
|
201
209
|
private _tombstoned = false;
|
|
202
210
|
public get tombstoned() { return this._tombstoned; }
|
|
211
|
+
/** If true, throw an error when a tombstone data store is used. */
|
|
212
|
+
private readonly throwOnTombstoneUsage: boolean;
|
|
203
213
|
|
|
204
214
|
public get attachState(): AttachState {
|
|
205
215
|
return this._attachState;
|
|
@@ -243,7 +253,7 @@ export abstract class FluidDataStoreContext extends TypedEventEmitter<IFluidData
|
|
|
243
253
|
protected _attachState: AttachState;
|
|
244
254
|
private _isInMemoryRoot: boolean = false;
|
|
245
255
|
protected readonly summarizerNode: ISummarizerNodeWithGC;
|
|
246
|
-
private readonly
|
|
256
|
+
private readonly mc: MonitoringContext;
|
|
247
257
|
private readonly thresholdOpsCounter: ThresholdCounter;
|
|
248
258
|
private static readonly pendingOpsCountThreshold = 1000;
|
|
249
259
|
|
|
@@ -297,8 +307,13 @@ export abstract class FluidDataStoreContext extends TypedEventEmitter<IFluidData
|
|
|
297
307
|
async () => this.getBaseGCDetails(),
|
|
298
308
|
);
|
|
299
309
|
|
|
300
|
-
this.
|
|
301
|
-
this.thresholdOpsCounter = new ThresholdCounter(FluidDataStoreContext.pendingOpsCountThreshold, this.
|
|
310
|
+
this.mc = loggerToMonitoringContext(ChildLogger.create(this.logger, "FluidDataStoreContext"));
|
|
311
|
+
this.thresholdOpsCounter = new ThresholdCounter(FluidDataStoreContext.pendingOpsCountThreshold, this.mc.logger);
|
|
312
|
+
|
|
313
|
+
// Tombstone should only throw when the feature flag is enabled and the client isn't a summarizer
|
|
314
|
+
this.throwOnTombstoneUsage =
|
|
315
|
+
this.mc.config.getBoolean(throwOnTombstoneUsageKey) === true &&
|
|
316
|
+
this.clientDetails.type !== summarizerClientType;
|
|
302
317
|
}
|
|
303
318
|
|
|
304
319
|
public dispose(): void {
|
|
@@ -760,10 +775,23 @@ export abstract class FluidDataStoreContext extends TypedEventEmitter<IFluidData
|
|
|
760
775
|
|
|
761
776
|
if (checkTombstone && this.tombstoned) {
|
|
762
777
|
const messageString = `Context is tombstoned! Call site [${callSite}]`;
|
|
763
|
-
|
|
778
|
+
const error = new DataCorruptionError(messageString, {
|
|
764
779
|
errorMessage: messageString,
|
|
765
780
|
...safeTelemetryProps,
|
|
766
781
|
});
|
|
782
|
+
|
|
783
|
+
// Always log an error when tombstoned data store is used. However, throw an error only if
|
|
784
|
+
// throwOnTombstoneUsage is set.
|
|
785
|
+
this.mc.logger.sendErrorEvent({
|
|
786
|
+
eventName: "GC_Tombstone_DataStore_Changed",
|
|
787
|
+
callSite,
|
|
788
|
+
pkg: packagePathToTelemetryProperty(this.pkg),
|
|
789
|
+
}, error);
|
|
790
|
+
// Always log an error when tombstoned data store is used. However, throw an error only if
|
|
791
|
+
// throwOnTombstoneUsage is set and the client is not a summarizer.
|
|
792
|
+
if (this.throwOnTombstoneUsage) {
|
|
793
|
+
throw error;
|
|
794
|
+
}
|
|
767
795
|
}
|
|
768
796
|
}
|
|
769
797
|
|
package/src/dataStores.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Licensed under the MIT License.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import {
|
|
6
|
+
import { ITelemetryBaseLogger, IDisposable } from "@fluidframework/common-definitions";
|
|
7
7
|
import { DataCorruptionError, extractSafePropertiesFromMessage } from "@fluidframework/container-utils";
|
|
8
8
|
import { IFluidHandle } from "@fluidframework/core-interfaces";
|
|
9
9
|
import { FluidObjectHandle } from "@fluidframework/datastore";
|
|
@@ -34,9 +34,10 @@ import {
|
|
|
34
34
|
create404Response,
|
|
35
35
|
createResponseError,
|
|
36
36
|
responseToException,
|
|
37
|
+
packagePathToTelemetryProperty,
|
|
37
38
|
SummaryTreeBuilder,
|
|
38
39
|
} from "@fluidframework/runtime-utils";
|
|
39
|
-
import { ChildLogger, LoggingError, TelemetryDataTag } from "@fluidframework/telemetry-utils";
|
|
40
|
+
import { ChildLogger, loggerToMonitoringContext, LoggingError, MonitoringContext, TelemetryDataTag } from "@fluidframework/telemetry-utils";
|
|
40
41
|
import { AttachState } from "@fluidframework/container-definitions";
|
|
41
42
|
import { BlobCacheStorageService, buildSnapshotTree } from "@fluidframework/driver-utils";
|
|
42
43
|
import { assert, Lazy, LazyPromise } from "@fluidframework/common-utils";
|
|
@@ -54,6 +55,8 @@ import {
|
|
|
54
55
|
import { IContainerRuntimeMetadata, nonDataStorePaths, rootHasIsolatedChannels } from "./summaryFormat";
|
|
55
56
|
import { IDataStoreAliasMessage, isDataStoreAliasMessage } from "./dataStore";
|
|
56
57
|
import { GCNodeType } from "./garbageCollection";
|
|
58
|
+
import { throwOnTombstoneUsageKey } from "./garbageCollectionConstants";
|
|
59
|
+
import { summarizerClientType } from "./summarizerClientElection";
|
|
57
60
|
|
|
58
61
|
type PendingAliasResolve = (success: boolean) => void;
|
|
59
62
|
|
|
@@ -67,7 +70,7 @@ export class DataStores implements IDisposable {
|
|
|
67
70
|
// 0.24 back-compat attachingBeforeSummary
|
|
68
71
|
public readonly attachOpFiredForDataStore = new Set<string>();
|
|
69
72
|
|
|
70
|
-
private readonly
|
|
73
|
+
private readonly mc: MonitoringContext;
|
|
71
74
|
|
|
72
75
|
private readonly disposeOnce = new Lazy<void>(() => this.contexts.dispose());
|
|
73
76
|
|
|
@@ -81,6 +84,8 @@ export class DataStores implements IDisposable {
|
|
|
81
84
|
// Stores the ids of new data stores between two GC runs. This is used to notify the garbage collector of new
|
|
82
85
|
// root data stores that are added.
|
|
83
86
|
private dataStoresSinceLastGC: string[] = [];
|
|
87
|
+
/** If true, throw an error when a tombstone data store is retrieved. */
|
|
88
|
+
private readonly throwOnTombstoneUsage: boolean;
|
|
84
89
|
// The handle to the container runtime. This is used mainly for GC purposes to represent outbound reference from
|
|
85
90
|
// the container runtime to other nodes.
|
|
86
91
|
private readonly containerRuntimeHandle: IFluidHandle;
|
|
@@ -100,7 +105,7 @@ export class DataStores implements IDisposable {
|
|
|
100
105
|
private readonly aliasMap: Map<string, string>,
|
|
101
106
|
private readonly contexts: DataStoreContexts = new DataStoreContexts(baseLogger),
|
|
102
107
|
) {
|
|
103
|
-
this.
|
|
108
|
+
this.mc = loggerToMonitoringContext(ChildLogger.create(baseLogger));
|
|
104
109
|
this.containerRuntimeHandle = new FluidObjectHandle(this.runtime, "/", this.runtime.IFluidHandleContext);
|
|
105
110
|
|
|
106
111
|
const baseGCDetailsP = new LazyPromise(async () => {
|
|
@@ -111,6 +116,10 @@ export class DataStores implements IDisposable {
|
|
|
111
116
|
const baseGCDetails = await baseGCDetailsP;
|
|
112
117
|
return baseGCDetails.get(dataStoreId);
|
|
113
118
|
};
|
|
119
|
+
// Tombstone should only throw when the feature flag is enabled and the client isn't a summarizer
|
|
120
|
+
this.throwOnTombstoneUsage =
|
|
121
|
+
this.mc.config.getBoolean(throwOnTombstoneUsageKey) === true &&
|
|
122
|
+
this.runtime.clientDetails.type !== summarizerClientType;
|
|
114
123
|
|
|
115
124
|
// Extract stores stored inside the snapshot
|
|
116
125
|
const fluidDataStores = new Map<string, ISnapshotTree>();
|
|
@@ -280,7 +289,7 @@ export class DataStores implements IDisposable {
|
|
|
280
289
|
|
|
281
290
|
const context = this.contexts.get(aliasMessage.internalId);
|
|
282
291
|
if (context === undefined) {
|
|
283
|
-
this.logger.sendErrorEvent({
|
|
292
|
+
this.mc.logger.sendErrorEvent({
|
|
284
293
|
eventName: "AliasFluidDataStoreNotFound",
|
|
285
294
|
fluidDataStoreId: aliasMessage.internalId,
|
|
286
295
|
});
|
|
@@ -429,14 +438,20 @@ export class DataStores implements IDisposable {
|
|
|
429
438
|
}
|
|
430
439
|
|
|
431
440
|
if (context.tombstoned) {
|
|
441
|
+
// The requested data store is removed by gc. Create a 404 gc response exception.
|
|
442
|
+
const error = responseToException(createResponseError(404, "Datastore removed by gc", request), request);
|
|
432
443
|
// Note: if a user writes a request to look like it's viaHandle, we will also send this telemetry event
|
|
433
|
-
this.logger.sendErrorEvent({
|
|
434
|
-
eventName: "
|
|
444
|
+
this.mc.logger.sendErrorEvent({
|
|
445
|
+
eventName: "GC_Tombstone_DataStore_Requested",
|
|
435
446
|
url: request.url,
|
|
447
|
+
pkg: packagePathToTelemetryProperty(context.isLoaded ? context.packagePath : undefined),
|
|
436
448
|
viaHandle,
|
|
437
|
-
});
|
|
438
|
-
//
|
|
439
|
-
|
|
449
|
+
}, error);
|
|
450
|
+
// Always log an error when tombstoned data store is used. However, throw an error only if
|
|
451
|
+
// throwOnTombstoneUsage is set.
|
|
452
|
+
if (this.throwOnTombstoneUsage) {
|
|
453
|
+
throw error;
|
|
454
|
+
}
|
|
440
455
|
}
|
|
441
456
|
|
|
442
457
|
return context;
|
|
@@ -447,7 +462,7 @@ export class DataStores implements IDisposable {
|
|
|
447
462
|
if (!context) {
|
|
448
463
|
// Attach message may not have been processed yet
|
|
449
464
|
assert(!local, 0x163 /* "Missing datastore for local signal" */);
|
|
450
|
-
this.logger.sendTelemetryEvent({
|
|
465
|
+
this.mc.logger.sendTelemetryEvent({
|
|
451
466
|
eventName: "SignalFluidDataStoreNotFound",
|
|
452
467
|
fluidDataStoreId: {
|
|
453
468
|
value: address,
|
|
@@ -465,7 +480,7 @@ export class DataStores implements IDisposable {
|
|
|
465
480
|
try {
|
|
466
481
|
context.setConnectionState(connected, clientId);
|
|
467
482
|
} catch (error) {
|
|
468
|
-
this.logger.sendErrorEvent({
|
|
483
|
+
this.mc.logger.sendErrorEvent({
|
|
469
484
|
eventName: "SetConnectionStateError",
|
|
470
485
|
clientId,
|
|
471
486
|
fluidDataStore,
|
|
@@ -623,12 +638,13 @@ export class DataStores implements IDisposable {
|
|
|
623
638
|
}
|
|
624
639
|
|
|
625
640
|
/**
|
|
626
|
-
*
|
|
627
|
-
*
|
|
641
|
+
* This is called to update objects whose routes are unused. The unused objects are either deleted or marked as
|
|
642
|
+
* tombstones.
|
|
628
643
|
* @param unusedRoutes - The routes that are unused in all data stores in this Container.
|
|
629
|
-
* @param tombstone -
|
|
644
|
+
* @param tombstone - if true, the objects corresponding to unused routes are marked tombstones. Otherwise, they
|
|
645
|
+
* are deleted.
|
|
630
646
|
*/
|
|
631
|
-
public
|
|
647
|
+
public updateUnusedRoutes(unusedRoutes: string[], tombstone: boolean) {
|
|
632
648
|
for (const route of unusedRoutes) {
|
|
633
649
|
const pathParts = route.split("/");
|
|
634
650
|
// Delete data store only if its route (/datastoreId) is in unusedRoutes. We don't want to delete a data
|
package/src/garbageCollection.ts
CHANGED
|
@@ -29,6 +29,7 @@ import {
|
|
|
29
29
|
} from "@fluidframework/runtime-definitions";
|
|
30
30
|
import {
|
|
31
31
|
mergeStats,
|
|
32
|
+
packagePathToTelemetryProperty,
|
|
32
33
|
ReadAndParseBlob,
|
|
33
34
|
RefreshSummaryResult,
|
|
34
35
|
SummaryTreeBuilder,
|
|
@@ -44,6 +45,21 @@ import {
|
|
|
44
45
|
|
|
45
46
|
import { IGCRuntimeOptions, RuntimeHeaders } from "./containerRuntime";
|
|
46
47
|
import { getSummaryForDatastores } from "./dataStores";
|
|
48
|
+
import {
|
|
49
|
+
defaultInactiveTimeoutMs,
|
|
50
|
+
defaultSessionExpiryDurationMs,
|
|
51
|
+
disableSweepLogKey,
|
|
52
|
+
disableTombstoneKey,
|
|
53
|
+
gcBlobPrefix,
|
|
54
|
+
gcTestModeKey,
|
|
55
|
+
gcTombstoneBlobKey,
|
|
56
|
+
gcTreeKey,
|
|
57
|
+
oneDayMs,
|
|
58
|
+
runGCKey,
|
|
59
|
+
runSessionExpiryKey,
|
|
60
|
+
runSweepKey,
|
|
61
|
+
trackGCStateKey
|
|
62
|
+
} from "./garbageCollectionConstants";
|
|
47
63
|
import { SweepReadyUsageDetectionHandler } from "./gcSweepReadyUsageDetection";
|
|
48
64
|
import {
|
|
49
65
|
getGCVersion,
|
|
@@ -53,39 +69,12 @@ import {
|
|
|
53
69
|
ReadFluidDataStoreAttributes,
|
|
54
70
|
dataStoreAttributesBlobName,
|
|
55
71
|
IGCMetadata,
|
|
72
|
+
ICreateContainerMetadata,
|
|
56
73
|
} from "./summaryFormat";
|
|
57
74
|
|
|
58
75
|
/** This is the current version of garbage collection. */
|
|
59
76
|
const GCVersion = 1;
|
|
60
77
|
|
|
61
|
-
// The key for the GC tree in summary.
|
|
62
|
-
export const gcTreeKey = "gc";
|
|
63
|
-
// They prefix for GC blobs in the GC tree in summary.
|
|
64
|
-
export const gcBlobPrefix = "__gc";
|
|
65
|
-
// The key for tombstone blob in the GC tree in summary.
|
|
66
|
-
export const gcTombstoneBlobKey = "__tombstones";
|
|
67
|
-
|
|
68
|
-
// Feature gate key to turn GC on / off.
|
|
69
|
-
export const runGCKey = "Fluid.GarbageCollection.RunGC";
|
|
70
|
-
// Feature gate key to turn GC sweep on / off.
|
|
71
|
-
export const runSweepKey = "Fluid.GarbageCollection.RunSweep";
|
|
72
|
-
// Feature gate key to turn GC test mode on / off.
|
|
73
|
-
export const gcTestModeKey = "Fluid.GarbageCollection.GCTestMode";
|
|
74
|
-
// Feature gate key to expire a session after a set period of time.
|
|
75
|
-
export const runSessionExpiryKey = "Fluid.GarbageCollection.RunSessionExpiry";
|
|
76
|
-
// Feature gate key to write the gc blob as a handle if the data is the same.
|
|
77
|
-
export const trackGCStateKey = "Fluid.GarbageCollection.TrackGCState";
|
|
78
|
-
// Feature gate key to turn GC sweep log off.
|
|
79
|
-
export const disableSweepLogKey = "Fluid.GarbageCollection.DisableSweepLog";
|
|
80
|
-
// Feature gate key to tombstone datastores.
|
|
81
|
-
export const testTombstoneKey = "Fluid.GarbageCollection.Test.Tombstone";
|
|
82
|
-
|
|
83
|
-
// One day in milliseconds.
|
|
84
|
-
export const oneDayMs = 1 * 24 * 60 * 60 * 1000;
|
|
85
|
-
|
|
86
|
-
export const defaultInactiveTimeoutMs = 7 * oneDayMs; // 7 days
|
|
87
|
-
export const defaultSessionExpiryDurationMs = 30 * oneDayMs; // 30 days
|
|
88
|
-
|
|
89
78
|
/** The statistics of the system state after a garbage collection run. */
|
|
90
79
|
export interface IGCStats {
|
|
91
80
|
/** The number of nodes in the container. */
|
|
@@ -129,8 +118,8 @@ export interface IGarbageCollectionRuntime {
|
|
|
129
118
|
getGCData(fullGC?: boolean): Promise<IGarbageCollectionData>;
|
|
130
119
|
/** After GC has run, called to notify the runtime of routes that are used in it. */
|
|
131
120
|
updateUsedRoutes(usedRoutes: string[]): void;
|
|
132
|
-
/** After GC has run, called to
|
|
133
|
-
|
|
121
|
+
/** After GC has run, called to notify the runtime of routes that are unused in it. */
|
|
122
|
+
updateUnusedRoutes(unusedRoutes: string[], tombstone: boolean): void;
|
|
134
123
|
/** Returns a referenced timestamp to be used to track unreferenced nodes. */
|
|
135
124
|
getCurrentReferenceTimestampMs(): number | undefined;
|
|
136
125
|
/** Returns the type of the GC node. */
|
|
@@ -146,6 +135,8 @@ export interface IGarbageCollector {
|
|
|
146
135
|
/** Tells whether the GC state in summary needs to be reset in the next summary. */
|
|
147
136
|
readonly summaryStateNeedsReset: boolean;
|
|
148
137
|
readonly trackGCState: boolean;
|
|
138
|
+
/** Initialize the state from the base snapshot after its creation. */
|
|
139
|
+
initializeBaseState(): Promise<void>;
|
|
149
140
|
/** Run garbage collection and update the reference / used state of the system. */
|
|
150
141
|
collectGarbage(
|
|
151
142
|
options: { logger?: ITelemetryLogger; runSweep?: boolean; fullGC?: boolean; },
|
|
@@ -183,6 +174,7 @@ export interface IGarbageCollectorCreateParams {
|
|
|
183
174
|
readonly baseLogger: ITelemetryLogger;
|
|
184
175
|
readonly existing: boolean;
|
|
185
176
|
readonly metadata: IContainerRuntimeMetadata | undefined;
|
|
177
|
+
readonly createContainerMetadata: ICreateContainerMetadata;
|
|
186
178
|
readonly baseSnapshot: ISnapshotTree | undefined;
|
|
187
179
|
readonly isSummarizerClient: boolean;
|
|
188
180
|
readonly getNodePackagePath: (nodePath: string) => Promise<readonly string[] | undefined>;
|
|
@@ -227,6 +219,14 @@ interface IGCSummaryTrackingData {
|
|
|
227
219
|
serializedTombstones: string | undefined;
|
|
228
220
|
}
|
|
229
221
|
|
|
222
|
+
/**
|
|
223
|
+
* The GC data that is read from a snapshot. It contains the GC state and tombstone state.
|
|
224
|
+
*/
|
|
225
|
+
interface IGCSnapshotData {
|
|
226
|
+
gcState: IGarbageCollectionState;
|
|
227
|
+
tombstones: string[] | undefined;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
230
|
/**
|
|
231
231
|
* Helper class that tracks the state of an unreferenced node such as the time it was unreferenced and if it can
|
|
232
232
|
* be deleted by the sweep phase.
|
|
@@ -421,8 +421,10 @@ export class GarbageCollector implements IGarbageCollector {
|
|
|
421
421
|
*/
|
|
422
422
|
private pendingSummaryData: IGCSummaryTrackingData | undefined;
|
|
423
423
|
|
|
424
|
-
// Promise when resolved
|
|
425
|
-
private readonly
|
|
424
|
+
// Promise when resolved returns the GC data data in the base snapshot.
|
|
425
|
+
private readonly baseSnapshotDataP: Promise<IGCSnapshotData | undefined>;
|
|
426
|
+
// Promise when resolved initializes the GC state from the data in the base snapshot.
|
|
427
|
+
private readonly initializeGCStateFromBaseSnapshotP: Promise<void>;
|
|
426
428
|
// The map of data store ids to their GC details in the base summary returned in getDataStoreGCDetails().
|
|
427
429
|
private readonly baseGCDetailsP: Promise<Map<string, IGarbageCollectionDetailsBase>>;
|
|
428
430
|
// Map of node ids to their unreferenced state tracker.
|
|
@@ -440,6 +442,7 @@ export class GarbageCollector implements IGarbageCollector {
|
|
|
440
442
|
private completedRuns = 0;
|
|
441
443
|
|
|
442
444
|
private readonly runtime: IGarbageCollectionRuntime;
|
|
445
|
+
private readonly createContainerMetadata: ICreateContainerMetadata;
|
|
443
446
|
private readonly gcOptions: IGCRuntimeOptions;
|
|
444
447
|
private readonly isSummarizerClient: boolean;
|
|
445
448
|
|
|
@@ -481,6 +484,7 @@ export class GarbageCollector implements IGarbageCollector {
|
|
|
481
484
|
this.runtime = createParams.runtime;
|
|
482
485
|
this.isSummarizerClient = createParams.isSummarizerClient;
|
|
483
486
|
this.gcOptions = createParams.gcOptions;
|
|
487
|
+
this.createContainerMetadata = createParams.createContainerMetadata;
|
|
484
488
|
this.getNodePackagePath = createParams.getNodePackagePath;
|
|
485
489
|
this.getLastSummaryTimestampMs = createParams.getLastSummaryTimestampMs;
|
|
486
490
|
this.activeConnection = createParams.activeConnection;
|
|
@@ -623,16 +627,17 @@ export class GarbageCollector implements IGarbageCollector {
|
|
|
623
627
|
|
|
624
628
|
// Whether we are running in test mode. In this mode, unreferenced nodes are immediately deleted.
|
|
625
629
|
this.testMode = this.mc.config.getBoolean(gcTestModeKey) ?? this.gcOptions.runGCInTestMode === true;
|
|
626
|
-
|
|
630
|
+
// Whether we are running in tombstone mode. This is true by default unless disabled via feature flags.
|
|
631
|
+
this.tombstoneMode = this.mc.config.getBoolean(disableTombstoneKey) !== true;
|
|
627
632
|
|
|
628
633
|
// The GC state needs to be reset if the base snapshot contains GC tree and GC is disabled or it doesn't
|
|
629
634
|
// contain GC tree and GC is enabled.
|
|
630
635
|
const gcTreePresent = baseSnapshot?.trees[gcTreeKey] !== undefined;
|
|
631
636
|
this.initialStateNeedsReset = gcTreePresent !== this.shouldRunGC;
|
|
632
637
|
|
|
633
|
-
// Get the GC
|
|
634
|
-
//
|
|
635
|
-
|
|
638
|
+
// Get the GC data from the base snapshot. Use LazyPromise because we only want to do this once since it
|
|
639
|
+
// it involves fetching blobs from storage which is expensive.
|
|
640
|
+
this.baseSnapshotDataP = new LazyPromise<IGCSnapshotData | undefined>(async () => {
|
|
636
641
|
if (baseSnapshot === undefined) {
|
|
637
642
|
return undefined;
|
|
638
643
|
}
|
|
@@ -641,20 +646,10 @@ export class GarbageCollector implements IGarbageCollector {
|
|
|
641
646
|
// For newer documents, GC data should be present in the GC tree in the root of the snapshot.
|
|
642
647
|
const gcSnapshotTree = baseSnapshot.trees[gcTreeKey];
|
|
643
648
|
if (gcSnapshotTree !== undefined) {
|
|
644
|
-
|
|
649
|
+
return getGCDataFromSnapshot(
|
|
645
650
|
gcSnapshotTree,
|
|
646
651
|
readAndParseBlob,
|
|
647
652
|
);
|
|
648
|
-
if (baseGCData.tombstones !== undefined && this.tombstoneMode) {
|
|
649
|
-
this.tombstones = baseGCData.tombstones;
|
|
650
|
-
}
|
|
651
|
-
if (this.trackGCState) {
|
|
652
|
-
this.latestSummaryData = {
|
|
653
|
-
serializedGCState: JSON.stringify(generateSortedGCState(baseGCData.gcState)),
|
|
654
|
-
serializedTombstones: JSON.stringify(baseGCData.tombstones),
|
|
655
|
-
};
|
|
656
|
-
}
|
|
657
|
-
return baseGCData.gcState;
|
|
658
653
|
}
|
|
659
654
|
|
|
660
655
|
// back-compat - Older documents will have the GC blobs in each data store's summary tree. Get them and
|
|
@@ -698,7 +693,7 @@ export class GarbageCollector implements IGarbageCollector {
|
|
|
698
693
|
}
|
|
699
694
|
// If there is only one node (root node just added above), either GC is disabled or we are loading from
|
|
700
695
|
// the first summary generated by detached container. In both cases, GC was not run - return undefined.
|
|
701
|
-
return Object.keys(gcState.gcNodes).length === 1 ? undefined : gcState;
|
|
696
|
+
return Object.keys(gcState.gcNodes).length === 1 ? undefined : { gcState, tombstones: undefined };
|
|
702
697
|
} catch (error) {
|
|
703
698
|
const dpe = DataProcessingError.wrapIfUnrecognized(
|
|
704
699
|
error,
|
|
@@ -710,11 +705,11 @@ export class GarbageCollector implements IGarbageCollector {
|
|
|
710
705
|
});
|
|
711
706
|
|
|
712
707
|
/**
|
|
713
|
-
* Set up the initializer which initializes the
|
|
714
|
-
*
|
|
715
|
-
*
|
|
708
|
+
* Set up the initializer which initializes the GC state from the data in base snapshot. This is done when
|
|
709
|
+
* connected in write mode or when GC runs the first time. It sets up all unreferenced nodes from the base
|
|
710
|
+
* GC state and updates their inactive or sweep ready state.
|
|
716
711
|
*/
|
|
717
|
-
this.
|
|
712
|
+
this.initializeGCStateFromBaseSnapshotP = new LazyPromise<void>(async () => {
|
|
718
713
|
const currentReferenceTimestampMs = this.runtime.getCurrentReferenceTimestampMs();
|
|
719
714
|
/**
|
|
720
715
|
* If there is no current reference timestamp, skip initialization. We need the current timestamp to track
|
|
@@ -733,19 +728,19 @@ export class GarbageCollector implements IGarbageCollector {
|
|
|
733
728
|
return;
|
|
734
729
|
}
|
|
735
730
|
|
|
736
|
-
const
|
|
731
|
+
const baseSnapshotData = await this.baseSnapshotDataP;
|
|
737
732
|
/**
|
|
738
|
-
* The base
|
|
733
|
+
* The base snapshot data will not be present if the container is loaded from:
|
|
739
734
|
* 1. The first summary created by the detached container.
|
|
740
735
|
* 2. A summary that was generated with GC disabled.
|
|
741
736
|
* 3. A summary that was generated before GC even existed.
|
|
742
737
|
*/
|
|
743
|
-
if (
|
|
738
|
+
if (baseSnapshotData === undefined) {
|
|
744
739
|
return;
|
|
745
740
|
}
|
|
746
741
|
|
|
747
742
|
const gcNodes: { [id: string]: string[]; } = {};
|
|
748
|
-
for (const [nodeId, nodeData] of Object.entries(
|
|
743
|
+
for (const [nodeId, nodeData] of Object.entries(baseSnapshotData.gcState.gcNodes)) {
|
|
749
744
|
if (nodeData.unreferencedTimestampMs !== undefined) {
|
|
750
745
|
this.unreferencedNodesState.set(
|
|
751
746
|
nodeId,
|
|
@@ -760,18 +755,26 @@ export class GarbageCollector implements IGarbageCollector {
|
|
|
760
755
|
gcNodes[nodeId] = Array.from(nodeData.outboundRoutes);
|
|
761
756
|
}
|
|
762
757
|
this.previousGCDataFromLastRun = { gcNodes };
|
|
758
|
+
|
|
759
|
+
// If tracking state across summaries, update latest summary data from the base snapshot's GC data.
|
|
760
|
+
if (this.trackGCState) {
|
|
761
|
+
this.latestSummaryData = {
|
|
762
|
+
serializedGCState: JSON.stringify(generateSortedGCState(baseSnapshotData.gcState)),
|
|
763
|
+
serializedTombstones: JSON.stringify(baseSnapshotData.tombstones),
|
|
764
|
+
};
|
|
765
|
+
}
|
|
763
766
|
});
|
|
764
767
|
|
|
765
768
|
// Get the GC details for each node from the GC state in the base summary. This is returned in getBaseGCDetails
|
|
766
769
|
// which the caller uses to initialize each node's GC state.
|
|
767
770
|
this.baseGCDetailsP = new LazyPromise<Map<string, IGarbageCollectionDetailsBase>>(async () => {
|
|
768
|
-
const
|
|
769
|
-
if (
|
|
771
|
+
const baseSnapshotData = await this.baseSnapshotDataP;
|
|
772
|
+
if (baseSnapshotData === undefined) {
|
|
770
773
|
return new Map();
|
|
771
774
|
}
|
|
772
775
|
|
|
773
776
|
const gcNodes: { [id: string]: string[]; } = {};
|
|
774
|
-
for (const [nodeId, nodeData] of Object.entries(
|
|
777
|
+
for (const [nodeId, nodeData] of Object.entries(baseSnapshotData.gcState.gcNodes)) {
|
|
775
778
|
gcNodes[nodeId] = Array.from(nodeData.outboundRoutes);
|
|
776
779
|
}
|
|
777
780
|
// Run GC on the nodes in the base summary to get the routes used in each node in the container.
|
|
@@ -782,7 +785,7 @@ export class GarbageCollector implements IGarbageCollector {
|
|
|
782
785
|
const baseGCDetailsMap = unpackChildNodesGCDetails({ gcData: { gcNodes }, usedRoutes });
|
|
783
786
|
// Currently, the nodes may write the GC data. So, we need to update its base GC details with the
|
|
784
787
|
// unreferenced timestamp. Once we start writing the GC data here, we won't need to do this anymore.
|
|
785
|
-
for (const [nodeId, nodeData] of Object.entries(
|
|
788
|
+
for (const [nodeId, nodeData] of Object.entries(baseSnapshotData.gcState.gcNodes)) {
|
|
786
789
|
if (nodeData.unreferencedTimestampMs !== undefined) {
|
|
787
790
|
const dataStoreGCDetails = baseGCDetailsMap.get(nodeId.slice(1));
|
|
788
791
|
if (dataStoreGCDetails !== undefined) {
|
|
@@ -803,6 +806,27 @@ export class GarbageCollector implements IGarbageCollector {
|
|
|
803
806
|
}
|
|
804
807
|
}
|
|
805
808
|
|
|
809
|
+
/**
|
|
810
|
+
* Called during container initialization. Initialize the tombstone state so that object are marked as tombstones
|
|
811
|
+
* before they are loaded or used. This is important to get accurate information of whether tombstoned object are
|
|
812
|
+
* in use or not.
|
|
813
|
+
*/
|
|
814
|
+
public async initializeBaseState(): Promise<void> {
|
|
815
|
+
const baseSnapshotData = await this.baseSnapshotDataP;
|
|
816
|
+
/**
|
|
817
|
+
* The base snapshot data or tombstone state will not be present if the container is loaded from:
|
|
818
|
+
* 1. The first summary created by the detached container.
|
|
819
|
+
* 2. A summary that was generated with GC disabled.
|
|
820
|
+
* 3. A summary that was generated before GC even existed.
|
|
821
|
+
* 4. A summary that was generated with tombstone feature disabled.
|
|
822
|
+
*/
|
|
823
|
+
if (!this.tombstoneMode || baseSnapshotData?.tombstones === undefined) {
|
|
824
|
+
return;
|
|
825
|
+
}
|
|
826
|
+
this.tombstones = baseSnapshotData.tombstones;
|
|
827
|
+
this.runtime.updateUnusedRoutes(this.tombstones, true /* tombstone */);
|
|
828
|
+
}
|
|
829
|
+
|
|
806
830
|
/**
|
|
807
831
|
* Called when the connection state of the runtime changes, i.e., it connects or disconnects. GC subscribes to this
|
|
808
832
|
* to initialize the base state for non-summarizer clients so that they can track inactive / sweep ready nodes.
|
|
@@ -824,7 +848,7 @@ export class GarbageCollector implements IGarbageCollector {
|
|
|
824
848
|
* sweep in phases and we want to track when inactive and sweep ready objects are used in any client.
|
|
825
849
|
*/
|
|
826
850
|
if (this.activeConnection() && this.shouldRunGC) {
|
|
827
|
-
this.
|
|
851
|
+
this.initializeGCStateFromBaseSnapshotP.catch((error) => {});
|
|
828
852
|
}
|
|
829
853
|
}
|
|
830
854
|
|
|
@@ -880,8 +904,8 @@ export class GarbageCollector implements IGarbageCollector {
|
|
|
880
904
|
}
|
|
881
905
|
|
|
882
906
|
private async runPreGCSteps() {
|
|
883
|
-
// Ensure that
|
|
884
|
-
await this.
|
|
907
|
+
// Ensure that state has been initialized from the base snapshot data.
|
|
908
|
+
await this.initializeGCStateFromBaseSnapshotP;
|
|
885
909
|
// Let the runtime update its pending state before GC runs.
|
|
886
910
|
await this.runtime.updateStateBeforeGC();
|
|
887
911
|
}
|
|
@@ -911,24 +935,12 @@ export class GarbageCollector implements IGarbageCollector {
|
|
|
911
935
|
// If we are running in GC test mode, delete objects for unused routes. This enables testing scenarios
|
|
912
936
|
// involving access to deleted data.
|
|
913
937
|
if (this.testMode) {
|
|
914
|
-
this.runtime.
|
|
915
|
-
} else {
|
|
938
|
+
this.runtime.updateUnusedRoutes(gcResult.deletedNodeIds, false /* tombstone */);
|
|
939
|
+
} else if (this.tombstoneMode) {
|
|
916
940
|
// If we are running in GC tombstone mode, tombstone objects for unused routes. This enables testing
|
|
917
941
|
// scenarios involving access to "deleted" data without actually deleting the data from summaries.
|
|
918
942
|
// Note: we will not tombstone in test mode
|
|
919
|
-
|
|
920
|
-
const tombstoneRoutes: string[] = [];
|
|
921
|
-
// Currently only tombstone datastores
|
|
922
|
-
for (const [key, value] of this.unreferencedNodesState.entries()) {
|
|
923
|
-
if (
|
|
924
|
-
value.state === UnreferencedState.SweepReady &&
|
|
925
|
-
this.runtime.getNodeType(key) === GCNodeType.DataStore
|
|
926
|
-
) {
|
|
927
|
-
tombstoneRoutes.push(key);
|
|
928
|
-
}
|
|
929
|
-
}
|
|
930
|
-
this.runtime.deleteUnusedRoutes(tombstoneRoutes);
|
|
931
|
-
}
|
|
943
|
+
this.runtime.updateUnusedRoutes(this.tombstones, true /* tombstone */);
|
|
932
944
|
}
|
|
933
945
|
|
|
934
946
|
// Log pending unreferenced events such as a node being used after inactive. This is done after GC runs and
|
|
@@ -962,7 +974,9 @@ export class GarbageCollector implements IGarbageCollector {
|
|
|
962
974
|
}
|
|
963
975
|
|
|
964
976
|
const serializedGCState = JSON.stringify(generateSortedGCState(gcState));
|
|
965
|
-
const serializedTombstones = this.
|
|
977
|
+
const serializedTombstones = this.tombstoneMode
|
|
978
|
+
? (this.tombstones.length > 0 ? JSON.stringify(this.tombstones.sort()) : undefined)
|
|
979
|
+
: undefined;
|
|
966
980
|
|
|
967
981
|
/**
|
|
968
982
|
* Incremental summary of GC data - If any of the GC state or tombstone state hasn't changed since the last
|
|
@@ -1497,6 +1511,7 @@ export class GarbageCollector implements IGarbageCollector {
|
|
|
1497
1511
|
: this.sweepTimeoutMs,
|
|
1498
1512
|
completedGCRuns: this.completedRuns,
|
|
1499
1513
|
lastSummaryTime: this.getLastSummaryTimestampMs(),
|
|
1514
|
+
...this.createContainerMetadata,
|
|
1500
1515
|
externalRequest: requestHeaders?.[RuntimeHeaders.externalRequest],
|
|
1501
1516
|
viaHandle: requestHeaders?.[RuntimeHeaders.viaHandle],
|
|
1502
1517
|
fromId: fromNodeId,
|
|
@@ -1505,16 +1520,20 @@ export class GarbageCollector implements IGarbageCollector {
|
|
|
1505
1520
|
// For summarizer client, queue the event so it is logged the next time GC runs if the event is still valid.
|
|
1506
1521
|
// For non-summarizer client, log the event now since GC won't run on it. This may result in false positives
|
|
1507
1522
|
// but it's a good signal nonetheless and we can consume it with a grain of salt.
|
|
1523
|
+
// Inactive errors are usages of Objects that are unreferenced for at least a period of 7 days.
|
|
1524
|
+
// SweepReady errors are usages of Objects that will be deleted by GC Sweep!
|
|
1508
1525
|
if (this.isSummarizerClient) {
|
|
1509
1526
|
this.pendingEventsQueue.push({ ...propsToLog, usageType, state });
|
|
1510
1527
|
} else {
|
|
1511
1528
|
// For non-summarizer clients, only log "Loaded" type events since these objects may not be loaded in the
|
|
1512
1529
|
// summarizer clients if they are based off of user actions (such as scrolling to content for these objects)
|
|
1530
|
+
// Events generated:
|
|
1531
|
+
// InactiveObject_Loaded, SweepReadyObject_Loaded
|
|
1513
1532
|
if (usageType === "Loaded") {
|
|
1514
1533
|
this.mc.logger.sendErrorEvent({
|
|
1515
1534
|
...propsToLog,
|
|
1516
1535
|
eventName: `${state}Object_${usageType}`,
|
|
1517
|
-
pkg: packagePath
|
|
1536
|
+
pkg: packagePathToTelemetryProperty(packagePath),
|
|
1518
1537
|
stack: generateStack(),
|
|
1519
1538
|
});
|
|
1520
1539
|
}
|
|
@@ -1529,6 +1548,11 @@ export class GarbageCollector implements IGarbageCollector {
|
|
|
1529
1548
|
}
|
|
1530
1549
|
|
|
1531
1550
|
private async logUnreferencedEvents(logger: ITelemetryLogger) {
|
|
1551
|
+
// Events sent come only from the summarizer client. In between summaries, events are pushed to a queue and at
|
|
1552
|
+
// summary time they are then logged.
|
|
1553
|
+
// Events generated:
|
|
1554
|
+
// InactiveObject_Loaded, InactiveObject_Changed, InactiveObject_Revived
|
|
1555
|
+
// SweepReadyObject_Loaded, SweepReadyObject_Changed, SweepReadyObject_Revived
|
|
1532
1556
|
for (const eventProps of this.pendingEventsQueue) {
|
|
1533
1557
|
const { usageType, state, ...propsToLog } = eventProps;
|
|
1534
1558
|
/**
|
|
@@ -1555,13 +1579,13 @@ export class GarbageCollector implements IGarbageCollector {
|
|
|
1555
1579
|
}
|
|
1556
1580
|
|
|
1557
1581
|
/**
|
|
1558
|
-
* Gets the garbage collection
|
|
1582
|
+
* Gets the base garbage collection state from the given snapshot tree. It contains GC state and tombstone state.
|
|
1559
1583
|
* The GC state may be written into multiple blobs. Merge the GC state from all such blobs into one.
|
|
1560
1584
|
*/
|
|
1561
1585
|
async function getGCDataFromSnapshot(
|
|
1562
1586
|
gcSnapshotTree: ISnapshotTree,
|
|
1563
1587
|
readAndParseBlob: ReadAndParseBlob,
|
|
1564
|
-
) {
|
|
1588
|
+
): Promise<IGCSnapshotData> {
|
|
1565
1589
|
let rootGCState: IGarbageCollectionState = { gcNodes: {} };
|
|
1566
1590
|
let tombstones: string[] | undefined;
|
|
1567
1591
|
for (const key of Object.keys(gcSnapshotTree.blobs)) {
|