@fluidframework/container-runtime 2.0.0-dev-rc.5.0.0.271717 → 2.0.0-dev-rc.5.0.0.272889

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 (257) hide show
  1. package/api-extractor/api-extractor-lint-bundle.json +5 -0
  2. package/api-extractor/api-extractor-lint-legacy.cjs.json +5 -0
  3. package/api-extractor/api-extractor-lint-legacy.esm.json +5 -0
  4. package/api-extractor/api-extractor-lint-public.cjs.json +5 -0
  5. package/api-extractor/api-extractor-lint-public.esm.json +5 -0
  6. package/api-extractor.json +1 -1
  7. package/api-report/container-runtime.alpha.api.md +1 -1
  8. package/container-runtime.test-files.tar +0 -0
  9. package/dist/batchTracker.d.ts.map +1 -1
  10. package/dist/batchTracker.js.map +1 -1
  11. package/dist/blobManager.d.ts.map +1 -1
  12. package/dist/blobManager.js.map +1 -1
  13. package/dist/channelCollection.d.ts +12 -2
  14. package/dist/channelCollection.d.ts.map +1 -1
  15. package/dist/channelCollection.js +86 -90
  16. package/dist/channelCollection.js.map +1 -1
  17. package/dist/connectionTelemetry.d.ts.map +1 -1
  18. package/dist/connectionTelemetry.js.map +1 -1
  19. package/dist/containerRuntime.d.ts +2 -1
  20. package/dist/containerRuntime.d.ts.map +1 -1
  21. package/dist/containerRuntime.js +29 -9
  22. package/dist/containerRuntime.js.map +1 -1
  23. package/dist/dataStoreContext.d.ts +2 -1
  24. package/dist/dataStoreContext.d.ts.map +1 -1
  25. package/dist/dataStoreContext.js +5 -3
  26. package/dist/dataStoreContext.js.map +1 -1
  27. package/dist/dataStoreContexts.d.ts.map +1 -1
  28. package/dist/dataStoreContexts.js.map +1 -1
  29. package/dist/deltaManagerProxies.d.ts.map +1 -1
  30. package/dist/deltaManagerProxies.js.map +1 -1
  31. package/dist/deltaScheduler.d.ts.map +1 -1
  32. package/dist/deltaScheduler.js +1 -3
  33. package/dist/deltaScheduler.js.map +1 -1
  34. package/dist/gc/garbageCollection.d.ts +4 -2
  35. package/dist/gc/garbageCollection.d.ts.map +1 -1
  36. package/dist/gc/garbageCollection.js +12 -8
  37. package/dist/gc/garbageCollection.js.map +1 -1
  38. package/dist/gc/gcConfigs.js +1 -1
  39. package/dist/gc/gcConfigs.js.map +1 -1
  40. package/dist/gc/gcDefinitions.d.ts +7 -4
  41. package/dist/gc/gcDefinitions.d.ts.map +1 -1
  42. package/dist/gc/gcDefinitions.js.map +1 -1
  43. package/dist/gc/gcHelpers.d.ts.map +1 -1
  44. package/dist/gc/gcHelpers.js.map +1 -1
  45. package/dist/gc/gcSummaryStateTracker.d.ts.map +1 -1
  46. package/dist/gc/gcSummaryStateTracker.js.map +1 -1
  47. package/dist/gc/gcTelemetry.d.ts +1 -1
  48. package/dist/gc/gcTelemetry.d.ts.map +1 -1
  49. package/dist/gc/gcTelemetry.js +1 -7
  50. package/dist/gc/gcTelemetry.js.map +1 -1
  51. package/dist/index.d.ts +1 -1
  52. package/dist/index.d.ts.map +1 -1
  53. package/dist/index.js.map +1 -1
  54. package/dist/messageTypes.d.ts +5 -21
  55. package/dist/messageTypes.d.ts.map +1 -1
  56. package/dist/messageTypes.js.map +1 -1
  57. package/dist/opLifecycle/index.d.ts +1 -1
  58. package/dist/opLifecycle/index.d.ts.map +1 -1
  59. package/dist/opLifecycle/index.js.map +1 -1
  60. package/dist/opLifecycle/opDecompressor.d.ts.map +1 -1
  61. package/dist/opLifecycle/opDecompressor.js +1 -2
  62. package/dist/opLifecycle/opDecompressor.js.map +1 -1
  63. package/dist/opLifecycle/opGroupingManager.d.ts.map +1 -1
  64. package/dist/opLifecycle/opGroupingManager.js.map +1 -1
  65. package/dist/opLifecycle/opSplitter.d.ts.map +1 -1
  66. package/dist/opLifecycle/opSplitter.js +1 -1
  67. package/dist/opLifecycle/opSplitter.js.map +1 -1
  68. package/dist/opLifecycle/outbox.d.ts.map +1 -1
  69. package/dist/opLifecycle/outbox.js.map +1 -1
  70. package/dist/packageVersion.d.ts +1 -1
  71. package/dist/packageVersion.js +1 -1
  72. package/dist/packageVersion.js.map +1 -1
  73. package/dist/pendingStateManager.d.ts.map +1 -1
  74. package/dist/pendingStateManager.js +1 -1
  75. package/dist/pendingStateManager.js.map +1 -1
  76. package/dist/summary/documentSchema.d.ts.map +1 -1
  77. package/dist/summary/documentSchema.js +1 -2
  78. package/dist/summary/documentSchema.js.map +1 -1
  79. package/dist/summary/index.d.ts +1 -1
  80. package/dist/summary/index.d.ts.map +1 -1
  81. package/dist/summary/index.js.map +1 -1
  82. package/dist/summary/orderedClientElection.d.ts.map +1 -1
  83. package/dist/summary/orderedClientElection.js.map +1 -1
  84. package/dist/summary/runningSummarizer.d.ts.map +1 -1
  85. package/dist/summary/runningSummarizer.js +1 -2
  86. package/dist/summary/runningSummarizer.js.map +1 -1
  87. package/dist/summary/summarizerClientElection.d.ts.map +1 -1
  88. package/dist/summary/summarizerClientElection.js.map +1 -1
  89. package/dist/summary/summarizerNode/summarizerNode.d.ts.map +1 -1
  90. package/dist/summary/summarizerNode/summarizerNode.js.map +1 -1
  91. package/dist/summary/summarizerNode/summarizerNodeUtils.d.ts.map +1 -1
  92. package/dist/summary/summarizerNode/summarizerNodeUtils.js.map +1 -1
  93. package/dist/summary/summarizerNode/summarizerNodeWithGc.d.ts.map +1 -1
  94. package/dist/summary/summarizerNode/summarizerNodeWithGc.js +4 -1
  95. package/dist/summary/summarizerNode/summarizerNodeWithGc.js.map +1 -1
  96. package/dist/summary/summarizerTypes.d.ts.map +1 -1
  97. package/dist/summary/summarizerTypes.js.map +1 -1
  98. package/dist/summary/summaryCollection.d.ts.map +1 -1
  99. package/dist/summary/summaryCollection.js.map +1 -1
  100. package/dist/summary/summaryFormat.d.ts.map +1 -1
  101. package/dist/summary/summaryFormat.js.map +1 -1
  102. package/dist/summary/summaryGenerator.d.ts.map +1 -1
  103. package/dist/summary/summaryGenerator.js +1 -2
  104. package/dist/summary/summaryGenerator.js.map +1 -1
  105. package/dist/summary/summaryManager.d.ts.map +1 -1
  106. package/dist/summary/summaryManager.js +1 -2
  107. package/dist/summary/summaryManager.js.map +1 -1
  108. package/dist/throttler.d.ts.map +1 -1
  109. package/dist/throttler.js +3 -1
  110. package/dist/throttler.js.map +1 -1
  111. package/lib/batchTracker.d.ts.map +1 -1
  112. package/lib/batchTracker.js +1 -1
  113. package/lib/batchTracker.js.map +1 -1
  114. package/lib/blobManager.d.ts.map +1 -1
  115. package/lib/blobManager.js +1 -1
  116. package/lib/blobManager.js.map +1 -1
  117. package/lib/channelCollection.d.ts +12 -2
  118. package/lib/channelCollection.d.ts.map +1 -1
  119. package/lib/channelCollection.js +86 -90
  120. package/lib/channelCollection.js.map +1 -1
  121. package/lib/connectionTelemetry.d.ts.map +1 -1
  122. package/lib/connectionTelemetry.js.map +1 -1
  123. package/lib/containerRuntime.d.ts +2 -1
  124. package/lib/containerRuntime.d.ts.map +1 -1
  125. package/lib/containerRuntime.js +32 -12
  126. package/lib/containerRuntime.js.map +1 -1
  127. package/lib/dataStoreContext.d.ts +2 -1
  128. package/lib/dataStoreContext.d.ts.map +1 -1
  129. package/lib/dataStoreContext.js +5 -3
  130. package/lib/dataStoreContext.js.map +1 -1
  131. package/lib/dataStoreContexts.d.ts.map +1 -1
  132. package/lib/dataStoreContexts.js +1 -1
  133. package/lib/dataStoreContexts.js.map +1 -1
  134. package/lib/deltaManagerProxies.d.ts.map +1 -1
  135. package/lib/deltaManagerProxies.js.map +1 -1
  136. package/lib/deltaScheduler.d.ts.map +1 -1
  137. package/lib/deltaScheduler.js +1 -3
  138. package/lib/deltaScheduler.js.map +1 -1
  139. package/lib/gc/garbageCollection.d.ts +4 -2
  140. package/lib/gc/garbageCollection.d.ts.map +1 -1
  141. package/lib/gc/garbageCollection.js +13 -9
  142. package/lib/gc/garbageCollection.js.map +1 -1
  143. package/lib/gc/gcConfigs.js +1 -1
  144. package/lib/gc/gcConfigs.js.map +1 -1
  145. package/lib/gc/gcDefinitions.d.ts +7 -4
  146. package/lib/gc/gcDefinitions.d.ts.map +1 -1
  147. package/lib/gc/gcDefinitions.js.map +1 -1
  148. package/lib/gc/gcHelpers.d.ts.map +1 -1
  149. package/lib/gc/gcHelpers.js.map +1 -1
  150. package/lib/gc/gcSummaryStateTracker.d.ts.map +1 -1
  151. package/lib/gc/gcSummaryStateTracker.js.map +1 -1
  152. package/lib/gc/gcTelemetry.d.ts +1 -1
  153. package/lib/gc/gcTelemetry.d.ts.map +1 -1
  154. package/lib/gc/gcTelemetry.js +1 -7
  155. package/lib/gc/gcTelemetry.js.map +1 -1
  156. package/lib/index.d.ts +1 -1
  157. package/lib/index.d.ts.map +1 -1
  158. package/lib/index.js.map +1 -1
  159. package/lib/messageTypes.d.ts +5 -21
  160. package/lib/messageTypes.d.ts.map +1 -1
  161. package/lib/messageTypes.js.map +1 -1
  162. package/lib/opLifecycle/index.d.ts +1 -1
  163. package/lib/opLifecycle/index.d.ts.map +1 -1
  164. package/lib/opLifecycle/index.js +1 -1
  165. package/lib/opLifecycle/index.js.map +1 -1
  166. package/lib/opLifecycle/opDecompressor.d.ts.map +1 -1
  167. package/lib/opLifecycle/opDecompressor.js +1 -2
  168. package/lib/opLifecycle/opDecompressor.js.map +1 -1
  169. package/lib/opLifecycle/opGroupingManager.d.ts.map +1 -1
  170. package/lib/opLifecycle/opGroupingManager.js.map +1 -1
  171. package/lib/opLifecycle/opSplitter.d.ts.map +1 -1
  172. package/lib/opLifecycle/opSplitter.js +1 -1
  173. package/lib/opLifecycle/opSplitter.js.map +1 -1
  174. package/lib/opLifecycle/outbox.d.ts.map +1 -1
  175. package/lib/opLifecycle/outbox.js.map +1 -1
  176. package/lib/packageVersion.d.ts +1 -1
  177. package/lib/packageVersion.js +1 -1
  178. package/lib/packageVersion.js.map +1 -1
  179. package/lib/pendingStateManager.d.ts.map +1 -1
  180. package/lib/pendingStateManager.js +1 -1
  181. package/lib/pendingStateManager.js.map +1 -1
  182. package/lib/summary/documentSchema.d.ts.map +1 -1
  183. package/lib/summary/documentSchema.js +1 -2
  184. package/lib/summary/documentSchema.js.map +1 -1
  185. package/lib/summary/index.d.ts +1 -1
  186. package/lib/summary/index.d.ts.map +1 -1
  187. package/lib/summary/index.js +1 -1
  188. package/lib/summary/index.js.map +1 -1
  189. package/lib/summary/orderedClientElection.d.ts.map +1 -1
  190. package/lib/summary/orderedClientElection.js.map +1 -1
  191. package/lib/summary/runningSummarizer.d.ts.map +1 -1
  192. package/lib/summary/runningSummarizer.js +1 -2
  193. package/lib/summary/runningSummarizer.js.map +1 -1
  194. package/lib/summary/summarizerClientElection.d.ts.map +1 -1
  195. package/lib/summary/summarizerClientElection.js.map +1 -1
  196. package/lib/summary/summarizerNode/summarizerNode.d.ts.map +1 -1
  197. package/lib/summary/summarizerNode/summarizerNode.js.map +1 -1
  198. package/lib/summary/summarizerNode/summarizerNodeUtils.d.ts.map +1 -1
  199. package/lib/summary/summarizerNode/summarizerNodeUtils.js.map +1 -1
  200. package/lib/summary/summarizerNode/summarizerNodeWithGc.d.ts.map +1 -1
  201. package/lib/summary/summarizerNode/summarizerNodeWithGc.js +4 -1
  202. package/lib/summary/summarizerNode/summarizerNodeWithGc.js.map +1 -1
  203. package/lib/summary/summarizerTypes.d.ts.map +1 -1
  204. package/lib/summary/summarizerTypes.js.map +1 -1
  205. package/lib/summary/summaryCollection.d.ts.map +1 -1
  206. package/lib/summary/summaryCollection.js.map +1 -1
  207. package/lib/summary/summaryFormat.d.ts.map +1 -1
  208. package/lib/summary/summaryFormat.js.map +1 -1
  209. package/lib/summary/summaryGenerator.d.ts.map +1 -1
  210. package/lib/summary/summaryGenerator.js +1 -2
  211. package/lib/summary/summaryGenerator.js.map +1 -1
  212. package/lib/summary/summaryManager.d.ts.map +1 -1
  213. package/lib/summary/summaryManager.js +1 -2
  214. package/lib/summary/summaryManager.js.map +1 -1
  215. package/lib/throttler.d.ts.map +1 -1
  216. package/lib/throttler.js +3 -1
  217. package/lib/throttler.js.map +1 -1
  218. package/package.json +33 -20
  219. package/src/batchTracker.ts +4 -1
  220. package/src/blobManager.ts +14 -16
  221. package/src/channelCollection.ts +139 -132
  222. package/src/connectionTelemetry.ts +3 -8
  223. package/src/containerRuntime.ts +72 -44
  224. package/src/dataStoreContext.ts +34 -11
  225. package/src/dataStoreContexts.ts +7 -2
  226. package/src/deltaManagerProxies.ts +12 -3
  227. package/src/deltaScheduler.ts +1 -3
  228. package/src/gc/garbageCollection.ts +47 -31
  229. package/src/gc/gcConfigs.ts +7 -3
  230. package/src/gc/gcDefinitions.ts +16 -4
  231. package/src/gc/gcHelpers.ts +6 -2
  232. package/src/gc/gcSummaryStateTracker.ts +4 -1
  233. package/src/gc/gcTelemetry.ts +2 -9
  234. package/src/index.ts +0 -1
  235. package/src/messageTypes.ts +7 -23
  236. package/src/opLifecycle/index.ts +5 -1
  237. package/src/opLifecycle/opDecompressor.ts +2 -6
  238. package/src/opLifecycle/opGroupingManager.ts +1 -4
  239. package/src/opLifecycle/opSplitter.ts +9 -3
  240. package/src/opLifecycle/outbox.ts +1 -4
  241. package/src/packageVersion.ts +1 -1
  242. package/src/pendingStateManager.ts +4 -2
  243. package/src/summary/documentSchema.ts +4 -7
  244. package/src/summary/index.ts +4 -1
  245. package/src/summary/orderedClientElection.ts +17 -10
  246. package/src/summary/runningSummarizer.ts +20 -9
  247. package/src/summary/summarizerClientElection.ts +2 -1
  248. package/src/summary/summarizerNode/summarizerNode.ts +6 -4
  249. package/src/summary/summarizerNode/summarizerNodeUtils.ts +7 -2
  250. package/src/summary/summarizerNode/summarizerNodeWithGc.ts +10 -6
  251. package/src/summary/summarizerTypes.ts +9 -2
  252. package/src/summary/summaryCollection.ts +4 -1
  253. package/src/summary/summaryFormat.ts +8 -3
  254. package/src/summary/summaryGenerator.ts +4 -9
  255. package/src/summary/summaryManager.ts +6 -9
  256. package/src/throttler.ts +3 -1
  257. package/tsdoc.json +4 -0
@@ -397,11 +397,14 @@ export class ChannelCollection implements IFluidDataStoreChannel, IDisposable {
397
397
  const attachMessage = message.contents as InboundAttachMessage;
398
398
 
399
399
  // We need to process the GC Data for both local and remote attach messages
400
- const foundGCData = processAttachMessageGCData(attachMessage.snapshot, (nodeId, toPath) => {
401
- // nodeId is the relative path under the node being attached. Always starts with "/", but no trailing "/" after an id
402
- const fromPath = `/${attachMessage.id}${nodeId === "/" ? "" : nodeId}`;
403
- this.parentContext.addedGCOutboundRoute(fromPath, toPath);
404
- });
400
+ const foundGCData = processAttachMessageGCData(
401
+ attachMessage.snapshot,
402
+ (nodeId, toPath) => {
403
+ // nodeId is the relative path under the node being attached. Always starts with "/", but no trailing "/" after an id
404
+ const fromPath = `/${attachMessage.id}${nodeId === "/" ? "" : nodeId}`;
405
+ this.parentContext.addedGCOutboundRoute(fromPath, toPath, message.timestamp);
406
+ },
407
+ );
405
408
 
406
409
  // Only log once per container to avoid noise/cost.
407
410
  // Allows longitudinal tracking of various state (e.g. foundGCData), and some sampled details
@@ -495,13 +498,18 @@ export class ChannelCollection implements IFluidDataStoreChannel, IDisposable {
495
498
  const aliasResult = this.processAliasMessageCore(
496
499
  aliasMessage.internalId,
497
500
  aliasMessage.alias,
501
+ message.timestamp,
498
502
  );
499
503
  if (local) {
500
504
  resolve(aliasResult);
501
505
  }
502
506
  }
503
507
 
504
- public processAliasMessageCore(internalId: string, alias: string): boolean {
508
+ public processAliasMessageCore(
509
+ internalId: string,
510
+ alias: string,
511
+ messageTimestampMs?: number,
512
+ ): boolean {
505
513
  if (this.alreadyProcessed(alias)) {
506
514
  return false;
507
515
  }
@@ -521,7 +529,11 @@ export class ChannelCollection implements IFluidDataStoreChannel, IDisposable {
521
529
  return false;
522
530
  }
523
531
 
524
- this.parentContext.addedGCOutboundRoute("/", `/${internalId}`);
532
+ // If message timestamp doesn't exist, this is called in a detached container. Don't notify GC in that case
533
+ // because it doesn't run in detached container and doesn't need to know about this route.
534
+ if (messageTimestampMs) {
535
+ this.parentContext.addedGCOutboundRoute("/", `/${internalId}`, messageTimestampMs);
536
+ }
525
537
 
526
538
  this.aliasMap.set(alias, context.id);
527
539
  this.aliasedDataStores.add(context.id);
@@ -829,7 +841,7 @@ export class ChannelCollection implements IFluidDataStoreChannel, IDisposable {
829
841
  envelope.address,
830
842
  transformed.contents,
831
843
  (fromPath: string, toPath: string) =>
832
- this.parentContext.addedGCOutboundRoute(fromPath, toPath),
844
+ this.parentContext.addedGCOutboundRoute(fromPath, toPath, message.timestamp),
833
845
  );
834
846
  break;
835
847
  }
@@ -966,9 +978,7 @@ export class ChannelCollection implements IFluidDataStoreChannel, IDisposable {
966
978
  }
967
979
 
968
980
  const idToLog =
969
- originalRequest !== undefined
970
- ? urlToGCNodePath(originalRequest.url)
971
- : dataStoreNodePath;
981
+ originalRequest !== undefined ? urlToGCNodePath(originalRequest.url) : dataStoreNodePath;
972
982
 
973
983
  // Log the package details asynchronously since getInitialSnapshotDetails is async
974
984
  const recentlyDeletedContext = this.contexts.getRecentlyDeletedContext(id);
@@ -1075,86 +1085,30 @@ export class ChannelCollection implements IFluidDataStoreChannel, IDisposable {
1075
1085
  return this.contexts.size;
1076
1086
  }
1077
1087
 
1078
- public async summarize(
1079
- fullTree: boolean,
1080
- trackState: boolean,
1081
- telemetryContext?: ITelemetryContext,
1082
- ): Promise<ISummaryTreeWithStats> {
1083
- const summaryBuilder = new SummaryTreeBuilder();
1084
-
1085
- // Iterate over each store and ask it to snapshot
1086
- await Promise.all(
1087
- Array.from(this.contexts)
1088
- .filter(([_, context]) => {
1089
- // Summarizer works only with clients with no local changes. A data store in attaching
1090
- // state indicates an op was sent to attach a local data store, and the the attach op
1091
- // had not yet round tripped back to the client.
1092
- if (context.attachState === AttachState.Attaching) {
1093
- // Formerly assert 0x588
1094
- const error = DataProcessingError.create(
1095
- "Local data store detected in attaching state during summarize",
1096
- "summarize",
1097
- );
1098
- throw error;
1099
- }
1100
- return context.attachState === AttachState.Attached;
1101
- })
1102
- .map(async ([contextId, context]) => {
1103
- const contextSummary = await context.summarize(
1104
- fullTree,
1105
- trackState,
1106
- telemetryContext,
1107
- );
1108
- summaryBuilder.addWithStats(contextId, contextSummary);
1109
- }),
1110
- );
1111
-
1112
- return summaryBuilder.getSummaryTree();
1113
- }
1114
-
1115
1088
  /**
1116
1089
  * Create a summary. Used when attaching or serializing a detached container.
1117
1090
  */
1118
1091
  public getAttachSummary(telemetryContext?: ITelemetryContext): ISummaryTreeWithStats {
1119
1092
  const builder = new SummaryTreeBuilder();
1120
- // Attaching graph of some stores can cause other stores to get bound too.
1121
- // So keep taking summary until no new stores get bound.
1122
- let notBoundContextsLength: number;
1123
- do {
1124
- const builderTree = builder.summary.tree;
1125
- notBoundContextsLength = this.contexts.notBoundLength();
1126
- // Iterate over each data store and ask it to snapshot
1127
- Array.from(this.contexts)
1128
- .filter(
1129
- ([key, _]) =>
1130
- // Take summary of bounded data stores only, make sure we haven't summarized them already
1131
- // and no attach op has been fired for that data store because for loader versions <= 0.24
1132
- // we set attach state as "attaching" before taking createNew summary.
1133
- !(
1134
- this.contexts.isNotBound(key) ||
1135
- builderTree[key] ||
1136
- this.attachOpFiredForDataStore.has(key)
1137
- ),
1138
- )
1139
- .map(([key, value]) => {
1140
- let dataStoreSummary: ISummarizeResult;
1141
- if (value.isLoaded) {
1142
- dataStoreSummary = value.getAttachSummary(telemetryContext);
1143
- } else {
1144
- // If this data store is not yet loaded, then there should be no changes in the snapshot from
1145
- // which it was created as it is detached container. So just use the previous snapshot.
1146
- assert(
1147
- !!this.baseSnapshot,
1148
- 0x166 /* "BaseSnapshot should be there as detached container loaded from snapshot" */,
1149
- );
1150
- dataStoreSummary = convertSnapshotTreeToSummaryTree(
1151
- getSnapshotTree(this.baseSnapshot).trees[key],
1152
- );
1153
- }
1154
- builder.addWithStats(key, dataStoreSummary);
1155
- });
1156
- } while (notBoundContextsLength !== this.contexts.notBoundLength());
1157
-
1093
+ this.visitLocalBoundContextsDuringAttach(
1094
+ (contextId: string, context: FluidDataStoreContext) => {
1095
+ let dataStoreSummary: ISummarizeResult;
1096
+ if (context.isLoaded) {
1097
+ dataStoreSummary = context.getAttachSummary(telemetryContext);
1098
+ } else {
1099
+ // If this data store is not yet loaded, then there should be no changes in the snapshot from
1100
+ // which it was created as it is detached container. So just use the previous snapshot.
1101
+ assert(
1102
+ !!this.baseSnapshot,
1103
+ 0x166 /* "BaseSnapshot should be there as detached container loaded from snapshot" */,
1104
+ );
1105
+ dataStoreSummary = convertSnapshotTreeToSummaryTree(
1106
+ getSnapshotTree(this.baseSnapshot).trees[contextId],
1107
+ );
1108
+ }
1109
+ builder.addWithStats(contextId, dataStoreSummary);
1110
+ },
1111
+ );
1158
1112
  return builder.getSummaryTree();
1159
1113
  }
1160
1114
 
@@ -1163,31 +1117,96 @@ export class ChannelCollection implements IFluidDataStoreChannel, IDisposable {
1163
1117
  */
1164
1118
  public getAttachGCData(telemetryContext?: ITelemetryContext): IGarbageCollectionData {
1165
1119
  const builder = new GCDataBuilder();
1166
- // Attaching graph of some stores can cause other stores to get bound too.
1167
- // So keep taking summary until no new stores get bound.
1168
- let notBoundContextsLength: number;
1169
- do {
1170
- notBoundContextsLength = this.contexts.notBoundLength();
1171
- // Iterate over each data store and ask for its GC data.
1172
- Array.from(this.contexts)
1173
- .filter(
1174
- ([key, _]) =>
1175
- // Take GC data of bounded data stores only.
1176
- !this.contexts.isNotBound(key),
1177
- )
1178
- .map(([key, value]) => {
1179
- const contextGCData = value.getAttachGCData(telemetryContext);
1180
- // Prefix the child's id to the ids of its GC nodes so they can be identified as belonging to the child.
1181
- // This also gradually builds the id of each node to be a path from the root.
1182
- builder.prefixAndAddNodes(key, contextGCData.gcNodes);
1183
- });
1184
- } while (notBoundContextsLength !== this.contexts.notBoundLength());
1185
-
1120
+ this.visitLocalBoundContextsDuringAttach(
1121
+ (contextId: string, context: FluidDataStoreContext) => {
1122
+ const contextGCData = context.getAttachGCData(telemetryContext);
1123
+ // Prefix the child's id to the ids of its GC nodes so they can be identified as belonging to the child.
1124
+ // This also gradually builds the id of each node to be a path from the root.
1125
+ builder.prefixAndAddNodes(contextId, contextGCData.gcNodes);
1126
+ },
1127
+ );
1186
1128
  // Get the outbound routes (aliased data stores) and add a GC node for this channel.
1187
1129
  builder.addNode("/", Array.from(this.aliasedDataStores));
1188
1130
  return builder.getGCData();
1189
1131
  }
1190
1132
 
1133
+ /**
1134
+ * Helper method for preparing to attach this channel.
1135
+ * Runs the callback for each bound context to incorporate its data however the caller specifies
1136
+ */
1137
+ private visitLocalBoundContextsDuringAttach(
1138
+ visitor: (contextId: string, context: FluidDataStoreContext) => void,
1139
+ ): void {
1140
+ const visitedContexts = new Set<string>();
1141
+ let visitedLength = -1;
1142
+ let notBoundContextsLength = -1;
1143
+ while (
1144
+ visitedLength !== visitedContexts.size &&
1145
+ notBoundContextsLength !== this.contexts.notBoundLength()
1146
+ ) {
1147
+ // detect changes in the visitedContexts set, as on visiting a context
1148
+ // it could could make contexts available by removing other contexts
1149
+ // from the not bound context list, so we need to ensure those get processed as well.
1150
+ // only once the loop can run with no new contexts added to the visitedContexts set do we
1151
+ // know for sure all possible contexts have been visited.
1152
+ visitedLength = visitedContexts.size;
1153
+ notBoundContextsLength = this.contexts.notBoundLength();
1154
+ for (const [contextId, context] of this.contexts) {
1155
+ if (
1156
+ !(
1157
+ visitedContexts.has(contextId) ||
1158
+ this.contexts.isNotBound(contextId) ||
1159
+ this.attachOpFiredForDataStore.has(contextId)
1160
+ )
1161
+ ) {
1162
+ visitor(contextId, context);
1163
+ visitedContexts.add(contextId);
1164
+ }
1165
+ }
1166
+ }
1167
+ }
1168
+
1169
+ /**
1170
+ * Helper method for preparing to summarize this channel.
1171
+ * Runs the callback for each bound context to incorporate its data however the caller specifies
1172
+ */
1173
+ private async visitContextsDuringSummary(
1174
+ visitor: (contextId: string, context: FluidDataStoreContext) => Promise<void>,
1175
+ ): Promise<void> {
1176
+ for (const [contextId, context] of this.contexts) {
1177
+ // Summarizer client and hence GC works only with clients with no local changes. A data store in
1178
+ // attaching state indicates an op was sent to attach a local data store, and the the attach op
1179
+ // had not yet round tripped back to the client.
1180
+ // Formerly assert 0x589
1181
+ if (context.attachState === AttachState.Attaching) {
1182
+ const error = DataProcessingError.create(
1183
+ "Local data store detected in attaching state",
1184
+ "summarize/getGCData",
1185
+ );
1186
+ throw error;
1187
+ }
1188
+
1189
+ if (context.attachState === AttachState.Attached) {
1190
+ await visitor(contextId, context);
1191
+ }
1192
+ }
1193
+ }
1194
+
1195
+ public async summarize(
1196
+ fullTree: boolean,
1197
+ trackState: boolean,
1198
+ telemetryContext?: ITelemetryContext,
1199
+ ): Promise<ISummaryTreeWithStats> {
1200
+ const summaryBuilder = new SummaryTreeBuilder();
1201
+ await this.visitContextsDuringSummary(
1202
+ async (contextId: string, context: FluidDataStoreContext) => {
1203
+ const contextSummary = await context.summarize(fullTree, trackState, telemetryContext);
1204
+ summaryBuilder.addWithStats(contextId, contextSummary);
1205
+ },
1206
+ );
1207
+ return summaryBuilder.getSummaryTree();
1208
+ }
1209
+
1191
1210
  /**
1192
1211
  * Generates data used for garbage collection. It does the following:
1193
1212
  *
@@ -1203,30 +1222,13 @@ export class ChannelCollection implements IFluidDataStoreChannel, IDisposable {
1203
1222
  */
1204
1223
  public async getGCData(fullGC: boolean = false): Promise<IGarbageCollectionData> {
1205
1224
  const builder = new GCDataBuilder();
1206
- // Iterate over each store and get their GC data.
1207
- await Promise.all(
1208
- Array.from(this.contexts)
1209
- .filter(([_, context]) => {
1210
- // Summarizer client and hence GC works only with clients with no local changes. A data store in
1211
- // attaching state indicates an op was sent to attach a local data store, and the the attach op
1212
- // had not yet round tripped back to the client.
1213
- // Formerly assert 0x589
1214
- if (context.attachState === AttachState.Attaching) {
1215
- const error = DataProcessingError.create(
1216
- "Local data store detected in attaching state while running GC",
1217
- "getGCData",
1218
- );
1219
- throw error;
1220
- }
1221
-
1222
- return context.attachState === AttachState.Attached;
1223
- })
1224
- .map(async ([contextId, context]) => {
1225
- const contextGCData = await context.getGCData(fullGC);
1226
- // Prefix the child's id to the ids of its GC nodes so they can be identified as belonging to the child.
1227
- // This also gradually builds the id of each node to be a path from the root.
1228
- builder.prefixAndAddNodes(contextId, contextGCData.gcNodes);
1229
- }),
1225
+ await this.visitContextsDuringSummary(
1226
+ async (contextId: string, context: FluidDataStoreContext) => {
1227
+ const contextGCData = await context.getGCData(fullGC);
1228
+ // Prefix the child's id to the ids of its GC nodes so they can be identified as belonging to the child.
1229
+ // This also gradually builds the id of each node to be a path from the root.
1230
+ builder.prefixAndAddNodes(contextId, contextGCData.gcNodes);
1231
+ },
1230
1232
  );
1231
1233
 
1232
1234
  // Get the outbound routes and add a GC node for this channel.
@@ -1283,7 +1285,9 @@ export class ChannelCollection implements IFluidDataStoreChannel, IDisposable {
1283
1285
  * be deleted.
1284
1286
  * @returns The routes of data stores and its objects that were deleted.
1285
1287
  */
1286
- public deleteSweepReadyNodes(sweepReadyDataStoreRoutes: readonly string[]): readonly string[] {
1288
+ public deleteSweepReadyNodes(
1289
+ sweepReadyDataStoreRoutes: readonly string[],
1290
+ ): readonly string[] {
1287
1291
  for (const route of sweepReadyDataStoreRoutes) {
1288
1292
  const pathParts = route.split("/");
1289
1293
  const dataStoreId = pathParts[1];
@@ -1361,7 +1365,9 @@ export class ChannelCollection implements IFluidDataStoreChannel, IDisposable {
1361
1365
  /**
1362
1366
  * Called by GC to retrieve the package path of a data store node with the given path.
1363
1367
  */
1364
- public async getDataStorePackagePath(nodePath: string): Promise<readonly string[] | undefined> {
1368
+ public async getDataStorePackagePath(
1369
+ nodePath: string,
1370
+ ): Promise<readonly string[] | undefined> {
1365
1371
  // If the node belongs to a data store, return its package path. For DDSes, we return the package path of the
1366
1372
  // data store that contains it.
1367
1373
  const context = this.contexts.get(nodePath.split("/")[1]);
@@ -1432,6 +1438,7 @@ export class ChannelCollection implements IFluidDataStoreChannel, IDisposable {
1432
1438
  packagePath: details.pkg,
1433
1439
  request,
1434
1440
  headerData,
1441
+ timestampMs: undefined, // This will be added by the parent context if needed.
1435
1442
  });
1436
1443
 
1437
1444
  const dataStore = await dataStoreContext.realize();
@@ -141,8 +141,7 @@ class OpPerfTelemetry {
141
141
  return {
142
142
  sample: () => {
143
143
  eventCount++;
144
- const shouldSample =
145
- eventCount % OpPerfTelemetry.DELTA_LATENCY_SAMPLE_RATE === 0;
144
+ const shouldSample = eventCount % OpPerfTelemetry.DELTA_LATENCY_SAMPLE_RATE === 0;
146
145
  if (shouldSample) {
147
146
  eventCount = 0;
148
147
  }
@@ -163,8 +162,7 @@ class OpPerfTelemetry {
163
162
  return {
164
163
  sample: () => {
165
164
  eventCount++;
166
- const shouldSample =
167
- eventCount % OpPerfTelemetry.PROCESSED_OPS_SAMPLE_RATE === 0;
165
+ const shouldSample = eventCount % OpPerfTelemetry.PROCESSED_OPS_SAMPLE_RATE === 0;
168
166
  if (shouldSample) {
169
167
  eventCount = 0;
170
168
  this.noOpCountForTelemetry = 0;
@@ -212,10 +210,7 @@ class OpPerfTelemetry {
212
210
  ) {
213
211
  // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
214
212
  const latencyStats = this.latencyStatistics.get(msg.clientSequenceNumber)!;
215
- assert(
216
- latencyStats !== undefined,
217
- 0x7c2 /* Latency stats for op should exist */,
218
- );
213
+ assert(latencyStats !== undefined, 0x7c2 /* Latency stats for op should exist */);
219
214
  assert(
220
215
  latencyStats.opProcessingTimes.outboundPushEventTime === undefined,
221
216
  0x2c8 /* "outboundPushEventTime should be undefined" */,
@@ -121,17 +121,25 @@ import {
121
121
  loggerToMonitoringContext,
122
122
  raiseConnectedEvent,
123
123
  wrapError,
124
+ tagCodeArtifacts,
124
125
  } from "@fluidframework/telemetry-utils/internal";
125
126
  import { v4 as uuid } from "uuid";
126
127
 
127
128
  import { BindBatchTracker } from "./batchTracker.js";
128
129
  import { BlobManager, IBlobManagerLoadInfo, IPendingBlobs } from "./blobManager.js";
129
- import { ChannelCollection, getSummaryForDatastores, wrapContext } from "./channelCollection.js";
130
+ import {
131
+ ChannelCollection,
132
+ getSummaryForDatastores,
133
+ wrapContext,
134
+ } from "./channelCollection.js";
130
135
  import { IPerfSignalReport, ReportOpPerfTelemetry } from "./connectionTelemetry.js";
131
136
  import { ContainerFluidHandleContext } from "./containerHandleContext.js";
132
137
  import { channelToDataStore } from "./dataStore.js";
133
138
  import { FluidDataStoreRegistry } from "./dataStoreRegistry.js";
134
- import { DeltaManagerPendingOpsProxy, DeltaManagerSummarizerProxy } from "./deltaManagerProxies.js";
139
+ import {
140
+ DeltaManagerPendingOpsProxy,
141
+ DeltaManagerSummarizerProxy,
142
+ } from "./deltaManagerProxies.js";
135
143
  import {
136
144
  GCNodeType,
137
145
  GarbageCollector,
@@ -1135,10 +1143,7 @@ export class ContainerRuntime
1135
1143
  // That's because any other usage will require immidiate loading of ID Compressor in next sessions in order
1136
1144
  // to reason over such things as session ID space.
1137
1145
  if (this.idCompressorMode === "on") {
1138
- assert(
1139
- this._idCompressor !== undefined,
1140
- 0x8ea /* compressor should have been loaded */,
1141
- );
1146
+ assert(this._idCompressor !== undefined, 0x8ea /* compressor should have been loaded */);
1142
1147
  return this._idCompressor;
1143
1148
  }
1144
1149
  }
@@ -1172,7 +1177,10 @@ export class ContainerRuntime
1172
1177
  * should be sufficient. This should be used only if necessary. For example, for validating and propagating connected
1173
1178
  * events which requires access to the actual real only info, this is needed.
1174
1179
  */
1175
- private readonly innerDeltaManager: IDeltaManager<ISequencedDocumentMessage, IDocumentMessage>;
1180
+ private readonly innerDeltaManager: IDeltaManager<
1181
+ ISequencedDocumentMessage,
1182
+ IDocumentMessage
1183
+ >;
1176
1184
 
1177
1185
  // internal logger for ContainerRuntime. Use this.logger for stores, summaries, etc.
1178
1186
  private readonly mc: MonitoringContext;
@@ -1537,7 +1545,9 @@ export class ContainerRuntime
1537
1545
  const useDeltaManagerOpsProxy =
1538
1546
  this.mc.config.getBoolean("Fluid.ContainerRuntime.DeltaManagerOpsProxy") !== false;
1539
1547
  // The summarizerDeltaManager Proxy is used to lie to the summarizer to convince it is in the right state as a summarizer client.
1540
- const summarizerDeltaManagerProxy = new DeltaManagerSummarizerProxy(this.innerDeltaManager);
1548
+ const summarizerDeltaManagerProxy = new DeltaManagerSummarizerProxy(
1549
+ this.innerDeltaManager,
1550
+ );
1541
1551
  outerDeltaManager = summarizerDeltaManagerProxy;
1542
1552
 
1543
1553
  // The DeltaManagerPendingOpsProxy is used to control the minimum sequence number
@@ -1663,7 +1673,11 @@ export class ContainerRuntime
1663
1673
  snapshot,
1664
1674
  parentContext,
1665
1675
  this.mc.logger,
1666
- (props) => this.garbageCollector.nodeUpdated(props),
1676
+ (props) =>
1677
+ this.garbageCollector.nodeUpdated({
1678
+ ...props,
1679
+ timestampMs: props.timestampMs ?? this.getCurrentReferenceTimestampMs(),
1680
+ }),
1667
1681
  (path: string) => this.garbageCollector.isNodeDeleted(path),
1668
1682
  new Map<string, string>(dataStoreAliasMap),
1669
1683
  async (runtime: ChannelCollection) => provideEntryPoint,
@@ -1689,6 +1703,7 @@ export class ContainerRuntime
1689
1703
  this.garbageCollector.nodeUpdated({
1690
1704
  node: { type: "Blob", path: blobPath },
1691
1705
  reason: "Loaded",
1706
+ timestampMs: this.getCurrentReferenceTimestampMs(),
1692
1707
  }),
1693
1708
  isBlobDeleted: (blobPath: string) => this.garbageCollector.isNodeDeleted(blobPath),
1694
1709
  runtime: this,
@@ -1748,7 +1763,7 @@ export class ContainerRuntime
1748
1763
  : ({
1749
1764
  clientId,
1750
1765
  client: audience.getMember(clientId),
1751
- } satisfies ISelf);
1766
+ } satisfies ISelf);
1752
1767
  };
1753
1768
 
1754
1769
  let oldClientId = this.clientId;
@@ -1767,7 +1782,8 @@ export class ContainerRuntime
1767
1782
  const closeSummarizerDelayOverride = this.mc.config.getNumber(
1768
1783
  "Fluid.ContainerRuntime.Test.CloseSummarizerDelayOverrideMs",
1769
1784
  );
1770
- this.closeSummarizerDelayMs = closeSummarizerDelayOverride ?? defaultCloseSummarizerDelayMs;
1785
+ this.closeSummarizerDelayMs =
1786
+ closeSummarizerDelayOverride ?? defaultCloseSummarizerDelayMs;
1771
1787
  this.summaryCollection = new SummaryCollection(this.deltaManager, this.logger);
1772
1788
 
1773
1789
  this.dirtyContainer =
@@ -1934,7 +1950,10 @@ export class ContainerRuntime
1934
1950
  }
1935
1951
  }
1936
1952
 
1937
- public getCreateChildSummarizerNodeFn(id: string, createParam: CreateChildSummarizerNodeParam) {
1953
+ public getCreateChildSummarizerNodeFn(
1954
+ id: string,
1955
+ createParam: CreateChildSummarizerNodeParam,
1956
+ ) {
1938
1957
  return (
1939
1958
  summarizeInternal: SummarizeInternalFn,
1940
1959
  getGCDataFn: (fullGC?: boolean) => Promise<IGarbageCollectionData>,
@@ -2081,9 +2100,7 @@ export class ContainerRuntime
2081
2100
  // the summarizer state is not up to date.
2082
2101
  // This should be a recoverable scenario and shouldn't happen as we should process the ack first.
2083
2102
  if (this.isSummarizerClient) {
2084
- throw new Error(
2085
- "Summarizer client behind, loaded newer snapshot with loadingGroupId",
2086
- );
2103
+ throw new Error("Summarizer client behind, loaded newer snapshot with loadingGroupId");
2087
2104
  }
2088
2105
 
2089
2106
  // We want to catchup from sequenceNumber to targetSequenceNumber
@@ -2190,7 +2207,7 @@ export class ContainerRuntime
2190
2207
  status: 200,
2191
2208
  mimeType: "fluid/object",
2192
2209
  value: blob,
2193
- }
2210
+ }
2194
2211
  : create404Response(request);
2195
2212
  } else if (requestParser.pathParts.length > 0) {
2196
2213
  return await this.channelCollection.request(request);
@@ -2591,13 +2608,13 @@ export class ContainerRuntime
2591
2608
  message: message as InboundSequencedContainerRuntimeMessage,
2592
2609
  local,
2593
2610
  modernRuntimeMessage,
2594
- }
2611
+ }
2595
2612
  : // Unrecognized message will be ignored.
2596
- {
2613
+ {
2597
2614
  message,
2598
2615
  local,
2599
2616
  modernRuntimeMessage,
2600
- };
2617
+ };
2601
2618
  msg.savedOp = savedOp;
2602
2619
 
2603
2620
  // ensure that we observe any re-entrancy, and if needed, rebase ops
@@ -2715,7 +2732,11 @@ export class ContainerRuntime
2715
2732
  }
2716
2733
  break;
2717
2734
  case ContainerMessageType.GC:
2718
- this.garbageCollector.processMessage(messageWithContext.message, local);
2735
+ this.garbageCollector.processMessage(
2736
+ messageWithContext.message,
2737
+ messageWithContext.message.timestamp,
2738
+ local,
2739
+ );
2719
2740
  break;
2720
2741
  case ContainerMessageType.ChunkedOp:
2721
2742
  // From observability POV, we should not exppse the rest of the system (including "op" events on object) to these messages.
@@ -2739,10 +2760,7 @@ export class ContainerRuntime
2739
2760
 
2740
2761
  const compatBehavior = messageWithContext.message.compatDetails?.behavior;
2741
2762
  if (
2742
- !compatBehaviorAllowsMessageType(
2743
- messageWithContext.message.type,
2744
- compatBehavior,
2745
- )
2763
+ !compatBehaviorAllowsMessageType(messageWithContext.message.type, compatBehavior)
2746
2764
  ) {
2747
2765
  const { message } = messageWithContext;
2748
2766
  const error = DataProcessingError.create(
@@ -2797,8 +2815,7 @@ export class ContainerRuntime
2797
2815
  // Check to see if the signal was lost.
2798
2816
  if (
2799
2817
  this._perfSignalData.trackingSignalSequenceNumber !== undefined &&
2800
- envelope.clientSignalSequenceNumber >
2801
- this._perfSignalData.trackingSignalSequenceNumber
2818
+ envelope.clientSignalSequenceNumber > this._perfSignalData.trackingSignalSequenceNumber
2802
2819
  ) {
2803
2820
  this._perfSignalData.signalsLost++;
2804
2821
  this._perfSignalData.trackingSignalSequenceNumber = undefined;
@@ -2949,6 +2966,7 @@ export class ContainerRuntime
2949
2966
  node: { type: "DataStore", path: `/${internalId}` },
2950
2967
  reason: "Loaded",
2951
2968
  packagePath: context.packagePath,
2969
+ timestampMs: this.getCurrentReferenceTimestampMs(),
2952
2970
  });
2953
2971
  return channel.entryPoint;
2954
2972
  }
@@ -3402,9 +3420,25 @@ export class ContainerRuntime
3402
3420
  * all references added in the system.
3403
3421
  * @param fromPath - The absolute path of the node that added the reference.
3404
3422
  * @param toPath - The absolute path of the outbound node that is referenced.
3423
+ * @param messageTimestampMs - The timestamp of the message that added the reference.
3405
3424
  */
3406
- public addedGCOutboundRoute(fromPath: string, toPath: string) {
3407
- this.garbageCollector.addedOutboundReference(fromPath, toPath);
3425
+ public addedGCOutboundRoute(fromPath: string, toPath: string, messageTimestampMs?: number) {
3426
+ // This is always called when processing an op so messageTimestampMs should exist. Due to back-compat
3427
+ // across the data store runtime / container runtime boundary, this may be undefined and if so, get
3428
+ // the timestamp from the last processed message which should exist.
3429
+ // If a timestamp doesn't exist, log so we can learn about these cases and return.
3430
+ const timestampMs = messageTimestampMs ?? this.getCurrentReferenceTimestampMs();
3431
+ if (timestampMs === undefined) {
3432
+ this.mc.logger.sendTelemetryEvent({
3433
+ eventName: "NoTimestampInGCOutboundRoute",
3434
+ ...tagCodeArtifacts({
3435
+ id: toPath,
3436
+ fromId: fromPath,
3437
+ }),
3438
+ });
3439
+ return;
3440
+ }
3441
+ this.garbageCollector.addedOutboundReference(fromPath, toPath, timestampMs);
3408
3442
  }
3409
3443
 
3410
3444
  /**
@@ -3516,10 +3550,7 @@ export class ContainerRuntime
3516
3550
  * Generally the validate sequence number comes from the running summarizer and the node sequence number comes from the
3517
3551
  * summarizer nodes.
3518
3552
  */
3519
- if (
3520
- startSummaryResult.invalidNodes > 0 ||
3521
- startSummaryResult.mismatchNumbers.size > 0
3522
- ) {
3553
+ if (startSummaryResult.invalidNodes > 0 || startSummaryResult.mismatchNumbers.size > 0) {
3523
3554
  summaryLogger.sendTelemetryEvent({
3524
3555
  eventName: "LatestSummaryRefSeqNumMismatch",
3525
3556
  details: {
@@ -3687,12 +3718,12 @@ export class ContainerRuntime
3687
3718
  proposalHandle: undefined,
3688
3719
  ackHandle: this.loadedFromVersionId,
3689
3720
  referenceSequenceNumber: summaryRefSeqNum,
3690
- }
3721
+ }
3691
3722
  : {
3692
3723
  proposalHandle: lastAck.summaryOp.contents.handle,
3693
3724
  ackHandle: lastAck.summaryAck.contents.handle,
3694
3725
  referenceSequenceNumber: summaryRefSeqNum,
3695
- };
3726
+ };
3696
3727
 
3697
3728
  let handle: string;
3698
3729
  try {
@@ -4240,10 +4271,7 @@ export class ContainerRuntime
4240
4271
  "prefetchLatestSummaryBeforeClose",
4241
4272
  FetchSource.noCache,
4242
4273
  );
4243
- assert(
4244
- !!versions && !!versions[0],
4245
- 0x137 /* "Failed to get version from storage" */,
4246
- );
4274
+ assert(!!versions && !!versions[0], 0x137 /* "Failed to get version from storage" */);
4247
4275
  stats.getVersionDuration = trace.trace().duration;
4248
4276
 
4249
4277
  const maybeSnapshot = await this.storage.getSnapshotTree(versions[0]);
@@ -4311,15 +4339,13 @@ export class ContainerRuntime
4311
4339
  logAndReturnPendingState(
4312
4340
  event,
4313
4341
  getSyncState(
4314
- await this.blobManager.attachAndGetPendingBlobs(
4315
- props?.stopBlobAttachingSignal,
4316
- ),
4342
+ await this.blobManager.attachAndGetPendingBlobs(props?.stopBlobAttachingSignal),
4317
4343
  ),
4318
4344
  ),
4319
- )
4345
+ )
4320
4346
  : PerformanceEvent.timedExec(this.mc.logger, perfEvent, (event) =>
4321
4347
  logAndReturnPendingState(event, getSyncState()),
4322
- );
4348
+ );
4323
4349
  }
4324
4350
 
4325
4351
  public summarizeOnDemand(options: IOnDemandSummarizeOptions): ISummarizeResults {
@@ -4357,7 +4383,9 @@ export class ContainerRuntime
4357
4383
  };
4358
4384
  }
4359
4385
 
4360
- private validateSummaryHeuristicConfiguration(configuration: ISummaryConfigurationHeuristics) {
4386
+ private validateSummaryHeuristicConfiguration(
4387
+ configuration: ISummaryConfigurationHeuristics,
4388
+ ) {
4361
4389
  // eslint-disable-next-line no-restricted-syntax
4362
4390
  for (const prop in configuration) {
4363
4391
  if (typeof configuration[prop] === "number" && configuration[prop] < 0) {