@fluidframework/container-runtime 2.0.0-internal.5.3.2 → 2.0.0-internal.6.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (290) hide show
  1. package/CHANGELOG.md +80 -0
  2. package/dist/batchTracker.d.ts +2 -1
  3. package/dist/batchTracker.d.ts.map +1 -1
  4. package/dist/batchTracker.js +1 -1
  5. package/dist/batchTracker.js.map +1 -1
  6. package/dist/blobManager.d.ts +13 -2
  7. package/dist/blobManager.d.ts.map +1 -1
  8. package/dist/blobManager.js +103 -25
  9. package/dist/blobManager.js.map +1 -1
  10. package/dist/connectionTelemetry.d.ts.map +1 -1
  11. package/dist/connectionTelemetry.js +12 -4
  12. package/dist/connectionTelemetry.js.map +1 -1
  13. package/dist/containerRuntime.d.ts +69 -22
  14. package/dist/containerRuntime.d.ts.map +1 -1
  15. package/dist/containerRuntime.js +344 -238
  16. package/dist/containerRuntime.js.map +1 -1
  17. package/dist/dataStore.js +11 -2
  18. package/dist/dataStore.js.map +1 -1
  19. package/dist/dataStoreContext.d.ts +1 -1
  20. package/dist/dataStoreContext.d.ts.map +1 -1
  21. package/dist/dataStoreContext.js +40 -44
  22. package/dist/dataStoreContext.js.map +1 -1
  23. package/dist/dataStoreContexts.js +1 -1
  24. package/dist/dataStoreContexts.js.map +1 -1
  25. package/dist/dataStores.d.ts +21 -5
  26. package/dist/dataStores.d.ts.map +1 -1
  27. package/dist/dataStores.js +102 -58
  28. package/dist/dataStores.js.map +1 -1
  29. package/dist/deltaManagerSummarizerProxy.d.ts.map +1 -1
  30. package/dist/deltaManagerSummarizerProxy.js +2 -0
  31. package/dist/deltaManagerSummarizerProxy.js.map +1 -1
  32. package/dist/deltaScheduler.d.ts.map +1 -1
  33. package/dist/deltaScheduler.js +5 -5
  34. package/dist/deltaScheduler.js.map +1 -1
  35. package/dist/gc/garbageCollection.d.ts.map +1 -1
  36. package/dist/gc/garbageCollection.js +29 -25
  37. package/dist/gc/garbageCollection.js.map +1 -1
  38. package/dist/gc/gcConfigs.js +13 -11
  39. package/dist/gc/gcConfigs.js.map +1 -1
  40. package/dist/gc/gcHelpers.d.ts +1 -0
  41. package/dist/gc/gcHelpers.d.ts.map +1 -1
  42. package/dist/gc/gcHelpers.js +5 -6
  43. package/dist/gc/gcHelpers.js.map +1 -1
  44. package/dist/gc/gcSummaryStateTracker.js +4 -6
  45. package/dist/gc/gcSummaryStateTracker.js.map +1 -1
  46. package/dist/gc/gcTelemetry.d.ts.map +1 -1
  47. package/dist/gc/gcTelemetry.js +44 -33
  48. package/dist/gc/gcTelemetry.js.map +1 -1
  49. package/dist/id-compressor/idCompressor.d.ts +3 -3
  50. package/dist/id-compressor/idCompressor.d.ts.map +1 -1
  51. package/dist/id-compressor/idCompressor.js +52 -52
  52. package/dist/id-compressor/idCompressor.js.map +1 -1
  53. package/dist/id-compressor/idRange.js +2 -2
  54. package/dist/id-compressor/idRange.js.map +1 -1
  55. package/dist/id-compressor/sessionIdNormalizer.js +11 -16
  56. package/dist/id-compressor/sessionIdNormalizer.js.map +1 -1
  57. package/dist/index.d.ts +1 -1
  58. package/dist/index.d.ts.map +1 -1
  59. package/dist/index.js.map +1 -1
  60. package/dist/opLifecycle/batchManager.js +10 -6
  61. package/dist/opLifecycle/batchManager.js.map +1 -1
  62. package/dist/opLifecycle/opCompressor.d.ts +2 -2
  63. package/dist/opLifecycle/opCompressor.d.ts.map +1 -1
  64. package/dist/opLifecycle/opCompressor.js +7 -2
  65. package/dist/opLifecycle/opCompressor.js.map +1 -1
  66. package/dist/opLifecycle/opDecompressor.d.ts +2 -2
  67. package/dist/opLifecycle/opDecompressor.d.ts.map +1 -1
  68. package/dist/opLifecycle/opDecompressor.js +12 -10
  69. package/dist/opLifecycle/opDecompressor.js.map +1 -1
  70. package/dist/opLifecycle/opGroupingManager.js +13 -5
  71. package/dist/opLifecycle/opGroupingManager.js.map +1 -1
  72. package/dist/opLifecycle/opSplitter.d.ts +2 -2
  73. package/dist/opLifecycle/opSplitter.d.ts.map +1 -1
  74. package/dist/opLifecycle/opSplitter.js +11 -7
  75. package/dist/opLifecycle/opSplitter.js.map +1 -1
  76. package/dist/opLifecycle/outbox.d.ts +6 -5
  77. package/dist/opLifecycle/outbox.d.ts.map +1 -1
  78. package/dist/opLifecycle/outbox.js +6 -14
  79. package/dist/opLifecycle/outbox.js.map +1 -1
  80. package/dist/opLifecycle/remoteMessageProcessor.d.ts +6 -1
  81. package/dist/opLifecycle/remoteMessageProcessor.d.ts.map +1 -1
  82. package/dist/opLifecycle/remoteMessageProcessor.js +8 -2
  83. package/dist/opLifecycle/remoteMessageProcessor.js.map +1 -1
  84. package/dist/opProperties.js +1 -2
  85. package/dist/opProperties.js.map +1 -1
  86. package/dist/packageVersion.d.ts +1 -1
  87. package/dist/packageVersion.js +1 -1
  88. package/dist/packageVersion.js.map +1 -1
  89. package/dist/pendingStateManager.d.ts +6 -3
  90. package/dist/pendingStateManager.d.ts.map +1 -1
  91. package/dist/pendingStateManager.js +41 -32
  92. package/dist/pendingStateManager.js.map +1 -1
  93. package/dist/scheduleManager.d.ts.map +1 -1
  94. package/dist/scheduleManager.js +15 -11
  95. package/dist/scheduleManager.js.map +1 -1
  96. package/dist/summary/orderedClientElection.d.ts +2 -1
  97. package/dist/summary/orderedClientElection.d.ts.map +1 -1
  98. package/dist/summary/orderedClientElection.js +18 -19
  99. package/dist/summary/orderedClientElection.js.map +1 -1
  100. package/dist/summary/runningSummarizer.d.ts +3 -5
  101. package/dist/summary/runningSummarizer.d.ts.map +1 -1
  102. package/dist/summary/runningSummarizer.js +42 -66
  103. package/dist/summary/runningSummarizer.js.map +1 -1
  104. package/dist/summary/summarizer.js +5 -8
  105. package/dist/summary/summarizer.js.map +1 -1
  106. package/dist/summary/summarizerClientElection.js +5 -9
  107. package/dist/summary/summarizerClientElection.js.map +1 -1
  108. package/dist/summary/summarizerHeuristics.js +8 -12
  109. package/dist/summary/summarizerHeuristics.js.map +1 -1
  110. package/dist/summary/summarizerNode/summarizerNode.d.ts +5 -5
  111. package/dist/summary/summarizerNode/summarizerNode.d.ts.map +1 -1
  112. package/dist/summary/summarizerNode/summarizerNode.js +26 -22
  113. package/dist/summary/summarizerNode/summarizerNode.js.map +1 -1
  114. package/dist/summary/summarizerNode/summarizerNodeUtils.js +2 -4
  115. package/dist/summary/summarizerNode/summarizerNodeUtils.js.map +1 -1
  116. package/dist/summary/summarizerNode/summarizerNodeWithGc.d.ts +4 -3
  117. package/dist/summary/summarizerNode/summarizerNodeWithGc.d.ts.map +1 -1
  118. package/dist/summary/summarizerNode/summarizerNodeWithGc.js +13 -16
  119. package/dist/summary/summarizerNode/summarizerNodeWithGc.js.map +1 -1
  120. package/dist/summary/summaryCollection.js +3 -5
  121. package/dist/summary/summaryCollection.js.map +1 -1
  122. package/dist/summary/summaryFormat.js +1 -2
  123. package/dist/summary/summaryFormat.js.map +1 -1
  124. package/dist/summary/summaryGenerator.d.ts.map +1 -1
  125. package/dist/summary/summaryGenerator.js +67 -21
  126. package/dist/summary/summaryGenerator.js.map +1 -1
  127. package/dist/summary/summaryManager.d.ts +2 -3
  128. package/dist/summary/summaryManager.d.ts.map +1 -1
  129. package/dist/summary/summaryManager.js +9 -7
  130. package/dist/summary/summaryManager.js.map +1 -1
  131. package/lib/batchTracker.d.ts +2 -1
  132. package/lib/batchTracker.d.ts.map +1 -1
  133. package/lib/batchTracker.js +2 -2
  134. package/lib/batchTracker.js.map +1 -1
  135. package/lib/blobManager.d.ts +13 -2
  136. package/lib/blobManager.d.ts.map +1 -1
  137. package/lib/blobManager.js +103 -25
  138. package/lib/blobManager.js.map +1 -1
  139. package/lib/connectionTelemetry.d.ts.map +1 -1
  140. package/lib/connectionTelemetry.js +13 -5
  141. package/lib/connectionTelemetry.js.map +1 -1
  142. package/lib/containerRuntime.d.ts +69 -22
  143. package/lib/containerRuntime.d.ts.map +1 -1
  144. package/lib/containerRuntime.js +343 -238
  145. package/lib/containerRuntime.js.map +1 -1
  146. package/lib/dataStore.js +11 -2
  147. package/lib/dataStore.js.map +1 -1
  148. package/lib/dataStoreContext.d.ts +1 -1
  149. package/lib/dataStoreContext.d.ts.map +1 -1
  150. package/lib/dataStoreContext.js +42 -46
  151. package/lib/dataStoreContext.js.map +1 -1
  152. package/lib/dataStoreContexts.js +2 -2
  153. package/lib/dataStoreContexts.js.map +1 -1
  154. package/lib/dataStores.d.ts +21 -5
  155. package/lib/dataStores.d.ts.map +1 -1
  156. package/lib/dataStores.js +103 -59
  157. package/lib/dataStores.js.map +1 -1
  158. package/lib/deltaManagerSummarizerProxy.d.ts.map +1 -1
  159. package/lib/deltaManagerSummarizerProxy.js +2 -0
  160. package/lib/deltaManagerSummarizerProxy.js.map +1 -1
  161. package/lib/deltaScheduler.d.ts.map +1 -1
  162. package/lib/deltaScheduler.js +6 -6
  163. package/lib/deltaScheduler.js.map +1 -1
  164. package/lib/gc/garbageCollection.d.ts.map +1 -1
  165. package/lib/gc/garbageCollection.js +30 -26
  166. package/lib/gc/garbageCollection.js.map +1 -1
  167. package/lib/gc/gcConfigs.js +13 -11
  168. package/lib/gc/gcConfigs.js.map +1 -1
  169. package/lib/gc/gcHelpers.d.ts +1 -0
  170. package/lib/gc/gcHelpers.d.ts.map +1 -1
  171. package/lib/gc/gcHelpers.js +5 -6
  172. package/lib/gc/gcHelpers.js.map +1 -1
  173. package/lib/gc/gcSummaryStateTracker.js +4 -6
  174. package/lib/gc/gcSummaryStateTracker.js.map +1 -1
  175. package/lib/gc/gcTelemetry.d.ts.map +1 -1
  176. package/lib/gc/gcTelemetry.js +45 -34
  177. package/lib/gc/gcTelemetry.js.map +1 -1
  178. package/lib/id-compressor/idCompressor.d.ts +3 -3
  179. package/lib/id-compressor/idCompressor.d.ts.map +1 -1
  180. package/lib/id-compressor/idCompressor.js +52 -52
  181. package/lib/id-compressor/idCompressor.js.map +1 -1
  182. package/lib/id-compressor/idRange.js +2 -2
  183. package/lib/id-compressor/idRange.js.map +1 -1
  184. package/lib/id-compressor/sessionIdNormalizer.js +11 -16
  185. package/lib/id-compressor/sessionIdNormalizer.js.map +1 -1
  186. package/lib/index.d.ts +1 -1
  187. package/lib/index.d.ts.map +1 -1
  188. package/lib/index.js.map +1 -1
  189. package/lib/opLifecycle/batchManager.js +10 -6
  190. package/lib/opLifecycle/batchManager.js.map +1 -1
  191. package/lib/opLifecycle/opCompressor.d.ts +2 -2
  192. package/lib/opLifecycle/opCompressor.d.ts.map +1 -1
  193. package/lib/opLifecycle/opCompressor.js +8 -3
  194. package/lib/opLifecycle/opCompressor.js.map +1 -1
  195. package/lib/opLifecycle/opDecompressor.d.ts +2 -2
  196. package/lib/opLifecycle/opDecompressor.d.ts.map +1 -1
  197. package/lib/opLifecycle/opDecompressor.js +13 -11
  198. package/lib/opLifecycle/opDecompressor.js.map +1 -1
  199. package/lib/opLifecycle/opGroupingManager.js +13 -5
  200. package/lib/opLifecycle/opGroupingManager.js.map +1 -1
  201. package/lib/opLifecycle/opSplitter.d.ts +2 -2
  202. package/lib/opLifecycle/opSplitter.d.ts.map +1 -1
  203. package/lib/opLifecycle/opSplitter.js +12 -8
  204. package/lib/opLifecycle/opSplitter.js.map +1 -1
  205. package/lib/opLifecycle/outbox.d.ts +6 -5
  206. package/lib/opLifecycle/outbox.d.ts.map +1 -1
  207. package/lib/opLifecycle/outbox.js +7 -15
  208. package/lib/opLifecycle/outbox.js.map +1 -1
  209. package/lib/opLifecycle/remoteMessageProcessor.d.ts +6 -1
  210. package/lib/opLifecycle/remoteMessageProcessor.d.ts.map +1 -1
  211. package/lib/opLifecycle/remoteMessageProcessor.js +8 -2
  212. package/lib/opLifecycle/remoteMessageProcessor.js.map +1 -1
  213. package/lib/opProperties.js +1 -2
  214. package/lib/opProperties.js.map +1 -1
  215. package/lib/packageVersion.d.ts +1 -1
  216. package/lib/packageVersion.js +1 -1
  217. package/lib/packageVersion.js.map +1 -1
  218. package/lib/pendingStateManager.d.ts +6 -3
  219. package/lib/pendingStateManager.d.ts.map +1 -1
  220. package/lib/pendingStateManager.js +41 -32
  221. package/lib/pendingStateManager.js.map +1 -1
  222. package/lib/scheduleManager.d.ts.map +1 -1
  223. package/lib/scheduleManager.js +16 -12
  224. package/lib/scheduleManager.js.map +1 -1
  225. package/lib/summary/orderedClientElection.d.ts +2 -1
  226. package/lib/summary/orderedClientElection.d.ts.map +1 -1
  227. package/lib/summary/orderedClientElection.js +19 -20
  228. package/lib/summary/orderedClientElection.js.map +1 -1
  229. package/lib/summary/runningSummarizer.d.ts +3 -5
  230. package/lib/summary/runningSummarizer.d.ts.map +1 -1
  231. package/lib/summary/runningSummarizer.js +43 -67
  232. package/lib/summary/runningSummarizer.js.map +1 -1
  233. package/lib/summary/summarizer.js +6 -9
  234. package/lib/summary/summarizer.js.map +1 -1
  235. package/lib/summary/summarizerClientElection.js +5 -9
  236. package/lib/summary/summarizerClientElection.js.map +1 -1
  237. package/lib/summary/summarizerHeuristics.js +8 -12
  238. package/lib/summary/summarizerHeuristics.js.map +1 -1
  239. package/lib/summary/summarizerNode/summarizerNode.d.ts +5 -5
  240. package/lib/summary/summarizerNode/summarizerNode.d.ts.map +1 -1
  241. package/lib/summary/summarizerNode/summarizerNode.js +27 -23
  242. package/lib/summary/summarizerNode/summarizerNode.js.map +1 -1
  243. package/lib/summary/summarizerNode/summarizerNodeUtils.js +2 -4
  244. package/lib/summary/summarizerNode/summarizerNodeUtils.js.map +1 -1
  245. package/lib/summary/summarizerNode/summarizerNodeWithGc.d.ts +4 -3
  246. package/lib/summary/summarizerNode/summarizerNodeWithGc.d.ts.map +1 -1
  247. package/lib/summary/summarizerNode/summarizerNodeWithGc.js +14 -17
  248. package/lib/summary/summarizerNode/summarizerNodeWithGc.js.map +1 -1
  249. package/lib/summary/summaryCollection.js +3 -5
  250. package/lib/summary/summaryCollection.js.map +1 -1
  251. package/lib/summary/summaryFormat.js +1 -2
  252. package/lib/summary/summaryFormat.js.map +1 -1
  253. package/lib/summary/summaryGenerator.d.ts.map +1 -1
  254. package/lib/summary/summaryGenerator.js +68 -22
  255. package/lib/summary/summaryGenerator.js.map +1 -1
  256. package/lib/summary/summaryManager.d.ts +2 -3
  257. package/lib/summary/summaryManager.d.ts.map +1 -1
  258. package/lib/summary/summaryManager.js +10 -8
  259. package/lib/summary/summaryManager.js.map +1 -1
  260. package/package.json +30 -18
  261. package/src/batchTracker.ts +4 -3
  262. package/src/blobManager.ts +113 -15
  263. package/src/connectionTelemetry.ts +7 -3
  264. package/src/containerRuntime.ts +354 -194
  265. package/src/dataStore.ts +10 -1
  266. package/src/dataStoreContext.ts +31 -33
  267. package/src/dataStoreContexts.ts +2 -2
  268. package/src/dataStores.ts +108 -71
  269. package/src/deltaManagerSummarizerProxy.ts +2 -0
  270. package/src/deltaScheduler.ts +6 -10
  271. package/src/gc/garbageCollection.ts +13 -8
  272. package/src/gc/gcHelpers.ts +1 -0
  273. package/src/gc/gcTelemetry.ts +13 -10
  274. package/src/id-compressor/idCompressor.ts +6 -5
  275. package/src/index.ts +0 -1
  276. package/src/opLifecycle/opCompressor.ts +4 -3
  277. package/src/opLifecycle/opDecompressor.ts +4 -3
  278. package/src/opLifecycle/opSplitter.ts +4 -3
  279. package/src/opLifecycle/outbox.ts +13 -25
  280. package/src/opLifecycle/remoteMessageProcessor.ts +8 -2
  281. package/src/packageVersion.ts +1 -1
  282. package/src/pendingStateManager.ts +34 -25
  283. package/src/scheduleManager.ts +2 -2
  284. package/src/summary/orderedClientElection.ts +4 -3
  285. package/src/summary/runningSummarizer.ts +18 -44
  286. package/src/summary/summarizer.ts +2 -2
  287. package/src/summary/summarizerNode/summarizerNode.ts +13 -15
  288. package/src/summary/summarizerNode/summarizerNodeWithGc.ts +8 -7
  289. package/src/summary/summaryGenerator.ts +6 -2
  290. package/src/summary/summaryManager.ts +9 -5
@@ -14,6 +14,7 @@ import {
14
14
  } from "@fluidframework/core-interfaces";
15
15
  import {
16
16
  IAudience,
17
+ IBatchMessage,
17
18
  IContainerContext,
18
19
  IDeltaManager,
19
20
  IRuntime,
@@ -35,14 +36,14 @@ import {
35
36
  } from "@fluidframework/common-utils";
36
37
  import { LazyPromise } from "@fluidframework/core-utils";
37
38
  import {
38
- ChildLogger,
39
+ createChildLogger,
39
40
  raiseConnectedEvent,
40
41
  PerformanceEvent,
41
42
  TaggedLoggerAdapter,
42
43
  MonitoringContext,
43
- loggerToMonitoringContext,
44
44
  wrapError,
45
45
  ITelemetryLoggerExt,
46
+ createChildMonitoringContext,
46
47
  } from "@fluidframework/telemetry-utils";
47
48
  import {
48
49
  DriverHeader,
@@ -173,6 +174,7 @@ import { BindBatchTracker } from "./batchTracker";
173
174
  import { ScheduleManager } from "./scheduleManager";
174
175
  import {
175
176
  BatchMessage,
177
+ IBatch,
176
178
  IBatchCheckpoint,
177
179
  OpCompressor,
178
180
  OpDecompressor,
@@ -443,14 +445,6 @@ export interface IContainerRuntimeOptions {
443
445
  readonly enableGroupedBatching?: boolean;
444
446
  }
445
447
 
446
- /**
447
- * The summary tree returned by the root node. It adds state relevant to the root of the tree.
448
- */
449
- export interface IRootSummaryTreeWithStats extends ISummaryTreeWithStats {
450
- /** The garbage collection stats if GC ran, undefined otherwise. */
451
- gcStats?: IGCStats;
452
- }
453
-
454
448
  /**
455
449
  * Accepted header keys for requests coming to the runtime.
456
450
  */
@@ -540,7 +534,7 @@ const defaultChunkSizeInBytes = 204800;
540
534
  * of the current system, we should close the summarizer and let it recover.
541
535
  * This delay's goal is to prevent tight restart loops
542
536
  */
543
- const defaultCloseSummarizerDelayMs = 10000; // 10 seconds
537
+ const defaultCloseSummarizerDelayMs = 5000; // 5 seconds
544
538
 
545
539
  /**
546
540
  * @deprecated - use ContainerRuntimeMessage instead
@@ -582,6 +576,30 @@ export function getDeviceSpec() {
582
576
  return {};
583
577
  }
584
578
 
579
+ /**
580
+ * Older loader doesn't have a submitBatchFn member, this is the older way of submitting a batch.
581
+ * Rather than exposing the submitFn (now deprecated) and IDeltaManager (dangerous to hand out) to the Outbox,
582
+ * we can provide a partially-applied function to keep those items private to the ContainerRuntime.
583
+ */
584
+ export const makeLegacySendBatchFn =
585
+ (
586
+ submitFn: (type: MessageType, contents: any, batch: boolean, appData?: any) => number,
587
+ deltaManager: Pick<IDeltaManager<unknown, unknown>, "flush">,
588
+ ) =>
589
+ (batch: IBatch) => {
590
+ for (const message of batch.content) {
591
+ submitFn(
592
+ MessageType.Operation,
593
+ // For back-compat (submitFn only works on deserialized content)
594
+ message.contents === undefined ? undefined : JSON.parse(message.contents),
595
+ true, // batch
596
+ message.metadata,
597
+ );
598
+ }
599
+
600
+ deltaManager.flush();
601
+ };
602
+
585
603
  /**
586
604
  * Represents the runtime of the container. Contains helper functions/state of the container.
587
605
  * It will define the store level mappings.
@@ -590,6 +608,9 @@ export class ContainerRuntime
590
608
  extends TypedEventEmitter<IContainerRuntimeEvents>
591
609
  implements IContainerRuntime, IRuntime, ISummarizerRuntime, ISummarizerInternalsProvider
592
610
  {
611
+ /**
612
+ * @deprecated - Will be removed in future major release. Migrate all usage of IFluidRouter to the "entryPoint" pattern. Refer to Removing-IFluidRouter.md
613
+ */
593
614
  public get IFluidRouter() {
594
615
  return this;
595
616
  }
@@ -671,9 +692,12 @@ export class ContainerRuntime
671
692
  const passLogger =
672
693
  backCompatContext.taggedLogger ??
673
694
  new TaggedLoggerAdapter((backCompatContext as OldContainerContextWithLogger).logger);
674
- const logger = ChildLogger.create(passLogger, undefined, {
675
- all: {
676
- runtimeVersion: pkgVersion,
695
+ const logger = createChildLogger({
696
+ logger: passLogger,
697
+ properties: {
698
+ all: {
699
+ runtimeVersion: pkgVersion,
700
+ },
677
701
  },
678
702
  });
679
703
 
@@ -714,8 +738,6 @@ export class ContainerRuntime
714
738
  tryFetchBlob<SerializedIdCompressorWithNoSession>(idCompressorBlobName),
715
739
  ]);
716
740
 
717
- const loadExisting = existing === true || context.existing === true;
718
-
719
741
  // read snapshot blobs needed for BlobManager to load
720
742
  const blobManagerSnapshot = await BlobManager.load(
721
743
  context.baseSnapshot?.trees[blobsTreeName],
@@ -750,9 +772,7 @@ export class ContainerRuntime
750
772
  if (loadSequenceNumberVerification === "log") {
751
773
  logger.sendErrorEvent({ eventName: "SequenceNumberMismatch" }, error);
752
774
  } else {
753
- // Call both close and dispose as closeFn implementation will no longer dispose runtime in future
754
775
  context.closeFn(error);
755
- context.disposeFn?.(error);
756
776
  }
757
777
  }
758
778
  }
@@ -789,7 +809,7 @@ export class ContainerRuntime
789
809
  },
790
810
  containerScope,
791
811
  logger,
792
- loadExisting,
812
+ existing,
793
813
  blobManagerSnapshot,
794
814
  context.storage,
795
815
  idCompressor,
@@ -808,49 +828,48 @@ export class ContainerRuntime
808
828
  return runtime;
809
829
  }
810
830
 
811
- public get options(): ILoaderOptions {
812
- return this.context.options;
813
- }
831
+ public readonly options: ILoaderOptions;
814
832
 
833
+ private readonly _getClientId: () => string | undefined;
815
834
  public get clientId(): string | undefined {
816
- return this.context.clientId;
835
+ return this._getClientId();
817
836
  }
818
837
 
819
- public get clientDetails(): IClientDetails {
820
- return this.context.clientDetails;
821
- }
838
+ public readonly clientDetails: IClientDetails;
822
839
 
823
840
  public get storage(): IDocumentStorageService {
824
841
  return this._storage;
825
842
  }
826
843
 
827
- public get reSubmitFn(): (
828
- type: ContainerMessageType,
829
- content: any,
830
- localOpMetadata: unknown,
831
- opMetadata: Record<string, unknown> | undefined,
832
- ) => void {
833
- // eslint-disable-next-line @typescript-eslint/unbound-method
834
- return this.reSubmitCore;
835
- }
836
-
837
- public get disposeFn(): (error?: ICriticalContainerError) => void {
838
- // In old loaders without dispose functionality, closeFn is equivalent but will also switch container to readonly mode
839
- return this.context.disposeFn ?? this.context.closeFn;
844
+ /** @deprecated - The functionality is no longer exposed publicly */
845
+ public get reSubmitFn() {
846
+ return (
847
+ type: ContainerMessageType,
848
+ contents: any,
849
+ localOpMetadata: unknown,
850
+ opMetadata: Record<string, unknown> | undefined,
851
+ ) => this.reSubmitCore({ type, contents }, localOpMetadata, opMetadata);
840
852
  }
841
853
 
842
- public get closeFn(): (error?: ICriticalContainerError) => void {
843
- if (this._summarizer !== undefined) {
844
- // In cases of summarizer, we want to dispose instead since consumer doesn't interact with this container
845
- return this.disposeFn;
846
- }
847
-
848
- // Also call disposeFn to retain functionality of runtime being disposed on close
849
- return (error?: ICriticalContainerError) => {
850
- this.context.closeFn(error);
851
- this.context.disposeFn?.(error);
852
- };
853
- }
854
+ private readonly submitFn: (
855
+ type: MessageType,
856
+ contents: any,
857
+ batch: boolean,
858
+ appData?: any,
859
+ ) => number;
860
+ /**
861
+ * Although current IContainerContext guarantees submitBatchFn, it is not available on older loaders.
862
+ */
863
+ private readonly submitBatchFn:
864
+ | ((batch: IBatchMessage[], referenceSequenceNumber?: number) => number)
865
+ | undefined;
866
+ private readonly submitSummaryFn: (
867
+ summaryOp: ISummaryContent,
868
+ referenceSequenceNumber?: number,
869
+ ) => number;
870
+ private readonly submitSignalFn: (contents: any) => void;
871
+ public readonly disposeFn: (error?: ICriticalContainerError) => void;
872
+ public readonly closeFn: (error?: ICriticalContainerError) => void;
854
873
 
855
874
  public get flushMode(): FlushMode {
856
875
  return this._flushMode;
@@ -864,8 +883,9 @@ export class ContainerRuntime
864
883
  return this.registry;
865
884
  }
866
885
 
886
+ private readonly _getAttachState: () => AttachState;
867
887
  public get attachState(): AttachState {
868
- return this.context.attachState;
888
+ return this._getAttachState();
869
889
  }
870
890
 
871
891
  public idCompressor: (IIdCompressor & IIdCompressorCore) | undefined;
@@ -1056,11 +1076,21 @@ export class ContainerRuntime
1056
1076
  */
1057
1077
  private readonly idCompressorEnabled: boolean;
1058
1078
 
1079
+ /**
1080
+ * Whether this client is the summarizer client itself (type is summarizerClientType)
1081
+ */
1082
+ private readonly isSummarizerClient: boolean;
1083
+
1084
+ /**
1085
+ * The id of the version used to initially load this runtime, or undefined if it's newly created.
1086
+ */
1087
+ private readonly loadedFromVersionId: string | undefined;
1088
+
1059
1089
  /**
1060
1090
  * @internal
1061
1091
  */
1062
1092
  protected constructor(
1063
- private readonly context: IContainerContext,
1093
+ context: IContainerContext,
1064
1094
  private readonly registry: IFluidDataStoreRegistry,
1065
1095
  metadata: IContainerRuntimeMetadata | undefined,
1066
1096
  electedSummarizerData: ISerializedElection | undefined,
@@ -1087,10 +1117,64 @@ export class ContainerRuntime
1087
1117
  ) {
1088
1118
  super();
1089
1119
 
1090
- this.innerDeltaManager = context.deltaManager;
1091
- this.deltaManager = new DeltaManagerSummarizerProxy(context.deltaManager);
1120
+ const {
1121
+ options,
1122
+ clientDetails,
1123
+ connected,
1124
+ baseSnapshot,
1125
+ submitFn,
1126
+ submitBatchFn,
1127
+ submitSummaryFn,
1128
+ submitSignalFn,
1129
+ disposeFn,
1130
+ closeFn,
1131
+ deltaManager,
1132
+ quorum,
1133
+ audience,
1134
+ loader,
1135
+ pendingLocalState,
1136
+ supportedFeatures,
1137
+ } = context;
1138
+
1139
+ this.innerDeltaManager = deltaManager;
1140
+ this.deltaManager = new DeltaManagerSummarizerProxy(this.innerDeltaManager);
1141
+
1142
+ // Here we could wrap/intercept on these functions to block/modify outgoing messages if needed.
1143
+ // This makes ContainerRuntime the final gatekeeper for outgoing messages.
1144
+ this.submitFn = submitFn;
1145
+ this.submitBatchFn = submitBatchFn;
1146
+ this.submitSummaryFn = submitSummaryFn;
1147
+ this.submitSignalFn = submitSignalFn;
1148
+
1149
+ this.options = options;
1150
+ this.clientDetails = clientDetails;
1151
+ this.isSummarizerClient = this.clientDetails.type === summarizerClientType;
1152
+ this.loadedFromVersionId = context.getLoadedFromVersion()?.id;
1153
+ this._getClientId = () => context.clientId;
1154
+ this._getAttachState = () => context.attachState;
1155
+ this.getAbsoluteUrl = async (relativeUrl: string) => {
1156
+ if (context.getAbsoluteUrl === undefined) {
1157
+ throw new Error("Driver does not implement getAbsoluteUrl");
1158
+ }
1159
+ if (this.attachState !== AttachState.Attached) {
1160
+ return undefined;
1161
+ }
1162
+ return context.getAbsoluteUrl(relativeUrl);
1163
+ };
1164
+ // TODO: Consider that the Container could just listen to these events itself, or even more appropriately maybe the
1165
+ // customer should observe dirty state on the runtime (the owner of dirty state) directly, rather than on the IContainer.
1166
+ this.on("dirty", () => context.updateDirtyContainerState(true));
1167
+ this.on("saved", () => context.updateDirtyContainerState(false));
1168
+
1169
+ // In old loaders without dispose functionality, closeFn is equivalent but will also switch container to readonly mode
1170
+ this.disposeFn = disposeFn ?? closeFn;
1171
+ // In cases of summarizer, we want to dispose instead since consumer doesn't interact with this container
1172
+ this.closeFn = this.isSummarizerClient ? this.disposeFn : closeFn;
1092
1173
 
1093
- this.mc = loggerToMonitoringContext(ChildLogger.create(this.logger, "ContainerRuntime"));
1174
+ this.mc = createChildMonitoringContext({
1175
+ logger: this.logger,
1176
+ namespace: "ContainerRuntime",
1177
+ });
1094
1178
 
1095
1179
  let loadSummaryNumber: number;
1096
1180
  // Get the container creation metadata. For new container, we initialize these. For existing containers,
@@ -1122,7 +1206,9 @@ export class ContainerRuntime
1122
1206
 
1123
1207
  this.messageAtLastSummary = metadata?.message;
1124
1208
 
1125
- this._connected = this.context.connected;
1209
+ // Note that we only need to pull the *initial* connected state from the context.
1210
+ // Later updates come through calls to setConnectionState.
1211
+ this._connected = connected;
1126
1212
 
1127
1213
  this.gcTombstoneEnforcementAllowed = shouldAllowGcTombstoneEnforcement(
1128
1214
  metadata?.gcFeatureMatrix?.tombstoneGeneration /* persisted */,
@@ -1151,7 +1237,7 @@ export class ContainerRuntime
1151
1237
 
1152
1238
  const opSplitter = new OpSplitter(
1153
1239
  chunks,
1154
- this.context.submitBatchFn,
1240
+ this.submitBatchFn,
1155
1241
  disableChunking === true ? Number.POSITIVE_INFINITY : runtimeOptions.chunkSizeInBytes,
1156
1242
  runtimeOptions.maxBatchSizeInBytes,
1157
1243
  this.mc.logger,
@@ -1192,7 +1278,7 @@ export class ContainerRuntime
1192
1278
 
1193
1279
  if (
1194
1280
  runtimeOptions.flushMode === (FlushModeExperimental.Async as unknown as FlushMode) &&
1195
- context.supportedFeatures?.get("referenceSequenceNumbers") !== true
1281
+ supportedFeatures?.get("referenceSequenceNumbers") !== true
1196
1282
  ) {
1197
1283
  // The loader does not support reference sequence numbers, falling back on FlushMode.TurnBased
1198
1284
  this.mc.logger.sendErrorEvent({ eventName: "FlushModeFallback" });
@@ -1201,7 +1287,7 @@ export class ContainerRuntime
1201
1287
  this._flushMode = runtimeOptions.flushMode;
1202
1288
  }
1203
1289
 
1204
- const pendingRuntimeState = context.pendingLocalState as IPendingRuntimeState | undefined;
1290
+ const pendingRuntimeState = pendingLocalState as IPendingRuntimeState | undefined;
1205
1291
 
1206
1292
  const maxSnapshotCacheDurationMs = this._storage?.policies?.maximumCacheDurationMs;
1207
1293
  if (
@@ -1217,12 +1303,12 @@ export class ContainerRuntime
1217
1303
  this.garbageCollector = GarbageCollector.create({
1218
1304
  runtime: this,
1219
1305
  gcOptions: this.runtimeOptions.gcOptions,
1220
- baseSnapshot: context.baseSnapshot,
1306
+ baseSnapshot,
1221
1307
  baseLogger: this.mc.logger,
1222
1308
  existing,
1223
1309
  metadata,
1224
1310
  createContainerMetadata: this.createContainerMetadata,
1225
- isSummarizerClient: this.context.clientDetails.type === summarizerClientType,
1311
+ isSummarizerClient: this.isSummarizerClient,
1226
1312
  getNodePackagePath: async (nodePath: string) => this.getGCNodePackagePath(nodePath),
1227
1313
  getLastSummaryTimestampMs: () => this.messageAtLastSummary?.timestamp,
1228
1314
  readAndParseBlob: async <T>(id: string) => readAndParse<T>(this.storage, id),
@@ -1233,14 +1319,14 @@ export class ContainerRuntime
1233
1319
 
1234
1320
  const loadedFromSequenceNumber = this.deltaManager.initialSequenceNumber;
1235
1321
  this.summarizerNode = createRootSummarizerNodeWithGC(
1236
- ChildLogger.create(this.logger, "SummarizerNode"),
1322
+ createChildLogger({ logger: this.logger, namespace: "SummarizerNode" }),
1237
1323
  // Summarize function to call when summarize is called. Summarizer node always tracks summary state.
1238
1324
  async (fullTree: boolean, trackState: boolean, telemetryContext?: ITelemetryContext) =>
1239
1325
  this.summarizeInternal(fullTree, trackState, telemetryContext),
1240
1326
  // Latest change sequence number, no changes since summary applied yet
1241
1327
  loadedFromSequenceNumber,
1242
1328
  // Summary reference sequence number, undefined if no summary yet
1243
- context.baseSnapshot ? loadedFromSequenceNumber : undefined,
1329
+ baseSnapshot !== undefined ? loadedFromSequenceNumber : undefined,
1244
1330
  {
1245
1331
  // Must set to false to prevent sending summary handle which would be pointing to
1246
1332
  // a summary with an older protocol state.
@@ -1257,14 +1343,14 @@ export class ContainerRuntime
1257
1343
  async () => this.garbageCollector.getBaseGCDetails(),
1258
1344
  );
1259
1345
 
1260
- if (context.baseSnapshot) {
1261
- this.summarizerNode.updateBaseSummaryState(context.baseSnapshot);
1346
+ if (baseSnapshot) {
1347
+ this.summarizerNode.updateBaseSummaryState(baseSnapshot);
1262
1348
  }
1263
1349
 
1264
1350
  this.dataStores = new DataStores(
1265
- getSummaryForDatastores(context.baseSnapshot, metadata),
1351
+ getSummaryForDatastores(baseSnapshot, metadata),
1266
1352
  this,
1267
- (attachMsg) => this.submit(ContainerMessageType.Attach, attachMsg),
1353
+ (attachMsg) => this.submit({ type: ContainerMessageType.Attach, contents: attachMsg }),
1268
1354
  (id: string, createParam: CreateChildSummarizerNodeParam) =>
1269
1355
  (
1270
1356
  summarizeInternal: SummarizeInternalFn,
@@ -1291,10 +1377,14 @@ export class ContainerRuntime
1291
1377
  () => this.storage,
1292
1378
  (localId: string, blobId?: string) => {
1293
1379
  if (!this.disposed) {
1294
- this.submit(ContainerMessageType.BlobAttach, undefined, undefined, {
1295
- localId,
1296
- blobId,
1297
- });
1380
+ this.submit(
1381
+ { type: ContainerMessageType.BlobAttach, contents: undefined },
1382
+ undefined,
1383
+ {
1384
+ localId,
1385
+ blobId,
1386
+ },
1387
+ );
1298
1388
  }
1299
1389
  },
1300
1390
  (blobPath: string) => this.garbageCollector.nodeUpdated(blobPath, "Loaded"),
@@ -1305,10 +1395,10 @@ export class ContainerRuntime
1305
1395
  );
1306
1396
 
1307
1397
  this.scheduleManager = new ScheduleManager(
1308
- context.deltaManager,
1398
+ this.innerDeltaManager,
1309
1399
  this,
1310
1400
  () => this.clientId,
1311
- ChildLogger.create(this.logger, "ScheduleManager"),
1401
+ createChildLogger({ logger: this.logger, namespace: "ScheduleManager" }),
1312
1402
  );
1313
1403
 
1314
1404
  this.pendingStateManager = new PendingStateManager(
@@ -1319,8 +1409,10 @@ export class ContainerRuntime
1319
1409
  connected: () => this.connected,
1320
1410
  reSubmit: this.reSubmit.bind(this),
1321
1411
  reSubmitBatch: this.reSubmitBatch.bind(this),
1412
+ isActiveConnection: () => this.innerDeltaManager.active,
1322
1413
  },
1323
1414
  pendingRuntimeState?.pending,
1415
+ this.logger,
1324
1416
  );
1325
1417
 
1326
1418
  const disableCompression = this.mc.config.getBoolean(
@@ -1337,10 +1429,14 @@ export class ContainerRuntime
1337
1429
  const disablePartialFlush = this.mc.config.getBoolean(
1338
1430
  "Fluid.ContainerRuntime.DisablePartialFlush",
1339
1431
  );
1432
+
1433
+ const legacySendBatchFn = makeLegacySendBatchFn(this.submitFn, this.innerDeltaManager);
1434
+
1340
1435
  this.outbox = new Outbox({
1341
1436
  shouldSend: () => this.canSendOps(),
1342
1437
  pendingStateManager: this.pendingStateManager,
1343
- containerContext: this.context,
1438
+ submitBatchFn: this.submitBatchFn,
1439
+ legacySendBatchFn,
1344
1440
  compressor: new OpCompressor(this.mc.logger),
1345
1441
  splitter: opSplitter,
1346
1442
  config: {
@@ -1360,10 +1456,14 @@ export class ContainerRuntime
1360
1456
  closeContainer: this.closeFn,
1361
1457
  });
1362
1458
 
1363
- this.context.quorum.on("removeMember", (clientId: string) => {
1459
+ this._quorum = quorum;
1460
+ this._quorum.on("removeMember", (clientId: string) => {
1364
1461
  this.remoteMessageProcessor.clearPartialMessagesFor(clientId);
1365
1462
  });
1366
1463
 
1464
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
1465
+ this._audience = audience!;
1466
+
1367
1467
  this.summaryStateUpdateMethod = this.mc.config.getString(
1368
1468
  "Fluid.ContainerRuntime.Test.SummaryStateUpdateMethodV2",
1369
1469
  );
@@ -1378,23 +1478,26 @@ export class ContainerRuntime
1378
1478
  this.summaryCollection = new SummaryCollection(this.deltaManager, this.logger);
1379
1479
 
1380
1480
  this.dirtyContainer =
1381
- this.context.attachState !== AttachState.Attached ||
1481
+ this.attachState !== AttachState.Attached ||
1382
1482
  this.pendingStateManager.hasPendingMessages();
1383
- this.context.updateDirtyContainerState(this.dirtyContainer);
1483
+ context.updateDirtyContainerState(this.dirtyContainer);
1384
1484
 
1385
1485
  if (this.summariesDisabled) {
1386
1486
  this.mc.logger.sendTelemetryEvent({ eventName: "SummariesDisabled" });
1387
1487
  } else {
1388
- const orderedClientLogger = ChildLogger.create(this.logger, "OrderedClientElection");
1488
+ const orderedClientLogger = createChildLogger({
1489
+ logger: this.logger,
1490
+ namespace: "OrderedClientElection",
1491
+ });
1389
1492
  const orderedClientCollection = new OrderedClientCollection(
1390
1493
  orderedClientLogger,
1391
- this.context.deltaManager,
1392
- this.context.quorum,
1494
+ this.innerDeltaManager,
1495
+ this._quorum,
1393
1496
  );
1394
1497
  const orderedClientElectionForSummarizer = new OrderedClientElection(
1395
1498
  orderedClientLogger,
1396
1499
  orderedClientCollection,
1397
- electedSummarizerData ?? this.context.deltaManager.lastSequenceNumber,
1500
+ electedSummarizerData ?? this.innerDeltaManager.lastSequenceNumber,
1398
1501
  SummarizerClientElection.isClientEligible,
1399
1502
  );
1400
1503
 
@@ -1405,7 +1508,7 @@ export class ContainerRuntime
1405
1508
  this.maxOpsSinceLastSummary,
1406
1509
  );
1407
1510
 
1408
- if (this.context.clientDetails.type === summarizerClientType) {
1511
+ if (this.isSummarizerClient) {
1409
1512
  this._summarizer = new Summarizer(
1410
1513
  this /* ISummarizerRuntime */,
1411
1514
  () => this.summaryConfiguration,
@@ -1420,19 +1523,19 @@ export class ContainerRuntime
1420
1523
  () => this.innerDeltaManager.active,
1421
1524
  ),
1422
1525
  );
1423
- } else if (
1424
- SummarizerClientElection.clientDetailsPermitElection(this.context.clientDetails)
1425
- ) {
1526
+ } else if (SummarizerClientElection.clientDetailsPermitElection(this.clientDetails)) {
1426
1527
  // Only create a SummaryManager and SummarizerClientElection
1427
1528
  // if summaries are enabled and we are not the summarizer client.
1428
1529
  const defaultAction = () => {
1429
1530
  if (this.summaryCollection.opsSinceLastAck > this.maxOpsSinceLastSummary) {
1430
- this.logger.sendTelemetryEvent({ eventName: "SummaryStatus:Behind" });
1531
+ this.mc.logger.sendTelemetryEvent({ eventName: "SummaryStatus:Behind" });
1431
1532
  // unregister default to no log on every op after falling behind
1432
1533
  // and register summary ack handler to re-register this handler
1433
1534
  // after successful summary
1434
1535
  this.summaryCollection.once(MessageType.SummaryAck, () => {
1435
- this.logger.sendTelemetryEvent({ eventName: "SummaryStatus:CaughtUp" });
1536
+ this.mc.logger.sendTelemetryEvent({
1537
+ eventName: "SummaryStatus:CaughtUp",
1538
+ });
1436
1539
  // we've caught up, so re-register the default action to monitor for
1437
1540
  // falling behind, and unregister ourself
1438
1541
  this.summaryCollection.on("default", defaultAction);
@@ -1449,7 +1552,7 @@ export class ContainerRuntime
1449
1552
  this, // IConnectedState
1450
1553
  this.summaryCollection,
1451
1554
  this.logger,
1452
- this.formRequestSummarizerFn(this.context.loader),
1555
+ this.formRequestSummarizerFn(loader),
1453
1556
  new Throttler(
1454
1557
  60 * 1000, // 60 sec delay window
1455
1558
  30 * 1000, // 30 sec max delay
@@ -1500,7 +1603,7 @@ export class ContainerRuntime
1500
1603
  ...getDeviceSpec(),
1501
1604
  });
1502
1605
 
1503
- this.logger.sendTelemetryEvent({
1606
+ this.mc.logger.sendTelemetryEvent({
1504
1607
  eventName: "ContainerLoadStats",
1505
1608
  ...this.createContainerMetadata,
1506
1609
  ...this.dataStores.containerLoadStats,
@@ -1523,11 +1626,11 @@ export class ContainerRuntime
1523
1626
  groupedBatchingEnabled: this.groupedBatchingEnabled,
1524
1627
  });
1525
1628
 
1526
- ReportOpPerfTelemetry(this.context.clientId, this.deltaManager, this.logger);
1629
+ ReportOpPerfTelemetry(this.clientId, this.deltaManager, this.logger);
1527
1630
  BindBatchTracker(this, this.logger);
1528
1631
 
1529
1632
  this.entryPoint = new LazyPromise(async () => {
1530
- if (this.context.clientDetails.type === summarizerClientType) {
1633
+ if (this.isSummarizerClient) {
1531
1634
  assert(
1532
1635
  this._summarizer !== undefined,
1533
1636
  0x5bf /* Summarizer object is undefined in a summarizer client */,
@@ -1551,7 +1654,7 @@ export class ContainerRuntime
1551
1654
  }
1552
1655
  this._disposed = true;
1553
1656
 
1554
- this.logger.sendTelemetryEvent(
1657
+ this.mc.logger.sendTelemetryEvent(
1555
1658
  {
1556
1659
  eventName: "ContainerRuntimeDisposed",
1557
1660
  isDirty: this.isDirty,
@@ -1575,6 +1678,7 @@ export class ContainerRuntime
1575
1678
  /**
1576
1679
  * Notifies this object about the request made to the container.
1577
1680
  * @param request - Request made to the handler.
1681
+ * @deprecated - Will be removed in future major release. Migrate all usage of IFluidRouter to the "entryPoint" pattern. Refer to Removing-IFluidRouter.md
1578
1682
  */
1579
1683
  public async request(request: IRequest): Promise<IResponse> {
1580
1684
  try {
@@ -1841,14 +1945,11 @@ export class ContainerRuntime
1841
1945
  * Parse an op's type and actual content from given serialized content
1842
1946
  * ! Note: this format needs to be in-line with what is set in the "ContainerRuntime.submit(...)" method
1843
1947
  */
1844
- private parseOpContent(serializedContent?: string): {
1845
- type: ContainerMessageType;
1846
- contents: unknown;
1847
- } {
1948
+ private parseOpContent(serializedContent?: string): ContainerRuntimeMessage {
1848
1949
  assert(serializedContent !== undefined, 0x6d5 /* content must be defined */);
1849
- const parsed = JSON.parse(serializedContent);
1850
- assert(parsed.type !== undefined, 0x6d6 /* incorrect op content format */);
1851
- return { type: parsed.type as ContainerMessageType, contents: parsed.contents };
1950
+ const { type, contents } = JSON.parse(serializedContent);
1951
+ assert(type !== undefined, 0x6d6 /* incorrect op content format */);
1952
+ return { type, contents };
1852
1953
  }
1853
1954
 
1854
1955
  private async applyStashedOp(op: string): Promise<unknown> {
@@ -1926,6 +2027,14 @@ export class ContainerRuntime
1926
2027
  // There might be no change of state due to Container calling this API after loading runtime.
1927
2028
  const changeOfState = this._connected !== connected;
1928
2029
  const reconnection = changeOfState && !connected;
2030
+
2031
+ // We need to flush the ops currently collected by Outbox to preserve original order.
2032
+ // This flush NEEDS to happen before we set the ContainerRuntime to "connected".
2033
+ // We want these ops to get to the PendingStateManager without sending to service and have them return to the Outbox upon calling "replayPendingStates".
2034
+ if (changeOfState && connected) {
2035
+ this.flush();
2036
+ }
2037
+
1929
2038
  this._connected = connected;
1930
2039
 
1931
2040
  if (!connected) {
@@ -1991,6 +2100,12 @@ export class ContainerRuntime
1991
2100
 
1992
2101
  private _processedClientSequenceNumber: number | undefined;
1993
2102
 
2103
+ /**
2104
+ * Direct the message to the correct subsystem for processing, and implement other side effects
2105
+ * @param message - The unpacked message. Likely a ContainerRuntimeMessage, but could also be a system op
2106
+ * @param local - Did this client send the op?
2107
+ * @param runtimeMessage - Does this appear like a current ContainerRuntimeMessage? If true, certain validation will occur.
2108
+ */
1994
2109
  private processCore(
1995
2110
  message: ISequencedDocumentMessage,
1996
2111
  local: boolean,
@@ -2059,9 +2174,7 @@ export class ContainerRuntime
2059
2174
  }
2060
2175
  }
2061
2176
 
2062
- if (runtimeMessage || this.groupedBatchingEnabled) {
2063
- this.emit("op", message, runtimeMessage);
2064
- }
2177
+ this.emit("op", message, runtimeMessage);
2065
2178
 
2066
2179
  this.scheduleManager.afterOpProcessing(undefined, message);
2067
2180
 
@@ -2091,7 +2204,7 @@ export class ContainerRuntime
2091
2204
  */
2092
2205
  private sendSignalTelemetryEvent(clientSignalSequenceNumber: number) {
2093
2206
  const duration = Date.now() - this._perfSignalData.signalTimestamp;
2094
- this.logger.sendPerformanceEvent({
2207
+ this.mc.logger.sendPerformanceEvent({
2095
2208
  eventName: "SignalLatency",
2096
2209
  duration,
2097
2210
  signalsLost: this._perfSignalData.signalsLost,
@@ -2119,7 +2232,7 @@ export class ContainerRuntime
2119
2232
  ) {
2120
2233
  this._perfSignalData.signalsLost++;
2121
2234
  this._perfSignalData.trackingSignalSequenceNumber = undefined;
2122
- this.logger.sendErrorEvent({
2235
+ this.mc.logger.sendErrorEvent({
2123
2236
  eventName: "SignalLost",
2124
2237
  type: envelope.contents.type,
2125
2238
  signalsLost: this._perfSignalData.signalsLost,
@@ -2147,6 +2260,12 @@ export class ContainerRuntime
2147
2260
  this.dataStores.processSignal(envelope.address, transformed, local);
2148
2261
  }
2149
2262
 
2263
+ /**
2264
+ * Returns the runtime of the data store.
2265
+ * @param id - Id supplied during creating the data store.
2266
+ * @param wait - True if you want to wait for it.
2267
+ * @deprecated - Use getAliasedDataStoreEntryPoint instead to get an aliased data store's entry point.
2268
+ */
2150
2269
  public async getRootDataStore(id: string, wait = true): Promise<IFluidRouter> {
2151
2270
  return this.getRootDataStoreChannel(id, wait);
2152
2271
  }
@@ -2222,15 +2341,30 @@ export class ContainerRuntime
2222
2341
  return result;
2223
2342
  }
2224
2343
 
2225
- public async createDataStore(pkg: string | string[]): Promise<IDataStore> {
2226
- const internalId = uuid();
2227
- return channelToDataStore(
2228
- await this._createDataStore(pkg, internalId),
2229
- internalId,
2230
- this,
2231
- this.dataStores,
2232
- this.mc.logger,
2233
- );
2344
+ /**
2345
+ * Returns the aliased data store's entryPoint, given the alias.
2346
+ * @param alias - The alias for the data store.
2347
+ * @returns - The data store's entry point (IFluidHandle) if it exists and is aliased. Returns undefined if no
2348
+ * data store has been assigned the given alias.
2349
+ */
2350
+ public async getAliasedDataStoreEntryPoint(
2351
+ alias: string,
2352
+ ): Promise<IFluidHandle<FluidObject> | undefined> {
2353
+ await this.dataStores.waitIfPendingAlias(alias);
2354
+ const internalId = this.internalId(alias);
2355
+ const context = await this.dataStores.getDataStoreIfAvailable(internalId, { wait: false });
2356
+ // If the data store is not available or not an alias, return undefined.
2357
+ if (context === undefined || !(await context.isRoot())) {
2358
+ return undefined;
2359
+ }
2360
+
2361
+ const channel = await context.realize();
2362
+ if (channel.entryPoint === undefined) {
2363
+ throw new UsageError(
2364
+ "entryPoint must be defined on data store runtime for using getAliasedDataStoreEntryPoint",
2365
+ );
2366
+ }
2367
+ return channel.entryPoint;
2234
2368
  }
2235
2369
 
2236
2370
  public createDetachedRootDataStore(
@@ -2247,25 +2381,37 @@ export class ContainerRuntime
2247
2381
  return this.dataStores.createDetachedDataStoreCore(pkg, false);
2248
2382
  }
2249
2383
 
2384
+ public async createDataStore(pkg: string | string[]): Promise<IDataStore> {
2385
+ const id = uuid();
2386
+ return channelToDataStore(
2387
+ await this.dataStores
2388
+ ._createFluidDataStoreContext(Array.isArray(pkg) ? pkg : [pkg], id)
2389
+ .realize(),
2390
+ id,
2391
+ this,
2392
+ this.dataStores,
2393
+ this.mc.logger,
2394
+ );
2395
+ }
2396
+
2397
+ /**
2398
+ * @deprecated 0.16 Issue #1537, #3631
2399
+ * @internal
2400
+ */
2250
2401
  public async _createDataStoreWithProps(
2251
2402
  pkg: string | string[],
2252
2403
  props?: any,
2253
2404
  id = uuid(),
2254
2405
  ): Promise<IDataStore> {
2255
- const fluidDataStore = await this.dataStores
2256
- ._createFluidDataStoreContext(Array.isArray(pkg) ? pkg : [pkg], id, props)
2257
- .realize();
2258
- return channelToDataStore(fluidDataStore, id, this, this.dataStores, this.mc.logger);
2259
- }
2260
-
2261
- private async _createDataStore(
2262
- pkg: string | string[],
2263
- id = uuid(),
2264
- props?: any,
2265
- ): Promise<IFluidDataStoreChannel> {
2266
- return this.dataStores
2267
- ._createFluidDataStoreContext(Array.isArray(pkg) ? pkg : [pkg], id, props)
2268
- .realize();
2406
+ return channelToDataStore(
2407
+ await this.dataStores
2408
+ ._createFluidDataStoreContext(Array.isArray(pkg) ? pkg : [pkg], id, props)
2409
+ .realize(),
2410
+ id,
2411
+ this,
2412
+ this.dataStores,
2413
+ this.mc.logger,
2414
+ );
2269
2415
  }
2270
2416
 
2271
2417
  private canSendOps() {
@@ -2281,13 +2427,14 @@ export class ContainerRuntime
2281
2427
  return this.flushMode !== FlushMode.Immediate || this._orderSequentiallyCalls !== 0;
2282
2428
  }
2283
2429
 
2430
+ private readonly _quorum: IQuorumClients;
2284
2431
  public getQuorum(): IQuorumClients {
2285
- return this.context.quorum;
2432
+ return this._quorum;
2286
2433
  }
2287
2434
 
2435
+ private readonly _audience: IAudience;
2288
2436
  public getAudience(): IAudience {
2289
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
2290
- return this.context.audience!;
2437
+ return this._audience;
2291
2438
  }
2292
2439
 
2293
2440
  /**
@@ -2298,7 +2445,7 @@ export class ContainerRuntime
2298
2445
  return this.dirtyContainer;
2299
2446
  }
2300
2447
 
2301
- private isContainerMessageDirtyable(type: ContainerMessageType, contents: any) {
2448
+ private isContainerMessageDirtyable({ type, contents }: ContainerRuntimeMessage) {
2302
2449
  // For legacy purposes, exclude the old built-in AgentScheduler from dirty consideration as a special-case.
2303
2450
  // Ultimately we should have no special-cases from the ContainerRuntime's perspective.
2304
2451
  if (type === ContainerMessageType.Attach) {
@@ -2347,12 +2494,12 @@ export class ContainerRuntime
2347
2494
  public submitSignal(type: string, content: any) {
2348
2495
  this.verifyNotClosed();
2349
2496
  const envelope = this.createNewSignalEnvelope(undefined /* address */, type, content);
2350
- return this.context.submitSignalFn(envelope);
2497
+ return this.submitSignalFn(envelope);
2351
2498
  }
2352
2499
 
2353
2500
  public submitDataStoreSignal(address: string, type: string, content: any) {
2354
2501
  const envelope = this.createNewSignalEnvelope(address, type, content);
2355
- return this.context.submitSignalFn(envelope);
2502
+ return this.submitSignalFn(envelope);
2356
2503
  }
2357
2504
 
2358
2505
  public setAttachState(attachState: AttachState.Attaching | AttachState.Attached): void {
@@ -2404,15 +2551,7 @@ export class ContainerRuntime
2404
2551
  return summarizeResult.summary;
2405
2552
  }
2406
2553
 
2407
- public async getAbsoluteUrl(relativeUrl: string): Promise<string | undefined> {
2408
- if (this.context.getAbsoluteUrl === undefined) {
2409
- throw new Error("Driver does not implement getAbsoluteUrl");
2410
- }
2411
- if (this.attachState !== AttachState.Attached) {
2412
- return undefined;
2413
- }
2414
- return this.context.getAbsoluteUrl(relativeUrl);
2415
- }
2554
+ public readonly getAbsoluteUrl: (relativeUrl: string) => Promise<string | undefined>;
2416
2555
 
2417
2556
  private async summarizeInternal(
2418
2557
  fullTree: boolean,
@@ -2453,7 +2592,7 @@ export class ContainerRuntime
2453
2592
  fullGC?: boolean;
2454
2593
  /** True to run GC sweep phase after the mark phase */
2455
2594
  runSweep?: boolean;
2456
- }): Promise<IRootSummaryTreeWithStats> {
2595
+ }): Promise<ISummaryTreeWithStats> {
2457
2596
  this.verifyNotClosed();
2458
2597
 
2459
2598
  const {
@@ -2476,9 +2615,8 @@ export class ContainerRuntime
2476
2615
  });
2477
2616
 
2478
2617
  try {
2479
- let gcStats: IGCStats | undefined;
2480
2618
  if (runGC) {
2481
- gcStats = await this.collectGarbage(
2619
+ await this.collectGarbage(
2482
2620
  { logger: summaryLogger, runSweep, fullGC },
2483
2621
  telemetryContext,
2484
2622
  );
@@ -2495,9 +2633,9 @@ export class ContainerRuntime
2495
2633
  0x12f /* "Container Runtime's summarize should always return a tree" */,
2496
2634
  );
2497
2635
 
2498
- return { stats, summary, gcStats };
2636
+ return { stats, summary };
2499
2637
  } finally {
2500
- this.logger.sendTelemetryEvent({
2638
+ this.mc.logger.sendTelemetryEvent({
2501
2639
  eventName: "SummarizeTelemetry",
2502
2640
  details: telemetryContext.serialize(),
2503
2641
  });
@@ -2700,8 +2838,11 @@ export class ContainerRuntime
2700
2838
  // The summary number for this summary. This will be updated during the summary process, so get it now and
2701
2839
  // use it for all events logged during this summary.
2702
2840
  const summaryNumber = this.nextSummaryNumber;
2703
- const summaryNumberLogger = ChildLogger.create(summaryLogger, undefined, {
2704
- all: { summaryNumber },
2841
+ const summaryNumberLogger = createChildLogger({
2842
+ logger: summaryLogger,
2843
+ properties: {
2844
+ all: { summaryNumber },
2845
+ },
2705
2846
  });
2706
2847
 
2707
2848
  assert(this.outbox.isEmpty, 0x3d1 /* Can't trigger summary in the middle of a batch */);
@@ -2709,7 +2850,11 @@ export class ContainerRuntime
2709
2850
  let latestSnapshotVersionId: string | undefined;
2710
2851
  if (refreshLatestAck) {
2711
2852
  const latestSnapshotInfo = await this.refreshLatestSummaryAckFromServer(
2712
- ChildLogger.create(summaryNumberLogger, undefined, { all: { safeSummary: true } }),
2853
+ createChildLogger({
2854
+ logger: summaryNumberLogger,
2855
+ namespace: undefined,
2856
+ properties: { all: { safeSummary: true } },
2857
+ }),
2713
2858
  );
2714
2859
  const latestSnapshotRefSeq = latestSnapshotInfo.latestSnapshotRefSeq;
2715
2860
  latestSnapshotVersionId = latestSnapshotInfo.latestSnapshotVersionId;
@@ -2788,7 +2933,7 @@ export class ContainerRuntime
2788
2933
  }
2789
2934
 
2790
2935
  const trace = Trace.start();
2791
- let summarizeResult: IRootSummaryTreeWithStats;
2936
+ let summarizeResult: ISummaryTreeWithStats;
2792
2937
  // If the GC state needs to be reset, we need to force a full tree summary and update the unreferenced
2793
2938
  // state of all the nodes.
2794
2939
  const forcedFullTree = this.garbageCollector.summaryStateNeedsReset;
@@ -2881,7 +3026,7 @@ export class ContainerRuntime
2881
3026
  } else if (lastAck === undefined) {
2882
3027
  summaryContext = {
2883
3028
  proposalHandle: undefined,
2884
- ackHandle: this.context.getLoadedFromVersion()?.id,
3029
+ ackHandle: this.loadedFromVersionId,
2885
3030
  referenceSequenceNumber: summaryRefSeqNum,
2886
3031
  };
2887
3032
  } else {
@@ -2982,7 +3127,6 @@ export class ContainerRuntime
2982
3127
  this.dirtyContainer = dirty;
2983
3128
  if (this.emitDirtyDocumentEvent) {
2984
3129
  this.emit(dirty ? "dirty" : "saved");
2985
- this.context.updateDirtyContainerState(dirty);
2986
3130
  }
2987
3131
  }
2988
3132
 
@@ -2995,7 +3139,10 @@ export class ContainerRuntime
2995
3139
  address: id,
2996
3140
  contents,
2997
3141
  };
2998
- this.submit(ContainerMessageType.FluidDataStoreOp, envelope, localOpMetadata);
3142
+ this.submit(
3143
+ { type: ContainerMessageType.FluidDataStoreOp, contents: envelope },
3144
+ localOpMetadata,
3145
+ );
2999
3146
  }
3000
3147
 
3001
3148
  public submitDataStoreAliasOp(contents: any, localOpMetadata: unknown): void {
@@ -3004,12 +3151,15 @@ export class ContainerRuntime
3004
3151
  throw new UsageError("malformedDataStoreAliasMessage");
3005
3152
  }
3006
3153
 
3007
- this.submit(ContainerMessageType.Alias, contents, localOpMetadata);
3154
+ this.submit({ type: ContainerMessageType.Alias, contents }, localOpMetadata);
3008
3155
  }
3009
3156
 
3010
- public async uploadBlob(blob: ArrayBufferLike): Promise<IFluidHandle<ArrayBufferLike>> {
3157
+ public async uploadBlob(
3158
+ blob: ArrayBufferLike,
3159
+ signal?: AbortSignal,
3160
+ ): Promise<IFluidHandle<ArrayBufferLike>> {
3011
3161
  this.verifyNotClosed();
3012
- return this.blobManager.createBlob(blob);
3162
+ return this.blobManager.createBlob(blob, signal);
3013
3163
  }
3014
3164
 
3015
3165
  private maybeSubmitIdAllocationOp(type: ContainerMessageType) {
@@ -3047,8 +3197,7 @@ export class ContainerRuntime
3047
3197
  }
3048
3198
 
3049
3199
  private submit(
3050
- type: ContainerMessageType,
3051
- contents: any,
3200
+ containerRuntimeMessage: ContainerRuntimeMessage,
3052
3201
  localOpMetadata: unknown = undefined,
3053
3202
  metadata: Record<string, unknown> | undefined = undefined,
3054
3203
  ): void {
@@ -3061,17 +3210,18 @@ export class ContainerRuntime
3061
3210
  0x132 /* "sending ops in detached container" */,
3062
3211
  );
3063
3212
 
3064
- const serializedContent = JSON.stringify({ type, contents });
3213
+ const serializedContent = JSON.stringify(containerRuntimeMessage);
3065
3214
 
3066
3215
  // Note that the real (non-proxy) delta manager is used here to get the readonly info. This is because
3067
3216
  // container runtime's ability to submit ops depend on the actual readonly state of the delta manager.
3068
3217
  if (this.innerDeltaManager.readOnlyInfo.readonly) {
3069
- this.logger.sendTelemetryEvent({
3218
+ this.mc.logger.sendTelemetryEvent({
3070
3219
  eventName: "SubmitOpInReadonly",
3071
3220
  connected: this.connected,
3072
3221
  });
3073
3222
  }
3074
3223
 
3224
+ const type = containerRuntimeMessage.type;
3075
3225
  const message: BatchMessage = {
3076
3226
  contents: serializedContent,
3077
3227
  type,
@@ -3130,7 +3280,7 @@ export class ContainerRuntime
3130
3280
  throw error;
3131
3281
  }
3132
3282
 
3133
- if (this.isContainerMessageDirtyable(type, contents)) {
3283
+ if (this.isContainerMessageDirtyable(containerRuntimeMessage)) {
3134
3284
  this.updateDocumentDirtyState(true);
3135
3285
  }
3136
3286
  }
@@ -3186,9 +3336,9 @@ export class ContainerRuntime
3186
3336
  assert(this.outbox.isEmpty, 0x3d4 /* System op in the middle of a batch */);
3187
3337
 
3188
3338
  // back-compat: ADO #1385: Make this call unconditional in the future
3189
- return this.context.submitSummaryFn !== undefined
3190
- ? this.context.submitSummaryFn(contents, referenceSequenceNumber)
3191
- : this.context.submitFn(MessageType.Summarize, contents, false);
3339
+ return this.submitSummaryFn !== undefined
3340
+ ? this.submitSummaryFn(contents, referenceSequenceNumber)
3341
+ : this.submitFn(MessageType.Summarize, contents, false);
3192
3342
  }
3193
3343
 
3194
3344
  /**
@@ -3243,38 +3393,38 @@ export class ContainerRuntime
3243
3393
 
3244
3394
  private reSubmit(message: IPendingBatchMessage) {
3245
3395
  // Need to parse from string for back-compat
3246
- const { contents, type } = this.parseOpContent(message.content);
3247
- this.reSubmitCore(type, contents, message.localOpMetadata, message.opMetadata);
3396
+ const containerRuntimeMessage = this.parseOpContent(message.content);
3397
+ this.reSubmitCore(containerRuntimeMessage, message.localOpMetadata, message.opMetadata);
3248
3398
  }
3249
3399
 
3250
3400
  /**
3251
3401
  * Finds the right store and asks it to resubmit the message. This typically happens when we
3252
3402
  * reconnect and there are pending messages.
3253
- * @param content - The content of the original message.
3403
+ * @param message - The original ContainerRuntimeMessage.
3254
3404
  * @param localOpMetadata - The local metadata associated with the original message.
3255
3405
  */
3256
3406
  private reSubmitCore(
3257
- type: ContainerMessageType,
3258
- content: any,
3407
+ message: ContainerRuntimeMessage,
3259
3408
  localOpMetadata: unknown,
3260
3409
  opMetadata: Record<string, unknown> | undefined,
3261
3410
  ) {
3262
- switch (type) {
3411
+ const contents = message.contents;
3412
+ switch (message.type) {
3263
3413
  case ContainerMessageType.FluidDataStoreOp:
3264
3414
  // For Operations, call resubmitDataStoreOp which will find the right store
3265
3415
  // and trigger resubmission on it.
3266
- this.dataStores.resubmitDataStoreOp(content, localOpMetadata);
3416
+ this.dataStores.resubmitDataStoreOp(contents, localOpMetadata);
3267
3417
  break;
3268
3418
  case ContainerMessageType.Attach:
3269
3419
  case ContainerMessageType.Alias:
3270
- this.submit(type, content, localOpMetadata);
3420
+ this.submit(message, localOpMetadata);
3271
3421
  break;
3272
3422
  case ContainerMessageType.IdAllocation:
3273
3423
  // Remove the stashedState from the op if it's a stashed op
3274
- if (content.stashedState !== undefined) {
3275
- delete content.stashedState;
3424
+ if (contents.stashedState !== undefined) {
3425
+ delete contents.stashedState;
3276
3426
  }
3277
- this.submit(type, content, localOpMetadata);
3427
+ this.submit(message, localOpMetadata);
3278
3428
  break;
3279
3429
  case ContainerMessageType.ChunkedOp:
3280
3430
  throw new Error(`chunkedOp not expected here`);
@@ -3282,10 +3432,13 @@ export class ContainerRuntime
3282
3432
  this.blobManager.reSubmit(opMetadata);
3283
3433
  break;
3284
3434
  case ContainerMessageType.Rejoin:
3285
- this.submit(type, content);
3435
+ this.submit(message);
3286
3436
  break;
3287
3437
  default:
3288
- unreachableCase(type, `Unknown ContainerMessageType: ${type}`);
3438
+ unreachableCase(
3439
+ message.type,
3440
+ `Unknown ContainerMessageType [type: ${message.type}]`,
3441
+ );
3289
3442
  }
3290
3443
  }
3291
3444
 
@@ -3348,7 +3501,7 @@ export class ContainerRuntime
3348
3501
  * change that started fetching latest snapshot always.
3349
3502
  */
3350
3503
  if (fetchResult.latestSnapshotRefSeq < summaryRefSeq) {
3351
- fetchResult = await this.fetchSnapshotFromStorage(
3504
+ fetchResult = await this.fetchSnapshotFromStorageAndClose(
3352
3505
  summaryLogger,
3353
3506
  {
3354
3507
  eventName: "RefreshLatestSummaryAckFetchBackCompat",
@@ -3381,7 +3534,7 @@ export class ContainerRuntime
3381
3534
  fetchedSnapshotRefSeq: fetchResult.latestSnapshotRefSeq,
3382
3535
  },
3383
3536
  );
3384
- this.closeFn(error);
3537
+ this.disposeFn(error);
3385
3538
  throw error;
3386
3539
  }
3387
3540
 
@@ -3455,10 +3608,15 @@ export class ContainerRuntime
3455
3608
  event: ITelemetryGenericEvent,
3456
3609
  readAndParseBlob: ReadAndParseBlob,
3457
3610
  ): Promise<{ snapshotTree: ISnapshotTree; versionId: string; latestSnapshotRefSeq: number }> {
3458
- return this.fetchSnapshotFromStorage(logger, event, readAndParseBlob, null /* latest */);
3611
+ return this.fetchSnapshotFromStorageAndClose(
3612
+ logger,
3613
+ event,
3614
+ readAndParseBlob,
3615
+ null /* latest */,
3616
+ );
3459
3617
  }
3460
3618
 
3461
- private async fetchSnapshotFromStorage(
3619
+ private async fetchSnapshotFromStorageAndClose(
3462
3620
  logger: ITelemetryLoggerExt,
3463
3621
  event: ITelemetryGenericEvent,
3464
3622
  readAndParseBlob: ReadAndParseBlob,
@@ -3514,9 +3672,7 @@ export class ContainerRuntime
3514
3672
  // We choose to close the summarizer after the snapshot cache is updated to avoid
3515
3673
  // situations which the main client (which is likely to be re-elected as the leader again)
3516
3674
  // loads the summarizer from cache.
3517
- if (this.summaryStateUpdateMethod === "restart") {
3518
- const error = new GenericError("Restarting summarizer instead of refreshing");
3519
-
3675
+ if (this.summaryStateUpdateMethod !== "refreshFromSnapshot") {
3520
3676
  this.mc.logger.sendTelemetryEvent(
3521
3677
  {
3522
3678
  ...event,
@@ -3526,14 +3682,13 @@ export class ContainerRuntime
3526
3682
  versionId: versionId != null ? versionId : undefined,
3527
3683
  closeSummarizerDelayMs: this.closeSummarizerDelayMs,
3528
3684
  },
3529
- error,
3685
+ new GenericError("Restarting summarizer instead of refreshing"),
3530
3686
  );
3531
3687
 
3532
- // Delay 10 seconds before restarting summarizer to prevent the summarizer from restarting too frequently.
3688
+ // Delay before restarting summarizer to prevent the summarizer from restarting too frequently.
3533
3689
  await delay(this.closeSummarizerDelayMs);
3534
3690
  this._summarizer?.stop("latestSummaryStateStale");
3535
- this.closeFn();
3536
- throw error;
3691
+ this.disposeFn();
3537
3692
  }
3538
3693
 
3539
3694
  return snapshotResults;
@@ -3541,10 +3696,15 @@ export class ContainerRuntime
3541
3696
 
3542
3697
  public notifyAttaching() {} // do nothing (deprecated method)
3543
3698
 
3544
- public getPendingLocalState(): unknown {
3699
+ public async getPendingLocalState(props?: {
3700
+ notifyImminentClosure: boolean;
3701
+ }): Promise<unknown> {
3702
+ this.verifyNotClosed();
3703
+ const waitBlobsToAttach = props?.notifyImminentClosure;
3545
3704
  if (this._orderSequentiallyCalls !== 0) {
3546
3705
  throw new UsageError("can't get state during orderSequentially");
3547
3706
  }
3707
+ const pendingAttachmentBlobs = await this.blobManager.getPendingBlobs(waitBlobsToAttach);
3548
3708
  // Flush pending batch.
3549
3709
  // getPendingLocalState() is only exposed through Container.closeAndGetPendingLocalState(), so it's safe
3550
3710
  // to close current batch.
@@ -3552,12 +3712,12 @@ export class ContainerRuntime
3552
3712
 
3553
3713
  return {
3554
3714
  pending: this.pendingStateManager.getLocalState(),
3555
- pendingAttachmentBlobs: this.blobManager.getPendingBlobs(),
3715
+ pendingAttachmentBlobs,
3556
3716
  };
3557
3717
  }
3558
3718
 
3559
3719
  public readonly summarizeOnDemand: ISummarizer["summarizeOnDemand"] = (...args) => {
3560
- if (this.clientDetails.type === summarizerClientType) {
3720
+ if (this.isSummarizerClient) {
3561
3721
  return this.summarizer.summarizeOnDemand(...args);
3562
3722
  } else if (this.summaryManager !== undefined) {
3563
3723
  return this.summaryManager.summarizeOnDemand(...args);
@@ -3570,7 +3730,7 @@ export class ContainerRuntime
3570
3730
  };
3571
3731
 
3572
3732
  public readonly enqueueSummarize: ISummarizer["enqueueSummarize"] = (...args) => {
3573
- if (this.clientDetails.type === summarizerClientType) {
3733
+ if (this.isSummarizerClient) {
3574
3734
  return this.summarizer.enqueueSummarize(...args);
3575
3735
  } else if (this.summaryManager !== undefined) {
3576
3736
  return this.summaryManager.enqueueSummarize(...args);