@fluidframework/container-runtime 2.0.0-internal.5.3.1 → 2.0.0-internal.5.4.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 (231) hide show
  1. package/CHANGELOG.md +9 -0
  2. package/dist/batchTracker.d.ts.map +1 -1
  3. package/dist/batchTracker.js +1 -1
  4. package/dist/batchTracker.js.map +1 -1
  5. package/dist/blobManager.d.ts +9 -1
  6. package/dist/blobManager.d.ts.map +1 -1
  7. package/dist/blobManager.js +55 -12
  8. package/dist/blobManager.js.map +1 -1
  9. package/dist/connectionTelemetry.d.ts.map +1 -1
  10. package/dist/connectionTelemetry.js +2 -2
  11. package/dist/connectionTelemetry.js.map +1 -1
  12. package/dist/containerRuntime.d.ts +43 -11
  13. package/dist/containerRuntime.d.ts.map +1 -1
  14. package/dist/containerRuntime.js +189 -137
  15. package/dist/containerRuntime.js.map +1 -1
  16. package/dist/dataStore.js +3 -0
  17. package/dist/dataStore.js.map +1 -1
  18. package/dist/dataStoreContext.d.ts +1 -1
  19. package/dist/dataStoreContext.d.ts.map +1 -1
  20. package/dist/dataStoreContext.js +24 -27
  21. package/dist/dataStoreContext.js.map +1 -1
  22. package/dist/dataStoreContexts.js +1 -1
  23. package/dist/dataStoreContexts.js.map +1 -1
  24. package/dist/dataStores.d.ts +1 -1
  25. package/dist/dataStores.d.ts.map +1 -1
  26. package/dist/dataStores.js +14 -24
  27. package/dist/dataStores.js.map +1 -1
  28. package/dist/deltaManagerSummarizerProxy.d.ts.map +1 -1
  29. package/dist/deltaManagerSummarizerProxy.js +2 -0
  30. package/dist/deltaManagerSummarizerProxy.js.map +1 -1
  31. package/dist/deltaScheduler.d.ts.map +1 -1
  32. package/dist/deltaScheduler.js +5 -5
  33. package/dist/deltaScheduler.js.map +1 -1
  34. package/dist/gc/garbageCollection.d.ts.map +1 -1
  35. package/dist/gc/garbageCollection.js +12 -5
  36. package/dist/gc/garbageCollection.js.map +1 -1
  37. package/dist/gc/gcHelpers.d.ts +1 -0
  38. package/dist/gc/gcHelpers.d.ts.map +1 -1
  39. package/dist/gc/gcHelpers.js +1 -0
  40. package/dist/gc/gcHelpers.js.map +1 -1
  41. package/dist/gc/gcTelemetry.d.ts.map +1 -1
  42. package/dist/gc/gcTelemetry.js +15 -22
  43. package/dist/gc/gcTelemetry.js.map +1 -1
  44. package/dist/id-compressor/idCompressor.d.ts +3 -3
  45. package/dist/id-compressor/idCompressor.d.ts.map +1 -1
  46. package/dist/id-compressor/idCompressor.js +3 -1
  47. package/dist/id-compressor/idCompressor.js.map +1 -1
  48. package/dist/opLifecycle/opCompressor.d.ts +2 -2
  49. package/dist/opLifecycle/opCompressor.d.ts.map +1 -1
  50. package/dist/opLifecycle/opCompressor.js +1 -1
  51. package/dist/opLifecycle/opCompressor.js.map +1 -1
  52. package/dist/opLifecycle/opDecompressor.d.ts +2 -2
  53. package/dist/opLifecycle/opDecompressor.d.ts.map +1 -1
  54. package/dist/opLifecycle/opDecompressor.js +1 -1
  55. package/dist/opLifecycle/opDecompressor.js.map +1 -1
  56. package/dist/opLifecycle/opSplitter.d.ts +2 -2
  57. package/dist/opLifecycle/opSplitter.d.ts.map +1 -1
  58. package/dist/opLifecycle/opSplitter.js +1 -1
  59. package/dist/opLifecycle/opSplitter.js.map +1 -1
  60. package/dist/opLifecycle/outbox.d.ts +6 -5
  61. package/dist/opLifecycle/outbox.d.ts.map +1 -1
  62. package/dist/opLifecycle/outbox.js +5 -12
  63. package/dist/opLifecycle/outbox.js.map +1 -1
  64. package/dist/opLifecycle/remoteMessageProcessor.d.ts +6 -1
  65. package/dist/opLifecycle/remoteMessageProcessor.d.ts.map +1 -1
  66. package/dist/opLifecycle/remoteMessageProcessor.js +7 -1
  67. package/dist/opLifecycle/remoteMessageProcessor.js.map +1 -1
  68. package/dist/packageVersion.d.ts +1 -1
  69. package/dist/packageVersion.js +1 -1
  70. package/dist/packageVersion.js.map +1 -1
  71. package/dist/pendingStateManager.d.ts +4 -1
  72. package/dist/pendingStateManager.d.ts.map +1 -1
  73. package/dist/pendingStateManager.js +21 -12
  74. package/dist/pendingStateManager.js.map +1 -1
  75. package/dist/scheduleManager.d.ts.map +1 -1
  76. package/dist/scheduleManager.js +1 -1
  77. package/dist/scheduleManager.js.map +1 -1
  78. package/dist/summary/orderedClientElection.d.ts.map +1 -1
  79. package/dist/summary/orderedClientElection.js +1 -1
  80. package/dist/summary/orderedClientElection.js.map +1 -1
  81. package/dist/summary/runningSummarizer.d.ts +3 -5
  82. package/dist/summary/runningSummarizer.d.ts.map +1 -1
  83. package/dist/summary/runningSummarizer.js +10 -28
  84. package/dist/summary/runningSummarizer.js.map +1 -1
  85. package/dist/summary/summarizer.js +1 -1
  86. package/dist/summary/summarizer.js.map +1 -1
  87. package/dist/summary/summarizerNode/summarizerNode.d.ts +5 -5
  88. package/dist/summary/summarizerNode/summarizerNode.d.ts.map +1 -1
  89. package/dist/summary/summarizerNode/summarizerNode.js +7 -10
  90. package/dist/summary/summarizerNode/summarizerNode.js.map +1 -1
  91. package/dist/summary/summarizerNode/summarizerNodeWithGc.d.ts +4 -3
  92. package/dist/summary/summarizerNode/summarizerNodeWithGc.d.ts.map +1 -1
  93. package/dist/summary/summarizerNode/summarizerNodeWithGc.js +4 -8
  94. package/dist/summary/summarizerNode/summarizerNodeWithGc.js.map +1 -1
  95. package/dist/summary/summaryGenerator.d.ts.map +1 -1
  96. package/dist/summary/summaryGenerator.js +5 -1
  97. package/dist/summary/summaryGenerator.js.map +1 -1
  98. package/dist/summary/summaryManager.d.ts +2 -3
  99. package/dist/summary/summaryManager.d.ts.map +1 -1
  100. package/dist/summary/summaryManager.js +6 -2
  101. package/dist/summary/summaryManager.js.map +1 -1
  102. package/lib/batchTracker.d.ts.map +1 -1
  103. package/lib/batchTracker.js +2 -2
  104. package/lib/batchTracker.js.map +1 -1
  105. package/lib/blobManager.d.ts +9 -1
  106. package/lib/blobManager.d.ts.map +1 -1
  107. package/lib/blobManager.js +55 -12
  108. package/lib/blobManager.js.map +1 -1
  109. package/lib/connectionTelemetry.d.ts.map +1 -1
  110. package/lib/connectionTelemetry.js +3 -3
  111. package/lib/connectionTelemetry.js.map +1 -1
  112. package/lib/containerRuntime.d.ts +43 -11
  113. package/lib/containerRuntime.d.ts.map +1 -1
  114. package/lib/containerRuntime.js +188 -137
  115. package/lib/containerRuntime.js.map +1 -1
  116. package/lib/dataStore.js +3 -0
  117. package/lib/dataStore.js.map +1 -1
  118. package/lib/dataStoreContext.d.ts +1 -1
  119. package/lib/dataStoreContext.d.ts.map +1 -1
  120. package/lib/dataStoreContext.js +26 -29
  121. package/lib/dataStoreContext.js.map +1 -1
  122. package/lib/dataStoreContexts.js +2 -2
  123. package/lib/dataStoreContexts.js.map +1 -1
  124. package/lib/dataStores.d.ts +1 -1
  125. package/lib/dataStores.d.ts.map +1 -1
  126. package/lib/dataStores.js +15 -25
  127. package/lib/dataStores.js.map +1 -1
  128. package/lib/deltaManagerSummarizerProxy.d.ts.map +1 -1
  129. package/lib/deltaManagerSummarizerProxy.js +2 -0
  130. package/lib/deltaManagerSummarizerProxy.js.map +1 -1
  131. package/lib/deltaScheduler.d.ts.map +1 -1
  132. package/lib/deltaScheduler.js +6 -6
  133. package/lib/deltaScheduler.js.map +1 -1
  134. package/lib/gc/garbageCollection.d.ts.map +1 -1
  135. package/lib/gc/garbageCollection.js +13 -6
  136. package/lib/gc/garbageCollection.js.map +1 -1
  137. package/lib/gc/gcHelpers.d.ts +1 -0
  138. package/lib/gc/gcHelpers.d.ts.map +1 -1
  139. package/lib/gc/gcHelpers.js +1 -0
  140. package/lib/gc/gcHelpers.js.map +1 -1
  141. package/lib/gc/gcTelemetry.d.ts.map +1 -1
  142. package/lib/gc/gcTelemetry.js +16 -23
  143. package/lib/gc/gcTelemetry.js.map +1 -1
  144. package/lib/id-compressor/idCompressor.d.ts +3 -3
  145. package/lib/id-compressor/idCompressor.d.ts.map +1 -1
  146. package/lib/id-compressor/idCompressor.js +3 -1
  147. package/lib/id-compressor/idCompressor.js.map +1 -1
  148. package/lib/opLifecycle/opCompressor.d.ts +2 -2
  149. package/lib/opLifecycle/opCompressor.d.ts.map +1 -1
  150. package/lib/opLifecycle/opCompressor.js +2 -2
  151. package/lib/opLifecycle/opCompressor.js.map +1 -1
  152. package/lib/opLifecycle/opDecompressor.d.ts +2 -2
  153. package/lib/opLifecycle/opDecompressor.d.ts.map +1 -1
  154. package/lib/opLifecycle/opDecompressor.js +2 -2
  155. package/lib/opLifecycle/opDecompressor.js.map +1 -1
  156. package/lib/opLifecycle/opSplitter.d.ts +2 -2
  157. package/lib/opLifecycle/opSplitter.d.ts.map +1 -1
  158. package/lib/opLifecycle/opSplitter.js +2 -2
  159. package/lib/opLifecycle/opSplitter.js.map +1 -1
  160. package/lib/opLifecycle/outbox.d.ts +6 -5
  161. package/lib/opLifecycle/outbox.d.ts.map +1 -1
  162. package/lib/opLifecycle/outbox.js +6 -13
  163. package/lib/opLifecycle/outbox.js.map +1 -1
  164. package/lib/opLifecycle/remoteMessageProcessor.d.ts +6 -1
  165. package/lib/opLifecycle/remoteMessageProcessor.d.ts.map +1 -1
  166. package/lib/opLifecycle/remoteMessageProcessor.js +7 -1
  167. package/lib/opLifecycle/remoteMessageProcessor.js.map +1 -1
  168. package/lib/packageVersion.d.ts +1 -1
  169. package/lib/packageVersion.js +1 -1
  170. package/lib/packageVersion.js.map +1 -1
  171. package/lib/pendingStateManager.d.ts +4 -1
  172. package/lib/pendingStateManager.d.ts.map +1 -1
  173. package/lib/pendingStateManager.js +21 -12
  174. package/lib/pendingStateManager.js.map +1 -1
  175. package/lib/scheduleManager.d.ts.map +1 -1
  176. package/lib/scheduleManager.js +2 -2
  177. package/lib/scheduleManager.js.map +1 -1
  178. package/lib/summary/orderedClientElection.d.ts.map +1 -1
  179. package/lib/summary/orderedClientElection.js +2 -2
  180. package/lib/summary/orderedClientElection.js.map +1 -1
  181. package/lib/summary/runningSummarizer.d.ts +3 -5
  182. package/lib/summary/runningSummarizer.d.ts.map +1 -1
  183. package/lib/summary/runningSummarizer.js +11 -29
  184. package/lib/summary/runningSummarizer.js.map +1 -1
  185. package/lib/summary/summarizer.js +2 -2
  186. package/lib/summary/summarizer.js.map +1 -1
  187. package/lib/summary/summarizerNode/summarizerNode.d.ts +5 -5
  188. package/lib/summary/summarizerNode/summarizerNode.d.ts.map +1 -1
  189. package/lib/summary/summarizerNode/summarizerNode.js +8 -11
  190. package/lib/summary/summarizerNode/summarizerNode.js.map +1 -1
  191. package/lib/summary/summarizerNode/summarizerNodeWithGc.d.ts +4 -3
  192. package/lib/summary/summarizerNode/summarizerNodeWithGc.d.ts.map +1 -1
  193. package/lib/summary/summarizerNode/summarizerNodeWithGc.js +5 -9
  194. package/lib/summary/summarizerNode/summarizerNodeWithGc.js.map +1 -1
  195. package/lib/summary/summaryGenerator.d.ts.map +1 -1
  196. package/lib/summary/summaryGenerator.js +6 -2
  197. package/lib/summary/summaryGenerator.js.map +1 -1
  198. package/lib/summary/summaryManager.d.ts +2 -3
  199. package/lib/summary/summaryManager.d.ts.map +1 -1
  200. package/lib/summary/summaryManager.js +7 -3
  201. package/lib/summary/summaryManager.js.map +1 -1
  202. package/package.json +16 -16
  203. package/src/batchTracker.ts +2 -2
  204. package/src/blobManager.ts +70 -13
  205. package/src/connectionTelemetry.ts +7 -3
  206. package/src/containerRuntime.ts +287 -150
  207. package/src/dataStore.ts +3 -0
  208. package/src/dataStoreContext.ts +31 -33
  209. package/src/dataStoreContexts.ts +2 -2
  210. package/src/dataStores.ts +15 -18
  211. package/src/deltaManagerSummarizerProxy.ts +2 -0
  212. package/src/deltaScheduler.ts +6 -10
  213. package/src/gc/garbageCollection.ts +13 -8
  214. package/src/gc/gcHelpers.ts +1 -0
  215. package/src/gc/gcTelemetry.ts +12 -8
  216. package/src/id-compressor/idCompressor.ts +6 -5
  217. package/src/opLifecycle/opCompressor.ts +4 -3
  218. package/src/opLifecycle/opDecompressor.ts +4 -3
  219. package/src/opLifecycle/opSplitter.ts +4 -3
  220. package/src/opLifecycle/outbox.ts +13 -25
  221. package/src/opLifecycle/remoteMessageProcessor.ts +8 -2
  222. package/src/packageVersion.ts +1 -1
  223. package/src/pendingStateManager.ts +22 -10
  224. package/src/scheduleManager.ts +2 -2
  225. package/src/summary/orderedClientElection.ts +2 -2
  226. package/src/summary/runningSummarizer.ts +18 -44
  227. package/src/summary/summarizer.ts +2 -2
  228. package/src/summary/summarizerNode/summarizerNode.ts +13 -15
  229. package/src/summary/summarizerNode/summarizerNodeWithGc.ts +8 -7
  230. package/src/summary/summaryGenerator.ts +6 -2
  231. 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,
@@ -540,7 +542,7 @@ const defaultChunkSizeInBytes = 204800;
540
542
  * of the current system, we should close the summarizer and let it recover.
541
543
  * This delay's goal is to prevent tight restart loops
542
544
  */
543
- const defaultCloseSummarizerDelayMs = 10000; // 10 seconds
545
+ const defaultCloseSummarizerDelayMs = 5000; // 5 seconds
544
546
 
545
547
  /**
546
548
  * @deprecated - use ContainerRuntimeMessage instead
@@ -582,6 +584,30 @@ export function getDeviceSpec() {
582
584
  return {};
583
585
  }
584
586
 
587
+ /**
588
+ * Older loader doesn't have a submitBatchFn member, this is the older way of submitting a batch.
589
+ * Rather than exposing the submitFn (now deprecated) and IDeltaManager (dangerous to hand out) to the Outbox,
590
+ * we can provide a partially-applied function to keep those items private to the ContainerRuntime.
591
+ */
592
+ export const makeLegacySendBatchFn =
593
+ (
594
+ submitFn: (type: MessageType, contents: any, batch: boolean, appData?: any) => number,
595
+ deltaManager: Pick<IDeltaManager<unknown, unknown>, "flush">,
596
+ ) =>
597
+ (batch: IBatch) => {
598
+ for (const message of batch.content) {
599
+ submitFn(
600
+ MessageType.Operation,
601
+ // For back-compat (submitFn only works on deserialized content)
602
+ message.contents === undefined ? undefined : JSON.parse(message.contents),
603
+ true, // batch
604
+ message.metadata,
605
+ );
606
+ }
607
+
608
+ deltaManager.flush();
609
+ };
610
+
585
611
  /**
586
612
  * Represents the runtime of the container. Contains helper functions/state of the container.
587
613
  * It will define the store level mappings.
@@ -671,9 +697,12 @@ export class ContainerRuntime
671
697
  const passLogger =
672
698
  backCompatContext.taggedLogger ??
673
699
  new TaggedLoggerAdapter((backCompatContext as OldContainerContextWithLogger).logger);
674
- const logger = ChildLogger.create(passLogger, undefined, {
675
- all: {
676
- runtimeVersion: pkgVersion,
700
+ const logger = createChildLogger({
701
+ logger: passLogger,
702
+ properties: {
703
+ all: {
704
+ runtimeVersion: pkgVersion,
705
+ },
677
706
  },
678
707
  });
679
708
 
@@ -808,49 +837,48 @@ export class ContainerRuntime
808
837
  return runtime;
809
838
  }
810
839
 
811
- public get options(): ILoaderOptions {
812
- return this.context.options;
813
- }
840
+ public readonly options: ILoaderOptions;
814
841
 
842
+ private readonly _getClientId: () => string | undefined;
815
843
  public get clientId(): string | undefined {
816
- return this.context.clientId;
844
+ return this._getClientId();
817
845
  }
818
846
 
819
- public get clientDetails(): IClientDetails {
820
- return this.context.clientDetails;
821
- }
847
+ public readonly clientDetails: IClientDetails;
822
848
 
823
849
  public get storage(): IDocumentStorageService {
824
850
  return this._storage;
825
851
  }
826
852
 
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;
853
+ /** @deprecated - The functionality is no longer exposed publicly */
854
+ public get reSubmitFn() {
855
+ return (
856
+ type: ContainerMessageType,
857
+ contents: any,
858
+ localOpMetadata: unknown,
859
+ opMetadata: Record<string, unknown> | undefined,
860
+ ) => this.reSubmitCore({ type, contents }, localOpMetadata, opMetadata);
835
861
  }
836
862
 
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;
840
- }
841
-
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
- }
863
+ private readonly submitFn: (
864
+ type: MessageType,
865
+ contents: any,
866
+ batch: boolean,
867
+ appData?: any,
868
+ ) => number;
869
+ /**
870
+ * Although current IContainerContext guarantees submitBatchFn, it is not available on older loaders.
871
+ */
872
+ private readonly submitBatchFn:
873
+ | ((batch: IBatchMessage[], referenceSequenceNumber?: number) => number)
874
+ | undefined;
875
+ private readonly submitSummaryFn: (
876
+ summaryOp: ISummaryContent,
877
+ referenceSequenceNumber?: number,
878
+ ) => number;
879
+ private readonly submitSignalFn: (contents: any) => void;
880
+ public readonly disposeFn: (error?: ICriticalContainerError) => void;
881
+ public readonly closeFn: (error?: ICriticalContainerError) => void;
854
882
 
855
883
  public get flushMode(): FlushMode {
856
884
  return this._flushMode;
@@ -864,8 +892,9 @@ export class ContainerRuntime
864
892
  return this.registry;
865
893
  }
866
894
 
895
+ private readonly _getAttachState: () => AttachState;
867
896
  public get attachState(): AttachState {
868
- return this.context.attachState;
897
+ return this._getAttachState();
869
898
  }
870
899
 
871
900
  public idCompressor: (IIdCompressor & IIdCompressorCore) | undefined;
@@ -1056,11 +1085,21 @@ export class ContainerRuntime
1056
1085
  */
1057
1086
  private readonly idCompressorEnabled: boolean;
1058
1087
 
1088
+ /**
1089
+ * Whether this client is the summarizer client itself (type is summarizerClientType)
1090
+ */
1091
+ private readonly isSummarizerClient: boolean;
1092
+
1093
+ /**
1094
+ * The id of the version used to initially load this runtime, or undefined if it's newly created.
1095
+ */
1096
+ private readonly loadedFromVersionId: string | undefined;
1097
+
1059
1098
  /**
1060
1099
  * @internal
1061
1100
  */
1062
1101
  protected constructor(
1063
- private readonly context: IContainerContext,
1102
+ context: IContainerContext,
1064
1103
  private readonly registry: IFluidDataStoreRegistry,
1065
1104
  metadata: IContainerRuntimeMetadata | undefined,
1066
1105
  electedSummarizerData: ISerializedElection | undefined,
@@ -1087,10 +1126,70 @@ export class ContainerRuntime
1087
1126
  ) {
1088
1127
  super();
1089
1128
 
1090
- this.innerDeltaManager = context.deltaManager;
1091
- this.deltaManager = new DeltaManagerSummarizerProxy(context.deltaManager);
1129
+ const {
1130
+ options,
1131
+ clientDetails,
1132
+ connected,
1133
+ baseSnapshot,
1134
+ submitFn,
1135
+ submitBatchFn,
1136
+ submitSummaryFn,
1137
+ submitSignalFn,
1138
+ disposeFn,
1139
+ closeFn,
1140
+ deltaManager,
1141
+ quorum,
1142
+ audience,
1143
+ loader,
1144
+ pendingLocalState,
1145
+ supportedFeatures,
1146
+ } = context;
1147
+
1148
+ this.innerDeltaManager = deltaManager;
1149
+ this.deltaManager = new DeltaManagerSummarizerProxy(this.innerDeltaManager);
1150
+
1151
+ // Here we could wrap/intercept on these functions to block/modify outgoing messages if needed.
1152
+ // This makes ContainerRuntime the final gatekeeper for outgoing messages.
1153
+ this.submitFn = submitFn;
1154
+ this.submitBatchFn = submitBatchFn;
1155
+ this.submitSummaryFn = submitSummaryFn;
1156
+ this.submitSignalFn = submitSignalFn;
1157
+
1158
+ this.options = options;
1159
+ this.clientDetails = clientDetails;
1160
+ this.isSummarizerClient = this.clientDetails.type === summarizerClientType;
1161
+ this.loadedFromVersionId = context.getLoadedFromVersion()?.id;
1162
+ this._getClientId = () => context.clientId;
1163
+ this._getAttachState = () => context.attachState;
1164
+ this.getAbsoluteUrl = async (relativeUrl: string) => {
1165
+ if (context.getAbsoluteUrl === undefined) {
1166
+ throw new Error("Driver does not implement getAbsoluteUrl");
1167
+ }
1168
+ if (this.attachState !== AttachState.Attached) {
1169
+ return undefined;
1170
+ }
1171
+ return context.getAbsoluteUrl(relativeUrl);
1172
+ };
1173
+ // TODO: Consider that the Container could just listen to these events itself, or even more appropriately maybe the
1174
+ // customer should observe dirty state on the runtime (the owner of dirty state) directly, rather than on the IContainer.
1175
+ this.on("dirty", () => context.updateDirtyContainerState(true));
1176
+ this.on("saved", () => context.updateDirtyContainerState(false));
1092
1177
 
1093
- this.mc = loggerToMonitoringContext(ChildLogger.create(this.logger, "ContainerRuntime"));
1178
+ // In old loaders without dispose functionality, closeFn is equivalent but will also switch container to readonly mode
1179
+ this.disposeFn = disposeFn ?? closeFn;
1180
+ // In cases of summarizer, we want to dispose instead since consumer doesn't interact with this container
1181
+ this.closeFn = this.isSummarizerClient
1182
+ ? this.disposeFn
1183
+ : (error?: ICriticalContainerError) => {
1184
+ closeFn(error);
1185
+ // Also call disposeFn to retain functionality of runtime being disposed on close
1186
+ disposeFn?.(error);
1187
+ };
1188
+
1189
+ this.mc = createChildMonitoringContext({
1190
+ logger: this.logger,
1191
+ namespace: "ContainerRuntime",
1192
+ });
1094
1193
 
1095
1194
  let loadSummaryNumber: number;
1096
1195
  // Get the container creation metadata. For new container, we initialize these. For existing containers,
@@ -1122,7 +1221,9 @@ export class ContainerRuntime
1122
1221
 
1123
1222
  this.messageAtLastSummary = metadata?.message;
1124
1223
 
1125
- this._connected = this.context.connected;
1224
+ // Note that we only need to pull the *initial* connected state from the context.
1225
+ // Later updates come through calls to setConnectionState.
1226
+ this._connected = connected;
1126
1227
 
1127
1228
  this.gcTombstoneEnforcementAllowed = shouldAllowGcTombstoneEnforcement(
1128
1229
  metadata?.gcFeatureMatrix?.tombstoneGeneration /* persisted */,
@@ -1151,7 +1252,7 @@ export class ContainerRuntime
1151
1252
 
1152
1253
  const opSplitter = new OpSplitter(
1153
1254
  chunks,
1154
- this.context.submitBatchFn,
1255
+ this.submitBatchFn,
1155
1256
  disableChunking === true ? Number.POSITIVE_INFINITY : runtimeOptions.chunkSizeInBytes,
1156
1257
  runtimeOptions.maxBatchSizeInBytes,
1157
1258
  this.mc.logger,
@@ -1192,7 +1293,7 @@ export class ContainerRuntime
1192
1293
 
1193
1294
  if (
1194
1295
  runtimeOptions.flushMode === (FlushModeExperimental.Async as unknown as FlushMode) &&
1195
- context.supportedFeatures?.get("referenceSequenceNumbers") !== true
1296
+ supportedFeatures?.get("referenceSequenceNumbers") !== true
1196
1297
  ) {
1197
1298
  // The loader does not support reference sequence numbers, falling back on FlushMode.TurnBased
1198
1299
  this.mc.logger.sendErrorEvent({ eventName: "FlushModeFallback" });
@@ -1201,7 +1302,7 @@ export class ContainerRuntime
1201
1302
  this._flushMode = runtimeOptions.flushMode;
1202
1303
  }
1203
1304
 
1204
- const pendingRuntimeState = context.pendingLocalState as IPendingRuntimeState | undefined;
1305
+ const pendingRuntimeState = pendingLocalState as IPendingRuntimeState | undefined;
1205
1306
 
1206
1307
  const maxSnapshotCacheDurationMs = this._storage?.policies?.maximumCacheDurationMs;
1207
1308
  if (
@@ -1217,12 +1318,12 @@ export class ContainerRuntime
1217
1318
  this.garbageCollector = GarbageCollector.create({
1218
1319
  runtime: this,
1219
1320
  gcOptions: this.runtimeOptions.gcOptions,
1220
- baseSnapshot: context.baseSnapshot,
1321
+ baseSnapshot,
1221
1322
  baseLogger: this.mc.logger,
1222
1323
  existing,
1223
1324
  metadata,
1224
1325
  createContainerMetadata: this.createContainerMetadata,
1225
- isSummarizerClient: this.context.clientDetails.type === summarizerClientType,
1326
+ isSummarizerClient: this.isSummarizerClient,
1226
1327
  getNodePackagePath: async (nodePath: string) => this.getGCNodePackagePath(nodePath),
1227
1328
  getLastSummaryTimestampMs: () => this.messageAtLastSummary?.timestamp,
1228
1329
  readAndParseBlob: async <T>(id: string) => readAndParse<T>(this.storage, id),
@@ -1233,14 +1334,14 @@ export class ContainerRuntime
1233
1334
 
1234
1335
  const loadedFromSequenceNumber = this.deltaManager.initialSequenceNumber;
1235
1336
  this.summarizerNode = createRootSummarizerNodeWithGC(
1236
- ChildLogger.create(this.logger, "SummarizerNode"),
1337
+ createChildLogger({ logger: this.logger, namespace: "SummarizerNode" }),
1237
1338
  // Summarize function to call when summarize is called. Summarizer node always tracks summary state.
1238
1339
  async (fullTree: boolean, trackState: boolean, telemetryContext?: ITelemetryContext) =>
1239
1340
  this.summarizeInternal(fullTree, trackState, telemetryContext),
1240
1341
  // Latest change sequence number, no changes since summary applied yet
1241
1342
  loadedFromSequenceNumber,
1242
1343
  // Summary reference sequence number, undefined if no summary yet
1243
- context.baseSnapshot ? loadedFromSequenceNumber : undefined,
1344
+ baseSnapshot !== undefined ? loadedFromSequenceNumber : undefined,
1244
1345
  {
1245
1346
  // Must set to false to prevent sending summary handle which would be pointing to
1246
1347
  // a summary with an older protocol state.
@@ -1257,14 +1358,14 @@ export class ContainerRuntime
1257
1358
  async () => this.garbageCollector.getBaseGCDetails(),
1258
1359
  );
1259
1360
 
1260
- if (context.baseSnapshot) {
1261
- this.summarizerNode.updateBaseSummaryState(context.baseSnapshot);
1361
+ if (baseSnapshot) {
1362
+ this.summarizerNode.updateBaseSummaryState(baseSnapshot);
1262
1363
  }
1263
1364
 
1264
1365
  this.dataStores = new DataStores(
1265
- getSummaryForDatastores(context.baseSnapshot, metadata),
1366
+ getSummaryForDatastores(baseSnapshot, metadata),
1266
1367
  this,
1267
- (attachMsg) => this.submit(ContainerMessageType.Attach, attachMsg),
1368
+ (attachMsg) => this.submit({ type: ContainerMessageType.Attach, contents: attachMsg }),
1268
1369
  (id: string, createParam: CreateChildSummarizerNodeParam) =>
1269
1370
  (
1270
1371
  summarizeInternal: SummarizeInternalFn,
@@ -1291,10 +1392,14 @@ export class ContainerRuntime
1291
1392
  () => this.storage,
1292
1393
  (localId: string, blobId?: string) => {
1293
1394
  if (!this.disposed) {
1294
- this.submit(ContainerMessageType.BlobAttach, undefined, undefined, {
1295
- localId,
1296
- blobId,
1297
- });
1395
+ this.submit(
1396
+ { type: ContainerMessageType.BlobAttach, contents: undefined },
1397
+ undefined,
1398
+ {
1399
+ localId,
1400
+ blobId,
1401
+ },
1402
+ );
1298
1403
  }
1299
1404
  },
1300
1405
  (blobPath: string) => this.garbageCollector.nodeUpdated(blobPath, "Loaded"),
@@ -1305,10 +1410,10 @@ export class ContainerRuntime
1305
1410
  );
1306
1411
 
1307
1412
  this.scheduleManager = new ScheduleManager(
1308
- context.deltaManager,
1413
+ this.innerDeltaManager,
1309
1414
  this,
1310
1415
  () => this.clientId,
1311
- ChildLogger.create(this.logger, "ScheduleManager"),
1416
+ createChildLogger({ logger: this.logger, namespace: "ScheduleManager" }),
1312
1417
  );
1313
1418
 
1314
1419
  this.pendingStateManager = new PendingStateManager(
@@ -1319,8 +1424,10 @@ export class ContainerRuntime
1319
1424
  connected: () => this.connected,
1320
1425
  reSubmit: this.reSubmit.bind(this),
1321
1426
  reSubmitBatch: this.reSubmitBatch.bind(this),
1427
+ isActiveConnection: () => this.innerDeltaManager.active,
1322
1428
  },
1323
1429
  pendingRuntimeState?.pending,
1430
+ this.logger,
1324
1431
  );
1325
1432
 
1326
1433
  const disableCompression = this.mc.config.getBoolean(
@@ -1337,10 +1444,14 @@ export class ContainerRuntime
1337
1444
  const disablePartialFlush = this.mc.config.getBoolean(
1338
1445
  "Fluid.ContainerRuntime.DisablePartialFlush",
1339
1446
  );
1447
+
1448
+ const legacySendBatchFn = makeLegacySendBatchFn(this.submitFn, this.innerDeltaManager);
1449
+
1340
1450
  this.outbox = new Outbox({
1341
1451
  shouldSend: () => this.canSendOps(),
1342
1452
  pendingStateManager: this.pendingStateManager,
1343
- containerContext: this.context,
1453
+ submitBatchFn: this.submitBatchFn,
1454
+ legacySendBatchFn,
1344
1455
  compressor: new OpCompressor(this.mc.logger),
1345
1456
  splitter: opSplitter,
1346
1457
  config: {
@@ -1360,10 +1471,14 @@ export class ContainerRuntime
1360
1471
  closeContainer: this.closeFn,
1361
1472
  });
1362
1473
 
1363
- this.context.quorum.on("removeMember", (clientId: string) => {
1474
+ this._quorum = quorum;
1475
+ this._quorum.on("removeMember", (clientId: string) => {
1364
1476
  this.remoteMessageProcessor.clearPartialMessagesFor(clientId);
1365
1477
  });
1366
1478
 
1479
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
1480
+ this._audience = audience!;
1481
+
1367
1482
  this.summaryStateUpdateMethod = this.mc.config.getString(
1368
1483
  "Fluid.ContainerRuntime.Test.SummaryStateUpdateMethodV2",
1369
1484
  );
@@ -1378,23 +1493,26 @@ export class ContainerRuntime
1378
1493
  this.summaryCollection = new SummaryCollection(this.deltaManager, this.logger);
1379
1494
 
1380
1495
  this.dirtyContainer =
1381
- this.context.attachState !== AttachState.Attached ||
1496
+ this.attachState !== AttachState.Attached ||
1382
1497
  this.pendingStateManager.hasPendingMessages();
1383
- this.context.updateDirtyContainerState(this.dirtyContainer);
1498
+ context.updateDirtyContainerState(this.dirtyContainer);
1384
1499
 
1385
1500
  if (this.summariesDisabled) {
1386
1501
  this.mc.logger.sendTelemetryEvent({ eventName: "SummariesDisabled" });
1387
1502
  } else {
1388
- const orderedClientLogger = ChildLogger.create(this.logger, "OrderedClientElection");
1503
+ const orderedClientLogger = createChildLogger({
1504
+ logger: this.logger,
1505
+ namespace: "OrderedClientElection",
1506
+ });
1389
1507
  const orderedClientCollection = new OrderedClientCollection(
1390
1508
  orderedClientLogger,
1391
- this.context.deltaManager,
1392
- this.context.quorum,
1509
+ this.innerDeltaManager,
1510
+ this._quorum,
1393
1511
  );
1394
1512
  const orderedClientElectionForSummarizer = new OrderedClientElection(
1395
1513
  orderedClientLogger,
1396
1514
  orderedClientCollection,
1397
- electedSummarizerData ?? this.context.deltaManager.lastSequenceNumber,
1515
+ electedSummarizerData ?? this.innerDeltaManager.lastSequenceNumber,
1398
1516
  SummarizerClientElection.isClientEligible,
1399
1517
  );
1400
1518
 
@@ -1405,7 +1523,7 @@ export class ContainerRuntime
1405
1523
  this.maxOpsSinceLastSummary,
1406
1524
  );
1407
1525
 
1408
- if (this.context.clientDetails.type === summarizerClientType) {
1526
+ if (this.isSummarizerClient) {
1409
1527
  this._summarizer = new Summarizer(
1410
1528
  this /* ISummarizerRuntime */,
1411
1529
  () => this.summaryConfiguration,
@@ -1420,19 +1538,19 @@ export class ContainerRuntime
1420
1538
  () => this.innerDeltaManager.active,
1421
1539
  ),
1422
1540
  );
1423
- } else if (
1424
- SummarizerClientElection.clientDetailsPermitElection(this.context.clientDetails)
1425
- ) {
1541
+ } else if (SummarizerClientElection.clientDetailsPermitElection(this.clientDetails)) {
1426
1542
  // Only create a SummaryManager and SummarizerClientElection
1427
1543
  // if summaries are enabled and we are not the summarizer client.
1428
1544
  const defaultAction = () => {
1429
1545
  if (this.summaryCollection.opsSinceLastAck > this.maxOpsSinceLastSummary) {
1430
- this.logger.sendTelemetryEvent({ eventName: "SummaryStatus:Behind" });
1546
+ this.mc.logger.sendTelemetryEvent({ eventName: "SummaryStatus:Behind" });
1431
1547
  // unregister default to no log on every op after falling behind
1432
1548
  // and register summary ack handler to re-register this handler
1433
1549
  // after successful summary
1434
1550
  this.summaryCollection.once(MessageType.SummaryAck, () => {
1435
- this.logger.sendTelemetryEvent({ eventName: "SummaryStatus:CaughtUp" });
1551
+ this.mc.logger.sendTelemetryEvent({
1552
+ eventName: "SummaryStatus:CaughtUp",
1553
+ });
1436
1554
  // we've caught up, so re-register the default action to monitor for
1437
1555
  // falling behind, and unregister ourself
1438
1556
  this.summaryCollection.on("default", defaultAction);
@@ -1449,7 +1567,7 @@ export class ContainerRuntime
1449
1567
  this, // IConnectedState
1450
1568
  this.summaryCollection,
1451
1569
  this.logger,
1452
- this.formRequestSummarizerFn(this.context.loader),
1570
+ this.formRequestSummarizerFn(loader),
1453
1571
  new Throttler(
1454
1572
  60 * 1000, // 60 sec delay window
1455
1573
  30 * 1000, // 30 sec max delay
@@ -1500,7 +1618,7 @@ export class ContainerRuntime
1500
1618
  ...getDeviceSpec(),
1501
1619
  });
1502
1620
 
1503
- this.logger.sendTelemetryEvent({
1621
+ this.mc.logger.sendTelemetryEvent({
1504
1622
  eventName: "ContainerLoadStats",
1505
1623
  ...this.createContainerMetadata,
1506
1624
  ...this.dataStores.containerLoadStats,
@@ -1523,11 +1641,11 @@ export class ContainerRuntime
1523
1641
  groupedBatchingEnabled: this.groupedBatchingEnabled,
1524
1642
  });
1525
1643
 
1526
- ReportOpPerfTelemetry(this.context.clientId, this.deltaManager, this.logger);
1644
+ ReportOpPerfTelemetry(this.clientId, this.deltaManager, this.logger);
1527
1645
  BindBatchTracker(this, this.logger);
1528
1646
 
1529
1647
  this.entryPoint = new LazyPromise(async () => {
1530
- if (this.context.clientDetails.type === summarizerClientType) {
1648
+ if (this.isSummarizerClient) {
1531
1649
  assert(
1532
1650
  this._summarizer !== undefined,
1533
1651
  0x5bf /* Summarizer object is undefined in a summarizer client */,
@@ -1551,7 +1669,7 @@ export class ContainerRuntime
1551
1669
  }
1552
1670
  this._disposed = true;
1553
1671
 
1554
- this.logger.sendTelemetryEvent(
1672
+ this.mc.logger.sendTelemetryEvent(
1555
1673
  {
1556
1674
  eventName: "ContainerRuntimeDisposed",
1557
1675
  isDirty: this.isDirty,
@@ -1841,14 +1959,11 @@ export class ContainerRuntime
1841
1959
  * Parse an op's type and actual content from given serialized content
1842
1960
  * ! Note: this format needs to be in-line with what is set in the "ContainerRuntime.submit(...)" method
1843
1961
  */
1844
- private parseOpContent(serializedContent?: string): {
1845
- type: ContainerMessageType;
1846
- contents: unknown;
1847
- } {
1962
+ private parseOpContent(serializedContent?: string): ContainerRuntimeMessage {
1848
1963
  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 };
1964
+ const { type, contents } = JSON.parse(serializedContent);
1965
+ assert(type !== undefined, 0x6d6 /* incorrect op content format */);
1966
+ return { type, contents };
1852
1967
  }
1853
1968
 
1854
1969
  private async applyStashedOp(op: string): Promise<unknown> {
@@ -1926,6 +2041,14 @@ export class ContainerRuntime
1926
2041
  // There might be no change of state due to Container calling this API after loading runtime.
1927
2042
  const changeOfState = this._connected !== connected;
1928
2043
  const reconnection = changeOfState && !connected;
2044
+
2045
+ // We need to flush the ops currently collected by Outbox to preserve original order.
2046
+ // This flush NEEDS to happen before we set the ContainerRuntime to "connected".
2047
+ // We want these ops to get to the PendingStateManager without sending to service and have them return to the Outbox upon calling "replayPendingStates".
2048
+ if (changeOfState && connected) {
2049
+ this.flush();
2050
+ }
2051
+
1929
2052
  this._connected = connected;
1930
2053
 
1931
2054
  if (!connected) {
@@ -1991,6 +2114,12 @@ export class ContainerRuntime
1991
2114
 
1992
2115
  private _processedClientSequenceNumber: number | undefined;
1993
2116
 
2117
+ /**
2118
+ * Direct the message to the correct subsystem for processing, and implement other side effects
2119
+ * @param message - The unpacked message. Likely a ContainerRuntimeMessage, but could also be a system op
2120
+ * @param local - Did this client send the op?
2121
+ * @param runtimeMessage - Does this appear like a current ContainerRuntimeMessage? If true, certain validation will occur.
2122
+ */
1994
2123
  private processCore(
1995
2124
  message: ISequencedDocumentMessage,
1996
2125
  local: boolean,
@@ -2059,9 +2188,7 @@ export class ContainerRuntime
2059
2188
  }
2060
2189
  }
2061
2190
 
2062
- if (runtimeMessage || this.groupedBatchingEnabled) {
2063
- this.emit("op", message, runtimeMessage);
2064
- }
2191
+ this.emit("op", message, runtimeMessage);
2065
2192
 
2066
2193
  this.scheduleManager.afterOpProcessing(undefined, message);
2067
2194
 
@@ -2091,7 +2218,7 @@ export class ContainerRuntime
2091
2218
  */
2092
2219
  private sendSignalTelemetryEvent(clientSignalSequenceNumber: number) {
2093
2220
  const duration = Date.now() - this._perfSignalData.signalTimestamp;
2094
- this.logger.sendPerformanceEvent({
2221
+ this.mc.logger.sendPerformanceEvent({
2095
2222
  eventName: "SignalLatency",
2096
2223
  duration,
2097
2224
  signalsLost: this._perfSignalData.signalsLost,
@@ -2119,7 +2246,7 @@ export class ContainerRuntime
2119
2246
  ) {
2120
2247
  this._perfSignalData.signalsLost++;
2121
2248
  this._perfSignalData.trackingSignalSequenceNumber = undefined;
2122
- this.logger.sendErrorEvent({
2249
+ this.mc.logger.sendErrorEvent({
2123
2250
  eventName: "SignalLost",
2124
2251
  type: envelope.contents.type,
2125
2252
  signalsLost: this._perfSignalData.signalsLost,
@@ -2281,13 +2408,14 @@ export class ContainerRuntime
2281
2408
  return this.flushMode !== FlushMode.Immediate || this._orderSequentiallyCalls !== 0;
2282
2409
  }
2283
2410
 
2411
+ private readonly _quorum: IQuorumClients;
2284
2412
  public getQuorum(): IQuorumClients {
2285
- return this.context.quorum;
2413
+ return this._quorum;
2286
2414
  }
2287
2415
 
2416
+ private readonly _audience: IAudience;
2288
2417
  public getAudience(): IAudience {
2289
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
2290
- return this.context.audience!;
2418
+ return this._audience;
2291
2419
  }
2292
2420
 
2293
2421
  /**
@@ -2298,7 +2426,7 @@ export class ContainerRuntime
2298
2426
  return this.dirtyContainer;
2299
2427
  }
2300
2428
 
2301
- private isContainerMessageDirtyable(type: ContainerMessageType, contents: any) {
2429
+ private isContainerMessageDirtyable({ type, contents }: ContainerRuntimeMessage) {
2302
2430
  // For legacy purposes, exclude the old built-in AgentScheduler from dirty consideration as a special-case.
2303
2431
  // Ultimately we should have no special-cases from the ContainerRuntime's perspective.
2304
2432
  if (type === ContainerMessageType.Attach) {
@@ -2347,12 +2475,12 @@ export class ContainerRuntime
2347
2475
  public submitSignal(type: string, content: any) {
2348
2476
  this.verifyNotClosed();
2349
2477
  const envelope = this.createNewSignalEnvelope(undefined /* address */, type, content);
2350
- return this.context.submitSignalFn(envelope);
2478
+ return this.submitSignalFn(envelope);
2351
2479
  }
2352
2480
 
2353
2481
  public submitDataStoreSignal(address: string, type: string, content: any) {
2354
2482
  const envelope = this.createNewSignalEnvelope(address, type, content);
2355
- return this.context.submitSignalFn(envelope);
2483
+ return this.submitSignalFn(envelope);
2356
2484
  }
2357
2485
 
2358
2486
  public setAttachState(attachState: AttachState.Attaching | AttachState.Attached): void {
@@ -2404,15 +2532,7 @@ export class ContainerRuntime
2404
2532
  return summarizeResult.summary;
2405
2533
  }
2406
2534
 
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
- }
2535
+ public readonly getAbsoluteUrl: (relativeUrl: string) => Promise<string | undefined>;
2416
2536
 
2417
2537
  private async summarizeInternal(
2418
2538
  fullTree: boolean,
@@ -2497,7 +2617,7 @@ export class ContainerRuntime
2497
2617
 
2498
2618
  return { stats, summary, gcStats };
2499
2619
  } finally {
2500
- this.logger.sendTelemetryEvent({
2620
+ this.mc.logger.sendTelemetryEvent({
2501
2621
  eventName: "SummarizeTelemetry",
2502
2622
  details: telemetryContext.serialize(),
2503
2623
  });
@@ -2700,8 +2820,11 @@ export class ContainerRuntime
2700
2820
  // The summary number for this summary. This will be updated during the summary process, so get it now and
2701
2821
  // use it for all events logged during this summary.
2702
2822
  const summaryNumber = this.nextSummaryNumber;
2703
- const summaryNumberLogger = ChildLogger.create(summaryLogger, undefined, {
2704
- all: { summaryNumber },
2823
+ const summaryNumberLogger = createChildLogger({
2824
+ logger: summaryLogger,
2825
+ properties: {
2826
+ all: { summaryNumber },
2827
+ },
2705
2828
  });
2706
2829
 
2707
2830
  assert(this.outbox.isEmpty, 0x3d1 /* Can't trigger summary in the middle of a batch */);
@@ -2709,7 +2832,11 @@ export class ContainerRuntime
2709
2832
  let latestSnapshotVersionId: string | undefined;
2710
2833
  if (refreshLatestAck) {
2711
2834
  const latestSnapshotInfo = await this.refreshLatestSummaryAckFromServer(
2712
- ChildLogger.create(summaryNumberLogger, undefined, { all: { safeSummary: true } }),
2835
+ createChildLogger({
2836
+ logger: summaryNumberLogger,
2837
+ namespace: undefined,
2838
+ properties: { all: { safeSummary: true } },
2839
+ }),
2713
2840
  );
2714
2841
  const latestSnapshotRefSeq = latestSnapshotInfo.latestSnapshotRefSeq;
2715
2842
  latestSnapshotVersionId = latestSnapshotInfo.latestSnapshotVersionId;
@@ -2881,7 +3008,7 @@ export class ContainerRuntime
2881
3008
  } else if (lastAck === undefined) {
2882
3009
  summaryContext = {
2883
3010
  proposalHandle: undefined,
2884
- ackHandle: this.context.getLoadedFromVersion()?.id,
3011
+ ackHandle: this.loadedFromVersionId,
2885
3012
  referenceSequenceNumber: summaryRefSeqNum,
2886
3013
  };
2887
3014
  } else {
@@ -2982,7 +3109,6 @@ export class ContainerRuntime
2982
3109
  this.dirtyContainer = dirty;
2983
3110
  if (this.emitDirtyDocumentEvent) {
2984
3111
  this.emit(dirty ? "dirty" : "saved");
2985
- this.context.updateDirtyContainerState(dirty);
2986
3112
  }
2987
3113
  }
2988
3114
 
@@ -2995,7 +3121,10 @@ export class ContainerRuntime
2995
3121
  address: id,
2996
3122
  contents,
2997
3123
  };
2998
- this.submit(ContainerMessageType.FluidDataStoreOp, envelope, localOpMetadata);
3124
+ this.submit(
3125
+ { type: ContainerMessageType.FluidDataStoreOp, contents: envelope },
3126
+ localOpMetadata,
3127
+ );
2999
3128
  }
3000
3129
 
3001
3130
  public submitDataStoreAliasOp(contents: any, localOpMetadata: unknown): void {
@@ -3004,12 +3133,15 @@ export class ContainerRuntime
3004
3133
  throw new UsageError("malformedDataStoreAliasMessage");
3005
3134
  }
3006
3135
 
3007
- this.submit(ContainerMessageType.Alias, contents, localOpMetadata);
3136
+ this.submit({ type: ContainerMessageType.Alias, contents }, localOpMetadata);
3008
3137
  }
3009
3138
 
3010
- public async uploadBlob(blob: ArrayBufferLike): Promise<IFluidHandle<ArrayBufferLike>> {
3139
+ public async uploadBlob(
3140
+ blob: ArrayBufferLike,
3141
+ signal?: AbortSignal,
3142
+ ): Promise<IFluidHandle<ArrayBufferLike>> {
3011
3143
  this.verifyNotClosed();
3012
- return this.blobManager.createBlob(blob);
3144
+ return this.blobManager.createBlob(blob, signal);
3013
3145
  }
3014
3146
 
3015
3147
  private maybeSubmitIdAllocationOp(type: ContainerMessageType) {
@@ -3047,8 +3179,7 @@ export class ContainerRuntime
3047
3179
  }
3048
3180
 
3049
3181
  private submit(
3050
- type: ContainerMessageType,
3051
- contents: any,
3182
+ containerRuntimeMessage: ContainerRuntimeMessage,
3052
3183
  localOpMetadata: unknown = undefined,
3053
3184
  metadata: Record<string, unknown> | undefined = undefined,
3054
3185
  ): void {
@@ -3061,17 +3192,18 @@ export class ContainerRuntime
3061
3192
  0x132 /* "sending ops in detached container" */,
3062
3193
  );
3063
3194
 
3064
- const serializedContent = JSON.stringify({ type, contents });
3195
+ const serializedContent = JSON.stringify(containerRuntimeMessage);
3065
3196
 
3066
3197
  // Note that the real (non-proxy) delta manager is used here to get the readonly info. This is because
3067
3198
  // container runtime's ability to submit ops depend on the actual readonly state of the delta manager.
3068
3199
  if (this.innerDeltaManager.readOnlyInfo.readonly) {
3069
- this.logger.sendTelemetryEvent({
3200
+ this.mc.logger.sendTelemetryEvent({
3070
3201
  eventName: "SubmitOpInReadonly",
3071
3202
  connected: this.connected,
3072
3203
  });
3073
3204
  }
3074
3205
 
3206
+ const type = containerRuntimeMessage.type;
3075
3207
  const message: BatchMessage = {
3076
3208
  contents: serializedContent,
3077
3209
  type,
@@ -3130,7 +3262,7 @@ export class ContainerRuntime
3130
3262
  throw error;
3131
3263
  }
3132
3264
 
3133
- if (this.isContainerMessageDirtyable(type, contents)) {
3265
+ if (this.isContainerMessageDirtyable(containerRuntimeMessage)) {
3134
3266
  this.updateDocumentDirtyState(true);
3135
3267
  }
3136
3268
  }
@@ -3186,9 +3318,9 @@ export class ContainerRuntime
3186
3318
  assert(this.outbox.isEmpty, 0x3d4 /* System op in the middle of a batch */);
3187
3319
 
3188
3320
  // 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);
3321
+ return this.submitSummaryFn !== undefined
3322
+ ? this.submitSummaryFn(contents, referenceSequenceNumber)
3323
+ : this.submitFn(MessageType.Summarize, contents, false);
3192
3324
  }
3193
3325
 
3194
3326
  /**
@@ -3243,38 +3375,38 @@ export class ContainerRuntime
3243
3375
 
3244
3376
  private reSubmit(message: IPendingBatchMessage) {
3245
3377
  // 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);
3378
+ const containerRuntimeMessage = this.parseOpContent(message.content);
3379
+ this.reSubmitCore(containerRuntimeMessage, message.localOpMetadata, message.opMetadata);
3248
3380
  }
3249
3381
 
3250
3382
  /**
3251
3383
  * Finds the right store and asks it to resubmit the message. This typically happens when we
3252
3384
  * reconnect and there are pending messages.
3253
- * @param content - The content of the original message.
3385
+ * @param message - The original ContainerRuntimeMessage.
3254
3386
  * @param localOpMetadata - The local metadata associated with the original message.
3255
3387
  */
3256
3388
  private reSubmitCore(
3257
- type: ContainerMessageType,
3258
- content: any,
3389
+ message: ContainerRuntimeMessage,
3259
3390
  localOpMetadata: unknown,
3260
3391
  opMetadata: Record<string, unknown> | undefined,
3261
3392
  ) {
3262
- switch (type) {
3393
+ const contents = message.contents;
3394
+ switch (message.type) {
3263
3395
  case ContainerMessageType.FluidDataStoreOp:
3264
3396
  // For Operations, call resubmitDataStoreOp which will find the right store
3265
3397
  // and trigger resubmission on it.
3266
- this.dataStores.resubmitDataStoreOp(content, localOpMetadata);
3398
+ this.dataStores.resubmitDataStoreOp(contents, localOpMetadata);
3267
3399
  break;
3268
3400
  case ContainerMessageType.Attach:
3269
3401
  case ContainerMessageType.Alias:
3270
- this.submit(type, content, localOpMetadata);
3402
+ this.submit(message, localOpMetadata);
3271
3403
  break;
3272
3404
  case ContainerMessageType.IdAllocation:
3273
3405
  // Remove the stashedState from the op if it's a stashed op
3274
- if (content.stashedState !== undefined) {
3275
- delete content.stashedState;
3406
+ if (contents.stashedState !== undefined) {
3407
+ delete contents.stashedState;
3276
3408
  }
3277
- this.submit(type, content, localOpMetadata);
3409
+ this.submit(message, localOpMetadata);
3278
3410
  break;
3279
3411
  case ContainerMessageType.ChunkedOp:
3280
3412
  throw new Error(`chunkedOp not expected here`);
@@ -3282,10 +3414,13 @@ export class ContainerRuntime
3282
3414
  this.blobManager.reSubmit(opMetadata);
3283
3415
  break;
3284
3416
  case ContainerMessageType.Rejoin:
3285
- this.submit(type, content);
3417
+ this.submit(message);
3286
3418
  break;
3287
3419
  default:
3288
- unreachableCase(type, `Unknown ContainerMessageType: ${type}`);
3420
+ unreachableCase(
3421
+ message.type,
3422
+ `Unknown ContainerMessageType [type: ${message.type}]`,
3423
+ );
3289
3424
  }
3290
3425
  }
3291
3426
 
@@ -3348,7 +3483,7 @@ export class ContainerRuntime
3348
3483
  * change that started fetching latest snapshot always.
3349
3484
  */
3350
3485
  if (fetchResult.latestSnapshotRefSeq < summaryRefSeq) {
3351
- fetchResult = await this.fetchSnapshotFromStorage(
3486
+ fetchResult = await this.fetchSnapshotFromStorageAndClose(
3352
3487
  summaryLogger,
3353
3488
  {
3354
3489
  eventName: "RefreshLatestSummaryAckFetchBackCompat",
@@ -3455,10 +3590,15 @@ export class ContainerRuntime
3455
3590
  event: ITelemetryGenericEvent,
3456
3591
  readAndParseBlob: ReadAndParseBlob,
3457
3592
  ): Promise<{ snapshotTree: ISnapshotTree; versionId: string; latestSnapshotRefSeq: number }> {
3458
- return this.fetchSnapshotFromStorage(logger, event, readAndParseBlob, null /* latest */);
3593
+ return this.fetchSnapshotFromStorageAndClose(
3594
+ logger,
3595
+ event,
3596
+ readAndParseBlob,
3597
+ null /* latest */,
3598
+ );
3459
3599
  }
3460
3600
 
3461
- private async fetchSnapshotFromStorage(
3601
+ private async fetchSnapshotFromStorageAndClose(
3462
3602
  logger: ITelemetryLoggerExt,
3463
3603
  event: ITelemetryGenericEvent,
3464
3604
  readAndParseBlob: ReadAndParseBlob,
@@ -3514,9 +3654,7 @@ export class ContainerRuntime
3514
3654
  // We choose to close the summarizer after the snapshot cache is updated to avoid
3515
3655
  // situations which the main client (which is likely to be re-elected as the leader again)
3516
3656
  // loads the summarizer from cache.
3517
- if (this.summaryStateUpdateMethod === "restart") {
3518
- const error = new GenericError("Restarting summarizer instead of refreshing");
3519
-
3657
+ if (this.summaryStateUpdateMethod !== "refreshFromSnapshot") {
3520
3658
  this.mc.logger.sendTelemetryEvent(
3521
3659
  {
3522
3660
  ...event,
@@ -3526,14 +3664,13 @@ export class ContainerRuntime
3526
3664
  versionId: versionId != null ? versionId : undefined,
3527
3665
  closeSummarizerDelayMs: this.closeSummarizerDelayMs,
3528
3666
  },
3529
- error,
3667
+ new GenericError("Restarting summarizer instead of refreshing"),
3530
3668
  );
3531
3669
 
3532
- // Delay 10 seconds before restarting summarizer to prevent the summarizer from restarting too frequently.
3670
+ // Delay before restarting summarizer to prevent the summarizer from restarting too frequently.
3533
3671
  await delay(this.closeSummarizerDelayMs);
3534
3672
  this._summarizer?.stop("latestSummaryStateStale");
3535
- this.closeFn();
3536
- throw error;
3673
+ this.disposeFn();
3537
3674
  }
3538
3675
 
3539
3676
  return snapshotResults;
@@ -3557,7 +3694,7 @@ export class ContainerRuntime
3557
3694
  }
3558
3695
 
3559
3696
  public readonly summarizeOnDemand: ISummarizer["summarizeOnDemand"] = (...args) => {
3560
- if (this.clientDetails.type === summarizerClientType) {
3697
+ if (this.isSummarizerClient) {
3561
3698
  return this.summarizer.summarizeOnDemand(...args);
3562
3699
  } else if (this.summaryManager !== undefined) {
3563
3700
  return this.summaryManager.summarizeOnDemand(...args);
@@ -3570,7 +3707,7 @@ export class ContainerRuntime
3570
3707
  };
3571
3708
 
3572
3709
  public readonly enqueueSummarize: ISummarizer["enqueueSummarize"] = (...args) => {
3573
- if (this.clientDetails.type === summarizerClientType) {
3710
+ if (this.isSummarizerClient) {
3574
3711
  return this.summarizer.enqueueSummarize(...args);
3575
3712
  } else if (this.summaryManager !== undefined) {
3576
3713
  return this.summaryManager.enqueueSummarize(...args);