@fluidframework/container-runtime 0.59.2001 → 0.59.3000

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.
Files changed (143) hide show
  1. package/.eslintrc.js +0 -1
  2. package/dist/batchTracker.js +1 -1
  3. package/dist/batchTracker.js.map +1 -1
  4. package/dist/blobManager.d.ts +8 -1
  5. package/dist/blobManager.d.ts.map +1 -1
  6. package/dist/blobManager.js +27 -17
  7. package/dist/blobManager.js.map +1 -1
  8. package/dist/connectionTelemetry.js +8 -8
  9. package/dist/connectionTelemetry.js.map +1 -1
  10. package/dist/containerHandleContext.js +1 -1
  11. package/dist/containerHandleContext.js.map +1 -1
  12. package/dist/containerRuntime.d.ts +27 -17
  13. package/dist/containerRuntime.d.ts.map +1 -1
  14. package/dist/containerRuntime.js +152 -176
  15. package/dist/containerRuntime.js.map +1 -1
  16. package/dist/dataStore.js +1 -1
  17. package/dist/dataStore.js.map +1 -1
  18. package/dist/dataStoreContext.d.ts.map +1 -1
  19. package/dist/dataStoreContext.js +44 -44
  20. package/dist/dataStoreContext.js.map +1 -1
  21. package/dist/dataStoreContexts.d.ts +2 -2
  22. package/dist/dataStoreContexts.d.ts.map +1 -1
  23. package/dist/dataStoreContexts.js +8 -8
  24. package/dist/dataStoreContexts.js.map +1 -1
  25. package/dist/dataStores.d.ts +6 -4
  26. package/dist/dataStores.d.ts.map +1 -1
  27. package/dist/dataStores.js +50 -37
  28. package/dist/dataStores.js.map +1 -1
  29. package/dist/garbageCollection.d.ts +23 -23
  30. package/dist/garbageCollection.d.ts.map +1 -1
  31. package/dist/garbageCollection.js +81 -50
  32. package/dist/garbageCollection.js.map +1 -1
  33. package/dist/opTelemetry.js +1 -1
  34. package/dist/opTelemetry.js.map +1 -1
  35. package/dist/orderedClientElection.d.ts.map +1 -1
  36. package/dist/orderedClientElection.js +2 -2
  37. package/dist/orderedClientElection.js.map +1 -1
  38. package/dist/packageVersion.d.ts +1 -1
  39. package/dist/packageVersion.js +1 -1
  40. package/dist/packageVersion.js.map +1 -1
  41. package/dist/pendingStateManager.js +17 -17
  42. package/dist/pendingStateManager.js.map +1 -1
  43. package/dist/runWhileConnectedCoordinator.js +1 -1
  44. package/dist/runWhileConnectedCoordinator.js.map +1 -1
  45. package/dist/runningSummarizer.d.ts.map +1 -1
  46. package/dist/runningSummarizer.js +7 -6
  47. package/dist/runningSummarizer.js.map +1 -1
  48. package/dist/summarizer.d.ts.map +1 -1
  49. package/dist/summarizer.js +4 -3
  50. package/dist/summarizer.js.map +1 -1
  51. package/dist/summarizerClientElection.js.map +1 -1
  52. package/dist/summarizerHeuristics.d.ts +1 -1
  53. package/dist/summarizerHeuristics.d.ts.map +1 -1
  54. package/dist/summarizerHeuristics.js +1 -1
  55. package/dist/summarizerHeuristics.js.map +1 -1
  56. package/dist/summarizerTypes.d.ts +4 -2
  57. package/dist/summarizerTypes.d.ts.map +1 -1
  58. package/dist/summarizerTypes.js.map +1 -1
  59. package/dist/summaryCollection.js +2 -2
  60. package/dist/summaryCollection.js.map +1 -1
  61. package/dist/summaryFormat.d.ts +37 -11
  62. package/dist/summaryFormat.d.ts.map +1 -1
  63. package/dist/summaryFormat.js +12 -4
  64. package/dist/summaryFormat.js.map +1 -1
  65. package/dist/summaryGenerator.d.ts.map +1 -1
  66. package/dist/summaryGenerator.js +6 -4
  67. package/dist/summaryGenerator.js.map +1 -1
  68. package/dist/summaryManager.js +5 -5
  69. package/dist/summaryManager.js.map +1 -1
  70. package/dist/throttler.js +2 -2
  71. package/dist/throttler.js.map +1 -1
  72. package/lib/blobManager.d.ts +8 -1
  73. package/lib/blobManager.d.ts.map +1 -1
  74. package/lib/blobManager.js +19 -9
  75. package/lib/blobManager.js.map +1 -1
  76. package/lib/containerRuntime.d.ts +27 -17
  77. package/lib/containerRuntime.d.ts.map +1 -1
  78. package/lib/containerRuntime.js +71 -95
  79. package/lib/containerRuntime.js.map +1 -1
  80. package/lib/dataStore.js.map +1 -1
  81. package/lib/dataStoreContext.d.ts.map +1 -1
  82. package/lib/dataStoreContext.js.map +1 -1
  83. package/lib/dataStoreContexts.d.ts +2 -2
  84. package/lib/dataStoreContexts.d.ts.map +1 -1
  85. package/lib/dataStoreContexts.js +2 -2
  86. package/lib/dataStoreContexts.js.map +1 -1
  87. package/lib/dataStores.d.ts +6 -4
  88. package/lib/dataStores.d.ts.map +1 -1
  89. package/lib/dataStores.js +27 -14
  90. package/lib/dataStores.js.map +1 -1
  91. package/lib/garbageCollection.d.ts +23 -23
  92. package/lib/garbageCollection.d.ts.map +1 -1
  93. package/lib/garbageCollection.js +68 -37
  94. package/lib/garbageCollection.js.map +1 -1
  95. package/lib/opTelemetry.js.map +1 -1
  96. package/lib/orderedClientElection.d.ts.map +1 -1
  97. package/lib/orderedClientElection.js.map +1 -1
  98. package/lib/packageVersion.d.ts +1 -1
  99. package/lib/packageVersion.js +1 -1
  100. package/lib/packageVersion.js.map +1 -1
  101. package/lib/pendingStateManager.js.map +1 -1
  102. package/lib/runningSummarizer.d.ts.map +1 -1
  103. package/lib/runningSummarizer.js +4 -3
  104. package/lib/runningSummarizer.js.map +1 -1
  105. package/lib/summarizer.d.ts.map +1 -1
  106. package/lib/summarizer.js +1 -0
  107. package/lib/summarizer.js.map +1 -1
  108. package/lib/summarizerClientElection.js.map +1 -1
  109. package/lib/summarizerHeuristics.d.ts +1 -1
  110. package/lib/summarizerHeuristics.d.ts.map +1 -1
  111. package/lib/summarizerHeuristics.js +1 -1
  112. package/lib/summarizerHeuristics.js.map +1 -1
  113. package/lib/summarizerTypes.d.ts +4 -2
  114. package/lib/summarizerTypes.d.ts.map +1 -1
  115. package/lib/summarizerTypes.js.map +1 -1
  116. package/lib/summaryCollection.js.map +1 -1
  117. package/lib/summaryFormat.d.ts +37 -11
  118. package/lib/summaryFormat.d.ts.map +1 -1
  119. package/lib/summaryFormat.js +10 -2
  120. package/lib/summaryFormat.js.map +1 -1
  121. package/lib/summaryGenerator.d.ts.map +1 -1
  122. package/lib/summaryGenerator.js +2 -0
  123. package/lib/summaryGenerator.js.map +1 -1
  124. package/lib/summaryManager.js.map +1 -1
  125. package/lib/throttler.js.map +1 -1
  126. package/package.json +26 -20
  127. package/src/blobManager.ts +23 -11
  128. package/src/containerRuntime.ts +111 -139
  129. package/src/dataStoreContext.ts +8 -11
  130. package/src/dataStoreContexts.ts +5 -5
  131. package/src/dataStores.ts +35 -17
  132. package/src/garbageCollection.ts +100 -57
  133. package/src/orderedClientElection.ts +5 -10
  134. package/src/packageVersion.ts +1 -1
  135. package/src/pendingStateManager.ts +2 -2
  136. package/src/runningSummarizer.ts +8 -9
  137. package/src/summarizer.ts +2 -2
  138. package/src/summarizerHeuristics.ts +1 -1
  139. package/src/summarizerTypes.ts +8 -6
  140. package/src/summaryFormat.ts +38 -11
  141. package/src/summaryGenerator.ts +7 -5
  142. package/src/summaryManager.ts +2 -2
  143. package/src/throttler.ts +1 -1
@@ -3,7 +3,7 @@
3
3
  * Licensed under the MIT License.
4
4
  */
5
5
  import { assert, LazyPromise, Timer } from "@fluidframework/common-utils";
6
- import { ClientSessionExpiredError, DataProcessingError } from "@fluidframework/container-utils";
6
+ import { ClientSessionExpiredError, DataProcessingError, UsageError } from "@fluidframework/container-utils";
7
7
  import { cloneGCData, concatGarbageCollectionStates, concatGarbageCollectionData, runGarbageCollection, unpackChildNodesGCDetails, } from "@fluidframework/garbage-collector";
8
8
  import { gcBlobKey, } from "@fluidframework/runtime-definitions";
9
9
  import { SummaryTreeBuilder, } from "@fluidframework/runtime-utils";
@@ -26,7 +26,9 @@ const runSweepKey = "Fluid.GarbageCollection.RunSweep";
26
26
  // Feature gate key to write GC data at the root of the summary tree.
27
27
  const writeAtRootKey = "Fluid.GarbageCollection.WriteDataAtRoot";
28
28
  // Feature gate key to expire a session after a set period of time.
29
- const runSessionExpiry = "Fluid.GarbageCollection.RunSessionExpiry";
29
+ const runSessionExpiryKey = "Fluid.GarbageCollection.RunSessionExpiry";
30
+ // Feature gate key to disable expiring session after a set period of time, even if expiry value is present
31
+ const disableSessionExpiryKey = "Fluid.GarbageCollection.DisableSessionExpiry";
30
32
  // Feature gate key to log error messages if GC reference validation fails.
31
33
  const logUnknownOutboundReferencesKey = "Fluid.GarbageCollection.LogUnknownOutboundReferences";
32
34
  const defaultDeleteTimeoutMs = 7 * 24 * 60 * 60 * 1000; // 7 days
@@ -35,9 +37,11 @@ export const defaultSessionExpiryDurationMs = 30 * 24 * 60 * 60 * 1000; // 30 da
35
37
  export const GCNodeType = {
36
38
  // Nodes that are for data stores.
37
39
  DataStore: "DataStore",
40
+ // Nodes that are within a data store. For example, DDS nodes.
41
+ SubDataStore: "SubDataStore",
38
42
  // Nodes that are for attachment blobs, i.e., blobs uploaded via BlobManager.
39
43
  Blob: "Blob",
40
- // Nodes that are neither data store not blobs. For example, root node and DDS nodes.
44
+ // Nodes that are neither of the above. For example, root node.
41
45
  Other: "Other",
42
46
  };
43
47
  /**
@@ -91,15 +95,15 @@ class UnreferencedStateTracker {
91
95
  * its state across summaries.
92
96
  *
93
97
  * Node - represented as nodeId, it's a node on the GC graph
94
- * Outbound Route - a path from one node to another node, think `nodeA` -> `nodeB`
98
+ * Outbound Route - a path from one node to another node, think `nodeA` -\> `nodeB`
95
99
  * Graph - all nodes with their respective routes
96
100
  * GC Graph
97
101
  *
98
102
  * Node
99
103
  * NodeId = "datastore1"
100
- * / \
104
+ * / \\
101
105
  * OutboundRoute OutboundRoute
102
- * / \
106
+ * / \\
103
107
  * Node Node
104
108
  * NodeId = "dds1" NodeId = "dds2"
105
109
  */
@@ -109,7 +113,7 @@ export class GarbageCollector {
109
113
  getNodePackagePath,
110
114
  /** Returns the timestamp of the last summary generated for this container. */
111
115
  getLastSummaryTimestampMs, baseSnapshot, readAndParseBlob, baseLogger, existing, metadata) {
112
- var _a, _b, _c, _d, _e;
116
+ var _a, _b, _c, _d, _e, _f;
113
117
  this.runtime = runtime;
114
118
  this.gcOptions = gcOptions;
115
119
  this.getNodePackagePath = getNodePackagePath;
@@ -144,26 +148,43 @@ export class GarbageCollector {
144
148
  this.mc = loggerToMonitoringContext(ChildLogger.create(baseLogger, "GarbageCollector"));
145
149
  this.deleteTimeoutMs = (_a = this.gcOptions.deleteTimeoutMs) !== null && _a !== void 0 ? _a : defaultDeleteTimeoutMs;
146
150
  let prevSummaryGCVersion;
147
- // GC can only be enabled during creation. After that, it can never be enabled again. So, for existing
148
- // documents, we get this information from the metadata blob. Similarly the session timeout should be
149
- // consistent across all clients, thus we grab it as well from the metadata blob, and set it once on creation.
151
+ /**
152
+ * The following GC state is enabled during container creation and cannot be changed throughout its lifetime:
153
+ * 1. Whether running GC mark phase is allowed or not.
154
+ * 2. Whether running GC sweep phase is allowed or not.
155
+ * 3. Whether GC session expiry is enabled or not.
156
+ * For existing containers, we get this information from the metadata blob of its summary.
157
+ */
150
158
  if (existing) {
151
159
  prevSummaryGCVersion = getGCVersion(metadata);
152
160
  // Existing documents which did not have metadata blob or had GC disabled have version as 0. For all
153
161
  // other existing documents, GC is enabled.
154
162
  this.gcEnabled = prevSummaryGCVersion > 0;
163
+ this.sweepEnabled = (_b = metadata === null || metadata === void 0 ? void 0 : metadata.sweepEnabled) !== null && _b !== void 0 ? _b : false;
155
164
  this.sessionExpiryTimeoutMs = metadata === null || metadata === void 0 ? void 0 : metadata.sessionExpiryTimeoutMs;
156
165
  }
157
166
  else {
158
- // For new documents, GC has to be explicitly enabled via the gcAllowed flag in GC options.
167
+ // Sweep should not be enabled without enabling GC mark phase. We could silently disable sweep in this
168
+ // scenario but explicitly failing makes it clearer and promotes correct usage.
169
+ if (gcOptions.sweepAllowed && !gcOptions.gcAllowed) {
170
+ throw new UsageError("GC sweep phase cannot be enabled without enabling GC mark phase");
171
+ }
172
+ // For new documents, GC has to be explicitly enabled via the flags in GC options.
159
173
  this.gcEnabled = gcOptions.gcAllowed === true;
174
+ this.sweepEnabled = gcOptions.sweepAllowed === true;
160
175
  // Set the Session Expiry only if the flag is enabled or the test option is set.
161
- if (this.mc.config.getBoolean(runSessionExpiry) && this.gcEnabled) {
176
+ if (this.mc.config.getBoolean(runSessionExpiryKey) && this.gcEnabled) {
162
177
  this.sessionExpiryTimeoutMs = defaultSessionExpiryDurationMs;
163
178
  }
164
179
  }
165
180
  // If session expiry is enabled, we need to close the container when the timeout expires
166
- if (this.sessionExpiryTimeoutMs !== undefined) {
181
+ if (this.sessionExpiryTimeoutMs !== undefined
182
+ && this.mc.config.getBoolean(disableSessionExpiryKey) !== true) {
183
+ // If Test Override config is set, override Session Expiry timeout
184
+ const overrideSessionExpiryTimeoutMs = this.mc.config.getNumber("Fluid.GarbageCollection.TestOverride.SessionExpiryMs");
185
+ if (overrideSessionExpiryTimeoutMs !== undefined) {
186
+ this.sessionExpiryTimeoutMs = overrideSessionExpiryTimeoutMs;
187
+ }
167
188
  const timeoutMs = this.sessionExpiryTimeoutMs;
168
189
  setLongTimeout(timeoutMs, () => {
169
190
  this.runtime.closeFn(new ClientSessionExpiredError(`Client session expired.`, timeoutMs));
@@ -174,19 +195,26 @@ export class GarbageCollector {
174
195
  // For existing document, the latest summary is the one that we loaded from. So, use its GC version as the
175
196
  // latest tracked GC version. For new documents, we will be writing the first summary with the current version.
176
197
  this.latestSummaryGCVersion = prevSummaryGCVersion !== null && prevSummaryGCVersion !== void 0 ? prevSummaryGCVersion : this.currentGCVersion;
177
- // Whether GC should run or not. Can override with localStorage flag.
178
- this.shouldRunGC = (_b = this.mc.config.getBoolean(runGCKey)) !== null && _b !== void 0 ? _b : (
198
+ /**
199
+ * Whether GC should run or not. The following conditions have to be met to run sweep:
200
+ * 1. GC should be enabled for this container.
201
+ * 2. GC should not be disabled via disableGC GC option.
202
+ * These conditions can be overridden via runGCKey feature flag.
203
+ */
204
+ this.shouldRunGC = (_c = this.mc.config.getBoolean(runGCKey)) !== null && _c !== void 0 ? _c : (
179
205
  // GC must be enabled for the document.
180
206
  this.gcEnabled
181
207
  // GC must not be disabled via GC options.
182
208
  && !gcOptions.disableGC);
183
- // Whether GC sweep phase should run or not. If this is false, only GC mark phase is run. Can override with
184
- // localStorage flag.
185
- this.shouldRunSweep = this.shouldRunGC &&
186
- ((_c = this.mc.config.getBoolean(runSweepKey)) !== null && _c !== void 0 ? _c : gcOptions.runSweep === true)
187
- && this.sessionExpiryTimer !== undefined;
209
+ /**
210
+ * Whether sweep should run or not. The following conditions have to be met to run sweep:
211
+ * 1. Overall GC or mark phase must be enabled (this.shouldRunGC).
212
+ * 2. Session expiry and sweep should be enabled for this container. Without session expiry we cannot safely
213
+ * delete unreferenced objects. This condition (#2) can be overridden via runSweepKey feature flag.
214
+ */
215
+ this.shouldRunSweep = this.shouldRunGC && ((_d = this.mc.config.getBoolean(runSweepKey)) !== null && _d !== void 0 ? _d : (this.sessionExpiryTimeoutMs !== undefined && this.sweepEnabled));
188
216
  // Whether we are running in test mode. In this mode, unreferenced nodes are immediately deleted.
189
- this.testMode = (_d = this.mc.config.getBoolean(gcTestModeKey)) !== null && _d !== void 0 ? _d : gcOptions.runGCInTestMode === true;
217
+ this.testMode = (_e = this.mc.config.getBoolean(gcTestModeKey)) !== null && _e !== void 0 ? _e : gcOptions.runGCInTestMode === true;
190
218
  /**
191
219
  * Enable resetting initial state once the following issue is resolved:
192
220
  * https://github.com/microsoft/FluidFramework/issues/8878.
@@ -198,7 +226,7 @@ export class GarbageCollector {
198
226
  // this.initialStateNeedsReset = gcTreePresent ? !this.shouldRunGC : this.shouldRunGC;
199
227
  // If `writeDataAtRoot` setting is true, write the GC data into the root of the summary tree. We do this so that
200
228
  // the roll out can be staged. Once its rolled out everywhere, we will start writing at root by default.
201
- this._writeDataAtRoot = (_e = this.mc.config.getBoolean(writeAtRootKey)) !== null && _e !== void 0 ? _e : this.gcOptions.writeDataAtRoot === true;
229
+ this._writeDataAtRoot = (_f = this.mc.config.getBoolean(writeAtRootKey)) !== null && _f !== void 0 ? _f : this.gcOptions.writeDataAtRoot === true;
202
230
  // Get the GC state from the GC blob in the base snapshot. Use LazyPromise because we only want to do
203
231
  // this once since it involves fetching blobs from storage which is expensive.
204
232
  const baseSummaryStateP = new LazyPromise(async () => {
@@ -316,14 +344,6 @@ export class GarbageCollector {
316
344
  static create(provider, gcOptions, getNodePackagePath, getLastSummaryTimestampMs, baseSnapshot, readAndParseBlob, baseLogger, existing, metadata) {
317
345
  return new GarbageCollector(provider, gcOptions, getNodePackagePath, getLastSummaryTimestampMs, baseSnapshot, readAndParseBlob, baseLogger, existing, metadata);
318
346
  }
319
- /**
320
- * This tracks two things:
321
- * 1. Whether GC is enabled - If this is 0, GC is disabled. If this is greater than 0, GC is enabled.
322
- * 2. If GC is enabled, the version of GC used to generate the GC data written in a summary.
323
- */
324
- get gcSummaryFeatureVersion() {
325
- return this.gcEnabled ? this.currentGCVersion : 0;
326
- }
327
347
  /**
328
348
  * Tells whether the GC state needs to be reset in the next summary. We need to do this if:
329
349
  * 1. GC was enabled and is now disabled. The GC state needs to be removed and everything becomes referenced.
@@ -352,10 +372,10 @@ export class GarbageCollector {
352
372
  // Get the runtime's GC data and run GC on the reference graph in it.
353
373
  const gcData = await this.runtime.getGCData(fullGC);
354
374
  const gcResult = runGarbageCollection(gcData.gcNodes, ["/"], logger);
355
- const gcStats = this.generateStatsAndLogEvents(gcResult);
375
+ const gcStats = this.generateStatsAndLogEvents(gcResult, logger);
356
376
  // Update the state since the last GC run. There can be nodes that were referenced between the last and
357
377
  // the current run. We need to identify than and update their unreferenced state if needed.
358
- this.updateStateSinceLastRun(gcData);
378
+ this.updateStateSinceLastRun(gcData, logger);
359
379
  // Update the current state of the system based on the GC run.
360
380
  const currentReferenceTimestampMs = this.runtime.getCurrentReferenceTimestampMs();
361
381
  this.updateCurrentState(gcData, gcResult, currentReferenceTimestampMs);
@@ -393,6 +413,17 @@ export class GarbageCollector {
393
413
  builder.addBlob(`${gcBlobPrefix}_root`, JSON.stringify(gcState));
394
414
  return builder.getSummaryTree();
395
415
  }
416
+ getMetadata() {
417
+ return {
418
+ /**
419
+ * If GC is enabled, the GC data is written using the current GC version and that is the gcFeature that goes
420
+ * into the metadata blob. If GC is disabled, the gcFeature is 0.
421
+ */
422
+ gcFeature: this.gcEnabled ? this.currentGCVersion : 0,
423
+ sessionExpiryTimeoutMs: this.sessionExpiryTimeoutMs,
424
+ sweepEnabled: this.sweepEnabled,
425
+ };
426
+ }
396
427
  /**
397
428
  * Returns a map of node ids to their base GC details generated from the base summary. This is used by the caller
398
429
  * to initialize the GC state of the nodes.
@@ -522,7 +553,7 @@ export class GarbageCollector {
522
553
  * This function identifies nodes that were referenced since last run and removes their unreferenced state, if any.
523
554
  * If these nodes are currently unreferenced, they will be assigned new unreferenced state by the current run.
524
555
  */
525
- updateStateSinceLastRun(currentGCData) {
556
+ updateStateSinceLastRun(currentGCData, logger) {
526
557
  // If we haven't run GC before there is nothing to do.
527
558
  if (this.previousGCDataFromLastRun === undefined) {
528
559
  return;
@@ -539,7 +570,7 @@ export class GarbageCollector {
539
570
  gcNodeId: missingExplicitReference[0],
540
571
  gcRoutes: JSON.stringify(missingExplicitReference[1]),
541
572
  };
542
- this.mc.logger.sendPerformanceEvent(event);
573
+ logger.sendPerformanceEvent(event);
543
574
  });
544
575
  }
545
576
  // No references were added since the last run so we don't have to update reference states of any unreferenced
@@ -576,7 +607,7 @@ export class GarbageCollector {
576
607
  * unreferenced, stop tracking them and remove from unreferenced list.
577
608
  * Some of these nodes may be unreferenced now and if so, the current run will add unreferenced state for them.
578
609
  */
579
- const gcResult = runGarbageCollection(gcDataSuperSet.gcNodes, ["/"], this.mc.logger);
610
+ const gcResult = runGarbageCollection(gcDataSuperSet.gcNodes, ["/"], logger);
580
611
  for (const nodeId of gcResult.referencedNodeIds) {
581
612
  const nodeStateTracker = this.unreferencedNodesState.get(nodeId);
582
613
  if (nodeStateTracker !== undefined) {
@@ -634,13 +665,13 @@ export class GarbageCollector {
634
665
  * @param gcResult - The result of a GC run.
635
666
  * @returns the GC stats of the GC run.
636
667
  */
637
- generateStatsAndLogEvents(gcResult) {
668
+ generateStatsAndLogEvents(gcResult, logger) {
638
669
  // Log pending events for unreferenced nodes after GC has run. We should have the package data available for
639
670
  // them now since the GC run should have loaded these nodes.
640
671
  let event = this.pendingEventsQueue.shift();
641
672
  while (event !== undefined) {
642
673
  const pkg = this.getNodePackagePath(event.id);
643
- this.mc.logger.sendErrorEvent(Object.assign(Object.assign({}, event), { pkg: pkg ? { value: `/${pkg.join("/")}`, tag: TelemetryDataTag.PackageData } : undefined }));
674
+ logger.sendErrorEvent(Object.assign(Object.assign({}, event), { pkg: pkg ? { value: `/${pkg.join("/")}`, tag: TelemetryDataTag.PackageData } : undefined }));
644
675
  event = this.pendingEventsQueue.shift();
645
676
  }
646
677
  const gcStats = {
@@ -1 +1 @@
1
- {"version":3,"file":"garbageCollection.js","sourceRoot":"","sources":["../src/garbageCollection.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 { ITelemetryLogger, ITelemetryPerformanceEvent } from \"@fluidframework/common-definitions\";\nimport { assert, LazyPromise, Timer } from \"@fluidframework/common-utils\";\nimport { ICriticalContainerError } from \"@fluidframework/container-definitions\";\nimport { ClientSessionExpiredError, DataProcessingError } from \"@fluidframework/container-utils\";\nimport { IRequestHeader } from \"@fluidframework/core-interfaces\";\nimport {\n cloneGCData,\n concatGarbageCollectionStates,\n concatGarbageCollectionData,\n IGCResult,\n runGarbageCollection,\n unpackChildNodesGCDetails,\n} from \"@fluidframework/garbage-collector\";\nimport { ISnapshotTree } from \"@fluidframework/protocol-definitions\";\nimport {\n gcBlobKey,\n IGarbageCollectionData,\n IGarbageCollectionState,\n IGarbageCollectionDetailsBase,\n ISummaryTreeWithStats,\n} from \"@fluidframework/runtime-definitions\";\nimport {\n ReadAndParseBlob,\n RefreshSummaryResult,\n SummaryTreeBuilder,\n} from \"@fluidframework/runtime-utils\";\nimport {\n ChildLogger,\n loggerToMonitoringContext,\n MonitoringContext,\n PerformanceEvent,\n TelemetryDataTag,\n} from \"@fluidframework/telemetry-utils\";\n\nimport { IGCRuntimeOptions, RuntimeHeaders } from \"./containerRuntime\";\nimport { getSummaryForDatastores } from \"./dataStores\";\nimport {\n getGCVersion,\n GCVersion,\n IContainerRuntimeMetadata,\n metadataBlobName,\n ReadFluidDataStoreAttributes,\n dataStoreAttributesBlobName,\n} from \"./summaryFormat\";\n\n/** This is the current version of garbage collection. */\nconst GCVersion = 1;\n\n// The key for the GC tree in summary.\nexport const gcTreeKey = \"gc\";\n// They prefix for GC blobs in the GC tree in summary.\nexport const gcBlobPrefix = \"__gc\";\n\n// Feature gate key to turn GC on / off.\nconst runGCKey = \"Fluid.GarbageCollection.RunGC\";\n// Feature gate key to turn GC test mode on / off.\nconst gcTestModeKey = \"Fluid.GarbageCollection.GCTestMode\";\n// Feature gate key to turn GC sweep on / off.\nconst runSweepKey = \"Fluid.GarbageCollection.RunSweep\";\n// Feature gate key to write GC data at the root of the summary tree.\nconst writeAtRootKey = \"Fluid.GarbageCollection.WriteDataAtRoot\";\n// Feature gate key to expire a session after a set period of time.\nconst runSessionExpiry = \"Fluid.GarbageCollection.RunSessionExpiry\";\n// Feature gate key to log error messages if GC reference validation fails.\nconst logUnknownOutboundReferencesKey = \"Fluid.GarbageCollection.LogUnknownOutboundReferences\";\n\nconst defaultDeleteTimeoutMs = 7 * 24 * 60 * 60 * 1000; // 7 days\nexport const defaultSessionExpiryDurationMs = 30 * 24 * 60 * 60 * 1000; // 30 days\n\n/** The statistics of the system state after a garbage collection run. */\nexport interface IGCStats {\n /** The number of nodes in the container. */\n nodeCount: number;\n /** The number of data stores in the container. */\n dataStoreCount: number;\n /** The number of attachment blobs in the container. */\n attachmentBlobCount: number;\n /** The number of unreferenced nodes in the container. */\n unrefNodeCount: number;\n /** The number of unreferenced data stores in the container. */\n unrefDataStoreCount: number;\n /** The number of unreferenced attachment blobs in the container. */\n unrefAttachmentBlobCount: number;\n /** The number of nodes whose reference state updated since last GC run. */\n updatedNodeCount: number;\n /** The number of data stores whose reference state updated since last GC run. */\n updatedDataStoreCount: number;\n /** The number of attachment blobs whose reference state updated since last GC run. */\n updatedAttachmentBlobCount: number;\n}\n\n/** The types of GC nodes in the GC reference graph. */\nexport const GCNodeType = {\n // Nodes that are for data stores.\n DataStore: \"DataStore\",\n // Nodes that are for attachment blobs, i.e., blobs uploaded via BlobManager.\n Blob: \"Blob\",\n // Nodes that are neither data store not blobs. For example, root node and DDS nodes.\n Other: \"Other\",\n};\nexport type GCNodeType = typeof GCNodeType[keyof typeof GCNodeType];\n\n/** The event that is logged when unreferenced node is used after a certain time. */\ninterface IUnreferencedEvent {\n eventName: string;\n id: string;\n age: number;\n timeout: number;\n lastSummaryTime?: number;\n externalRequest?: boolean;\n viaHandle?: boolean;\n}\n\n/** Defines the APIs for the runtime object to be passed to the garbage collector. */\nexport interface IGarbageCollectionRuntime {\n /** Before GC runs, called to notify the runtime to update any pending GC state. */\n updateStateBeforeGC(): Promise<void>;\n /** Returns the garbage collection data of the runtime. */\n getGCData(fullGC?: boolean): Promise<IGarbageCollectionData>;\n /** After GC has run, called to notify the runtime of routes that are used in it. */\n updateUsedRoutes(usedRoutes: string[], gcTimestamp?: number): void;\n /** After GC has run, called to delete objects in the runtime whose routes are unused. */\n deleteUnusedRoutes(unusedRoutes: string[]): void;\n /** Returns a referenced timestamp to be used to track unreferenced nodes. */\n getCurrentReferenceTimestampMs(): number | undefined;\n /** Returns the type of the GC node. */\n getNodeType(nodePath: string): GCNodeType;\n /** Called when the runtime should close because of an error. */\n closeFn(error?: ICriticalContainerError): void;\n}\n\n/** Defines the contract for the garbage collector. */\nexport interface IGarbageCollector {\n /** Tells whether GC should run or not. */\n readonly shouldRunGC: boolean;\n /** The time in ms to expire a session for a client for gc. */\n readonly sessionExpiryTimeoutMs: number | undefined;\n /**\n * This tracks two things:\n * 1. Whether GC is enabled - If this is 0, GC is disabled. If this is greater than 0, GC is enabled.\n * 2. If GC is enabled, the version of GC used to generate the GC data written in a summary.\n */\n readonly gcSummaryFeatureVersion: number;\n /** Tells whether the GC state in summary needs to be reset in the next summary. */\n readonly summaryStateNeedsReset: boolean;\n /** Tells whether GC data should be written to the root of the summary tree. */\n readonly writeDataAtRoot: boolean;\n /** Run garbage collection and update the reference / used state of the system. */\n collectGarbage(\n options: { logger?: ITelemetryLogger, runGC?: boolean, runSweep?: boolean, fullGC?: boolean },\n ): Promise<IGCStats>;\n /** Summarizes the GC data and returns it as a summary tree. */\n summarize(): ISummaryTreeWithStats | undefined;\n /** Returns a map of each node id to its base GC details in the base summary. */\n getBaseGCDetails(): Promise<Map<string, IGarbageCollectionDetailsBase>>;\n /** Called when the latest summary of the system has been refreshed. */\n latestSummaryStateRefreshed(result: RefreshSummaryResult, readAndParseBlob: ReadAndParseBlob): Promise<void>;\n /** Called when a node is updated. Used to detect and log when an inactive node is changed or loaded. */\n nodeUpdated(\n nodePath: string,\n reason: \"Loaded\" | \"Changed\",\n timestampMs?: number,\n packagePath?: readonly string[],\n requestHeaders?: IRequestHeader,\n ): void;\n /** Called when a reference is added to a node. Used to identify nodes that were referenced between summaries. */\n addedOutboundReference(fromNodePath: string, toNodePath: string): void;\n dispose(): void;\n}\n\n/**\n * Helper class that tracks the state of an unreferenced node such as the time it was unreferenced. It also sets\n * the node's state to inactive if it remains unreferenced for a given amount of time (inactiveTimeoutMs).\n */\nclass UnreferencedStateTracker {\n private _inactive: boolean = false;\n public get inactive(): boolean {\n return this._inactive;\n }\n\n private timer: Timer | undefined;\n\n constructor(\n public readonly unreferencedTimestampMs: number,\n private readonly inactiveTimeoutMs: number,\n currentReferenceTimestampMs?: number,\n ) {\n // If there is no current reference timestamp, don't track the node's inactive state. This will happen later\n // when updateTracking is called with a reference timestamp.\n if (currentReferenceTimestampMs !== undefined) {\n this.updateTracking(currentReferenceTimestampMs);\n }\n }\n\n /**\n * Updates the tracking state based on the provided timestamp.\n */\n public updateTracking(currentReferenceTimestampMs: number) {\n const unreferencedDurationMs = currentReferenceTimestampMs - this.unreferencedTimestampMs;\n // If the timeout has already expired, the node has become inactive.\n if (unreferencedDurationMs > this.inactiveTimeoutMs) {\n this._inactive = true;\n this.timer?.clear();\n return;\n }\n\n // The node isn't inactive yet. Restart a timer for the duration remaining for it to become inactive.\n const remainingDurationMs = this.inactiveTimeoutMs - unreferencedDurationMs;\n if (this.timer === undefined) {\n this.timer = new Timer(remainingDurationMs, () => { this._inactive = true; });\n }\n this.timer.restart(remainingDurationMs);\n }\n\n /**\n * Stop tracking this node. Reset the unreferenced timer, if any, and reset inactive state.\n */\n public stopTracking() {\n this.timer?.clear();\n this._inactive = false;\n }\n}\n\n/**\n * The garbage collector for the container runtime. It consolidates the garbage collection functionality and maintains\n * its state across summaries.\n *\n * Node - represented as nodeId, it's a node on the GC graph\n * Outbound Route - a path from one node to another node, think `nodeA` -> `nodeB`\n * Graph - all nodes with their respective routes\n * GC Graph\n *\n * Node\n * NodeId = \"datastore1\"\n * / \\\n * OutboundRoute OutboundRoute\n * / \\\n * Node Node\n * NodeId = \"dds1\" NodeId = \"dds2\"\n */\nexport class GarbageCollector implements IGarbageCollector {\n public static create(\n provider: IGarbageCollectionRuntime,\n gcOptions: IGCRuntimeOptions,\n getNodePackagePath: (nodePath: string) => readonly string[] | undefined,\n getLastSummaryTimestampMs: () => number | undefined,\n baseSnapshot: ISnapshotTree | undefined,\n readAndParseBlob: ReadAndParseBlob,\n baseLogger: ITelemetryLogger,\n existing: boolean,\n metadata?: IContainerRuntimeMetadata,\n ): IGarbageCollector {\n return new GarbageCollector(\n provider,\n gcOptions,\n getNodePackagePath,\n getLastSummaryTimestampMs,\n baseSnapshot,\n readAndParseBlob,\n baseLogger,\n existing,\n metadata,\n );\n }\n\n /**\n * Tells whether GC should be run based on the GC options and local storage flags.\n */\n public readonly shouldRunGC: boolean;\n\n /**\n * The time in ms to expire a session for a client for gc.\n */\n public readonly sessionExpiryTimeoutMs: number | undefined;\n\n /**\n * This tracks two things:\n * 1. Whether GC is enabled - If this is 0, GC is disabled. If this is greater than 0, GC is enabled.\n * 2. If GC is enabled, the version of GC used to generate the GC data written in a summary.\n */\n public get gcSummaryFeatureVersion(): number {\n return this.gcEnabled ? this.currentGCVersion : 0;\n }\n\n /**\n * Tells whether the GC state needs to be reset in the next summary. We need to do this if:\n * 1. GC was enabled and is now disabled. The GC state needs to be removed and everything becomes referenced.\n * 2. GC was disabled and is now enabled. The GC state needs to be regenerated and added to summary.\n * 3. The GC version in the latest summary is different from the current GC version. This can happen if:\n * 3.1. The summary this client loaded with has data from a different GC version.\n * 3.2. This client's latest summary was updated from a snapshot that has a different GC version.\n */\n public get summaryStateNeedsReset(): boolean {\n return this.initialStateNeedsReset ||\n (this.shouldRunGC && this.latestSummaryGCVersion !== this.currentGCVersion);\n }\n\n /**\n * Tracks if GC is enabled for this document. This is specified during document creation and doesn't change\n * throughout its lifetime.\n */\n private readonly gcEnabled: boolean;\n private readonly shouldRunSweep: boolean;\n private readonly testMode: boolean;\n private readonly mc: MonitoringContext;\n\n /**\n * Tells whether the GC data should be written to the root of the summary tree.\n */\n private _writeDataAtRoot: boolean = false;\n public get writeDataAtRoot(): boolean {\n return this._writeDataAtRoot;\n }\n\n /**\n * Tells whether the initial GC state needs to be reset. This can happen under 2 conditions:\n * 1. The base snapshot contains GC state but GC is disabled. This will happen the first time GC is disabled after\n * it was enabled before. GC state needs to be removed from summary and all nodes should be marked referenced.\n * 2. The base snapshot does not have GC state but GC is enabled. This will happen the very first time GC runs on\n * a document and the first time GC is enabled after is was disabled before.\n *\n * Note that the state needs reset only for the very first time summary is generated by this client. After that, the\n * state will be up-to-date and this flag will be reset.\n */\n private initialStateNeedsReset: boolean = false;\n\n // The current GC version that this container is running.\n private readonly currentGCVersion = GCVersion;\n // This is the version of GC data in the latest summary being tracked.\n private latestSummaryGCVersion: GCVersion;\n\n // Keeps track of the GC state from the last run.\n private previousGCDataFromLastRun: IGarbageCollectionData | undefined;\n // Keeps a list of references (edges in the GC graph) between GC runs. Each entry has a node id and a list of\n // outbound routes from that node.\n private readonly newReferencesSinceLastRun: Map<string, string[]> = new Map();\n\n // Promise when resolved initializes the base state of the nodes from the base summary state.\n private readonly initializeBaseStateP: Promise<void>;\n // The map of data store ids to their GC details in the base summary returned in getDataStoreGCDetails().\n private readonly baseGCDetailsP: Promise<Map<string, IGarbageCollectionDetailsBase>>;\n // The time after which an unreferenced node can be deleted. Currently, we only set the node's state to expired.\n private readonly deleteTimeoutMs: number;\n // Map of node ids to their unreferenced state tracker.\n private readonly unreferencedNodesState: Map<string, UnreferencedStateTracker> = new Map();\n // The timeout responsible for closing the container when the session has expired\n private sessionExpiryTimer?: ReturnType<typeof setTimeout>;\n\n // Keeps track of unreferenced events that are logged for a node. This is used to limit the log generation to one\n // per event per node.\n private readonly loggedUnreferencedEvents: Set<string> = new Set();\n // Queue for unreferenced events that should be logged the next time GC runs.\n private readonly pendingEventsQueue: IUnreferencedEvent[] = [];\n\n protected constructor(\n private readonly runtime: IGarbageCollectionRuntime,\n private readonly gcOptions: IGCRuntimeOptions,\n /** For a given node path, returns the node's package path. */\n private readonly getNodePackagePath: (nodePath: string) => readonly string[] | undefined,\n /** Returns the timestamp of the last summary generated for this container. */\n private readonly getLastSummaryTimestampMs: () => number | undefined,\n baseSnapshot: ISnapshotTree | undefined,\n readAndParseBlob: ReadAndParseBlob,\n baseLogger: ITelemetryLogger,\n existing: boolean,\n metadata?: IContainerRuntimeMetadata,\n ) {\n this.mc = loggerToMonitoringContext(\n ChildLogger.create(baseLogger, \"GarbageCollector\"));\n\n this.deleteTimeoutMs = this.gcOptions.deleteTimeoutMs ?? defaultDeleteTimeoutMs;\n\n let prevSummaryGCVersion: number | undefined;\n\n // GC can only be enabled during creation. After that, it can never be enabled again. So, for existing\n // documents, we get this information from the metadata blob. Similarly the session timeout should be\n // consistent across all clients, thus we grab it as well from the metadata blob, and set it once on creation.\n if (existing) {\n prevSummaryGCVersion = getGCVersion(metadata);\n // Existing documents which did not have metadata blob or had GC disabled have version as 0. For all\n // other existing documents, GC is enabled.\n this.gcEnabled = prevSummaryGCVersion > 0;\n this.sessionExpiryTimeoutMs = metadata?.sessionExpiryTimeoutMs;\n } else {\n // For new documents, GC has to be explicitly enabled via the gcAllowed flag in GC options.\n this.gcEnabled = gcOptions.gcAllowed === true;\n // Set the Session Expiry only if the flag is enabled or the test option is set.\n if (this.mc.config.getBoolean(runSessionExpiry) && this.gcEnabled) {\n this.sessionExpiryTimeoutMs = defaultSessionExpiryDurationMs;\n }\n }\n\n // If session expiry is enabled, we need to close the container when the timeout expires\n if (this.sessionExpiryTimeoutMs !== undefined) {\n const timeoutMs = this.sessionExpiryTimeoutMs;\n setLongTimeout(timeoutMs,\n () => {\n this.runtime.closeFn(new ClientSessionExpiredError(`Client session expired.`, timeoutMs));\n },\n (timer) => {\n this.sessionExpiryTimer = timer;\n });\n }\n\n // For existing document, the latest summary is the one that we loaded from. So, use its GC version as the\n // latest tracked GC version. For new documents, we will be writing the first summary with the current version.\n this.latestSummaryGCVersion = prevSummaryGCVersion ?? this.currentGCVersion;\n\n // Whether GC should run or not. Can override with localStorage flag.\n this.shouldRunGC = this.mc.config.getBoolean(runGCKey) ?? (\n // GC must be enabled for the document.\n this.gcEnabled\n // GC must not be disabled via GC options.\n && !gcOptions.disableGC\n );\n\n // Whether GC sweep phase should run or not. If this is false, only GC mark phase is run. Can override with\n // localStorage flag.\n this.shouldRunSweep = this.shouldRunGC &&\n (this.mc.config.getBoolean(runSweepKey) ?? gcOptions.runSweep === true)\n && this.sessionExpiryTimer !== undefined;\n\n // Whether we are running in test mode. In this mode, unreferenced nodes are immediately deleted.\n this.testMode = this.mc.config.getBoolean(gcTestModeKey) ?? gcOptions.runGCInTestMode === true;\n\n /**\n * Enable resetting initial state once the following issue is resolved:\n * https://github.com/microsoft/FluidFramework/issues/8878.\n * Currently, the GC tree is not written at root, so we don't know if the base snapshot contains GC tree or not.\n */\n // The GC state needs to be reset if the base snapshot contains GC tree and GC is disabled or it doesn't contain\n // GC tree and GC is enabled.\n // const gcTreePresent = baseSnapshot?.trees[gcTreeKey] !== undefined;\n // this.initialStateNeedsReset = gcTreePresent ? !this.shouldRunGC : this.shouldRunGC;\n\n // If `writeDataAtRoot` setting is true, write the GC data into the root of the summary tree. We do this so that\n // the roll out can be staged. Once its rolled out everywhere, we will start writing at root by default.\n this._writeDataAtRoot = this.mc.config.getBoolean(writeAtRootKey) ?? this.gcOptions.writeDataAtRoot === true;\n\n // Get the GC state from the GC blob in the base snapshot. Use LazyPromise because we only want to do\n // this once since it involves fetching blobs from storage which is expensive.\n const baseSummaryStateP = new LazyPromise<IGarbageCollectionState | undefined>(async () => {\n if (baseSnapshot === undefined) {\n return undefined;\n }\n\n // For newer documents, GC data should be present in the GC tree in the root of the snapshot.\n const gcSnapshotTree = baseSnapshot.trees[gcTreeKey];\n if (gcSnapshotTree !== undefined) {\n // If the GC tree is written at root, we should also do the same.\n this._writeDataAtRoot = true;\n return getGCStateFromSnapshot(gcSnapshotTree, readAndParseBlob);\n }\n\n // back-compat - Older documents will have the GC blobs in each data store's summary tree. Get them and\n // consolidate into IGarbageCollectionState format.\n // Add a node for the root node that is not present in older snapshot format.\n const gcState: IGarbageCollectionState = { gcNodes: { \"/\": { outboundRoutes: [] } } };\n const dataStoreSnapshotTree = getSummaryForDatastores(baseSnapshot, metadata);\n assert(dataStoreSnapshotTree !== undefined,\n 0x2a8 /* \"Expected data store snapshot tree in base snapshot\" */);\n for (const [dsId, dsSnapshotTree] of Object.entries(dataStoreSnapshotTree.trees)) {\n const blobId = dsSnapshotTree.blobs[gcBlobKey];\n if (blobId === undefined) {\n continue;\n }\n\n const gcSummaryDetails = await readAndParseBlob<IGarbageCollectionDetailsBase>(blobId);\n // If there are no nodes for this data store, skip it.\n if (gcSummaryDetails.gcData?.gcNodes === undefined) {\n continue;\n }\n\n const dsRootId = `/${dsId}`;\n // Since we used to write GC data at data store level, we won't have an entry for the root (\"/\").\n // Construct that entry by adding root data store ids to its outbound routes.\n const initialSnapshotDetails = await readAndParseBlob<ReadFluidDataStoreAttributes>(\n dsSnapshotTree.blobs[dataStoreAttributesBlobName],\n );\n if (initialSnapshotDetails.isRootDataStore) {\n gcState.gcNodes[\"/\"].outboundRoutes.push(dsRootId);\n }\n\n for (const [id, outboundRoutes] of Object.entries(gcSummaryDetails.gcData.gcNodes)) {\n // Prefix the data store id to the GC node ids to make them relative to the root from being\n // relative to the data store. Similar to how its done in DataStore::getGCData.\n const rootId = id === \"/\" ? dsRootId : `${dsRootId}${id}`;\n gcState.gcNodes[rootId] = { outboundRoutes: Array.from(outboundRoutes) };\n }\n assert(gcState.gcNodes[dsRootId] !== undefined,\n 0x2a9 /* `GC nodes for data store ${dsId} not in GC blob` */);\n gcState.gcNodes[dsRootId].unreferencedTimestampMs = gcSummaryDetails.unrefTimestamp;\n }\n\n // If there is only one node (root node just added above), either GC is disabled or we are loading from the\n // very first summary generated by detached container. In both cases, GC was not run - return undefined.\n return Object.keys(gcState.gcNodes).length === 1 ? undefined : gcState;\n });\n\n /**\n * Set up the initializer which initializes the base GC state from the base snapshot. Note that the reference\n * timestamp maybe from old ops which were not summarized and stored in the file. So, the unreferenced state\n * may be out of date. This is fine because the state is updated every time GC runs based on the time then.\n */\n this.initializeBaseStateP = new LazyPromise<void>(async () => {\n const currentReferenceTimestampMs = this.runtime.getCurrentReferenceTimestampMs();\n const baseState = await baseSummaryStateP;\n if (baseState === undefined) {\n return;\n }\n\n const gcNodes: { [ id: string ]: string[] } = {};\n for (const [nodeId, nodeData] of Object.entries(baseState.gcNodes)) {\n if (nodeData.unreferencedTimestampMs !== undefined) {\n this.unreferencedNodesState.set(\n nodeId,\n new UnreferencedStateTracker(\n nodeData.unreferencedTimestampMs,\n this.deleteTimeoutMs,\n currentReferenceTimestampMs,\n ),\n );\n }\n gcNodes[nodeId] = Array.from(nodeData.outboundRoutes);\n }\n this.previousGCDataFromLastRun = { gcNodes };\n });\n\n // Get the GC details for each node from the GC state in the base summary. This is returned in getBaseGCDetails\n // which the caller uses to initialize each node's GC state.\n this.baseGCDetailsP = new LazyPromise<Map<string, IGarbageCollectionDetailsBase>>(async () => {\n const baseState = await baseSummaryStateP;\n if (baseState === undefined) {\n return new Map();\n }\n\n const gcNodes: { [ id: string ]: string[] } = {};\n for (const [nodeId, nodeData] of Object.entries(baseState.gcNodes)) {\n gcNodes[nodeId] = Array.from(nodeData.outboundRoutes);\n }\n // Run GC on the nodes in the base summary to get the routes used in each node in the container.\n // This is an optimization for space (vs performance) wherein we don't need to store the used routes of\n // each node in the summary.\n const usedRoutes = runGarbageCollection(\n gcNodes,\n [ \"/\" ],\n this.mc.logger,\n ).referencedNodeIds;\n\n const baseGCDetailsMap = unpackChildNodesGCDetails({ gcData: { gcNodes }, usedRoutes });\n // Currently, the nodes may write the GC data. So, we need to update it's base GC details with the\n // unreferenced timestamp. Once we start writing the GC data here, we won't need to do this anymore.\n for (const [nodeId, nodeData] of Object.entries(baseState.gcNodes)) {\n if (nodeData.unreferencedTimestampMs !== undefined) {\n const dataStoreGCDetails = baseGCDetailsMap.get(nodeId.slice(1));\n if (dataStoreGCDetails !== undefined) {\n dataStoreGCDetails.unrefTimestamp = nodeData.unreferencedTimestampMs;\n }\n }\n }\n return baseGCDetailsMap;\n });\n\n // Initialize the base state. The base GC data is used to detect and log when inactive / deleted objects are\n // used in the container.\n if (this.shouldRunGC) {\n this.initializeBaseStateP.catch((error) => {\n const dpe = DataProcessingError.wrapIfUnrecognized(\n error,\n \"FailedToInitializeGC\",\n );\n dpe.addTelemetryProperties({\n gcEnabled: this.gcEnabled,\n runSweep: this.shouldRunSweep,\n writeAtRoot: this._writeDataAtRoot,\n testMode: this.testMode,\n sessionExpiry: this.sessionExpiryTimeoutMs,\n });\n throw dpe;\n });\n }\n }\n\n /**\n * Runs garbage collection and updates the reference / used state of the nodes in the container.\n * @returns the number of data stores that have been marked as unreferenced.\n */\n public async collectGarbage(\n options: {\n /** Logger to use for logging GC events */\n logger?: ITelemetryLogger,\n /** True to run GC sweep phase after the mark phase */\n runSweep?: boolean,\n /** True to generate full GC data */\n fullGC?: boolean,\n },\n ): Promise<IGCStats> {\n const {\n logger = this.mc.logger,\n runSweep = this.shouldRunSweep,\n fullGC = this.gcOptions.runFullGC === true || this.summaryStateNeedsReset,\n } = options;\n\n return PerformanceEvent.timedExecAsync(logger, { eventName: \"GarbageCollection\" }, async (event) => {\n await this.initializeBaseStateP;\n\n // Let the runtime update its pending state before GC runs.\n await this.runtime.updateStateBeforeGC();\n\n // Get the runtime's GC data and run GC on the reference graph in it.\n const gcData = await this.runtime.getGCData(fullGC);\n const gcResult = runGarbageCollection(\n gcData.gcNodes,\n [ \"/\" ],\n logger,\n );\n const gcStats = this.generateStatsAndLogEvents(gcResult);\n\n // Update the state since the last GC run. There can be nodes that were referenced between the last and\n // the current run. We need to identify than and update their unreferenced state if needed.\n this.updateStateSinceLastRun(gcData);\n\n // Update the current state of the system based on the GC run.\n const currentReferenceTimestampMs = this.runtime.getCurrentReferenceTimestampMs();\n this.updateCurrentState(gcData, gcResult, currentReferenceTimestampMs);\n\n this.runtime.updateUsedRoutes(gcResult.referencedNodeIds, currentReferenceTimestampMs);\n\n if (runSweep) {\n // Placeholder for running sweep logic.\n }\n\n // If we are running in GC test mode, delete objects for unused routes. This enables testing scenarios\n // involving access to deleted data.\n if (this.testMode) {\n this.runtime.deleteUnusedRoutes(gcResult.deletedNodeIds);\n }\n event.end({ ...gcStats });\n return gcStats;\n },\n { end: true, cancel: \"error\" });\n }\n\n /**\n * Summarizes the GC data and returns it as a summary tree.\n * We current write the entire GC state in a single blob. This can be modified later to write multiple\n * blobs. All the blob keys should start with `gcBlobPrefix`.\n */\n public summarize(): ISummaryTreeWithStats | undefined {\n if (!this.shouldRunGC || this.previousGCDataFromLastRun === undefined) {\n return;\n }\n\n const gcState: IGarbageCollectionState = { gcNodes: {} };\n for (const [nodeId, outboundRoutes] of Object.entries(this.previousGCDataFromLastRun.gcNodes)) {\n gcState.gcNodes[nodeId] = {\n outboundRoutes,\n unreferencedTimestampMs: this.unreferencedNodesState.get(nodeId)?.unreferencedTimestampMs,\n };\n }\n\n const builder = new SummaryTreeBuilder();\n builder.addBlob(`${gcBlobPrefix}_root`, JSON.stringify(gcState));\n return builder.getSummaryTree();\n }\n\n /**\n * Returns a map of node ids to their base GC details generated from the base summary. This is used by the caller\n * to initialize the GC state of the nodes.\n */\n public async getBaseGCDetails(): Promise<Map<string, IGarbageCollectionDetailsBase>> {\n return this.baseGCDetailsP;\n }\n\n /**\n * Called when the latest summary of the system has been refreshed. This will be used to update the state of the\n * latest summary tracked.\n */\n public async latestSummaryStateRefreshed(\n result: RefreshSummaryResult,\n readAndParseBlob: ReadAndParseBlob,\n ): Promise<void> {\n // After a summary is successfully submitted and ack'd by this client, the GC state should have been reset in\n // the summary and doesn't need to be reset anymore.\n this.initialStateNeedsReset = false;\n\n if (!this.shouldRunGC || !result.latestSummaryUpdated) {\n return;\n }\n\n // If the summary was tracked by this client, it was the one that generated the summary in the first place.\n // Basically, it was written in the current GC version.\n if (result.wasSummaryTracked) {\n this.latestSummaryGCVersion = this.currentGCVersion;\n return;\n }\n // If the summary was not tracked by this client, update latest GC version from the snapshot in the result as\n // that is now the latest summary.\n await this.updateSummaryGCVersionFromSnapshot(result.snapshot, readAndParseBlob);\n }\n\n /**\n * Called when a node with the given id is updated. If the node is inactive, log an error.\n * @param nodePath - The id of the node that changed.\n * @param reason - Whether the node was loaded or changed.\n * @param timestampMs - The timestamp when the node changed.\n * @param packagePath - The package path of the node. This may not be available if the node hasn't been loaded yet.\n * @param requestHeaders - If the node was loaded via request path, the headers in the request.\n */\n public nodeUpdated(\n nodePath: string,\n reason: \"Loaded\" | \"Changed\",\n timestampMs?: number,\n packagePath?: readonly string[],\n requestHeaders?: IRequestHeader,\n ) {\n if (!this.shouldRunGC) {\n return;\n }\n\n this.logIfInactive(\n reason,\n nodePath,\n timestampMs,\n packagePath,\n requestHeaders,\n );\n }\n\n /**\n * Called when an outbound reference is added to a node. This is used to identify all nodes that have been\n * referenced between summaries so that their unreferenced timestamp can be reset.\n *\n * @param fromNodePath - The node from which the reference is added.\n * @param toNodePath - The node to which the reference is added.\n */\n public addedOutboundReference(fromNodePath: string, toNodePath: string) {\n if (!this.shouldRunGC) {\n return;\n }\n\n const outboundRoutes = this.newReferencesSinceLastRun.get(fromNodePath) ?? [];\n outboundRoutes.push(toNodePath);\n this.newReferencesSinceLastRun.set(fromNodePath, outboundRoutes);\n\n // If the node that got referenced is inactive, log an event as that may indicate use-after-delete.\n this.logIfInactive(\n \"Revived\",\n toNodePath,\n );\n }\n\n public dispose(): void {\n if (this.sessionExpiryTimer !== undefined) {\n clearTimeout(this.sessionExpiryTimer);\n this.sessionExpiryTimer = undefined;\n }\n }\n\n /**\n * Update the latest summary GC version from the metadata blob in the given snapshot.\n */\n private async updateSummaryGCVersionFromSnapshot(snapshot: ISnapshotTree, readAndParseBlob: ReadAndParseBlob) {\n const metadataBlobId = snapshot.blobs[metadataBlobName];\n if (metadataBlobId) {\n const metadata = await readAndParseBlob<IContainerRuntimeMetadata>(metadataBlobId);\n this.latestSummaryGCVersion = getGCVersion(metadata);\n }\n }\n\n /**\n * Updates the state of the system as per the current GC run. It does the following:\n * 1. Sets up the current GC state as per the gcData.\n * 2. Starts tracking for nodes that have become unreferenced in this run.\n * 3. Clears tracking for nodes that were unreferenced but became referenced in this run.\n * @param gcData - The data representing the reference graph on which GC is run.\n * @param gcResult - The result of the GC run on the gcData.\n * @param currentReferenceTimestampMs - The timestamp to be used for unreferenced nodes' timestamp.\n */\n private updateCurrentState(\n gcData: IGarbageCollectionData,\n gcResult: IGCResult,\n currentReferenceTimestampMs?: number,\n ) {\n this.previousGCDataFromLastRun = cloneGCData(gcData);\n this.newReferencesSinceLastRun.clear();\n\n // Iterate through the referenced nodes and stop tracking if they were unreferenced before.\n for (const nodeId of gcResult.referencedNodeIds) {\n const nodeStateTracker = this.unreferencedNodesState.get(nodeId);\n if (nodeStateTracker !== undefined) {\n // Stop tracking so as to clear out any running timers.\n nodeStateTracker.stopTracking();\n // Delete the node as we don't need to track it any more.\n this.unreferencedNodesState.delete(nodeId);\n }\n }\n\n /**\n * If there is no current reference time, skip tracking when a node becomes unreferenced. This would happen\n * if no ops have been processed ever and we still try to run GC. If so, there is nothing interesting to track\n * anyway.\n */\n if (currentReferenceTimestampMs === undefined) {\n return;\n }\n\n /**\n * If a node became unreferenced in this run, start tracking it.\n * If a node was already unreferenced, update its tracking information. Since the current reference time is\n * from the ops seen, this will ensure that we keep updating the unreferenced state as time moves forward.\n */\n for (const nodeId of gcResult.deletedNodeIds) {\n const nodeStateTracker = this.unreferencedNodesState.get(nodeId);\n if (nodeStateTracker === undefined) {\n this.unreferencedNodesState.set(\n nodeId,\n new UnreferencedStateTracker(\n currentReferenceTimestampMs,\n this.deleteTimeoutMs,\n currentReferenceTimestampMs,\n ),\n );\n } else {\n nodeStateTracker.updateTracking(currentReferenceTimestampMs);\n }\n }\n }\n\n /**\n * Since GC runs periodically, the GC data that is generated only tells us the state of the world at that point in\n * time. It's possible that nodes transition from `unreferenced -> referenced -> unreferenced` between two runs. The\n * unreferenced timestamp of such nodes needs to be reset as they may have been accessed when they were referenced.\n *\n * This function identifies nodes that were referenced since last run and removes their unreferenced state, if any.\n * If these nodes are currently unreferenced, they will be assigned new unreferenced state by the current run.\n */\n private updateStateSinceLastRun(currentGCData: IGarbageCollectionData) {\n // If we haven't run GC before there is nothing to do.\n if (this.previousGCDataFromLastRun === undefined) {\n return;\n }\n\n // Find any references that haven't been identified correctly.\n const missingExplicitReferences = this.findMissingExplicitReferences(\n currentGCData,\n this.previousGCDataFromLastRun,\n this.newReferencesSinceLastRun,\n );\n\n // The following log will be enabled once this issue is resolved:\n // https://github.com/microsoft/FluidFramework/issues/8878.\n if(this.mc.config.getBoolean(logUnknownOutboundReferencesKey) === true\n && missingExplicitReferences.length > 0) {\n missingExplicitReferences.forEach((missingExplicitReference) => {\n const event: ITelemetryPerformanceEvent = {\n eventName: \"gcUnknownOutboundReferences\",\n gcNodeId: missingExplicitReference[0],\n gcRoutes: JSON.stringify(missingExplicitReference[1]),\n };\n this.mc.logger.sendPerformanceEvent(event);\n });\n }\n\n // No references were added since the last run so we don't have to update reference states of any unreferenced\n // nodes\n if (this.newReferencesSinceLastRun.size === 0) {\n return;\n }\n\n /**\n * Generate a super set of the GC data that contains the nodes and edges from last run, plus any new node and\n * edges that have been added since then. To do this, combine the GC data from the last run and the current\n * run, and then add the references since last run.\n *\n * Note on why we need to combine the data from previous run, current run and all references in between -\n * 1. We need data from last run because some of its references may have been deleted since then. If those\n * references added new outbound references before getting deleted, we need to detect them.\n * 2. We need new outbound references since last run because some of them may have been deleted later. If those\n * references added new outbound references before getting deleted, we need to detect them.\n * 3. We need data from the current run because currently we may not detect when DDSs are referenced:\n * - We don't require DDSs handles to be stored in a referenced DDS. For this, we need GC at DDS level\n * which is tracked by https://github.com/microsoft/FluidFramework/issues/8470.\n * - A new data store may have \"root\" DDSs already created and we don't detect them today.\n */\n const gcDataSuperSet = concatGarbageCollectionData(this.previousGCDataFromLastRun, currentGCData);\n this.newReferencesSinceLastRun.forEach((outboundRoutes: string[], sourceNodeId: string) => {\n if (gcDataSuperSet.gcNodes[sourceNodeId] === undefined) {\n gcDataSuperSet.gcNodes[sourceNodeId] = outboundRoutes;\n } else {\n gcDataSuperSet.gcNodes[sourceNodeId].push(...outboundRoutes);\n }\n });\n\n /**\n * Run GC on the above reference graph to find all nodes that are referenced. For each one, if they are\n * unreferenced, stop tracking them and remove from unreferenced list.\n * Some of these nodes may be unreferenced now and if so, the current run will add unreferenced state for them.\n */\n const gcResult = runGarbageCollection(gcDataSuperSet.gcNodes, [\"/\"], this.mc.logger);\n for (const nodeId of gcResult.referencedNodeIds) {\n const nodeStateTracker = this.unreferencedNodesState.get(nodeId);\n if (nodeStateTracker !== undefined) {\n // Stop tracking so as to clear out any running timers.\n nodeStateTracker.stopTracking();\n // Delete the node as we don't need to track it any more.\n this.unreferencedNodesState.delete(nodeId);\n }\n }\n }\n\n /**\n * Finds all new references or outbound routes in the current graph that haven't been explicitly notified to GC.\n * The principle is that every new reference or outbound route must be notified to GC via the\n * addedOutboundReference method. It it hasn't, its a bug and we want to identify these scenarios.\n *\n * In more simple terms:\n * Missing Explicit References = Current References - Previous References - Explicitly Added References;\n *\n * @param currentGCData - The GC data (reference graph) from the current GC run.\n * @param previousGCData - The GC data (reference graph) from the previous GC run.\n * @param explicitReferences - New references added explicity between the previous and the current run.\n * @returns - a list of missing explicit references\n */\n private findMissingExplicitReferences(\n currentGCData: IGarbageCollectionData,\n previousGCData: IGarbageCollectionData,\n explicitReferences: Map<string, string[]>,\n ): [string, string[]][] {\n assert(\n previousGCData !== undefined,\n 0x2b7, /* \"Can't validate correctness without GC data from last run\" */\n );\n\n const currentGraph = Object.entries(currentGCData.gcNodes);\n const missingExplicitReferences: [string, string[]][] = [];\n currentGraph.forEach(([nodeId, currentOutboundRoutes]) => {\n const previousRoutes = previousGCData.gcNodes[nodeId] ?? [];\n const explicitRoutes = explicitReferences.get(nodeId) ?? [];\n const missingExplicitRoutes: string[] = [];\n currentOutboundRoutes.forEach((route) => {\n const isBlobOrDataStoreRoute =\n this.runtime.getNodeType(route) === GCNodeType.Blob ||\n this.runtime.getNodeType(route) === GCNodeType.DataStore;\n // Ignore implicitly added DDS routes to their parent datastores\n const notRouteFromDDSToParentDataStore = !nodeId.startsWith(route);\n if (\n isBlobOrDataStoreRoute &&\n notRouteFromDDSToParentDataStore &&\n (!previousRoutes.includes(route) && !explicitRoutes.includes(route))\n ) {\n missingExplicitRoutes.push(route);\n }\n });\n if (missingExplicitRoutes.length > 0) {\n missingExplicitReferences.push([nodeId, missingExplicitRoutes]);\n }\n });\n\n // Ideally missingExplicitReferences should always have a size 0\n return missingExplicitReferences;\n }\n\n /**\n * Generates the stats of a garbage collection run from the given results of the run. Also, logs any pending events\n * in the pendingEventsQueue. This should be called before updating the current state because it generates stats\n * based on previous state of the system.\n * @param gcResult - The result of a GC run.\n * @returns the GC stats of the GC run.\n */\n private generateStatsAndLogEvents(gcResult: IGCResult): IGCStats {\n // Log pending events for unreferenced nodes after GC has run. We should have the package data available for\n // them now since the GC run should have loaded these nodes.\n let event = this.pendingEventsQueue.shift();\n while (event !== undefined) {\n const pkg = this.getNodePackagePath(event.id);\n this.mc.logger.sendErrorEvent({\n ...event,\n pkg: pkg ? { value: `/${pkg.join(\"/\")}`, tag: TelemetryDataTag.PackageData } : undefined,\n });\n event = this.pendingEventsQueue.shift();\n }\n\n const gcStats: IGCStats = {\n nodeCount: 0,\n dataStoreCount: 0,\n attachmentBlobCount: 0,\n unrefNodeCount: 0,\n unrefDataStoreCount: 0,\n unrefAttachmentBlobCount: 0,\n updatedNodeCount: 0,\n updatedDataStoreCount: 0,\n updatedAttachmentBlobCount: 0,\n };\n\n const updateNodeStats = (nodeId: string, referenced: boolean) => {\n gcStats.nodeCount++;\n /**\n * `this.unreferencedNodesState` has the previous unreferenced state of all nodes. `referenced` flag passed\n * here is current state of the give node. Check if the reference state of the changed.\n */\n const stateUpdated = this.unreferencedNodesState.has(nodeId) ? referenced : !referenced;\n if (stateUpdated) {\n gcStats.updatedNodeCount++;\n }\n if (!referenced) {\n gcStats.unrefNodeCount++;\n }\n\n if (this.runtime.getNodeType(nodeId) === GCNodeType.DataStore) {\n gcStats.dataStoreCount++;\n if (stateUpdated) {\n gcStats.updatedDataStoreCount++;\n }\n if (!referenced) {\n gcStats.unrefDataStoreCount++;\n }\n }\n if (this.runtime.getNodeType(nodeId) === GCNodeType.Blob) {\n gcStats.attachmentBlobCount++;\n if (stateUpdated) {\n gcStats.updatedAttachmentBlobCount++;\n }\n if (!referenced) {\n gcStats.unrefAttachmentBlobCount++;\n }\n }\n };\n\n for (const nodeId of gcResult.referencedNodeIds) {\n updateNodeStats(nodeId, true /* referenced */);\n }\n\n for (const nodeId of gcResult.deletedNodeIds) {\n updateNodeStats(nodeId, false /* referenced */);\n }\n\n return gcStats;\n }\n\n /**\n * Logs an event if a node is inactive and is used.\n */\n private logIfInactive(\n eventSuffix: \"Changed\" | \"Loaded\" | \"Revived\",\n nodeId: string,\n currentReferenceTimestampMs = this.runtime.getCurrentReferenceTimestampMs(),\n packagePath?: readonly string[],\n requestHeaders?: IRequestHeader,\n ) {\n // If there is no reference timestamp to work with, no ops have been processed after creation. If so, skip\n // logging as nothing interesting would have happened worth logging.\n if (currentReferenceTimestampMs === undefined) {\n return;\n }\n\n const eventName = `inactiveObject_${eventSuffix}`;\n // We log a particular event for a given node only once so that it is not too noisy.\n const uniqueEventId = `${nodeId}-${eventName}`;\n const nodeState = this.unreferencedNodesState.get(nodeId);\n if (nodeState?.inactive && !this.loggedUnreferencedEvents.has(uniqueEventId)) {\n this.loggedUnreferencedEvents.add(uniqueEventId);\n const event: IUnreferencedEvent = {\n eventName,\n id: nodeId,\n age: currentReferenceTimestampMs - nodeState.unreferencedTimestampMs,\n timeout: this.deleteTimeoutMs,\n lastSummaryTime: this.getLastSummaryTimestampMs(),\n externalRequest: requestHeaders?.[RuntimeHeaders.externalRequest],\n viaHandle: requestHeaders?.[RuntimeHeaders.viaHandle],\n };\n\n // If the package data for the node exists, log immediately. Otherwise, queue it and it will be logged the\n // next time GC runs as the package data should be available then.\n const pkg = packagePath ?? this.getNodePackagePath(nodeId);\n if (pkg !== undefined) {\n this.mc.logger.sendErrorEvent({\n ...event,\n pkg: { value: `/${pkg.join(\"/\")}`, tag: TelemetryDataTag.PackageData },\n });\n } else {\n this.pendingEventsQueue.push(event);\n }\n }\n }\n}\n\n/**\n * Gets the garbage collection state from the given snapshot tree. The GC state may be written into multiple blobs.\n * Merge the GC state from all such blobs and return the merged GC state.\n */\nasync function getGCStateFromSnapshot(\n gcSnapshotTree: ISnapshotTree,\n readAndParseBlob: ReadAndParseBlob,\n): Promise<IGarbageCollectionState> {\n let rootGCState: IGarbageCollectionState = { gcNodes: {} };\n for (const key of Object.keys(gcSnapshotTree.blobs)) {\n // Skip blobs that do not start with the GC prefix.\n if (!key.startsWith(gcBlobPrefix)) {\n continue;\n }\n\n const blobId = gcSnapshotTree.blobs[key];\n if (blobId === undefined) {\n continue;\n }\n const gcState = await readAndParseBlob<IGarbageCollectionState>(blobId);\n assert(gcState !== undefined, 0x2ad /* \"GC blob missing from snapshot\" */);\n // Merge the GC state of this blob into the root GC state.\n rootGCState = concatGarbageCollectionStates(rootGCState, gcState);\n }\n return rootGCState;\n}\n\n/**\n * setLongTimeout is used for timeouts longer than setTimeout's ~24.8 day max\n * @param timeoutMs - the total time the timeout needs to last in ms\n * @param timeoutFn - the function to execute when the timer ends\n * @param setTimerFn - the function used to update your timer variable\n */\nfunction setLongTimeout(\n timeoutMs: number,\n timeoutFn: () => void,\n setTimerFn: (timer: ReturnType<typeof setTimeout>) => void,\n) {\n // The setTimeout max is 24.8 days before looping occurs.\n const maxTimeout = 2147483647;\n let timer: ReturnType<typeof setTimeout>;\n if (timeoutMs > maxTimeout) {\n const newTimeoutMs = timeoutMs - maxTimeout;\n timer = setTimeout(() => setLongTimeout(newTimeoutMs, timeoutFn, setTimerFn), maxTimeout);\n } else {\n timer = setTimeout(() => timeoutFn(), timeoutMs);\n }\n setTimerFn(timer);\n}\n"]}
1
+ {"version":3,"file":"garbageCollection.js","sourceRoot":"","sources":["../src/garbageCollection.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 { ITelemetryLogger, ITelemetryPerformanceEvent } from \"@fluidframework/common-definitions\";\nimport { assert, LazyPromise, Timer } from \"@fluidframework/common-utils\";\nimport { ICriticalContainerError } from \"@fluidframework/container-definitions\";\nimport { ClientSessionExpiredError, DataProcessingError, UsageError } from \"@fluidframework/container-utils\";\nimport { IRequestHeader } from \"@fluidframework/core-interfaces\";\nimport {\n cloneGCData,\n concatGarbageCollectionStates,\n concatGarbageCollectionData,\n IGCResult,\n runGarbageCollection,\n unpackChildNodesGCDetails,\n} from \"@fluidframework/garbage-collector\";\nimport { ISnapshotTree } from \"@fluidframework/protocol-definitions\";\nimport {\n gcBlobKey,\n IGarbageCollectionData,\n IGarbageCollectionState,\n IGarbageCollectionDetailsBase,\n ISummaryTreeWithStats,\n} from \"@fluidframework/runtime-definitions\";\nimport {\n ReadAndParseBlob,\n RefreshSummaryResult,\n SummaryTreeBuilder,\n} from \"@fluidframework/runtime-utils\";\nimport {\n ChildLogger,\n loggerToMonitoringContext,\n MonitoringContext,\n PerformanceEvent,\n TelemetryDataTag,\n} from \"@fluidframework/telemetry-utils\";\n\nimport { IGCRuntimeOptions, RuntimeHeaders } from \"./containerRuntime\";\nimport { getSummaryForDatastores } from \"./dataStores\";\nimport {\n getGCVersion,\n GCVersion,\n IContainerRuntimeMetadata,\n metadataBlobName,\n ReadFluidDataStoreAttributes,\n dataStoreAttributesBlobName,\n IGCMetadata,\n} from \"./summaryFormat\";\n\n/** This is the current version of garbage collection. */\nconst GCVersion = 1;\n\n// The key for the GC tree in summary.\nexport const gcTreeKey = \"gc\";\n// They prefix for GC blobs in the GC tree in summary.\nexport const gcBlobPrefix = \"__gc\";\n\n// Feature gate key to turn GC on / off.\nconst runGCKey = \"Fluid.GarbageCollection.RunGC\";\n// Feature gate key to turn GC test mode on / off.\nconst gcTestModeKey = \"Fluid.GarbageCollection.GCTestMode\";\n// Feature gate key to turn GC sweep on / off.\nconst runSweepKey = \"Fluid.GarbageCollection.RunSweep\";\n// Feature gate key to write GC data at the root of the summary tree.\nconst writeAtRootKey = \"Fluid.GarbageCollection.WriteDataAtRoot\";\n// Feature gate key to expire a session after a set period of time.\nconst runSessionExpiryKey = \"Fluid.GarbageCollection.RunSessionExpiry\";\n// Feature gate key to disable expiring session after a set period of time, even if expiry value is present\nconst disableSessionExpiryKey = \"Fluid.GarbageCollection.DisableSessionExpiry\";\n// Feature gate key to log error messages if GC reference validation fails.\nconst logUnknownOutboundReferencesKey = \"Fluid.GarbageCollection.LogUnknownOutboundReferences\";\n\nconst defaultDeleteTimeoutMs = 7 * 24 * 60 * 60 * 1000; // 7 days\nexport const defaultSessionExpiryDurationMs = 30 * 24 * 60 * 60 * 1000; // 30 days\n\n/** The statistics of the system state after a garbage collection run. */\nexport interface IGCStats {\n /** The number of nodes in the container. */\n nodeCount: number;\n /** The number of data stores in the container. */\n dataStoreCount: number;\n /** The number of attachment blobs in the container. */\n attachmentBlobCount: number;\n /** The number of unreferenced nodes in the container. */\n unrefNodeCount: number;\n /** The number of unreferenced data stores in the container. */\n unrefDataStoreCount: number;\n /** The number of unreferenced attachment blobs in the container. */\n unrefAttachmentBlobCount: number;\n /** The number of nodes whose reference state updated since last GC run. */\n updatedNodeCount: number;\n /** The number of data stores whose reference state updated since last GC run. */\n updatedDataStoreCount: number;\n /** The number of attachment blobs whose reference state updated since last GC run. */\n updatedAttachmentBlobCount: number;\n}\n\n/** The types of GC nodes in the GC reference graph. */\nexport const GCNodeType = {\n // Nodes that are for data stores.\n DataStore: \"DataStore\",\n // Nodes that are within a data store. For example, DDS nodes.\n SubDataStore: \"SubDataStore\",\n // Nodes that are for attachment blobs, i.e., blobs uploaded via BlobManager.\n Blob: \"Blob\",\n // Nodes that are neither of the above. For example, root node.\n Other: \"Other\",\n};\nexport type GCNodeType = typeof GCNodeType[keyof typeof GCNodeType];\n\n/** The event that is logged when unreferenced node is used after a certain time. */\ninterface IUnreferencedEvent {\n eventName: string;\n id: string;\n age: number;\n timeout: number;\n lastSummaryTime?: number;\n externalRequest?: boolean;\n viaHandle?: boolean;\n}\n\n/** Defines the APIs for the runtime object to be passed to the garbage collector. */\nexport interface IGarbageCollectionRuntime {\n /** Before GC runs, called to notify the runtime to update any pending GC state. */\n updateStateBeforeGC(): Promise<void>;\n /** Returns the garbage collection data of the runtime. */\n getGCData(fullGC?: boolean): Promise<IGarbageCollectionData>;\n /** After GC has run, called to notify the runtime of routes that are used in it. */\n updateUsedRoutes(usedRoutes: string[], gcTimestamp?: number): void;\n /** After GC has run, called to delete objects in the runtime whose routes are unused. */\n deleteUnusedRoutes(unusedRoutes: string[]): void;\n /** Returns a referenced timestamp to be used to track unreferenced nodes. */\n getCurrentReferenceTimestampMs(): number | undefined;\n /** Returns the type of the GC node. */\n getNodeType(nodePath: string): GCNodeType;\n /** Called when the runtime should close because of an error. */\n closeFn(error?: ICriticalContainerError): void;\n}\n\n/** Defines the contract for the garbage collector. */\nexport interface IGarbageCollector {\n /** Tells whether GC should run or not. */\n readonly shouldRunGC: boolean;\n /** Tells whether the GC state in summary needs to be reset in the next summary. */\n readonly summaryStateNeedsReset: boolean;\n /** Tells whether GC data should be written to the root of the summary tree. */\n readonly writeDataAtRoot: boolean;\n /** Run garbage collection and update the reference / used state of the system. */\n collectGarbage(\n options: { logger?: ITelemetryLogger; runGC?: boolean; runSweep?: boolean; fullGC?: boolean; },\n ): Promise<IGCStats>;\n /** Summarizes the GC data and returns it as a summary tree. */\n summarize(): ISummaryTreeWithStats | undefined;\n /** Returns the garbage collector specific metadata to be written into the summary. */\n getMetadata(): IGCMetadata;\n /** Returns a map of each node id to its base GC details in the base summary. */\n getBaseGCDetails(): Promise<Map<string, IGarbageCollectionDetailsBase>>;\n /** Called when the latest summary of the system has been refreshed. */\n latestSummaryStateRefreshed(result: RefreshSummaryResult, readAndParseBlob: ReadAndParseBlob): Promise<void>;\n /** Called when a node is updated. Used to detect and log when an inactive node is changed or loaded. */\n nodeUpdated(\n nodePath: string,\n reason: \"Loaded\" | \"Changed\",\n timestampMs?: number,\n packagePath?: readonly string[],\n requestHeaders?: IRequestHeader,\n ): void;\n /** Called when a reference is added to a node. Used to identify nodes that were referenced between summaries. */\n addedOutboundReference(fromNodePath: string, toNodePath: string): void;\n dispose(): void;\n}\n\n/**\n * Helper class that tracks the state of an unreferenced node such as the time it was unreferenced. It also sets\n * the node's state to inactive if it remains unreferenced for a given amount of time (inactiveTimeoutMs).\n */\nclass UnreferencedStateTracker {\n private _inactive: boolean = false;\n public get inactive(): boolean {\n return this._inactive;\n }\n\n private timer: Timer | undefined;\n\n constructor(\n public readonly unreferencedTimestampMs: number,\n private readonly inactiveTimeoutMs: number,\n currentReferenceTimestampMs?: number,\n ) {\n // If there is no current reference timestamp, don't track the node's inactive state. This will happen later\n // when updateTracking is called with a reference timestamp.\n if (currentReferenceTimestampMs !== undefined) {\n this.updateTracking(currentReferenceTimestampMs);\n }\n }\n\n /**\n * Updates the tracking state based on the provided timestamp.\n */\n public updateTracking(currentReferenceTimestampMs: number) {\n const unreferencedDurationMs = currentReferenceTimestampMs - this.unreferencedTimestampMs;\n // If the timeout has already expired, the node has become inactive.\n if (unreferencedDurationMs > this.inactiveTimeoutMs) {\n this._inactive = true;\n this.timer?.clear();\n return;\n }\n\n // The node isn't inactive yet. Restart a timer for the duration remaining for it to become inactive.\n const remainingDurationMs = this.inactiveTimeoutMs - unreferencedDurationMs;\n if (this.timer === undefined) {\n this.timer = new Timer(remainingDurationMs, () => { this._inactive = true; });\n }\n this.timer.restart(remainingDurationMs);\n }\n\n /**\n * Stop tracking this node. Reset the unreferenced timer, if any, and reset inactive state.\n */\n public stopTracking() {\n this.timer?.clear();\n this._inactive = false;\n }\n}\n\n/**\n * The garbage collector for the container runtime. It consolidates the garbage collection functionality and maintains\n * its state across summaries.\n *\n * Node - represented as nodeId, it's a node on the GC graph\n * Outbound Route - a path from one node to another node, think `nodeA` -\\> `nodeB`\n * Graph - all nodes with their respective routes\n * GC Graph\n *\n * Node\n * NodeId = \"datastore1\"\n * / \\\\\n * OutboundRoute OutboundRoute\n * / \\\\\n * Node Node\n * NodeId = \"dds1\" NodeId = \"dds2\"\n */\nexport class GarbageCollector implements IGarbageCollector {\n public static create(\n provider: IGarbageCollectionRuntime,\n gcOptions: IGCRuntimeOptions,\n getNodePackagePath: (nodePath: string) => readonly string[] | undefined,\n getLastSummaryTimestampMs: () => number | undefined,\n baseSnapshot: ISnapshotTree | undefined,\n readAndParseBlob: ReadAndParseBlob,\n baseLogger: ITelemetryLogger,\n existing: boolean,\n metadata?: IContainerRuntimeMetadata,\n ): IGarbageCollector {\n return new GarbageCollector(\n provider,\n gcOptions,\n getNodePackagePath,\n getLastSummaryTimestampMs,\n baseSnapshot,\n readAndParseBlob,\n baseLogger,\n existing,\n metadata,\n );\n }\n\n /**\n * The time in ms to expire a session for a client for gc.\n */\n private readonly sessionExpiryTimeoutMs: number | undefined;\n\n /**\n * Tells whether the GC state needs to be reset in the next summary. We need to do this if:\n * 1. GC was enabled and is now disabled. The GC state needs to be removed and everything becomes referenced.\n * 2. GC was disabled and is now enabled. The GC state needs to be regenerated and added to summary.\n * 3. The GC version in the latest summary is different from the current GC version. This can happen if:\n * 3.1. The summary this client loaded with has data from a different GC version.\n * 3.2. This client's latest summary was updated from a snapshot that has a different GC version.\n */\n public get summaryStateNeedsReset(): boolean {\n return this.initialStateNeedsReset ||\n (this.shouldRunGC && this.latestSummaryGCVersion !== this.currentGCVersion);\n }\n\n /**\n * Tracks if GC is enabled for this document. This is specified during document creation and doesn't change\n * throughout its lifetime.\n */\n private readonly gcEnabled: boolean;\n /**\n * Tracks if sweep phase is enabled for this document. This is specified during document creation and doesn't change\n * throughout its lifetime.\n */\n private readonly sweepEnabled: boolean;\n\n /**\n * Tracks if GC should run or not. Even if GC is enabled for a document (see gcEnabled), it can be explicitly\n * disabled via runtime options or feature flags.\n */\n public readonly shouldRunGC: boolean;\n /**\n * Tracks if sweep phase should run or not. Even if the sweep phase is enabled for a document (see sweepEnabled), it\n * can be explicitly disabled via feature flags. It also won't run if session expiry is not enabled.\n */\n private readonly shouldRunSweep: boolean;\n\n private readonly testMode: boolean;\n private readonly mc: MonitoringContext;\n\n /**\n * Tells whether the GC data should be written to the root of the summary tree.\n */\n private _writeDataAtRoot: boolean = false;\n public get writeDataAtRoot(): boolean {\n return this._writeDataAtRoot;\n }\n\n /**\n * Tells whether the initial GC state needs to be reset. This can happen under 2 conditions:\n * 1. The base snapshot contains GC state but GC is disabled. This will happen the first time GC is disabled after\n * it was enabled before. GC state needs to be removed from summary and all nodes should be marked referenced.\n * 2. The base snapshot does not have GC state but GC is enabled. This will happen the very first time GC runs on\n * a document and the first time GC is enabled after is was disabled before.\n *\n * Note that the state needs reset only for the very first time summary is generated by this client. After that, the\n * state will be up-to-date and this flag will be reset.\n */\n private initialStateNeedsReset: boolean = false;\n\n // The current GC version that this container is running.\n private readonly currentGCVersion = GCVersion;\n // This is the version of GC data in the latest summary being tracked.\n private latestSummaryGCVersion: GCVersion;\n\n // Keeps track of the GC state from the last run.\n private previousGCDataFromLastRun: IGarbageCollectionData | undefined;\n // Keeps a list of references (edges in the GC graph) between GC runs. Each entry has a node id and a list of\n // outbound routes from that node.\n private readonly newReferencesSinceLastRun: Map<string, string[]> = new Map();\n\n // Promise when resolved initializes the base state of the nodes from the base summary state.\n private readonly initializeBaseStateP: Promise<void>;\n // The map of data store ids to their GC details in the base summary returned in getDataStoreGCDetails().\n private readonly baseGCDetailsP: Promise<Map<string, IGarbageCollectionDetailsBase>>;\n // The time after which an unreferenced node can be deleted. Currently, we only set the node's state to expired.\n private readonly deleteTimeoutMs: number;\n // Map of node ids to their unreferenced state tracker.\n private readonly unreferencedNodesState: Map<string, UnreferencedStateTracker> = new Map();\n // The timeout responsible for closing the container when the session has expired\n private sessionExpiryTimer?: ReturnType<typeof setTimeout>;\n\n // Keeps track of unreferenced events that are logged for a node. This is used to limit the log generation to one\n // per event per node.\n private readonly loggedUnreferencedEvents: Set<string> = new Set();\n // Queue for unreferenced events that should be logged the next time GC runs.\n private readonly pendingEventsQueue: IUnreferencedEvent[] = [];\n\n protected constructor(\n private readonly runtime: IGarbageCollectionRuntime,\n private readonly gcOptions: IGCRuntimeOptions,\n /** For a given node path, returns the node's package path. */\n private readonly getNodePackagePath: (nodePath: string) => readonly string[] | undefined,\n /** Returns the timestamp of the last summary generated for this container. */\n private readonly getLastSummaryTimestampMs: () => number | undefined,\n baseSnapshot: ISnapshotTree | undefined,\n readAndParseBlob: ReadAndParseBlob,\n baseLogger: ITelemetryLogger,\n existing: boolean,\n metadata?: IContainerRuntimeMetadata,\n ) {\n this.mc = loggerToMonitoringContext(\n ChildLogger.create(baseLogger, \"GarbageCollector\"));\n\n this.deleteTimeoutMs = this.gcOptions.deleteTimeoutMs ?? defaultDeleteTimeoutMs;\n\n let prevSummaryGCVersion: number | undefined;\n\n /**\n * The following GC state is enabled during container creation and cannot be changed throughout its lifetime:\n * 1. Whether running GC mark phase is allowed or not.\n * 2. Whether running GC sweep phase is allowed or not.\n * 3. Whether GC session expiry is enabled or not.\n * For existing containers, we get this information from the metadata blob of its summary.\n */\n if (existing) {\n prevSummaryGCVersion = getGCVersion(metadata);\n // Existing documents which did not have metadata blob or had GC disabled have version as 0. For all\n // other existing documents, GC is enabled.\n this.gcEnabled = prevSummaryGCVersion > 0;\n this.sweepEnabled = metadata?.sweepEnabled ?? false;\n this.sessionExpiryTimeoutMs = metadata?.sessionExpiryTimeoutMs;\n } else {\n // Sweep should not be enabled without enabling GC mark phase. We could silently disable sweep in this\n // scenario but explicitly failing makes it clearer and promotes correct usage.\n if (gcOptions.sweepAllowed && !gcOptions.gcAllowed) {\n throw new UsageError(\"GC sweep phase cannot be enabled without enabling GC mark phase\");\n }\n\n // For new documents, GC has to be explicitly enabled via the flags in GC options.\n this.gcEnabled = gcOptions.gcAllowed === true;\n this.sweepEnabled = gcOptions.sweepAllowed === true;\n\n // Set the Session Expiry only if the flag is enabled or the test option is set.\n if (this.mc.config.getBoolean(runSessionExpiryKey) && this.gcEnabled) {\n this.sessionExpiryTimeoutMs = defaultSessionExpiryDurationMs;\n }\n }\n\n // If session expiry is enabled, we need to close the container when the timeout expires\n if (this.sessionExpiryTimeoutMs !== undefined\n && this.mc.config.getBoolean(disableSessionExpiryKey) !== true) {\n // If Test Override config is set, override Session Expiry timeout\n const overrideSessionExpiryTimeoutMs =\n this.mc.config.getNumber(\"Fluid.GarbageCollection.TestOverride.SessionExpiryMs\");\n if (overrideSessionExpiryTimeoutMs !== undefined) {\n this.sessionExpiryTimeoutMs = overrideSessionExpiryTimeoutMs;\n }\n\n const timeoutMs = this.sessionExpiryTimeoutMs;\n setLongTimeout(timeoutMs,\n () => {\n this.runtime.closeFn(new ClientSessionExpiredError(`Client session expired.`, timeoutMs));\n },\n (timer) => {\n this.sessionExpiryTimer = timer;\n });\n }\n\n // For existing document, the latest summary is the one that we loaded from. So, use its GC version as the\n // latest tracked GC version. For new documents, we will be writing the first summary with the current version.\n this.latestSummaryGCVersion = prevSummaryGCVersion ?? this.currentGCVersion;\n\n /**\n * Whether GC should run or not. The following conditions have to be met to run sweep:\n * 1. GC should be enabled for this container.\n * 2. GC should not be disabled via disableGC GC option.\n * These conditions can be overridden via runGCKey feature flag.\n */\n this.shouldRunGC = this.mc.config.getBoolean(runGCKey) ?? (\n // GC must be enabled for the document.\n this.gcEnabled\n // GC must not be disabled via GC options.\n && !gcOptions.disableGC\n );\n\n /**\n * Whether sweep should run or not. The following conditions have to be met to run sweep:\n * 1. Overall GC or mark phase must be enabled (this.shouldRunGC).\n * 2. Session expiry and sweep should be enabled for this container. Without session expiry we cannot safely\n * delete unreferenced objects. This condition (#2) can be overridden via runSweepKey feature flag.\n */\n this.shouldRunSweep = this.shouldRunGC && (\n this.mc.config.getBoolean(runSweepKey) ?? (this.sessionExpiryTimeoutMs !== undefined && this.sweepEnabled)\n );\n\n // Whether we are running in test mode. In this mode, unreferenced nodes are immediately deleted.\n this.testMode = this.mc.config.getBoolean(gcTestModeKey) ?? gcOptions.runGCInTestMode === true;\n\n /**\n * Enable resetting initial state once the following issue is resolved:\n * https://github.com/microsoft/FluidFramework/issues/8878.\n * Currently, the GC tree is not written at root, so we don't know if the base snapshot contains GC tree or not.\n */\n // The GC state needs to be reset if the base snapshot contains GC tree and GC is disabled or it doesn't contain\n // GC tree and GC is enabled.\n // const gcTreePresent = baseSnapshot?.trees[gcTreeKey] !== undefined;\n // this.initialStateNeedsReset = gcTreePresent ? !this.shouldRunGC : this.shouldRunGC;\n\n // If `writeDataAtRoot` setting is true, write the GC data into the root of the summary tree. We do this so that\n // the roll out can be staged. Once its rolled out everywhere, we will start writing at root by default.\n this._writeDataAtRoot = this.mc.config.getBoolean(writeAtRootKey) ?? this.gcOptions.writeDataAtRoot === true;\n\n // Get the GC state from the GC blob in the base snapshot. Use LazyPromise because we only want to do\n // this once since it involves fetching blobs from storage which is expensive.\n const baseSummaryStateP = new LazyPromise<IGarbageCollectionState | undefined>(async () => {\n if (baseSnapshot === undefined) {\n return undefined;\n }\n\n // For newer documents, GC data should be present in the GC tree in the root of the snapshot.\n const gcSnapshotTree = baseSnapshot.trees[gcTreeKey];\n if (gcSnapshotTree !== undefined) {\n // If the GC tree is written at root, we should also do the same.\n this._writeDataAtRoot = true;\n return getGCStateFromSnapshot(gcSnapshotTree, readAndParseBlob);\n }\n\n // back-compat - Older documents will have the GC blobs in each data store's summary tree. Get them and\n // consolidate into IGarbageCollectionState format.\n // Add a node for the root node that is not present in older snapshot format.\n const gcState: IGarbageCollectionState = { gcNodes: { \"/\": { outboundRoutes: [] } } };\n const dataStoreSnapshotTree = getSummaryForDatastores(baseSnapshot, metadata);\n assert(dataStoreSnapshotTree !== undefined,\n 0x2a8 /* \"Expected data store snapshot tree in base snapshot\" */);\n for (const [dsId, dsSnapshotTree] of Object.entries(dataStoreSnapshotTree.trees)) {\n const blobId = dsSnapshotTree.blobs[gcBlobKey];\n if (blobId === undefined) {\n continue;\n }\n\n const gcSummaryDetails = await readAndParseBlob<IGarbageCollectionDetailsBase>(blobId);\n // If there are no nodes for this data store, skip it.\n if (gcSummaryDetails.gcData?.gcNodes === undefined) {\n continue;\n }\n\n const dsRootId = `/${dsId}`;\n // Since we used to write GC data at data store level, we won't have an entry for the root (\"/\").\n // Construct that entry by adding root data store ids to its outbound routes.\n const initialSnapshotDetails = await readAndParseBlob<ReadFluidDataStoreAttributes>(\n dsSnapshotTree.blobs[dataStoreAttributesBlobName],\n );\n if (initialSnapshotDetails.isRootDataStore) {\n gcState.gcNodes[\"/\"].outboundRoutes.push(dsRootId);\n }\n\n for (const [id, outboundRoutes] of Object.entries(gcSummaryDetails.gcData.gcNodes)) {\n // Prefix the data store id to the GC node ids to make them relative to the root from being\n // relative to the data store. Similar to how its done in DataStore::getGCData.\n const rootId = id === \"/\" ? dsRootId : `${dsRootId}${id}`;\n gcState.gcNodes[rootId] = { outboundRoutes: Array.from(outboundRoutes) };\n }\n assert(gcState.gcNodes[dsRootId] !== undefined,\n 0x2a9 /* `GC nodes for data store ${dsId} not in GC blob` */);\n gcState.gcNodes[dsRootId].unreferencedTimestampMs = gcSummaryDetails.unrefTimestamp;\n }\n\n // If there is only one node (root node just added above), either GC is disabled or we are loading from the\n // very first summary generated by detached container. In both cases, GC was not run - return undefined.\n return Object.keys(gcState.gcNodes).length === 1 ? undefined : gcState;\n });\n\n /**\n * Set up the initializer which initializes the base GC state from the base snapshot. Note that the reference\n * timestamp maybe from old ops which were not summarized and stored in the file. So, the unreferenced state\n * may be out of date. This is fine because the state is updated every time GC runs based on the time then.\n */\n this.initializeBaseStateP = new LazyPromise<void>(async () => {\n const currentReferenceTimestampMs = this.runtime.getCurrentReferenceTimestampMs();\n const baseState = await baseSummaryStateP;\n if (baseState === undefined) {\n return;\n }\n\n const gcNodes: { [ id: string ]: string[]; } = {};\n for (const [nodeId, nodeData] of Object.entries(baseState.gcNodes)) {\n if (nodeData.unreferencedTimestampMs !== undefined) {\n this.unreferencedNodesState.set(\n nodeId,\n new UnreferencedStateTracker(\n nodeData.unreferencedTimestampMs,\n this.deleteTimeoutMs,\n currentReferenceTimestampMs,\n ),\n );\n }\n gcNodes[nodeId] = Array.from(nodeData.outboundRoutes);\n }\n this.previousGCDataFromLastRun = { gcNodes };\n });\n\n // Get the GC details for each node from the GC state in the base summary. This is returned in getBaseGCDetails\n // which the caller uses to initialize each node's GC state.\n this.baseGCDetailsP = new LazyPromise<Map<string, IGarbageCollectionDetailsBase>>(async () => {\n const baseState = await baseSummaryStateP;\n if (baseState === undefined) {\n return new Map();\n }\n\n const gcNodes: { [ id: string ]: string[]; } = {};\n for (const [nodeId, nodeData] of Object.entries(baseState.gcNodes)) {\n gcNodes[nodeId] = Array.from(nodeData.outboundRoutes);\n }\n // Run GC on the nodes in the base summary to get the routes used in each node in the container.\n // This is an optimization for space (vs performance) wherein we don't need to store the used routes of\n // each node in the summary.\n const usedRoutes = runGarbageCollection(\n gcNodes,\n [\"/\"],\n this.mc.logger,\n ).referencedNodeIds;\n\n const baseGCDetailsMap = unpackChildNodesGCDetails({ gcData: { gcNodes }, usedRoutes });\n // Currently, the nodes may write the GC data. So, we need to update it's base GC details with the\n // unreferenced timestamp. Once we start writing the GC data here, we won't need to do this anymore.\n for (const [nodeId, nodeData] of Object.entries(baseState.gcNodes)) {\n if (nodeData.unreferencedTimestampMs !== undefined) {\n const dataStoreGCDetails = baseGCDetailsMap.get(nodeId.slice(1));\n if (dataStoreGCDetails !== undefined) {\n dataStoreGCDetails.unrefTimestamp = nodeData.unreferencedTimestampMs;\n }\n }\n }\n return baseGCDetailsMap;\n });\n\n // Initialize the base state. The base GC data is used to detect and log when inactive / deleted objects are\n // used in the container.\n if (this.shouldRunGC) {\n this.initializeBaseStateP.catch((error) => {\n const dpe = DataProcessingError.wrapIfUnrecognized(\n error,\n \"FailedToInitializeGC\",\n );\n dpe.addTelemetryProperties({\n gcEnabled: this.gcEnabled,\n runSweep: this.shouldRunSweep,\n writeAtRoot: this._writeDataAtRoot,\n testMode: this.testMode,\n sessionExpiry: this.sessionExpiryTimeoutMs,\n });\n throw dpe;\n });\n }\n }\n\n /**\n * Runs garbage collection and updates the reference / used state of the nodes in the container.\n * @returns the number of data stores that have been marked as unreferenced.\n */\n public async collectGarbage(\n options: {\n /** Logger to use for logging GC events */\n logger?: ITelemetryLogger;\n /** True to run GC sweep phase after the mark phase */\n runSweep?: boolean;\n /** True to generate full GC data */\n fullGC?: boolean;\n },\n ): Promise<IGCStats> {\n const {\n logger = this.mc.logger,\n runSweep = this.shouldRunSweep,\n fullGC = this.gcOptions.runFullGC === true || this.summaryStateNeedsReset,\n } = options;\n\n return PerformanceEvent.timedExecAsync(logger, { eventName: \"GarbageCollection\" }, async (event) => {\n await this.initializeBaseStateP;\n\n // Let the runtime update its pending state before GC runs.\n await this.runtime.updateStateBeforeGC();\n\n // Get the runtime's GC data and run GC on the reference graph in it.\n const gcData = await this.runtime.getGCData(fullGC);\n const gcResult = runGarbageCollection(\n gcData.gcNodes,\n [\"/\"],\n logger,\n );\n const gcStats = this.generateStatsAndLogEvents(gcResult, logger);\n\n // Update the state since the last GC run. There can be nodes that were referenced between the last and\n // the current run. We need to identify than and update their unreferenced state if needed.\n this.updateStateSinceLastRun(gcData, logger);\n\n // Update the current state of the system based on the GC run.\n const currentReferenceTimestampMs = this.runtime.getCurrentReferenceTimestampMs();\n this.updateCurrentState(gcData, gcResult, currentReferenceTimestampMs);\n\n this.runtime.updateUsedRoutes(gcResult.referencedNodeIds, currentReferenceTimestampMs);\n\n if (runSweep) {\n // Placeholder for running sweep logic.\n }\n\n // If we are running in GC test mode, delete objects for unused routes. This enables testing scenarios\n // involving access to deleted data.\n if (this.testMode) {\n this.runtime.deleteUnusedRoutes(gcResult.deletedNodeIds);\n }\n event.end({ ...gcStats });\n return gcStats;\n },\n { end: true, cancel: \"error\" });\n }\n\n /**\n * Summarizes the GC data and returns it as a summary tree.\n * We current write the entire GC state in a single blob. This can be modified later to write multiple\n * blobs. All the blob keys should start with `gcBlobPrefix`.\n */\n public summarize(): ISummaryTreeWithStats | undefined {\n if (!this.shouldRunGC || this.previousGCDataFromLastRun === undefined) {\n return;\n }\n\n const gcState: IGarbageCollectionState = { gcNodes: {} };\n for (const [nodeId, outboundRoutes] of Object.entries(this.previousGCDataFromLastRun.gcNodes)) {\n gcState.gcNodes[nodeId] = {\n outboundRoutes,\n unreferencedTimestampMs: this.unreferencedNodesState.get(nodeId)?.unreferencedTimestampMs,\n };\n }\n\n const builder = new SummaryTreeBuilder();\n builder.addBlob(`${gcBlobPrefix}_root`, JSON.stringify(gcState));\n return builder.getSummaryTree();\n }\n\n public getMetadata(): IGCMetadata {\n return {\n /**\n * If GC is enabled, the GC data is written using the current GC version and that is the gcFeature that goes\n * into the metadata blob. If GC is disabled, the gcFeature is 0.\n */\n gcFeature: this.gcEnabled ? this.currentGCVersion : 0,\n sessionExpiryTimeoutMs: this.sessionExpiryTimeoutMs,\n sweepEnabled: this.sweepEnabled,\n };\n }\n\n /**\n * Returns a map of node ids to their base GC details generated from the base summary. This is used by the caller\n * to initialize the GC state of the nodes.\n */\n public async getBaseGCDetails(): Promise<Map<string, IGarbageCollectionDetailsBase>> {\n return this.baseGCDetailsP;\n }\n\n /**\n * Called when the latest summary of the system has been refreshed. This will be used to update the state of the\n * latest summary tracked.\n */\n public async latestSummaryStateRefreshed(\n result: RefreshSummaryResult,\n readAndParseBlob: ReadAndParseBlob,\n ): Promise<void> {\n // After a summary is successfully submitted and ack'd by this client, the GC state should have been reset in\n // the summary and doesn't need to be reset anymore.\n this.initialStateNeedsReset = false;\n\n if (!this.shouldRunGC || !result.latestSummaryUpdated) {\n return;\n }\n\n // If the summary was tracked by this client, it was the one that generated the summary in the first place.\n // Basically, it was written in the current GC version.\n if (result.wasSummaryTracked) {\n this.latestSummaryGCVersion = this.currentGCVersion;\n return;\n }\n // If the summary was not tracked by this client, update latest GC version from the snapshot in the result as\n // that is now the latest summary.\n await this.updateSummaryGCVersionFromSnapshot(result.snapshot, readAndParseBlob);\n }\n\n /**\n * Called when a node with the given id is updated. If the node is inactive, log an error.\n * @param nodePath - The id of the node that changed.\n * @param reason - Whether the node was loaded or changed.\n * @param timestampMs - The timestamp when the node changed.\n * @param packagePath - The package path of the node. This may not be available if the node hasn't been loaded yet.\n * @param requestHeaders - If the node was loaded via request path, the headers in the request.\n */\n public nodeUpdated(\n nodePath: string,\n reason: \"Loaded\" | \"Changed\",\n timestampMs?: number,\n packagePath?: readonly string[],\n requestHeaders?: IRequestHeader,\n ) {\n if (!this.shouldRunGC) {\n return;\n }\n\n this.logIfInactive(\n reason,\n nodePath,\n timestampMs,\n packagePath,\n requestHeaders,\n );\n }\n\n /**\n * Called when an outbound reference is added to a node. This is used to identify all nodes that have been\n * referenced between summaries so that their unreferenced timestamp can be reset.\n *\n * @param fromNodePath - The node from which the reference is added.\n * @param toNodePath - The node to which the reference is added.\n */\n public addedOutboundReference(fromNodePath: string, toNodePath: string) {\n if (!this.shouldRunGC) {\n return;\n }\n\n const outboundRoutes = this.newReferencesSinceLastRun.get(fromNodePath) ?? [];\n outboundRoutes.push(toNodePath);\n this.newReferencesSinceLastRun.set(fromNodePath, outboundRoutes);\n\n // If the node that got referenced is inactive, log an event as that may indicate use-after-delete.\n this.logIfInactive(\n \"Revived\",\n toNodePath,\n );\n }\n\n public dispose(): void {\n if (this.sessionExpiryTimer !== undefined) {\n clearTimeout(this.sessionExpiryTimer);\n this.sessionExpiryTimer = undefined;\n }\n }\n\n /**\n * Update the latest summary GC version from the metadata blob in the given snapshot.\n */\n private async updateSummaryGCVersionFromSnapshot(snapshot: ISnapshotTree, readAndParseBlob: ReadAndParseBlob) {\n const metadataBlobId = snapshot.blobs[metadataBlobName];\n if (metadataBlobId) {\n const metadata = await readAndParseBlob<IContainerRuntimeMetadata>(metadataBlobId);\n this.latestSummaryGCVersion = getGCVersion(metadata);\n }\n }\n\n /**\n * Updates the state of the system as per the current GC run. It does the following:\n * 1. Sets up the current GC state as per the gcData.\n * 2. Starts tracking for nodes that have become unreferenced in this run.\n * 3. Clears tracking for nodes that were unreferenced but became referenced in this run.\n * @param gcData - The data representing the reference graph on which GC is run.\n * @param gcResult - The result of the GC run on the gcData.\n * @param currentReferenceTimestampMs - The timestamp to be used for unreferenced nodes' timestamp.\n */\n private updateCurrentState(\n gcData: IGarbageCollectionData,\n gcResult: IGCResult,\n currentReferenceTimestampMs?: number,\n ) {\n this.previousGCDataFromLastRun = cloneGCData(gcData);\n this.newReferencesSinceLastRun.clear();\n\n // Iterate through the referenced nodes and stop tracking if they were unreferenced before.\n for (const nodeId of gcResult.referencedNodeIds) {\n const nodeStateTracker = this.unreferencedNodesState.get(nodeId);\n if (nodeStateTracker !== undefined) {\n // Stop tracking so as to clear out any running timers.\n nodeStateTracker.stopTracking();\n // Delete the node as we don't need to track it any more.\n this.unreferencedNodesState.delete(nodeId);\n }\n }\n\n /**\n * If there is no current reference time, skip tracking when a node becomes unreferenced. This would happen\n * if no ops have been processed ever and we still try to run GC. If so, there is nothing interesting to track\n * anyway.\n */\n if (currentReferenceTimestampMs === undefined) {\n return;\n }\n\n /**\n * If a node became unreferenced in this run, start tracking it.\n * If a node was already unreferenced, update its tracking information. Since the current reference time is\n * from the ops seen, this will ensure that we keep updating the unreferenced state as time moves forward.\n */\n for (const nodeId of gcResult.deletedNodeIds) {\n const nodeStateTracker = this.unreferencedNodesState.get(nodeId);\n if (nodeStateTracker === undefined) {\n this.unreferencedNodesState.set(\n nodeId,\n new UnreferencedStateTracker(\n currentReferenceTimestampMs,\n this.deleteTimeoutMs,\n currentReferenceTimestampMs,\n ),\n );\n } else {\n nodeStateTracker.updateTracking(currentReferenceTimestampMs);\n }\n }\n }\n\n /**\n * Since GC runs periodically, the GC data that is generated only tells us the state of the world at that point in\n * time. It's possible that nodes transition from `unreferenced -> referenced -> unreferenced` between two runs. The\n * unreferenced timestamp of such nodes needs to be reset as they may have been accessed when they were referenced.\n *\n * This function identifies nodes that were referenced since last run and removes their unreferenced state, if any.\n * If these nodes are currently unreferenced, they will be assigned new unreferenced state by the current run.\n */\n private updateStateSinceLastRun(currentGCData: IGarbageCollectionData, logger: ITelemetryLogger) {\n // If we haven't run GC before there is nothing to do.\n if (this.previousGCDataFromLastRun === undefined) {\n return;\n }\n\n // Find any references that haven't been identified correctly.\n const missingExplicitReferences = this.findMissingExplicitReferences(\n currentGCData,\n this.previousGCDataFromLastRun,\n this.newReferencesSinceLastRun,\n );\n\n // The following log will be enabled once this issue is resolved:\n // https://github.com/microsoft/FluidFramework/issues/8878.\n if (this.mc.config.getBoolean(logUnknownOutboundReferencesKey) === true\n && missingExplicitReferences.length > 0) {\n missingExplicitReferences.forEach((missingExplicitReference) => {\n const event: ITelemetryPerformanceEvent = {\n eventName: \"gcUnknownOutboundReferences\",\n gcNodeId: missingExplicitReference[0],\n gcRoutes: JSON.stringify(missingExplicitReference[1]),\n };\n logger.sendPerformanceEvent(event);\n });\n }\n\n // No references were added since the last run so we don't have to update reference states of any unreferenced\n // nodes\n if (this.newReferencesSinceLastRun.size === 0) {\n return;\n }\n\n /**\n * Generate a super set of the GC data that contains the nodes and edges from last run, plus any new node and\n * edges that have been added since then. To do this, combine the GC data from the last run and the current\n * run, and then add the references since last run.\n *\n * Note on why we need to combine the data from previous run, current run and all references in between -\n * 1. We need data from last run because some of its references may have been deleted since then. If those\n * references added new outbound references before getting deleted, we need to detect them.\n * 2. We need new outbound references since last run because some of them may have been deleted later. If those\n * references added new outbound references before getting deleted, we need to detect them.\n * 3. We need data from the current run because currently we may not detect when DDSs are referenced:\n * - We don't require DDSs handles to be stored in a referenced DDS. For this, we need GC at DDS level\n * which is tracked by https://github.com/microsoft/FluidFramework/issues/8470.\n * - A new data store may have \"root\" DDSs already created and we don't detect them today.\n */\n const gcDataSuperSet = concatGarbageCollectionData(this.previousGCDataFromLastRun, currentGCData);\n this.newReferencesSinceLastRun.forEach((outboundRoutes: string[], sourceNodeId: string) => {\n if (gcDataSuperSet.gcNodes[sourceNodeId] === undefined) {\n gcDataSuperSet.gcNodes[sourceNodeId] = outboundRoutes;\n } else {\n gcDataSuperSet.gcNodes[sourceNodeId].push(...outboundRoutes);\n }\n });\n\n /**\n * Run GC on the above reference graph to find all nodes that are referenced. For each one, if they are\n * unreferenced, stop tracking them and remove from unreferenced list.\n * Some of these nodes may be unreferenced now and if so, the current run will add unreferenced state for them.\n */\n const gcResult = runGarbageCollection(gcDataSuperSet.gcNodes, [\"/\"], logger);\n for (const nodeId of gcResult.referencedNodeIds) {\n const nodeStateTracker = this.unreferencedNodesState.get(nodeId);\n if (nodeStateTracker !== undefined) {\n // Stop tracking so as to clear out any running timers.\n nodeStateTracker.stopTracking();\n // Delete the node as we don't need to track it any more.\n this.unreferencedNodesState.delete(nodeId);\n }\n }\n }\n\n /**\n * Finds all new references or outbound routes in the current graph that haven't been explicitly notified to GC.\n * The principle is that every new reference or outbound route must be notified to GC via the\n * addedOutboundReference method. It it hasn't, its a bug and we want to identify these scenarios.\n *\n * In more simple terms:\n * Missing Explicit References = Current References - Previous References - Explicitly Added References;\n *\n * @param currentGCData - The GC data (reference graph) from the current GC run.\n * @param previousGCData - The GC data (reference graph) from the previous GC run.\n * @param explicitReferences - New references added explicity between the previous and the current run.\n * @returns - a list of missing explicit references\n */\n private findMissingExplicitReferences(\n currentGCData: IGarbageCollectionData,\n previousGCData: IGarbageCollectionData,\n explicitReferences: Map<string, string[]>,\n ): [string, string[]][] {\n assert(\n previousGCData !== undefined,\n 0x2b7, /* \"Can't validate correctness without GC data from last run\" */\n );\n\n const currentGraph = Object.entries(currentGCData.gcNodes);\n const missingExplicitReferences: [string, string[]][] = [];\n currentGraph.forEach(([nodeId, currentOutboundRoutes]) => {\n const previousRoutes = previousGCData.gcNodes[nodeId] ?? [];\n const explicitRoutes = explicitReferences.get(nodeId) ?? [];\n const missingExplicitRoutes: string[] = [];\n currentOutboundRoutes.forEach((route) => {\n const isBlobOrDataStoreRoute =\n this.runtime.getNodeType(route) === GCNodeType.Blob ||\n this.runtime.getNodeType(route) === GCNodeType.DataStore;\n // Ignore implicitly added DDS routes to their parent datastores\n const notRouteFromDDSToParentDataStore = !nodeId.startsWith(route);\n if (\n isBlobOrDataStoreRoute &&\n notRouteFromDDSToParentDataStore &&\n (!previousRoutes.includes(route) && !explicitRoutes.includes(route))\n ) {\n missingExplicitRoutes.push(route);\n }\n });\n if (missingExplicitRoutes.length > 0) {\n missingExplicitReferences.push([nodeId, missingExplicitRoutes]);\n }\n });\n\n // Ideally missingExplicitReferences should always have a size 0\n return missingExplicitReferences;\n }\n\n /**\n * Generates the stats of a garbage collection run from the given results of the run. Also, logs any pending events\n * in the pendingEventsQueue. This should be called before updating the current state because it generates stats\n * based on previous state of the system.\n * @param gcResult - The result of a GC run.\n * @returns the GC stats of the GC run.\n */\n private generateStatsAndLogEvents(gcResult: IGCResult, logger: ITelemetryLogger): IGCStats {\n // Log pending events for unreferenced nodes after GC has run. We should have the package data available for\n // them now since the GC run should have loaded these nodes.\n let event = this.pendingEventsQueue.shift();\n while (event !== undefined) {\n const pkg = this.getNodePackagePath(event.id);\n logger.sendErrorEvent({\n ...event,\n pkg: pkg ? { value: `/${pkg.join(\"/\")}`, tag: TelemetryDataTag.PackageData } : undefined,\n });\n event = this.pendingEventsQueue.shift();\n }\n\n const gcStats: IGCStats = {\n nodeCount: 0,\n dataStoreCount: 0,\n attachmentBlobCount: 0,\n unrefNodeCount: 0,\n unrefDataStoreCount: 0,\n unrefAttachmentBlobCount: 0,\n updatedNodeCount: 0,\n updatedDataStoreCount: 0,\n updatedAttachmentBlobCount: 0,\n };\n\n const updateNodeStats = (nodeId: string, referenced: boolean) => {\n gcStats.nodeCount++;\n /**\n * `this.unreferencedNodesState` has the previous unreferenced state of all nodes. `referenced` flag passed\n * here is current state of the give node. Check if the reference state of the changed.\n */\n const stateUpdated = this.unreferencedNodesState.has(nodeId) ? referenced : !referenced;\n if (stateUpdated) {\n gcStats.updatedNodeCount++;\n }\n if (!referenced) {\n gcStats.unrefNodeCount++;\n }\n\n if (this.runtime.getNodeType(nodeId) === GCNodeType.DataStore) {\n gcStats.dataStoreCount++;\n if (stateUpdated) {\n gcStats.updatedDataStoreCount++;\n }\n if (!referenced) {\n gcStats.unrefDataStoreCount++;\n }\n }\n if (this.runtime.getNodeType(nodeId) === GCNodeType.Blob) {\n gcStats.attachmentBlobCount++;\n if (stateUpdated) {\n gcStats.updatedAttachmentBlobCount++;\n }\n if (!referenced) {\n gcStats.unrefAttachmentBlobCount++;\n }\n }\n };\n\n for (const nodeId of gcResult.referencedNodeIds) {\n updateNodeStats(nodeId, true /* referenced */);\n }\n\n for (const nodeId of gcResult.deletedNodeIds) {\n updateNodeStats(nodeId, false /* referenced */);\n }\n\n return gcStats;\n }\n\n /**\n * Logs an event if a node is inactive and is used.\n */\n private logIfInactive(\n eventSuffix: \"Changed\" | \"Loaded\" | \"Revived\",\n nodeId: string,\n currentReferenceTimestampMs = this.runtime.getCurrentReferenceTimestampMs(),\n packagePath?: readonly string[],\n requestHeaders?: IRequestHeader,\n ) {\n // If there is no reference timestamp to work with, no ops have been processed after creation. If so, skip\n // logging as nothing interesting would have happened worth logging.\n if (currentReferenceTimestampMs === undefined) {\n return;\n }\n\n const eventName = `inactiveObject_${eventSuffix}`;\n // We log a particular event for a given node only once so that it is not too noisy.\n const uniqueEventId = `${nodeId}-${eventName}`;\n const nodeState = this.unreferencedNodesState.get(nodeId);\n if (nodeState?.inactive && !this.loggedUnreferencedEvents.has(uniqueEventId)) {\n this.loggedUnreferencedEvents.add(uniqueEventId);\n const event: IUnreferencedEvent = {\n eventName,\n id: nodeId,\n age: currentReferenceTimestampMs - nodeState.unreferencedTimestampMs,\n timeout: this.deleteTimeoutMs,\n lastSummaryTime: this.getLastSummaryTimestampMs(),\n externalRequest: requestHeaders?.[RuntimeHeaders.externalRequest],\n viaHandle: requestHeaders?.[RuntimeHeaders.viaHandle],\n };\n\n // If the package data for the node exists, log immediately. Otherwise, queue it and it will be logged the\n // next time GC runs as the package data should be available then.\n const pkg = packagePath ?? this.getNodePackagePath(nodeId);\n if (pkg !== undefined) {\n this.mc.logger.sendErrorEvent({\n ...event,\n pkg: { value: `/${pkg.join(\"/\")}`, tag: TelemetryDataTag.PackageData },\n });\n } else {\n this.pendingEventsQueue.push(event);\n }\n }\n }\n}\n\n/**\n * Gets the garbage collection state from the given snapshot tree. The GC state may be written into multiple blobs.\n * Merge the GC state from all such blobs and return the merged GC state.\n */\nasync function getGCStateFromSnapshot(\n gcSnapshotTree: ISnapshotTree,\n readAndParseBlob: ReadAndParseBlob,\n): Promise<IGarbageCollectionState> {\n let rootGCState: IGarbageCollectionState = { gcNodes: {} };\n for (const key of Object.keys(gcSnapshotTree.blobs)) {\n // Skip blobs that do not start with the GC prefix.\n if (!key.startsWith(gcBlobPrefix)) {\n continue;\n }\n\n const blobId = gcSnapshotTree.blobs[key];\n if (blobId === undefined) {\n continue;\n }\n const gcState = await readAndParseBlob<IGarbageCollectionState>(blobId);\n assert(gcState !== undefined, 0x2ad /* \"GC blob missing from snapshot\" */);\n // Merge the GC state of this blob into the root GC state.\n rootGCState = concatGarbageCollectionStates(rootGCState, gcState);\n }\n return rootGCState;\n}\n\n/**\n * setLongTimeout is used for timeouts longer than setTimeout's ~24.8 day max\n * @param timeoutMs - the total time the timeout needs to last in ms\n * @param timeoutFn - the function to execute when the timer ends\n * @param setTimerFn - the function used to update your timer variable\n */\nfunction setLongTimeout(\n timeoutMs: number,\n timeoutFn: () => void,\n setTimerFn: (timer: ReturnType<typeof setTimeout>) => void,\n) {\n // The setTimeout max is 24.8 days before looping occurs.\n const maxTimeout = 2147483647;\n let timer: ReturnType<typeof setTimeout>;\n if (timeoutMs > maxTimeout) {\n const newTimeoutMs = timeoutMs - maxTimeout;\n timer = setTimeout(() => setLongTimeout(newTimeoutMs, timeoutFn, setTimerFn), maxTimeout);\n } else {\n timer = setTimeout(() => timeoutFn(), timeoutMs);\n }\n setTimerFn(timer);\n}\n"]}