@fluidframework/container-runtime 1.2.0-77818 → 1.2.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.
@@ -1 +1 @@
1
- {"version":3,"file":"summaryFormat.js","sourceRoot":"","sources":["../src/summaryFormat.ts"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { assert } from \"@fluidframework/common-utils\";\nimport { IDocumentStorageService } from \"@fluidframework/driver-definitions\";\nimport { readAndParse } from \"@fluidframework/driver-utils\";\nimport { ISequencedDocumentMessage, ISnapshotTree, SummaryType } from \"@fluidframework/protocol-definitions\";\nimport { channelsTreeName, ISummaryTreeWithStats } from \"@fluidframework/runtime-definitions\";\nimport { gcTreeKey } from \"./garbageCollection\";\n\ntype OmitAttributesVersions<T> = Omit<T, \"snapshotFormatVersion\" | \"summaryFormatVersion\">;\ninterface IFluidDataStoreAttributes0 {\n readonly snapshotFormatVersion?: undefined;\n readonly summaryFormatVersion?: undefined;\n pkg: string;\n /**\n * This tells whether a data store is root. Root data stores are never collected.\n * Non-root data stores may be collected if they are not used. If this is not present, default it to\n * true. This will ensure that older data stores are incorrectly collected.\n */\n readonly isRootDataStore?: boolean;\n}\ninterface IFluidDataStoreAttributes1 extends OmitAttributesVersions<IFluidDataStoreAttributes0> {\n readonly snapshotFormatVersion: \"0.1\";\n readonly summaryFormatVersion?: undefined;\n}\ninterface IFluidDataStoreAttributes2 extends OmitAttributesVersions<IFluidDataStoreAttributes1> {\n /** Switch from snapshotFormatVersion to summaryFormatVersion */\n readonly snapshotFormatVersion?: undefined;\n readonly summaryFormatVersion: 2;\n /**\n * True if channels are not isolated in .channels subtrees, otherwise isolated.\n * This is required in both datastore attributes as well as the root container,\n * because reused summary handles may cause different format versions in each\n * datastore subtree within the summary.\n */\n readonly disableIsolatedChannels?: true;\n}\n/**\n * Added IFluidDataStoreAttributes similar to IChannelAttributes which will tell the attributes of a\n * store like the package, snapshotFormatVersion to take different decisions based on a particular\n * snapshotFormatVersion.\n */\nexport type ReadFluidDataStoreAttributes =\n | IFluidDataStoreAttributes0\n | IFluidDataStoreAttributes1\n | IFluidDataStoreAttributes2;\nexport type WriteFluidDataStoreAttributes = IFluidDataStoreAttributes1 | IFluidDataStoreAttributes2;\n\nexport function getAttributesFormatVersion(attributes: ReadFluidDataStoreAttributes): number {\n if (attributes.summaryFormatVersion) {\n /**\n * Version 2+: Introduces .channels trees for isolation of\n * channel trees from data store objects.\n */\n return attributes.summaryFormatVersion;\n } else if (attributes.snapshotFormatVersion === \"0.1\") {\n /**\n * Version 1: from this version the pkg within the data store\n * attributes blob is a JSON array rather than a string.\n */\n return 1;\n }\n /**\n * Version 0: format version is missing from summary.\n * This indicates it is an older version.\n */\n return 0;\n}\n\nexport function hasIsolatedChannels(attributes: ReadFluidDataStoreAttributes): boolean {\n return !!attributes.summaryFormatVersion && !attributes.disableIsolatedChannels;\n}\n\nexport interface IContainerRuntimeMetadata extends ICreateContainerMetadata, IGCMetadata {\n readonly summaryFormatVersion: 1;\n /** The last message processed at the time of summary. Only primitive property types are added to the summary. */\n readonly message: ISummaryMetadataMessage | undefined;\n /** True if channels are not isolated in .channels subtrees, otherwise isolated. */\n readonly disableIsolatedChannels?: true;\n /** The summary number for a container's summary. Incremented on summaries throughout its lifetime. */\n readonly summaryNumber?: number;\n /**\n * @deprecated - User summaryNumber instead.\n * Counter of the last summary happened, increments every time we summarize\n */\n readonly summaryCount?: number;\n}\n\nexport interface ICreateContainerMetadata {\n /** Runtime version of the container when it was first created */\n createContainerRuntimeVersion?: string;\n /** Timestamp of the container when it was first created */\n createContainerTimestamp?: number;\n}\n\nexport type GCVersion = number;\nexport interface IGCMetadata {\n /**\n * The version of the GC code that was run to generate the GC data that is written in the summary.\n * Also, used to determine whether GC is enabled for this container or not:\n * - A value of 0 or undefined means GC is disabled.\n * - A value greater than 0 means GC is enabled.\n */\n readonly gcFeature?: GCVersion;\n /** If this is present, the session for this container will expire after this time and the container will close */\n readonly sessionExpiryTimeoutMs?: number;\n /**\n * Tells whether the GC sweep phase is enabled for this container.\n * - True means sweep phase is enabled.\n * - False means sweep phase is disabled. If GC is disabled as per gcFeature, sweep is also disabled.\n */\n readonly sweepEnabled?: boolean;\n}\n\n/** The properties of an ISequencedDocumentMessage to be stored in the metadata blob in summary. */\nexport type ISummaryMetadataMessage = Pick<ISequencedDocumentMessage,\n | \"clientId\"\n | \"clientSequenceNumber\"\n | \"minimumSequenceNumber\"\n | \"referenceSequenceNumber\"\n | \"sequenceNumber\"\n | \"timestamp\"\n | \"type\">;\n\n/**\n * Extracts the properties from an ISequencedDocumentMessage as defined by ISummaryMetadataMessage. This message is\n * added to the metadata blob in summary.\n */\nexport const extractSummaryMetadataMessage = (\n message?: ISequencedDocumentMessage,\n): ISummaryMetadataMessage | undefined => message === undefined ? undefined : {\n clientId: message.clientId,\n clientSequenceNumber: message.clientSequenceNumber,\n minimumSequenceNumber: message.minimumSequenceNumber,\n referenceSequenceNumber: message.referenceSequenceNumber,\n sequenceNumber: message.sequenceNumber,\n timestamp: message.timestamp,\n type: message.type,\n};\n\nexport function getMetadataFormatVersion(metadata?: IContainerRuntimeMetadata): number {\n /**\n * Version 2+: Introduces runtime sequence number for data verification.\n *\n * Version 1+: Introduces .metadata blob and .channels trees for isolation of\n * data store trees from container-level objects.\n * Also introduces enableGC option stored in the summary.\n *\n * Version 0: metadata blob missing; format version is missing from summary.\n * This indicates it is an older version.\n */\n return metadata?.summaryFormatVersion ?? 0;\n}\n\nexport const aliasBlobName = \".aliases\";\nexport const metadataBlobName = \".metadata\";\nexport const chunksBlobName = \".chunks\";\nexport const electedSummarizerBlobName = \".electedSummarizer\";\nexport const blobsTreeName = \".blobs\";\n\nexport function rootHasIsolatedChannels(metadata?: IContainerRuntimeMetadata): boolean {\n return !!metadata && !metadata.disableIsolatedChannels;\n}\n\nexport function getGCVersion(metadata?: IGCMetadata): GCVersion {\n if (!metadata) {\n // Force to 0/disallowed in prior versions\n return 0;\n }\n return metadata.gcFeature ?? 0;\n}\n\nexport const protocolTreeName = \".protocol\";\n\n/**\n * List of tree IDs at the container level which are reserved.\n * This is for older versions of summaries that do not yet have an\n * isolated data stores namespace. Without the namespace, this must\n * be used to prevent name collisions with data store IDs.\n */\nexport const nonDataStorePaths = [protocolTreeName, \".logTail\", \".serviceProtocol\", blobsTreeName, gcTreeKey];\n\nexport const dataStoreAttributesBlobName = \".component\";\n\n/**\n * Modifies summary tree and stats to put tree under .channels tree.\n * Converts from:\n * ```ts\n * {\n * type: SummaryType.Tree,\n * tree: { a: {...}, b: {...}, c: {...} },\n * }\n * ```\n *\n * to:\n *\n * ```ts\n * {\n * type: SummaryType.Tree,\n * tree: {\n * \".channels\": {\n * type: SummaryType.Tree,\n * tree: { a: {...}, b: {...}, c: {...} }\n * },\n * },\n * }\n * ```\n * And adds +1 to treeNodeCount in stats.\n * @param summarizeResult - summary tree and stats to modify\n */\nexport function wrapSummaryInChannelsTree(summarizeResult: ISummaryTreeWithStats): void {\n summarizeResult.summary = {\n type: SummaryType.Tree,\n tree: { [channelsTreeName]: summarizeResult.summary },\n };\n summarizeResult.stats.treeNodeCount++;\n}\n\nexport async function getFluidDataStoreAttributes(\n storage: IDocumentStorageService,\n snapshot: ISnapshotTree,\n): Promise<ReadFluidDataStoreAttributes> {\n const attributes = await readAndParse<ReadFluidDataStoreAttributes>(\n storage, snapshot.blobs[dataStoreAttributesBlobName]);\n // Use the snapshotFormatVersion to determine how the pkg is encoded in the snapshot.\n // For snapshotFormatVersion = \"0.1\" (1) or above, pkg is jsonified, otherwise it is just a string.\n // However the feature of loading a detached container from snapshot, is added when the\n // snapshotFormatVersion is at least \"0.1\" (1), so we don't expect it to be anything else.\n const formatVersion = getAttributesFormatVersion(attributes);\n assert(formatVersion > 0,\n 0x1d5 /* Invalid snapshot format version */);\n return attributes;\n}\n"]}
1
+ {"version":3,"file":"summaryFormat.js","sourceRoot":"","sources":["../src/summaryFormat.ts"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { assert } from \"@fluidframework/common-utils\";\nimport { IDocumentStorageService } from \"@fluidframework/driver-definitions\";\nimport { readAndParse } from \"@fluidframework/driver-utils\";\nimport { ISequencedDocumentMessage, ISnapshotTree, SummaryType } from \"@fluidframework/protocol-definitions\";\nimport { channelsTreeName, ISummaryTreeWithStats } from \"@fluidframework/runtime-definitions\";\nimport { gcTreeKey } from \"./garbageCollection\";\n\ntype OmitAttributesVersions<T> = Omit<T, \"snapshotFormatVersion\" | \"summaryFormatVersion\">;\ninterface IFluidDataStoreAttributes0 {\n readonly snapshotFormatVersion?: undefined;\n readonly summaryFormatVersion?: undefined;\n pkg: string;\n /**\n * This tells whether a data store is root. Root data stores are never collected.\n * Non-root data stores may be collected if they are not used. If this is not present, default it to\n * true. This will ensure that older data stores are incorrectly collected.\n */\n readonly isRootDataStore?: boolean;\n}\ninterface IFluidDataStoreAttributes1 extends OmitAttributesVersions<IFluidDataStoreAttributes0> {\n readonly snapshotFormatVersion: \"0.1\";\n readonly summaryFormatVersion?: undefined;\n}\ninterface IFluidDataStoreAttributes2 extends OmitAttributesVersions<IFluidDataStoreAttributes1> {\n /** Switch from snapshotFormatVersion to summaryFormatVersion */\n readonly snapshotFormatVersion?: undefined;\n readonly summaryFormatVersion: 2;\n /**\n * True if channels are not isolated in .channels subtrees, otherwise isolated.\n * This is required in both datastore attributes as well as the root container,\n * because reused summary handles may cause different format versions in each\n * datastore subtree within the summary.\n */\n readonly disableIsolatedChannels?: true;\n}\n/**\n * Added IFluidDataStoreAttributes similar to IChannelAttributes which will tell the attributes of a\n * store like the package, snapshotFormatVersion to take different decisions based on a particular\n * snapshotFormatVersion.\n */\nexport type ReadFluidDataStoreAttributes =\n | IFluidDataStoreAttributes0\n | IFluidDataStoreAttributes1\n | IFluidDataStoreAttributes2;\nexport type WriteFluidDataStoreAttributes = IFluidDataStoreAttributes1 | IFluidDataStoreAttributes2;\n\nexport function getAttributesFormatVersion(attributes: ReadFluidDataStoreAttributes): number {\n if (attributes.summaryFormatVersion) {\n /**\n * Version 2+: Introduces .channels trees for isolation of\n * channel trees from data store objects.\n */\n return attributes.summaryFormatVersion;\n } else if (attributes.snapshotFormatVersion === \"0.1\") {\n /**\n * Version 1: from this version the pkg within the data store\n * attributes blob is a JSON array rather than a string.\n */\n return 1;\n }\n /**\n * Version 0: format version is missing from summary.\n * This indicates it is an older version.\n */\n return 0;\n}\n\nexport function hasIsolatedChannels(attributes: ReadFluidDataStoreAttributes): boolean {\n return !!attributes.summaryFormatVersion && !attributes.disableIsolatedChannels;\n}\n\nexport interface IContainerRuntimeMetadata extends ICreateContainerMetadata, IGCMetadata {\n readonly summaryFormatVersion: 1;\n /** The last message processed at the time of summary. Only primitive property types are added to the summary. */\n readonly message: ISummaryMetadataMessage | undefined;\n /** True if channels are not isolated in .channels subtrees, otherwise isolated. */\n readonly disableIsolatedChannels?: true;\n /** The summary number for a container's summary. Incremented on summaries throughout its lifetime. */\n readonly summaryNumber?: number;\n /**\n * @deprecated - User summaryNumber instead.\n * Counter of the last summary happened, increments every time we summarize\n */\n readonly summaryCount?: number;\n}\n\nexport interface ICreateContainerMetadata {\n /** Runtime version of the container when it was first created */\n createContainerRuntimeVersion?: string;\n /** Timestamp of the container when it was first created */\n createContainerTimestamp?: number;\n}\n\nexport type GCVersion = number;\nexport interface IGCMetadata {\n /**\n * The version of the GC code that was run to generate the GC data that is written in the summary.\n * Also, used to determine whether GC is enabled for this container or not:\n * - A value of 0 or undefined means GC is disabled.\n * - A value greater than 0 means GC is enabled.\n */\n readonly gcFeature?: GCVersion;\n /** If this is present, the session for this container will expire after this time and the container will close */\n readonly sessionExpiryTimeoutMs?: number;\n /**\n * Tells whether the GC sweep phase is enabled for this container.\n * - True means sweep phase is enabled.\n * - False means sweep phase is disabled. If GC is disabled as per gcFeature, sweep is also disabled.\n */\n readonly sweepEnabled?: boolean;\n}\n\n/** The properties of an ISequencedDocumentMessage to be stored in the metadata blob in summary. */\nexport type ISummaryMetadataMessage = Pick<ISequencedDocumentMessage,\n | \"clientId\"\n | \"clientSequenceNumber\"\n | \"minimumSequenceNumber\"\n | \"referenceSequenceNumber\"\n | \"sequenceNumber\"\n | \"timestamp\"\n | \"type\">;\n\n/**\n * Extracts the properties from an ISequencedDocumentMessage as defined by ISummaryMetadataMessage. This message is\n * added to the metadata blob in summary.\n */\nexport const extractSummaryMetadataMessage = (\n message?: ISequencedDocumentMessage,\n): ISummaryMetadataMessage | undefined => message === undefined ? undefined : {\n clientId: message.clientId,\n clientSequenceNumber: message.clientSequenceNumber,\n minimumSequenceNumber: message.minimumSequenceNumber,\n referenceSequenceNumber: message.referenceSequenceNumber,\n sequenceNumber: message.sequenceNumber,\n timestamp: message.timestamp,\n type: message.type,\n};\n\nexport function getMetadataFormatVersion(metadata?: IContainerRuntimeMetadata): number {\n /**\n * Version 2+: Introduces runtime sequence number for data verification.\n *\n * Version 1+: Introduces .metadata blob and .channels trees for isolation of\n * data store trees from container-level objects.\n * Also introduces enableGC option stored in the summary.\n *\n * Version 0: metadata blob missing; format version is missing from summary.\n * This indicates it is an older version.\n */\n return metadata?.summaryFormatVersion ?? 0;\n}\n\nexport const aliasBlobName = \".aliases\";\nexport const metadataBlobName = \".metadata\";\nexport const chunksBlobName = \".chunks\";\nexport const electedSummarizerBlobName = \".electedSummarizer\";\nexport const blobsTreeName = \".blobs\";\n\nexport function rootHasIsolatedChannels(metadata?: IContainerRuntimeMetadata): boolean {\n return !!metadata && !metadata.disableIsolatedChannels;\n}\n\nexport function getGCVersion(metadata?: IGCMetadata): GCVersion {\n if (!metadata) {\n // Force to 0/disallowed in prior versions\n return 0;\n }\n return metadata.gcFeature ?? 0;\n}\n\nexport const protocolTreeName = \".protocol\";\n\n/**\n * List of tree IDs at the container level which are reserved.\n * This is for older versions of summaries that do not yet have an\n * isolated data stores namespace. Without the namespace, this must\n * be used to prevent name collisions with data store IDs.\n */\nexport const nonDataStorePaths = [protocolTreeName, \".logTail\", \".serviceProtocol\", blobsTreeName, gcTreeKey];\n\nexport const dataStoreAttributesBlobName = \".component\";\n\n/**\n * Modifies summary tree and stats to put tree under .channels tree.\n *\n * @param summarizeResult - Summary tree and stats to modify\n *\n * @example\n * Converts from:\n * ```typescript\n * {\n * type: SummaryType.Tree,\n * tree: { a: {...}, b: {...}, c: {...} },\n * }\n * ```\n *\n * to:\n *\n * ```typescript\n * {\n * type: SummaryType.Tree,\n * tree: {\n * \".channels\": {\n * type: SummaryType.Tree,\n * tree: { a: {...}, b: {...}, c: {...} }\n * },\n * },\n * }\n * ```\n * And adds +1 to treeNodeCount in stats.\n */\nexport function wrapSummaryInChannelsTree(summarizeResult: ISummaryTreeWithStats): void {\n summarizeResult.summary = {\n type: SummaryType.Tree,\n tree: { [channelsTreeName]: summarizeResult.summary },\n };\n summarizeResult.stats.treeNodeCount++;\n}\n\nexport async function getFluidDataStoreAttributes(\n storage: IDocumentStorageService,\n snapshot: ISnapshotTree,\n): Promise<ReadFluidDataStoreAttributes> {\n const attributes = await readAndParse<ReadFluidDataStoreAttributes>(\n storage, snapshot.blobs[dataStoreAttributesBlobName]);\n // Use the snapshotFormatVersion to determine how the pkg is encoded in the snapshot.\n // For snapshotFormatVersion = \"0.1\" (1) or above, pkg is jsonified, otherwise it is just a string.\n // However the feature of loading a detached container from snapshot, is added when the\n // snapshotFormatVersion is at least \"0.1\" (1), so we don't expect it to be anything else.\n const formatVersion = getAttributesFormatVersion(attributes);\n assert(formatVersion > 0,\n 0x1d5 /* Invalid snapshot format version */);\n return attributes;\n}\n"]}
@@ -15,6 +15,7 @@ export declare const gcBlobPrefix = "__gc";
15
15
  export declare const runSessionExpiryKey = "Fluid.GarbageCollection.RunSessionExpiry";
16
16
  export declare const disableSessionExpiryKey = "Fluid.GarbageCollection.DisableSessionExpiry";
17
17
  export declare const trackGCStateKey = "Fluid.GarbageCollection.TrackGCState";
18
+ export declare const oneDayMs: number;
18
19
  export declare const defaultSessionExpiryDurationMs: number;
19
20
  /** The statistics of the system state after a garbage collection run. */
20
21
  export interface IGCStats {
@@ -74,7 +75,6 @@ export interface IGarbageCollector {
74
75
  /** Run garbage collection and update the reference / used state of the system. */
75
76
  collectGarbage(options: {
76
77
  logger?: ITelemetryLogger;
77
- runGC?: boolean;
78
78
  runSweep?: boolean;
79
79
  fullGC?: boolean;
80
80
  }): Promise<IGCStats>;
@@ -104,7 +104,18 @@ export interface IGarbageCollectorCreateParams {
104
104
  readonly getNodePackagePath: (nodePath: string) => Promise<readonly string[] | undefined>;
105
105
  readonly getLastSummaryTimestampMs: () => number | undefined;
106
106
  readonly readAndParseBlob: ReadAndParseBlob;
107
+ readonly snapshotCacheExpiryMs?: number;
107
108
  }
109
+ /** The state of node that is unreferenced. */
110
+ declare const UnreferencedState: {
111
+ /** The node is active, i.e., it can become referenced again. */
112
+ Active: string;
113
+ /** The node is inactive, i.e., it should not become referenced. */
114
+ Inactive: string;
115
+ /** The node is ready to be deleted by the sweep phase. */
116
+ SweepReady: string;
117
+ };
118
+ export declare type UnreferencedState = typeof UnreferencedState[keyof typeof UnreferencedState];
108
119
  /**
109
120
  * The garbage collector for the container runtime. It consolidates the garbage collection functionality and maintains
110
121
  * its state across summaries.
@@ -124,10 +135,6 @@ export interface IGarbageCollectorCreateParams {
124
135
  */
125
136
  export declare class GarbageCollector implements IGarbageCollector {
126
137
  static create(createParams: IGarbageCollectorCreateParams): IGarbageCollector;
127
- /**
128
- * The time in ms to expire a session for a client for gc.
129
- */
130
- private readonly sessionExpiryTimeoutMs;
131
138
  /**
132
139
  * Tells whether the GC state needs to be reset in the next summary. We need to do this if:
133
140
  * 1. GC was enabled and is now disabled. The GC state needs to be removed and everything becomes referenced.
@@ -190,7 +197,6 @@ export declare class GarbageCollector implements IGarbageCollector {
190
197
  private readonly newReferencesSinceLastRun;
191
198
  private readonly initializeBaseStateP;
192
199
  private readonly baseGCDetailsP;
193
- private readonly inactiveTimeoutMs;
194
200
  private readonly unreferencedNodesState;
195
201
  private sessionExpiryTimer?;
196
202
  private readonly loggedUnreferencedEvents;
@@ -199,6 +205,12 @@ export declare class GarbageCollector implements IGarbageCollector {
199
205
  private readonly runtime;
200
206
  private readonly gcOptions;
201
207
  private readonly isSummarizerClient;
208
+ /** The time in ms to expire a session for a client for gc. */
209
+ private readonly sessionExpiryTimeoutMs;
210
+ /** The time after which an unreferenced node is inactive. */
211
+ private readonly inactiveTimeoutMs;
212
+ /** The time after which an unreferenced node is ready to be swept. */
213
+ private readonly sweepTimeoutMs;
202
214
  /** For a given node path, returns the node's package path. */
203
215
  private readonly getNodePackagePath;
204
216
  /** Returns the timestamp of the last summary generated for this container. */
@@ -293,12 +305,15 @@ export declare class GarbageCollector implements IGarbageCollector {
293
305
  */
294
306
  private generateStats;
295
307
  /**
296
- * Called when a node is used. If the node is inactive, queue up an event that will be logged next time GC runs.
308
+ * For nodes that are ready to sweep, log an event for now. Until we start running sweep which deletes objects,
309
+ * this will give us a view into how much deleted content a container has.
297
310
  */
298
- private inactiveNodeUsed;
311
+ private logSweepEvents;
299
312
  /**
300
- * Logs pending unreferenced events if they are still valid.
313
+ * Called when an inactive node is used after. Queue up an event that will be logged next time GC runs.
301
314
  */
302
- private logPendingEvents;
315
+ private inactiveNodeUsed;
316
+ private logUnreferencedEvents;
303
317
  }
318
+ export {};
304
319
  //# sourceMappingURL=garbageCollection.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"garbageCollection.d.ts","sourceRoot":"","sources":["../src/garbageCollection.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"garbageCollection.d.ts","sourceRoot":"","sources":["../src/garbageCollection.ts"],"names":[],"mappings":"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"}
@@ -2,6 +2,17 @@
2
2
  * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
3
  * Licensed under the MIT License.
4
4
  */
5
+ var __rest = (this && this.__rest) || function (s, e) {
6
+ var t = {};
7
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
8
+ t[p] = s[p];
9
+ if (s != null && typeof Object.getOwnPropertySymbols === "function")
10
+ for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
11
+ if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
12
+ t[p[i]] = s[p[i]];
13
+ }
14
+ return t;
15
+ };
5
16
  import { assert, LazyPromise, Timer } from "@fluidframework/common-utils";
6
17
  import { ClientSessionExpiredError, DataProcessingError, UsageError } from "@fluidframework/container-utils";
7
18
  import { cloneGCData, concatGarbageCollectionStates, concatGarbageCollectionData, runGarbageCollection, unpackChildNodesGCDetails, } from "@fluidframework/garbage-collector";
@@ -20,10 +31,10 @@ export const gcTreeKey = "gc";
20
31
  export const gcBlobPrefix = "__gc";
21
32
  // Feature gate key to turn GC on / off.
22
33
  const runGCKey = "Fluid.GarbageCollection.RunGC";
23
- // Feature gate key to turn GC test mode on / off.
24
- const gcTestModeKey = "Fluid.GarbageCollection.GCTestMode";
25
34
  // Feature gate key to turn GC sweep on / off.
26
35
  const runSweepKey = "Fluid.GarbageCollection.RunSweep";
36
+ // Feature gate key to turn GC test mode on / off.
37
+ const gcTestModeKey = "Fluid.GarbageCollection.GCTestMode";
27
38
  // Feature gate key to write GC data at the root of the summary tree.
28
39
  const writeAtRootKey = "Fluid.GarbageCollection.WriteDataAtRoot";
29
40
  // Feature gate key to expire a session after a set period of time.
@@ -32,8 +43,12 @@ export const runSessionExpiryKey = "Fluid.GarbageCollection.RunSessionExpiry";
32
43
  export const disableSessionExpiryKey = "Fluid.GarbageCollection.DisableSessionExpiry";
33
44
  // Feature gate key to write the gc blob as a handle if the data is the same.
34
45
  export const trackGCStateKey = "Fluid.GarbageCollection.TrackGCState";
35
- const defaultInactiveTimeoutMs = 7 * 24 * 60 * 60 * 1000; // 7 days
36
- export const defaultSessionExpiryDurationMs = 30 * 24 * 60 * 60 * 1000; // 30 days
46
+ // Feature gate key to turn GC sweep log off.
47
+ const disableSweepLogKey = "Fluid.GarbageCollection.DisableSweepLog";
48
+ // One day in milliseconds.
49
+ export const oneDayMs = 1 * 24 * 60 * 60 * 1000;
50
+ const defaultInactiveTimeoutMs = 7 * oneDayMs; // 7 days
51
+ export const defaultSessionExpiryDurationMs = 30 * oneDayMs; // 30 days
37
52
  /** The types of GC nodes in the GC reference graph. */
38
53
  export const GCNodeType = {
39
54
  // Nodes that are for data stores.
@@ -45,50 +60,84 @@ export const GCNodeType = {
45
60
  // Nodes that are neither of the above. For example, root node.
46
61
  Other: "Other",
47
62
  };
63
+ /** The state of node that is unreferenced. */
64
+ const UnreferencedState = {
65
+ /** The node is active, i.e., it can become referenced again. */
66
+ Active: "Active",
67
+ /** The node is inactive, i.e., it should not become referenced. */
68
+ Inactive: "Inactive",
69
+ /** The node is ready to be deleted by the sweep phase. */
70
+ SweepReady: "SweepReady",
71
+ };
48
72
  /**
49
- * Helper class that tracks the state of an unreferenced node such as the time it was unreferenced. It also sets
50
- * the node's state to inactive if it remains unreferenced for a given amount of time (inactiveTimeoutMs).
73
+ * Helper class that tracks the state of an unreferenced node such as the time it was unreferenced and if it can
74
+ * be deleted by the sweep phase.
51
75
  */
52
76
  class UnreferencedStateTracker {
53
- constructor(unreferencedTimestampMs, inactiveTimeoutMs, currentReferenceTimestampMs) {
77
+ constructor(unreferencedTimestampMs,
78
+ /** The time after which node transitions to Inactive state. */
79
+ inactiveTimeoutMs,
80
+ /** The time after which node transitions to SweepReady state; undefined if session expiry is disabled. */
81
+ sweepTimeoutMs,
82
+ /** The current reference timestamp; undefined if no ops have ever been processed which can happen in tests. */
83
+ currentReferenceTimestampMs) {
54
84
  this.unreferencedTimestampMs = unreferencedTimestampMs;
55
85
  this.inactiveTimeoutMs = inactiveTimeoutMs;
56
- this._inactive = false;
57
- // If there is no current reference timestamp, don't track the node's inactive state. This will happen later
58
- // when updateTracking is called with a reference timestamp.
86
+ this.sweepTimeoutMs = sweepTimeoutMs;
87
+ this._state = UnreferencedState.Active;
88
+ // If there is no current reference timestamp, don't track the node's unreferenced state. This will happen
89
+ // later when updateTracking is called with a reference timestamp.
59
90
  if (currentReferenceTimestampMs !== undefined) {
60
91
  this.updateTracking(currentReferenceTimestampMs);
61
92
  }
62
93
  }
63
- get inactive() {
64
- return this._inactive;
94
+ get state() {
95
+ return this._state;
65
96
  }
66
- /**
67
- * Updates the tracking state based on the provided timestamp.
68
- */
97
+ /* Updates the unreferenced state based on the provided timestamp. */
69
98
  updateTracking(currentReferenceTimestampMs) {
70
- var _a;
71
99
  const unreferencedDurationMs = currentReferenceTimestampMs - this.unreferencedTimestampMs;
72
- // If the timeout has already expired, the node has become inactive.
73
- if (unreferencedDurationMs > this.inactiveTimeoutMs) {
74
- this._inactive = true;
75
- (_a = this.timer) === null || _a === void 0 ? void 0 : _a.clear();
100
+ // If the node has been unreferenced for sweep timeout amount of time, update the state to SweepReady.
101
+ if (this.sweepTimeoutMs !== undefined && unreferencedDurationMs >= this.sweepTimeoutMs) {
102
+ this._state = UnreferencedState.SweepReady;
103
+ this.clearTimers();
104
+ return;
105
+ }
106
+ // If the node has been unreferenced for inactive timeoutMs amount of time, update the state to inactive.
107
+ // Also, start a timer for the sweep timeout.
108
+ if (unreferencedDurationMs >= this.inactiveTimeoutMs) {
109
+ this._state = UnreferencedState.Inactive;
110
+ this.clearTimers();
111
+ if (this.sweepTimeoutMs !== undefined) {
112
+ setLongTimeout(this.sweepTimeoutMs - unreferencedDurationMs, () => { this._state = UnreferencedState.SweepReady; }, (timer) => { this.sweepTimer = timer; });
113
+ }
76
114
  return;
77
115
  }
78
- // The node isn't inactive yet. Restart a timer for the duration remaining for it to become inactive.
116
+ // The node is still active. Start the inactive timer for the remaining duration.
79
117
  const remainingDurationMs = this.inactiveTimeoutMs - unreferencedDurationMs;
80
- if (this.timer === undefined) {
81
- this.timer = new Timer(remainingDurationMs, () => { this._inactive = true; });
118
+ if (this.inactiveTimer === undefined) {
119
+ const inactiveTimeoutHandler = () => {
120
+ this._state = UnreferencedState.Inactive;
121
+ // After the node becomes inactive, start the sweep timer after which the node will be ready for sweep.
122
+ if (this.sweepTimeoutMs !== undefined) {
123
+ setLongTimeout(this.sweepTimeoutMs - this.inactiveTimeoutMs, () => { this._state = UnreferencedState.SweepReady; }, (timer) => { this.sweepTimer = timer; });
124
+ }
125
+ };
126
+ this.inactiveTimer = new Timer(remainingDurationMs, () => inactiveTimeoutHandler());
82
127
  }
83
- this.timer.restart(remainingDurationMs);
128
+ this.inactiveTimer.restart(remainingDurationMs);
84
129
  }
85
- /**
86
- * Stop tracking this node. Reset the unreferenced timer, if any, and reset inactive state.
87
- */
88
- stopTracking() {
130
+ clearTimers() {
89
131
  var _a;
90
- (_a = this.timer) === null || _a === void 0 ? void 0 : _a.clear();
91
- this._inactive = false;
132
+ (_a = this.inactiveTimer) === null || _a === void 0 ? void 0 : _a.clear();
133
+ if (this.sweepTimer !== undefined) {
134
+ clearTimeout(this.sweepTimer);
135
+ }
136
+ }
137
+ /** Stop tracking this node. Reset the unreferenced timers and state, if any. */
138
+ stopTracking() {
139
+ this.clearTimers();
140
+ this._state = UnreferencedState.Active;
92
141
  }
93
142
  }
94
143
  /**
@@ -181,20 +230,21 @@ export class GarbageCollector {
181
230
  this.sessionExpiryTimeoutMs = defaultSessionExpiryDurationMs;
182
231
  }
183
232
  }
184
- // If session expiry is enabled, we need to close the container when the timeout expires
185
- if (this.sessionExpiryTimeoutMs !== undefined
186
- && this.mc.config.getBoolean(disableSessionExpiryKey) !== true) {
187
- // If Test Override config is set, override Session Expiry timeout
233
+ // If session expiry is enabled, we need to close the container when the session expiry timeout expires.
234
+ if (this.sessionExpiryTimeoutMs !== undefined && this.mc.config.getBoolean(disableSessionExpiryKey) !== true) {
235
+ // If Test Override config is set, override Session Expiry timeout.
188
236
  const overrideSessionExpiryTimeoutMs = this.mc.config.getNumber("Fluid.GarbageCollection.TestOverride.SessionExpiryMs");
189
- if (overrideSessionExpiryTimeoutMs !== undefined) {
190
- this.sessionExpiryTimeoutMs = overrideSessionExpiryTimeoutMs;
237
+ const timeoutMs = overrideSessionExpiryTimeoutMs !== null && overrideSessionExpiryTimeoutMs !== void 0 ? overrideSessionExpiryTimeoutMs : this.sessionExpiryTimeoutMs;
238
+ setLongTimeout(timeoutMs, () => { this.runtime.closeFn(new ClientSessionExpiredError(`Client session expired.`, timeoutMs)); }, (timer) => { this.sessionExpiryTimer = timer; });
239
+ /**
240
+ * Sweep timeout is the time after which unreferenced content can be swept.
241
+ * Sweep timeout = session expiry timeout + snapshot cache expiry timeout + one day buffer. The buffer is
242
+ * added to account for any clock skew. We use server timestamps throughout so the skew should be minimal
243
+ * but make it one day to be safe.
244
+ */
245
+ if (createParams.snapshotCacheExpiryMs !== undefined) {
246
+ this.sweepTimeoutMs = this.sessionExpiryTimeoutMs + createParams.snapshotCacheExpiryMs + oneDayMs;
191
247
  }
192
- const timeoutMs = this.sessionExpiryTimeoutMs;
193
- setLongTimeout(timeoutMs, () => {
194
- this.runtime.closeFn(new ClientSessionExpiredError(`Client session expired.`, timeoutMs));
195
- }, (timer) => {
196
- this.sessionExpiryTimer = timer;
197
- });
198
248
  }
199
249
  // For existing document, the latest summary is the one that we loaded from. So, use its GC version as the
200
250
  // latest tracked GC version. For new documents, we will be writing the first summary with the current version.
@@ -210,17 +260,23 @@ export class GarbageCollector {
210
260
  this.gcEnabled
211
261
  // GC must not be disabled via GC options.
212
262
  && !this.gcOptions.disableGC);
213
- this.trackGCState = this.mc.config.getBoolean(trackGCStateKey) === true;
214
263
  /**
215
264
  * Whether sweep should run or not. The following conditions have to be met to run sweep:
216
265
  * 1. Overall GC or mark phase must be enabled (this.shouldRunGC).
217
- * 2. Session expiry and sweep should be enabled for this container. Without session expiry we cannot safely
218
- * delete unreferenced objects. This condition (#2) can be overridden via runSweepKey feature flag.
266
+ * 2. Sweep timeout should be available. Without this, we wouldn't know when an object should be deleted.
267
+ * 3. Sweep should be enabled for this container (this.sweepEnabled). This can be overridden via runSweep
268
+ * feature flag.
219
269
  */
220
- this.shouldRunSweep = this.shouldRunGC && ((_c = this.mc.config.getBoolean(runSweepKey)) !== null && _c !== void 0 ? _c : (this.sessionExpiryTimeoutMs !== undefined && this.sweepEnabled));
270
+ this.shouldRunSweep = this.shouldRunGC
271
+ && this.sweepTimeoutMs !== undefined
272
+ && ((_c = this.mc.config.getBoolean(runSweepKey)) !== null && _c !== void 0 ? _c : this.sweepEnabled);
273
+ this.trackGCState = this.mc.config.getBoolean(trackGCStateKey) === true;
221
274
  // Override inactive timeout if test config or gc options to override it is set.
222
- this.inactiveTimeoutMs =
223
- (_e = (_d = this.mc.config.getNumber("Fluid.GarbageCollection.TestOverride.InactiveTimeoutMs")) !== null && _d !== void 0 ? _d : this.gcOptions.inactiveTimeoutMs) !== null && _e !== void 0 ? _e : defaultInactiveTimeoutMs;
275
+ this.inactiveTimeoutMs = (_e = (_d = this.mc.config.getNumber("Fluid.GarbageCollection.TestOverride.InactiveTimeoutMs")) !== null && _d !== void 0 ? _d : this.gcOptions.inactiveTimeoutMs) !== null && _e !== void 0 ? _e : defaultInactiveTimeoutMs;
276
+ // Inactive timeout must be greater than sweep timeout since a node goes from active -> inactive -> sweep ready.
277
+ if (this.sweepTimeoutMs !== undefined && this.inactiveTimeoutMs > this.sweepTimeoutMs) {
278
+ throw new UsageError("inactive timeout should not be greated than the sweep timeout");
279
+ }
224
280
  // Whether we are running in test mode. In this mode, unreferenced nodes are immediately deleted.
225
281
  this.testMode = (_f = this.mc.config.getBoolean(gcTestModeKey)) !== null && _f !== void 0 ? _f : this.gcOptions.runGCInTestMode === true;
226
282
  // GC state is written into root of the summary tree by default. Can be overridden via feature flag for now.
@@ -299,7 +355,7 @@ export class GarbageCollector {
299
355
  const gcNodes = {};
300
356
  for (const [nodeId, nodeData] of Object.entries(baseState.gcNodes)) {
301
357
  if (nodeData.unreferencedTimestampMs !== undefined) {
302
- this.unreferencedNodesState.set(nodeId, new UnreferencedStateTracker(nodeData.unreferencedTimestampMs, this.inactiveTimeoutMs, currentReferenceTimestampMs));
358
+ this.unreferencedNodesState.set(nodeId, new UnreferencedStateTracker(nodeData.unreferencedTimestampMs, this.inactiveTimeoutMs, this.sweepTimeoutMs, currentReferenceTimestampMs));
303
359
  }
304
360
  gcNodes[nodeId] = Array.from(nodeData.outboundRoutes);
305
361
  }
@@ -335,7 +391,7 @@ export class GarbageCollector {
335
391
  });
336
392
  // Log all the GC options and the state determined by the garbage collector. This is interesting only for the
337
393
  // summarizer client since it is the only one that runs GC. It also helps keep the telemetry less noisy.
338
- const gcConfigProps = JSON.stringify(Object.assign({ gcEnabled: this.gcEnabled, sweepEnabled: this.sweepEnabled, runGC: this.shouldRunGC, runSweep: this.shouldRunSweep, writeAtRoot: this._writeDataAtRoot, testMode: this.testMode, sessionExpiry: this.sessionExpiryTimeoutMs, inactiveTimeout: this.inactiveTimeoutMs, existing: createParams.existing }, this.gcOptions));
394
+ const gcConfigProps = JSON.stringify(Object.assign({ gcEnabled: this.gcEnabled, sweepEnabled: this.sweepEnabled, runGC: this.shouldRunGC, runSweep: this.shouldRunSweep, writeAtRoot: this._writeDataAtRoot, testMode: this.testMode, sessionExpiry: this.sessionExpiryTimeoutMs, inactiveTimeout: this.inactiveTimeoutMs, existing: createParams.existing, trackGCState: this.trackGCState }, this.gcOptions));
339
395
  if (this.isSummarizerClient) {
340
396
  this.mc.logger.sendTelemetryEvent({
341
397
  eventName: "GarbageCollectorLoaded",
@@ -406,6 +462,9 @@ export class GarbageCollector {
406
462
  const currentReferenceTimestampMs = this.runtime.getCurrentReferenceTimestampMs();
407
463
  this.updateCurrentState(gcData, gcResult, currentReferenceTimestampMs);
408
464
  this.runtime.updateUsedRoutes(gcResult.referencedNodeIds, currentReferenceTimestampMs);
465
+ // Log events for objects that are ready to be deleted by sweep. When we have sweep enabled, we will
466
+ // delete these objects here instead.
467
+ this.logSweepEvents(logger, currentReferenceTimestampMs);
409
468
  // If we are running in GC test mode, delete objects for unused routes. This enables testing scenarios
410
469
  // involving access to deleted data.
411
470
  if (this.testMode) {
@@ -414,7 +473,7 @@ export class GarbageCollector {
414
473
  // Log pending unreferenced events such as a node being used after inactive. This is done after GC runs and
415
474
  // updates its state so that we don't send false positives based on intermediate state. For example, we may get
416
475
  // reference to an unreferenced node from another unreferenced node which means the node wasn't revived.
417
- await this.logPendingEvents(logger);
476
+ await this.logUnreferencedEvents(logger);
418
477
  return gcStats;
419
478
  }
420
479
  /**
@@ -528,9 +587,9 @@ export class GarbageCollector {
528
587
  if (!this.shouldRunGC) {
529
588
  return;
530
589
  }
531
- const unreferencedState = this.unreferencedNodesState.get(nodePath);
532
- if (unreferencedState === null || unreferencedState === void 0 ? void 0 : unreferencedState.inactive) {
533
- this.inactiveNodeUsed(reason, nodePath, unreferencedState, undefined /* fromNodeId */, packagePath, timestampMs, requestHeaders);
590
+ const nodeStateTracker = this.unreferencedNodesState.get(nodePath);
591
+ if (nodeStateTracker && nodeStateTracker.state !== UnreferencedState.Active) {
592
+ this.inactiveNodeUsed(reason, nodePath, nodeStateTracker, undefined /* fromNodeId */, packagePath, timestampMs, requestHeaders);
534
593
  }
535
594
  }
536
595
  /**
@@ -548,9 +607,9 @@ export class GarbageCollector {
548
607
  const outboundRoutes = (_a = this.newReferencesSinceLastRun.get(fromNodePath)) !== null && _a !== void 0 ? _a : [];
549
608
  outboundRoutes.push(toNodePath);
550
609
  this.newReferencesSinceLastRun.set(fromNodePath, outboundRoutes);
551
- const unreferencedState = this.unreferencedNodesState.get(toNodePath);
552
- if (unreferencedState === null || unreferencedState === void 0 ? void 0 : unreferencedState.inactive) {
553
- this.inactiveNodeUsed("Revived", toNodePath, unreferencedState, fromNodePath);
610
+ const nodeStateTracker = this.unreferencedNodesState.get(toNodePath);
611
+ if (nodeStateTracker && nodeStateTracker.state !== UnreferencedState.Active) {
612
+ this.inactiveNodeUsed("Revived", toNodePath, nodeStateTracker, fromNodePath);
554
613
  }
555
614
  }
556
615
  dispose() {
@@ -597,7 +656,7 @@ export class GarbageCollector {
597
656
  for (const nodeId of gcResult.deletedNodeIds) {
598
657
  const nodeStateTracker = this.unreferencedNodesState.get(nodeId);
599
658
  if (nodeStateTracker === undefined) {
600
- this.unreferencedNodesState.set(nodeId, new UnreferencedStateTracker(currentReferenceTimestampMs, this.inactiveTimeoutMs, currentReferenceTimestampMs));
659
+ this.unreferencedNodesState.set(nodeId, new UnreferencedStateTracker(currentReferenceTimestampMs, this.inactiveTimeoutMs, this.sweepTimeoutMs, currentReferenceTimestampMs));
601
660
  }
602
661
  else {
603
662
  nodeStateTracker.updateTracking(currentReferenceTimestampMs);
@@ -771,12 +830,48 @@ export class GarbageCollector {
771
830
  return gcStats;
772
831
  }
773
832
  /**
774
- * Called when a node is used. If the node is inactive, queue up an event that will be logged next time GC runs.
833
+ * For nodes that are ready to sweep, log an event for now. Until we start running sweep which deletes objects,
834
+ * this will give us a view into how much deleted content a container has.
835
+ */
836
+ logSweepEvents(logger, currentReferenceTimestampMs) {
837
+ if (this.mc.config.getBoolean(disableSweepLogKey) === true
838
+ || currentReferenceTimestampMs === undefined
839
+ || this.sweepTimeoutMs === undefined) {
840
+ return;
841
+ }
842
+ this.unreferencedNodesState.forEach((nodeStateTracker, nodeId) => {
843
+ if (nodeStateTracker.state !== UnreferencedState.SweepReady) {
844
+ return;
845
+ }
846
+ const nodeType = this.runtime.getNodeType(nodeId);
847
+ if (nodeType !== GCNodeType.DataStore && nodeType !== GCNodeType.Blob) {
848
+ return;
849
+ }
850
+ // Log deleted event for each node only once to reduce noise in telemetry.
851
+ const uniqueEventId = `Deleted-${nodeId}`;
852
+ if (this.loggedUnreferencedEvents.has(uniqueEventId)) {
853
+ return;
854
+ }
855
+ this.loggedUnreferencedEvents.add(uniqueEventId);
856
+ logger.sendTelemetryEvent({
857
+ eventName: "GCObjectDeleted",
858
+ id: nodeId,
859
+ type: nodeType,
860
+ age: currentReferenceTimestampMs - nodeStateTracker.unreferencedTimestampMs,
861
+ timeout: this.sweepTimeoutMs,
862
+ completedGCRuns: this.completedRuns,
863
+ lastSummaryTime: this.getLastSummaryTimestampMs(),
864
+ });
865
+ });
866
+ }
867
+ /**
868
+ * Called when an inactive node is used after. Queue up an event that will be logged next time GC runs.
775
869
  */
776
- inactiveNodeUsed(usageType, nodeId, unreferencedState, fromNodeId, packagePath, currentReferenceTimestampMs = this.runtime.getCurrentReferenceTimestampMs(), requestHeaders) {
870
+ inactiveNodeUsed(usageType, nodeId, nodeStateTracker, fromNodeId, packagePath, currentReferenceTimestampMs = this.runtime.getCurrentReferenceTimestampMs(), requestHeaders) {
777
871
  // If there is no reference timestamp to work with, no ops have been processed after creation. If so, skip
778
872
  // logging as nothing interesting would have happened worth logging.
779
- if (currentReferenceTimestampMs === undefined) {
873
+ // If the node is active, skip logging.
874
+ if (currentReferenceTimestampMs === undefined || nodeStateTracker.state === UnreferencedState.Active) {
780
875
  return;
781
876
  }
782
877
  // For non-summarizer clients, only log "Loaded" type events since these objects may not be loaded in the
@@ -790,18 +885,20 @@ export class GarbageCollector {
790
885
  if (nodeType !== GCNodeType.DataStore && nodeType !== GCNodeType.Blob) {
791
886
  return;
792
887
  }
793
- // A particular event is logged for a given node only once so that it is not too noisy.
794
- const uniqueEventId = `${nodeId}-${usageType}`;
888
+ const state = nodeStateTracker.state;
889
+ const uniqueEventId = `${state}-${nodeId}-${usageType}`;
795
890
  if (this.loggedUnreferencedEvents.has(uniqueEventId)) {
796
891
  return;
797
892
  }
798
893
  this.loggedUnreferencedEvents.add(uniqueEventId);
799
- const event = {
800
- usageType,
894
+ const propsToLog = {
801
895
  id: nodeId,
802
896
  type: nodeType,
803
- age: currentReferenceTimestampMs - unreferencedState.unreferencedTimestampMs,
804
- timeout: this.inactiveTimeoutMs,
897
+ unrefTime: nodeStateTracker.unreferencedTimestampMs,
898
+ age: currentReferenceTimestampMs - nodeStateTracker.unreferencedTimestampMs,
899
+ timeout: nodeStateTracker.state === UnreferencedState.Inactive
900
+ ? this.inactiveTimeoutMs
901
+ : this.sweepTimeoutMs,
805
902
  completedGCRuns: this.completedRuns,
806
903
  lastSummaryTime: this.getLastSummaryTimestampMs(),
807
904
  externalRequest: requestHeaders === null || requestHeaders === void 0 ? void 0 : requestHeaders[RuntimeHeaders.externalRequest],
@@ -812,31 +909,28 @@ export class GarbageCollector {
812
909
  // For non-summarizer client, log the event now since GC won't run on it. This may result in false positives
813
910
  // but it's a good signal nonetheless and we can consume it with a grain of salt.
814
911
  if (this.isSummarizerClient) {
815
- this.pendingEventsQueue.push(event);
912
+ this.pendingEventsQueue.push(Object.assign(Object.assign({}, propsToLog), { usageType, state }));
816
913
  }
817
914
  else {
818
- this.mc.logger.sendErrorEvent(Object.assign(Object.assign({}, event), { eventName: `inactiveObject-${usageType}`, pkg: packagePath ? { value: packagePath.join("/"), tag: TelemetryDataTag.PackageData } : undefined }));
915
+ this.mc.logger.sendErrorEvent(Object.assign(Object.assign({}, propsToLog), { eventName: `${state}Object_${usageType}`, pkg: packagePath ? { value: packagePath.join("/"), tag: TelemetryDataTag.CodeArtifact } : undefined }));
819
916
  }
820
917
  }
821
- /**
822
- * Logs pending unreferenced events if they are still valid.
823
- */
824
- async logPendingEvents(logger) {
825
- for (const event of this.pendingEventsQueue) {
826
- const unreferencedState = this.unreferencedNodesState.get(event.id);
827
- // Only log revived event if the node is not inactive anymore. If the node is still inactive, the
828
- // reference was from another unreferenced node and we don't care about logging this scenario.
829
- if (event.usageType === "Revived" && (unreferencedState === null || unreferencedState === void 0 ? void 0 : unreferencedState.inactive)) {
830
- continue;
831
- }
832
- // Only log loaded and changed event if the node is still inactive. If the node is not inactive, it was
833
- // revived and a revived event will be logged for it.
834
- if (event.usageType !== "Revived" && !(unreferencedState === null || unreferencedState === void 0 ? void 0 : unreferencedState.inactive)) {
835
- continue;
918
+ async logUnreferencedEvents(logger) {
919
+ for (const eventProps of this.pendingEventsQueue) {
920
+ const { usageType, state } = eventProps, propsToLog = __rest(eventProps, ["usageType", "state"]);
921
+ /**
922
+ * Revived event is logged only if the node is active. If the node is not active, the reference to it was
923
+ * from another unreferenced node and this scenario is not interesting to log.
924
+ * Loaded and Changed events are logged only if the node is not active. If the node is active, it was
925
+ * revived and a Revived event will be logged for it.
926
+ */
927
+ const nodeStateTracker = this.unreferencedNodesState.get(eventProps.id);
928
+ const active = nodeStateTracker === undefined || nodeStateTracker.state === UnreferencedState.Active;
929
+ if ((usageType === "Revived") === active) {
930
+ const pkg = await this.getNodePackagePath(eventProps.id);
931
+ const fromPkg = eventProps.fromId ? await this.getNodePackagePath(eventProps.fromId) : undefined;
932
+ logger.sendErrorEvent(Object.assign(Object.assign({}, propsToLog), { eventName: `${state}Object_${usageType}`, pkg: pkg ? { value: pkg.join("/"), tag: TelemetryDataTag.CodeArtifact } : undefined, fromPkg: fromPkg ? { value: fromPkg.join("/"), tag: TelemetryDataTag.CodeArtifact } : undefined }));
836
933
  }
837
- const pkg = await this.getNodePackagePath(event.id);
838
- const fromPkg = event.fromId ? await this.getNodePackagePath(event.fromId) : undefined;
839
- logger.sendErrorEvent(Object.assign(Object.assign({}, event), { eventName: `inactiveObject_${event.usageType}`, pkg: pkg ? { value: pkg.join("/"), tag: TelemetryDataTag.PackageData } : undefined, fromPkg: fromPkg ? { value: fromPkg.join("/"), tag: TelemetryDataTag.PackageData } : undefined }));
840
934
  }
841
935
  this.pendingEventsQueue = [];
842
936
  }