@fluidframework/container-runtime 2.0.0-rc.3.0.3 → 2.0.0-rc.4.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (289) hide show
  1. package/CHANGELOG.md +46 -0
  2. package/api-report/container-runtime.api.md +72 -34
  3. package/dist/batchTracker.d.ts +1 -1
  4. package/dist/batchTracker.d.ts.map +1 -1
  5. package/dist/batchTracker.js.map +1 -1
  6. package/dist/blobManager.d.ts +7 -7
  7. package/dist/blobManager.d.ts.map +1 -1
  8. package/dist/blobManager.js +2 -4
  9. package/dist/blobManager.js.map +1 -1
  10. package/dist/channelCollection.d.ts +6 -4
  11. package/dist/channelCollection.d.ts.map +1 -1
  12. package/dist/channelCollection.js +20 -7
  13. package/dist/channelCollection.js.map +1 -1
  14. package/dist/connectionTelemetry.d.ts +2 -2
  15. package/dist/connectionTelemetry.d.ts.map +1 -1
  16. package/dist/connectionTelemetry.js +54 -5
  17. package/dist/connectionTelemetry.js.map +1 -1
  18. package/dist/containerRuntime.d.ts +17 -35
  19. package/dist/containerRuntime.d.ts.map +1 -1
  20. package/dist/containerRuntime.js +194 -163
  21. package/dist/containerRuntime.js.map +1 -1
  22. package/dist/dataStore.d.ts +1 -1
  23. package/dist/dataStore.d.ts.map +1 -1
  24. package/dist/dataStore.js.map +1 -1
  25. package/dist/dataStoreContext.d.ts +9 -6
  26. package/dist/dataStoreContext.d.ts.map +1 -1
  27. package/dist/dataStoreContext.js +19 -5
  28. package/dist/dataStoreContext.js.map +1 -1
  29. package/dist/dataStoreContexts.d.ts.map +1 -1
  30. package/dist/dataStoreContexts.js.map +1 -1
  31. package/dist/deltaManagerProxies.d.ts +81 -0
  32. package/dist/deltaManagerProxies.d.ts.map +1 -0
  33. package/dist/{deltaManagerSummarizerProxy.js → deltaManagerProxies.js} +75 -20
  34. package/dist/deltaManagerProxies.js.map +1 -0
  35. package/dist/deltaScheduler.d.ts +2 -2
  36. package/dist/deltaScheduler.d.ts.map +1 -1
  37. package/dist/deltaScheduler.js.map +1 -1
  38. package/dist/gc/garbageCollection.d.ts +1 -1
  39. package/dist/gc/garbageCollection.d.ts.map +1 -1
  40. package/dist/gc/garbageCollection.js.map +1 -1
  41. package/dist/gc/gcDefinitions.d.ts +1 -1
  42. package/dist/gc/gcDefinitions.d.ts.map +1 -1
  43. package/dist/gc/gcDefinitions.js.map +1 -1
  44. package/dist/gc/gcTelemetry.d.ts +1 -2
  45. package/dist/gc/gcTelemetry.d.ts.map +1 -1
  46. package/dist/gc/gcTelemetry.js.map +1 -1
  47. package/dist/index.d.ts +1 -1
  48. package/dist/index.d.ts.map +1 -1
  49. package/dist/index.js.map +1 -1
  50. package/dist/legacy.d.ts +6 -1
  51. package/dist/messageTypes.d.ts +5 -2
  52. package/dist/messageTypes.d.ts.map +1 -1
  53. package/dist/messageTypes.js.map +1 -1
  54. package/dist/metadata.d.ts +2 -2
  55. package/dist/metadata.d.ts.map +1 -1
  56. package/dist/metadata.js.map +1 -1
  57. package/dist/opLifecycle/batchManager.d.ts +4 -1
  58. package/dist/opLifecycle/batchManager.d.ts.map +1 -1
  59. package/dist/opLifecycle/batchManager.js +0 -10
  60. package/dist/opLifecycle/batchManager.js.map +1 -1
  61. package/dist/opLifecycle/outbox.d.ts +0 -4
  62. package/dist/opLifecycle/outbox.d.ts.map +1 -1
  63. package/dist/opLifecycle/outbox.js +7 -38
  64. package/dist/opLifecycle/outbox.js.map +1 -1
  65. package/dist/packageVersion.d.ts +1 -1
  66. package/dist/packageVersion.js +1 -1
  67. package/dist/packageVersion.js.map +1 -1
  68. package/dist/pendingStateManager.d.ts +9 -2
  69. package/dist/pendingStateManager.d.ts.map +1 -1
  70. package/dist/pendingStateManager.js +26 -10
  71. package/dist/pendingStateManager.js.map +1 -1
  72. package/dist/scheduleManager.d.ts +2 -2
  73. package/dist/scheduleManager.d.ts.map +1 -1
  74. package/dist/scheduleManager.js.map +1 -1
  75. package/dist/summary/documentSchema.d.ts +3 -1
  76. package/dist/summary/documentSchema.d.ts.map +1 -1
  77. package/dist/summary/documentSchema.js +25 -7
  78. package/dist/summary/documentSchema.js.map +1 -1
  79. package/dist/summary/index.d.ts +1 -1
  80. package/dist/summary/index.d.ts.map +1 -1
  81. package/dist/summary/index.js.map +1 -1
  82. package/dist/summary/orderedClientElection.d.ts +2 -2
  83. package/dist/summary/orderedClientElection.d.ts.map +1 -1
  84. package/dist/summary/orderedClientElection.js.map +1 -1
  85. package/dist/summary/runningSummarizer.js +10 -10
  86. package/dist/summary/runningSummarizer.js.map +1 -1
  87. package/dist/summary/summarizer.d.ts +1 -2
  88. package/dist/summary/summarizer.d.ts.map +1 -1
  89. package/dist/summary/summarizer.js.map +1 -1
  90. package/dist/summary/summarizerClientElection.d.ts +1 -1
  91. package/dist/summary/summarizerClientElection.d.ts.map +1 -1
  92. package/dist/summary/summarizerClientElection.js.map +1 -1
  93. package/dist/summary/summarizerHeuristics.d.ts +1 -1
  94. package/dist/summary/summarizerHeuristics.d.ts.map +1 -1
  95. package/dist/summary/summarizerHeuristics.js.map +1 -1
  96. package/dist/summary/summarizerNode/summarizerNode.d.ts +4 -3
  97. package/dist/summary/summarizerNode/summarizerNode.d.ts.map +1 -1
  98. package/dist/summary/summarizerNode/summarizerNode.js +4 -10
  99. package/dist/summary/summarizerNode/summarizerNode.js.map +1 -1
  100. package/dist/summary/summarizerNode/summarizerNodeUtils.d.ts +2 -3
  101. package/dist/summary/summarizerNode/summarizerNodeUtils.d.ts.map +1 -1
  102. package/dist/summary/summarizerNode/summarizerNodeUtils.js.map +1 -1
  103. package/dist/summary/summarizerNode/summarizerNodeWithGc.d.ts +1 -2
  104. package/dist/summary/summarizerNode/summarizerNodeWithGc.d.ts.map +1 -1
  105. package/dist/summary/summarizerNode/summarizerNodeWithGc.js +2 -9
  106. package/dist/summary/summarizerNode/summarizerNodeWithGc.js.map +1 -1
  107. package/dist/summary/summarizerTypes.d.ts +3 -5
  108. package/dist/summary/summarizerTypes.d.ts.map +1 -1
  109. package/dist/summary/summarizerTypes.js.map +1 -1
  110. package/dist/summary/summaryCollection.d.ts +2 -2
  111. package/dist/summary/summaryCollection.d.ts.map +1 -1
  112. package/dist/summary/summaryCollection.js.map +1 -1
  113. package/dist/summary/summaryFormat.d.ts +25 -5
  114. package/dist/summary/summaryFormat.d.ts.map +1 -1
  115. package/dist/summary/summaryFormat.js.map +1 -1
  116. package/dist/summary/summaryGenerator.d.ts +1 -2
  117. package/dist/summary/summaryGenerator.d.ts.map +1 -1
  118. package/dist/summary/summaryGenerator.js +12 -11
  119. package/dist/summary/summaryGenerator.js.map +1 -1
  120. package/dist/summary/summaryManager.d.ts.map +1 -1
  121. package/dist/summary/summaryManager.js +5 -5
  122. package/dist/summary/summaryManager.js.map +1 -1
  123. package/lib/batchTracker.d.ts +1 -1
  124. package/lib/batchTracker.d.ts.map +1 -1
  125. package/lib/batchTracker.js.map +1 -1
  126. package/lib/blobManager.d.ts +7 -7
  127. package/lib/blobManager.d.ts.map +1 -1
  128. package/lib/blobManager.js +3 -5
  129. package/lib/blobManager.js.map +1 -1
  130. package/lib/channelCollection.d.ts +6 -4
  131. package/lib/channelCollection.d.ts.map +1 -1
  132. package/lib/channelCollection.js +21 -8
  133. package/lib/channelCollection.js.map +1 -1
  134. package/lib/connectionTelemetry.d.ts +2 -2
  135. package/lib/connectionTelemetry.d.ts.map +1 -1
  136. package/lib/connectionTelemetry.js +49 -0
  137. package/lib/connectionTelemetry.js.map +1 -1
  138. package/lib/containerRuntime.d.ts +17 -35
  139. package/lib/containerRuntime.d.ts.map +1 -1
  140. package/lib/containerRuntime.js +195 -164
  141. package/lib/containerRuntime.js.map +1 -1
  142. package/lib/dataStore.d.ts +1 -1
  143. package/lib/dataStore.d.ts.map +1 -1
  144. package/lib/dataStore.js +1 -1
  145. package/lib/dataStore.js.map +1 -1
  146. package/lib/dataStoreContext.d.ts +9 -6
  147. package/lib/dataStoreContext.d.ts.map +1 -1
  148. package/lib/dataStoreContext.js +21 -7
  149. package/lib/dataStoreContext.js.map +1 -1
  150. package/lib/dataStoreContexts.d.ts.map +1 -1
  151. package/lib/dataStoreContexts.js.map +1 -1
  152. package/lib/deltaManagerProxies.d.ts +81 -0
  153. package/lib/deltaManagerProxies.d.ts.map +1 -0
  154. package/lib/{deltaManagerSummarizerProxy.js → deltaManagerProxies.js} +72 -19
  155. package/lib/deltaManagerProxies.js.map +1 -0
  156. package/lib/deltaScheduler.d.ts +2 -2
  157. package/lib/deltaScheduler.d.ts.map +1 -1
  158. package/lib/deltaScheduler.js.map +1 -1
  159. package/lib/gc/garbageCollection.d.ts +1 -1
  160. package/lib/gc/garbageCollection.d.ts.map +1 -1
  161. package/lib/gc/garbageCollection.js.map +1 -1
  162. package/lib/gc/gcDefinitions.d.ts +1 -1
  163. package/lib/gc/gcDefinitions.d.ts.map +1 -1
  164. package/lib/gc/gcDefinitions.js.map +1 -1
  165. package/lib/gc/gcTelemetry.d.ts +1 -2
  166. package/lib/gc/gcTelemetry.d.ts.map +1 -1
  167. package/lib/gc/gcTelemetry.js.map +1 -1
  168. package/lib/index.d.ts +1 -1
  169. package/lib/index.d.ts.map +1 -1
  170. package/lib/index.js.map +1 -1
  171. package/lib/legacy.d.ts +6 -1
  172. package/lib/messageTypes.d.ts +5 -2
  173. package/lib/messageTypes.d.ts.map +1 -1
  174. package/lib/messageTypes.js.map +1 -1
  175. package/lib/metadata.d.ts +2 -2
  176. package/lib/metadata.d.ts.map +1 -1
  177. package/lib/metadata.js.map +1 -1
  178. package/lib/opLifecycle/batchManager.d.ts +4 -1
  179. package/lib/opLifecycle/batchManager.d.ts.map +1 -1
  180. package/lib/opLifecycle/batchManager.js +0 -10
  181. package/lib/opLifecycle/batchManager.js.map +1 -1
  182. package/lib/opLifecycle/outbox.d.ts +0 -4
  183. package/lib/opLifecycle/outbox.d.ts.map +1 -1
  184. package/lib/opLifecycle/outbox.js +7 -38
  185. package/lib/opLifecycle/outbox.js.map +1 -1
  186. package/lib/packageVersion.d.ts +1 -1
  187. package/lib/packageVersion.js +1 -1
  188. package/lib/packageVersion.js.map +1 -1
  189. package/lib/pendingStateManager.d.ts +9 -2
  190. package/lib/pendingStateManager.d.ts.map +1 -1
  191. package/lib/pendingStateManager.js +27 -11
  192. package/lib/pendingStateManager.js.map +1 -1
  193. package/lib/scheduleManager.d.ts +2 -2
  194. package/lib/scheduleManager.d.ts.map +1 -1
  195. package/lib/scheduleManager.js.map +1 -1
  196. package/lib/summary/documentSchema.d.ts +3 -1
  197. package/lib/summary/documentSchema.d.ts.map +1 -1
  198. package/lib/summary/documentSchema.js +25 -7
  199. package/lib/summary/documentSchema.js.map +1 -1
  200. package/lib/summary/index.d.ts +1 -1
  201. package/lib/summary/index.d.ts.map +1 -1
  202. package/lib/summary/index.js.map +1 -1
  203. package/lib/summary/orderedClientElection.d.ts +2 -2
  204. package/lib/summary/orderedClientElection.d.ts.map +1 -1
  205. package/lib/summary/orderedClientElection.js +1 -1
  206. package/lib/summary/orderedClientElection.js.map +1 -1
  207. package/lib/summary/runningSummarizer.js +1 -1
  208. package/lib/summary/runningSummarizer.js.map +1 -1
  209. package/lib/summary/summarizer.d.ts +1 -2
  210. package/lib/summary/summarizer.d.ts.map +1 -1
  211. package/lib/summary/summarizer.js.map +1 -1
  212. package/lib/summary/summarizerClientElection.d.ts +1 -1
  213. package/lib/summary/summarizerClientElection.d.ts.map +1 -1
  214. package/lib/summary/summarizerClientElection.js.map +1 -1
  215. package/lib/summary/summarizerHeuristics.d.ts +1 -1
  216. package/lib/summary/summarizerHeuristics.d.ts.map +1 -1
  217. package/lib/summary/summarizerHeuristics.js.map +1 -1
  218. package/lib/summary/summarizerNode/summarizerNode.d.ts +4 -3
  219. package/lib/summary/summarizerNode/summarizerNode.d.ts.map +1 -1
  220. package/lib/summary/summarizerNode/summarizerNode.js +4 -10
  221. package/lib/summary/summarizerNode/summarizerNode.js.map +1 -1
  222. package/lib/summary/summarizerNode/summarizerNodeUtils.d.ts +2 -3
  223. package/lib/summary/summarizerNode/summarizerNodeUtils.d.ts.map +1 -1
  224. package/lib/summary/summarizerNode/summarizerNodeUtils.js.map +1 -1
  225. package/lib/summary/summarizerNode/summarizerNodeWithGc.d.ts +1 -2
  226. package/lib/summary/summarizerNode/summarizerNodeWithGc.d.ts.map +1 -1
  227. package/lib/summary/summarizerNode/summarizerNodeWithGc.js +2 -9
  228. package/lib/summary/summarizerNode/summarizerNodeWithGc.js.map +1 -1
  229. package/lib/summary/summarizerTypes.d.ts +3 -5
  230. package/lib/summary/summarizerTypes.d.ts.map +1 -1
  231. package/lib/summary/summarizerTypes.js.map +1 -1
  232. package/lib/summary/summaryCollection.d.ts +2 -2
  233. package/lib/summary/summaryCollection.d.ts.map +1 -1
  234. package/lib/summary/summaryCollection.js.map +1 -1
  235. package/lib/summary/summaryFormat.d.ts +25 -5
  236. package/lib/summary/summaryFormat.d.ts.map +1 -1
  237. package/lib/summary/summaryFormat.js.map +1 -1
  238. package/lib/summary/summaryGenerator.d.ts +1 -2
  239. package/lib/summary/summaryGenerator.d.ts.map +1 -1
  240. package/lib/summary/summaryGenerator.js +5 -4
  241. package/lib/summary/summaryGenerator.js.map +1 -1
  242. package/lib/summary/summaryManager.d.ts.map +1 -1
  243. package/lib/summary/summaryManager.js +2 -2
  244. package/lib/summary/summaryManager.js.map +1 -1
  245. package/lib/tsdoc-metadata.json +1 -1
  246. package/package.json +28 -50
  247. package/src/batchTracker.ts +1 -2
  248. package/src/blobManager.ts +11 -10
  249. package/src/channelCollection.ts +30 -12
  250. package/src/connectionTelemetry.ts +59 -4
  251. package/src/containerRuntime.ts +262 -239
  252. package/src/dataStore.ts +7 -4
  253. package/src/dataStoreContext.ts +57 -16
  254. package/src/dataStoreContexts.ts +1 -2
  255. package/src/{deltaManagerSummarizerProxy.ts → deltaManagerProxies.ts} +98 -24
  256. package/src/deltaScheduler.ts +2 -3
  257. package/src/gc/garbageCollection.ts +1 -1
  258. package/src/gc/gcDefinitions.ts +1 -1
  259. package/src/gc/gcTelemetry.ts +1 -3
  260. package/src/index.ts +5 -0
  261. package/src/messageTypes.ts +4 -2
  262. package/src/metadata.ts +2 -2
  263. package/src/opLifecycle/README.md +4 -4
  264. package/src/opLifecycle/batchManager.ts +5 -14
  265. package/src/opLifecycle/outbox.ts +7 -53
  266. package/src/packageVersion.ts +1 -1
  267. package/src/pendingStateManager.ts +38 -15
  268. package/src/scheduleManager.ts +2 -2
  269. package/src/summary/documentSchema.ts +37 -12
  270. package/src/summary/index.ts +4 -0
  271. package/src/summary/orderedClientElection.ts +6 -3
  272. package/src/summary/runningSummarizer.ts +1 -1
  273. package/src/summary/summarizer.ts +1 -1
  274. package/src/summary/summarizerClientElection.ts +1 -1
  275. package/src/summary/summarizerHeuristics.ts +1 -1
  276. package/src/summary/summarizerNode/summarizerNode.ts +3 -12
  277. package/src/summary/summarizerNode/summarizerNodeUtils.ts +2 -3
  278. package/src/summary/summarizerNode/summarizerNodeWithGc.ts +1 -10
  279. package/src/summary/summarizerTypes.ts +6 -5
  280. package/src/summary/summaryCollection.ts +2 -2
  281. package/src/summary/summaryFormat.ts +30 -4
  282. package/src/summary/summaryGenerator.ts +20 -9
  283. package/src/summary/summaryManager.ts +6 -3
  284. package/dist/deltaManagerSummarizerProxy.d.ts +0 -44
  285. package/dist/deltaManagerSummarizerProxy.d.ts.map +0 -1
  286. package/dist/deltaManagerSummarizerProxy.js.map +0 -1
  287. package/lib/deltaManagerSummarizerProxy.d.ts +0 -44
  288. package/lib/deltaManagerSummarizerProxy.d.ts.map +0 -1
  289. package/lib/deltaManagerSummarizerProxy.js.map +0 -1
@@ -7,8 +7,8 @@ import { Trace, TypedEventEmitter } from "@fluid-internal/client-utils";
7
7
  import {
8
8
  AttachState,
9
9
  IAudience,
10
+ ISelf,
10
11
  ICriticalContainerError,
11
- IDeltaManager,
12
12
  } from "@fluidframework/container-definitions";
13
13
  import {
14
14
  IBatchMessage,
@@ -17,6 +17,8 @@ import {
17
17
  ILoader,
18
18
  IRuntime,
19
19
  LoaderHeader,
20
+ type IAudienceEvents,
21
+ IDeltaManager,
20
22
  } from "@fluidframework/container-definitions/internal";
21
23
  import {
22
24
  IContainerRuntime,
@@ -26,11 +28,12 @@ import {
26
28
  FluidObject,
27
29
  IFluidHandle,
28
30
  IFluidHandleContext,
31
+ type IFluidHandleInternal,
29
32
  IProvideFluidHandleContext,
30
33
  IRequest,
31
34
  IResponse,
32
35
  ITelemetryBaseLogger,
33
- } from "@fluidframework/core-interfaces";
36
+ } from "@fluidframework/core-interfaces/internal";
34
37
  import { ISignalEnvelope } from "@fluidframework/core-interfaces/internal";
35
38
  import {
36
39
  assert,
@@ -99,11 +102,9 @@ import {
99
102
  responseToException,
100
103
  seqFromTree,
101
104
  } from "@fluidframework/runtime-utils/internal";
105
+ import type { ITelemetryGenericEventExt } from "@fluidframework/telemetry-utils/internal";
102
106
  import {
103
- type ITelemetryGenericEventExt,
104
107
  ITelemetryLoggerExt,
105
- } from "@fluidframework/telemetry-utils";
106
- import {
107
108
  DataCorruptionError,
108
109
  DataProcessingError,
109
110
  GenericError,
@@ -130,7 +131,7 @@ import { IPerfSignalReport, ReportOpPerfTelemetry } from "./connectionTelemetry.
130
131
  import { ContainerFluidHandleContext } from "./containerHandleContext.js";
131
132
  import { channelToDataStore } from "./dataStore.js";
132
133
  import { FluidDataStoreRegistry } from "./dataStoreRegistry.js";
133
- import { DeltaManagerSummarizerProxy } from "./deltaManagerSummarizerProxy.js";
134
+ import { DeltaManagerPendingOpsProxy, DeltaManagerSummarizerProxy } from "./deltaManagerProxies.js";
134
135
  import {
135
136
  GCNodeType,
136
137
  GarbageCollector,
@@ -150,7 +151,7 @@ import {
150
151
  type OutboundContainerRuntimeMessage,
151
152
  type UnknownContainerRuntimeMessage,
152
153
  } from "./messageTypes.js";
153
- import { IBatchMetadata, IIdAllocationMetadata } from "./metadata.js";
154
+ import { IBatchMetadata, ISavedOpMetadata } from "./metadata.js";
154
155
  import {
155
156
  BatchMessage,
156
157
  IBatch,
@@ -161,7 +162,6 @@ import {
161
162
  OpSplitter,
162
163
  Outbox,
163
164
  RemoteMessageProcessor,
164
- getLongStack,
165
165
  } from "./opLifecycle/index.js";
166
166
  import { pkgVersion } from "./packageVersion.js";
167
167
  import {
@@ -461,25 +461,12 @@ export interface IContainerRuntimeOptions {
461
461
  */
462
462
  readonly enableRuntimeIdCompressor?: IdCompressorMode;
463
463
 
464
- /**
465
- * If enabled, the runtime will block all attempts to send an op inside the
466
- * {@link ContainerRuntime#ensureNoDataModelChanges} callback. The callback is used by
467
- * {@link @fluidframework/shared-object-base#SharedObjectCore} for event handlers so enabling this
468
- * will disallow modifying DDSes while handling DDS events.
469
- *
470
- * By default, the feature is disabled. If enabled from options, the `Fluid.ContainerRuntime.DisableOpReentryCheck`
471
- * can be used to disable it at runtime.
472
- */
473
- readonly enableOpReentryCheck?: boolean;
474
464
  /**
475
465
  * If enabled, the runtime will group messages within a batch into a single
476
466
  * message to be sent to the service.
477
467
  * The grouping an ungrouping of such messages is handled by the "OpGroupingManager".
478
468
  *
479
- * By default, the feature is disabled. If enabled from options, the `Fluid.ContainerRuntime.DisableGroupedBatching`
480
- * flag can be used to disable it at runtime.
481
- *
482
- * @experimental Not ready for use.
469
+ * By default, the feature is enabled.
483
470
  */
484
471
  readonly enableGroupedBatching?: boolean;
485
472
 
@@ -671,11 +658,13 @@ type MessageWithContext =
671
658
  message: InboundSequencedContainerRuntimeMessage;
672
659
  modernRuntimeMessage: true;
673
660
  local: boolean;
661
+ savedOp?: boolean;
674
662
  }
675
663
  | {
676
664
  message: InboundSequencedContainerRuntimeMessageOrSystemMessage;
677
665
  modernRuntimeMessage: false;
678
666
  local: boolean;
667
+ savedOp?: boolean;
679
668
  };
680
669
 
681
670
  const summarizerRequestUrl = "_summarizer";
@@ -810,8 +799,7 @@ export class ContainerRuntime
810
799
  maxBatchSizeInBytes = defaultMaxBatchSizeInBytes,
811
800
  enableRuntimeIdCompressor,
812
801
  chunkSizeInBytes = defaultChunkSizeInBytes,
813
- enableOpReentryCheck = false,
814
- enableGroupedBatching = false,
802
+ enableGroupedBatching = true,
815
803
  explicitSchemaControl = false,
816
804
  } = runtimeOptions;
817
805
 
@@ -857,9 +845,9 @@ export class ContainerRuntime
857
845
 
858
846
  // Verify summary runtime sequence number matches protocol sequence number.
859
847
  const runtimeSequenceNumber = messageAtLastSummary?.sequenceNumber;
848
+ const protocolSequenceNumber = context.deltaManager.initialSequenceNumber;
860
849
  // When we load with pending state, we reuse an old snapshot so we don't expect these numbers to match
861
850
  if (!context.pendingLocalState && runtimeSequenceNumber !== undefined) {
862
- const protocolSequenceNumber = context.deltaManager.initialSequenceNumber;
863
851
  // Unless bypass is explicitly set, then take action when sequence numbers mismatch.
864
852
  if (
865
853
  loadSequenceNumberVerification !== "bypass" &&
@@ -972,9 +960,6 @@ export class ContainerRuntime
972
960
  }
973
961
  };
974
962
 
975
- const disableGroupedBatching = mc.config.getBoolean(
976
- "Fluid.ContainerRuntime.DisableGroupedBatching",
977
- );
978
963
  const disableCompression = mc.config.getBoolean(
979
964
  "Fluid.ContainerRuntime.CompressionDisabled",
980
965
  );
@@ -983,16 +968,15 @@ export class ContainerRuntime
983
968
  compressionOptions.minimumBatchSizeInBytes !== Infinity &&
984
969
  compressionOptions.compressionAlgorithm === "lz4";
985
970
 
986
- const opGroupingEnabled = disableGroupedBatching !== true && enableGroupedBatching;
987
-
988
971
  const documentSchemaController = new DocumentsSchemaController(
989
972
  existing,
973
+ protocolSequenceNumber,
990
974
  metadata?.documentSchema,
991
975
  {
992
976
  explicitSchemaControl,
993
977
  compressionLz4,
994
978
  idCompressorMode,
995
- opGroupingEnabled,
979
+ opGroupingEnabled: enableGroupedBatching,
996
980
  disallowedVersions: [],
997
981
  },
998
982
  (schema) => {
@@ -1001,7 +985,6 @@ export class ContainerRuntime
1001
985
  );
1002
986
 
1003
987
  const featureGatesForTelemetry: Record<string, boolean | number | undefined> = {
1004
- disableGroupedBatching,
1005
988
  disableCompression,
1006
989
  };
1007
990
 
@@ -1022,7 +1005,6 @@ export class ContainerRuntime
1022
1005
  chunkSizeInBytes,
1023
1006
  // Requires<> drops undefined from IdCompressorType
1024
1007
  enableRuntimeIdCompressor: enableRuntimeIdCompressor as "on" | "delayed",
1025
- enableOpReentryCheck,
1026
1008
  enableGroupedBatching,
1027
1009
  explicitSchemaControl,
1028
1010
  },
@@ -1115,7 +1097,17 @@ export class ContainerRuntime
1115
1097
  return this._getAttachState();
1116
1098
  }
1117
1099
 
1118
- public get documentSchema() {
1100
+ /**
1101
+ * Current session schema - defines what options are on & off.
1102
+ * It's overlap of document schema (controlled by summary & ops) and options controlling this session.
1103
+ * For example, document schema might have compression ON, but feature gates / runtime options turn it Off.
1104
+ * In such case it will be off in session schema (i.e. this session should not use compression), but this client
1105
+ * has to deal with compressed ops as other clients might send them.
1106
+ * And in reverse, session schema can have compression Off, but feature gates / runtime options want it On.
1107
+ * In such case it will be off in session schema, however this client will propose change to schema, and once / if
1108
+ * this op rountrips, compression will be On. Client can't send compressed ops until it's change in schema.
1109
+ */
1110
+ public get sessionSchema() {
1119
1111
  return this.documentsSchemaController.sessionSchema.runtime;
1120
1112
  }
1121
1113
 
@@ -1128,11 +1120,11 @@ export class ContainerRuntime
1128
1120
  // Id Compressor serializes final state (see getPendingLocalState()). As result, it needs to skip all ops that preceeded that state
1129
1121
  // (such ops will be marked by Loader layer as savedOp === true)
1130
1122
  // That said, in "delayed" mode it's possible that Id Compressor was never initialized before getPendingLocalState() is called.
1131
- // In such case we have to process all ops, including those marked with saveOp === true.
1123
+ // In such case we have to process all ops, including those marked with savedOp === true.
1132
1124
  private readonly skipSavedCompressorOps: boolean;
1133
1125
 
1134
1126
  public get idCompressorMode() {
1135
- return this.documentSchema.idCompressorMode;
1127
+ return this.sessionSchema.idCompressorMode;
1136
1128
  }
1137
1129
  /**
1138
1130
  * See IContainerRuntimeBase.idCompressor() for details.
@@ -1215,14 +1207,6 @@ export class ContainerRuntime
1215
1207
 
1216
1208
  private ensureNoDataModelChangesCalls = 0;
1217
1209
 
1218
- /**
1219
- * Tracks the number of detected reentrant ops to report,
1220
- * in order to self-throttle the telemetry events.
1221
- *
1222
- * This should be removed as part of ADO:2322
1223
- */
1224
- private opReentryCallsToReport = 5;
1225
-
1226
1210
  /**
1227
1211
  * Invokes the given callback and expects that no ops are submitted
1228
1212
  * until execution finishes. If an op is submitted, an error will be raised.
@@ -1256,16 +1240,8 @@ export class ContainerRuntime
1256
1240
 
1257
1241
  private dirtyContainer: boolean;
1258
1242
  private emitDirtyDocumentEvent = true;
1259
- private readonly enableOpReentryCheck: boolean;
1260
1243
  private readonly disableAttachReorder: boolean | undefined;
1261
1244
  private readonly closeSummarizerDelayMs: number;
1262
- /**
1263
- * If true, summary generated is validate before uploading it to the server. With single commit summaries,
1264
- * summaries will be accepted once uploaded, so they should be validated before upload. However, this can
1265
- * currently be controlled via a feature flag as its a new functionality.
1266
- */
1267
- private readonly validateSummaryBeforeUpload: boolean;
1268
-
1269
1245
  private readonly defaultTelemetrySignalSampleCount = 100;
1270
1246
  private readonly _perfSignalData: IPerfSignalReport = {
1271
1247
  signalsLost: 0,
@@ -1410,6 +1386,7 @@ export class ContainerRuntime
1410
1386
  loader,
1411
1387
  pendingLocalState,
1412
1388
  supportedFeatures,
1389
+ snapshotWithContents,
1413
1390
  } = context;
1414
1391
 
1415
1392
  this.mc = createChildMonitoringContext({
@@ -1422,14 +1399,13 @@ export class ContainerRuntime
1422
1399
  // If it's not in the list, then we will need to either use no compression, or fallback to some other (supported by format)
1423
1400
  // compression.
1424
1401
  const compressionOptions: ICompressionRuntimeOptions = {
1425
- minimumBatchSizeInBytes: this.documentSchema.compressionLz4
1402
+ minimumBatchSizeInBytes: this.sessionSchema.compressionLz4
1426
1403
  ? runtimeOptions.compressionOptions.minimumBatchSizeInBytes
1427
1404
  : Number.POSITIVE_INFINITY,
1428
1405
  compressionAlgorithm: CompressionAlgorithms.lz4,
1429
1406
  };
1430
1407
 
1431
1408
  this.innerDeltaManager = deltaManager;
1432
- this.deltaManager = new DeltaManagerSummarizerProxy(this.innerDeltaManager);
1433
1409
 
1434
1410
  // Here we could wrap/intercept on these functions to block/modify outgoing messages if needed.
1435
1411
  // This makes ContainerRuntime the final gatekeeper for outgoing messages.
@@ -1534,20 +1510,50 @@ export class ContainerRuntime
1534
1510
  opGroupingManager,
1535
1511
  );
1536
1512
 
1513
+ const pendingRuntimeState = pendingLocalState as IPendingRuntimeState | undefined;
1514
+ this.pendingStateManager = new PendingStateManager(
1515
+ {
1516
+ applyStashedOp: this.applyStashedOp.bind(this),
1517
+ clientId: () => this.clientId,
1518
+ close: this.closeFn,
1519
+ connected: () => this.connected,
1520
+ reSubmit: (message: IPendingBatchMessage) => {
1521
+ this.reSubmit(message);
1522
+ this.flush();
1523
+ },
1524
+ reSubmitBatch: this.reSubmitBatch.bind(this),
1525
+ isActiveConnection: () => this.innerDeltaManager.active,
1526
+ isAttached: () => this.attachState !== AttachState.Detached,
1527
+ },
1528
+ pendingRuntimeState?.pending,
1529
+ this.logger,
1530
+ );
1531
+
1532
+ let outerDeltaManager: IDeltaManager<ISequencedDocumentMessage, IDocumentMessage>;
1533
+ const useDeltaManagerOpsProxy =
1534
+ this.mc.config.getBoolean("Fluid.ContainerRuntime.DeltaManagerOpsProxy") !== false;
1535
+ // The summarizerDeltaManager Proxy is used to lie to the summarizer to convince it is in the right state as a summarizer client.
1536
+ const summarizerDeltaManagerProxy = new DeltaManagerSummarizerProxy(this.innerDeltaManager);
1537
+ outerDeltaManager = summarizerDeltaManagerProxy;
1538
+
1539
+ // The DeltaManagerPendingOpsProxy is used to control the minimum sequence number
1540
+ // It allows us to lie to the layers below so that they can maintain enough local state for rebasing ops.
1541
+ if (useDeltaManagerOpsProxy) {
1542
+ const pendingOpsDeltaManagerProxy = new DeltaManagerPendingOpsProxy(
1543
+ summarizerDeltaManagerProxy,
1544
+ this.pendingStateManager,
1545
+ );
1546
+ outerDeltaManager = pendingOpsDeltaManagerProxy;
1547
+ }
1548
+
1549
+ this.deltaManager = outerDeltaManager;
1550
+
1537
1551
  this.handleContext = new ContainerFluidHandleContext("", this);
1538
1552
 
1539
1553
  if (this.summaryConfiguration.state === "enabled") {
1540
1554
  this.validateSummaryHeuristicConfiguration(this.summaryConfiguration);
1541
1555
  }
1542
1556
 
1543
- const disableOpReentryCheck = this.mc.config.getBoolean(
1544
- "Fluid.ContainerRuntime.DisableOpReentryCheck",
1545
- );
1546
- this.enableOpReentryCheck =
1547
- runtimeOptions.enableOpReentryCheck === true &&
1548
- // Allow for a break-glass config to override the options
1549
- disableOpReentryCheck !== true;
1550
-
1551
1557
  this.summariesDisabled = this.isSummariesDisabled();
1552
1558
  this.maxOpsSinceLastSummary = this.getMaxOpsSinceLastSummary();
1553
1559
  this.initialSummarizerDelayMs = this.getInitialSummarizerDelayMs();
@@ -1567,8 +1573,6 @@ export class ContainerRuntime
1567
1573
  this._flushMode = runtimeOptions.flushMode;
1568
1574
  }
1569
1575
 
1570
- const pendingRuntimeState = pendingLocalState as IPendingRuntimeState | undefined;
1571
-
1572
1576
  if (context.attachState === AttachState.Attached) {
1573
1577
  const maxSnapshotCacheDurationMs = this._storage?.policies?.maximumCacheDurationMs;
1574
1578
  if (
@@ -1640,8 +1644,19 @@ export class ContainerRuntime
1640
1644
  return this.submitSignalFn(envelope2, targetClientId);
1641
1645
  };
1642
1646
 
1647
+ let snapshot: ISnapshot | ISnapshotTree | undefined = getSummaryForDatastores(
1648
+ baseSnapshot,
1649
+ metadata,
1650
+ );
1651
+ if (snapshot !== undefined && snapshotWithContents !== undefined) {
1652
+ snapshot = {
1653
+ ...snapshotWithContents,
1654
+ snapshotTree: snapshot,
1655
+ };
1656
+ }
1657
+
1643
1658
  this.channelCollection = new ChannelCollection(
1644
- getSummaryForDatastores(baseSnapshot, metadata),
1659
+ snapshot,
1645
1660
  parentContext,
1646
1661
  this.mc.logger,
1647
1662
  (props) => this.garbageCollector.nodeUpdated(props),
@@ -1684,24 +1699,6 @@ export class ContainerRuntime
1684
1699
  createChildLogger({ logger: this.logger, namespace: "ScheduleManager" }),
1685
1700
  );
1686
1701
 
1687
- this.pendingStateManager = new PendingStateManager(
1688
- {
1689
- applyStashedOp: this.applyStashedOp.bind(this),
1690
- clientId: () => this.clientId,
1691
- close: this.closeFn,
1692
- connected: () => this.connected,
1693
- reSubmit: (message: IPendingBatchMessage) => {
1694
- this.reSubmit(message);
1695
- this.flush();
1696
- },
1697
- reSubmitBatch: this.reSubmitBatch.bind(this),
1698
- isActiveConnection: () => this.innerDeltaManager.active,
1699
- isAttached: () => this.attachState !== AttachState.Detached,
1700
- },
1701
- pendingRuntimeState?.pending,
1702
- this.logger,
1703
- );
1704
-
1705
1702
  const disablePartialFlush = this.mc.config.getBoolean(
1706
1703
  "Fluid.ContainerRuntime.DisablePartialFlush",
1707
1704
  );
@@ -1736,16 +1733,37 @@ export class ContainerRuntime
1736
1733
  this.remoteMessageProcessor.clearPartialMessagesFor(clientId);
1737
1734
  });
1738
1735
 
1739
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
1740
- this._audience = audience!;
1736
+ this._audience = audience;
1737
+ if (audience.getSelf === undefined) {
1738
+ // back-compat, added in 2.0 RC3.
1739
+ // Purpose: deal with cases when we run against old loader that does not have newly added capabilities
1740
+ audience.getSelf = () => {
1741
+ const clientId = this._getClientId();
1742
+ return clientId === undefined
1743
+ ? undefined
1744
+ : ({
1745
+ clientId,
1746
+ client: audience.getMember(clientId),
1747
+ } satisfies ISelf);
1748
+ };
1749
+
1750
+ let oldClientId = this.clientId;
1751
+ this.on("connected", () => {
1752
+ const clientId = this.clientId;
1753
+ assert(clientId !== undefined, 0x975 /* can't be undefined */);
1754
+ (audience as unknown as TypedEventEmitter<IAudienceEvents>).emit(
1755
+ "selfChanged",
1756
+ { clientId: oldClientId },
1757
+ { clientId, client: audience.getMember(clientId) },
1758
+ );
1759
+ oldClientId = clientId;
1760
+ });
1761
+ }
1741
1762
 
1742
1763
  const closeSummarizerDelayOverride = this.mc.config.getNumber(
1743
1764
  "Fluid.ContainerRuntime.Test.CloseSummarizerDelayOverrideMs",
1744
1765
  );
1745
1766
  this.closeSummarizerDelayMs = closeSummarizerDelayOverride ?? defaultCloseSummarizerDelayMs;
1746
- this.validateSummaryBeforeUpload =
1747
- this.mc.config.getBoolean("Fluid.Summarizer.ValidateSummaryBeforeUpload") ?? false;
1748
-
1749
1767
  this.summaryCollection = new SummaryCollection(this.deltaManager, this.logger);
1750
1768
 
1751
1769
  this.dirtyContainer =
@@ -1857,9 +1875,9 @@ export class ContainerRuntime
1857
1875
  options: JSON.stringify(runtimeOptions),
1858
1876
  idCompressorModeMetadata: metadata?.documentSchema?.runtime?.idCompressorMode,
1859
1877
  idCompressorMode: this.idCompressorMode,
1878
+ sessionRuntimeSchema: JSON.stringify(this.sessionSchema),
1860
1879
  featureGates: JSON.stringify({
1861
1880
  ...featureGatesForTelemetry,
1862
- disableOpReentryCheck,
1863
1881
  disableChunking,
1864
1882
  disableAttachReorder: this.disableAttachReorder,
1865
1883
  disablePartialFlush,
@@ -1867,6 +1885,7 @@ export class ContainerRuntime
1867
1885
  }),
1868
1886
  telemetryDocumentId: this.telemetryDocumentId,
1869
1887
  groupedBatchingEnabled: this.groupedBatchingEnabled,
1888
+ initialSequenceNumber: this.deltaManager.initialSequenceNumber,
1870
1889
  });
1871
1890
 
1872
1891
  ReportOpPerfTelemetry(this.clientId, this.deltaManager, this, this.logger);
@@ -1889,6 +1908,11 @@ export class ContainerRuntime
1889
1908
  }
1890
1909
 
1891
1910
  public onSchemaChange(schema: IDocumentSchemaCurrent) {
1911
+ this.logger.sendTelemetryEvent({
1912
+ eventName: "SchemaChangeAccept",
1913
+ sessionRuntimeSchema: JSON.stringify(schema),
1914
+ });
1915
+
1892
1916
  // Most of the settings will be picked up only by new sessions (i.e. after reload).
1893
1917
  // We can make it better in the future (i.e. start to use op compression right away), but for simplicity
1894
1918
  // this is not done.
@@ -1938,9 +1962,9 @@ export class ContainerRuntime
1938
1962
  this.idCompressorMode === "on" ||
1939
1963
  (this.idCompressorMode === "delayed" && this.connected)
1940
1964
  ) {
1965
+ this._idCompressor = await this.createIdCompressor();
1941
1966
  // This is called from loadRuntime(), long before we process any ops, so there should be no ops accumulated yet.
1942
1967
  assert(this.pendingIdCompressorOps.length === 0, 0x8ec /* no pending ops */);
1943
- this._idCompressor = await this.createIdCompressor();
1944
1968
  }
1945
1969
 
1946
1970
  await this.garbageCollector.initializeBaseState();
@@ -2318,6 +2342,7 @@ export class ContainerRuntime
2318
2342
  let newState: boolean;
2319
2343
 
2320
2344
  try {
2345
+ this.submitIdAllocationOpIfNeeded(true);
2321
2346
  // replay the ops
2322
2347
  this.pendingStateManager.replayPendingStates();
2323
2348
  } finally {
@@ -2370,8 +2395,6 @@ export class ContainerRuntime
2370
2395
  return;
2371
2396
  case ContainerMessageType.BlobAttach:
2372
2397
  return;
2373
- case ContainerMessageType.ChunkedOp:
2374
- throw new Error("chunkedOp not expected here");
2375
2398
  case ContainerMessageType.Rejoin:
2376
2399
  throw new Error("rejoin not expected here");
2377
2400
  case ContainerMessageType.GC:
@@ -2384,7 +2407,7 @@ export class ContainerRuntime
2384
2407
  const compatBehavior = opContents.compatDetails?.behavior;
2385
2408
  if (!compatBehaviorAllowsMessageType(opContents.type, compatBehavior)) {
2386
2409
  const error = DataProcessingError.create(
2387
- "Stashed runtime message of unknown type",
2410
+ "Stashed runtime message of unexpected type",
2388
2411
  "applyStashedOp",
2389
2412
  undefined /* sequencedMessage */,
2390
2413
  {
@@ -2411,12 +2434,14 @@ export class ContainerRuntime
2411
2434
  ) {
2412
2435
  this._loadIdCompressor = this.createIdCompressor()
2413
2436
  .then((compressor) => {
2414
- this._idCompressor = compressor;
2415
2437
  // Finalize any ranges we received while the compressor was turned off.
2416
- for (const range of this.pendingIdCompressorOps) {
2417
- this._idCompressor.finalizeCreationRange(range);
2418
- }
2438
+ const ops = this.pendingIdCompressorOps;
2419
2439
  this.pendingIdCompressorOps = [];
2440
+ for (const range of ops) {
2441
+ compressor.finalizeCreationRange(range);
2442
+ }
2443
+ assert(this.pendingIdCompressorOps.length === 0, 0x976 /* No new ops added */);
2444
+ this._idCompressor = compressor;
2420
2445
  })
2421
2446
  .catch((error) => {
2422
2447
  this.logger.sendErrorEvent({ eventName: "IdCompressorDelayedLoad" }, error);
@@ -2427,6 +2452,14 @@ export class ContainerRuntime
2427
2452
  }
2428
2453
 
2429
2454
  public setConnectionState(connected: boolean, clientId?: string) {
2455
+ // Validate we have consistent state
2456
+ const currentClientId = this._audience.getSelf()?.clientId;
2457
+ assert(clientId === currentClientId, 0x977 /* input clientId does not match Audience */);
2458
+ assert(
2459
+ this.clientId === currentClientId,
2460
+ 0x978 /* this.clientId does not match Audience */,
2461
+ );
2462
+
2430
2463
  if (connected && this.idCompressorMode === "delayed") {
2431
2464
  // eslint-disable-next-line @typescript-eslint/no-floating-promises
2432
2465
  this.loadIdCompressor();
@@ -2540,21 +2573,28 @@ export class ContainerRuntime
2540
2573
  // We do not need to make a deep copy. Each layer will just replace message.contents itself,
2541
2574
  // but will not modify the contents object (likely it will replace it on the message).
2542
2575
  const messageCopy = { ...messageArg };
2576
+ const savedOp = (messageCopy.metadata as ISavedOpMetadata)?.savedOp;
2543
2577
  for (const message of this.remoteMessageProcessor.process(messageCopy)) {
2544
- if (modernRuntimeMessage) {
2545
- this.processCore({
2546
- // Cast it since we expect it to be this based on modernRuntimeMessage computation above.
2547
- // There is nothing really ensuring that anytime original message.type is Operation that
2548
- // the result messages will be so. In the end modern bool being true only directs to
2549
- // throw error if ultimately unrecognized without compat details saying otherwise.
2550
- message: message as InboundSequencedContainerRuntimeMessage,
2551
- local,
2552
- modernRuntimeMessage,
2553
- });
2554
- } else {
2555
- // Unrecognized message will be ignored.
2556
- this.processCore({ message, local, modernRuntimeMessage });
2557
- }
2578
+ const msg: MessageWithContext = modernRuntimeMessage
2579
+ ? {
2580
+ // Cast it since we expect it to be this based on modernRuntimeMessage computation above.
2581
+ // There is nothing really ensuring that anytime original message.type is Operation that
2582
+ // the result messages will be so. In the end modern bool being true only directs to
2583
+ // throw error if ultimately unrecognized without compat details saying otherwise.
2584
+ message: message as InboundSequencedContainerRuntimeMessage,
2585
+ local,
2586
+ modernRuntimeMessage,
2587
+ }
2588
+ : // Unrecognized message will be ignored.
2589
+ {
2590
+ message,
2591
+ local,
2592
+ modernRuntimeMessage,
2593
+ };
2594
+ msg.savedOp = savedOp;
2595
+
2596
+ // ensure that we observe any re-entrancy, and if needed, rebase ops
2597
+ this.ensureNoDataModelChanges(() => this.processCore(msg));
2558
2598
  }
2559
2599
  }
2560
2600
 
@@ -2565,6 +2605,17 @@ export class ContainerRuntime
2565
2605
  */
2566
2606
  private processCore(messageWithContext: MessageWithContext) {
2567
2607
  const { message, local } = messageWithContext;
2608
+
2609
+ // Intercept to reduce minimum sequence number to the delta manager's minimum sequence number.
2610
+ // Sequence numbers are not guaranteed to follow any sort of order. Re-entrancy is one of those situations
2611
+ if (
2612
+ this.deltaManager.minimumSequenceNumber <
2613
+ messageWithContext.message.minimumSequenceNumber
2614
+ ) {
2615
+ messageWithContext.message.minimumSequenceNumber =
2616
+ this.deltaManager.minimumSequenceNumber;
2617
+ }
2618
+
2568
2619
  // Surround the actual processing of the operation with messages to the schedule manager indicating
2569
2620
  // the beginning and end. This allows it to emit appropriate events and/or pause the processing of new
2570
2621
  // messages once a batch has been fully processed.
@@ -2642,13 +2693,7 @@ export class ContainerRuntime
2642
2693
  // stashed ops flow. The compressor is stashed with these ops already processed.
2643
2694
  // That said, in idCompressorMode === "delayed", we might not serialize ID compressor, and
2644
2695
  // thus we need to process all the ops.
2645
- if (
2646
- !(
2647
- this.skipSavedCompressorOps &&
2648
- (messageWithContext.message.metadata as IIdAllocationMetadata)?.savedOp ===
2649
- true
2650
- )
2651
- ) {
2696
+ if (!(this.skipSavedCompressorOps && messageWithContext.savedOp === true)) {
2652
2697
  const range = messageWithContext.message.contents;
2653
2698
  // Some other client turned on the id compressor. If we have not turned it on,
2654
2699
  // put it in a pending queue and delay finalization.
@@ -2659,6 +2704,10 @@ export class ContainerRuntime
2659
2704
  );
2660
2705
  this.pendingIdCompressorOps.push(range);
2661
2706
  } else {
2707
+ assert(
2708
+ this.pendingIdCompressorOps.length === 0,
2709
+ 0x979 /* there should be no pending ops! */,
2710
+ );
2662
2711
  this._idCompressor.finalizeCreationRange(range);
2663
2712
  }
2664
2713
  }
@@ -2809,9 +2858,9 @@ export class ContainerRuntime
2809
2858
  let checkpoint: IBatchCheckpoint | undefined;
2810
2859
  let result: T;
2811
2860
  if (this.mc.config.getBoolean("Fluid.ContainerRuntime.EnableRollback")) {
2812
- // Note: we are not touching this.pendingAttachBatch here, for two reasons:
2813
- // 1. It would not help, as we flush attach ops as they become available.
2814
- // 2. There is no way to undo process of data store creation.
2861
+ // Note: we are not touching any batches other than mainBatch here, for two reasons:
2862
+ // 1. It would not help, as other batches are flushed independently from main batch.
2863
+ // 2. There is no way to undo process of data store creation, blob creation, ID compressor ops, or other things tracked by other batches.
2815
2864
  checkpoint = this.outbox.checkpoint().mainBatch;
2816
2865
  }
2817
2866
  try {
@@ -3393,10 +3442,14 @@ export class ContainerRuntime
3393
3442
  // The summary number for this summary. This will be updated during the summary process, so get it now and
3394
3443
  // use it for all events logged during this summary.
3395
3444
  const summaryNumber = this.nextSummaryNumber;
3445
+ let summaryRefSeqNum: number | undefined;
3396
3446
  const summaryNumberLogger = createChildLogger({
3397
3447
  logger: summaryLogger,
3398
3448
  properties: {
3399
- all: { summaryNumber },
3449
+ all: {
3450
+ summaryNumber,
3451
+ referenceSequenceNumber: () => summaryRefSeqNum,
3452
+ },
3400
3453
  },
3401
3454
  });
3402
3455
 
@@ -3415,7 +3468,7 @@ export class ContainerRuntime
3415
3468
  // If the container is dirty, i.e., there are pending unacked ops, the summary will not be eventual consistent
3416
3469
  // and it may even be incorrect. So, wait for the container to be saved with a timeout. If the container is not
3417
3470
  // saved within the timeout, check if it should be failed or can continue.
3418
- if (this.validateSummaryBeforeUpload && this.isDirty) {
3471
+ if (this.isDirty) {
3419
3472
  const countBefore = this.pendingMessagesCount;
3420
3473
  // The timeout for waiting for pending ops can be overridden via configurations.
3421
3474
  const pendingOpsTimeout =
@@ -3465,8 +3518,6 @@ export class ContainerRuntime
3465
3518
  "Fluid.ContainerRuntime.SubmitSummary.shouldValidatePreSummaryState",
3466
3519
  ) === true;
3467
3520
 
3468
- let summaryRefSeqNum: number | undefined;
3469
-
3470
3521
  try {
3471
3522
  await this.deltaManager.inbound.pause();
3472
3523
  if (shouldPauseInboundSignal) {
@@ -3509,7 +3560,9 @@ export class ContainerRuntime
3509
3560
  stage: "base",
3510
3561
  referenceSequenceNumber: summaryRefSeqNum,
3511
3562
  minimumSequenceNumber,
3512
- error: `Summarizer node state inconsistent with summarizer state.`,
3563
+ error: new LoggingError(
3564
+ `Summarizer node state inconsistent with summarizer state.`,
3565
+ ),
3513
3566
  };
3514
3567
  }
3515
3568
  }
@@ -3559,7 +3612,7 @@ export class ContainerRuntime
3559
3612
  stage: "base",
3560
3613
  referenceSequenceNumber: summaryRefSeqNum,
3561
3614
  minimumSequenceNumber,
3562
- error: continueResult.error,
3615
+ error: new LoggingError(continueResult.error),
3563
3616
  };
3564
3617
  }
3565
3618
 
@@ -3580,39 +3633,38 @@ export class ContainerRuntime
3580
3633
  stage: "base",
3581
3634
  referenceSequenceNumber: summaryRefSeqNum,
3582
3635
  minimumSequenceNumber,
3583
- error,
3636
+ error: wrapError(error, (msg) => new LoggingError(msg)),
3584
3637
  };
3585
3638
  }
3586
3639
 
3587
- // If validateSummaryBeforeUpload is true, validate that the summary generated is correct before uploading.
3588
- if (this.validateSummaryBeforeUpload) {
3589
- // Validate that the summaries generated by summarize nodes is correct.
3590
- const validateResult = this.summarizerNode.validateSummary();
3591
- if (!validateResult.success) {
3592
- const { success, ...loggingProps } = validateResult;
3593
- const error = new RetriableSummaryError(
3594
- validateResult.reason,
3595
- validateResult.retryAfterSeconds,
3596
- { ...loggingProps },
3597
- );
3598
- return {
3599
- stage: "base",
3600
- referenceSequenceNumber: summaryRefSeqNum,
3601
- minimumSequenceNumber,
3602
- error,
3603
- };
3604
- }
3605
-
3606
- const pendingMessagesFailResult = await this.shouldFailSummaryOnPendingOps(
3607
- summaryNumberLogger,
3608
- summaryRefSeqNum,
3609
- minimumSequenceNumber,
3610
- finalAttempt,
3611
- false /* beforeSummaryGeneration */,
3640
+ // Validate that the summary generated by summarizer nodes is correct before uploading.
3641
+ const validateResult = this.summarizerNode.validateSummary();
3642
+ if (!validateResult.success) {
3643
+ const { success, ...loggingProps } = validateResult;
3644
+ const error = new RetriableSummaryError(
3645
+ validateResult.reason,
3646
+ validateResult.retryAfterSeconds,
3647
+ { ...loggingProps },
3612
3648
  );
3613
- if (pendingMessagesFailResult !== undefined) {
3614
- return pendingMessagesFailResult;
3615
- }
3649
+ return {
3650
+ stage: "base",
3651
+ referenceSequenceNumber: summaryRefSeqNum,
3652
+ minimumSequenceNumber,
3653
+ error,
3654
+ };
3655
+ }
3656
+
3657
+ // If there are pending unacked ops, this summary attempt may fail as the uploaded
3658
+ // summary would be eventually inconsistent.
3659
+ const pendingMessagesFailResult = await this.shouldFailSummaryOnPendingOps(
3660
+ summaryNumberLogger,
3661
+ summaryRefSeqNum,
3662
+ minimumSequenceNumber,
3663
+ finalAttempt,
3664
+ false /* beforeSummaryGeneration */,
3665
+ );
3666
+ if (pendingMessagesFailResult !== undefined) {
3667
+ return pendingMessagesFailResult;
3616
3668
  }
3617
3669
 
3618
3670
  const { summary: summaryTree, stats: partialStats } = summarizeResult;
@@ -3653,7 +3705,11 @@ export class ContainerRuntime
3653
3705
 
3654
3706
  continueResult = checkContinue();
3655
3707
  if (!continueResult.continue) {
3656
- return { stage: "generate", ...generateSummaryData, error: continueResult.error };
3708
+ return {
3709
+ stage: "generate",
3710
+ ...generateSummaryData,
3711
+ error: new LoggingError(continueResult.error),
3712
+ };
3657
3713
  }
3658
3714
 
3659
3715
  const summaryContext =
@@ -3676,7 +3732,11 @@ export class ContainerRuntime
3676
3732
  summaryContext,
3677
3733
  );
3678
3734
  } catch (error) {
3679
- return { stage: "generate", ...generateSummaryData, error };
3735
+ return {
3736
+ stage: "generate",
3737
+ ...generateSummaryData,
3738
+ error: wrapError(error, (msg) => new LoggingError(msg)),
3739
+ };
3680
3740
  }
3681
3741
 
3682
3742
  const parent = summaryContext.ackHandle;
@@ -3695,14 +3755,22 @@ export class ContainerRuntime
3695
3755
 
3696
3756
  continueResult = checkContinue();
3697
3757
  if (!continueResult.continue) {
3698
- return { stage: "upload", ...uploadData, error: continueResult.error };
3758
+ return {
3759
+ stage: "upload",
3760
+ ...uploadData,
3761
+ error: new LoggingError(continueResult.error),
3762
+ };
3699
3763
  }
3700
3764
 
3701
3765
  let clientSequenceNumber: number;
3702
3766
  try {
3703
3767
  clientSequenceNumber = this.submitSummaryMessage(summaryMessage, summaryRefSeqNum);
3704
3768
  } catch (error) {
3705
- return { stage: "upload", ...uploadData, error };
3769
+ return {
3770
+ stage: "upload",
3771
+ ...uploadData,
3772
+ error: wrapError(error, (msg) => new LoggingError(msg)),
3773
+ };
3706
3774
  }
3707
3775
 
3708
3776
  const submitData = {
@@ -3713,13 +3781,13 @@ export class ContainerRuntime
3713
3781
  } as const;
3714
3782
 
3715
3783
  try {
3716
- // If validateSummaryBeforeUpload is false, the summary should be validated in this step.
3717
- this.summarizerNode.completeSummary(
3718
- handle,
3719
- !this.validateSummaryBeforeUpload /* validate */,
3720
- );
3784
+ this.summarizerNode.completeSummary(handle);
3721
3785
  } catch (error) {
3722
- return { stage: "upload", ...uploadData, error };
3786
+ return {
3787
+ stage: "upload",
3788
+ ...uploadData,
3789
+ error: wrapError(error, (msg) => new LoggingError(msg)),
3790
+ };
3723
3791
  }
3724
3792
  return submitData;
3725
3793
  } finally {
@@ -3846,14 +3914,16 @@ export class ContainerRuntime
3846
3914
  public async uploadBlob(
3847
3915
  blob: ArrayBufferLike,
3848
3916
  signal?: AbortSignal,
3849
- ): Promise<IFluidHandle<ArrayBufferLike>> {
3917
+ ): Promise<IFluidHandleInternal<ArrayBufferLike>> {
3850
3918
  this.verifyNotClosed();
3851
3919
  return this.blobManager.createBlob(blob, signal);
3852
3920
  }
3853
3921
 
3854
- private submitIdAllocationOpIfNeeded(): void {
3922
+ private submitIdAllocationOpIfNeeded(resubmitOutstandingRanges = false): void {
3855
3923
  if (this._idCompressor) {
3856
- const idRange = this._idCompressor.takeNextCreationRange();
3924
+ const idRange = resubmitOutstandingRanges
3925
+ ? this.idCompressor?.takeUnfinalizedCreationRange()
3926
+ : this._idCompressor.takeNextCreationRange();
3857
3927
  // Don't include the idRange if there weren't any Ids allocated
3858
3928
  if (idRange?.ids !== undefined) {
3859
3929
  const idAllocationMessage: ContainerRuntimeIdAllocationMessage = {
@@ -3875,7 +3945,6 @@ export class ContainerRuntime
3875
3945
  metadata?: { localId: string; blobId?: string },
3876
3946
  ): void {
3877
3947
  this.verifyNotClosed();
3878
- this.verifyCanSubmitOps();
3879
3948
 
3880
3949
  // There should be no ops in detached container state!
3881
3950
  assert(
@@ -3923,6 +3992,14 @@ export class ContainerRuntime
3923
3992
  // on this callback to do actual sending.
3924
3993
  const contents = this.documentsSchemaController.maybeSendSchemaMessage();
3925
3994
  if (contents) {
3995
+ this.logger.sendTelemetryEvent({
3996
+ eventName: "SchemaChangeProposal",
3997
+ refSeq: contents.refSeq,
3998
+ version: contents.version,
3999
+ newRuntimeSchema: JSON.stringify(contents.runtime),
4000
+ sessionRuntimeSchema: JSON.stringify(this.sessionSchema),
4001
+ oldRuntimeSchema: JSON.stringify(this.metadata?.documentSchema?.runtime),
4002
+ });
3926
4003
  const msg: ContainerRuntimeDocumentSchemaMessage = {
3927
4004
  type: ContainerMessageType.DocumentSchemaChange,
3928
4005
  contents,
@@ -3933,33 +4010,7 @@ export class ContainerRuntime
3933
4010
  });
3934
4011
  }
3935
4012
 
3936
- // If this is attach message for new data store, and we are in a batch, send this op out of order
3937
- // Is it safe:
3938
- // Yes, this should be safe reordering. Newly created data stores are not visible through API surface.
3939
- // They become visible only when aliased, or handle to some sub-element of newly created datastore
3940
- // is stored in some DDS, i.e. only after some other op.
3941
- // Why:
3942
- // Attach ops are large, and expensive to process. Plus there are scenarios where a lot of new data
3943
- // stores are created, causing issues like relay service throttling (too many ops) and catastrophic
3944
- // failure (batch is too large). Pushing them earlier and outside of main batch should alleviate
3945
- // these issues.
3946
- // Cons:
3947
- // 1. With large batches, relay service may throttle clients. Clients may disconnect while throttled.
3948
- // This change creates new possibility of a lot of newly created data stores never being referenced
3949
- // because client died before it had a change to submit the rest of the ops. This will create more
3950
- // garbage that needs to be collected leveraging GC (Garbage Collection) feature.
3951
- // 2. Sending ops out of order means they are excluded from rollback functionality. This is not an issue
3952
- // today as rollback can't undo creation of data store. To some extent not sending them is a bigger
3953
- // issue than sending.
3954
- // Please note that this does not change file format, so it can be disabled in the future if this
3955
- // optimization no longer makes sense (for example, batch compression may make it less appealing).
3956
- if (
3957
- this.currentlyBatching() &&
3958
- type === ContainerMessageType.Attach &&
3959
- this.disableAttachReorder !== true
3960
- ) {
3961
- this.outbox.submitAttach(message);
3962
- } else if (type === ContainerMessageType.BlobAttach) {
4013
+ if (type === ContainerMessageType.BlobAttach) {
3963
4014
  // BlobAttach ops must have their metadata visible and cannot be grouped (see opGroupingManager.ts)
3964
4015
  this.outbox.submitBlobAttach(message);
3965
4016
  } else {
@@ -4048,37 +4099,6 @@ export class ContainerRuntime
4048
4099
  }
4049
4100
  }
4050
4101
 
4051
- private verifyCanSubmitOps() {
4052
- if (this.ensureNoDataModelChangesCalls > 0) {
4053
- const errorMessage =
4054
- "Op was submitted from within a `ensureNoDataModelChanges` callback";
4055
- if (this.opReentryCallsToReport > 0) {
4056
- this.mc.logger.sendTelemetryEvent(
4057
- { eventName: "OpReentry" },
4058
- // We need to capture the call stack in order to inspect the source of this usage pattern
4059
- getLongStack(() => new UsageError(errorMessage)),
4060
- );
4061
- this.opReentryCallsToReport--;
4062
- }
4063
-
4064
- // Creating ops while processing ops can lead
4065
- // to undefined behavior and events observed in the wrong order.
4066
- // For example, we have two callbacks registered for a DDS, A and B.
4067
- // Then if on change #1 callback A creates change #2, the invocation flow will be:
4068
- //
4069
- // A because of #1
4070
- // A because of #2
4071
- // B because of #2
4072
- // B because of #1
4073
- //
4074
- // The runtime must enforce op coherence by not allowing ops to be submitted
4075
- // while ops are being processed.
4076
- if (this.enableOpReentryCheck) {
4077
- throw new UsageError(errorMessage);
4078
- }
4079
- }
4080
- }
4081
-
4082
4102
  private reSubmitBatch(batch: IPendingBatchMessage[]) {
4083
4103
  this.orderSequentially(() => {
4084
4104
  for (const message of batch) {
@@ -4119,11 +4139,15 @@ export class ContainerRuntime
4119
4139
  this.channelCollection.reSubmit(message.type, message.contents, localOpMetadata);
4120
4140
  break;
4121
4141
  case ContainerMessageType.IdAllocation: {
4122
- this.submit(message, localOpMetadata);
4142
+ // Allocation ops are never resubmitted/rebased. This is because they require special handling to
4143
+ // avoid being submitted out of order. For example, if the pending state manager contained
4144
+ // [idOp1, dataOp1, idOp2, dataOp2] and the resubmission of dataOp1 generated idOp3, that would be
4145
+ // placed into the outbox in the same batch as idOp1, but before idOp2 is resubmitted.
4146
+ // To avoid this, allocation ops are simply never resubmitted. Prior to invoking the pending state
4147
+ // manager to replay pending ops, the runtime will always submit a new allocation range that includes
4148
+ // all pending IDs. The resubmitted allocation ops are then ignored here.
4123
4149
  break;
4124
4150
  }
4125
- case ContainerMessageType.ChunkedOp:
4126
- throw new Error(`chunkedOp not expected here`);
4127
4151
  case ContainerMessageType.BlobAttach:
4128
4152
  this.blobManager.reSubmit(opMetadata);
4129
4153
  break;
@@ -4150,7 +4174,7 @@ export class ContainerRuntime
4150
4174
  });
4151
4175
  } else {
4152
4176
  const error = DataProcessingError.create(
4153
- "Resubmitting runtime message of unknown type",
4177
+ "Resubmitting runtime message of unexpected type",
4154
4178
  "reSubmitCore",
4155
4179
  undefined /* sequencedMessage */,
4156
4180
  {
@@ -4243,7 +4267,7 @@ export class ContainerRuntime
4243
4267
 
4244
4268
  return {
4245
4269
  stage: "base",
4246
- error: "summary state stale - Unsupported option 'refreshLatestAck'",
4270
+ error: new LoggingError("summary state stale - Unsupported option 'refreshLatestAck'"),
4247
4271
  referenceSequenceNumber: this.deltaManager.lastSequenceNumber,
4248
4272
  minimumSequenceNumber: this.deltaManager.minimumSequenceNumber,
4249
4273
  };
@@ -4325,10 +4349,9 @@ export class ContainerRuntime
4325
4349
  const getSyncState = (
4326
4350
  pendingAttachmentBlobs?: IPendingBlobs,
4327
4351
  ): IPendingRuntimeState | undefined => {
4328
- const pending = this.pendingStateManager.getLocalState();
4329
- if (pendingAttachmentBlobs === undefined && !this.hasPendingMessages()) {
4330
- return; // no pending state to save
4331
- }
4352
+ const pending = this.pendingStateManager.getLocalState(props?.snapshotSequenceNumber);
4353
+ const sessionExpiryTimerStarted =
4354
+ props?.sessionExpiryTimerStarted ?? this.garbageCollector.sessionExpiryTimerStarted;
4332
4355
 
4333
4356
  const pendingIdCompressorState = this._idCompressor?.serialize(true);
4334
4357
 
@@ -4336,7 +4359,7 @@ export class ContainerRuntime
4336
4359
  pending,
4337
4360
  pendingIdCompressorState,
4338
4361
  pendingAttachmentBlobs,
4339
- sessionExpiryTimerStarted: this.garbageCollector.sessionExpiryTimerStarted,
4362
+ sessionExpiryTimerStarted,
4340
4363
  };
4341
4364
  };
4342
4365
  const perfEvent = {
@@ -4427,6 +4450,6 @@ export class ContainerRuntime
4427
4450
  }
4428
4451
 
4429
4452
  private get groupedBatchingEnabled(): boolean {
4430
- return this.documentSchema.opGroupingEnabled === true;
4453
+ return this.sessionSchema.opGroupingEnabled === true;
4431
4454
  }
4432
4455
  }