@fluidframework/container-runtime 2.0.0-dev-rc.3.0.0.254866 → 2.0.0-dev-rc.5.0.0.263932

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 (338) hide show
  1. package/CHANGELOG.md +69 -0
  2. package/api-report/container-runtime.api.md +93 -39
  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 +10 -6
  11. package/dist/channelCollection.d.ts.map +1 -1
  12. package/dist/channelCollection.js +85 -22
  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 +22 -35
  19. package/dist/containerRuntime.d.ts.map +1 -1
  20. package/dist/containerRuntime.js +232 -174
  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 +2 -0
  30. package/dist/dataStoreContexts.d.ts.map +1 -1
  31. package/dist/dataStoreContexts.js +7 -0
  32. package/dist/dataStoreContexts.js.map +1 -1
  33. package/dist/deltaManagerProxies.d.ts +81 -0
  34. package/dist/deltaManagerProxies.d.ts.map +1 -0
  35. package/dist/{deltaManagerSummarizerProxy.js → deltaManagerProxies.js} +75 -20
  36. package/dist/deltaManagerProxies.js.map +1 -0
  37. package/dist/deltaScheduler.d.ts +2 -2
  38. package/dist/deltaScheduler.d.ts.map +1 -1
  39. package/dist/deltaScheduler.js.map +1 -1
  40. package/dist/gc/garbageCollection.d.ts +5 -12
  41. package/dist/gc/garbageCollection.d.ts.map +1 -1
  42. package/dist/gc/garbageCollection.js +45 -29
  43. package/dist/gc/garbageCollection.js.map +1 -1
  44. package/dist/gc/gcDefinitions.d.ts +27 -6
  45. package/dist/gc/gcDefinitions.d.ts.map +1 -1
  46. package/dist/gc/gcDefinitions.js.map +1 -1
  47. package/dist/gc/gcHelpers.d.ts +5 -4
  48. package/dist/gc/gcHelpers.d.ts.map +1 -1
  49. package/dist/gc/gcHelpers.js +14 -2
  50. package/dist/gc/gcHelpers.js.map +1 -1
  51. package/dist/gc/gcTelemetry.d.ts +14 -4
  52. package/dist/gc/gcTelemetry.d.ts.map +1 -1
  53. package/dist/gc/gcTelemetry.js +24 -21
  54. package/dist/gc/gcTelemetry.js.map +1 -1
  55. package/dist/gc/index.d.ts +2 -2
  56. package/dist/gc/index.d.ts.map +1 -1
  57. package/dist/gc/index.js +2 -2
  58. package/dist/gc/index.js.map +1 -1
  59. package/dist/index.d.ts +3 -3
  60. package/dist/index.d.ts.map +1 -1
  61. package/dist/index.js +2 -1
  62. package/dist/index.js.map +1 -1
  63. package/dist/{alpha.d.ts → legacy.d.ts} +8 -1
  64. package/dist/messageTypes.d.ts +5 -2
  65. package/dist/messageTypes.d.ts.map +1 -1
  66. package/dist/messageTypes.js.map +1 -1
  67. package/dist/metadata.d.ts +2 -2
  68. package/dist/metadata.d.ts.map +1 -1
  69. package/dist/metadata.js.map +1 -1
  70. package/dist/opLifecycle/batchManager.d.ts +4 -1
  71. package/dist/opLifecycle/batchManager.d.ts.map +1 -1
  72. package/dist/opLifecycle/batchManager.js +0 -10
  73. package/dist/opLifecycle/batchManager.js.map +1 -1
  74. package/dist/opLifecycle/opDecompressor.d.ts.map +1 -1
  75. package/dist/opLifecycle/opDecompressor.js +6 -6
  76. package/dist/opLifecycle/opDecompressor.js.map +1 -1
  77. package/dist/opLifecycle/opGroupingManager.js +2 -2
  78. package/dist/opLifecycle/opGroupingManager.js.map +1 -1
  79. package/dist/opLifecycle/opSplitter.js +1 -1
  80. package/dist/opLifecycle/opSplitter.js.map +1 -1
  81. package/dist/opLifecycle/outbox.d.ts +0 -4
  82. package/dist/opLifecycle/outbox.d.ts.map +1 -1
  83. package/dist/opLifecycle/outbox.js +7 -38
  84. package/dist/opLifecycle/outbox.js.map +1 -1
  85. package/dist/packageVersion.d.ts +1 -1
  86. package/dist/packageVersion.js +1 -1
  87. package/dist/packageVersion.js.map +1 -1
  88. package/dist/pendingStateManager.d.ts +9 -2
  89. package/dist/pendingStateManager.d.ts.map +1 -1
  90. package/dist/pendingStateManager.js +26 -10
  91. package/dist/pendingStateManager.js.map +1 -1
  92. package/dist/public.d.ts +3 -0
  93. package/dist/scheduleManager.d.ts +2 -2
  94. package/dist/scheduleManager.d.ts.map +1 -1
  95. package/dist/scheduleManager.js.map +1 -1
  96. package/dist/summary/documentSchema.d.ts +3 -1
  97. package/dist/summary/documentSchema.d.ts.map +1 -1
  98. package/dist/summary/documentSchema.js +34 -16
  99. package/dist/summary/documentSchema.js.map +1 -1
  100. package/dist/summary/index.d.ts +1 -1
  101. package/dist/summary/index.d.ts.map +1 -1
  102. package/dist/summary/index.js.map +1 -1
  103. package/dist/summary/orderedClientElection.d.ts +2 -2
  104. package/dist/summary/orderedClientElection.d.ts.map +1 -1
  105. package/dist/summary/orderedClientElection.js.map +1 -1
  106. package/dist/summary/runningSummarizer.js +10 -10
  107. package/dist/summary/runningSummarizer.js.map +1 -1
  108. package/dist/summary/summarizer.d.ts +1 -2
  109. package/dist/summary/summarizer.d.ts.map +1 -1
  110. package/dist/summary/summarizer.js.map +1 -1
  111. package/dist/summary/summarizerClientElection.d.ts +1 -1
  112. package/dist/summary/summarizerClientElection.d.ts.map +1 -1
  113. package/dist/summary/summarizerClientElection.js.map +1 -1
  114. package/dist/summary/summarizerHeuristics.d.ts +1 -1
  115. package/dist/summary/summarizerHeuristics.d.ts.map +1 -1
  116. package/dist/summary/summarizerHeuristics.js.map +1 -1
  117. package/dist/summary/summarizerNode/summarizerNode.d.ts +4 -3
  118. package/dist/summary/summarizerNode/summarizerNode.d.ts.map +1 -1
  119. package/dist/summary/summarizerNode/summarizerNode.js +4 -10
  120. package/dist/summary/summarizerNode/summarizerNode.js.map +1 -1
  121. package/dist/summary/summarizerNode/summarizerNodeUtils.d.ts +2 -3
  122. package/dist/summary/summarizerNode/summarizerNodeUtils.d.ts.map +1 -1
  123. package/dist/summary/summarizerNode/summarizerNodeUtils.js.map +1 -1
  124. package/dist/summary/summarizerNode/summarizerNodeWithGc.d.ts +1 -2
  125. package/dist/summary/summarizerNode/summarizerNodeWithGc.d.ts.map +1 -1
  126. package/dist/summary/summarizerNode/summarizerNodeWithGc.js +2 -9
  127. package/dist/summary/summarizerNode/summarizerNodeWithGc.js.map +1 -1
  128. package/dist/summary/summarizerTypes.d.ts +3 -5
  129. package/dist/summary/summarizerTypes.d.ts.map +1 -1
  130. package/dist/summary/summarizerTypes.js.map +1 -1
  131. package/dist/summary/summaryCollection.d.ts +2 -2
  132. package/dist/summary/summaryCollection.d.ts.map +1 -1
  133. package/dist/summary/summaryCollection.js.map +1 -1
  134. package/dist/summary/summaryFormat.d.ts +25 -5
  135. package/dist/summary/summaryFormat.d.ts.map +1 -1
  136. package/dist/summary/summaryFormat.js.map +1 -1
  137. package/dist/summary/summaryGenerator.d.ts +1 -2
  138. package/dist/summary/summaryGenerator.d.ts.map +1 -1
  139. package/dist/summary/summaryGenerator.js +12 -11
  140. package/dist/summary/summaryGenerator.js.map +1 -1
  141. package/dist/summary/summaryManager.d.ts.map +1 -1
  142. package/dist/summary/summaryManager.js +5 -5
  143. package/dist/summary/summaryManager.js.map +1 -1
  144. package/{lib/beta.d.ts → internal.d.ts} +2 -0
  145. package/{dist/beta.d.ts → legacy.d.ts} +2 -0
  146. package/lib/batchTracker.d.ts +1 -1
  147. package/lib/batchTracker.d.ts.map +1 -1
  148. package/lib/batchTracker.js.map +1 -1
  149. package/lib/blobManager.d.ts +7 -7
  150. package/lib/blobManager.d.ts.map +1 -1
  151. package/lib/blobManager.js +3 -5
  152. package/lib/blobManager.js.map +1 -1
  153. package/lib/channelCollection.d.ts +10 -6
  154. package/lib/channelCollection.d.ts.map +1 -1
  155. package/lib/channelCollection.js +88 -25
  156. package/lib/channelCollection.js.map +1 -1
  157. package/lib/connectionTelemetry.d.ts +2 -2
  158. package/lib/connectionTelemetry.d.ts.map +1 -1
  159. package/lib/connectionTelemetry.js +49 -0
  160. package/lib/connectionTelemetry.js.map +1 -1
  161. package/lib/containerRuntime.d.ts +22 -35
  162. package/lib/containerRuntime.d.ts.map +1 -1
  163. package/lib/containerRuntime.js +232 -174
  164. package/lib/containerRuntime.js.map +1 -1
  165. package/lib/dataStore.d.ts +1 -1
  166. package/lib/dataStore.d.ts.map +1 -1
  167. package/lib/dataStore.js +1 -1
  168. package/lib/dataStore.js.map +1 -1
  169. package/lib/dataStoreContext.d.ts +9 -6
  170. package/lib/dataStoreContext.d.ts.map +1 -1
  171. package/lib/dataStoreContext.js +21 -7
  172. package/lib/dataStoreContext.js.map +1 -1
  173. package/lib/dataStoreContexts.d.ts +2 -0
  174. package/lib/dataStoreContexts.d.ts.map +1 -1
  175. package/lib/dataStoreContexts.js +7 -0
  176. package/lib/dataStoreContexts.js.map +1 -1
  177. package/lib/deltaManagerProxies.d.ts +81 -0
  178. package/lib/deltaManagerProxies.d.ts.map +1 -0
  179. package/lib/{deltaManagerSummarizerProxy.js → deltaManagerProxies.js} +72 -19
  180. package/lib/deltaManagerProxies.js.map +1 -0
  181. package/lib/deltaScheduler.d.ts +2 -2
  182. package/lib/deltaScheduler.d.ts.map +1 -1
  183. package/lib/deltaScheduler.js.map +1 -1
  184. package/lib/gc/garbageCollection.d.ts +5 -12
  185. package/lib/gc/garbageCollection.d.ts.map +1 -1
  186. package/lib/gc/garbageCollection.js +47 -31
  187. package/lib/gc/garbageCollection.js.map +1 -1
  188. package/lib/gc/gcDefinitions.d.ts +27 -6
  189. package/lib/gc/gcDefinitions.d.ts.map +1 -1
  190. package/lib/gc/gcDefinitions.js.map +1 -1
  191. package/lib/gc/gcHelpers.d.ts +5 -4
  192. package/lib/gc/gcHelpers.d.ts.map +1 -1
  193. package/lib/gc/gcHelpers.js +12 -1
  194. package/lib/gc/gcHelpers.js.map +1 -1
  195. package/lib/gc/gcTelemetry.d.ts +14 -4
  196. package/lib/gc/gcTelemetry.d.ts.map +1 -1
  197. package/lib/gc/gcTelemetry.js +24 -21
  198. package/lib/gc/gcTelemetry.js.map +1 -1
  199. package/lib/gc/index.d.ts +2 -2
  200. package/lib/gc/index.d.ts.map +1 -1
  201. package/lib/gc/index.js +1 -1
  202. package/lib/gc/index.js.map +1 -1
  203. package/lib/index.d.ts +3 -3
  204. package/lib/index.d.ts.map +1 -1
  205. package/lib/index.js +1 -1
  206. package/lib/index.js.map +1 -1
  207. package/lib/{alpha.d.ts → legacy.d.ts} +8 -1
  208. package/lib/messageTypes.d.ts +5 -2
  209. package/lib/messageTypes.d.ts.map +1 -1
  210. package/lib/messageTypes.js.map +1 -1
  211. package/lib/metadata.d.ts +2 -2
  212. package/lib/metadata.d.ts.map +1 -1
  213. package/lib/metadata.js.map +1 -1
  214. package/lib/opLifecycle/batchManager.d.ts +4 -1
  215. package/lib/opLifecycle/batchManager.d.ts.map +1 -1
  216. package/lib/opLifecycle/batchManager.js +0 -10
  217. package/lib/opLifecycle/batchManager.js.map +1 -1
  218. package/lib/opLifecycle/opDecompressor.d.ts.map +1 -1
  219. package/lib/opLifecycle/opDecompressor.js +6 -6
  220. package/lib/opLifecycle/opDecompressor.js.map +1 -1
  221. package/lib/opLifecycle/opGroupingManager.js +2 -2
  222. package/lib/opLifecycle/opGroupingManager.js.map +1 -1
  223. package/lib/opLifecycle/opSplitter.js +1 -1
  224. package/lib/opLifecycle/opSplitter.js.map +1 -1
  225. package/lib/opLifecycle/outbox.d.ts +0 -4
  226. package/lib/opLifecycle/outbox.d.ts.map +1 -1
  227. package/lib/opLifecycle/outbox.js +7 -38
  228. package/lib/opLifecycle/outbox.js.map +1 -1
  229. package/lib/packageVersion.d.ts +1 -1
  230. package/lib/packageVersion.js +1 -1
  231. package/lib/packageVersion.js.map +1 -1
  232. package/lib/pendingStateManager.d.ts +9 -2
  233. package/lib/pendingStateManager.d.ts.map +1 -1
  234. package/lib/pendingStateManager.js +27 -11
  235. package/lib/pendingStateManager.js.map +1 -1
  236. package/lib/public.d.ts +3 -0
  237. package/lib/scheduleManager.d.ts +2 -2
  238. package/lib/scheduleManager.d.ts.map +1 -1
  239. package/lib/scheduleManager.js.map +1 -1
  240. package/lib/summary/documentSchema.d.ts +3 -1
  241. package/lib/summary/documentSchema.d.ts.map +1 -1
  242. package/lib/summary/documentSchema.js +34 -16
  243. package/lib/summary/documentSchema.js.map +1 -1
  244. package/lib/summary/index.d.ts +1 -1
  245. package/lib/summary/index.d.ts.map +1 -1
  246. package/lib/summary/index.js.map +1 -1
  247. package/lib/summary/orderedClientElection.d.ts +2 -2
  248. package/lib/summary/orderedClientElection.d.ts.map +1 -1
  249. package/lib/summary/orderedClientElection.js +1 -1
  250. package/lib/summary/orderedClientElection.js.map +1 -1
  251. package/lib/summary/runningSummarizer.js +1 -1
  252. package/lib/summary/runningSummarizer.js.map +1 -1
  253. package/lib/summary/summarizer.d.ts +1 -2
  254. package/lib/summary/summarizer.d.ts.map +1 -1
  255. package/lib/summary/summarizer.js.map +1 -1
  256. package/lib/summary/summarizerClientElection.d.ts +1 -1
  257. package/lib/summary/summarizerClientElection.d.ts.map +1 -1
  258. package/lib/summary/summarizerClientElection.js.map +1 -1
  259. package/lib/summary/summarizerHeuristics.d.ts +1 -1
  260. package/lib/summary/summarizerHeuristics.d.ts.map +1 -1
  261. package/lib/summary/summarizerHeuristics.js.map +1 -1
  262. package/lib/summary/summarizerNode/summarizerNode.d.ts +4 -3
  263. package/lib/summary/summarizerNode/summarizerNode.d.ts.map +1 -1
  264. package/lib/summary/summarizerNode/summarizerNode.js +4 -10
  265. package/lib/summary/summarizerNode/summarizerNode.js.map +1 -1
  266. package/lib/summary/summarizerNode/summarizerNodeUtils.d.ts +2 -3
  267. package/lib/summary/summarizerNode/summarizerNodeUtils.d.ts.map +1 -1
  268. package/lib/summary/summarizerNode/summarizerNodeUtils.js.map +1 -1
  269. package/lib/summary/summarizerNode/summarizerNodeWithGc.d.ts +1 -2
  270. package/lib/summary/summarizerNode/summarizerNodeWithGc.d.ts.map +1 -1
  271. package/lib/summary/summarizerNode/summarizerNodeWithGc.js +2 -9
  272. package/lib/summary/summarizerNode/summarizerNodeWithGc.js.map +1 -1
  273. package/lib/summary/summarizerTypes.d.ts +3 -5
  274. package/lib/summary/summarizerTypes.d.ts.map +1 -1
  275. package/lib/summary/summarizerTypes.js.map +1 -1
  276. package/lib/summary/summaryCollection.d.ts +2 -2
  277. package/lib/summary/summaryCollection.d.ts.map +1 -1
  278. package/lib/summary/summaryCollection.js.map +1 -1
  279. package/lib/summary/summaryFormat.d.ts +25 -5
  280. package/lib/summary/summaryFormat.d.ts.map +1 -1
  281. package/lib/summary/summaryFormat.js.map +1 -1
  282. package/lib/summary/summaryGenerator.d.ts +1 -2
  283. package/lib/summary/summaryGenerator.d.ts.map +1 -1
  284. package/lib/summary/summaryGenerator.js +5 -4
  285. package/lib/summary/summaryGenerator.js.map +1 -1
  286. package/lib/summary/summaryManager.d.ts.map +1 -1
  287. package/lib/summary/summaryManager.js +2 -2
  288. package/lib/summary/summaryManager.js.map +1 -1
  289. package/lib/tsdoc-metadata.json +1 -1
  290. package/package.json +37 -59
  291. package/src/batchTracker.ts +1 -2
  292. package/src/blobManager.ts +11 -10
  293. package/src/channelCollection.ts +115 -47
  294. package/src/connectionTelemetry.ts +59 -4
  295. package/src/containerRuntime.ts +302 -270
  296. package/src/dataStore.ts +7 -4
  297. package/src/dataStoreContext.ts +57 -16
  298. package/src/dataStoreContexts.ts +13 -2
  299. package/src/{deltaManagerSummarizerProxy.ts → deltaManagerProxies.ts} +98 -24
  300. package/src/deltaScheduler.ts +2 -3
  301. package/src/gc/garbageCollection.ts +64 -42
  302. package/src/gc/gcDefinitions.ts +22 -10
  303. package/src/gc/gcHelpers.ts +14 -1
  304. package/src/gc/gcTelemetry.ts +57 -50
  305. package/src/gc/index.ts +2 -1
  306. package/src/index.ts +7 -0
  307. package/src/messageTypes.ts +4 -2
  308. package/src/metadata.ts +2 -2
  309. package/src/opLifecycle/README.md +4 -4
  310. package/src/opLifecycle/batchManager.ts +5 -14
  311. package/src/opLifecycle/opDecompressor.ts +12 -6
  312. package/src/opLifecycle/opGroupingManager.ts +2 -2
  313. package/src/opLifecycle/opSplitter.ts +1 -1
  314. package/src/opLifecycle/outbox.ts +7 -53
  315. package/src/packageVersion.ts +1 -1
  316. package/src/pendingStateManager.ts +38 -15
  317. package/src/scheduleManager.ts +2 -2
  318. package/src/summary/documentSchema.ts +52 -18
  319. package/src/summary/index.ts +4 -0
  320. package/src/summary/orderedClientElection.ts +6 -3
  321. package/src/summary/runningSummarizer.ts +1 -1
  322. package/src/summary/summarizer.ts +1 -1
  323. package/src/summary/summarizerClientElection.ts +1 -1
  324. package/src/summary/summarizerHeuristics.ts +1 -1
  325. package/src/summary/summarizerNode/summarizerNode.ts +3 -12
  326. package/src/summary/summarizerNode/summarizerNodeUtils.ts +2 -3
  327. package/src/summary/summarizerNode/summarizerNodeWithGc.ts +1 -10
  328. package/src/summary/summarizerTypes.ts +6 -5
  329. package/src/summary/summaryCollection.ts +2 -2
  330. package/src/summary/summaryFormat.ts +30 -4
  331. package/src/summary/summaryGenerator.ts +20 -9
  332. package/src/summary/summaryManager.ts +6 -3
  333. package/dist/deltaManagerSummarizerProxy.d.ts +0 -44
  334. package/dist/deltaManagerSummarizerProxy.d.ts.map +0 -1
  335. package/dist/deltaManagerSummarizerProxy.js.map +0 -1
  336. package/lib/deltaManagerSummarizerProxy.d.ts +0 -44
  337. package/lib/deltaManagerSummarizerProxy.d.ts.map +0 -1
  338. 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
 
@@ -493,6 +480,11 @@ export interface IContainerRuntimeOptions {
493
480
  readonly explicitSchemaControl?: boolean;
494
481
  }
495
482
 
483
+ /**
484
+ * Error responses when requesting a deleted object will have this header set to true
485
+ * @alpha
486
+ */
487
+ export const DeletedResponseHeaderKey = "wasDeleted";
496
488
  /**
497
489
  * Tombstone error responses will have this header set to true
498
490
  * @alpha
@@ -666,11 +658,13 @@ type MessageWithContext =
666
658
  message: InboundSequencedContainerRuntimeMessage;
667
659
  modernRuntimeMessage: true;
668
660
  local: boolean;
661
+ savedOp?: boolean;
669
662
  }
670
663
  | {
671
664
  message: InboundSequencedContainerRuntimeMessageOrSystemMessage;
672
665
  modernRuntimeMessage: false;
673
666
  local: boolean;
667
+ savedOp?: boolean;
674
668
  };
675
669
 
676
670
  const summarizerRequestUrl = "_summarizer";
@@ -805,8 +799,7 @@ export class ContainerRuntime
805
799
  maxBatchSizeInBytes = defaultMaxBatchSizeInBytes,
806
800
  enableRuntimeIdCompressor,
807
801
  chunkSizeInBytes = defaultChunkSizeInBytes,
808
- enableOpReentryCheck = false,
809
- enableGroupedBatching = false,
802
+ enableGroupedBatching = true,
810
803
  explicitSchemaControl = false,
811
804
  } = runtimeOptions;
812
805
 
@@ -852,9 +845,9 @@ export class ContainerRuntime
852
845
 
853
846
  // Verify summary runtime sequence number matches protocol sequence number.
854
847
  const runtimeSequenceNumber = messageAtLastSummary?.sequenceNumber;
848
+ const protocolSequenceNumber = context.deltaManager.initialSequenceNumber;
855
849
  // When we load with pending state, we reuse an old snapshot so we don't expect these numbers to match
856
850
  if (!context.pendingLocalState && runtimeSequenceNumber !== undefined) {
857
- const protocolSequenceNumber = context.deltaManager.initialSequenceNumber;
858
851
  // Unless bypass is explicitly set, then take action when sequence numbers mismatch.
859
852
  if (
860
853
  loadSequenceNumberVerification !== "bypass" &&
@@ -914,7 +907,16 @@ export class ContainerRuntime
914
907
 
915
908
  // This is the only exception to the rule above - we have proper plumbing to load ID compressor on schema change
916
909
  // event. It is loaded async (relative to op processing), so this conversion is only safe for off -> delayed conversion!
917
- if (idCompressorMode === undefined && desiredIdCompressorMode === "delayed") {
910
+ // Clients do not expect ID compressor ops unless ID compressor is On for them, and that could be achieved only through
911
+ // explicit schema change, i.e. only if explicitSchemaControl is on.
912
+ // Note: it would be better if we throw on combination of options (explicitSchemaControl = off, desiredIdCompressorMode === "delayed")
913
+ // that is not supported. But our service tests are oblivious to these problems and throwing here will cause a ton of failures
914
+ // We ignored incompatible ID compressor changes from the start (they were sticky), so that's not a new problem being introduced...
915
+ if (
916
+ idCompressorMode === undefined &&
917
+ desiredIdCompressorMode === "delayed" &&
918
+ explicitSchemaControl
919
+ ) {
918
920
  idCompressorMode = desiredIdCompressorMode;
919
921
  }
920
922
  } else {
@@ -958,9 +960,6 @@ export class ContainerRuntime
958
960
  }
959
961
  };
960
962
 
961
- const disableGroupedBatching = mc.config.getBoolean(
962
- "Fluid.ContainerRuntime.DisableGroupedBatching",
963
- );
964
963
  const disableCompression = mc.config.getBoolean(
965
964
  "Fluid.ContainerRuntime.CompressionDisabled",
966
965
  );
@@ -969,16 +968,15 @@ export class ContainerRuntime
969
968
  compressionOptions.minimumBatchSizeInBytes !== Infinity &&
970
969
  compressionOptions.compressionAlgorithm === "lz4";
971
970
 
972
- const opGroupingEnabled = disableGroupedBatching !== true && enableGroupedBatching;
973
-
974
971
  const documentSchemaController = new DocumentsSchemaController(
975
972
  existing,
973
+ protocolSequenceNumber,
976
974
  metadata?.documentSchema,
977
975
  {
978
976
  explicitSchemaControl,
979
977
  compressionLz4,
980
978
  idCompressorMode,
981
- opGroupingEnabled,
979
+ opGroupingEnabled: enableGroupedBatching,
982
980
  disallowedVersions: [],
983
981
  },
984
982
  (schema) => {
@@ -987,7 +985,6 @@ export class ContainerRuntime
987
985
  );
988
986
 
989
987
  const featureGatesForTelemetry: Record<string, boolean | number | undefined> = {
990
- disableGroupedBatching,
991
988
  disableCompression,
992
989
  };
993
990
 
@@ -1008,7 +1005,6 @@ export class ContainerRuntime
1008
1005
  chunkSizeInBytes,
1009
1006
  // Requires<> drops undefined from IdCompressorType
1010
1007
  enableRuntimeIdCompressor: enableRuntimeIdCompressor as "on" | "delayed",
1011
- enableOpReentryCheck,
1012
1008
  enableGroupedBatching,
1013
1009
  explicitSchemaControl,
1014
1010
  },
@@ -1101,7 +1097,17 @@ export class ContainerRuntime
1101
1097
  return this._getAttachState();
1102
1098
  }
1103
1099
 
1104
- 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() {
1105
1111
  return this.documentsSchemaController.sessionSchema.runtime;
1106
1112
  }
1107
1113
 
@@ -1114,11 +1120,11 @@ export class ContainerRuntime
1114
1120
  // Id Compressor serializes final state (see getPendingLocalState()). As result, it needs to skip all ops that preceeded that state
1115
1121
  // (such ops will be marked by Loader layer as savedOp === true)
1116
1122
  // That said, in "delayed" mode it's possible that Id Compressor was never initialized before getPendingLocalState() is called.
1117
- // 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.
1118
1124
  private readonly skipSavedCompressorOps: boolean;
1119
1125
 
1120
1126
  public get idCompressorMode() {
1121
- return this.documentSchema.idCompressorMode;
1127
+ return this.sessionSchema.idCompressorMode;
1122
1128
  }
1123
1129
  /**
1124
1130
  * See IContainerRuntimeBase.idCompressor() for details.
@@ -1201,14 +1207,6 @@ export class ContainerRuntime
1201
1207
 
1202
1208
  private ensureNoDataModelChangesCalls = 0;
1203
1209
 
1204
- /**
1205
- * Tracks the number of detected reentrant ops to report,
1206
- * in order to self-throttle the telemetry events.
1207
- *
1208
- * This should be removed as part of ADO:2322
1209
- */
1210
- private opReentryCallsToReport = 5;
1211
-
1212
1210
  /**
1213
1211
  * Invokes the given callback and expects that no ops are submitted
1214
1212
  * until execution finishes. If an op is submitted, an error will be raised.
@@ -1242,16 +1240,8 @@ export class ContainerRuntime
1242
1240
 
1243
1241
  private dirtyContainer: boolean;
1244
1242
  private emitDirtyDocumentEvent = true;
1245
- private readonly enableOpReentryCheck: boolean;
1246
1243
  private readonly disableAttachReorder: boolean | undefined;
1247
1244
  private readonly closeSummarizerDelayMs: number;
1248
- /**
1249
- * If true, summary generated is validate before uploading it to the server. With single commit summaries,
1250
- * summaries will be accepted once uploaded, so they should be validated before upload. However, this can
1251
- * currently be controlled via a feature flag as its a new functionality.
1252
- */
1253
- private readonly validateSummaryBeforeUpload: boolean;
1254
-
1255
1245
  private readonly defaultTelemetrySignalSampleCount = 100;
1256
1246
  private readonly _perfSignalData: IPerfSignalReport = {
1257
1247
  signalsLost: 0,
@@ -1396,6 +1386,7 @@ export class ContainerRuntime
1396
1386
  loader,
1397
1387
  pendingLocalState,
1398
1388
  supportedFeatures,
1389
+ snapshotWithContents,
1399
1390
  } = context;
1400
1391
 
1401
1392
  this.mc = createChildMonitoringContext({
@@ -1408,14 +1399,13 @@ export class ContainerRuntime
1408
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)
1409
1400
  // compression.
1410
1401
  const compressionOptions: ICompressionRuntimeOptions = {
1411
- minimumBatchSizeInBytes: this.documentSchema.compressionLz4
1402
+ minimumBatchSizeInBytes: this.sessionSchema.compressionLz4
1412
1403
  ? runtimeOptions.compressionOptions.minimumBatchSizeInBytes
1413
1404
  : Number.POSITIVE_INFINITY,
1414
1405
  compressionAlgorithm: CompressionAlgorithms.lz4,
1415
1406
  };
1416
1407
 
1417
1408
  this.innerDeltaManager = deltaManager;
1418
- this.deltaManager = new DeltaManagerSummarizerProxy(this.innerDeltaManager);
1419
1409
 
1420
1410
  // Here we could wrap/intercept on these functions to block/modify outgoing messages if needed.
1421
1411
  // This makes ContainerRuntime the final gatekeeper for outgoing messages.
@@ -1520,20 +1510,50 @@ export class ContainerRuntime
1520
1510
  opGroupingManager,
1521
1511
  );
1522
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
+
1523
1551
  this.handleContext = new ContainerFluidHandleContext("", this);
1524
1552
 
1525
1553
  if (this.summaryConfiguration.state === "enabled") {
1526
1554
  this.validateSummaryHeuristicConfiguration(this.summaryConfiguration);
1527
1555
  }
1528
1556
 
1529
- const disableOpReentryCheck = this.mc.config.getBoolean(
1530
- "Fluid.ContainerRuntime.DisableOpReentryCheck",
1531
- );
1532
- this.enableOpReentryCheck =
1533
- runtimeOptions.enableOpReentryCheck === true &&
1534
- // Allow for a break-glass config to override the options
1535
- disableOpReentryCheck !== true;
1536
-
1537
1557
  this.summariesDisabled = this.isSummariesDisabled();
1538
1558
  this.maxOpsSinceLastSummary = this.getMaxOpsSinceLastSummary();
1539
1559
  this.initialSummarizerDelayMs = this.getInitialSummarizerDelayMs();
@@ -1553,8 +1573,6 @@ export class ContainerRuntime
1553
1573
  this._flushMode = runtimeOptions.flushMode;
1554
1574
  }
1555
1575
 
1556
- const pendingRuntimeState = pendingLocalState as IPendingRuntimeState | undefined;
1557
-
1558
1576
  if (context.attachState === AttachState.Attached) {
1559
1577
  const maxSnapshotCacheDurationMs = this._storage?.policies?.maximumCacheDurationMs;
1560
1578
  if (
@@ -1626,26 +1644,22 @@ export class ContainerRuntime
1626
1644
  return this.submitSignalFn(envelope2, targetClientId);
1627
1645
  };
1628
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
+
1629
1658
  this.channelCollection = new ChannelCollection(
1630
- getSummaryForDatastores(baseSnapshot, metadata),
1659
+ snapshot,
1631
1660
  parentContext,
1632
1661
  this.mc.logger,
1633
- (
1634
- path: string,
1635
- reason: "Loaded" | "Changed",
1636
- timestampMs?: number,
1637
- packagePath?: readonly string[],
1638
- request?: IRequest,
1639
- headerData?: RuntimeHeaderData,
1640
- ) =>
1641
- this.garbageCollector.nodeUpdated(
1642
- path,
1643
- reason,
1644
- timestampMs,
1645
- packagePath,
1646
- request,
1647
- headerData,
1648
- ),
1662
+ (props) => this.garbageCollector.nodeUpdated(props),
1649
1663
  (path: string) => this.garbageCollector.isNodeDeleted(path),
1650
1664
  new Map<string, string>(dataStoreAliasMap),
1651
1665
  async (runtime: ChannelCollection) => provideEntryPoint,
@@ -1668,7 +1682,10 @@ export class ContainerRuntime
1668
1682
  }
1669
1683
  },
1670
1684
  blobRequested: (blobPath: string) =>
1671
- this.garbageCollector.nodeUpdated(blobPath, "Loaded"),
1685
+ this.garbageCollector.nodeUpdated({
1686
+ node: { type: "Blob", path: blobPath },
1687
+ reason: "Loaded",
1688
+ }),
1672
1689
  isBlobDeleted: (blobPath: string) => this.garbageCollector.isNodeDeleted(blobPath),
1673
1690
  runtime: this,
1674
1691
  stashedBlobs: pendingRuntimeState?.pendingAttachmentBlobs,
@@ -1682,24 +1699,6 @@ export class ContainerRuntime
1682
1699
  createChildLogger({ logger: this.logger, namespace: "ScheduleManager" }),
1683
1700
  );
1684
1701
 
1685
- this.pendingStateManager = new PendingStateManager(
1686
- {
1687
- applyStashedOp: this.applyStashedOp.bind(this),
1688
- clientId: () => this.clientId,
1689
- close: this.closeFn,
1690
- connected: () => this.connected,
1691
- reSubmit: (message: IPendingBatchMessage) => {
1692
- this.reSubmit(message);
1693
- this.flush();
1694
- },
1695
- reSubmitBatch: this.reSubmitBatch.bind(this),
1696
- isActiveConnection: () => this.innerDeltaManager.active,
1697
- isAttached: () => this.attachState !== AttachState.Detached,
1698
- },
1699
- pendingRuntimeState?.pending,
1700
- this.logger,
1701
- );
1702
-
1703
1702
  const disablePartialFlush = this.mc.config.getBoolean(
1704
1703
  "Fluid.ContainerRuntime.DisablePartialFlush",
1705
1704
  );
@@ -1734,16 +1733,37 @@ export class ContainerRuntime
1734
1733
  this.remoteMessageProcessor.clearPartialMessagesFor(clientId);
1735
1734
  });
1736
1735
 
1737
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
1738
- 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
+ }
1739
1762
 
1740
1763
  const closeSummarizerDelayOverride = this.mc.config.getNumber(
1741
1764
  "Fluid.ContainerRuntime.Test.CloseSummarizerDelayOverrideMs",
1742
1765
  );
1743
1766
  this.closeSummarizerDelayMs = closeSummarizerDelayOverride ?? defaultCloseSummarizerDelayMs;
1744
- this.validateSummaryBeforeUpload =
1745
- this.mc.config.getBoolean("Fluid.Summarizer.ValidateSummaryBeforeUpload") ?? false;
1746
-
1747
1767
  this.summaryCollection = new SummaryCollection(this.deltaManager, this.logger);
1748
1768
 
1749
1769
  this.dirtyContainer =
@@ -1855,9 +1875,9 @@ export class ContainerRuntime
1855
1875
  options: JSON.stringify(runtimeOptions),
1856
1876
  idCompressorModeMetadata: metadata?.documentSchema?.runtime?.idCompressorMode,
1857
1877
  idCompressorMode: this.idCompressorMode,
1878
+ sessionRuntimeSchema: JSON.stringify(this.sessionSchema),
1858
1879
  featureGates: JSON.stringify({
1859
1880
  ...featureGatesForTelemetry,
1860
- disableOpReentryCheck,
1861
1881
  disableChunking,
1862
1882
  disableAttachReorder: this.disableAttachReorder,
1863
1883
  disablePartialFlush,
@@ -1865,6 +1885,7 @@ export class ContainerRuntime
1865
1885
  }),
1866
1886
  telemetryDocumentId: this.telemetryDocumentId,
1867
1887
  groupedBatchingEnabled: this.groupedBatchingEnabled,
1888
+ initialSequenceNumber: this.deltaManager.initialSequenceNumber,
1868
1889
  });
1869
1890
 
1870
1891
  ReportOpPerfTelemetry(this.clientId, this.deltaManager, this, this.logger);
@@ -1887,6 +1908,11 @@ export class ContainerRuntime
1887
1908
  }
1888
1909
 
1889
1910
  public onSchemaChange(schema: IDocumentSchemaCurrent) {
1911
+ this.logger.sendTelemetryEvent({
1912
+ eventName: "SchemaChangeAccept",
1913
+ sessionRuntimeSchema: JSON.stringify(schema),
1914
+ });
1915
+
1890
1916
  // Most of the settings will be picked up only by new sessions (i.e. after reload).
1891
1917
  // We can make it better in the future (i.e. start to use op compression right away), but for simplicity
1892
1918
  // this is not done.
@@ -1936,9 +1962,9 @@ export class ContainerRuntime
1936
1962
  this.idCompressorMode === "on" ||
1937
1963
  (this.idCompressorMode === "delayed" && this.connected)
1938
1964
  ) {
1965
+ this._idCompressor = await this.createIdCompressor();
1939
1966
  // This is called from loadRuntime(), long before we process any ops, so there should be no ops accumulated yet.
1940
1967
  assert(this.pendingIdCompressorOps.length === 0, 0x8ec /* no pending ops */);
1941
- this._idCompressor = await this.createIdCompressor();
1942
1968
  }
1943
1969
 
1944
1970
  await this.garbageCollector.initializeBaseState();
@@ -2316,6 +2342,7 @@ export class ContainerRuntime
2316
2342
  let newState: boolean;
2317
2343
 
2318
2344
  try {
2345
+ this.submitIdAllocationOpIfNeeded(true);
2319
2346
  // replay the ops
2320
2347
  this.pendingStateManager.replayPendingStates();
2321
2348
  } finally {
@@ -2368,8 +2395,6 @@ export class ContainerRuntime
2368
2395
  return;
2369
2396
  case ContainerMessageType.BlobAttach:
2370
2397
  return;
2371
- case ContainerMessageType.ChunkedOp:
2372
- throw new Error("chunkedOp not expected here");
2373
2398
  case ContainerMessageType.Rejoin:
2374
2399
  throw new Error("rejoin not expected here");
2375
2400
  case ContainerMessageType.GC:
@@ -2382,7 +2407,7 @@ export class ContainerRuntime
2382
2407
  const compatBehavior = opContents.compatDetails?.behavior;
2383
2408
  if (!compatBehaviorAllowsMessageType(opContents.type, compatBehavior)) {
2384
2409
  const error = DataProcessingError.create(
2385
- "Stashed runtime message of unknown type",
2410
+ "Stashed runtime message of unexpected type",
2386
2411
  "applyStashedOp",
2387
2412
  undefined /* sequencedMessage */,
2388
2413
  {
@@ -2409,12 +2434,14 @@ export class ContainerRuntime
2409
2434
  ) {
2410
2435
  this._loadIdCompressor = this.createIdCompressor()
2411
2436
  .then((compressor) => {
2412
- this._idCompressor = compressor;
2413
2437
  // Finalize any ranges we received while the compressor was turned off.
2414
- for (const range of this.pendingIdCompressorOps) {
2415
- this._idCompressor.finalizeCreationRange(range);
2416
- }
2438
+ const ops = this.pendingIdCompressorOps;
2417
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;
2418
2445
  })
2419
2446
  .catch((error) => {
2420
2447
  this.logger.sendErrorEvent({ eventName: "IdCompressorDelayedLoad" }, error);
@@ -2425,6 +2452,14 @@ export class ContainerRuntime
2425
2452
  }
2426
2453
 
2427
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
+
2428
2463
  if (connected && this.idCompressorMode === "delayed") {
2429
2464
  // eslint-disable-next-line @typescript-eslint/no-floating-promises
2430
2465
  this.loadIdCompressor();
@@ -2538,21 +2573,28 @@ export class ContainerRuntime
2538
2573
  // We do not need to make a deep copy. Each layer will just replace message.contents itself,
2539
2574
  // but will not modify the contents object (likely it will replace it on the message).
2540
2575
  const messageCopy = { ...messageArg };
2576
+ const savedOp = (messageCopy.metadata as ISavedOpMetadata)?.savedOp;
2541
2577
  for (const message of this.remoteMessageProcessor.process(messageCopy)) {
2542
- if (modernRuntimeMessage) {
2543
- this.processCore({
2544
- // Cast it since we expect it to be this based on modernRuntimeMessage computation above.
2545
- // There is nothing really ensuring that anytime original message.type is Operation that
2546
- // the result messages will be so. In the end modern bool being true only directs to
2547
- // throw error if ultimately unrecognized without compat details saying otherwise.
2548
- message: message as InboundSequencedContainerRuntimeMessage,
2549
- local,
2550
- modernRuntimeMessage,
2551
- });
2552
- } else {
2553
- // Unrecognized message will be ignored.
2554
- this.processCore({ message, local, modernRuntimeMessage });
2555
- }
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));
2556
2598
  }
2557
2599
  }
2558
2600
 
@@ -2563,6 +2605,17 @@ export class ContainerRuntime
2563
2605
  */
2564
2606
  private processCore(messageWithContext: MessageWithContext) {
2565
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
+
2566
2619
  // Surround the actual processing of the operation with messages to the schedule manager indicating
2567
2620
  // the beginning and end. This allows it to emit appropriate events and/or pause the processing of new
2568
2621
  // messages once a batch has been fully processed.
@@ -2577,7 +2630,7 @@ export class ContainerRuntime
2577
2630
  // 2) this.resetReconnectCount() below
2578
2631
  assert(
2579
2632
  message.type !== ContainerMessageType.ChunkedOp,
2580
- "we should never get here with chunked ops",
2633
+ 0x93b /* we should never get here with chunked ops */,
2581
2634
  );
2582
2635
 
2583
2636
  let localOpMetadata: unknown;
@@ -2640,23 +2693,21 @@ export class ContainerRuntime
2640
2693
  // stashed ops flow. The compressor is stashed with these ops already processed.
2641
2694
  // That said, in idCompressorMode === "delayed", we might not serialize ID compressor, and
2642
2695
  // thus we need to process all the ops.
2643
- if (
2644
- !(
2645
- this.skipSavedCompressorOps &&
2646
- (messageWithContext.message.metadata as IIdAllocationMetadata)?.savedOp ===
2647
- true
2648
- )
2649
- ) {
2696
+ if (!(this.skipSavedCompressorOps && messageWithContext.savedOp === true)) {
2650
2697
  const range = messageWithContext.message.contents;
2651
2698
  // Some other client turned on the id compressor. If we have not turned it on,
2652
2699
  // put it in a pending queue and delay finalization.
2653
2700
  if (this._idCompressor === undefined) {
2654
2701
  assert(
2655
2702
  this.idCompressorMode !== undefined,
2656
- "id compressor should be enabled",
2703
+ 0x93c /* id compressor should be enabled */,
2657
2704
  );
2658
2705
  this.pendingIdCompressorOps.push(range);
2659
2706
  } else {
2707
+ assert(
2708
+ this.pendingIdCompressorOps.length === 0,
2709
+ 0x979 /* there should be no pending ops! */,
2710
+ );
2660
2711
  this._idCompressor.finalizeCreationRange(range);
2661
2712
  }
2662
2713
  }
@@ -2667,7 +2718,7 @@ export class ContainerRuntime
2667
2718
  case ContainerMessageType.ChunkedOp:
2668
2719
  // From observability POV, we should not exppse the rest of the system (including "op" events on object) to these messages.
2669
2720
  // Also resetReconnectCount() would be wrong - see comment that was there before this change was made.
2670
- assert(false, "should not even get here");
2721
+ assert(false, 0x93d /* should not even get here */);
2671
2722
  case ContainerMessageType.Rejoin:
2672
2723
  break;
2673
2724
  case ContainerMessageType.DocumentSchemaChange:
@@ -2807,9 +2858,9 @@ export class ContainerRuntime
2807
2858
  let checkpoint: IBatchCheckpoint | undefined;
2808
2859
  let result: T;
2809
2860
  if (this.mc.config.getBoolean("Fluid.ContainerRuntime.EnableRollback")) {
2810
- // Note: we are not touching this.pendingAttachBatch here, for two reasons:
2811
- // 1. It would not help, as we flush attach ops as they become available.
2812
- // 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.
2813
2864
  checkpoint = this.outbox.checkpoint().mainBatch;
2814
2865
  }
2815
2866
  try {
@@ -2892,12 +2943,11 @@ export class ContainerRuntime
2892
2943
  "entryPoint must be defined on data store runtime for using getAliasedDataStoreEntryPoint",
2893
2944
  );
2894
2945
  }
2895
- this.garbageCollector.nodeUpdated(
2896
- `/${internalId}`,
2897
- "Loaded",
2898
- undefined /* timestampMs */,
2899
- context.packagePath,
2900
- );
2946
+ this.garbageCollector.nodeUpdated({
2947
+ node: { type: "DataStore", path: `/${internalId}` },
2948
+ reason: "Loaded",
2949
+ packagePath: context.packagePath,
2950
+ });
2901
2951
  return channel.entryPoint;
2902
2952
  }
2903
2953
 
@@ -3083,7 +3133,7 @@ export class ContainerRuntime
3083
3133
  if (idRange !== undefined) {
3084
3134
  assert(
3085
3135
  idRange.ids === undefined || idRange.ids.firstGenCount === 1,
3086
- "No other ranges should be taken while container is detached.",
3136
+ 0x93e /* No other ranges should be taken while container is detached. */,
3087
3137
  );
3088
3138
  this._idCompressor?.finalizeCreationRange(idRange);
3089
3139
  }
@@ -3188,7 +3238,7 @@ export class ContainerRuntime
3188
3238
 
3189
3239
  return { stats, summary };
3190
3240
  } finally {
3191
- this.mc.logger.sendTelemetryEvent({
3241
+ summaryLogger.sendTelemetryEvent({
3192
3242
  eventName: "SummarizeTelemetry",
3193
3243
  details: telemetryContext.serialize(),
3194
3244
  });
@@ -3392,10 +3442,14 @@ export class ContainerRuntime
3392
3442
  // The summary number for this summary. This will be updated during the summary process, so get it now and
3393
3443
  // use it for all events logged during this summary.
3394
3444
  const summaryNumber = this.nextSummaryNumber;
3445
+ let summaryRefSeqNum: number | undefined;
3395
3446
  const summaryNumberLogger = createChildLogger({
3396
3447
  logger: summaryLogger,
3397
3448
  properties: {
3398
- all: { summaryNumber },
3449
+ all: {
3450
+ summaryNumber,
3451
+ referenceSequenceNumber: () => summaryRefSeqNum,
3452
+ },
3399
3453
  },
3400
3454
  });
3401
3455
 
@@ -3414,7 +3468,7 @@ export class ContainerRuntime
3414
3468
  // If the container is dirty, i.e., there are pending unacked ops, the summary will not be eventual consistent
3415
3469
  // and it may even be incorrect. So, wait for the container to be saved with a timeout. If the container is not
3416
3470
  // saved within the timeout, check if it should be failed or can continue.
3417
- if (this.validateSummaryBeforeUpload && this.isDirty) {
3471
+ if (this.isDirty) {
3418
3472
  const countBefore = this.pendingMessagesCount;
3419
3473
  // The timeout for waiting for pending ops can be overridden via configurations.
3420
3474
  const pendingOpsTimeout =
@@ -3464,8 +3518,6 @@ export class ContainerRuntime
3464
3518
  "Fluid.ContainerRuntime.SubmitSummary.shouldValidatePreSummaryState",
3465
3519
  ) === true;
3466
3520
 
3467
- let summaryRefSeqNum: number | undefined;
3468
-
3469
3521
  try {
3470
3522
  await this.deltaManager.inbound.pause();
3471
3523
  if (shouldPauseInboundSignal) {
@@ -3483,11 +3535,19 @@ export class ContainerRuntime
3483
3535
  latestSummaryRefSeqNum,
3484
3536
  );
3485
3537
 
3538
+ /**
3539
+ * This was added to validate that the summarizer node tree has the same reference sequence number from the
3540
+ * top running summarizer down to the lowest summarizer node.
3541
+ *
3542
+ * The order of mismatch numbers goes (validate sequence number)-(node sequence number).
3543
+ * Generally the validate sequence number comes from the running summarizer and the node sequence number comes from the
3544
+ * summarizer nodes.
3545
+ */
3486
3546
  if (
3487
3547
  startSummaryResult.invalidNodes > 0 ||
3488
3548
  startSummaryResult.mismatchNumbers.size > 0
3489
3549
  ) {
3490
- summaryLogger.sendErrorEvent({
3550
+ summaryLogger.sendTelemetryEvent({
3491
3551
  eventName: "LatestSummaryRefSeqNumMismatch",
3492
3552
  details: {
3493
3553
  ...startSummaryResult,
@@ -3500,7 +3560,9 @@ export class ContainerRuntime
3500
3560
  stage: "base",
3501
3561
  referenceSequenceNumber: summaryRefSeqNum,
3502
3562
  minimumSequenceNumber,
3503
- error: `Summarizer node state inconsistent with summarizer state.`,
3563
+ error: new LoggingError(
3564
+ `Summarizer node state inconsistent with summarizer state.`,
3565
+ ),
3504
3566
  };
3505
3567
  }
3506
3568
  }
@@ -3550,7 +3612,7 @@ export class ContainerRuntime
3550
3612
  stage: "base",
3551
3613
  referenceSequenceNumber: summaryRefSeqNum,
3552
3614
  minimumSequenceNumber,
3553
- error: continueResult.error,
3615
+ error: new LoggingError(continueResult.error),
3554
3616
  };
3555
3617
  }
3556
3618
 
@@ -3571,39 +3633,38 @@ export class ContainerRuntime
3571
3633
  stage: "base",
3572
3634
  referenceSequenceNumber: summaryRefSeqNum,
3573
3635
  minimumSequenceNumber,
3574
- error,
3636
+ error: wrapError(error, (msg) => new LoggingError(msg)),
3575
3637
  };
3576
3638
  }
3577
3639
 
3578
- // If validateSummaryBeforeUpload is true, validate that the summary generated is correct before uploading.
3579
- if (this.validateSummaryBeforeUpload) {
3580
- // Validate that the summaries generated by summarize nodes is correct.
3581
- const validateResult = this.summarizerNode.validateSummary();
3582
- if (!validateResult.success) {
3583
- const { success, ...loggingProps } = validateResult;
3584
- const error = new RetriableSummaryError(
3585
- validateResult.reason,
3586
- validateResult.retryAfterSeconds,
3587
- { ...loggingProps },
3588
- );
3589
- return {
3590
- stage: "base",
3591
- referenceSequenceNumber: summaryRefSeqNum,
3592
- minimumSequenceNumber,
3593
- error,
3594
- };
3595
- }
3596
-
3597
- const pendingMessagesFailResult = await this.shouldFailSummaryOnPendingOps(
3598
- summaryNumberLogger,
3599
- summaryRefSeqNum,
3600
- minimumSequenceNumber,
3601
- finalAttempt,
3602
- 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 },
3603
3648
  );
3604
- if (pendingMessagesFailResult !== undefined) {
3605
- return pendingMessagesFailResult;
3606
- }
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;
3607
3668
  }
3608
3669
 
3609
3670
  const { summary: summaryTree, stats: partialStats } = summarizeResult;
@@ -3644,7 +3705,11 @@ export class ContainerRuntime
3644
3705
 
3645
3706
  continueResult = checkContinue();
3646
3707
  if (!continueResult.continue) {
3647
- return { stage: "generate", ...generateSummaryData, error: continueResult.error };
3708
+ return {
3709
+ stage: "generate",
3710
+ ...generateSummaryData,
3711
+ error: new LoggingError(continueResult.error),
3712
+ };
3648
3713
  }
3649
3714
 
3650
3715
  const summaryContext =
@@ -3667,7 +3732,11 @@ export class ContainerRuntime
3667
3732
  summaryContext,
3668
3733
  );
3669
3734
  } catch (error) {
3670
- return { stage: "generate", ...generateSummaryData, error };
3735
+ return {
3736
+ stage: "generate",
3737
+ ...generateSummaryData,
3738
+ error: wrapError(error, (msg) => new LoggingError(msg)),
3739
+ };
3671
3740
  }
3672
3741
 
3673
3742
  const parent = summaryContext.ackHandle;
@@ -3686,14 +3755,22 @@ export class ContainerRuntime
3686
3755
 
3687
3756
  continueResult = checkContinue();
3688
3757
  if (!continueResult.continue) {
3689
- return { stage: "upload", ...uploadData, error: continueResult.error };
3758
+ return {
3759
+ stage: "upload",
3760
+ ...uploadData,
3761
+ error: new LoggingError(continueResult.error),
3762
+ };
3690
3763
  }
3691
3764
 
3692
3765
  let clientSequenceNumber: number;
3693
3766
  try {
3694
3767
  clientSequenceNumber = this.submitSummaryMessage(summaryMessage, summaryRefSeqNum);
3695
3768
  } catch (error) {
3696
- return { stage: "upload", ...uploadData, error };
3769
+ return {
3770
+ stage: "upload",
3771
+ ...uploadData,
3772
+ error: wrapError(error, (msg) => new LoggingError(msg)),
3773
+ };
3697
3774
  }
3698
3775
 
3699
3776
  const submitData = {
@@ -3704,13 +3781,13 @@ export class ContainerRuntime
3704
3781
  } as const;
3705
3782
 
3706
3783
  try {
3707
- // If validateSummaryBeforeUpload is false, the summary should be validated in this step.
3708
- this.summarizerNode.completeSummary(
3709
- handle,
3710
- !this.validateSummaryBeforeUpload /* validate */,
3711
- );
3784
+ this.summarizerNode.completeSummary(handle);
3712
3785
  } catch (error) {
3713
- return { stage: "upload", ...uploadData, error };
3786
+ return {
3787
+ stage: "upload",
3788
+ ...uploadData,
3789
+ error: wrapError(error, (msg) => new LoggingError(msg)),
3790
+ };
3714
3791
  }
3715
3792
  return submitData;
3716
3793
  } finally {
@@ -3837,14 +3914,16 @@ export class ContainerRuntime
3837
3914
  public async uploadBlob(
3838
3915
  blob: ArrayBufferLike,
3839
3916
  signal?: AbortSignal,
3840
- ): Promise<IFluidHandle<ArrayBufferLike>> {
3917
+ ): Promise<IFluidHandleInternal<ArrayBufferLike>> {
3841
3918
  this.verifyNotClosed();
3842
3919
  return this.blobManager.createBlob(blob, signal);
3843
3920
  }
3844
3921
 
3845
- private submitIdAllocationOpIfNeeded(): void {
3922
+ private submitIdAllocationOpIfNeeded(resubmitOutstandingRanges = false): void {
3846
3923
  if (this._idCompressor) {
3847
- const idRange = this._idCompressor.takeNextCreationRange();
3924
+ const idRange = resubmitOutstandingRanges
3925
+ ? this.idCompressor?.takeUnfinalizedCreationRange()
3926
+ : this._idCompressor.takeNextCreationRange();
3848
3927
  // Don't include the idRange if there weren't any Ids allocated
3849
3928
  if (idRange?.ids !== undefined) {
3850
3929
  const idAllocationMessage: ContainerRuntimeIdAllocationMessage = {
@@ -3866,7 +3945,6 @@ export class ContainerRuntime
3866
3945
  metadata?: { localId: string; blobId?: string },
3867
3946
  ): void {
3868
3947
  this.verifyNotClosed();
3869
- this.verifyCanSubmitOps();
3870
3948
 
3871
3949
  // There should be no ops in detached container state!
3872
3950
  assert(
@@ -3877,7 +3955,7 @@ export class ContainerRuntime
3877
3955
  assert(
3878
3956
  metadata === undefined ||
3879
3957
  containerRuntimeMessage.type === ContainerMessageType.BlobAttach,
3880
- "metadata",
3958
+ 0x93f /* metadata */,
3881
3959
  );
3882
3960
 
3883
3961
  const serializedContent = JSON.stringify(containerRuntimeMessage);
@@ -3914,6 +3992,14 @@ export class ContainerRuntime
3914
3992
  // on this callback to do actual sending.
3915
3993
  const contents = this.documentsSchemaController.maybeSendSchemaMessage();
3916
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
+ });
3917
4003
  const msg: ContainerRuntimeDocumentSchemaMessage = {
3918
4004
  type: ContainerMessageType.DocumentSchemaChange,
3919
4005
  contents,
@@ -3924,33 +4010,7 @@ export class ContainerRuntime
3924
4010
  });
3925
4011
  }
3926
4012
 
3927
- // If this is attach message for new data store, and we are in a batch, send this op out of order
3928
- // Is it safe:
3929
- // Yes, this should be safe reordering. Newly created data stores are not visible through API surface.
3930
- // They become visible only when aliased, or handle to some sub-element of newly created datastore
3931
- // is stored in some DDS, i.e. only after some other op.
3932
- // Why:
3933
- // Attach ops are large, and expensive to process. Plus there are scenarios where a lot of new data
3934
- // stores are created, causing issues like relay service throttling (too many ops) and catastrophic
3935
- // failure (batch is too large). Pushing them earlier and outside of main batch should alleviate
3936
- // these issues.
3937
- // Cons:
3938
- // 1. With large batches, relay service may throttle clients. Clients may disconnect while throttled.
3939
- // This change creates new possibility of a lot of newly created data stores never being referenced
3940
- // because client died before it had a change to submit the rest of the ops. This will create more
3941
- // garbage that needs to be collected leveraging GC (Garbage Collection) feature.
3942
- // 2. Sending ops out of order means they are excluded from rollback functionality. This is not an issue
3943
- // today as rollback can't undo creation of data store. To some extent not sending them is a bigger
3944
- // issue than sending.
3945
- // Please note that this does not change file format, so it can be disabled in the future if this
3946
- // optimization no longer makes sense (for example, batch compression may make it less appealing).
3947
- if (
3948
- this.currentlyBatching() &&
3949
- type === ContainerMessageType.Attach &&
3950
- this.disableAttachReorder !== true
3951
- ) {
3952
- this.outbox.submitAttach(message);
3953
- } else if (type === ContainerMessageType.BlobAttach) {
4013
+ if (type === ContainerMessageType.BlobAttach) {
3954
4014
  // BlobAttach ops must have their metadata visible and cannot be grouped (see opGroupingManager.ts)
3955
4015
  this.outbox.submitBlobAttach(message);
3956
4016
  } else {
@@ -4039,37 +4099,6 @@ export class ContainerRuntime
4039
4099
  }
4040
4100
  }
4041
4101
 
4042
- private verifyCanSubmitOps() {
4043
- if (this.ensureNoDataModelChangesCalls > 0) {
4044
- const errorMessage =
4045
- "Op was submitted from within a `ensureNoDataModelChanges` callback";
4046
- if (this.opReentryCallsToReport > 0) {
4047
- this.mc.logger.sendTelemetryEvent(
4048
- { eventName: "OpReentry" },
4049
- // We need to capture the call stack in order to inspect the source of this usage pattern
4050
- getLongStack(() => new UsageError(errorMessage)),
4051
- );
4052
- this.opReentryCallsToReport--;
4053
- }
4054
-
4055
- // Creating ops while processing ops can lead
4056
- // to undefined behavior and events observed in the wrong order.
4057
- // For example, we have two callbacks registered for a DDS, A and B.
4058
- // Then if on change #1 callback A creates change #2, the invocation flow will be:
4059
- //
4060
- // A because of #1
4061
- // A because of #2
4062
- // B because of #2
4063
- // B because of #1
4064
- //
4065
- // The runtime must enforce op coherence by not allowing ops to be submitted
4066
- // while ops are being processed.
4067
- if (this.enableOpReentryCheck) {
4068
- throw new UsageError(errorMessage);
4069
- }
4070
- }
4071
- }
4072
-
4073
4102
  private reSubmitBatch(batch: IPendingBatchMessage[]) {
4074
4103
  this.orderSequentially(() => {
4075
4104
  for (const message of batch) {
@@ -4110,11 +4139,15 @@ export class ContainerRuntime
4110
4139
  this.channelCollection.reSubmit(message.type, message.contents, localOpMetadata);
4111
4140
  break;
4112
4141
  case ContainerMessageType.IdAllocation: {
4113
- 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.
4114
4149
  break;
4115
4150
  }
4116
- case ContainerMessageType.ChunkedOp:
4117
- throw new Error(`chunkedOp not expected here`);
4118
4151
  case ContainerMessageType.BlobAttach:
4119
4152
  this.blobManager.reSubmit(opMetadata);
4120
4153
  break;
@@ -4141,7 +4174,7 @@ export class ContainerRuntime
4141
4174
  });
4142
4175
  } else {
4143
4176
  const error = DataProcessingError.create(
4144
- "Resubmitting runtime message of unknown type",
4177
+ "Resubmitting runtime message of unexpected type",
4145
4178
  "reSubmitCore",
4146
4179
  undefined /* sequencedMessage */,
4147
4180
  {
@@ -4234,7 +4267,7 @@ export class ContainerRuntime
4234
4267
 
4235
4268
  return {
4236
4269
  stage: "base",
4237
- error: "summary state stale - Unsupported option 'refreshLatestAck'",
4270
+ error: new LoggingError("summary state stale - Unsupported option 'refreshLatestAck'"),
4238
4271
  referenceSequenceNumber: this.deltaManager.lastSequenceNumber,
4239
4272
  minimumSequenceNumber: this.deltaManager.minimumSequenceNumber,
4240
4273
  };
@@ -4316,10 +4349,9 @@ export class ContainerRuntime
4316
4349
  const getSyncState = (
4317
4350
  pendingAttachmentBlobs?: IPendingBlobs,
4318
4351
  ): IPendingRuntimeState | undefined => {
4319
- const pending = this.pendingStateManager.getLocalState();
4320
- if (pendingAttachmentBlobs === undefined && !this.hasPendingMessages()) {
4321
- return; // no pending state to save
4322
- }
4352
+ const pending = this.pendingStateManager.getLocalState(props?.snapshotSequenceNumber);
4353
+ const sessionExpiryTimerStarted =
4354
+ props?.sessionExpiryTimerStarted ?? this.garbageCollector.sessionExpiryTimerStarted;
4323
4355
 
4324
4356
  const pendingIdCompressorState = this._idCompressor?.serialize(true);
4325
4357
 
@@ -4327,7 +4359,7 @@ export class ContainerRuntime
4327
4359
  pending,
4328
4360
  pendingIdCompressorState,
4329
4361
  pendingAttachmentBlobs,
4330
- sessionExpiryTimerStarted: this.garbageCollector.sessionExpiryTimerStarted,
4362
+ sessionExpiryTimerStarted,
4331
4363
  };
4332
4364
  };
4333
4365
  const perfEvent = {
@@ -4418,6 +4450,6 @@ export class ContainerRuntime
4418
4450
  }
4419
4451
 
4420
4452
  private get groupedBatchingEnabled(): boolean {
4421
- return this.documentSchema.opGroupingEnabled === true;
4453
+ return this.sessionSchema.opGroupingEnabled === true;
4422
4454
  }
4423
4455
  }