@fluidframework/container-runtime 2.0.0-dev.3.1.0.125672 → 2.0.0-dev.4.2.0.153917

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 (486) hide show
  1. package/CHANGELOG.md +58 -0
  2. package/README.md +69 -0
  3. package/dist/blobManager.d.ts +29 -24
  4. package/dist/blobManager.d.ts.map +1 -1
  5. package/dist/blobManager.js +162 -92
  6. package/dist/blobManager.js.map +1 -1
  7. package/dist/containerRuntime.d.ts +74 -76
  8. package/dist/containerRuntime.d.ts.map +1 -1
  9. package/dist/containerRuntime.js +328 -264
  10. package/dist/containerRuntime.js.map +1 -1
  11. package/dist/dataStoreContext.d.ts +39 -13
  12. package/dist/dataStoreContext.d.ts.map +1 -1
  13. package/dist/dataStoreContext.js +112 -49
  14. package/dist/dataStoreContext.js.map +1 -1
  15. package/dist/dataStores.d.ts +28 -4
  16. package/dist/dataStores.d.ts.map +1 -1
  17. package/dist/dataStores.js +107 -41
  18. package/dist/dataStores.js.map +1 -1
  19. package/dist/deltaManagerSummarizerProxy.d.ts +19 -0
  20. package/dist/deltaManagerSummarizerProxy.d.ts.map +1 -0
  21. package/dist/deltaManagerSummarizerProxy.js +40 -0
  22. package/dist/deltaManagerSummarizerProxy.js.map +1 -0
  23. package/dist/gc/garbageCollection.d.ts +204 -0
  24. package/dist/gc/garbageCollection.d.ts.map +1 -0
  25. package/dist/{garbageCollection.js → gc/garbageCollection.js} +190 -554
  26. package/dist/gc/garbageCollection.js.map +1 -0
  27. package/dist/gc/gcConfigs.d.ts +22 -0
  28. package/dist/gc/gcConfigs.d.ts.map +1 -0
  29. package/dist/gc/gcConfigs.js +143 -0
  30. package/dist/gc/gcConfigs.js.map +1 -0
  31. package/dist/gc/gcDefinitions.d.ts +320 -0
  32. package/dist/gc/gcDefinitions.d.ts.map +1 -0
  33. package/dist/gc/gcDefinitions.js +81 -0
  34. package/dist/gc/gcDefinitions.js.map +1 -0
  35. package/dist/gc/gcHelpers.d.ts +86 -0
  36. package/dist/gc/gcHelpers.d.ts.map +1 -0
  37. package/dist/gc/gcHelpers.js +268 -0
  38. package/dist/gc/gcHelpers.js.map +1 -0
  39. package/dist/gc/gcReferenceGraphAlgorithm.d.ts +16 -0
  40. package/dist/gc/gcReferenceGraphAlgorithm.d.ts.map +1 -0
  41. package/dist/gc/gcReferenceGraphAlgorithm.js +49 -0
  42. package/dist/gc/gcReferenceGraphAlgorithm.js.map +1 -0
  43. package/dist/gc/gcSummaryDefinitions.d.ts +52 -0
  44. package/dist/gc/gcSummaryDefinitions.d.ts.map +1 -0
  45. package/dist/gc/gcSummaryDefinitions.js +7 -0
  46. package/dist/gc/gcSummaryDefinitions.js.map +1 -0
  47. package/dist/gc/gcSummaryStateTracker.d.ts +93 -0
  48. package/dist/gc/gcSummaryStateTracker.d.ts.map +1 -0
  49. package/dist/gc/gcSummaryStateTracker.js +239 -0
  50. package/dist/gc/gcSummaryStateTracker.js.map +1 -0
  51. package/dist/gc/gcSweepReadyUsageDetection.d.ts.map +1 -0
  52. package/dist/{gcSweepReadyUsageDetection.js → gc/gcSweepReadyUsageDetection.js} +2 -2
  53. package/dist/gc/gcSweepReadyUsageDetection.js.map +1 -0
  54. package/dist/gc/gcUnreferencedStateTracker.d.ts +34 -0
  55. package/dist/gc/gcUnreferencedStateTracker.d.ts.map +1 -0
  56. package/dist/gc/gcUnreferencedStateTracker.js +94 -0
  57. package/dist/gc/gcUnreferencedStateTracker.js.map +1 -0
  58. package/dist/gc/index.d.ts +13 -0
  59. package/dist/gc/index.d.ts.map +1 -0
  60. package/dist/gc/index.js +50 -0
  61. package/dist/gc/index.js.map +1 -0
  62. package/dist/index.d.ts +3 -7
  63. package/dist/index.d.ts.map +1 -1
  64. package/dist/index.js +5 -9
  65. package/dist/index.js.map +1 -1
  66. package/dist/opLifecycle/batchManager.d.ts +11 -13
  67. package/dist/opLifecycle/batchManager.d.ts.map +1 -1
  68. package/dist/opLifecycle/batchManager.js +26 -38
  69. package/dist/opLifecycle/batchManager.js.map +1 -1
  70. package/dist/opLifecycle/definitions.d.ts +4 -0
  71. package/dist/opLifecycle/definitions.d.ts.map +1 -1
  72. package/dist/opLifecycle/definitions.js.map +1 -1
  73. package/dist/opLifecycle/index.d.ts +2 -1
  74. package/dist/opLifecycle/index.d.ts.map +1 -1
  75. package/dist/opLifecycle/index.js +4 -1
  76. package/dist/opLifecycle/index.js.map +1 -1
  77. package/dist/opLifecycle/opCompressor.d.ts.map +1 -1
  78. package/dist/opLifecycle/opCompressor.js +25 -10
  79. package/dist/opLifecycle/opCompressor.js.map +1 -1
  80. package/dist/opLifecycle/opDecompressor.d.ts +4 -0
  81. package/dist/opLifecycle/opDecompressor.d.ts.map +1 -1
  82. package/dist/opLifecycle/opDecompressor.js +43 -4
  83. package/dist/opLifecycle/opDecompressor.js.map +1 -1
  84. package/dist/opLifecycle/opGroupingManager.d.ts +14 -0
  85. package/dist/opLifecycle/opGroupingManager.d.ts.map +1 -0
  86. package/dist/opLifecycle/opGroupingManager.js +56 -0
  87. package/dist/opLifecycle/opGroupingManager.js.map +1 -0
  88. package/dist/opLifecycle/opSplitter.d.ts +16 -4
  89. package/dist/opLifecycle/opSplitter.d.ts.map +1 -1
  90. package/dist/opLifecycle/opSplitter.js +39 -15
  91. package/dist/opLifecycle/opSplitter.js.map +1 -1
  92. package/dist/opLifecycle/outbox.d.ts +21 -3
  93. package/dist/opLifecycle/outbox.d.ts.map +1 -1
  94. package/dist/opLifecycle/outbox.js +90 -51
  95. package/dist/opLifecycle/outbox.js.map +1 -1
  96. package/dist/opLifecycle/remoteMessageProcessor.d.ts +4 -2
  97. package/dist/opLifecycle/remoteMessageProcessor.d.ts.map +1 -1
  98. package/dist/opLifecycle/remoteMessageProcessor.js +30 -20
  99. package/dist/opLifecycle/remoteMessageProcessor.js.map +1 -1
  100. package/dist/packageVersion.d.ts +1 -1
  101. package/dist/packageVersion.js +1 -1
  102. package/dist/packageVersion.js.map +1 -1
  103. package/dist/pendingStateManager.d.ts +3 -3
  104. package/dist/pendingStateManager.d.ts.map +1 -1
  105. package/dist/pendingStateManager.js +20 -21
  106. package/dist/pendingStateManager.js.map +1 -1
  107. package/dist/storageServiceWithAttachBlobs.d.ts +17 -0
  108. package/dist/storageServiceWithAttachBlobs.d.ts.map +1 -0
  109. package/dist/storageServiceWithAttachBlobs.js +32 -0
  110. package/dist/storageServiceWithAttachBlobs.js.map +1 -0
  111. package/dist/summary/index.d.ts +17 -0
  112. package/dist/summary/index.d.ts.map +1 -0
  113. package/dist/summary/index.js +48 -0
  114. package/dist/summary/index.js.map +1 -0
  115. package/dist/summary/orderedClientElection.d.ts.map +1 -0
  116. package/dist/summary/orderedClientElection.js.map +1 -0
  117. package/dist/{runWhileConnectedCoordinator.d.ts → summary/runWhileConnectedCoordinator.d.ts} +3 -2
  118. package/dist/summary/runWhileConnectedCoordinator.d.ts.map +1 -0
  119. package/dist/{runWhileConnectedCoordinator.js → summary/runWhileConnectedCoordinator.js} +5 -4
  120. package/dist/summary/runWhileConnectedCoordinator.js.map +1 -0
  121. package/{lib → dist/summary}/runningSummarizer.d.ts +23 -20
  122. package/dist/summary/runningSummarizer.d.ts.map +1 -0
  123. package/dist/{runningSummarizer.js → summary/runningSummarizer.js} +191 -74
  124. package/dist/summary/runningSummarizer.js.map +1 -0
  125. package/dist/{summarizer.d.ts → summary/summarizer.d.ts} +4 -9
  126. package/dist/summary/summarizer.d.ts.map +1 -0
  127. package/dist/{summarizer.js → summary/summarizer.js} +10 -79
  128. package/dist/summary/summarizer.js.map +1 -0
  129. package/dist/summary/summarizerClientElection.d.ts.map +1 -0
  130. package/dist/summary/summarizerClientElection.js.map +1 -0
  131. package/dist/{summarizerHeuristics.d.ts → summary/summarizerHeuristics.d.ts} +2 -1
  132. package/dist/summary/summarizerHeuristics.d.ts.map +1 -0
  133. package/dist/{summarizerHeuristics.js → summary/summarizerHeuristics.js} +6 -3
  134. package/dist/summary/summarizerHeuristics.js.map +1 -0
  135. package/dist/summary/summarizerNode/index.d.ts +8 -0
  136. package/dist/summary/summarizerNode/index.d.ts.map +1 -0
  137. package/dist/summary/summarizerNode/index.js +12 -0
  138. package/dist/summary/summarizerNode/index.js.map +1 -0
  139. package/dist/summary/summarizerNode/summarizerNode.d.ts +149 -0
  140. package/dist/summary/summarizerNode/summarizerNode.d.ts.map +1 -0
  141. package/dist/summary/summarizerNode/summarizerNode.js +531 -0
  142. package/dist/summary/summarizerNode/summarizerNode.js.map +1 -0
  143. package/dist/summary/summarizerNode/summarizerNodeUtils.d.ts +125 -0
  144. package/dist/summary/summarizerNode/summarizerNodeUtils.d.ts.map +1 -0
  145. package/dist/summary/summarizerNode/summarizerNodeUtils.js +132 -0
  146. package/dist/summary/summarizerNode/summarizerNodeUtils.js.map +1 -0
  147. package/dist/summary/summarizerNode/summarizerNodeWithGc.d.ts +148 -0
  148. package/dist/summary/summarizerNode/summarizerNodeWithGc.d.ts.map +1 -0
  149. package/dist/summary/summarizerNode/summarizerNodeWithGc.js +424 -0
  150. package/dist/summary/summarizerNode/summarizerNodeWithGc.js.map +1 -0
  151. package/{lib → dist/summary}/summarizerTypes.d.ts +21 -19
  152. package/dist/summary/summarizerTypes.d.ts.map +1 -0
  153. package/dist/{summarizerTypes.js → summary/summarizerTypes.js} +0 -5
  154. package/dist/summary/summarizerTypes.js.map +1 -0
  155. package/dist/summary/summaryCollection.d.ts.map +1 -0
  156. package/dist/summary/summaryCollection.js.map +1 -0
  157. package/{lib → dist/summary}/summaryFormat.d.ts +3 -21
  158. package/dist/summary/summaryFormat.d.ts.map +1 -0
  159. package/dist/{summaryFormat.js → summary/summaryFormat.js} +1 -10
  160. package/dist/summary/summaryFormat.js.map +1 -0
  161. package/{lib → dist/summary}/summaryGenerator.d.ts +28 -2
  162. package/dist/summary/summaryGenerator.d.ts.map +1 -0
  163. package/dist/{summaryGenerator.js → summary/summaryGenerator.js} +23 -20
  164. package/dist/summary/summaryGenerator.js.map +1 -0
  165. package/dist/{summaryManager.d.ts → summary/summaryManager.d.ts} +1 -1
  166. package/dist/summary/summaryManager.d.ts.map +1 -0
  167. package/dist/summary/summaryManager.js.map +1 -0
  168. package/lib/blobManager.d.ts +29 -24
  169. package/lib/blobManager.d.ts.map +1 -1
  170. package/lib/blobManager.js +159 -89
  171. package/lib/blobManager.js.map +1 -1
  172. package/lib/containerRuntime.d.ts +74 -76
  173. package/lib/containerRuntime.d.ts.map +1 -1
  174. package/lib/containerRuntime.js +301 -237
  175. package/lib/containerRuntime.js.map +1 -1
  176. package/lib/dataStoreContext.d.ts +39 -13
  177. package/lib/dataStoreContext.d.ts.map +1 -1
  178. package/lib/dataStoreContext.js +101 -38
  179. package/lib/dataStoreContext.js.map +1 -1
  180. package/lib/dataStores.d.ts +28 -4
  181. package/lib/dataStores.d.ts.map +1 -1
  182. package/lib/dataStores.js +100 -34
  183. package/lib/dataStores.js.map +1 -1
  184. package/lib/deltaManagerSummarizerProxy.d.ts +19 -0
  185. package/lib/deltaManagerSummarizerProxy.d.ts.map +1 -0
  186. package/lib/deltaManagerSummarizerProxy.js +36 -0
  187. package/lib/deltaManagerSummarizerProxy.js.map +1 -0
  188. package/lib/gc/garbageCollection.d.ts +204 -0
  189. package/lib/gc/garbageCollection.d.ts.map +1 -0
  190. package/lib/{garbageCollection.js → gc/garbageCollection.js} +172 -535
  191. package/lib/gc/garbageCollection.js.map +1 -0
  192. package/lib/gc/gcConfigs.d.ts +22 -0
  193. package/lib/gc/gcConfigs.d.ts.map +1 -0
  194. package/lib/gc/gcConfigs.js +139 -0
  195. package/lib/gc/gcConfigs.js.map +1 -0
  196. package/lib/gc/gcDefinitions.d.ts +320 -0
  197. package/lib/gc/gcDefinitions.d.ts.map +1 -0
  198. package/lib/gc/gcDefinitions.js +78 -0
  199. package/lib/gc/gcDefinitions.js.map +1 -0
  200. package/lib/gc/gcHelpers.d.ts +86 -0
  201. package/lib/gc/gcHelpers.d.ts.map +1 -0
  202. package/lib/gc/gcHelpers.js +254 -0
  203. package/lib/gc/gcHelpers.js.map +1 -0
  204. package/lib/gc/gcReferenceGraphAlgorithm.d.ts +16 -0
  205. package/lib/gc/gcReferenceGraphAlgorithm.d.ts.map +1 -0
  206. package/lib/gc/gcReferenceGraphAlgorithm.js +45 -0
  207. package/lib/gc/gcReferenceGraphAlgorithm.js.map +1 -0
  208. package/lib/gc/gcSummaryDefinitions.d.ts +52 -0
  209. package/lib/gc/gcSummaryDefinitions.d.ts.map +1 -0
  210. package/lib/gc/gcSummaryDefinitions.js +6 -0
  211. package/lib/gc/gcSummaryDefinitions.js.map +1 -0
  212. package/lib/gc/gcSummaryStateTracker.d.ts +93 -0
  213. package/lib/gc/gcSummaryStateTracker.d.ts.map +1 -0
  214. package/lib/gc/gcSummaryStateTracker.js +235 -0
  215. package/lib/gc/gcSummaryStateTracker.js.map +1 -0
  216. package/lib/gc/gcSweepReadyUsageDetection.d.ts.map +1 -0
  217. package/lib/{gcSweepReadyUsageDetection.js → gc/gcSweepReadyUsageDetection.js} +1 -1
  218. package/lib/gc/gcSweepReadyUsageDetection.js.map +1 -0
  219. package/lib/gc/gcUnreferencedStateTracker.d.ts +34 -0
  220. package/lib/gc/gcUnreferencedStateTracker.d.ts.map +1 -0
  221. package/lib/gc/gcUnreferencedStateTracker.js +90 -0
  222. package/lib/gc/gcUnreferencedStateTracker.js.map +1 -0
  223. package/lib/gc/index.d.ts +13 -0
  224. package/lib/gc/index.d.ts.map +1 -0
  225. package/lib/gc/index.js +12 -0
  226. package/lib/gc/index.js.map +1 -0
  227. package/lib/index.d.ts +3 -7
  228. package/lib/index.d.ts.map +1 -1
  229. package/lib/index.js +1 -4
  230. package/lib/index.js.map +1 -1
  231. package/lib/opLifecycle/batchManager.d.ts +11 -13
  232. package/lib/opLifecycle/batchManager.d.ts.map +1 -1
  233. package/lib/opLifecycle/batchManager.js +24 -37
  234. package/lib/opLifecycle/batchManager.js.map +1 -1
  235. package/lib/opLifecycle/definitions.d.ts +4 -0
  236. package/lib/opLifecycle/definitions.d.ts.map +1 -1
  237. package/lib/opLifecycle/definitions.js.map +1 -1
  238. package/lib/opLifecycle/index.d.ts +2 -1
  239. package/lib/opLifecycle/index.d.ts.map +1 -1
  240. package/lib/opLifecycle/index.js +2 -1
  241. package/lib/opLifecycle/index.js.map +1 -1
  242. package/lib/opLifecycle/opCompressor.d.ts.map +1 -1
  243. package/lib/opLifecycle/opCompressor.js +26 -11
  244. package/lib/opLifecycle/opCompressor.js.map +1 -1
  245. package/lib/opLifecycle/opDecompressor.d.ts +4 -0
  246. package/lib/opLifecycle/opDecompressor.d.ts.map +1 -1
  247. package/lib/opLifecycle/opDecompressor.js +43 -4
  248. package/lib/opLifecycle/opDecompressor.js.map +1 -1
  249. package/lib/opLifecycle/opGroupingManager.d.ts +14 -0
  250. package/lib/opLifecycle/opGroupingManager.d.ts.map +1 -0
  251. package/lib/opLifecycle/opGroupingManager.js +52 -0
  252. package/lib/opLifecycle/opGroupingManager.js.map +1 -0
  253. package/lib/opLifecycle/opSplitter.d.ts +16 -4
  254. package/lib/opLifecycle/opSplitter.d.ts.map +1 -1
  255. package/lib/opLifecycle/opSplitter.js +39 -15
  256. package/lib/opLifecycle/opSplitter.js.map +1 -1
  257. package/lib/opLifecycle/outbox.d.ts +21 -3
  258. package/lib/opLifecycle/outbox.d.ts.map +1 -1
  259. package/lib/opLifecycle/outbox.js +92 -53
  260. package/lib/opLifecycle/outbox.js.map +1 -1
  261. package/lib/opLifecycle/remoteMessageProcessor.d.ts +4 -2
  262. package/lib/opLifecycle/remoteMessageProcessor.d.ts.map +1 -1
  263. package/lib/opLifecycle/remoteMessageProcessor.js +30 -20
  264. package/lib/opLifecycle/remoteMessageProcessor.js.map +1 -1
  265. package/lib/packageVersion.d.ts +1 -1
  266. package/lib/packageVersion.js +1 -1
  267. package/lib/packageVersion.js.map +1 -1
  268. package/lib/pendingStateManager.d.ts +3 -3
  269. package/lib/pendingStateManager.d.ts.map +1 -1
  270. package/lib/pendingStateManager.js +20 -21
  271. package/lib/pendingStateManager.js.map +1 -1
  272. package/lib/storageServiceWithAttachBlobs.d.ts +17 -0
  273. package/lib/storageServiceWithAttachBlobs.d.ts.map +1 -0
  274. package/lib/storageServiceWithAttachBlobs.js +28 -0
  275. package/lib/storageServiceWithAttachBlobs.js.map +1 -0
  276. package/lib/summary/index.d.ts +17 -0
  277. package/lib/summary/index.d.ts.map +1 -0
  278. package/lib/summary/index.js +16 -0
  279. package/lib/summary/index.js.map +1 -0
  280. package/lib/summary/orderedClientElection.d.ts.map +1 -0
  281. package/lib/summary/orderedClientElection.js.map +1 -0
  282. package/lib/{runWhileConnectedCoordinator.d.ts → summary/runWhileConnectedCoordinator.d.ts} +3 -2
  283. package/lib/summary/runWhileConnectedCoordinator.d.ts.map +1 -0
  284. package/lib/{runWhileConnectedCoordinator.js → summary/runWhileConnectedCoordinator.js} +5 -4
  285. package/lib/summary/runWhileConnectedCoordinator.js.map +1 -0
  286. package/{dist → lib/summary}/runningSummarizer.d.ts +23 -20
  287. package/lib/summary/runningSummarizer.d.ts.map +1 -0
  288. package/lib/{runningSummarizer.js → summary/runningSummarizer.js} +192 -75
  289. package/lib/summary/runningSummarizer.js.map +1 -0
  290. package/lib/{summarizer.d.ts → summary/summarizer.d.ts} +4 -9
  291. package/lib/summary/summarizer.d.ts.map +1 -0
  292. package/lib/{summarizer.js → summary/summarizer.js} +12 -81
  293. package/lib/summary/summarizer.js.map +1 -0
  294. package/lib/summary/summarizerClientElection.d.ts.map +1 -0
  295. package/lib/summary/summarizerClientElection.js.map +1 -0
  296. package/lib/{summarizerHeuristics.d.ts → summary/summarizerHeuristics.d.ts} +2 -1
  297. package/lib/summary/summarizerHeuristics.d.ts.map +1 -0
  298. package/lib/{summarizerHeuristics.js → summary/summarizerHeuristics.js} +6 -3
  299. package/lib/summary/summarizerHeuristics.js.map +1 -0
  300. package/lib/summary/summarizerNode/index.d.ts +8 -0
  301. package/lib/summary/summarizerNode/index.d.ts.map +1 -0
  302. package/lib/summary/summarizerNode/index.js +7 -0
  303. package/lib/summary/summarizerNode/index.js.map +1 -0
  304. package/lib/summary/summarizerNode/summarizerNode.d.ts +149 -0
  305. package/lib/summary/summarizerNode/summarizerNode.d.ts.map +1 -0
  306. package/lib/summary/summarizerNode/summarizerNode.js +526 -0
  307. package/lib/summary/summarizerNode/summarizerNode.js.map +1 -0
  308. package/lib/summary/summarizerNode/summarizerNodeUtils.d.ts +125 -0
  309. package/lib/summary/summarizerNode/summarizerNodeUtils.d.ts.map +1 -0
  310. package/lib/summary/summarizerNode/summarizerNodeUtils.js +125 -0
  311. package/lib/summary/summarizerNode/summarizerNodeUtils.js.map +1 -0
  312. package/lib/summary/summarizerNode/summarizerNodeWithGc.d.ts +148 -0
  313. package/lib/summary/summarizerNode/summarizerNodeWithGc.d.ts.map +1 -0
  314. package/lib/summary/summarizerNode/summarizerNodeWithGc.js +419 -0
  315. package/lib/summary/summarizerNode/summarizerNodeWithGc.js.map +1 -0
  316. package/{dist → lib/summary}/summarizerTypes.d.ts +21 -19
  317. package/lib/summary/summarizerTypes.d.ts.map +1 -0
  318. package/lib/summary/summarizerTypes.js +6 -0
  319. package/lib/summary/summarizerTypes.js.map +1 -0
  320. package/lib/summary/summaryCollection.d.ts.map +1 -0
  321. package/lib/summary/summaryCollection.js.map +1 -0
  322. package/{dist → lib/summary}/summaryFormat.d.ts +3 -21
  323. package/lib/summary/summaryFormat.d.ts.map +1 -0
  324. package/lib/{summaryFormat.js → summary/summaryFormat.js} +0 -8
  325. package/lib/summary/summaryFormat.js.map +1 -0
  326. package/{dist → lib/summary}/summaryGenerator.d.ts +28 -2
  327. package/lib/summary/summaryGenerator.d.ts.map +1 -0
  328. package/lib/{summaryGenerator.js → summary/summaryGenerator.js} +21 -19
  329. package/lib/summary/summaryGenerator.js.map +1 -0
  330. package/lib/{summaryManager.d.ts → summary/summaryManager.d.ts} +1 -1
  331. package/lib/summary/summaryManager.d.ts.map +1 -0
  332. package/lib/summary/summaryManager.js.map +1 -0
  333. package/package.json +66 -60
  334. package/src/blobManager.ts +196 -110
  335. package/src/containerRuntime.ts +491 -391
  336. package/src/dataStoreContext.ts +140 -49
  337. package/src/dataStores.ts +139 -41
  338. package/src/deltaManagerSummarizerProxy.ts +46 -0
  339. package/{garbageCollection.md → src/gc/garbageCollection.md} +2 -2
  340. package/src/{garbageCollection.ts → gc/garbageCollection.ts} +245 -890
  341. package/src/gc/gcConfigs.ts +193 -0
  342. package/src/gc/gcDefinitions.ts +387 -0
  343. package/src/gc/gcHelpers.ts +335 -0
  344. package/src/gc/gcReferenceGraphAlgorithm.ts +52 -0
  345. package/src/gc/gcSummaryDefinitions.ts +54 -0
  346. package/src/gc/gcSummaryStateTracker.ts +329 -0
  347. package/src/{gcSweepReadyUsageDetection.ts → gc/gcSweepReadyUsageDetection.ts} +1 -1
  348. package/src/gc/gcUnreferencedStateTracker.ts +114 -0
  349. package/src/gc/index.ts +65 -0
  350. package/src/index.ts +10 -22
  351. package/src/opLifecycle/README.md +263 -0
  352. package/src/opLifecycle/batchManager.ts +26 -55
  353. package/src/opLifecycle/definitions.ts +4 -0
  354. package/src/opLifecycle/index.ts +2 -1
  355. package/src/opLifecycle/opCompressor.ts +32 -12
  356. package/src/opLifecycle/opDecompressor.ts +50 -5
  357. package/src/opLifecycle/opGroupingManager.ts +78 -0
  358. package/src/opLifecycle/opSplitter.ts +56 -17
  359. package/src/opLifecycle/outbox.ts +126 -62
  360. package/src/opLifecycle/remoteMessageProcessor.ts +38 -22
  361. package/src/packageVersion.ts +1 -1
  362. package/src/pendingStateManager.ts +34 -27
  363. package/src/storageServiceWithAttachBlobs.ts +38 -0
  364. package/src/summary/index.ts +105 -0
  365. package/src/{runWhileConnectedCoordinator.ts → summary/runWhileConnectedCoordinator.ts} +7 -7
  366. package/src/{runningSummarizer.ts → summary/runningSummarizer.ts} +318 -156
  367. package/src/{summarizer.ts → summary/summarizer.ts} +12 -105
  368. package/src/{summarizerHeuristics.ts → summary/summarizerHeuristics.ts} +13 -4
  369. package/src/summary/summarizerNode/index.ts +12 -0
  370. package/src/summary/summarizerNode/summarizerNode.ts +766 -0
  371. package/src/summary/summarizerNode/summarizerNodeUtils.ts +214 -0
  372. package/src/summary/summarizerNode/summarizerNodeWithGc.ts +644 -0
  373. package/src/{summarizerTypes.ts → summary/summarizerTypes.ts} +28 -25
  374. package/src/{summaryFormat.ts → summary/summaryFormat.ts} +3 -29
  375. package/src/{summaryGenerator.ts → summary/summaryGenerator.ts} +34 -27
  376. package/src/{summaryManager.ts → summary/summaryManager.ts} +1 -1
  377. package/dist/garbageCollection.d.ts +0 -411
  378. package/dist/garbageCollection.d.ts.map +0 -1
  379. package/dist/garbageCollection.js.map +0 -1
  380. package/dist/garbageCollectionConstants.d.ts +0 -23
  381. package/dist/garbageCollectionConstants.d.ts.map +0 -1
  382. package/dist/garbageCollectionConstants.js +0 -36
  383. package/dist/garbageCollectionConstants.js.map +0 -1
  384. package/dist/garbageCollectionHelpers.d.ts +0 -15
  385. package/dist/garbageCollectionHelpers.d.ts.map +0 -1
  386. package/dist/garbageCollectionHelpers.js +0 -27
  387. package/dist/garbageCollectionHelpers.js.map +0 -1
  388. package/dist/gcSweepReadyUsageDetection.d.ts.map +0 -1
  389. package/dist/gcSweepReadyUsageDetection.js.map +0 -1
  390. package/dist/orderedClientElection.d.ts.map +0 -1
  391. package/dist/orderedClientElection.js.map +0 -1
  392. package/dist/runWhileConnectedCoordinator.d.ts.map +0 -1
  393. package/dist/runWhileConnectedCoordinator.js.map +0 -1
  394. package/dist/runningSummarizer.d.ts.map +0 -1
  395. package/dist/runningSummarizer.js.map +0 -1
  396. package/dist/serializedSnapshotStorage.d.ts +0 -58
  397. package/dist/serializedSnapshotStorage.d.ts.map +0 -1
  398. package/dist/serializedSnapshotStorage.js +0 -110
  399. package/dist/serializedSnapshotStorage.js.map +0 -1
  400. package/dist/summarizer.d.ts.map +0 -1
  401. package/dist/summarizer.js.map +0 -1
  402. package/dist/summarizerClientElection.d.ts.map +0 -1
  403. package/dist/summarizerClientElection.js.map +0 -1
  404. package/dist/summarizerHandle.d.ts +0 -12
  405. package/dist/summarizerHandle.d.ts.map +0 -1
  406. package/dist/summarizerHandle.js +0 -22
  407. package/dist/summarizerHandle.js.map +0 -1
  408. package/dist/summarizerHeuristics.d.ts.map +0 -1
  409. package/dist/summarizerHeuristics.js.map +0 -1
  410. package/dist/summarizerTypes.d.ts.map +0 -1
  411. package/dist/summarizerTypes.js.map +0 -1
  412. package/dist/summaryCollection.d.ts.map +0 -1
  413. package/dist/summaryCollection.js.map +0 -1
  414. package/dist/summaryFormat.d.ts.map +0 -1
  415. package/dist/summaryFormat.js.map +0 -1
  416. package/dist/summaryGenerator.d.ts.map +0 -1
  417. package/dist/summaryGenerator.js.map +0 -1
  418. package/dist/summaryManager.d.ts.map +0 -1
  419. package/dist/summaryManager.js.map +0 -1
  420. package/lib/garbageCollection.d.ts +0 -411
  421. package/lib/garbageCollection.d.ts.map +0 -1
  422. package/lib/garbageCollection.js.map +0 -1
  423. package/lib/garbageCollectionConstants.d.ts +0 -23
  424. package/lib/garbageCollectionConstants.d.ts.map +0 -1
  425. package/lib/garbageCollectionConstants.js +0 -33
  426. package/lib/garbageCollectionConstants.js.map +0 -1
  427. package/lib/garbageCollectionHelpers.d.ts +0 -15
  428. package/lib/garbageCollectionHelpers.d.ts.map +0 -1
  429. package/lib/garbageCollectionHelpers.js +0 -23
  430. package/lib/garbageCollectionHelpers.js.map +0 -1
  431. package/lib/gcSweepReadyUsageDetection.d.ts.map +0 -1
  432. package/lib/gcSweepReadyUsageDetection.js.map +0 -1
  433. package/lib/orderedClientElection.d.ts.map +0 -1
  434. package/lib/orderedClientElection.js.map +0 -1
  435. package/lib/runWhileConnectedCoordinator.d.ts.map +0 -1
  436. package/lib/runWhileConnectedCoordinator.js.map +0 -1
  437. package/lib/runningSummarizer.d.ts.map +0 -1
  438. package/lib/runningSummarizer.js.map +0 -1
  439. package/lib/serializedSnapshotStorage.d.ts +0 -58
  440. package/lib/serializedSnapshotStorage.d.ts.map +0 -1
  441. package/lib/serializedSnapshotStorage.js +0 -106
  442. package/lib/serializedSnapshotStorage.js.map +0 -1
  443. package/lib/summarizer.d.ts.map +0 -1
  444. package/lib/summarizer.js.map +0 -1
  445. package/lib/summarizerClientElection.d.ts.map +0 -1
  446. package/lib/summarizerClientElection.js.map +0 -1
  447. package/lib/summarizerHandle.d.ts +0 -12
  448. package/lib/summarizerHandle.d.ts.map +0 -1
  449. package/lib/summarizerHandle.js +0 -18
  450. package/lib/summarizerHandle.js.map +0 -1
  451. package/lib/summarizerHeuristics.d.ts.map +0 -1
  452. package/lib/summarizerHeuristics.js.map +0 -1
  453. package/lib/summarizerTypes.d.ts.map +0 -1
  454. package/lib/summarizerTypes.js +0 -9
  455. package/lib/summarizerTypes.js.map +0 -1
  456. package/lib/summaryCollection.d.ts.map +0 -1
  457. package/lib/summaryCollection.js.map +0 -1
  458. package/lib/summaryFormat.d.ts.map +0 -1
  459. package/lib/summaryFormat.js.map +0 -1
  460. package/lib/summaryGenerator.d.ts.map +0 -1
  461. package/lib/summaryGenerator.js.map +0 -1
  462. package/lib/summaryManager.d.ts.map +0 -1
  463. package/lib/summaryManager.js.map +0 -1
  464. package/src/garbageCollectionConstants.ts +0 -38
  465. package/src/garbageCollectionHelpers.ts +0 -37
  466. package/src/serializedSnapshotStorage.ts +0 -151
  467. package/src/summarizerHandle.ts +0 -23
  468. /package/dist/{gcSweepReadyUsageDetection.d.ts → gc/gcSweepReadyUsageDetection.d.ts} +0 -0
  469. /package/dist/{orderedClientElection.d.ts → summary/orderedClientElection.d.ts} +0 -0
  470. /package/dist/{orderedClientElection.js → summary/orderedClientElection.js} +0 -0
  471. /package/dist/{summarizerClientElection.d.ts → summary/summarizerClientElection.d.ts} +0 -0
  472. /package/dist/{summarizerClientElection.js → summary/summarizerClientElection.js} +0 -0
  473. /package/dist/{summaryCollection.d.ts → summary/summaryCollection.d.ts} +0 -0
  474. /package/dist/{summaryCollection.js → summary/summaryCollection.js} +0 -0
  475. /package/dist/{summaryManager.js → summary/summaryManager.js} +0 -0
  476. /package/lib/{gcSweepReadyUsageDetection.d.ts → gc/gcSweepReadyUsageDetection.d.ts} +0 -0
  477. /package/lib/{orderedClientElection.d.ts → summary/orderedClientElection.d.ts} +0 -0
  478. /package/lib/{orderedClientElection.js → summary/orderedClientElection.js} +0 -0
  479. /package/lib/{summarizerClientElection.d.ts → summary/summarizerClientElection.d.ts} +0 -0
  480. /package/lib/{summarizerClientElection.js → summary/summarizerClientElection.js} +0 -0
  481. /package/lib/{summaryCollection.d.ts → summary/summaryCollection.d.ts} +0 -0
  482. /package/lib/{summaryCollection.js → summary/summaryCollection.js} +0 -0
  483. /package/lib/{summaryManager.js → summary/summaryManager.js} +0 -0
  484. /package/src/{orderedClientElection.ts → summary/orderedClientElection.ts} +0 -0
  485. /package/src/{summarizerClientElection.ts → summary/summarizerClientElection.ts} +0 -0
  486. /package/src/{summaryCollection.ts → summary/summaryCollection.ts} +0 -0
@@ -17,7 +17,6 @@ import {
17
17
  } from "@fluidframework/core-interfaces";
18
18
  import {
19
19
  IAudience,
20
- IFluidTokenProvider,
21
20
  IContainerContext,
22
21
  IDeltaManager,
23
22
  IRuntime,
@@ -25,13 +24,18 @@ import {
25
24
  AttachState,
26
25
  ILoaderOptions,
27
26
  LoaderHeader,
28
- ISnapshotTreeWithBlobContents,
29
27
  } from "@fluidframework/container-definitions";
30
28
  import {
31
29
  IContainerRuntime,
32
30
  IContainerRuntimeEvents,
33
31
  } from "@fluidframework/container-runtime-definitions";
34
- import { assert, Trace, TypedEventEmitter, unreachableCase } from "@fluidframework/common-utils";
32
+ import {
33
+ assert,
34
+ LazyPromise,
35
+ Trace,
36
+ TypedEventEmitter,
37
+ unreachableCase,
38
+ } from "@fluidframework/common-utils";
35
39
  import {
36
40
  ChildLogger,
37
41
  raiseConnectedEvent,
@@ -68,13 +72,13 @@ import {
68
72
  } from "@fluidframework/protocol-definitions";
69
73
  import {
70
74
  FlushMode,
75
+ FlushModeExperimental,
71
76
  gcTreeKey,
72
77
  InboundAttachMessage,
73
78
  IFluidDataStoreContextDetached,
74
79
  IFluidDataStoreRegistry,
75
80
  IFluidDataStoreChannel,
76
81
  IGarbageCollectionData,
77
- IGarbageCollectionDetailsBase,
78
82
  IEnvelope,
79
83
  IInboundSignalMessage,
80
84
  ISignalEnvelope,
@@ -92,23 +96,19 @@ import {
92
96
  addBlobToSummary,
93
97
  addSummarizeResultToSummary,
94
98
  addTreeToSummary,
95
- createRootSummarizerNodeWithGC,
96
- IRootSummarizerNodeWithGC,
97
99
  RequestParser,
98
100
  create404Response,
99
101
  exceptionToResponse,
102
+ GCDataBuilder,
100
103
  requestFluidObject,
101
- responseToException,
102
104
  seqFromTree,
103
105
  calculateStats,
104
106
  TelemetryContext,
107
+ ReadAndParseBlob,
105
108
  } from "@fluidframework/runtime-utils";
106
- import { GCDataBuilder, trimLeadingAndTrailingSlashes } from "@fluidframework/garbage-collector";
107
109
  import { v4 as uuid } from "uuid";
108
110
  import { ContainerFluidHandleContext } from "./containerHandleContext";
109
111
  import { FluidDataStoreRegistry } from "./dataStoreRegistry";
110
- import { Summarizer } from "./summarizer";
111
- import { SummaryManager } from "./summaryManager";
112
112
  import { ReportOpPerfTelemetry, IPerfSignalReport } from "./connectionTelemetry";
113
113
  import { IPendingLocalState, PendingStateManager } from "./pendingStateManager";
114
114
  import { pkgVersion } from "./packageVersion";
@@ -118,22 +118,24 @@ import {
118
118
  aliasBlobName,
119
119
  blobsTreeName,
120
120
  chunksBlobName,
121
+ createRootSummarizerNodeWithGC,
121
122
  electedSummarizerBlobName,
122
123
  extractSummaryMetadataMessage,
123
124
  IContainerRuntimeMetadata,
124
125
  ICreateContainerMetadata,
126
+ IFetchSnapshotResult,
127
+ IRootSummarizerNodeWithGC,
125
128
  ISummaryMetadataMessage,
126
129
  metadataBlobName,
130
+ Summarizer,
131
+ SummaryManager,
127
132
  wrapSummaryInChannelsTree,
128
- } from "./summaryFormat";
129
- import { SummaryCollection } from "./summaryCollection";
130
- import {
133
+ SummaryCollection,
131
134
  ISerializedElection,
132
135
  OrderedClientCollection,
133
136
  OrderedClientElection,
134
- } from "./orderedClientElection";
135
- import { SummarizerClientElection, summarizerClientType } from "./summarizerClientElection";
136
- import {
137
+ SummarizerClientElection,
138
+ summarizerClientType,
137
139
  SubmitSummaryResult,
138
140
  IConnectableRuntime,
139
141
  IGeneratedSummaryStats,
@@ -142,22 +144,21 @@ import {
142
144
  ISummarizerInternalsProvider,
143
145
  ISummarizerRuntime,
144
146
  IRefreshSummaryAckOptions,
145
- } from "./summarizerTypes";
147
+ RunWhileConnectedCoordinator,
148
+ } from "./summary";
146
149
  import { formExponentialFn, Throttler } from "./throttler";
147
- import { RunWhileConnectedCoordinator } from "./runWhileConnectedCoordinator";
148
150
  import {
149
151
  GarbageCollector,
150
152
  GCNodeType,
151
- IGarbageCollectionRuntime,
153
+ gcTombstoneGenerationOptionName,
152
154
  IGarbageCollector,
155
+ IGCRuntimeOptions,
153
156
  IGCStats,
154
- } from "./garbageCollection";
157
+ shouldAllowGcTombstoneEnforcement,
158
+ trimLeadingAndTrailingSlashes,
159
+ } from "./gc";
155
160
  import { channelToDataStore, IDataStoreAliasMessage, isDataStoreAliasMessage } from "./dataStore";
156
161
  import { BindBatchTracker } from "./batchTracker";
157
- import {
158
- ISerializedBaseSnapshotBlobs,
159
- SerializedSnapshotStorage,
160
- } from "./serializedSnapshotStorage";
161
162
  import { ScheduleManager } from "./scheduleManager";
162
163
  import {
163
164
  BatchMessage,
@@ -167,7 +168,9 @@ import {
167
168
  Outbox,
168
169
  OpSplitter,
169
170
  RemoteMessageProcessor,
171
+ OpGroupingManager,
170
172
  } from "./opLifecycle";
173
+ import { DeltaManagerSummarizerProxy } from "./deltaManagerSummarizerProxy";
171
174
 
172
175
  export enum ContainerMessageType {
173
176
  // An op to be delivered to store
@@ -203,7 +206,7 @@ export interface ISummaryBaseConfiguration {
203
206
  /**
204
207
  * Defines the maximum allowed time to wait for a pending summary ack.
205
208
  * The maximum amount of time client will wait for a summarize is the minimum of
206
- * maxSummarizeAckWaitTime (currently 10 * 60 * 1000) and maxAckWaitTime.
209
+ * maxSummarizeAckWaitTime (currently 3 * 60 * 1000) and maxAckWaitTime.
207
210
  */
208
211
  maxAckWaitTime: number;
209
212
  /**
@@ -299,7 +302,7 @@ export const DefaultSummaryConfiguration: ISummaryConfiguration = {
299
302
 
300
303
  minOpsForLastSummaryAttempt: 10,
301
304
 
302
- maxAckWaitTime: 10 * 60 * 1000, // 10 mins.
305
+ maxAckWaitTime: 3 * 60 * 1000, // 3 mins.
303
306
 
304
307
  maxOpsSinceLastSummary: 7000,
305
308
 
@@ -312,54 +315,6 @@ export const DefaultSummaryConfiguration: ISummaryConfiguration = {
312
315
  nonRuntimeHeuristicThreshold: 20,
313
316
  };
314
317
 
315
- export interface IGCRuntimeOptions {
316
- /**
317
- * Flag that if true, will enable running garbage collection (GC) for a new container.
318
- *
319
- * GC has mark phase and sweep phase. In mark phase, unreferenced objects are identified
320
- * and marked as such in the summary. This option enables the mark phase.
321
- * In sweep phase, unreferenced objects are eventually deleted from the container if they meet certain conditions.
322
- * Sweep phase can be enabled via the "sweepAllowed" option.
323
- *
324
- * Note: This setting is persisted in the container's summary and cannot be changed.
325
- */
326
- gcAllowed?: boolean;
327
-
328
- /**
329
- * Flag that if true, enables GC's sweep phase for a new container.
330
- *
331
- * This will allow GC to eventually delete unreferenced objects from the container.
332
- * This flag should only be set to true if "gcAllowed" is true.
333
- *
334
- * Note: This setting is persisted in the container's summary and cannot be changed.
335
- */
336
- sweepAllowed?: boolean;
337
-
338
- /**
339
- * Flag that if true, will disable garbage collection for the session.
340
- * Can be used to disable running GC on containers where it is allowed via the gcAllowed option.
341
- */
342
- disableGC?: boolean;
343
-
344
- /**
345
- * Flag that will bypass optimizations and generate GC data for all nodes irrespective of whether a node
346
- * changed or not.
347
- */
348
- runFullGC?: boolean;
349
-
350
- /**
351
- * Maximum session duration for a new container. If not present, a default value will be used.
352
- *
353
- * Note: This setting is persisted in the container's summary and cannot be changed.
354
- */
355
- sessionExpiryTimeoutMs?: number;
356
-
357
- /**
358
- * Allows additional GC options to be passed.
359
- */
360
- [key: string]: any;
361
- }
362
-
363
318
  export interface ISummaryRuntimeOptions {
364
319
  /** Override summary configurations set by the server. */
365
320
  summaryConfigOverrides?: ISummaryConfiguration;
@@ -411,10 +366,6 @@ export interface IContainerRuntimeOptions {
411
366
  * By default, flush mode is TurnBased.
412
367
  */
413
368
  readonly flushMode?: FlushMode;
414
- /**
415
- * Save enough runtime state to be able to serialize upon request and load to the same state in a new container.
416
- */
417
- readonly enableOfflineLoad?: boolean;
418
369
  /**
419
370
  * Enables the runtime to compress ops. Compression is disabled when undefined.
420
371
  * @experimental Not ready for use.
@@ -433,7 +384,8 @@ export interface IContainerRuntimeOptions {
433
384
  readonly maxBatchSizeInBytes?: number;
434
385
  /**
435
386
  * If the op payload needs to be chunked in order to work around the maximum size of the batch, this value represents
436
- * how large the individual chunks will be. This is only supported when compression is enabled.
387
+ * how large the individual chunks will be. This is only supported when compression is enabled. If after compression, the
388
+ * batch size exceeds this value, it will be chunked into smaller ops of this size.
437
389
  *
438
390
  * If unspecified, if a batch exceeds `maxBatchSizeInBytes` after compression, the container will close with an instance
439
391
  * of `GenericError` with the `BatchTooLarge` message.
@@ -451,6 +403,17 @@ export interface IContainerRuntimeOptions {
451
403
  * can be used to disable it at runtime.
452
404
  */
453
405
  readonly enableOpReentryCheck?: boolean;
406
+ /**
407
+ * If enabled, the runtime will group messages within a batch into a single
408
+ * message to be sent to the service.
409
+ * The grouping an ungrouping of such messages is handled by the "OpGroupingManager".
410
+ *
411
+ * By default, the feature is disabled. If enabled from options, the `Fluid.ContainerRuntime.DisableGroupedBatching`
412
+ * flag can be used to disable it at runtime.
413
+ *
414
+ * @experimental Not ready for use.
415
+ */
416
+ readonly enableGroupedBatching?: boolean;
454
417
  }
455
418
 
456
419
  /**
@@ -467,11 +430,6 @@ export interface IRootSummaryTreeWithStats extends ISummaryTreeWithStats {
467
430
  export enum RuntimeHeaders {
468
431
  /** True to wait for a data store to be created and loaded before returning it. */
469
432
  wait = "wait",
470
- /**
471
- * True if the request is from an external app. Used for GC to handle scenarios where a data store
472
- * is deleted and requested via an external app.
473
- */
474
- externalRequest = "externalRequest",
475
433
  /** True if the request is coming from an IFluidHandle. */
476
434
  viaHandle = "viaHandle",
477
435
  }
@@ -487,7 +445,6 @@ export const TombstoneResponseHeaderKey = "isTombstoned";
487
445
  */
488
446
  export interface RuntimeHeaderData {
489
447
  wait?: boolean;
490
- externalRequest?: boolean;
491
448
  viaHandle?: boolean;
492
449
  allowTombstone?: boolean;
493
450
  }
@@ -495,7 +452,6 @@ export interface RuntimeHeaderData {
495
452
  /** Default values for Runtime Headers */
496
453
  export const defaultRuntimeHeaderData: Required<RuntimeHeaderData> = {
497
454
  wait: true,
498
- externalRequest: false,
499
455
  viaHandle: false,
500
456
  allowTombstone: false,
501
457
  };
@@ -532,21 +488,6 @@ interface IPendingRuntimeState {
532
488
  * Pending blobs from BlobManager
533
489
  */
534
490
  pendingAttachmentBlobs?: IPendingBlobs;
535
- /**
536
- * A base snapshot at a sequence number prior to the first pending op
537
- */
538
- baseSnapshot: ISnapshotTree;
539
- /**
540
- * Serialized blobs from the base snapshot. Used to load offline since
541
- * storage is not available.
542
- */
543
- snapshotBlobs: ISerializedBaseSnapshotBlobs;
544
- /**
545
- * All runtime ops since base snapshot sequence number up to the latest op
546
- * seen when the container was closed. Used to apply stashed (saved pending)
547
- * ops at the same sequence number at which they were made.
548
- */
549
- savedOps: ISequencedDocumentMessage[];
550
491
  }
551
492
 
552
493
  const maxConsecutiveReconnectsKey = "Fluid.ContainerRuntime.MaxConsecutiveReconnects";
@@ -557,7 +498,15 @@ const defaultFlushMode = FlushMode.TurnBased;
557
498
  // We can't estimate it fully, as we
558
499
  // - do not know what properties relay service will add
559
500
  // - we do not stringify final op, thus we do not know how much escaping will be added.
560
- const defaultMaxBatchSizeInBytes = 950 * 1024;
501
+ const defaultMaxBatchSizeInBytes = 700 * 1024;
502
+
503
+ const defaultCompressionConfig = {
504
+ // Batches with content size exceeding this value will be compressed
505
+ minimumBatchSizeInBytes: 614400,
506
+ compressionAlgorithm: CompressionAlgorithms.lz4,
507
+ };
508
+
509
+ const defaultChunkSizeInBytes = 204800;
561
510
 
562
511
  /**
563
512
  * @deprecated - use ContainerRuntimeMessage instead
@@ -605,12 +554,7 @@ export function getDeviceSpec() {
605
554
  */
606
555
  export class ContainerRuntime
607
556
  extends TypedEventEmitter<IContainerRuntimeEvents>
608
- implements
609
- IContainerRuntime,
610
- IGarbageCollectionRuntime,
611
- IRuntime,
612
- ISummarizerRuntime,
613
- ISummarizerInternalsProvider
557
+ implements IContainerRuntime, IRuntime, ISummarizerRuntime, ISummarizerInternalsProvider
614
558
  {
615
559
  public get IContainerRuntime() {
616
560
  return this;
@@ -658,13 +602,16 @@ export class ContainerRuntime
658
602
  * Load the stores from a snapshot and returns the runtime.
659
603
  * @param params - An object housing the runtime properties:
660
604
  * - context - Context of the container.
661
- * - registryEntries - Mapping to the stores.
662
- * - existing - When loading from an existing snapshot
663
- * - requestHandler - Request handlers for the container runtime
605
+ * - registryEntries - Mapping from data store types to their corresponding factories.
606
+ * - existing - Pass 'true' if loading from an existing snapshot.
607
+ * - requestHandler - (optional) Request handler for the request() method of the container runtime.
608
+ * Only relevant for back-compat while we remove the request() method and move fully to entryPoint as the main pattern.
664
609
  * - runtimeOptions - Additional options to be passed to the runtime
665
610
  * - containerScope - runtime services provided with context
666
611
  * - containerRuntimeCtor - Constructor to use to create the ContainerRuntime instance.
667
612
  * This allows mixin classes to leverage this method to define their own async initializer.
613
+ * - initializeEntryPoint - Promise that resolves to an object which will act as entryPoint for the Container.
614
+ * This object should provide all the functionality that the Container is expected to provide to the loader layer.
668
615
  */
669
616
  public static async loadRuntime(params: {
670
617
  context: IContainerContext;
@@ -674,6 +621,7 @@ export class ContainerRuntime
674
621
  runtimeOptions?: IContainerRuntimeOptions;
675
622
  containerScope?: FluidObject;
676
623
  containerRuntimeCtor?: typeof ContainerRuntime;
624
+ initializeEntryPoint?: (containerRuntime: IContainerRuntime) => Promise<FluidObject>;
677
625
  }): Promise<ContainerRuntime> {
678
626
  const {
679
627
  context,
@@ -683,6 +631,7 @@ export class ContainerRuntime
683
631
  runtimeOptions = {},
684
632
  containerScope = {},
685
633
  containerRuntimeCtor = ContainerRuntime,
634
+ initializeEntryPoint,
686
635
  } = params;
687
636
 
688
637
  // If taggedLogger exists, use it. Otherwise, wrap the vanilla logger:
@@ -702,34 +651,25 @@ export class ContainerRuntime
702
651
  gcOptions = {},
703
652
  loadSequenceNumberVerification = "close",
704
653
  flushMode = defaultFlushMode,
705
- enableOfflineLoad = false,
706
- compressionOptions = {
707
- minimumBatchSizeInBytes: Number.POSITIVE_INFINITY,
708
- compressionAlgorithm: CompressionAlgorithms.lz4,
709
- },
654
+ compressionOptions = defaultCompressionConfig,
710
655
  maxBatchSizeInBytes = defaultMaxBatchSizeInBytes,
711
- chunkSizeInBytes = Number.POSITIVE_INFINITY,
656
+ chunkSizeInBytes = defaultChunkSizeInBytes,
712
657
  enableOpReentryCheck = false,
658
+ enableGroupedBatching = false,
713
659
  } = runtimeOptions;
714
660
 
715
- const pendingRuntimeState = context.pendingLocalState as IPendingRuntimeState | undefined;
716
- const baseSnapshot: ISnapshotTree | undefined =
717
- pendingRuntimeState?.baseSnapshot ?? context.baseSnapshot;
718
- const storage = !pendingRuntimeState
719
- ? context.storage
720
- : new SerializedSnapshotStorage(() => {
721
- return context.storage;
722
- }, pendingRuntimeState.snapshotBlobs);
723
-
724
661
  const registry = new FluidDataStoreRegistry(registryEntries);
725
662
 
726
663
  const tryFetchBlob = async <T>(blobName: string): Promise<T | undefined> => {
727
- const blobId = baseSnapshot?.blobs[blobName];
728
- if (baseSnapshot && blobId) {
664
+ const blobId = context.baseSnapshot?.blobs[blobName];
665
+ if (context.baseSnapshot && blobId) {
729
666
  // IContainerContext storage api return type still has undefined in 0.39 package version.
730
667
  // So once we release 0.40 container-defn package we can remove this check.
731
- assert(storage !== undefined, 0x1f5 /* "Attached state should have storage" */);
732
- return readAndParse<T>(storage, blobId);
668
+ assert(
669
+ context.storage !== undefined,
670
+ 0x1f5 /* "Attached state should have storage" */,
671
+ );
672
+ return readAndParse<T>(context.storage, blobId);
733
673
  }
734
674
  };
735
675
 
@@ -744,22 +684,22 @@ export class ContainerRuntime
744
684
 
745
685
  // read snapshot blobs needed for BlobManager to load
746
686
  const blobManagerSnapshot = await BlobManager.load(
747
- baseSnapshot?.trees[blobsTreeName],
687
+ context.baseSnapshot?.trees[blobsTreeName],
748
688
  async (id) => {
749
689
  // IContainerContext storage api return type still has undefined in 0.39 package version.
750
690
  // So once we release 0.40 container-defn package we can remove this check.
751
691
  assert(
752
- storage !== undefined,
692
+ context.storage !== undefined,
753
693
  0x256 /* "storage undefined in attached container" */,
754
694
  );
755
- return readAndParse(storage, id);
695
+ return readAndParse(context.storage, id);
756
696
  },
757
697
  );
758
698
 
759
699
  // Verify summary runtime sequence number matches protocol sequence number.
760
700
  const runtimeSequenceNumber = metadata?.message?.sequenceNumber;
761
701
  // When we load with pending state, we reuse an old snapshot so we don't expect these numbers to match
762
- if (!pendingRuntimeState && runtimeSequenceNumber !== undefined) {
702
+ if (!context.pendingLocalState && runtimeSequenceNumber !== undefined) {
763
703
  const protocolSequenceNumber = context.deltaManager.initialSequenceNumber;
764
704
  // Unless bypass is explicitly set, then take action when sequence numbers mismatch.
765
705
  if (
@@ -795,25 +735,25 @@ export class ContainerRuntime
795
735
  gcOptions,
796
736
  loadSequenceNumberVerification,
797
737
  flushMode,
798
- enableOfflineLoad,
799
738
  compressionOptions,
800
739
  maxBatchSizeInBytes,
801
740
  chunkSizeInBytes,
802
741
  enableOpReentryCheck,
742
+ enableGroupedBatching,
803
743
  },
804
744
  containerScope,
805
745
  logger,
806
746
  loadExisting,
807
747
  blobManagerSnapshot,
808
- storage,
748
+ context.storage,
809
749
  requestHandler,
750
+ undefined, // summaryConfiguration
751
+ initializeEntryPoint,
810
752
  );
811
753
 
812
- if (pendingRuntimeState) {
813
- await runtime.processSavedOps(pendingRuntimeState);
814
- // delete these once runtime has seen them to save space
815
- pendingRuntimeState.savedOps = [];
816
- }
754
+ // It's possible to have ops with a reference sequence number of 0. Op sequence numbers start
755
+ // at 1, so we won't see a replayed saved op with a sequence number of 0.
756
+ await runtime.pendingStateManager.applyStashedOpsAt(0);
817
757
 
818
758
  // Initialize the base state of the runtime before it's returned.
819
759
  await runtime.initializeBaseState();
@@ -833,10 +773,6 @@ export class ContainerRuntime
833
773
  return this.context.clientDetails;
834
774
  }
835
775
 
836
- public get deltaManager(): IDeltaManager<ISequencedDocumentMessage, IDocumentMessage> {
837
- return this.context.deltaManager;
838
- }
839
-
840
776
  public get storage(): IDocumentStorageService {
841
777
  return this._storage;
842
778
  }
@@ -885,6 +821,19 @@ export class ContainerRuntime
885
821
  }
886
822
  private readonly handleContext: ContainerFluidHandleContext;
887
823
 
824
+ /**
825
+ * This is a proxy to the delta manager provided by the container context (innerDeltaManager). It restricts certain
826
+ * accesses such as sets "read-only" mode for the summarizer client. This is the default delta manager that should
827
+ * be used unless the innerDeltaManager is required.
828
+ */
829
+ public readonly deltaManager: IDeltaManager<ISequencedDocumentMessage, IDocumentMessage>;
830
+ /**
831
+ * The delta manager provided by the container context. By default, using the default delta manager (proxy)
832
+ * should be sufficient. This should be used only if necessary. For example, for validating and propagating connected
833
+ * events which requires access to the actual real only info, this is needed.
834
+ */
835
+ private readonly innerDeltaManager: IDeltaManager<ISequencedDocumentMessage, IDocumentMessage>;
836
+
888
837
  // internal logger for ContainerRuntime. Use this.logger for stores, summaries, etc.
889
838
  private readonly mc: MonitoringContext;
890
839
 
@@ -904,13 +853,10 @@ export class ContainerRuntime
904
853
 
905
854
  private _orderSequentiallyCalls: number = 0;
906
855
  private readonly _flushMode: FlushMode;
907
- private flushMicroTaskExists = false;
856
+ private flushTaskExists = false;
908
857
 
909
858
  private _connected: boolean;
910
859
 
911
- private readonly savedOps: ISequencedDocumentMessage[] = [];
912
- private baseSnapshotBlobs?: ISerializedBaseSnapshotBlobs;
913
-
914
860
  private consecutiveReconnects = 0;
915
861
 
916
862
  /**
@@ -963,6 +909,7 @@ export class ContainerRuntime
963
909
  private dirtyContainer: boolean;
964
910
  private emitDirtyDocumentEvent = true;
965
911
  private readonly enableOpReentryCheck: boolean;
912
+ private readonly disableAttachReorder: boolean | undefined;
966
913
 
967
914
  private readonly defaultTelemetrySignalSampleCount = 100;
968
915
  private _perfSignalData: IPerfSignalReport = {
@@ -982,7 +929,6 @@ export class ContainerRuntime
982
929
  private readonly blobManager: BlobManager;
983
930
  private readonly pendingStateManager: PendingStateManager;
984
931
  private readonly outbox: Outbox;
985
-
986
932
  private readonly garbageCollector: IGarbageCollector;
987
933
 
988
934
  private readonly dataStores: DataStores;
@@ -1032,6 +978,17 @@ export class ContainerRuntime
1032
978
  */
1033
979
  private nextSummaryNumber: number;
1034
980
 
981
+ /**
982
+ * If false, loading or using a Tombstoned object should merely log, not fail
983
+ */
984
+ public readonly gcTombstoneEnforcementAllowed: boolean;
985
+
986
+ /**
987
+ * GUID to identify a document in telemetry
988
+ * ! Note: should not be used for anything other than telemetry and is not considered a stable GUID
989
+ */
990
+ private readonly telemetryDocumentId: string;
991
+
1035
992
  /**
1036
993
  * @internal
1037
994
  */
@@ -1058,9 +1015,13 @@ export class ContainerRuntime
1058
1015
  // the runtime configuration overrides
1059
1016
  ...runtimeOptions.summaryOptions?.summaryConfigOverrides,
1060
1017
  },
1018
+ initializeEntryPoint?: (containerRuntime: IContainerRuntime) => Promise<FluidObject>,
1061
1019
  ) {
1062
1020
  super();
1063
1021
 
1022
+ this.innerDeltaManager = context.deltaManager;
1023
+ this.deltaManager = new DeltaManagerSummarizerProxy(context.deltaManager);
1024
+
1064
1025
  let loadSummaryNumber: number;
1065
1026
  // Get the container creation metadata. For new container, we initialize these. For existing containers,
1066
1027
  // get the values from the metadata blob.
@@ -1085,18 +1046,46 @@ export class ContainerRuntime
1085
1046
 
1086
1047
  this._connected = this.context.connected;
1087
1048
 
1049
+ this.gcTombstoneEnforcementAllowed = shouldAllowGcTombstoneEnforcement(
1050
+ metadata?.gcFeatureMatrix?.tombstoneGeneration /* persisted */,
1051
+ this.runtimeOptions.gcOptions[gcTombstoneGenerationOptionName] /* current */,
1052
+ );
1053
+
1088
1054
  this.mc = loggerToMonitoringContext(ChildLogger.create(this.logger, "ContainerRuntime"));
1089
1055
 
1056
+ this.mc.logger.sendTelemetryEvent({
1057
+ eventName: "GCFeatureMatrix",
1058
+ metadataValue: JSON.stringify(metadata?.gcFeatureMatrix),
1059
+ inputs: JSON.stringify({
1060
+ gcOptions_gcTombstoneGeneration:
1061
+ this.runtimeOptions.gcOptions[gcTombstoneGenerationOptionName],
1062
+ }),
1063
+ });
1064
+
1065
+ this.telemetryDocumentId = metadata?.telemetryDocumentId ?? uuid();
1066
+
1067
+ this.disableAttachReorder = this.mc.config.getBoolean(
1068
+ "Fluid.ContainerRuntime.disableAttachOpReorder",
1069
+ );
1070
+ const disableChunking = this.mc.config.getBoolean(
1071
+ "Fluid.ContainerRuntime.CompressionChunkingDisabled",
1072
+ );
1073
+
1074
+ const opGroupingManager = new OpGroupingManager(this.groupedBatchingEnabled);
1075
+
1090
1076
  const opSplitter = new OpSplitter(
1091
1077
  chunks,
1092
1078
  this.context.submitBatchFn,
1093
- this.mc.config.getBoolean("Fluid.ContainerRuntime.DisableCompressionChunking") === true
1094
- ? Number.POSITIVE_INFINITY
1095
- : runtimeOptions.chunkSizeInBytes,
1079
+ disableChunking === true ? Number.POSITIVE_INFINITY : runtimeOptions.chunkSizeInBytes,
1096
1080
  runtimeOptions.maxBatchSizeInBytes,
1097
1081
  this.mc.logger,
1098
1082
  );
1099
- this.remoteMessageProcessor = new RemoteMessageProcessor(opSplitter, new OpDecompressor());
1083
+
1084
+ this.remoteMessageProcessor = new RemoteMessageProcessor(
1085
+ opSplitter,
1086
+ new OpDecompressor(this.mc.logger),
1087
+ opGroupingManager,
1088
+ );
1100
1089
 
1101
1090
  this.handleContext = new ContainerFluidHandleContext("", this);
1102
1091
 
@@ -1104,10 +1093,13 @@ export class ContainerRuntime
1104
1093
  this.validateSummaryHeuristicConfiguration(this.summaryConfiguration);
1105
1094
  }
1106
1095
 
1096
+ const disableOpReentryCheck = this.mc.config.getBoolean(
1097
+ "Fluid.ContainerRuntime.DisableOpReentryCheck",
1098
+ );
1107
1099
  this.enableOpReentryCheck =
1108
1100
  runtimeOptions.enableOpReentryCheck === true &&
1109
1101
  // Allow for a break-glass config to override the options
1110
- this.mc.config.getBoolean("Fluid.ContainerRuntime.DisableOpReentryCheck") !== true;
1102
+ disableOpReentryCheck !== true;
1111
1103
 
1112
1104
  this.summariesDisabled = this.isSummariesDisabled();
1113
1105
  this.heuristicsDisabled = this.isHeuristicsDisabled();
@@ -1118,11 +1110,18 @@ export class ContainerRuntime
1118
1110
  this.mc.config.getNumber(maxConsecutiveReconnectsKey) ??
1119
1111
  this.defaultMaxConsecutiveReconnects;
1120
1112
 
1121
- this._flushMode = runtimeOptions.flushMode;
1113
+ if (
1114
+ runtimeOptions.flushMode === (FlushModeExperimental.Async as unknown as FlushMode) &&
1115
+ context.supportedFeatures?.get("referenceSequenceNumbers") !== true
1116
+ ) {
1117
+ // The loader does not support reference sequence numbers, falling back on FlushMode.TurnBased
1118
+ this.mc.logger.sendErrorEvent({ eventName: "FlushModeFallback" });
1119
+ this._flushMode = FlushMode.TurnBased;
1120
+ } else {
1121
+ this._flushMode = runtimeOptions.flushMode;
1122
+ }
1122
1123
 
1123
1124
  const pendingRuntimeState = context.pendingLocalState as IPendingRuntimeState | undefined;
1124
- const baseSnapshot: ISnapshotTree | undefined =
1125
- pendingRuntimeState?.baseSnapshot ?? context.baseSnapshot;
1126
1125
 
1127
1126
  const maxSnapshotCacheDurationMs = this._storage?.policies?.maximumCacheDurationMs;
1128
1127
  if (
@@ -1138,7 +1137,7 @@ export class ContainerRuntime
1138
1137
  this.garbageCollector = GarbageCollector.create({
1139
1138
  runtime: this,
1140
1139
  gcOptions: this.runtimeOptions.gcOptions,
1141
- baseSnapshot,
1140
+ baseSnapshot: context.baseSnapshot,
1142
1141
  baseLogger: this.mc.logger,
1143
1142
  existing,
1144
1143
  metadata,
@@ -1148,7 +1147,9 @@ export class ContainerRuntime
1148
1147
  getLastSummaryTimestampMs: () => this.messageAtLastSummary?.timestamp,
1149
1148
  readAndParseBlob: async <T>(id: string) => readAndParse<T>(this.storage, id),
1150
1149
  getContainerDiagnosticId: () => this.context.id,
1151
- activeConnection: () => this.deltaManager.active,
1150
+ // GC runs in summarizer client and needs access to the real (non-proxy) active information. The proxy
1151
+ // delta manager would always return false for summarizer client.
1152
+ activeConnection: () => this.innerDeltaManager.active,
1152
1153
  });
1153
1154
 
1154
1155
  const loadedFromSequenceNumber = this.deltaManager.initialSequenceNumber;
@@ -1160,7 +1161,7 @@ export class ContainerRuntime
1160
1161
  // Latest change sequence number, no changes since summary applied yet
1161
1162
  loadedFromSequenceNumber,
1162
1163
  // Summary reference sequence number, undefined if no summary yet
1163
- baseSnapshot ? loadedFromSequenceNumber : undefined,
1164
+ context.baseSnapshot ? loadedFromSequenceNumber : undefined,
1164
1165
  {
1165
1166
  // Must set to false to prevent sending summary handle which would be pointing to
1166
1167
  // a summary with an older protocol state.
@@ -1177,19 +1178,18 @@ export class ContainerRuntime
1177
1178
  async () => this.garbageCollector.getBaseGCDetails(),
1178
1179
  );
1179
1180
 
1180
- if (baseSnapshot) {
1181
- this.summarizerNode.updateBaseSummaryState(baseSnapshot);
1181
+ if (context.baseSnapshot) {
1182
+ this.summarizerNode.updateBaseSummaryState(context.baseSnapshot);
1182
1183
  }
1183
1184
 
1184
1185
  this.dataStores = new DataStores(
1185
- getSummaryForDatastores(baseSnapshot, metadata),
1186
+ getSummaryForDatastores(context.baseSnapshot, metadata),
1186
1187
  this,
1187
1188
  (attachMsg) => this.submit(ContainerMessageType.Attach, attachMsg),
1188
1189
  (id: string, createParam: CreateChildSummarizerNodeParam) =>
1189
1190
  (
1190
1191
  summarizeInternal: SummarizeInternalFn,
1191
1192
  getGCDataFn: (fullGC?: boolean) => Promise<IGarbageCollectionData>,
1192
- getBaseGCDetailsFn?: () => Promise<IGarbageCollectionDetailsBase>,
1193
1193
  ) =>
1194
1194
  this.summarizerNode.createChild(
1195
1195
  summarizeInternal,
@@ -1197,13 +1197,12 @@ export class ContainerRuntime
1197
1197
  createParam,
1198
1198
  undefined,
1199
1199
  getGCDataFn,
1200
- getBaseGCDetailsFn,
1201
1200
  ),
1202
1201
  (id: string) => this.summarizerNode.deleteChild(id),
1203
1202
  this.mc.logger,
1204
- async () => this.garbageCollector.getBaseGCDetails(),
1205
1203
  (path: string, timestampMs: number, packagePath?: readonly string[]) =>
1206
1204
  this.garbageCollector.nodeUpdated(path, "Changed", timestampMs, packagePath),
1205
+ (path: string) => this.garbageCollector.isNodeDeleted(path),
1207
1206
  new Map<string, string>(dataStoreAliasMap),
1208
1207
  );
1209
1208
 
@@ -1213,19 +1212,23 @@ export class ContainerRuntime
1213
1212
  () => this.storage,
1214
1213
  (localId: string, blobId?: string) => {
1215
1214
  if (!this.disposed) {
1216
- this.submit(ContainerMessageType.BlobAttach, undefined, undefined, {
1217
- localId,
1218
- blobId,
1215
+ // eslint-disable-next-line @typescript-eslint/no-floating-promises
1216
+ Promise.resolve().then(() => {
1217
+ // Blob attaches need to be in their own batch (grouped batching would hide metadata)
1218
+ this.flush();
1219
+ this.submit(ContainerMessageType.BlobAttach, undefined, undefined, {
1220
+ localId,
1221
+ blobId,
1222
+ });
1223
+ this.flush();
1219
1224
  });
1220
1225
  }
1221
1226
  },
1222
1227
  (blobPath: string) => this.garbageCollector.nodeUpdated(blobPath, "Loaded"),
1223
- (fromPath: string, toPath: string) =>
1224
- this.garbageCollector.addedOutboundReference(fromPath, toPath),
1225
1228
  (blobPath: string) => this.garbageCollector.isNodeDeleted(blobPath),
1226
1229
  this,
1227
1230
  pendingRuntimeState?.pendingAttachmentBlobs,
1228
- () => this.getCurrentReferenceTimestampMs(),
1231
+ (error?: ICriticalContainerError) => this.closeFn(error),
1229
1232
  );
1230
1233
 
1231
1234
  this.scheduleManager = new ScheduleManager(
@@ -1248,14 +1251,20 @@ export class ContainerRuntime
1248
1251
  pendingRuntimeState?.pending,
1249
1252
  );
1250
1253
 
1254
+ const disableCompression = this.mc.config.getBoolean(
1255
+ "Fluid.ContainerRuntime.CompressionDisabled",
1256
+ );
1251
1257
  const compressionOptions =
1252
- this.mc.config.getBoolean("Fluid.ContainerRuntime.DisableCompression") === true
1258
+ disableCompression === true
1253
1259
  ? {
1254
1260
  minimumBatchSizeInBytes: Number.POSITIVE_INFINITY,
1255
1261
  compressionAlgorithm: CompressionAlgorithms.lz4,
1256
1262
  }
1257
1263
  : runtimeOptions.compressionOptions;
1258
1264
 
1265
+ const disablePartialFlush = this.mc.config.getBoolean(
1266
+ "Fluid.ContainerRuntime.DisablePartialFlush",
1267
+ );
1259
1268
  this.outbox = new Outbox({
1260
1269
  shouldSend: () => this.canSendOps(),
1261
1270
  pendingStateManager: this.pendingStateManager,
@@ -1265,9 +1274,10 @@ export class ContainerRuntime
1265
1274
  config: {
1266
1275
  compressionOptions,
1267
1276
  maxBatchSizeInBytes: runtimeOptions.maxBatchSizeInBytes,
1268
- enableOpReentryCheck: this.enableOpReentryCheck,
1277
+ disablePartialFlush: disablePartialFlush === true,
1269
1278
  },
1270
1279
  logger: this.mc.logger,
1280
+ groupingManager: opGroupingManager,
1271
1281
  });
1272
1282
 
1273
1283
  this.context.quorum.on("removeMember", (clientId: string) => {
@@ -1306,14 +1316,18 @@ export class ContainerRuntime
1306
1316
 
1307
1317
  if (this.context.clientDetails.type === summarizerClientType) {
1308
1318
  this._summarizer = new Summarizer(
1309
- "/_summarizer",
1310
1319
  this /* ISummarizerRuntime */,
1311
1320
  () => this.summaryConfiguration,
1312
1321
  this /* ISummarizerInternalsProvider */,
1313
1322
  this.handleContext,
1314
1323
  this.summaryCollection,
1315
1324
  async (runtime: IConnectableRuntime) =>
1316
- RunWhileConnectedCoordinator.create(runtime),
1325
+ RunWhileConnectedCoordinator.create(
1326
+ runtime,
1327
+ // Summarization runs in summarizer client and needs access to the real (non-proxy) active
1328
+ // information. The proxy delta manager would always return false for summarizer client.
1329
+ () => this.innerDeltaManager.active,
1330
+ ),
1317
1331
  );
1318
1332
  } else if (
1319
1333
  SummarizerClientElection.clientDetailsPermitElection(this.context.clientDetails)
@@ -1363,8 +1377,9 @@ export class ContainerRuntime
1363
1377
  this.deltaManager.on("readonly", (readonly: boolean) => {
1364
1378
  // we accumulate ops while being in read-only state.
1365
1379
  // once user gets write permissions and we have active connection, flush all pending ops.
1380
+ // Note that the inner (non-proxy) delta manager is needed here to get the readonly information.
1366
1381
  assert(
1367
- readonly === this.deltaManager.readOnlyInfo.readonly,
1382
+ readonly === this.innerDeltaManager.readOnlyInfo.readonly,
1368
1383
  0x124 /* "inconsistent readonly property/event state" */,
1369
1384
  );
1370
1385
 
@@ -1402,17 +1417,37 @@ export class ContainerRuntime
1402
1417
  summaryFormatVersion: metadata?.summaryFormatVersion,
1403
1418
  disableIsolatedChannels: metadata?.disableIsolatedChannels,
1404
1419
  gcVersion: metadata?.gcFeature,
1420
+ options: JSON.stringify(runtimeOptions),
1421
+ featureGates: JSON.stringify({
1422
+ disableCompression,
1423
+ disableOpReentryCheck,
1424
+ disableChunking,
1425
+ disableAttachReorder: this.disableAttachReorder,
1426
+ disablePartialFlush,
1427
+ }),
1428
+ telemetryDocumentId: this.telemetryDocumentId,
1429
+ groupedBatchingEnabled: this.groupedBatchingEnabled,
1405
1430
  });
1406
1431
 
1407
1432
  ReportOpPerfTelemetry(this.context.clientId, this.deltaManager, this.logger);
1408
1433
  BindBatchTracker(this, this.logger);
1434
+
1435
+ this.entryPoint = new LazyPromise(async () => {
1436
+ if (this.context.clientDetails.type === summarizerClientType) {
1437
+ assert(
1438
+ this._summarizer !== undefined,
1439
+ 0x5bf /* Summarizer object is undefined in a summarizer client */,
1440
+ );
1441
+ return this._summarizer;
1442
+ }
1443
+ return initializeEntryPoint?.(this);
1444
+ });
1409
1445
  }
1410
1446
 
1411
1447
  /**
1412
1448
  * Initializes the state from the base snapshot this container runtime loaded from.
1413
1449
  */
1414
1450
  private async initializeBaseState(): Promise<void> {
1415
- await this.initializeBaseSnapshotBlobs();
1416
1451
  await this.garbageCollector.initializeBaseState();
1417
1452
  }
1418
1453
 
@@ -1443,16 +1478,6 @@ export class ContainerRuntime
1443
1478
  this.removeAllListeners();
1444
1479
  }
1445
1480
 
1446
- public get IFluidTokenProvider() {
1447
- if (this.options?.intelligence) {
1448
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
1449
- return {
1450
- intelligence: this.options.intelligence,
1451
- } as IFluidTokenProvider;
1452
- }
1453
- return undefined;
1454
- }
1455
-
1456
1481
  /**
1457
1482
  * Notifies this object about the request made to the container.
1458
1483
  * @param request - Request made to the handler.
@@ -1522,6 +1547,14 @@ export class ContainerRuntime
1522
1547
  }
1523
1548
  }
1524
1549
 
1550
+ /**
1551
+ * {@inheritDoc @fluidframework/container-definitions#IRuntime.getEntryPoint}
1552
+ */
1553
+ public async getEntryPoint?(): Promise<FluidObject | undefined> {
1554
+ return this.entryPoint;
1555
+ }
1556
+ private readonly entryPoint: LazyPromise<FluidObject | undefined>;
1557
+
1525
1558
  private internalId(maybeAlias: string): string {
1526
1559
  return this.dataStores.aliases.get(maybeAlias) ?? maybeAlias;
1527
1560
  }
@@ -1541,29 +1574,6 @@ export class ContainerRuntime
1541
1574
  await this.dataStores.waitIfPendingAlias(id);
1542
1575
  const internalId = this.internalId(id);
1543
1576
  const dataStoreContext = await this.dataStores.getDataStore(internalId, headerData);
1544
-
1545
- /**
1546
- * If GC should run and this an external app request with "externalRequest" header, we need to return
1547
- * an error if the data store being requested is marked as unreferenced as per the data store's base
1548
- * GC data.
1549
- *
1550
- * This is a workaround to handle scenarios where a data store shared with an external app is deleted
1551
- * and marked as unreferenced by GC. Returning an error will fail to load the data store for the app.
1552
- */
1553
- if (
1554
- request.headers?.[RuntimeHeaders.externalRequest] &&
1555
- this.garbageCollector.shouldRunGC
1556
- ) {
1557
- // The data store is referenced if used routes in the base summary has a route to self.
1558
- // Older documents may not have used routes in the summary. They are considered referenced.
1559
- const usedRoutes = (await dataStoreContext.getBaseGCDetails()).usedRoutes;
1560
- if (
1561
- !(usedRoutes === undefined || usedRoutes.includes("") || usedRoutes.includes("/"))
1562
- ) {
1563
- throw responseToException(create404Response(request), request);
1564
- }
1565
- }
1566
-
1567
1577
  const dataStoreChannel = await dataStoreContext.realize();
1568
1578
 
1569
1579
  // Remove query params, leading and trailing slashes from the url. This is done to make sure the format is
@@ -1592,6 +1602,7 @@ export class ContainerRuntime
1592
1602
  message:
1593
1603
  extractSummaryMetadataMessage(this.deltaManager.lastMessage) ??
1594
1604
  this.messageAtLastSummary,
1605
+ telemetryDocumentId: this.telemetryDocumentId,
1595
1606
  };
1596
1607
  addBlobToSummary(summaryTree, metadataBlobName, JSON.stringify(metadata));
1597
1608
  }
@@ -1736,8 +1747,9 @@ export class ContainerRuntime
1736
1747
  // If attachment blobs were added while disconnected, we need to delay
1737
1748
  // propagation of the "connected" event until we have uploaded them to
1738
1749
  // ensure we don't submit ops referencing a blob that has not been uploaded
1750
+ // Note that the inner (non-proxy) delta manager is needed here to get the readonly information.
1739
1751
  const connecting =
1740
- connected && !this._connected && !this.deltaManager.readOnlyInfo.readonly;
1752
+ connected && !this._connected && !this.innerDeltaManager.readOnlyInfo.readonly;
1741
1753
  if (connecting && this.blobManager.hasPendingOfflineUploads) {
1742
1754
  assert(
1743
1755
  !this.delayConnectClientId,
@@ -1815,16 +1827,13 @@ export class ContainerRuntime
1815
1827
  raiseConnectedEvent(this.mc.logger, this, connected, clientId);
1816
1828
  }
1817
1829
 
1830
+ public async notifyOpReplay(message: ISequencedDocumentMessage) {
1831
+ await this.pendingStateManager.applyStashedOpsAt(message.sequenceNumber);
1832
+ }
1833
+
1818
1834
  public process(messageArg: ISequencedDocumentMessage, local: boolean) {
1819
1835
  this.verifyNotClosed();
1820
1836
 
1821
- if (
1822
- this.mc.config.getBoolean("enableOfflineLoad") ??
1823
- this.runtimeOptions.enableOfflineLoad
1824
- ) {
1825
- this.savedOps.push(messageArg);
1826
- }
1827
-
1828
1837
  // Whether or not the message is actually a runtime message.
1829
1838
  // It may be a legacy runtime message (ie already unpacked and ContainerMessageType)
1830
1839
  // or something different, like a system message.
@@ -1832,8 +1841,16 @@ export class ContainerRuntime
1832
1841
 
1833
1842
  // Do shallow copy of message, as the processing flow will modify it.
1834
1843
  const messageCopy = { ...messageArg };
1835
- const message = this.remoteMessageProcessor.process(messageCopy);
1844
+ for (const message of this.remoteMessageProcessor.process(messageCopy)) {
1845
+ this.processCore(message, local, runtimeMessage);
1846
+ }
1847
+ }
1836
1848
 
1849
+ private processCore(
1850
+ message: ISequencedDocumentMessage,
1851
+ local: boolean,
1852
+ runtimeMessage: boolean,
1853
+ ) {
1837
1854
  // Surround the actual processing of the operation with messages to the schedule manager indicating
1838
1855
  // the beginning and end. This allows it to emit appropriate events and/or pause the processing of new
1839
1856
  // messages once a batch has been fully processed.
@@ -1869,11 +1886,26 @@ export class ContainerRuntime
1869
1886
  case ContainerMessageType.Rejoin:
1870
1887
  break;
1871
1888
  default:
1872
- assert(!runtimeMessage, 0x3ce /* Runtime message of unknown type */);
1889
+ if (runtimeMessage) {
1890
+ const error = DataProcessingError.create(
1891
+ // Former assert 0x3ce
1892
+ "Runtime message of unknown type",
1893
+ "OpProcessing",
1894
+ message,
1895
+ {
1896
+ local,
1897
+ type: message.type,
1898
+ contentType: typeof message.contents,
1899
+ batch: message.metadata?.batch,
1900
+ compression: message.compression,
1901
+ },
1902
+ );
1903
+ this.closeFn(error);
1904
+ throw error;
1905
+ }
1873
1906
  }
1874
1907
 
1875
- // For back-compat, notify only about runtime messages for now.
1876
- if (runtimeMessage) {
1908
+ if (runtimeMessage || this.groupedBatchingEnabled) {
1877
1909
  this.emit("op", message, runtimeMessage);
1878
1910
  }
1879
1911
 
@@ -1944,7 +1976,10 @@ export class ContainerRuntime
1944
1976
  envelope.clientSignalSequenceNumber ===
1945
1977
  this._perfSignalData.trackingSignalSequenceNumber
1946
1978
  ) {
1947
- this.sendSignalTelemetryEvent(envelope.clientSignalSequenceNumber);
1979
+ // only logging for the first connection and the trackingSignalSequenceNUmber.
1980
+ if (this.consecutiveReconnects === 0) {
1981
+ this.sendSignalTelemetryEvent(envelope.clientSignalSequenceNumber);
1982
+ }
1948
1983
  this._perfSignalData.trackingSignalSequenceNumber = undefined;
1949
1984
  }
1950
1985
  }
@@ -2084,14 +2119,16 @@ export class ContainerRuntime
2084
2119
  }
2085
2120
 
2086
2121
  private canSendOps() {
2087
- return this.connected && !this.deltaManager.readOnlyInfo.readonly;
2122
+ // Note that the real (non-proxy) delta manager is needed here to get the readonly info. This is because
2123
+ // container runtime's ability to send ops depend on the actual readonly state of the delta manager.
2124
+ return this.connected && !this.innerDeltaManager.readOnlyInfo.readonly;
2088
2125
  }
2089
2126
 
2090
2127
  /**
2091
2128
  * Are we in the middle of batching ops together?
2092
2129
  */
2093
2130
  private currentlyBatching() {
2094
- return this.flushMode === FlushMode.TurnBased || this._orderSequentiallyCalls !== 0;
2131
+ return this.flushMode !== FlushMode.Immediate || this._orderSequentiallyCalls !== 0;
2095
2132
  }
2096
2133
 
2097
2134
  public getQuorum(): IQuorumClients {
@@ -2278,12 +2315,24 @@ export class ContainerRuntime
2278
2315
  fullGC,
2279
2316
  } = options;
2280
2317
 
2318
+ const telemetryContext = new TelemetryContext();
2319
+ // Add the options that are used to generate this summary to the telemetry context.
2320
+ telemetryContext.setMultiple("fluid_Summarize", "Options", {
2321
+ fullTree,
2322
+ trackState,
2323
+ runGC,
2324
+ fullGC,
2325
+ runSweep,
2326
+ });
2327
+
2281
2328
  let gcStats: IGCStats | undefined;
2282
2329
  if (runGC) {
2283
- gcStats = await this.collectGarbage({ logger: summaryLogger, runSweep, fullGC });
2330
+ gcStats = await this.collectGarbage(
2331
+ { logger: summaryLogger, runSweep, fullGC },
2332
+ telemetryContext,
2333
+ );
2284
2334
  }
2285
2335
 
2286
- const telemetryContext = new TelemetryContext();
2287
2336
  const { stats, summary } = await this.summarizerNode.summarize(
2288
2337
  fullTree,
2289
2338
  trackState,
@@ -2304,10 +2353,10 @@ export class ContainerRuntime
2304
2353
  }
2305
2354
 
2306
2355
  /**
2307
- * Implementation of IGarbageCollectionRuntime::updateStateBeforeGC.
2308
2356
  * Before GC runs, called by the garbage collector to update any pending GC state. This is mainly used to notify
2309
2357
  * the garbage collector of references detected since the last GC run. Most references are notified immediately
2310
2358
  * but there can be some for which async operation is required (such as detecting new root data stores).
2359
+ * @see IGarbageCollectionRuntime.updateStateBeforeGC
2311
2360
  */
2312
2361
  public async updateStateBeforeGC() {
2313
2362
  return this.dataStores.updateStateBeforeGC();
@@ -2318,9 +2367,9 @@ export class ContainerRuntime
2318
2367
  }
2319
2368
 
2320
2369
  /**
2321
- * Implementation of IGarbageCollectionRuntime::getGCData.
2322
2370
  * Generates and returns the GC data for this container.
2323
2371
  * @param fullGC - true to bypass optimizations and force full generation of GC data.
2372
+ * @see IGarbageCollectionRuntime.getGCData
2324
2373
  */
2325
2374
  public async getGCData(fullGC?: boolean): Promise<IGarbageCollectionData> {
2326
2375
  const builder = new GCDataBuilder();
@@ -2333,9 +2382,9 @@ export class ContainerRuntime
2333
2382
  }
2334
2383
 
2335
2384
  /**
2336
- * Implementation of IGarbageCollectionRuntime::updateUsedRoutes.
2337
2385
  * After GC has run, called to notify this container's nodes of routes that are used in it.
2338
2386
  * @param usedRoutes - The routes that are used in all nodes in this Container.
2387
+ * @see IGarbageCollectionRuntime.updateUsedRoutes
2339
2388
  */
2340
2389
  public updateUsedRoutes(usedRoutes: string[]) {
2341
2390
  // Update our summarizer node's used routes. Updating used routes in summarizer node before
@@ -2358,6 +2407,26 @@ export class ContainerRuntime
2358
2407
  this.dataStores.updateUnusedRoutes(dataStoreRoutes);
2359
2408
  }
2360
2409
 
2410
+ /**
2411
+ * @deprecated - Replaced by deleteSweepReadyNodes.
2412
+ */
2413
+ public deleteUnusedNodes(unusedRoutes: string[]): string[] {
2414
+ throw new Error("deleteUnusedRoutes should not be called");
2415
+ }
2416
+
2417
+ /**
2418
+ * After GC has run and identified nodes that are sweep ready, this is called to delete the sweep ready nodes.
2419
+ * @param sweepReadyRoutes - The routes of nodes that are sweep ready and should be deleted.
2420
+ * @returns - The routes of nodes that were deleted.
2421
+ */
2422
+ public deleteSweepReadyNodes(sweepReadyRoutes: string[]): string[] {
2423
+ const { dataStoreRoutes, blobManagerRoutes } =
2424
+ this.getDataStoreAndBlobManagerRoutes(sweepReadyRoutes);
2425
+
2426
+ const deletedRoutes = this.dataStores.deleteSweepReadyNodes(dataStoreRoutes);
2427
+ return deletedRoutes.concat(this.blobManager.deleteSweepReadyNodes(blobManagerRoutes));
2428
+ }
2429
+
2361
2430
  /**
2362
2431
  * This is called to update objects that are tombstones.
2363
2432
  * @param tombstonedRoutes - Data store and attachment blob routes that are tombstones in this Container.
@@ -2439,15 +2508,18 @@ export class ContainerRuntime
2439
2508
  * Runs garbage collection and updates the reference / used state of the nodes in the container.
2440
2509
  * @returns the statistics of the garbage collection run; undefined if GC did not run.
2441
2510
  */
2442
- public async collectGarbage(options: {
2443
- /** Logger to use for logging GC events */
2444
- logger?: ITelemetryLogger;
2445
- /** True to run GC sweep phase after the mark phase */
2446
- runSweep?: boolean;
2447
- /** True to generate full GC data */
2448
- fullGC?: boolean;
2449
- }): Promise<IGCStats | undefined> {
2450
- return this.garbageCollector.collectGarbage(options);
2511
+ public async collectGarbage(
2512
+ options: {
2513
+ /** Logger to use for logging GC events */
2514
+ logger?: ITelemetryLogger;
2515
+ /** True to run GC sweep phase after the mark phase */
2516
+ runSweep?: boolean;
2517
+ /** True to generate full GC data */
2518
+ fullGC?: boolean;
2519
+ },
2520
+ telemetryContext?: ITelemetryContext,
2521
+ ): Promise<IGCStats | undefined> {
2522
+ return this.garbageCollector.collectGarbage(options, telemetryContext);
2451
2523
  }
2452
2524
 
2453
2525
  /**
@@ -2472,7 +2544,7 @@ export class ContainerRuntime
2472
2544
  * @param options - options controlling how the summary is generated or submitted
2473
2545
  */
2474
2546
  public async submitSummary(options: ISubmitSummaryOptions): Promise<SubmitSummaryResult> {
2475
- const { fullTree, refreshLatestAck, summaryLogger } = options;
2547
+ const { fullTree = false, refreshLatestAck, summaryLogger } = options;
2476
2548
  // The summary number for this summary. This will be updated during the summary process, so get it now and
2477
2549
  // use it for all events logged during this summary.
2478
2550
  const summaryNumber = this.nextSummaryNumber;
@@ -2494,8 +2566,15 @@ export class ContainerRuntime
2494
2566
  await this.waitForDeltaManagerToCatchup(latestSnapshotRefSeq, summaryNumberLogger);
2495
2567
  }
2496
2568
 
2569
+ const shouldPauseInboundSignal =
2570
+ this.mc.config.getBoolean(
2571
+ "Fluid.ContainerRuntime.SubmitSummary.disableInboundSignalPause",
2572
+ ) !== true;
2497
2573
  try {
2498
2574
  await this.deltaManager.inbound.pause();
2575
+ if (shouldPauseInboundSignal) {
2576
+ await this.deltaManager.inboundSignal.pause();
2577
+ }
2499
2578
 
2500
2579
  const summaryRefSeqNum = this.deltaManager.lastSequenceNumber;
2501
2580
  const minimumSequenceNumber = this.deltaManager.minimumSequenceNumber;
@@ -2560,7 +2639,7 @@ export class ContainerRuntime
2560
2639
  const forcedFullTree = this.garbageCollector.summaryStateNeedsReset;
2561
2640
  try {
2562
2641
  summarizeResult = await this.summarize({
2563
- fullTree: fullTree ?? forcedFullTree,
2642
+ fullTree: fullTree || forcedFullTree,
2564
2643
  trackState: true,
2565
2644
  summaryLogger: summaryNumberLogger,
2566
2645
  runGC: this.garbageCollector.shouldRunGC,
@@ -2674,7 +2753,7 @@ export class ContainerRuntime
2674
2753
 
2675
2754
  let clientSequenceNumber: number;
2676
2755
  try {
2677
- clientSequenceNumber = this.submitSummaryMessage(summaryMessage);
2756
+ clientSequenceNumber = this.submitSummaryMessage(summaryMessage, summaryRefSeqNum);
2678
2757
  } catch (error) {
2679
2758
  return { stage: "upload", ...uploadData, error };
2680
2759
  }
@@ -2691,8 +2770,12 @@ export class ContainerRuntime
2691
2770
  } finally {
2692
2771
  // Cleanup wip summary in case of failure
2693
2772
  this.summarizerNode.clearSummary();
2773
+
2694
2774
  // Restart the delta manager
2695
2775
  this.deltaManager.inbound.resume();
2776
+ if (shouldPauseInboundSignal) {
2777
+ this.deltaManager.inboundSignal.resume();
2778
+ }
2696
2779
  }
2697
2780
  }
2698
2781
 
@@ -2763,10 +2846,11 @@ export class ContainerRuntime
2763
2846
  0x132 /* "sending ops in detached container" */,
2764
2847
  );
2765
2848
 
2766
- const deserializedContent: ContainerRuntimeMessage = { type, contents };
2767
- const serializedContent = JSON.stringify(deserializedContent);
2849
+ const serializedContent = JSON.stringify({ type, contents });
2768
2850
 
2769
- if (this.deltaManager.readOnlyInfo.readonly) {
2851
+ // Note that the real (non-proxy) delta manager is used here to get the readonly info. This is because
2852
+ // container runtime's ability to submit ops depend on the actual readonly state of the delta manager.
2853
+ if (this.innerDeltaManager.readOnlyInfo.readonly) {
2770
2854
  this.logger.sendTelemetryEvent({
2771
2855
  eventName: "SubmitOpInReadonly",
2772
2856
  connected: this.connected,
@@ -2775,7 +2859,7 @@ export class ContainerRuntime
2775
2859
 
2776
2860
  const message: BatchMessage = {
2777
2861
  contents: serializedContent,
2778
- deserializedContent,
2862
+ deserializedContent: JSON.parse(serializedContent), // Deep copy in case caller changes reference object
2779
2863
  metadata,
2780
2864
  localOpMetadata,
2781
2865
  referenceSequenceNumber: this.deltaManager.lastSequenceNumber,
@@ -2805,7 +2889,7 @@ export class ContainerRuntime
2805
2889
  if (
2806
2890
  this.currentlyBatching() &&
2807
2891
  type === ContainerMessageType.Attach &&
2808
- this.mc.config.getBoolean("Fluid.ContainerRuntime.disableAttachOpReorder") !== true
2892
+ this.disableAttachReorder !== true
2809
2893
  ) {
2810
2894
  this.outbox.submitAttach(message);
2811
2895
  } else {
@@ -2814,17 +2898,8 @@ export class ContainerRuntime
2814
2898
 
2815
2899
  if (!this.currentlyBatching()) {
2816
2900
  this.flush();
2817
- } else if (!this.flushMicroTaskExists) {
2818
- this.flushMicroTaskExists = true;
2819
- // Queue a microtask to detect the end of the turn and force a flush.
2820
- Promise.resolve()
2821
- .then(() => {
2822
- this.flushMicroTaskExists = false;
2823
- this.flush();
2824
- })
2825
- .catch((error) => {
2826
- this.closeFn(error as GenericError);
2827
- });
2901
+ } else {
2902
+ this.scheduleFlush();
2828
2903
  }
2829
2904
  } catch (error) {
2830
2905
  this.closeFn(error as GenericError);
@@ -2836,7 +2911,47 @@ export class ContainerRuntime
2836
2911
  }
2837
2912
  }
2838
2913
 
2839
- private submitSummaryMessage(contents: ISummaryContent) {
2914
+ private scheduleFlush() {
2915
+ if (this.flushTaskExists) {
2916
+ return;
2917
+ }
2918
+
2919
+ this.flushTaskExists = true;
2920
+ const flush = () => {
2921
+ this.flushTaskExists = false;
2922
+ try {
2923
+ this.flush();
2924
+ } catch (error) {
2925
+ this.closeFn(error as GenericError);
2926
+ }
2927
+ };
2928
+
2929
+ switch (this.flushMode) {
2930
+ case FlushMode.TurnBased:
2931
+ // When in TurnBased flush mode the runtime will buffer operations in the current turn and send them as a single
2932
+ // batch at the end of the turn
2933
+ // eslint-disable-next-line @typescript-eslint/no-floating-promises
2934
+ Promise.resolve().then(flush);
2935
+ break;
2936
+
2937
+ // FlushModeExperimental is experimental and not exposed directly in the runtime APIs
2938
+ case FlushModeExperimental.Async as unknown as FlushMode:
2939
+ // When in Async flush mode, the runtime will accumulate all operations across JS turns and send them as a single
2940
+ // batch when all micro-tasks are complete.
2941
+ // Compared to TurnBased, this flush mode will capture more ops into the same batch.
2942
+ setTimeout(flush, 0);
2943
+ break;
2944
+
2945
+ default:
2946
+ assert(
2947
+ this._orderSequentiallyCalls > 0,
2948
+ 0x587 /* Unreachable unless running under orderSequentially */,
2949
+ );
2950
+ break;
2951
+ }
2952
+ }
2953
+
2954
+ private submitSummaryMessage(contents: ISummaryContent, referenceSequenceNumber: number) {
2840
2955
  this.verifyNotClosed();
2841
2956
  assert(
2842
2957
  this.connected,
@@ -2848,7 +2963,7 @@ export class ContainerRuntime
2848
2963
 
2849
2964
  // back-compat: ADO #1385: Make this call unconditional in the future
2850
2965
  return this.context.submitSummaryFn !== undefined
2851
- ? this.context.submitSummaryFn(contents)
2966
+ ? this.context.submitSummaryFn(contents, referenceSequenceNumber)
2852
2967
  : this.context.submitFn(MessageType.Summarize, contents, false);
2853
2968
  }
2854
2969
 
@@ -2967,18 +3082,17 @@ export class ContainerRuntime
2967
3082
  // The call to fetch the snapshot is very expensive and not always needed.
2968
3083
  // It should only be done by the summarizerNode, if required.
2969
3084
  // When fetching from storage we will always get the latest version and do not use the ackHandle.
2970
- const snapshotTreeFetcher = async () => {
2971
- const fetchResult = await this.fetchLatestSnapshotFromStorage(summaryLogger, {
2972
- eventName: "RefreshLatestSummaryGetSnapshot",
2973
- ackHandle,
2974
- summaryRefSeq,
2975
- fetchLatest: true,
2976
- });
2977
-
2978
- const latestSnapshotRefSeq = await seqFromTree(
2979
- fetchResult.snapshotTree,
3085
+ const fetchLatestSnapshot: () => Promise<IFetchSnapshotResult> = async () => {
3086
+ let fetchResult = await this.fetchLatestSnapshotFromStorage(
3087
+ summaryLogger,
3088
+ {
3089
+ eventName: "RefreshLatestSummaryAckFetch",
3090
+ ackHandle,
3091
+ targetSequenceNumber: summaryRefSeq,
3092
+ },
2980
3093
  readAndParseBlob,
2981
3094
  );
3095
+
2982
3096
  /**
2983
3097
  * If the fetched snapshot is older than the one for which the ack was received, close the container.
2984
3098
  * This should never happen because an ack should be sent after the latest summary is updated in the server.
@@ -2989,50 +3103,58 @@ export class ContainerRuntime
2989
3103
  * such cases, the file will be rolled back along with the ack and we will eventually reach a consistent
2990
3104
  * state.
2991
3105
  */
2992
- if (latestSnapshotRefSeq < summaryRefSeq) {
2993
- const error = DataProcessingError.create(
2994
- "Fetched snapshot is older than the received ack",
2995
- "RefreshLatestSummaryAck",
2996
- undefined /* sequencedMessage */,
3106
+ if (fetchResult.latestSnapshotRefSeq < summaryRefSeq) {
3107
+ /* before failing, let's try to retrieve the latest snapshot for that specific ackHandle */
3108
+ fetchResult = await this.fetchSnapshotFromStorage(
3109
+ summaryLogger,
2997
3110
  {
3111
+ eventName: "RefreshLatestSummaryAckFetch",
2998
3112
  ackHandle,
2999
- summaryRefSeq,
3000
- latestSnapshotRefSeq,
3113
+ targetSequenceNumber: summaryRefSeq,
3001
3114
  },
3115
+ readAndParseBlob,
3116
+ ackHandle,
3002
3117
  );
3003
- this.closeFn(error);
3004
- throw error;
3005
- }
3006
3118
 
3007
- summaryLogger.sendTelemetryEvent({
3008
- eventName: "LatestSummaryRetrieved",
3009
- ackHandle,
3010
- lastSequenceNumber: latestSnapshotRefSeq,
3011
- targetSequenceNumber: summaryRefSeq,
3012
- });
3119
+ if (fetchResult.latestSnapshotRefSeq < summaryRefSeq) {
3120
+ const error = DataProcessingError.create(
3121
+ "Fetched snapshot is older than the received ack",
3122
+ "RefreshLatestSummaryAck",
3123
+ undefined /* sequencedMessage */,
3124
+ {
3125
+ ackHandle,
3126
+ summaryRefSeq,
3127
+ fetchedSnapshotRefSeq: fetchResult.latestSnapshotRefSeq,
3128
+ },
3129
+ );
3130
+ this.closeFn(error);
3131
+ throw error;
3132
+ }
3133
+ }
3013
3134
 
3014
3135
  // In case we had to retrieve the latest snapshot and it is different than summaryRefSeq,
3015
3136
  // wait for the delta manager to catch up before refreshing the latest Summary.
3016
- await this.waitForDeltaManagerToCatchup(latestSnapshotRefSeq, summaryLogger);
3137
+ await this.waitForDeltaManagerToCatchup(
3138
+ fetchResult.latestSnapshotRefSeq,
3139
+ summaryLogger,
3140
+ );
3017
3141
 
3018
- return fetchResult.snapshotTree;
3142
+ return {
3143
+ snapshotTree: fetchResult.snapshotTree,
3144
+ snapshotRefSeq: fetchResult.latestSnapshotRefSeq,
3145
+ };
3019
3146
  };
3020
3147
 
3021
3148
  const result = await this.summarizerNode.refreshLatestSummary(
3022
3149
  proposalHandle,
3023
3150
  summaryRefSeq,
3024
- snapshotTreeFetcher,
3151
+ fetchLatestSnapshot,
3025
3152
  readAndParseBlob,
3026
3153
  summaryLogger,
3027
3154
  );
3028
3155
 
3029
3156
  // Notify the garbage collector so it can update its latest summary state.
3030
- await this.garbageCollector.refreshLatestSummary(
3031
- result,
3032
- proposalHandle,
3033
- summaryRefSeq,
3034
- readAndParseBlob,
3035
- );
3157
+ await this.garbageCollector.refreshLatestSummary(proposalHandle, result, readAndParseBlob);
3036
3158
  }
3037
3159
 
3038
3160
  /**
@@ -3044,30 +3166,31 @@ export class ContainerRuntime
3044
3166
  private async refreshLatestSummaryAckFromServer(
3045
3167
  summaryLogger: ITelemetryLogger,
3046
3168
  ): Promise<{ latestSnapshotRefSeq: number; latestSnapshotVersionId: string | undefined }> {
3047
- const { snapshotTree, versionId } = await this.fetchLatestSnapshotFromStorage(
3048
- summaryLogger,
3049
- {
3050
- eventName: "RefreshLatestSummaryGetSnapshot",
3051
- fetchLatest: true,
3052
- },
3053
- );
3054
-
3055
3169
  const readAndParseBlob = async <T>(id: string) => readAndParse<T>(this.storage, id);
3056
- const latestSnapshotRefSeq = await seqFromTree(snapshotTree, readAndParseBlob);
3057
-
3170
+ const { snapshotTree, versionId, latestSnapshotRefSeq } =
3171
+ await this.fetchLatestSnapshotFromStorage(
3172
+ summaryLogger,
3173
+ {
3174
+ eventName: "RefreshLatestSummaryFromServerFetch",
3175
+ },
3176
+ readAndParseBlob,
3177
+ );
3178
+ const fetchLatestSnapshot: IFetchSnapshotResult = {
3179
+ snapshotTree,
3180
+ snapshotRefSeq: latestSnapshotRefSeq,
3181
+ };
3058
3182
  const result = await this.summarizerNode.refreshLatestSummary(
3059
- undefined,
3183
+ undefined /* proposalHandle */,
3060
3184
  latestSnapshotRefSeq,
3061
- async () => snapshotTree,
3185
+ async () => fetchLatestSnapshot,
3062
3186
  readAndParseBlob,
3063
3187
  summaryLogger,
3064
3188
  );
3065
3189
 
3066
3190
  // Notify the garbage collector so it can update its latest summary state.
3067
3191
  await this.garbageCollector.refreshLatestSummary(
3192
+ undefined /* proposalHandle */,
3068
3193
  result,
3069
- undefined,
3070
- latestSnapshotRefSeq,
3071
3194
  readAndParseBlob,
3072
3195
  );
3073
3196
 
@@ -3077,7 +3200,37 @@ export class ContainerRuntime
3077
3200
  private async fetchLatestSnapshotFromStorage(
3078
3201
  logger: ITelemetryLogger,
3079
3202
  event: ITelemetryGenericEvent,
3080
- ): Promise<{ snapshotTree: ISnapshotTree; versionId: string }> {
3203
+ readAndParseBlob: ReadAndParseBlob,
3204
+ ): Promise<{ snapshotTree: ISnapshotTree; versionId: string; latestSnapshotRefSeq: number }> {
3205
+ return this.fetchSnapshotFromStorage(logger, event, readAndParseBlob, null /* latest */);
3206
+ }
3207
+
3208
+ private async fetchSnapshotFromStorage(
3209
+ logger: ITelemetryLogger,
3210
+ event: ITelemetryGenericEvent,
3211
+ readAndParseBlob: ReadAndParseBlob,
3212
+ versionId: string | null,
3213
+ ): Promise<{ snapshotTree: ISnapshotTree; versionId: string; latestSnapshotRefSeq: number }> {
3214
+ const recoveryMethod = this.mc.config.getString(
3215
+ "Fluid.ContainerRuntime.Test.SummarizationRecoveryMethod",
3216
+ );
3217
+ if (recoveryMethod === "restart") {
3218
+ const error = new GenericError("Restarting summarizer instead of refreshing");
3219
+ this.mc.logger.sendTelemetryEvent(
3220
+ {
3221
+ ...event,
3222
+ eventName: "ClosingSummarizerOnSummaryStale",
3223
+ codePath: event.eventName,
3224
+ message: "Stopping fetch from storage",
3225
+ versionId: versionId != null ? versionId : undefined,
3226
+ },
3227
+ error,
3228
+ );
3229
+ this._summarizer?.stop("latestSummaryStateStale");
3230
+ this.closeFn();
3231
+ throw error;
3232
+ }
3233
+
3081
3234
  return PerformanceEvent.timedExecAsync(
3082
3235
  logger,
3083
3236
  event,
@@ -3085,16 +3238,23 @@ export class ContainerRuntime
3085
3238
  end: (arg0: {
3086
3239
  getVersionDuration?: number | undefined;
3087
3240
  getSnapshotDuration?: number | undefined;
3241
+ snapshotRefSeq?: number | undefined;
3242
+ snapshotVersion?: string | undefined;
3088
3243
  }) => void;
3089
3244
  }) => {
3090
- const stats: { getVersionDuration?: number; getSnapshotDuration?: number } = {};
3245
+ const stats: {
3246
+ getVersionDuration?: number;
3247
+ getSnapshotDuration?: number;
3248
+ snapshotRefSeq?: number;
3249
+ snapshotVersion?: string;
3250
+ } = {};
3091
3251
  const trace = Trace.start();
3092
3252
 
3093
3253
  const versions = await this.storage.getVersions(
3094
- null,
3254
+ versionId,
3095
3255
  1,
3096
3256
  "refreshLatestSummaryAckFromServer",
3097
- FetchSource.noCache,
3257
+ versionId === null ? FetchSource.noCache : undefined,
3098
3258
  );
3099
3259
  assert(
3100
3260
  !!versions && !!versions[0],
@@ -3105,51 +3265,23 @@ export class ContainerRuntime
3105
3265
  const maybeSnapshot = await this.storage.getSnapshotTree(versions[0]);
3106
3266
  assert(!!maybeSnapshot, 0x138 /* "Failed to get snapshot from storage" */);
3107
3267
  stats.getSnapshotDuration = trace.trace().duration;
3268
+ const latestSnapshotRefSeq = await seqFromTree(maybeSnapshot, readAndParseBlob);
3269
+ stats.snapshotRefSeq = latestSnapshotRefSeq;
3270
+ stats.snapshotVersion = versions[0].id;
3108
3271
 
3109
3272
  perfEvent.end(stats);
3110
- return { snapshotTree: maybeSnapshot, versionId: versions[0].id };
3273
+ return {
3274
+ snapshotTree: maybeSnapshot,
3275
+ versionId: versions[0].id,
3276
+ latestSnapshotRefSeq,
3277
+ };
3111
3278
  },
3112
3279
  );
3113
3280
  }
3114
3281
 
3115
- public notifyAttaching(snapshot: ISnapshotTreeWithBlobContents) {
3116
- if (
3117
- this.mc.config.getBoolean("enableOfflineLoad") ??
3118
- this.runtimeOptions.enableOfflineLoad
3119
- ) {
3120
- this.baseSnapshotBlobs =
3121
- SerializedSnapshotStorage.serializeTreeWithBlobContents(snapshot);
3122
- }
3123
- }
3124
-
3125
- private async initializeBaseSnapshotBlobs(): Promise<void> {
3126
- if (
3127
- !(
3128
- this.mc.config.getBoolean("enableOfflineLoad") ??
3129
- this.runtimeOptions.enableOfflineLoad
3130
- ) ||
3131
- this.attachState !== AttachState.Attached ||
3132
- this.context.pendingLocalState
3133
- ) {
3134
- return;
3135
- }
3136
- assert(!!this.context.baseSnapshot, 0x2e5 /* "Must have a base snapshot" */);
3137
- this.baseSnapshotBlobs = await SerializedSnapshotStorage.serializeTree(
3138
- this.context.baseSnapshot,
3139
- this.storage,
3140
- );
3141
- }
3282
+ public notifyAttaching() {} // do nothing (deprecated method)
3142
3283
 
3143
3284
  public getPendingLocalState(): unknown {
3144
- if (
3145
- !(
3146
- this.mc.config.getBoolean("enableOfflineLoad") ??
3147
- this.runtimeOptions.enableOfflineLoad
3148
- )
3149
- ) {
3150
- throw new UsageError("can't get state when offline load disabled");
3151
- }
3152
-
3153
3285
  if (this._orderSequentiallyCalls !== 0) {
3154
3286
  throw new UsageError("can't get state during orderSequentially");
3155
3287
  }
@@ -3158,29 +3290,9 @@ export class ContainerRuntime
3158
3290
  // to close current batch.
3159
3291
  this.flush();
3160
3292
 
3161
- const previousPendingState = this.context.pendingLocalState as
3162
- | IPendingRuntimeState
3163
- | undefined;
3164
- if (previousPendingState) {
3165
- return {
3166
- pending: this.pendingStateManager.getLocalState(),
3167
- pendingAttachmentBlobs: this.blobManager.getPendingBlobs(),
3168
- snapshotBlobs: previousPendingState.snapshotBlobs,
3169
- baseSnapshot: previousPendingState.baseSnapshot,
3170
- savedOps: this.savedOps,
3171
- };
3172
- }
3173
- assert(!!this.context.baseSnapshot, 0x2e6 /* "Must have a base snapshot" */);
3174
- assert(
3175
- !!this.baseSnapshotBlobs,
3176
- 0x2e7 /* "Must serialize base snapshot blobs before getting runtime state" */,
3177
- );
3178
3293
  return {
3179
3294
  pending: this.pendingStateManager.getLocalState(),
3180
3295
  pendingAttachmentBlobs: this.blobManager.getPendingBlobs(),
3181
- snapshotBlobs: this.baseSnapshotBlobs,
3182
- baseSnapshot: this.context.baseSnapshot,
3183
- savedOps: this.savedOps,
3184
3296
  };
3185
3297
  }
3186
3298
 
@@ -3243,25 +3355,6 @@ export class ContainerRuntime
3243
3355
  };
3244
3356
  }
3245
3357
 
3246
- private async processSavedOps(state: IPendingRuntimeState) {
3247
- for (const op of state.savedOps) {
3248
- this.process(op, false);
3249
- await this.pendingStateManager.applyStashedOpsAt(op.sequenceNumber);
3250
- }
3251
- // we may not have seen every sequence number (because of system ops) so apply everything once we
3252
- // don't have any more saved ops
3253
- await this.pendingStateManager.applyStashedOpsAt();
3254
-
3255
- // If it's not the case, we should take it into account when calculating dirty state.
3256
- assert(
3257
- this.context.attachState === AttachState.Attached,
3258
- 0x3d5 /* this function is called for attached containers only */,
3259
- );
3260
- if (!this.hasPendingMessages()) {
3261
- this.updateDocumentDirtyState(false);
3262
- }
3263
- }
3264
-
3265
3358
  private validateSummaryHeuristicConfiguration(configuration: ISummaryConfigurationHeuristics) {
3266
3359
  // eslint-disable-next-line no-restricted-syntax
3267
3360
  for (const prop in configuration) {
@@ -3277,6 +3370,13 @@ export class ContainerRuntime
3277
3370
  );
3278
3371
  }
3279
3372
  }
3373
+
3374
+ private get groupedBatchingEnabled(): boolean {
3375
+ const killSwitch = this.mc.config.getBoolean(
3376
+ "Fluid.ContainerRuntime.DisableGroupedBatching",
3377
+ );
3378
+ return killSwitch !== true && this.runtimeOptions.enableGroupedBatching;
3379
+ }
3280
3380
  }
3281
3381
 
3282
3382
  /**