@fluidframework/container-runtime 1.4.0-121020 → 2.0.0-dev-rc.1.0.0.224419

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 (703) hide show
  1. package/.eslintrc.js +19 -29
  2. package/.mocharc.js +12 -0
  3. package/CHANGELOG.md +427 -0
  4. package/README.md +69 -0
  5. package/api-extractor-lint.json +4 -0
  6. package/api-extractor.json +2 -2
  7. package/api-report/container-runtime.api.md +863 -0
  8. package/dist/{batchTracker.js → batchTracker.cjs} +9 -8
  9. package/dist/batchTracker.cjs.map +1 -0
  10. package/dist/batchTracker.d.ts +6 -5
  11. package/dist/batchTracker.d.ts.map +1 -1
  12. package/dist/blobManager.cjs +704 -0
  13. package/dist/blobManager.cjs.map +1 -0
  14. package/dist/blobManager.d.ts +135 -39
  15. package/dist/blobManager.d.ts.map +1 -1
  16. package/dist/connectionTelemetry.cjs +230 -0
  17. package/dist/connectionTelemetry.cjs.map +1 -0
  18. package/dist/connectionTelemetry.d.ts +2 -2
  19. package/dist/connectionTelemetry.d.ts.map +1 -1
  20. package/dist/container-runtime-alpha.d.ts +1677 -0
  21. package/dist/container-runtime-beta.d.ts +250 -0
  22. package/dist/container-runtime-public.d.ts +250 -0
  23. package/dist/container-runtime-untrimmed.d.ts +1792 -0
  24. package/dist/{containerHandleContext.js → containerHandleContext.cjs} +4 -2
  25. package/dist/containerHandleContext.cjs.map +1 -0
  26. package/dist/containerHandleContext.d.ts.map +1 -1
  27. package/dist/containerRuntime.cjs +2531 -0
  28. package/dist/containerRuntime.cjs.map +1 -0
  29. package/dist/containerRuntime.d.ts +454 -256
  30. package/dist/containerRuntime.d.ts.map +1 -1
  31. package/dist/{dataStore.js → dataStore.cjs} +54 -45
  32. package/dist/dataStore.cjs.map +1 -0
  33. package/dist/dataStore.d.ts +2 -2
  34. package/dist/dataStore.d.ts.map +1 -1
  35. package/dist/{dataStoreContext.js → dataStoreContext.cjs} +343 -247
  36. package/dist/dataStoreContext.cjs.map +1 -0
  37. package/dist/dataStoreContext.d.ts +73 -41
  38. package/dist/dataStoreContext.d.ts.map +1 -1
  39. package/dist/{dataStoreContexts.js → dataStoreContexts.cjs} +19 -15
  40. package/dist/dataStoreContexts.cjs.map +1 -0
  41. package/dist/dataStoreContexts.d.ts +1 -1
  42. package/dist/dataStoreContexts.d.ts.map +1 -1
  43. package/dist/{dataStoreRegistry.js → dataStoreRegistry.cjs} +9 -4
  44. package/dist/dataStoreRegistry.cjs.map +1 -0
  45. package/dist/dataStoreRegistry.d.ts +3 -0
  46. package/dist/dataStoreRegistry.d.ts.map +1 -1
  47. package/dist/{dataStores.js → dataStores.cjs} +273 -124
  48. package/dist/dataStores.cjs.map +1 -0
  49. package/dist/dataStores.d.ts +53 -23
  50. package/dist/dataStores.d.ts.map +1 -1
  51. package/dist/deltaManagerProxyBase.cjs +77 -0
  52. package/dist/deltaManagerProxyBase.cjs.map +1 -0
  53. package/dist/deltaManagerProxyBase.d.ts +35 -0
  54. package/dist/deltaManagerProxyBase.d.ts.map +1 -0
  55. package/dist/deltaManagerSummarizerProxy.cjs +42 -0
  56. package/dist/deltaManagerSummarizerProxy.cjs.map +1 -0
  57. package/dist/deltaManagerSummarizerProxy.d.ts +19 -0
  58. package/dist/deltaManagerSummarizerProxy.d.ts.map +1 -0
  59. package/dist/{deltaScheduler.js → deltaScheduler.cjs} +25 -18
  60. package/dist/deltaScheduler.cjs.map +1 -0
  61. package/dist/deltaScheduler.d.ts +8 -6
  62. package/dist/deltaScheduler.d.ts.map +1 -1
  63. package/dist/error.cjs +21 -0
  64. package/dist/error.cjs.map +1 -0
  65. package/dist/error.d.ts +14 -0
  66. package/dist/error.d.ts.map +1 -0
  67. package/dist/gc/garbageCollection.cjs +861 -0
  68. package/dist/gc/garbageCollection.cjs.map +1 -0
  69. package/dist/gc/garbageCollection.d.ts +224 -0
  70. package/dist/gc/garbageCollection.d.ts.map +1 -0
  71. package/dist/gc/gcConfigs.cjs +153 -0
  72. package/dist/gc/gcConfigs.cjs.map +1 -0
  73. package/dist/gc/gcConfigs.d.ts +23 -0
  74. package/dist/gc/gcConfigs.d.ts.map +1 -0
  75. package/dist/gc/gcDefinitions.cjs +96 -0
  76. package/dist/gc/gcDefinitions.cjs.map +1 -0
  77. package/dist/gc/gcDefinitions.d.ts +437 -0
  78. package/dist/gc/gcDefinitions.d.ts.map +1 -0
  79. package/dist/gc/gcHelpers.cjs +235 -0
  80. package/dist/gc/gcHelpers.cjs.map +1 -0
  81. package/dist/gc/gcHelpers.d.ts +71 -0
  82. package/dist/gc/gcHelpers.d.ts.map +1 -0
  83. package/dist/gc/gcReferenceGraphAlgorithm.cjs +49 -0
  84. package/dist/gc/gcReferenceGraphAlgorithm.cjs.map +1 -0
  85. package/dist/gc/gcReferenceGraphAlgorithm.d.ts +16 -0
  86. package/dist/gc/gcReferenceGraphAlgorithm.d.ts.map +1 -0
  87. package/dist/{summarizerTypes.js → gc/gcSummaryDefinitions.cjs} +1 -6
  88. package/dist/gc/gcSummaryDefinitions.cjs.map +1 -0
  89. package/dist/gc/gcSummaryDefinitions.d.ts +52 -0
  90. package/dist/gc/gcSummaryDefinitions.d.ts.map +1 -0
  91. package/dist/gc/gcSummaryStateTracker.cjs +213 -0
  92. package/dist/gc/gcSummaryStateTracker.cjs.map +1 -0
  93. package/dist/gc/gcSummaryStateTracker.d.ts +94 -0
  94. package/dist/gc/gcSummaryStateTracker.d.ts.map +1 -0
  95. package/dist/gc/gcTelemetry.cjs +298 -0
  96. package/dist/gc/gcTelemetry.cjs.map +1 -0
  97. package/dist/gc/gcTelemetry.d.ts +92 -0
  98. package/dist/gc/gcTelemetry.d.ts.map +1 -0
  99. package/dist/gc/gcUnreferencedStateTracker.cjs +118 -0
  100. package/dist/gc/gcUnreferencedStateTracker.cjs.map +1 -0
  101. package/dist/gc/gcUnreferencedStateTracker.d.ts +40 -0
  102. package/dist/gc/gcUnreferencedStateTracker.d.ts.map +1 -0
  103. package/dist/gc/index.cjs +44 -0
  104. package/dist/gc/index.cjs.map +1 -0
  105. package/dist/gc/index.d.ts +13 -0
  106. package/dist/gc/index.d.ts.map +1 -0
  107. package/dist/index.cjs +47 -0
  108. package/dist/index.cjs.map +1 -0
  109. package/dist/index.d.ts +19 -8
  110. package/dist/index.d.ts.map +1 -1
  111. package/dist/messageTypes.cjs +37 -0
  112. package/dist/messageTypes.cjs.map +1 -0
  113. package/dist/messageTypes.d.ts +142 -0
  114. package/dist/messageTypes.d.ts.map +1 -0
  115. package/dist/metadata.cjs +7 -0
  116. package/dist/metadata.cjs.map +1 -0
  117. package/dist/metadata.d.ts +24 -0
  118. package/dist/metadata.d.ts.map +1 -0
  119. package/dist/opLifecycle/batchManager.cjs +139 -0
  120. package/dist/opLifecycle/batchManager.cjs.map +1 -0
  121. package/dist/opLifecycle/batchManager.d.ts +48 -0
  122. package/dist/opLifecycle/batchManager.d.ts.map +1 -0
  123. package/dist/opLifecycle/definitions.cjs +7 -0
  124. package/dist/opLifecycle/definitions.cjs.map +1 -0
  125. package/dist/opLifecycle/definitions.d.ts +83 -0
  126. package/dist/opLifecycle/definitions.d.ts.map +1 -0
  127. package/dist/opLifecycle/index.cjs +26 -0
  128. package/dist/opLifecycle/index.cjs.map +1 -0
  129. package/dist/opLifecycle/index.d.ts +13 -0
  130. package/dist/opLifecycle/index.d.ts.map +1 -0
  131. package/dist/opLifecycle/opCompressor.cjs +84 -0
  132. package/dist/opLifecycle/opCompressor.cjs.map +1 -0
  133. package/dist/opLifecycle/opCompressor.d.ts +18 -0
  134. package/dist/opLifecycle/opCompressor.d.ts.map +1 -0
  135. package/dist/opLifecycle/opDecompressor.cjs +132 -0
  136. package/dist/opLifecycle/opDecompressor.cjs.map +1 -0
  137. package/dist/opLifecycle/opDecompressor.d.ts +25 -0
  138. package/dist/opLifecycle/opDecompressor.d.ts.map +1 -0
  139. package/dist/opLifecycle/opGroupingManager.cjs +95 -0
  140. package/dist/opLifecycle/opGroupingManager.cjs.map +1 -0
  141. package/dist/opLifecycle/opGroupingManager.d.ts +22 -0
  142. package/dist/opLifecycle/opGroupingManager.d.ts.map +1 -0
  143. package/dist/opLifecycle/opSplitter.cjs +202 -0
  144. package/dist/opLifecycle/opSplitter.cjs.map +1 -0
  145. package/dist/opLifecycle/opSplitter.d.ts +61 -0
  146. package/dist/opLifecycle/opSplitter.d.ts.map +1 -0
  147. package/dist/opLifecycle/outbox.cjs +323 -0
  148. package/dist/opLifecycle/outbox.cjs.map +1 -0
  149. package/dist/opLifecycle/outbox.d.ts +104 -0
  150. package/dist/opLifecycle/outbox.d.ts.map +1 -0
  151. package/dist/opLifecycle/remoteMessageProcessor.cjs +136 -0
  152. package/dist/opLifecycle/remoteMessageProcessor.cjs.map +1 -0
  153. package/dist/opLifecycle/remoteMessageProcessor.d.ts +47 -0
  154. package/dist/opLifecycle/remoteMessageProcessor.d.ts.map +1 -0
  155. package/dist/opProperties.cjs +17 -0
  156. package/dist/opProperties.cjs.map +1 -0
  157. package/dist/opProperties.d.ts +7 -0
  158. package/dist/opProperties.d.ts.map +1 -0
  159. package/dist/{packageVersion.js → packageVersion.cjs} +2 -2
  160. package/dist/packageVersion.cjs.map +1 -0
  161. package/dist/packageVersion.d.ts +1 -1
  162. package/dist/packageVersion.d.ts.map +1 -1
  163. package/dist/pendingStateManager.cjs +282 -0
  164. package/dist/pendingStateManager.cjs.map +1 -0
  165. package/dist/pendingStateManager.d.ts +32 -69
  166. package/dist/pendingStateManager.d.ts.map +1 -1
  167. package/dist/scheduleManager.cjs +258 -0
  168. package/dist/scheduleManager.cjs.map +1 -0
  169. package/dist/scheduleManager.d.ts +31 -0
  170. package/dist/scheduleManager.d.ts.map +1 -0
  171. package/dist/storageServiceWithAttachBlobs.cjs +32 -0
  172. package/dist/storageServiceWithAttachBlobs.cjs.map +1 -0
  173. package/dist/storageServiceWithAttachBlobs.d.ts +17 -0
  174. package/dist/storageServiceWithAttachBlobs.d.ts.map +1 -0
  175. package/dist/summary/index.cjs +51 -0
  176. package/dist/summary/index.cjs.map +1 -0
  177. package/dist/summary/index.d.ts +17 -0
  178. package/dist/summary/index.d.ts.map +1 -0
  179. package/dist/{orderedClientElection.js → summary/orderedClientElection.cjs} +100 -84
  180. package/dist/summary/orderedClientElection.cjs.map +1 -0
  181. package/{lib → dist/summary}/orderedClientElection.d.ts +41 -18
  182. package/dist/summary/orderedClientElection.d.ts.map +1 -0
  183. package/dist/{runWhileConnectedCoordinator.js → summary/runWhileConnectedCoordinator.cjs} +12 -10
  184. package/dist/summary/runWhileConnectedCoordinator.cjs.map +1 -0
  185. package/{lib → dist/summary}/runWhileConnectedCoordinator.d.ts +8 -2
  186. package/dist/summary/runWhileConnectedCoordinator.d.ts.map +1 -0
  187. package/dist/summary/runningSummarizer.cjs +679 -0
  188. package/dist/summary/runningSummarizer.cjs.map +1 -0
  189. package/dist/summary/runningSummarizer.d.ts +128 -0
  190. package/dist/summary/runningSummarizer.d.ts.map +1 -0
  191. package/dist/summary/summarizer.cjs +263 -0
  192. package/dist/summary/summarizer.cjs.map +1 -0
  193. package/dist/{summarizer.d.ts → summary/summarizer.d.ts} +18 -33
  194. package/dist/summary/summarizer.d.ts.map +1 -0
  195. package/dist/{summarizerClientElection.js → summary/summarizerClientElection.cjs} +15 -46
  196. package/dist/summary/summarizerClientElection.cjs.map +1 -0
  197. package/{lib → dist/summary}/summarizerClientElection.d.ts +4 -4
  198. package/dist/summary/summarizerClientElection.d.ts.map +1 -0
  199. package/dist/summary/summarizerHeuristics.cjs +156 -0
  200. package/dist/summary/summarizerHeuristics.cjs.map +1 -0
  201. package/{lib → dist/summary}/summarizerHeuristics.d.ts +28 -6
  202. package/dist/summary/summarizerHeuristics.d.ts.map +1 -0
  203. package/dist/summary/summarizerNode/index.cjs +12 -0
  204. package/dist/summary/summarizerNode/index.cjs.map +1 -0
  205. package/dist/summary/summarizerNode/index.d.ts +8 -0
  206. package/dist/summary/summarizerNode/index.d.ts.map +1 -0
  207. package/dist/summary/summarizerNode/summarizerNode.cjs +526 -0
  208. package/dist/summary/summarizerNode/summarizerNode.cjs.map +1 -0
  209. package/dist/summary/summarizerNode/summarizerNode.d.ts +167 -0
  210. package/dist/summary/summarizerNode/summarizerNode.d.ts.map +1 -0
  211. package/dist/summary/summarizerNode/summarizerNodeUtils.cjs +130 -0
  212. package/dist/summary/summarizerNode/summarizerNodeUtils.cjs.map +1 -0
  213. package/dist/summary/summarizerNode/summarizerNodeUtils.d.ts +121 -0
  214. package/dist/summary/summarizerNode/summarizerNodeUtils.d.ts.map +1 -0
  215. package/dist/summary/summarizerNode/summarizerNodeWithGc.cjs +375 -0
  216. package/dist/summary/summarizerNode/summarizerNodeWithGc.cjs.map +1 -0
  217. package/dist/summary/summarizerNode/summarizerNodeWithGc.d.ts +153 -0
  218. package/dist/summary/summarizerNode/summarizerNodeWithGc.d.ts.map +1 -0
  219. package/dist/summary/summarizerTypes.cjs +7 -0
  220. package/dist/summary/summarizerTypes.cjs.map +1 -0
  221. package/{lib → dist/summary}/summarizerTypes.d.ts +233 -83
  222. package/dist/summary/summarizerTypes.d.ts.map +1 -0
  223. package/dist/{summaryCollection.js → summary/summaryCollection.cjs} +80 -49
  224. package/dist/summary/summaryCollection.cjs.map +1 -0
  225. package/dist/{summaryCollection.d.ts → summary/summaryCollection.d.ts} +23 -5
  226. package/dist/summary/summaryCollection.d.ts.map +1 -0
  227. package/dist/{summaryFormat.js → summary/summaryFormat.cjs} +29 -26
  228. package/dist/summary/summaryFormat.cjs.map +1 -0
  229. package/{lib → dist/summary}/summaryFormat.d.ts +25 -30
  230. package/dist/summary/summaryFormat.d.ts.map +1 -0
  231. package/dist/{summaryGenerator.js → summary/summaryGenerator.cjs} +162 -74
  232. package/dist/summary/summaryGenerator.cjs.map +1 -0
  233. package/dist/{summaryGenerator.d.ts → summary/summaryGenerator.d.ts} +53 -11
  234. package/dist/summary/summaryGenerator.d.ts.map +1 -0
  235. package/dist/{summaryManager.js → summary/summaryManager.cjs} +98 -55
  236. package/dist/summary/summaryManager.cjs.map +1 -0
  237. package/{lib → dist/summary}/summaryManager.d.ts +13 -11
  238. package/dist/summary/summaryManager.d.ts.map +1 -0
  239. package/dist/{throttler.js → throttler.cjs} +21 -21
  240. package/dist/throttler.cjs.map +1 -0
  241. package/dist/throttler.d.ts +2 -2
  242. package/dist/throttler.d.ts.map +1 -1
  243. package/dist/tsdoc-metadata.json +11 -0
  244. package/lib/{batchTracker.d.ts → batchTracker.d.mts} +6 -5
  245. package/lib/batchTracker.d.mts.map +1 -0
  246. package/lib/{batchTracker.js → batchTracker.mjs} +9 -8
  247. package/lib/batchTracker.mjs.map +1 -0
  248. package/lib/blobManager.d.mts +191 -0
  249. package/lib/blobManager.d.mts.map +1 -0
  250. package/lib/blobManager.mjs +699 -0
  251. package/lib/blobManager.mjs.map +1 -0
  252. package/lib/{connectionTelemetry.d.ts → connectionTelemetry.d.mts} +2 -2
  253. package/lib/connectionTelemetry.d.mts.map +1 -0
  254. package/lib/connectionTelemetry.mjs +226 -0
  255. package/lib/connectionTelemetry.mjs.map +1 -0
  256. package/lib/container-runtime-alpha.d.mts +1677 -0
  257. package/lib/container-runtime-beta.d.mts +250 -0
  258. package/lib/container-runtime-public.d.mts +250 -0
  259. package/lib/container-runtime-untrimmed.d.mts +1792 -0
  260. package/lib/{containerHandleContext.d.ts → containerHandleContext.d.mts} +1 -1
  261. package/lib/containerHandleContext.d.mts.map +1 -0
  262. package/lib/{containerHandleContext.js → containerHandleContext.mjs} +4 -2
  263. package/lib/containerHandleContext.mjs.map +1 -0
  264. package/lib/containerRuntime.d.mts +809 -0
  265. package/lib/containerRuntime.d.mts.map +1 -0
  266. package/lib/containerRuntime.mjs +2526 -0
  267. package/lib/containerRuntime.mjs.map +1 -0
  268. package/lib/{dataStore.d.ts → dataStore.d.mts} +4 -4
  269. package/lib/dataStore.d.mts.map +1 -0
  270. package/lib/{dataStore.js → dataStore.mjs} +52 -43
  271. package/lib/dataStore.mjs.map +1 -0
  272. package/lib/{dataStoreContext.d.ts → dataStoreContext.d.mts} +74 -42
  273. package/lib/dataStoreContext.d.mts.map +1 -0
  274. package/lib/{dataStoreContext.js → dataStoreContext.mjs} +309 -213
  275. package/lib/dataStoreContext.mjs.map +1 -0
  276. package/lib/{dataStoreContexts.d.ts → dataStoreContexts.d.mts} +2 -2
  277. package/lib/dataStoreContexts.d.mts.map +1 -0
  278. package/lib/{dataStoreContexts.js → dataStoreContexts.mjs} +12 -8
  279. package/lib/dataStoreContexts.mjs.map +1 -0
  280. package/lib/{dataStoreRegistry.d.ts → dataStoreRegistry.d.mts} +3 -0
  281. package/lib/dataStoreRegistry.d.mts.map +1 -0
  282. package/lib/{dataStoreRegistry.js → dataStoreRegistry.mjs} +7 -6
  283. package/lib/dataStoreRegistry.mjs.map +1 -0
  284. package/lib/{dataStores.d.ts → dataStores.d.mts} +56 -26
  285. package/lib/dataStores.d.mts.map +1 -0
  286. package/lib/{dataStores.js → dataStores.mjs} +254 -105
  287. package/lib/dataStores.mjs.map +1 -0
  288. package/lib/deltaManagerProxyBase.d.mts +35 -0
  289. package/lib/deltaManagerProxyBase.d.mts.map +1 -0
  290. package/lib/deltaManagerProxyBase.mjs +73 -0
  291. package/lib/deltaManagerProxyBase.mjs.map +1 -0
  292. package/lib/deltaManagerSummarizerProxy.d.mts +19 -0
  293. package/lib/deltaManagerSummarizerProxy.d.mts.map +1 -0
  294. package/lib/deltaManagerSummarizerProxy.mjs +38 -0
  295. package/lib/deltaManagerSummarizerProxy.mjs.map +1 -0
  296. package/lib/{deltaScheduler.d.ts → deltaScheduler.d.mts} +8 -6
  297. package/lib/deltaScheduler.d.mts.map +1 -0
  298. package/lib/{deltaScheduler.js → deltaScheduler.mjs} +22 -15
  299. package/lib/deltaScheduler.mjs.map +1 -0
  300. package/lib/error.d.mts +14 -0
  301. package/lib/error.d.mts.map +1 -0
  302. package/lib/error.mjs +17 -0
  303. package/lib/error.mjs.map +1 -0
  304. package/lib/gc/garbageCollection.d.mts +224 -0
  305. package/lib/gc/garbageCollection.d.mts.map +1 -0
  306. package/lib/gc/garbageCollection.mjs +857 -0
  307. package/lib/gc/garbageCollection.mjs.map +1 -0
  308. package/lib/gc/gcConfigs.d.mts +23 -0
  309. package/lib/gc/gcConfigs.d.mts.map +1 -0
  310. package/lib/gc/gcConfigs.mjs +149 -0
  311. package/lib/gc/gcConfigs.mjs.map +1 -0
  312. package/lib/gc/gcDefinitions.d.mts +437 -0
  313. package/lib/gc/gcDefinitions.d.mts.map +1 -0
  314. package/lib/gc/gcDefinitions.mjs +93 -0
  315. package/lib/gc/gcDefinitions.mjs.map +1 -0
  316. package/lib/gc/gcHelpers.d.mts +71 -0
  317. package/lib/gc/gcHelpers.d.mts.map +1 -0
  318. package/lib/gc/gcHelpers.mjs +222 -0
  319. package/lib/gc/gcHelpers.mjs.map +1 -0
  320. package/lib/gc/gcReferenceGraphAlgorithm.d.mts +16 -0
  321. package/lib/gc/gcReferenceGraphAlgorithm.d.mts.map +1 -0
  322. package/lib/gc/gcReferenceGraphAlgorithm.mjs +45 -0
  323. package/lib/gc/gcReferenceGraphAlgorithm.mjs.map +1 -0
  324. package/lib/gc/gcSummaryDefinitions.d.mts +52 -0
  325. package/lib/gc/gcSummaryDefinitions.d.mts.map +1 -0
  326. package/lib/gc/gcSummaryDefinitions.mjs +6 -0
  327. package/lib/gc/gcSummaryDefinitions.mjs.map +1 -0
  328. package/lib/gc/gcSummaryStateTracker.d.mts +94 -0
  329. package/lib/gc/gcSummaryStateTracker.d.mts.map +1 -0
  330. package/lib/gc/gcSummaryStateTracker.mjs +209 -0
  331. package/lib/gc/gcSummaryStateTracker.mjs.map +1 -0
  332. package/lib/gc/gcTelemetry.d.mts +92 -0
  333. package/lib/gc/gcTelemetry.d.mts.map +1 -0
  334. package/lib/gc/gcTelemetry.mjs +293 -0
  335. package/lib/gc/gcTelemetry.mjs.map +1 -0
  336. package/lib/gc/gcUnreferencedStateTracker.d.mts +40 -0
  337. package/lib/gc/gcUnreferencedStateTracker.d.mts.map +1 -0
  338. package/lib/gc/gcUnreferencedStateTracker.mjs +114 -0
  339. package/lib/gc/gcUnreferencedStateTracker.mjs.map +1 -0
  340. package/lib/gc/index.d.mts +13 -0
  341. package/lib/gc/index.d.mts.map +1 -0
  342. package/lib/gc/index.mjs +12 -0
  343. package/lib/gc/index.mjs.map +1 -0
  344. package/lib/index.d.mts +25 -0
  345. package/lib/index.d.mts.map +1 -0
  346. package/lib/index.mjs +24 -0
  347. package/lib/index.mjs.map +1 -0
  348. package/lib/messageTypes.d.mts +142 -0
  349. package/lib/messageTypes.d.mts.map +1 -0
  350. package/lib/messageTypes.mjs +34 -0
  351. package/lib/messageTypes.mjs.map +1 -0
  352. package/lib/metadata.d.mts +24 -0
  353. package/lib/metadata.d.mts.map +1 -0
  354. package/lib/metadata.mjs +6 -0
  355. package/lib/metadata.mjs.map +1 -0
  356. package/lib/opLifecycle/batchManager.d.mts +48 -0
  357. package/lib/opLifecycle/batchManager.d.mts.map +1 -0
  358. package/lib/opLifecycle/batchManager.mjs +133 -0
  359. package/lib/opLifecycle/batchManager.mjs.map +1 -0
  360. package/lib/opLifecycle/definitions.d.mts +83 -0
  361. package/lib/opLifecycle/definitions.d.mts.map +1 -0
  362. package/lib/opLifecycle/definitions.mjs +6 -0
  363. package/lib/opLifecycle/definitions.mjs.map +1 -0
  364. package/lib/opLifecycle/index.d.mts +13 -0
  365. package/lib/opLifecycle/index.d.mts.map +1 -0
  366. package/lib/opLifecycle/index.mjs +12 -0
  367. package/lib/opLifecycle/index.mjs.map +1 -0
  368. package/lib/opLifecycle/opCompressor.d.mts +18 -0
  369. package/lib/opLifecycle/opCompressor.d.mts.map +1 -0
  370. package/lib/opLifecycle/opCompressor.mjs +80 -0
  371. package/lib/opLifecycle/opCompressor.mjs.map +1 -0
  372. package/lib/opLifecycle/opDecompressor.d.mts +25 -0
  373. package/lib/opLifecycle/opDecompressor.d.mts.map +1 -0
  374. package/lib/opLifecycle/opDecompressor.mjs +128 -0
  375. package/lib/opLifecycle/opDecompressor.mjs.map +1 -0
  376. package/lib/opLifecycle/opGroupingManager.d.mts +22 -0
  377. package/lib/opLifecycle/opGroupingManager.d.mts.map +1 -0
  378. package/lib/opLifecycle/opGroupingManager.mjs +91 -0
  379. package/lib/opLifecycle/opGroupingManager.mjs.map +1 -0
  380. package/lib/opLifecycle/opSplitter.d.mts +61 -0
  381. package/lib/opLifecycle/opSplitter.d.mts.map +1 -0
  382. package/lib/opLifecycle/opSplitter.mjs +197 -0
  383. package/lib/opLifecycle/opSplitter.mjs.map +1 -0
  384. package/lib/opLifecycle/outbox.d.mts +104 -0
  385. package/lib/opLifecycle/outbox.d.mts.map +1 -0
  386. package/lib/opLifecycle/outbox.mjs +318 -0
  387. package/lib/opLifecycle/outbox.mjs.map +1 -0
  388. package/lib/opLifecycle/remoteMessageProcessor.d.mts +47 -0
  389. package/lib/opLifecycle/remoteMessageProcessor.d.mts.map +1 -0
  390. package/lib/opLifecycle/remoteMessageProcessor.mjs +131 -0
  391. package/lib/opLifecycle/remoteMessageProcessor.mjs.map +1 -0
  392. package/lib/opProperties.d.mts +7 -0
  393. package/lib/opProperties.d.mts.map +1 -0
  394. package/lib/opProperties.mjs +13 -0
  395. package/lib/opProperties.mjs.map +1 -0
  396. package/lib/{packageVersion.d.ts → packageVersion.d.mts} +1 -1
  397. package/lib/{packageVersion.d.ts.map → packageVersion.d.mts.map} +1 -1
  398. package/lib/{packageVersion.js → packageVersion.mjs} +2 -2
  399. package/lib/packageVersion.mjs.map +1 -0
  400. package/lib/{pendingStateManager.d.ts → pendingStateManager.d.mts} +32 -69
  401. package/lib/pendingStateManager.d.mts.map +1 -0
  402. package/lib/pendingStateManager.mjs +275 -0
  403. package/lib/pendingStateManager.mjs.map +1 -0
  404. package/lib/scheduleManager.d.mts +27 -0
  405. package/lib/scheduleManager.d.mts.map +1 -0
  406. package/lib/scheduleManager.mjs +254 -0
  407. package/lib/scheduleManager.mjs.map +1 -0
  408. package/lib/storageServiceWithAttachBlobs.d.mts +17 -0
  409. package/lib/storageServiceWithAttachBlobs.d.mts.map +1 -0
  410. package/lib/storageServiceWithAttachBlobs.mjs +28 -0
  411. package/lib/storageServiceWithAttachBlobs.mjs.map +1 -0
  412. package/lib/summary/index.d.mts +17 -0
  413. package/lib/summary/index.d.mts.map +1 -0
  414. package/lib/summary/index.mjs +16 -0
  415. package/lib/summary/index.mjs.map +1 -0
  416. package/{dist/orderedClientElection.d.ts → lib/summary/orderedClientElection.d.mts} +41 -22
  417. package/lib/summary/orderedClientElection.d.mts.map +1 -0
  418. package/lib/{orderedClientElection.js → summary/orderedClientElection.mjs} +95 -79
  419. package/lib/summary/orderedClientElection.mjs.map +1 -0
  420. package/{dist/runWhileConnectedCoordinator.d.ts → lib/summary/runWhileConnectedCoordinator.d.mts} +9 -3
  421. package/lib/summary/runWhileConnectedCoordinator.d.mts.map +1 -0
  422. package/lib/{runWhileConnectedCoordinator.js → summary/runWhileConnectedCoordinator.mjs} +12 -10
  423. package/lib/summary/runWhileConnectedCoordinator.mjs.map +1 -0
  424. package/lib/summary/runningSummarizer.d.mts +128 -0
  425. package/lib/summary/runningSummarizer.d.mts.map +1 -0
  426. package/lib/summary/runningSummarizer.mjs +675 -0
  427. package/lib/summary/runningSummarizer.mjs.map +1 -0
  428. package/lib/{summarizer.d.ts → summary/summarizer.d.mts} +20 -35
  429. package/lib/summary/summarizer.d.mts.map +1 -0
  430. package/lib/summary/summarizer.mjs +257 -0
  431. package/lib/summary/summarizer.mjs.map +1 -0
  432. package/{dist/summarizerClientElection.d.ts → lib/summary/summarizerClientElection.d.mts} +6 -6
  433. package/lib/summary/summarizerClientElection.d.mts.map +1 -0
  434. package/lib/{summarizerClientElection.js → summary/summarizerClientElection.mjs} +14 -45
  435. package/lib/summary/summarizerClientElection.mjs.map +1 -0
  436. package/{dist/summarizerHeuristics.d.ts → lib/summary/summarizerHeuristics.d.mts} +29 -7
  437. package/lib/summary/summarizerHeuristics.d.mts.map +1 -0
  438. package/lib/summary/summarizerHeuristics.mjs +151 -0
  439. package/lib/summary/summarizerHeuristics.mjs.map +1 -0
  440. package/lib/summary/summarizerNode/index.d.mts +8 -0
  441. package/lib/summary/summarizerNode/index.d.mts.map +1 -0
  442. package/lib/summary/summarizerNode/index.mjs +7 -0
  443. package/lib/summary/summarizerNode/index.mjs.map +1 -0
  444. package/lib/summary/summarizerNode/summarizerNode.d.mts +167 -0
  445. package/lib/summary/summarizerNode/summarizerNode.d.mts.map +1 -0
  446. package/lib/summary/summarizerNode/summarizerNode.mjs +521 -0
  447. package/lib/summary/summarizerNode/summarizerNode.mjs.map +1 -0
  448. package/lib/summary/summarizerNode/summarizerNodeUtils.d.mts +121 -0
  449. package/lib/summary/summarizerNode/summarizerNodeUtils.d.mts.map +1 -0
  450. package/lib/summary/summarizerNode/summarizerNodeUtils.mjs +123 -0
  451. package/lib/summary/summarizerNode/summarizerNodeUtils.mjs.map +1 -0
  452. package/lib/summary/summarizerNode/summarizerNodeWithGc.d.mts +153 -0
  453. package/lib/summary/summarizerNode/summarizerNodeWithGc.d.mts.map +1 -0
  454. package/lib/summary/summarizerNode/summarizerNodeWithGc.mjs +370 -0
  455. package/lib/summary/summarizerNode/summarizerNodeWithGc.mjs.map +1 -0
  456. package/{dist/summarizerTypes.d.ts → lib/summary/summarizerTypes.d.mts} +234 -84
  457. package/lib/summary/summarizerTypes.d.mts.map +1 -0
  458. package/lib/summary/summarizerTypes.mjs +6 -0
  459. package/lib/summary/summarizerTypes.mjs.map +1 -0
  460. package/lib/{summaryCollection.d.ts → summary/summaryCollection.d.mts} +23 -5
  461. package/lib/summary/summaryCollection.d.mts.map +1 -0
  462. package/lib/{summaryCollection.js → summary/summaryCollection.mjs} +76 -45
  463. package/lib/summary/summaryCollection.mjs.map +1 -0
  464. package/{dist/summaryFormat.d.ts → lib/summary/summaryFormat.d.mts} +25 -30
  465. package/lib/summary/summaryFormat.d.mts.map +1 -0
  466. package/lib/{summaryFormat.js → summary/summaryFormat.mjs} +30 -26
  467. package/lib/summary/summaryFormat.mjs.map +1 -0
  468. package/lib/summary/summaryGenerator.d.mts +127 -0
  469. package/lib/summary/summaryGenerator.d.mts.map +1 -0
  470. package/lib/{summaryGenerator.js → summary/summaryGenerator.mjs} +153 -67
  471. package/lib/summary/summaryGenerator.mjs.map +1 -0
  472. package/{dist/summaryManager.d.ts → lib/summary/summaryManager.d.mts} +15 -13
  473. package/lib/summary/summaryManager.d.mts.map +1 -0
  474. package/lib/{summaryManager.js → summary/summaryManager.mjs} +94 -51
  475. package/lib/summary/summaryManager.mjs.map +1 -0
  476. package/lib/{throttler.d.ts → throttler.d.mts} +2 -2
  477. package/lib/throttler.d.mts.map +1 -0
  478. package/lib/{throttler.js → throttler.mjs} +21 -21
  479. package/lib/throttler.mjs.map +1 -0
  480. package/package.json +173 -72
  481. package/prettier.config.cjs +8 -0
  482. package/src/batchTracker.ts +59 -54
  483. package/src/blobManager.ts +937 -294
  484. package/src/connectionTelemetry.ts +342 -252
  485. package/src/containerHandleContext.ts +27 -29
  486. package/src/containerRuntime.ts +3879 -3143
  487. package/src/dataStore.ts +170 -140
  488. package/src/dataStoreContext.ts +1166 -986
  489. package/src/dataStoreContexts.ts +176 -163
  490. package/src/dataStoreRegistry.ts +29 -21
  491. package/src/dataStores.ts +921 -678
  492. package/src/deltaManagerProxyBase.ts +111 -0
  493. package/src/deltaManagerSummarizerProxy.ts +49 -0
  494. package/src/deltaScheduler.ts +161 -156
  495. package/src/error.ts +21 -0
  496. package/src/gc/garbageCollection.md +106 -0
  497. package/src/gc/garbageCollection.ts +1153 -0
  498. package/src/gc/gcConfigs.ts +216 -0
  499. package/src/gc/gcDefinitions.ts +502 -0
  500. package/src/gc/gcHelpers.ts +284 -0
  501. package/src/gc/gcReferenceGraphAlgorithm.ts +52 -0
  502. package/src/gc/gcSummaryDefinitions.ts +54 -0
  503. package/src/gc/gcSummaryStateTracker.ts +299 -0
  504. package/src/gc/gcTelemetry.ts +423 -0
  505. package/src/gc/gcUnreferencedStateTracker.ts +153 -0
  506. package/src/gc/index.ts +59 -0
  507. package/src/index.ts +101 -74
  508. package/src/messageTypes.ts +238 -0
  509. package/src/metadata.ts +26 -0
  510. package/src/opLifecycle/README.md +321 -0
  511. package/src/opLifecycle/batchManager.ts +179 -0
  512. package/src/opLifecycle/definitions.ts +89 -0
  513. package/src/opLifecycle/index.ts +19 -0
  514. package/src/opLifecycle/opCompressor.ts +99 -0
  515. package/src/opLifecycle/opDecompressor.ts +190 -0
  516. package/src/opLifecycle/opGroupingManager.ts +133 -0
  517. package/src/opLifecycle/opSplitter.ts +279 -0
  518. package/src/opLifecycle/outbox.ts +471 -0
  519. package/src/opLifecycle/remoteMessageProcessor.ts +175 -0
  520. package/src/opProperties.ts +21 -0
  521. package/src/packageVersion.ts +1 -1
  522. package/src/pendingStateManager.ts +396 -465
  523. package/src/scheduleManager.ts +358 -0
  524. package/src/storageServiceWithAttachBlobs.ts +38 -0
  525. package/src/summary/index.ts +109 -0
  526. package/src/summary/orderedClientElection.ts +571 -0
  527. package/src/summary/runWhileConnectedCoordinator.ts +117 -0
  528. package/src/summary/runningSummarizer.ts +920 -0
  529. package/src/summary/summarizer.ts +352 -0
  530. package/src/summary/summarizerClientElection.ts +140 -0
  531. package/src/summary/summarizerHeuristics.ts +227 -0
  532. package/src/summary/summarizerNode/index.ts +12 -0
  533. package/src/summary/summarizerNode/summarizerNode.ts +725 -0
  534. package/src/summary/summarizerNode/summarizerNodeUtils.ts +206 -0
  535. package/src/summary/summarizerNode/summarizerNodeWithGc.ts +571 -0
  536. package/src/summary/summarizerTypes.ts +631 -0
  537. package/src/summary/summaryCollection.ts +474 -0
  538. package/src/summary/summaryFormat.ts +249 -0
  539. package/src/summary/summaryGenerator.ts +539 -0
  540. package/src/summary/summaryManager.ts +452 -0
  541. package/src/throttler.ts +131 -122
  542. package/tsc-multi.test.json +4 -0
  543. package/tsconfig.json +11 -13
  544. package/dist/batchTracker.js.map +0 -1
  545. package/dist/blobManager.js +0 -249
  546. package/dist/blobManager.js.map +0 -1
  547. package/dist/connectionTelemetry.js +0 -178
  548. package/dist/connectionTelemetry.js.map +0 -1
  549. package/dist/containerHandleContext.js.map +0 -1
  550. package/dist/containerRuntime.js +0 -2174
  551. package/dist/containerRuntime.js.map +0 -1
  552. package/dist/dataStore.js.map +0 -1
  553. package/dist/dataStoreContext.js.map +0 -1
  554. package/dist/dataStoreContexts.js.map +0 -1
  555. package/dist/dataStoreRegistry.js.map +0 -1
  556. package/dist/dataStores.js.map +0 -1
  557. package/dist/deltaScheduler.js.map +0 -1
  558. package/dist/garbageCollection.d.ts +0 -319
  559. package/dist/garbageCollection.d.ts.map +0 -1
  560. package/dist/garbageCollection.js +0 -993
  561. package/dist/garbageCollection.js.map +0 -1
  562. package/dist/index.js +0 -33
  563. package/dist/index.js.map +0 -1
  564. package/dist/opTelemetry.d.ts +0 -22
  565. package/dist/opTelemetry.d.ts.map +0 -1
  566. package/dist/opTelemetry.js +0 -60
  567. package/dist/opTelemetry.js.map +0 -1
  568. package/dist/orderedClientElection.d.ts.map +0 -1
  569. package/dist/orderedClientElection.js.map +0 -1
  570. package/dist/packageVersion.js.map +0 -1
  571. package/dist/pendingStateManager.js +0 -346
  572. package/dist/pendingStateManager.js.map +0 -1
  573. package/dist/runWhileConnectedCoordinator.d.ts.map +0 -1
  574. package/dist/runWhileConnectedCoordinator.js.map +0 -1
  575. package/dist/runningSummarizer.d.ts +0 -93
  576. package/dist/runningSummarizer.d.ts.map +0 -1
  577. package/dist/runningSummarizer.js +0 -384
  578. package/dist/runningSummarizer.js.map +0 -1
  579. package/dist/serializedSnapshotStorage.d.ts +0 -58
  580. package/dist/serializedSnapshotStorage.d.ts.map +0 -1
  581. package/dist/serializedSnapshotStorage.js +0 -108
  582. package/dist/serializedSnapshotStorage.js.map +0 -1
  583. package/dist/summarizer.d.ts.map +0 -1
  584. package/dist/summarizer.js +0 -348
  585. package/dist/summarizer.js.map +0 -1
  586. package/dist/summarizerClientElection.d.ts.map +0 -1
  587. package/dist/summarizerClientElection.js.map +0 -1
  588. package/dist/summarizerHandle.d.ts +0 -12
  589. package/dist/summarizerHandle.d.ts.map +0 -1
  590. package/dist/summarizerHandle.js +0 -22
  591. package/dist/summarizerHandle.js.map +0 -1
  592. package/dist/summarizerHeuristics.d.ts.map +0 -1
  593. package/dist/summarizerHeuristics.js +0 -84
  594. package/dist/summarizerHeuristics.js.map +0 -1
  595. package/dist/summarizerTypes.d.ts.map +0 -1
  596. package/dist/summarizerTypes.js.map +0 -1
  597. package/dist/summaryCollection.d.ts.map +0 -1
  598. package/dist/summaryCollection.js.map +0 -1
  599. package/dist/summaryFormat.d.ts.map +0 -1
  600. package/dist/summaryFormat.js.map +0 -1
  601. package/dist/summaryGenerator.d.ts.map +0 -1
  602. package/dist/summaryGenerator.js.map +0 -1
  603. package/dist/summaryManager.d.ts.map +0 -1
  604. package/dist/summaryManager.js.map +0 -1
  605. package/dist/throttler.js.map +0 -1
  606. package/garbageCollection.md +0 -41
  607. package/lib/batchTracker.d.ts.map +0 -1
  608. package/lib/batchTracker.js.map +0 -1
  609. package/lib/blobManager.d.ts +0 -95
  610. package/lib/blobManager.d.ts.map +0 -1
  611. package/lib/blobManager.js +0 -244
  612. package/lib/blobManager.js.map +0 -1
  613. package/lib/connectionTelemetry.d.ts.map +0 -1
  614. package/lib/connectionTelemetry.js +0 -174
  615. package/lib/connectionTelemetry.js.map +0 -1
  616. package/lib/containerHandleContext.d.ts.map +0 -1
  617. package/lib/containerHandleContext.js.map +0 -1
  618. package/lib/containerRuntime.d.ts +0 -615
  619. package/lib/containerRuntime.d.ts.map +0 -1
  620. package/lib/containerRuntime.js +0 -2166
  621. package/lib/containerRuntime.js.map +0 -1
  622. package/lib/dataStore.d.ts.map +0 -1
  623. package/lib/dataStore.js.map +0 -1
  624. package/lib/dataStoreContext.d.ts.map +0 -1
  625. package/lib/dataStoreContext.js.map +0 -1
  626. package/lib/dataStoreContexts.d.ts.map +0 -1
  627. package/lib/dataStoreContexts.js.map +0 -1
  628. package/lib/dataStoreRegistry.d.ts.map +0 -1
  629. package/lib/dataStoreRegistry.js.map +0 -1
  630. package/lib/dataStores.d.ts.map +0 -1
  631. package/lib/dataStores.js.map +0 -1
  632. package/lib/deltaScheduler.d.ts.map +0 -1
  633. package/lib/deltaScheduler.js.map +0 -1
  634. package/lib/garbageCollection.d.ts +0 -319
  635. package/lib/garbageCollection.d.ts.map +0 -1
  636. package/lib/garbageCollection.js +0 -989
  637. package/lib/garbageCollection.js.map +0 -1
  638. package/lib/index.d.ts +0 -14
  639. package/lib/index.d.ts.map +0 -1
  640. package/lib/index.js +0 -13
  641. package/lib/index.js.map +0 -1
  642. package/lib/opTelemetry.d.ts +0 -22
  643. package/lib/opTelemetry.d.ts.map +0 -1
  644. package/lib/opTelemetry.js +0 -56
  645. package/lib/opTelemetry.js.map +0 -1
  646. package/lib/orderedClientElection.d.ts.map +0 -1
  647. package/lib/orderedClientElection.js.map +0 -1
  648. package/lib/packageVersion.js.map +0 -1
  649. package/lib/pendingStateManager.d.ts.map +0 -1
  650. package/lib/pendingStateManager.js +0 -339
  651. package/lib/pendingStateManager.js.map +0 -1
  652. package/lib/runWhileConnectedCoordinator.d.ts.map +0 -1
  653. package/lib/runWhileConnectedCoordinator.js.map +0 -1
  654. package/lib/runningSummarizer.d.ts +0 -93
  655. package/lib/runningSummarizer.d.ts.map +0 -1
  656. package/lib/runningSummarizer.js +0 -380
  657. package/lib/runningSummarizer.js.map +0 -1
  658. package/lib/serializedSnapshotStorage.d.ts +0 -58
  659. package/lib/serializedSnapshotStorage.d.ts.map +0 -1
  660. package/lib/serializedSnapshotStorage.js +0 -104
  661. package/lib/serializedSnapshotStorage.js.map +0 -1
  662. package/lib/summarizer.d.ts.map +0 -1
  663. package/lib/summarizer.js +0 -342
  664. package/lib/summarizer.js.map +0 -1
  665. package/lib/summarizerClientElection.d.ts.map +0 -1
  666. package/lib/summarizerClientElection.js.map +0 -1
  667. package/lib/summarizerHandle.d.ts +0 -12
  668. package/lib/summarizerHandle.d.ts.map +0 -1
  669. package/lib/summarizerHandle.js +0 -18
  670. package/lib/summarizerHandle.js.map +0 -1
  671. package/lib/summarizerHeuristics.d.ts.map +0 -1
  672. package/lib/summarizerHeuristics.js +0 -79
  673. package/lib/summarizerHeuristics.js.map +0 -1
  674. package/lib/summarizerTypes.d.ts.map +0 -1
  675. package/lib/summarizerTypes.js +0 -9
  676. package/lib/summarizerTypes.js.map +0 -1
  677. package/lib/summaryCollection.d.ts.map +0 -1
  678. package/lib/summaryCollection.js.map +0 -1
  679. package/lib/summaryFormat.d.ts.map +0 -1
  680. package/lib/summaryFormat.js.map +0 -1
  681. package/lib/summaryGenerator.d.ts +0 -85
  682. package/lib/summaryGenerator.d.ts.map +0 -1
  683. package/lib/summaryGenerator.js.map +0 -1
  684. package/lib/summaryManager.d.ts.map +0 -1
  685. package/lib/summaryManager.js.map +0 -1
  686. package/lib/throttler.d.ts.map +0 -1
  687. package/lib/throttler.js.map +0 -1
  688. package/src/garbageCollection.ts +0 -1434
  689. package/src/opTelemetry.ts +0 -71
  690. package/src/orderedClientElection.ts +0 -511
  691. package/src/runWhileConnectedCoordinator.ts +0 -106
  692. package/src/runningSummarizer.ts +0 -550
  693. package/src/serializedSnapshotStorage.ts +0 -146
  694. package/src/summarizer.ts +0 -438
  695. package/src/summarizerClientElection.ts +0 -161
  696. package/src/summarizerHandle.ts +0 -21
  697. package/src/summarizerHeuristics.ts +0 -108
  698. package/src/summarizerTypes.ts +0 -462
  699. package/src/summaryCollection.ts +0 -406
  700. package/src/summaryFormat.ts +0 -239
  701. package/src/summaryGenerator.ts +0 -427
  702. package/src/summaryManager.ts +0 -368
  703. package/tsconfig.esnext.json +0 -7
@@ -0,0 +1,2531 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ContainerRuntime = exports.makeLegacySendBatchFn = exports.getDeviceSpec = exports.agentSchedulerId = exports.isRuntimeMessage = exports.RuntimeMessage = exports.defaultPendingOpsRetryDelayMs = exports.defaultPendingOpsWaitTimeoutMs = exports.CompressionAlgorithms = exports.defaultRuntimeHeaderData = exports.InactiveResponseHeaderKey = exports.TombstoneResponseHeaderKey = exports.AllowInactiveRequestHeaderKey = exports.AllowTombstoneRequestHeaderKey = exports.RuntimeHeaders = exports.DefaultSummaryConfiguration = void 0;
4
+ const container_definitions_1 = require("@fluidframework/container-definitions");
5
+ const core_utils_1 = require("@fluidframework/core-utils");
6
+ const client_utils_1 = require("@fluid-internal/client-utils");
7
+ const telemetry_utils_1 = require("@fluidframework/telemetry-utils");
8
+ const driver_definitions_1 = require("@fluidframework/driver-definitions");
9
+ const driver_utils_1 = require("@fluidframework/driver-utils");
10
+ const protocol_definitions_1 = require("@fluidframework/protocol-definitions");
11
+ const runtime_definitions_1 = require("@fluidframework/runtime-definitions");
12
+ const runtime_utils_1 = require("@fluidframework/runtime-utils");
13
+ const uuid_1 = require("uuid");
14
+ const containerHandleContext_1 = require("./containerHandleContext.cjs");
15
+ const dataStoreRegistry_1 = require("./dataStoreRegistry.cjs");
16
+ const connectionTelemetry_1 = require("./connectionTelemetry.cjs");
17
+ const pendingStateManager_1 = require("./pendingStateManager.cjs");
18
+ const packageVersion_1 = require("./packageVersion.cjs");
19
+ const blobManager_1 = require("./blobManager.cjs");
20
+ const dataStores_1 = require("./dataStores.cjs");
21
+ const summary_1 = require("./summary/index.cjs");
22
+ const throttler_1 = require("./throttler.cjs");
23
+ const gc_1 = require("./gc/index.cjs");
24
+ const dataStore_1 = require("./dataStore.cjs");
25
+ const batchTracker_1 = require("./batchTracker.cjs");
26
+ const scheduleManager_1 = require("./scheduleManager.cjs");
27
+ const opLifecycle_1 = require("./opLifecycle/index.cjs");
28
+ const deltaManagerSummarizerProxy_1 = require("./deltaManagerSummarizerProxy.cjs");
29
+ const messageTypes_1 = require("./messageTypes.cjs");
30
+ /**
31
+ * Utility to implement compat behaviors given an unknown message type
32
+ * The parameters are typed to support compile-time enforcement of handling all known types/behaviors
33
+ *
34
+ * @param _unknownContainerRuntimeMessageType - Typed as something unexpected, to ensure all known types have been
35
+ * handled before calling this function (e.g. in a switch statement).
36
+ * @param compatBehavior - Typed redundantly with CompatModeBehavior to ensure handling is added when updating that type
37
+ */
38
+ function compatBehaviorAllowsMessageType(_unknownContainerRuntimeMessageType, compatBehavior) {
39
+ // undefined defaults to same behavior as "FailToProcess"
40
+ return compatBehavior === "Ignore";
41
+ }
42
+ /**
43
+ * @alpha
44
+ */
45
+ exports.DefaultSummaryConfiguration = {
46
+ state: "enabled",
47
+ minIdleTime: 0,
48
+ maxIdleTime: 30 * 1000,
49
+ maxTime: 60 * 1000,
50
+ maxOps: 100,
51
+ minOpsForLastSummaryAttempt: 10,
52
+ maxAckWaitTime: 3 * 60 * 1000,
53
+ maxOpsSinceLastSummary: 7000,
54
+ initialSummarizerDelayMs: 5 * 1000,
55
+ nonRuntimeOpWeight: 0.1,
56
+ runtimeOpWeight: 1.0,
57
+ nonRuntimeHeuristicThreshold: 20,
58
+ };
59
+ /**
60
+ * Accepted header keys for requests coming to the runtime.
61
+ * @internal
62
+ */
63
+ var RuntimeHeaders;
64
+ (function (RuntimeHeaders) {
65
+ /** True to wait for a data store to be created and loaded before returning it. */
66
+ RuntimeHeaders["wait"] = "wait";
67
+ /** True if the request is coming from an IFluidHandle. */
68
+ RuntimeHeaders["viaHandle"] = "viaHandle";
69
+ })(RuntimeHeaders || (exports.RuntimeHeaders = RuntimeHeaders = {}));
70
+ /** True if a tombstoned object should be returned without erroring
71
+ * @alpha
72
+ */
73
+ exports.AllowTombstoneRequestHeaderKey = "allowTombstone"; // Belongs in the enum above, but avoiding the breaking change
74
+ /**
75
+ * [IRRELEVANT IF throwOnInactiveLoad OPTION NOT SET] True if an inactive object should be returned without erroring
76
+ * @internal
77
+ */
78
+ exports.AllowInactiveRequestHeaderKey = "allowInactive"; // Belongs in the enum above, but avoiding the breaking change
79
+ /**
80
+ * Tombstone error responses will have this header set to true
81
+ * @alpha
82
+ */
83
+ exports.TombstoneResponseHeaderKey = "isTombstoned";
84
+ /**
85
+ * Inactive error responses will have this header set to true
86
+ * @alpha
87
+ */
88
+ exports.InactiveResponseHeaderKey = "isInactive";
89
+ /** Default values for Runtime Headers */
90
+ exports.defaultRuntimeHeaderData = {
91
+ wait: true,
92
+ viaHandle: false,
93
+ allowTombstone: false,
94
+ allowInactive: false,
95
+ };
96
+ /**
97
+ * Available compression algorithms for op compression.
98
+ * @alpha
99
+ */
100
+ var CompressionAlgorithms;
101
+ (function (CompressionAlgorithms) {
102
+ CompressionAlgorithms["lz4"] = "lz4";
103
+ })(CompressionAlgorithms || (exports.CompressionAlgorithms = CompressionAlgorithms = {}));
104
+ const maxConsecutiveReconnectsKey = "Fluid.ContainerRuntime.MaxConsecutiveReconnects";
105
+ const defaultFlushMode = runtime_definitions_1.FlushMode.TurnBased;
106
+ // The actual limit is 1Mb (socket.io and Kafka limits)
107
+ // We can't estimate it fully, as we
108
+ // - do not know what properties relay service will add
109
+ // - we do not stringify final op, thus we do not know how much escaping will be added.
110
+ const defaultMaxBatchSizeInBytes = 700 * 1024;
111
+ const defaultCompressionConfig = {
112
+ // Batches with content size exceeding this value will be compressed
113
+ minimumBatchSizeInBytes: 614400,
114
+ compressionAlgorithm: CompressionAlgorithms.lz4,
115
+ };
116
+ const defaultChunkSizeInBytes = 204800;
117
+ /** The default time to wait for pending ops to be processed during summarization */
118
+ exports.defaultPendingOpsWaitTimeoutMs = 1000;
119
+ /** The default time to delay a summarization retry attempt when there are pending ops */
120
+ exports.defaultPendingOpsRetryDelayMs = 1000;
121
+ /**
122
+ * Instead of refreshing from latest because we do not have 100% confidence in the state
123
+ * of the current system, we should close the summarizer and let it recover.
124
+ * This delay's goal is to prevent tight restart loops
125
+ */
126
+ const defaultCloseSummarizerDelayMs = 5000; // 5 seconds
127
+ /**
128
+ * @deprecated use ContainerRuntimeMessageType instead
129
+ * @internal
130
+ */
131
+ var RuntimeMessage;
132
+ (function (RuntimeMessage) {
133
+ RuntimeMessage["FluidDataStoreOp"] = "component";
134
+ RuntimeMessage["Attach"] = "attach";
135
+ RuntimeMessage["ChunkedOp"] = "chunkedOp";
136
+ RuntimeMessage["BlobAttach"] = "blobAttach";
137
+ RuntimeMessage["Rejoin"] = "rejoin";
138
+ RuntimeMessage["Alias"] = "alias";
139
+ RuntimeMessage["Operation"] = "op";
140
+ })(RuntimeMessage || (exports.RuntimeMessage = RuntimeMessage = {}));
141
+ /**
142
+ * @deprecated please use version in driver-utils
143
+ * @internal
144
+ */
145
+ function isRuntimeMessage(message) {
146
+ return Object.values(RuntimeMessage).includes(message.type);
147
+ }
148
+ exports.isRuntimeMessage = isRuntimeMessage;
149
+ /**
150
+ * Legacy ID for the built-in AgentScheduler. To minimize disruption while removing it, retaining this as a
151
+ * special-case for document dirty state. Ultimately we should have no special-cases from the
152
+ * ContainerRuntime's perspective.
153
+ * @internal
154
+ */
155
+ exports.agentSchedulerId = "_scheduler";
156
+ // safely check navigator and get the hardware spec value
157
+ function getDeviceSpec() {
158
+ try {
159
+ if (typeof navigator === "object" && navigator !== null) {
160
+ return {
161
+ deviceMemory: navigator.deviceMemory,
162
+ hardwareConcurrency: navigator.hardwareConcurrency,
163
+ };
164
+ }
165
+ }
166
+ catch { }
167
+ return {};
168
+ }
169
+ exports.getDeviceSpec = getDeviceSpec;
170
+ /**
171
+ * Older loader doesn't have a submitBatchFn member, this is the older way of submitting a batch.
172
+ * Rather than exposing the submitFn (now deprecated) and IDeltaManager (dangerous to hand out) to the Outbox,
173
+ * we can provide a partially-applied function to keep those items private to the ContainerRuntime.
174
+ */
175
+ const makeLegacySendBatchFn = (submitFn, deltaManager) => (batch) => {
176
+ for (const message of batch.content) {
177
+ submitFn(protocol_definitions_1.MessageType.Operation,
178
+ // For back-compat (submitFn only works on deserialized content)
179
+ message.contents === undefined ? undefined : JSON.parse(message.contents), true, // batch
180
+ message.metadata);
181
+ }
182
+ deltaManager.flush();
183
+ };
184
+ exports.makeLegacySendBatchFn = makeLegacySendBatchFn;
185
+ const summarizerRequestUrl = "_summarizer";
186
+ /**
187
+ * Create and retrieve the summmarizer
188
+ */
189
+ async function createSummarizer(loader, url) {
190
+ const request = {
191
+ headers: {
192
+ [container_definitions_1.LoaderHeader.cache]: false,
193
+ [container_definitions_1.LoaderHeader.clientDetails]: {
194
+ capabilities: { interactive: false },
195
+ type: summary_1.summarizerClientType,
196
+ },
197
+ [driver_definitions_1.DriverHeader.summarizingClient]: true,
198
+ [container_definitions_1.LoaderHeader.reconnect]: false,
199
+ },
200
+ url,
201
+ };
202
+ const resolvedContainer = await loader.resolve(request);
203
+ let fluidObject;
204
+ // Older containers may not have the "getEntryPoint" API
205
+ // ! This check will need to stay until LTS of loader moves past 2.0.0-internal.7.0.0
206
+ if (resolvedContainer.getEntryPoint !== undefined) {
207
+ fluidObject = await resolvedContainer.getEntryPoint();
208
+ }
209
+ else {
210
+ const response = await resolvedContainer.request({
211
+ url: `/${summarizerRequestUrl}`,
212
+ });
213
+ if (response.status !== 200 || response.mimeType !== "fluid/object") {
214
+ throw (0, runtime_utils_1.responseToException)(response, request);
215
+ }
216
+ fluidObject = response.value;
217
+ }
218
+ if (fluidObject?.ISummarizer === undefined) {
219
+ throw new telemetry_utils_1.UsageError("Fluid object does not implement ISummarizer");
220
+ }
221
+ return fluidObject.ISummarizer;
222
+ }
223
+ /**
224
+ * Represents the runtime of the container. Contains helper functions/state of the container.
225
+ * It will define the store level mappings.
226
+ * @alpha
227
+ */
228
+ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
229
+ /**
230
+ * Load the stores from a snapshot and returns the runtime.
231
+ * @param params - An object housing the runtime properties:
232
+ * - context - Context of the container.
233
+ * - registryEntries - Mapping from data store types to their corresponding factories.
234
+ * - existing - Pass 'true' if loading from an existing snapshot.
235
+ * - requestHandler - (optional) Request handler for the request() method of the container runtime.
236
+ * Only relevant for back-compat while we remove the request() method and move fully to entryPoint as the main pattern.
237
+ * - runtimeOptions - Additional options to be passed to the runtime
238
+ * - containerScope - runtime services provided with context
239
+ * - containerRuntimeCtor - Constructor to use to create the ContainerRuntime instance.
240
+ * This allows mixin classes to leverage this method to define their own async initializer.
241
+ * - provideEntryPoint - Promise that resolves to an object which will act as entryPoint for the Container.
242
+ * This object should provide all the functionality that the Container is expected to provide to the loader layer.
243
+ */
244
+ static async loadRuntime(params) {
245
+ const { context, registryEntries, existing, requestHandler, provideEntryPoint, runtimeOptions = {}, containerScope = {}, containerRuntimeCtor = ContainerRuntime, } = params;
246
+ // If taggedLogger exists, use it. Otherwise, wrap the vanilla logger:
247
+ // back-compat: Remove the TaggedLoggerAdapter fallback once all the host are using loader > 0.45
248
+ const backCompatContext = context;
249
+ const passLogger = backCompatContext.taggedLogger ??
250
+ // eslint-disable-next-line import/no-deprecated
251
+ new telemetry_utils_1.TaggedLoggerAdapter(backCompatContext.logger);
252
+ const logger = (0, telemetry_utils_1.createChildLogger)({
253
+ logger: passLogger,
254
+ properties: {
255
+ all: {
256
+ runtimeVersion: packageVersion_1.pkgVersion,
257
+ },
258
+ },
259
+ });
260
+ const { summaryOptions = {}, gcOptions = {}, loadSequenceNumberVerification = "close", flushMode = defaultFlushMode, compressionOptions = defaultCompressionConfig, maxBatchSizeInBytes = defaultMaxBatchSizeInBytes, enableRuntimeIdCompressor = false, chunkSizeInBytes = defaultChunkSizeInBytes, enableOpReentryCheck = false, enableGroupedBatching = false, } = runtimeOptions;
261
+ const registry = new dataStoreRegistry_1.FluidDataStoreRegistry(registryEntries);
262
+ const tryFetchBlob = async (blobName) => {
263
+ const blobId = context.baseSnapshot?.blobs[blobName];
264
+ if (context.baseSnapshot && blobId) {
265
+ // IContainerContext storage api return type still has undefined in 0.39 package version.
266
+ // So once we release 0.40 container-defn package we can remove this check.
267
+ (0, core_utils_1.assert)(context.storage !== undefined, 0x1f5 /* "Attached state should have storage" */);
268
+ return (0, driver_utils_1.readAndParse)(context.storage, blobId);
269
+ }
270
+ };
271
+ const [chunks, metadata, electedSummarizerData, aliases, serializedIdCompressor] = await Promise.all([
272
+ tryFetchBlob(summary_1.chunksBlobName),
273
+ tryFetchBlob(summary_1.metadataBlobName),
274
+ tryFetchBlob(summary_1.electedSummarizerBlobName),
275
+ tryFetchBlob(summary_1.aliasBlobName),
276
+ tryFetchBlob(summary_1.idCompressorBlobName),
277
+ ]);
278
+ // read snapshot blobs needed for BlobManager to load
279
+ const blobManagerSnapshot = await blobManager_1.BlobManager.load(context.baseSnapshot?.trees[summary_1.blobsTreeName], async (id) => {
280
+ // IContainerContext storage api return type still has undefined in 0.39 package version.
281
+ // So once we release 0.40 container-defn package we can remove this check.
282
+ (0, core_utils_1.assert)(context.storage !== undefined, 0x256 /* "storage undefined in attached container" */);
283
+ return (0, driver_utils_1.readAndParse)(context.storage, id);
284
+ });
285
+ // Verify summary runtime sequence number matches protocol sequence number.
286
+ const runtimeSequenceNumber = metadata?.message?.sequenceNumber;
287
+ // When we load with pending state, we reuse an old snapshot so we don't expect these numbers to match
288
+ if (!context.pendingLocalState && runtimeSequenceNumber !== undefined) {
289
+ const protocolSequenceNumber = context.deltaManager.initialSequenceNumber;
290
+ // Unless bypass is explicitly set, then take action when sequence numbers mismatch.
291
+ if (loadSequenceNumberVerification !== "bypass" &&
292
+ runtimeSequenceNumber !== protocolSequenceNumber) {
293
+ // "Load from summary, runtime metadata sequenceNumber !== initialSequenceNumber"
294
+ const error = new telemetry_utils_1.DataCorruptionError(
295
+ // pre-0.58 error message: SummaryMetadataMismatch
296
+ "Summary metadata mismatch", { runtimeVersion: packageVersion_1.pkgVersion, runtimeSequenceNumber, protocolSequenceNumber });
297
+ if (loadSequenceNumberVerification === "log") {
298
+ logger.sendErrorEvent({ eventName: "SequenceNumberMismatch" }, error);
299
+ }
300
+ else {
301
+ context.closeFn(error);
302
+ }
303
+ }
304
+ }
305
+ const idCompressorEnabled = metadata?.idCompressorEnabled ?? runtimeOptions.enableRuntimeIdCompressor ?? false;
306
+ let idCompressor;
307
+ if (idCompressorEnabled) {
308
+ const { createIdCompressor, deserializeIdCompressor, createSessionId } = await import("@fluidframework/id-compressor");
309
+ const pendingLocalState = context.pendingLocalState;
310
+ if (pendingLocalState?.pendingIdCompressorState !== undefined) {
311
+ idCompressor = deserializeIdCompressor(pendingLocalState.pendingIdCompressorState);
312
+ }
313
+ else if (serializedIdCompressor !== undefined) {
314
+ idCompressor = deserializeIdCompressor(serializedIdCompressor, createSessionId());
315
+ }
316
+ else {
317
+ idCompressor = createIdCompressor(logger);
318
+ }
319
+ }
320
+ const runtime = new containerRuntimeCtor(context, registry, metadata, electedSummarizerData, chunks ?? [], aliases ?? [], {
321
+ summaryOptions,
322
+ gcOptions,
323
+ loadSequenceNumberVerification,
324
+ flushMode,
325
+ compressionOptions,
326
+ maxBatchSizeInBytes,
327
+ chunkSizeInBytes,
328
+ enableRuntimeIdCompressor,
329
+ enableOpReentryCheck,
330
+ enableGroupedBatching,
331
+ }, containerScope, logger, existing, blobManagerSnapshot, context.storage, idCompressor, provideEntryPoint, requestHandler, undefined);
332
+ // Apply stashed ops with a reference sequence number equal to the sequence number of the snapshot,
333
+ // or zero. This must be done before Container replays saved ops.
334
+ await runtime.pendingStateManager.applyStashedOpsAt(runtimeSequenceNumber ?? 0);
335
+ // Initialize the base state of the runtime before it's returned.
336
+ await runtime.initializeBaseState();
337
+ return runtime;
338
+ }
339
+ get clientId() {
340
+ return this._getClientId();
341
+ }
342
+ get storage() {
343
+ return this._storage;
344
+ }
345
+ get flushMode() {
346
+ return this._flushMode;
347
+ }
348
+ get scope() {
349
+ return this.containerScope;
350
+ }
351
+ get IFluidDataStoreRegistry() {
352
+ return this.registry;
353
+ }
354
+ get attachState() {
355
+ return this._getAttachState();
356
+ }
357
+ get IFluidHandleContext() {
358
+ return this.handleContext;
359
+ }
360
+ /**
361
+ * Invokes the given callback and expects that no ops are submitted
362
+ * until execution finishes. If an op is submitted, an error will be raised.
363
+ *
364
+ * Can be disabled by feature gate `Fluid.ContainerRuntime.DisableOpReentryCheck`
365
+ *
366
+ * @param callback - the callback to be invoked
367
+ */
368
+ ensureNoDataModelChanges(callback) {
369
+ this.ensureNoDataModelChangesCalls++;
370
+ try {
371
+ return callback();
372
+ }
373
+ finally {
374
+ this.ensureNoDataModelChangesCalls--;
375
+ }
376
+ }
377
+ get connected() {
378
+ return this._connected;
379
+ }
380
+ /** clientId of parent (non-summarizing) container that owns summarizer container */
381
+ get summarizerClientId() {
382
+ return this.summarizerClientElection?.electedClientId;
383
+ }
384
+ get disposed() {
385
+ return this._disposed;
386
+ }
387
+ get summarizer() {
388
+ (0, core_utils_1.assert)(this._summarizer !== undefined, 0x257 /* "This is not summarizing container" */);
389
+ return this._summarizer;
390
+ }
391
+ isSummariesDisabled() {
392
+ return this.summaryConfiguration.state === "disabled";
393
+ }
394
+ isHeuristicsDisabled() {
395
+ return this.summaryConfiguration.state === "disableHeuristics";
396
+ }
397
+ getMaxOpsSinceLastSummary() {
398
+ return this.summaryConfiguration.state !== "disabled"
399
+ ? this.summaryConfiguration.maxOpsSinceLastSummary
400
+ : 0;
401
+ }
402
+ getInitialSummarizerDelayMs() {
403
+ // back-compat: initialSummarizerDelayMs was moved from ISummaryRuntimeOptions
404
+ // to ISummaryConfiguration in 0.60.
405
+ if (this.runtimeOptions.summaryOptions.initialSummarizerDelayMs !== undefined) {
406
+ return this.runtimeOptions.summaryOptions.initialSummarizerDelayMs;
407
+ }
408
+ return this.summaryConfiguration.state !== "disabled"
409
+ ? this.summaryConfiguration.initialSummarizerDelayMs
410
+ : 0;
411
+ }
412
+ /** If false, loading or using a Tombstoned object should merely log, not fail */
413
+ get gcTombstoneEnforcementAllowed() {
414
+ return this.garbageCollector.tombstoneEnforcementAllowed;
415
+ }
416
+ /** If true, throw an error when a tombstone data store is used. */
417
+ get gcThrowOnTombstoneUsage() {
418
+ return this.garbageCollector.throwOnTombstoneUsage;
419
+ }
420
+ /***/
421
+ constructor(context, registry, metadata, electedSummarizerData, chunks, dataStoreAliasMap, runtimeOptions, containerScope, logger, existing, blobManagerSnapshot, _storage, idCompressor, provideEntryPoint, requestHandler, summaryConfiguration = {
422
+ // the defaults
423
+ ...exports.DefaultSummaryConfiguration,
424
+ // the runtime configuration overrides
425
+ ...runtimeOptions.summaryOptions?.summaryConfigOverrides,
426
+ }) {
427
+ super();
428
+ this.registry = registry;
429
+ this.runtimeOptions = runtimeOptions;
430
+ this.containerScope = containerScope;
431
+ this.logger = logger;
432
+ this._storage = _storage;
433
+ this.requestHandler = requestHandler;
434
+ this.summaryConfiguration = summaryConfiguration;
435
+ this.imminentClosure = false;
436
+ this.defaultMaxConsecutiveReconnects = 7;
437
+ this._orderSequentiallyCalls = 0;
438
+ this.flushTaskExists = false;
439
+ this.consecutiveReconnects = 0;
440
+ this.ensureNoDataModelChangesCalls = 0;
441
+ /**
442
+ * Tracks the number of detected reentrant ops to report,
443
+ * in order to self-throttle the telemetry events.
444
+ *
445
+ * This should be removed as part of ADO:2322
446
+ */
447
+ this.opReentryCallsToReport = 5;
448
+ this._disposed = false;
449
+ this.emitDirtyDocumentEvent = true;
450
+ this.defaultTelemetrySignalSampleCount = 100;
451
+ this._perfSignalData = {
452
+ signalsLost: 0,
453
+ signalSequenceNumber: 0,
454
+ signalTimestamp: 0,
455
+ trackingSignalSequenceNumber: undefined,
456
+ };
457
+ const { options, clientDetails, connected, baseSnapshot, submitFn, submitBatchFn, submitSummaryFn, submitSignalFn, disposeFn, closeFn, deltaManager, quorum, audience, loader, pendingLocalState, supportedFeatures, } = context;
458
+ this.innerDeltaManager = deltaManager;
459
+ this.deltaManager = new deltaManagerSummarizerProxy_1.DeltaManagerSummarizerProxy(this.innerDeltaManager);
460
+ // Here we could wrap/intercept on these functions to block/modify outgoing messages if needed.
461
+ // This makes ContainerRuntime the final gatekeeper for outgoing messages.
462
+ this.submitFn = submitFn;
463
+ this.submitBatchFn = submitBatchFn;
464
+ this.submitSummaryFn = submitSummaryFn;
465
+ this.submitSignalFn = submitSignalFn;
466
+ this.options = options;
467
+ this.clientDetails = clientDetails;
468
+ this.isSummarizerClient = this.clientDetails.type === summary_1.summarizerClientType;
469
+ this.loadedFromVersionId = context.getLoadedFromVersion()?.id;
470
+ this._getClientId = () => context.clientId;
471
+ this._getAttachState = () => context.attachState;
472
+ this.getAbsoluteUrl = async (relativeUrl) => {
473
+ if (context.getAbsoluteUrl === undefined) {
474
+ throw new Error("Driver does not implement getAbsoluteUrl");
475
+ }
476
+ if (this.attachState !== container_definitions_1.AttachState.Attached) {
477
+ return undefined;
478
+ }
479
+ return context.getAbsoluteUrl(relativeUrl);
480
+ };
481
+ // TODO: Consider that the Container could just listen to these events itself, or even more appropriately maybe the
482
+ // customer should observe dirty state on the runtime (the owner of dirty state) directly, rather than on the IContainer.
483
+ this.on("dirty", () => context.updateDirtyContainerState(true));
484
+ this.on("saved", () => context.updateDirtyContainerState(false));
485
+ // In old loaders without dispose functionality, closeFn is equivalent but will also switch container to readonly mode
486
+ this.disposeFn = disposeFn ?? closeFn;
487
+ // In cases of summarizer, we want to dispose instead since consumer doesn't interact with this container
488
+ this.closeFn = this.isSummarizerClient ? this.disposeFn : closeFn;
489
+ this.mc = (0, telemetry_utils_1.createChildMonitoringContext)({
490
+ logger: this.logger,
491
+ namespace: "ContainerRuntime",
492
+ });
493
+ let loadSummaryNumber;
494
+ // Get the container creation metadata. For new container, we initialize these. For existing containers,
495
+ // get the values from the metadata blob.
496
+ if (existing) {
497
+ this.createContainerMetadata = {
498
+ createContainerRuntimeVersion: metadata?.createContainerRuntimeVersion,
499
+ createContainerTimestamp: metadata?.createContainerTimestamp,
500
+ };
501
+ // summaryNumber was renamed from summaryCount. For older docs that haven't been opened for a long time,
502
+ // the count is reset to 0.
503
+ loadSummaryNumber = metadata?.summaryNumber ?? 0;
504
+ // Enabling the IdCompressor is a one-way operation and we only want to
505
+ // allow new containers to turn it on
506
+ this.idCompressorEnabled = metadata?.idCompressorEnabled ?? false;
507
+ }
508
+ else {
509
+ this.createContainerMetadata = {
510
+ createContainerRuntimeVersion: packageVersion_1.pkgVersion,
511
+ createContainerTimestamp: Date.now(),
512
+ };
513
+ loadSummaryNumber = 0;
514
+ this.idCompressorEnabled =
515
+ this.mc.config.getBoolean("Fluid.ContainerRuntime.IdCompressorEnabled") ??
516
+ idCompressor !== undefined;
517
+ }
518
+ this.nextSummaryNumber = loadSummaryNumber + 1;
519
+ this.messageAtLastSummary = metadata?.message;
520
+ // Note that we only need to pull the *initial* connected state from the context.
521
+ // Later updates come through calls to setConnectionState.
522
+ this._connected = connected;
523
+ this.mc.logger.sendTelemetryEvent({
524
+ eventName: "GCFeatureMatrix",
525
+ metadataValue: JSON.stringify(metadata?.gcFeatureMatrix),
526
+ inputs: JSON.stringify({
527
+ gcOptions_gcGeneration: this.runtimeOptions.gcOptions[gc_1.gcGenerationOptionName],
528
+ }),
529
+ });
530
+ this.telemetryDocumentId = metadata?.telemetryDocumentId ?? (0, uuid_1.v4)();
531
+ this.disableAttachReorder = this.mc.config.getBoolean("Fluid.ContainerRuntime.disableAttachOpReorder");
532
+ const disableChunking = this.mc.config.getBoolean("Fluid.ContainerRuntime.CompressionChunkingDisabled");
533
+ const opGroupingManager = new opLifecycle_1.OpGroupingManager({
534
+ groupedBatchingEnabled: this.groupedBatchingEnabled,
535
+ opCountThreshold: this.mc.config.getNumber("Fluid.ContainerRuntime.GroupedBatchingOpCount") ?? 2,
536
+ reentrantBatchGroupingEnabled: this.mc.config.getBoolean("Fluid.ContainerRuntime.GroupedBatchingReentrancy") ??
537
+ true,
538
+ }, this.mc.logger);
539
+ const opSplitter = new opLifecycle_1.OpSplitter(chunks, this.submitBatchFn, disableChunking === true ? Number.POSITIVE_INFINITY : runtimeOptions.chunkSizeInBytes, runtimeOptions.maxBatchSizeInBytes, this.mc.logger);
540
+ this.remoteMessageProcessor = new opLifecycle_1.RemoteMessageProcessor(opSplitter, new opLifecycle_1.OpDecompressor(this.mc.logger), opGroupingManager);
541
+ this.handleContext = new containerHandleContext_1.ContainerFluidHandleContext("", this);
542
+ if (this.summaryConfiguration.state === "enabled") {
543
+ this.validateSummaryHeuristicConfiguration(this.summaryConfiguration);
544
+ }
545
+ const disableOpReentryCheck = this.mc.config.getBoolean("Fluid.ContainerRuntime.DisableOpReentryCheck");
546
+ this.enableOpReentryCheck =
547
+ runtimeOptions.enableOpReentryCheck === true &&
548
+ // Allow for a break-glass config to override the options
549
+ disableOpReentryCheck !== true;
550
+ this.summariesDisabled = this.isSummariesDisabled();
551
+ this.heuristicsDisabled = this.isHeuristicsDisabled();
552
+ this.maxOpsSinceLastSummary = this.getMaxOpsSinceLastSummary();
553
+ this.initialSummarizerDelayMs = this.getInitialSummarizerDelayMs();
554
+ if (this.idCompressorEnabled) {
555
+ this.idCompressor = idCompressor;
556
+ }
557
+ this.maxConsecutiveReconnects =
558
+ this.mc.config.getNumber(maxConsecutiveReconnectsKey) ??
559
+ this.defaultMaxConsecutiveReconnects;
560
+ if (runtimeOptions.flushMode === runtime_definitions_1.FlushModeExperimental.Async &&
561
+ supportedFeatures?.get("referenceSequenceNumbers") !== true) {
562
+ // The loader does not support reference sequence numbers, falling back on FlushMode.TurnBased
563
+ this.mc.logger.sendErrorEvent({ eventName: "FlushModeFallback" });
564
+ this._flushMode = runtime_definitions_1.FlushMode.TurnBased;
565
+ }
566
+ else {
567
+ this._flushMode = runtimeOptions.flushMode;
568
+ }
569
+ const pendingRuntimeState = pendingLocalState;
570
+ const maxSnapshotCacheDurationMs = this._storage?.policies?.maximumCacheDurationMs;
571
+ if (maxSnapshotCacheDurationMs !== undefined &&
572
+ maxSnapshotCacheDurationMs > 5 * 24 * 60 * 60 * 1000) {
573
+ // This is a runtime enforcement of what's already explicit in the policy's type itself,
574
+ // which dictates the value is either undefined or exactly 5 days in ms.
575
+ // As long as the actual value is less than 5 days, the assumptions GC makes here are valid.
576
+ throw new telemetry_utils_1.UsageError("Driver's maximumCacheDurationMs policy cannot exceed 5 days");
577
+ }
578
+ this.garbageCollector = gc_1.GarbageCollector.create({
579
+ runtime: this,
580
+ gcOptions: this.runtimeOptions.gcOptions,
581
+ baseSnapshot,
582
+ baseLogger: this.mc.logger,
583
+ existing,
584
+ metadata,
585
+ createContainerMetadata: this.createContainerMetadata,
586
+ isSummarizerClient: this.isSummarizerClient,
587
+ getNodePackagePath: async (nodePath) => this.getGCNodePackagePath(nodePath),
588
+ getLastSummaryTimestampMs: () => this.messageAtLastSummary?.timestamp,
589
+ readAndParseBlob: async (id) => (0, driver_utils_1.readAndParse)(this.storage, id),
590
+ submitMessage: (message) => this.submit(message),
591
+ });
592
+ const loadedFromSequenceNumber = this.deltaManager.initialSequenceNumber;
593
+ this.summarizerNode = (0, summary_1.createRootSummarizerNodeWithGC)((0, telemetry_utils_1.createChildLogger)({ logger: this.logger, namespace: "SummarizerNode" }),
594
+ // Summarize function to call when summarize is called. Summarizer node always tracks summary state.
595
+ async (fullTree, trackState, telemetryContext) => this.summarizeInternal(fullTree, trackState, telemetryContext),
596
+ // Latest change sequence number, no changes since summary applied yet
597
+ loadedFromSequenceNumber,
598
+ // Summary reference sequence number, undefined if no summary yet
599
+ baseSnapshot !== undefined ? loadedFromSequenceNumber : undefined, {
600
+ // Must set to false to prevent sending summary handle which would be pointing to
601
+ // a summary with an older protocol state.
602
+ canReuseHandle: false,
603
+ // Must set to true to throw on any data stores failure that was too severe to be handled.
604
+ // We also are not decoding the base summaries at the root.
605
+ throwOnFailure: true,
606
+ // If GC should not run, let the summarizer node know so that it does not track GC state.
607
+ gcDisabled: !this.garbageCollector.shouldRunGC,
608
+ },
609
+ // Function to get GC data if needed. This will always be called by the root summarizer node to get GC data.
610
+ async (fullGC) => this.getGCDataInternal(fullGC),
611
+ // Function to get the GC details from the base snapshot we loaded from.
612
+ async () => this.garbageCollector.getBaseGCDetails());
613
+ if (baseSnapshot) {
614
+ this.summarizerNode.updateBaseSummaryState(baseSnapshot);
615
+ }
616
+ this.dataStores = new dataStores_1.DataStores((0, dataStores_1.getSummaryForDatastores)(baseSnapshot, metadata), this, (attachMsg) => this.submit({ type: messageTypes_1.ContainerMessageType.Attach, contents: attachMsg }), (id, createParam) => (summarizeInternal, getGCDataFn) => this.summarizerNode.createChild(summarizeInternal, id, createParam, undefined, getGCDataFn), (id) => this.summarizerNode.deleteChild(id), this.mc.logger, (path, timestampMs, packagePath) => this.garbageCollector.nodeUpdated(path, "Changed", timestampMs, packagePath), (path) => this.garbageCollector.isNodeDeleted(path), new Map(dataStoreAliasMap));
617
+ this.blobManager = new blobManager_1.BlobManager(this.handleContext, blobManagerSnapshot, () => this.storage, (localId, blobId) => {
618
+ if (!this.disposed) {
619
+ this.submit({ type: messageTypes_1.ContainerMessageType.BlobAttach, contents: undefined }, undefined, {
620
+ localId,
621
+ blobId,
622
+ });
623
+ }
624
+ }, (blobPath) => this.garbageCollector.nodeUpdated(blobPath, "Loaded"), (blobPath) => this.garbageCollector.isNodeDeleted(blobPath), this, pendingRuntimeState?.pendingAttachmentBlobs, (error) => this.closeFn(error));
625
+ this.scheduleManager = new scheduleManager_1.ScheduleManager(this.innerDeltaManager, this, () => this.clientId, (0, telemetry_utils_1.createChildLogger)({ logger: this.logger, namespace: "ScheduleManager" }));
626
+ this.pendingStateManager = new pendingStateManager_1.PendingStateManager({
627
+ applyStashedOp: this.applyStashedOp.bind(this),
628
+ clientId: () => this.clientId,
629
+ close: this.closeFn,
630
+ connected: () => this.connected,
631
+ reSubmit: this.reSubmit.bind(this),
632
+ reSubmitBatch: this.reSubmitBatch.bind(this),
633
+ isActiveConnection: () => this.innerDeltaManager.active,
634
+ }, pendingRuntimeState?.pending, this.logger);
635
+ const disableCompression = this.mc.config.getBoolean("Fluid.ContainerRuntime.CompressionDisabled");
636
+ const compressionOptions = disableCompression === true
637
+ ? {
638
+ minimumBatchSizeInBytes: Number.POSITIVE_INFINITY,
639
+ compressionAlgorithm: CompressionAlgorithms.lz4,
640
+ }
641
+ : runtimeOptions.compressionOptions;
642
+ const disablePartialFlush = this.mc.config.getBoolean("Fluid.ContainerRuntime.DisablePartialFlush");
643
+ const legacySendBatchFn = (0, exports.makeLegacySendBatchFn)(this.submitFn, this.innerDeltaManager);
644
+ this.outbox = new opLifecycle_1.Outbox({
645
+ shouldSend: () => this.canSendOps(),
646
+ pendingStateManager: this.pendingStateManager,
647
+ submitBatchFn: this.submitBatchFn,
648
+ legacySendBatchFn,
649
+ compressor: new opLifecycle_1.OpCompressor(this.mc.logger),
650
+ splitter: opSplitter,
651
+ config: {
652
+ compressionOptions,
653
+ maxBatchSizeInBytes: runtimeOptions.maxBatchSizeInBytes,
654
+ disablePartialFlush: disablePartialFlush === true,
655
+ },
656
+ logger: this.mc.logger,
657
+ groupingManager: opGroupingManager,
658
+ getCurrentSequenceNumbers: () => ({
659
+ referenceSequenceNumber: this.deltaManager.lastSequenceNumber,
660
+ clientSequenceNumber: this._processedClientSequenceNumber,
661
+ }),
662
+ reSubmit: this.reSubmit.bind(this),
663
+ opReentrancy: () => this.ensureNoDataModelChangesCalls > 0,
664
+ closeContainer: this.closeFn,
665
+ });
666
+ this._quorum = quorum;
667
+ this._quorum.on("removeMember", (clientId) => {
668
+ this.remoteMessageProcessor.clearPartialMessagesFor(clientId);
669
+ });
670
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
671
+ this._audience = audience;
672
+ const closeSummarizerDelayOverride = this.mc.config.getNumber("Fluid.ContainerRuntime.Test.CloseSummarizerDelayOverrideMs");
673
+ this.closeSummarizerDelayMs = closeSummarizerDelayOverride ?? defaultCloseSummarizerDelayMs;
674
+ this.validateSummaryBeforeUpload =
675
+ this.mc.config.getBoolean("Fluid.Summarizer.ValidateSummaryBeforeUpload") ?? false;
676
+ this.summaryCollection = new summary_1.SummaryCollection(this.deltaManager, this.logger);
677
+ this.dirtyContainer =
678
+ this.attachState !== container_definitions_1.AttachState.Attached || this.hasPendingMessages();
679
+ context.updateDirtyContainerState(this.dirtyContainer);
680
+ if (this.summariesDisabled) {
681
+ this.mc.logger.sendTelemetryEvent({ eventName: "SummariesDisabled" });
682
+ }
683
+ else {
684
+ const orderedClientLogger = (0, telemetry_utils_1.createChildLogger)({
685
+ logger: this.logger,
686
+ namespace: "OrderedClientElection",
687
+ });
688
+ const orderedClientCollection = new summary_1.OrderedClientCollection(orderedClientLogger, this.innerDeltaManager, this._quorum);
689
+ const orderedClientElectionForSummarizer = new summary_1.OrderedClientElection(orderedClientLogger, orderedClientCollection, electedSummarizerData ?? this.innerDeltaManager.lastSequenceNumber, summary_1.SummarizerClientElection.isClientEligible);
690
+ this.summarizerClientElection = new summary_1.SummarizerClientElection(orderedClientLogger, this.summaryCollection, orderedClientElectionForSummarizer, this.maxOpsSinceLastSummary);
691
+ if (this.isSummarizerClient) {
692
+ this._summarizer = new summary_1.Summarizer(this /* ISummarizerRuntime */, () => this.summaryConfiguration, this /* ISummarizerInternalsProvider */, this.handleContext, this.summaryCollection, async (runtime) => summary_1.RunWhileConnectedCoordinator.create(runtime,
693
+ // Summarization runs in summarizer client and needs access to the real (non-proxy) active
694
+ // information. The proxy delta manager would always return false for summarizer client.
695
+ () => this.innerDeltaManager.active));
696
+ }
697
+ else if (summary_1.SummarizerClientElection.clientDetailsPermitElection(this.clientDetails)) {
698
+ // Only create a SummaryManager and SummarizerClientElection
699
+ // if summaries are enabled and we are not the summarizer client.
700
+ const defaultAction = () => {
701
+ if (this.summaryCollection.opsSinceLastAck > this.maxOpsSinceLastSummary) {
702
+ this.mc.logger.sendTelemetryEvent({ eventName: "SummaryStatus:Behind" });
703
+ // unregister default to no log on every op after falling behind
704
+ // and register summary ack handler to re-register this handler
705
+ // after successful summary
706
+ this.summaryCollection.once(protocol_definitions_1.MessageType.SummaryAck, () => {
707
+ this.mc.logger.sendTelemetryEvent({
708
+ eventName: "SummaryStatus:CaughtUp",
709
+ });
710
+ // we've caught up, so re-register the default action to monitor for
711
+ // falling behind, and unregister ourself
712
+ this.summaryCollection.on("default", defaultAction);
713
+ });
714
+ this.summaryCollection.off("default", defaultAction);
715
+ }
716
+ };
717
+ this.summaryCollection.on("default", defaultAction);
718
+ // Create the SummaryManager and mark the initial state
719
+ this.summaryManager = new summary_1.SummaryManager(this.summarizerClientElection, this, // IConnectedState
720
+ this.summaryCollection, this.logger, this.formCreateSummarizerFn(loader), new throttler_1.Throttler(60 * 1000, // 60 sec delay window
721
+ 30 * 1000, // 30 sec max delay
722
+ // throttling function increases exponentially (0ms, 40ms, 80ms, 160ms, etc)
723
+ (0, throttler_1.formExponentialFn)({ coefficient: 20, initialDelay: 0 })), {
724
+ initialDelayMs: this.initialSummarizerDelayMs,
725
+ }, this.heuristicsDisabled);
726
+ this.summaryManager.on("summarize", (eventProps) => {
727
+ this.emit("summarize", eventProps);
728
+ });
729
+ this.summaryManager.start();
730
+ }
731
+ }
732
+ // logging hardware telemetry
733
+ logger.sendTelemetryEvent({
734
+ eventName: "DeviceSpec",
735
+ ...getDeviceSpec(),
736
+ });
737
+ this.mc.logger.sendTelemetryEvent({
738
+ eventName: "ContainerLoadStats",
739
+ ...this.createContainerMetadata,
740
+ ...this.dataStores.containerLoadStats,
741
+ summaryNumber: loadSummaryNumber,
742
+ summaryFormatVersion: metadata?.summaryFormatVersion,
743
+ disableIsolatedChannels: metadata?.disableIsolatedChannels,
744
+ gcVersion: metadata?.gcFeature,
745
+ options: JSON.stringify(runtimeOptions),
746
+ featureGates: JSON.stringify({
747
+ disableCompression,
748
+ disableOpReentryCheck,
749
+ disableChunking,
750
+ disableAttachReorder: this.disableAttachReorder,
751
+ disablePartialFlush,
752
+ idCompressorEnabled: this.idCompressorEnabled,
753
+ closeSummarizerDelayOverride,
754
+ }),
755
+ telemetryDocumentId: this.telemetryDocumentId,
756
+ groupedBatchingEnabled: this.groupedBatchingEnabled,
757
+ });
758
+ (0, connectionTelemetry_1.ReportOpPerfTelemetry)(this.clientId, this.deltaManager, this.logger);
759
+ (0, batchTracker_1.BindBatchTracker)(this, this.logger);
760
+ this.entryPoint = new core_utils_1.LazyPromise(async () => {
761
+ if (this.isSummarizerClient) {
762
+ (0, core_utils_1.assert)(this._summarizer !== undefined, 0x5bf /* Summarizer object is undefined in a summarizer client */);
763
+ return this._summarizer;
764
+ }
765
+ return provideEntryPoint(this);
766
+ });
767
+ }
768
+ /**
769
+ * Initializes the state from the base snapshot this container runtime loaded from.
770
+ */
771
+ async initializeBaseState() {
772
+ await this.garbageCollector.initializeBaseState();
773
+ }
774
+ dispose(error) {
775
+ if (this._disposed) {
776
+ return;
777
+ }
778
+ this._disposed = true;
779
+ this.mc.logger.sendTelemetryEvent({
780
+ eventName: "ContainerRuntimeDisposed",
781
+ isDirty: this.isDirty,
782
+ lastSequenceNumber: this.deltaManager.lastSequenceNumber,
783
+ attachState: this.attachState,
784
+ }, error);
785
+ if (this.summaryManager !== undefined) {
786
+ this.summaryManager.dispose();
787
+ }
788
+ this.garbageCollector.dispose();
789
+ this._summarizer?.dispose();
790
+ this.dataStores.dispose();
791
+ this.pendingStateManager.dispose();
792
+ this.emit("dispose");
793
+ this.removeAllListeners();
794
+ }
795
+ /**
796
+ * Notifies this object about the request made to the container.
797
+ * @param request - Request made to the handler.
798
+ * @deprecated Will be removed in future major release. This method needs to stay private until LTS version of Loader moves to "2.0.0-internal.7.0.0".
799
+ */
800
+ // @ts-expect-error expected to be used by LTS Loaders and Containers
801
+ async request(request) {
802
+ try {
803
+ const parser = runtime_utils_1.RequestParser.create(request);
804
+ const id = parser.pathParts[0];
805
+ if (id === summarizerRequestUrl && parser.pathParts.length === 1) {
806
+ if (this._summarizer !== undefined) {
807
+ return {
808
+ status: 200,
809
+ mimeType: "fluid/object",
810
+ value: this.summarizer,
811
+ };
812
+ }
813
+ return (0, runtime_utils_1.create404Response)(request);
814
+ }
815
+ if (this.requestHandler !== undefined) {
816
+ // eslint-disable-next-line @typescript-eslint/return-await -- Adding an await here causes test failures
817
+ return this.requestHandler(parser, this);
818
+ }
819
+ return (0, runtime_utils_1.create404Response)(request);
820
+ }
821
+ catch (error) {
822
+ return (0, runtime_utils_1.exceptionToResponse)(error);
823
+ }
824
+ }
825
+ /**
826
+ * Resolves URI representing handle
827
+ * @param request - Request made to the handler.
828
+ */
829
+ async resolveHandle(request) {
830
+ try {
831
+ const requestParser = runtime_utils_1.RequestParser.create(request);
832
+ const id = requestParser.pathParts[0];
833
+ if (id === "_channels") {
834
+ // eslint-disable-next-line @typescript-eslint/return-await -- Adding an await here causes test failures
835
+ return this.resolveHandle(requestParser.createSubRequest(1));
836
+ }
837
+ if (id === blobManager_1.BlobManager.basePath && requestParser.isLeaf(2)) {
838
+ const blob = await this.blobManager.getBlob(requestParser.pathParts[1]);
839
+ return blob
840
+ ? {
841
+ status: 200,
842
+ mimeType: "fluid/object",
843
+ value: blob,
844
+ }
845
+ : (0, runtime_utils_1.create404Response)(request);
846
+ }
847
+ else if (requestParser.pathParts.length > 0) {
848
+ // Differentiate between requesting the dataStore directly, or one of its children
849
+ const requestForChild = !requestParser.isLeaf(1);
850
+ const dataStore = await this.getDataStoreFromRequest(id, request, requestForChild);
851
+ const subRequest = requestParser.createSubRequest(1);
852
+ // We always expect createSubRequest to include a leading slash, but asserting here to protect against
853
+ // unintentionally modifying the url if that changes.
854
+ (0, core_utils_1.assert)(subRequest.url.startsWith("/"), 0x126 /* "Expected createSubRequest url to include a leading slash" */);
855
+ // eslint-disable-next-line @typescript-eslint/return-await -- Adding an await here causes test failures
856
+ return dataStore.request(subRequest);
857
+ }
858
+ return (0, runtime_utils_1.create404Response)(request);
859
+ }
860
+ catch (error) {
861
+ return (0, runtime_utils_1.exceptionToResponse)(error);
862
+ }
863
+ }
864
+ /**
865
+ * {@inheritDoc @fluidframework/container-definitions#IRuntime.getEntryPoint}
866
+ */
867
+ async getEntryPoint() {
868
+ return this.entryPoint;
869
+ }
870
+ internalId(maybeAlias) {
871
+ return this.dataStores.aliases.get(maybeAlias) ?? maybeAlias;
872
+ }
873
+ async getDataStoreFromRequest(id, request, requestForChild) {
874
+ const headerData = {};
875
+ if (typeof request.headers?.[RuntimeHeaders.wait] === "boolean") {
876
+ headerData.wait = request.headers[RuntimeHeaders.wait];
877
+ }
878
+ if (typeof request.headers?.[RuntimeHeaders.viaHandle] === "boolean") {
879
+ headerData.viaHandle = request.headers[RuntimeHeaders.viaHandle];
880
+ }
881
+ if (typeof request.headers?.[exports.AllowTombstoneRequestHeaderKey] === "boolean") {
882
+ headerData.allowTombstone = request.headers[exports.AllowTombstoneRequestHeaderKey];
883
+ }
884
+ if (typeof request.headers?.[exports.AllowInactiveRequestHeaderKey] === "boolean") {
885
+ headerData.allowInactive = request.headers[exports.AllowInactiveRequestHeaderKey];
886
+ }
887
+ // We allow Tombstone requests for sub-DataStore objects
888
+ if (requestForChild) {
889
+ headerData.allowTombstone = true;
890
+ }
891
+ await this.dataStores.waitIfPendingAlias(id);
892
+ const internalId = this.internalId(id);
893
+ const dataStoreContext = await this.dataStores.getDataStore(internalId, headerData);
894
+ // Remove query params, leading and trailing slashes from the url. This is done to make sure the format is
895
+ // the same as GC nodes id.
896
+ const urlWithoutQuery = (0, gc_1.trimLeadingAndTrailingSlashes)(request.url.split("?")[0]);
897
+ // Get the initial snapshot details which contain the data store package path.
898
+ const details = await dataStoreContext.getInitialSnapshotDetails();
899
+ // Note that this will throw if the data store is inactive or tombstoned and throwing on incorrect usage
900
+ // is configured.
901
+ this.garbageCollector.nodeUpdated(`/${urlWithoutQuery}`, "Loaded", undefined /* timestampMs */, details.pkg, request, headerData);
902
+ return dataStoreContext.realize();
903
+ }
904
+ /** Adds the container's metadata to the given summary tree. */
905
+ addMetadataToSummary(summaryTree) {
906
+ const metadata = {
907
+ ...this.createContainerMetadata,
908
+ // Increment the summary number for the next summary that will be generated.
909
+ summaryNumber: this.nextSummaryNumber++,
910
+ summaryFormatVersion: 1,
911
+ ...this.garbageCollector.getMetadata(),
912
+ // The last message processed at the time of summary. If there are no new messages, use the message from the
913
+ // last summary.
914
+ message: (0, summary_1.extractSummaryMetadataMessage)(this.deltaManager.lastMessage) ??
915
+ this.messageAtLastSummary,
916
+ telemetryDocumentId: this.telemetryDocumentId,
917
+ idCompressorEnabled: this.idCompressorEnabled ? true : undefined,
918
+ };
919
+ (0, runtime_utils_1.addBlobToSummary)(summaryTree, summary_1.metadataBlobName, JSON.stringify(metadata));
920
+ }
921
+ addContainerStateToSummary(summaryTree, fullTree, trackState, telemetryContext) {
922
+ this.addMetadataToSummary(summaryTree);
923
+ if (this.idCompressorEnabled) {
924
+ (0, core_utils_1.assert)(this.idCompressor !== undefined, 0x67a /* IdCompressor should be defined if enabled */);
925
+ const idCompressorState = JSON.stringify(this.idCompressor.serialize(false));
926
+ (0, runtime_utils_1.addBlobToSummary)(summaryTree, summary_1.idCompressorBlobName, idCompressorState);
927
+ }
928
+ if (this.remoteMessageProcessor.partialMessages.size > 0) {
929
+ const content = JSON.stringify([...this.remoteMessageProcessor.partialMessages]);
930
+ (0, runtime_utils_1.addBlobToSummary)(summaryTree, summary_1.chunksBlobName, content);
931
+ }
932
+ const dataStoreAliases = this.dataStores.aliases;
933
+ if (dataStoreAliases.size > 0) {
934
+ (0, runtime_utils_1.addBlobToSummary)(summaryTree, summary_1.aliasBlobName, JSON.stringify([...dataStoreAliases]));
935
+ }
936
+ if (this.summarizerClientElection) {
937
+ const electedSummarizerContent = JSON.stringify(this.summarizerClientElection?.serialize());
938
+ (0, runtime_utils_1.addBlobToSummary)(summaryTree, summary_1.electedSummarizerBlobName, electedSummarizerContent);
939
+ }
940
+ const blobManagerSummary = this.blobManager.summarize();
941
+ // Some storage (like git) doesn't allow empty tree, so we can omit it.
942
+ // and the blob manager can handle the tree not existing when loading
943
+ if (Object.keys(blobManagerSummary.summary.tree).length > 0) {
944
+ (0, runtime_utils_1.addTreeToSummary)(summaryTree, summary_1.blobsTreeName, blobManagerSummary);
945
+ }
946
+ const gcSummary = this.garbageCollector.summarize(fullTree, trackState, telemetryContext);
947
+ if (gcSummary !== undefined) {
948
+ (0, runtime_utils_1.addSummarizeResultToSummary)(summaryTree, runtime_definitions_1.gcTreeKey, gcSummary);
949
+ }
950
+ }
951
+ // Track how many times the container tries to reconnect with pending messages.
952
+ // This happens when the connection state is changed and we reset the counter
953
+ // when we are able to process a local op or when there are no pending messages.
954
+ // If this counter reaches a max, it's a good indicator that the container
955
+ // is not making progress and it is stuck in a retry loop.
956
+ shouldContinueReconnecting() {
957
+ if (this.maxConsecutiveReconnects <= 0) {
958
+ // Feature disabled, we never stop reconnecting
959
+ return true;
960
+ }
961
+ if (!this.hasPendingMessages()) {
962
+ // If there are no pending messages, we can always reconnect
963
+ this.resetReconnectCount();
964
+ return true;
965
+ }
966
+ if (this.consecutiveReconnects === Math.floor(this.maxConsecutiveReconnects / 2)) {
967
+ // If we're halfway through the max reconnects, send an event in order
968
+ // to better identify false positives, if any. If the rate of this event
969
+ // matches Container Close count below, we can safely cut down
970
+ // maxConsecutiveReconnects to half.
971
+ this.mc.logger.sendTelemetryEvent({
972
+ eventName: "ReconnectsWithNoProgress",
973
+ attempts: this.consecutiveReconnects,
974
+ pendingMessages: this.pendingMessagesCount,
975
+ });
976
+ }
977
+ return this.consecutiveReconnects < this.maxConsecutiveReconnects;
978
+ }
979
+ resetReconnectCount(message) {
980
+ // Chunked ops don't count towards making progress as they are sent
981
+ // in their own batches before the originating batch is sent.
982
+ // Therefore, receiving them while attempting to send the originating batch
983
+ // does not mean that the container is making any progress.
984
+ if (message?.type !== messageTypes_1.ContainerMessageType.ChunkedOp) {
985
+ this.consecutiveReconnects = 0;
986
+ }
987
+ }
988
+ replayPendingStates() {
989
+ // We need to be able to send ops to replay states
990
+ if (!this.canSendOps()) {
991
+ return;
992
+ }
993
+ // We need to temporary clear the dirty flags and disable
994
+ // dirty state change events to detect whether replaying ops
995
+ // has any effect.
996
+ // Save the old state, reset to false, disable event emit
997
+ const oldState = this.dirtyContainer;
998
+ this.dirtyContainer = false;
999
+ (0, core_utils_1.assert)(this.emitDirtyDocumentEvent, 0x127 /* "dirty document event not set on replay" */);
1000
+ this.emitDirtyDocumentEvent = false;
1001
+ let newState;
1002
+ try {
1003
+ // replay the ops
1004
+ this.pendingStateManager.replayPendingStates();
1005
+ }
1006
+ finally {
1007
+ // Save the new start and restore the old state, re-enable event emit
1008
+ newState = this.dirtyContainer;
1009
+ this.dirtyContainer = oldState;
1010
+ this.emitDirtyDocumentEvent = true;
1011
+ }
1012
+ // Officially transition from the old state to the new state.
1013
+ this.updateDocumentDirtyState(newState);
1014
+ }
1015
+ /**
1016
+ * Parse an op's type and actual content from given serialized content
1017
+ * ! Note: this format needs to be in-line with what is set in the "ContainerRuntime.submit(...)" method
1018
+ */
1019
+ // TODO: markfields: confirm Local- versus Outbound- ContainerRuntimeMessage typing
1020
+ parseLocalOpContent(serializedContents) {
1021
+ (0, core_utils_1.assert)(serializedContents !== undefined, 0x6d5 /* content must be defined */);
1022
+ const message = JSON.parse(serializedContents);
1023
+ (0, core_utils_1.assert)(message.type !== undefined, 0x6d6 /* incorrect op content format */);
1024
+ return message;
1025
+ }
1026
+ async applyStashedOp(serializedOpContent) {
1027
+ // Need to parse from string for back-compat
1028
+ const opContents = this.parseLocalOpContent(serializedOpContent);
1029
+ switch (opContents.type) {
1030
+ case messageTypes_1.ContainerMessageType.FluidDataStoreOp:
1031
+ return this.dataStores.applyStashedOp(opContents.contents);
1032
+ case messageTypes_1.ContainerMessageType.Attach:
1033
+ return this.dataStores.applyStashedAttachOp(opContents.contents);
1034
+ case messageTypes_1.ContainerMessageType.IdAllocation:
1035
+ (0, core_utils_1.assert)(this.idCompressor !== undefined, 0x67b /* IdCompressor should be defined if enabled */);
1036
+ return;
1037
+ case messageTypes_1.ContainerMessageType.Alias:
1038
+ case messageTypes_1.ContainerMessageType.BlobAttach:
1039
+ return;
1040
+ case messageTypes_1.ContainerMessageType.ChunkedOp:
1041
+ throw new Error("chunkedOp not expected here");
1042
+ case messageTypes_1.ContainerMessageType.Rejoin:
1043
+ throw new Error("rejoin not expected here");
1044
+ case messageTypes_1.ContainerMessageType.GC:
1045
+ // GC op is only sent in summarizer which should never have stashed ops.
1046
+ throw new telemetry_utils_1.LoggingError("GC op not expected to be stashed in summarizer");
1047
+ default: {
1048
+ // This should be extremely rare for stashed ops.
1049
+ // It would require a newer runtime stashing ops and then an older one applying them,
1050
+ // e.g. if an app rolled back its container version
1051
+ const compatBehavior = opContents.compatDetails?.behavior;
1052
+ if (!compatBehaviorAllowsMessageType(opContents.type, compatBehavior)) {
1053
+ const error = telemetry_utils_1.DataProcessingError.create("Stashed runtime message of unknown type", "applyStashedOp", undefined /* sequencedMessage */, {
1054
+ messageDetails: JSON.stringify({
1055
+ type: opContents.type,
1056
+ compatBehavior,
1057
+ }),
1058
+ });
1059
+ this.closeFn(error);
1060
+ throw error;
1061
+ }
1062
+ }
1063
+ }
1064
+ }
1065
+ setConnectionState(connected, clientId) {
1066
+ if (connected === false && this.delayConnectClientId !== undefined) {
1067
+ this.delayConnectClientId = undefined;
1068
+ this.mc.logger.sendTelemetryEvent({
1069
+ eventName: "UnsuccessfulConnectedTransition",
1070
+ });
1071
+ // Don't propagate "disconnected" event because we didn't propagate the previous "connected" event
1072
+ return;
1073
+ }
1074
+ // If there are stashed blobs in the pending state, we need to delay
1075
+ // propagation of the "connected" event until we have uploaded them to
1076
+ // ensure we don't submit ops referencing a blob that has not been uploaded
1077
+ const connecting = connected && !this._connected;
1078
+ if (connecting && this.blobManager.hasPendingStashedBlobs()) {
1079
+ (0, core_utils_1.assert)(!this.delayConnectClientId, 0x791 /* Connect event delay must be canceled before subsequent connect event */);
1080
+ (0, core_utils_1.assert)(!!clientId, 0x792 /* Must have clientId when connecting */);
1081
+ this.delayConnectClientId = clientId;
1082
+ this.blobManager.processStashedChanges().then(() => {
1083
+ // make sure we didn't reconnect before the promise resolved
1084
+ if (this.delayConnectClientId === clientId && !this.disposed) {
1085
+ this.delayConnectClientId = undefined;
1086
+ this.setConnectionStateCore(connected, clientId);
1087
+ }
1088
+ }, (error) => this.closeFn(error));
1089
+ return;
1090
+ }
1091
+ this.setConnectionStateCore(connected, clientId);
1092
+ }
1093
+ setConnectionStateCore(connected, clientId) {
1094
+ (0, core_utils_1.assert)(!this.delayConnectClientId, 0x394 /* connect event delay must be cleared before propagating connect event */);
1095
+ this.verifyNotClosed();
1096
+ // There might be no change of state due to Container calling this API after loading runtime.
1097
+ const changeOfState = this._connected !== connected;
1098
+ const reconnection = changeOfState && !connected;
1099
+ // We need to flush the ops currently collected by Outbox to preserve original order.
1100
+ // This flush NEEDS to happen before we set the ContainerRuntime to "connected".
1101
+ // We want these ops to get to the PendingStateManager without sending to service and have them return to the Outbox upon calling "replayPendingStates".
1102
+ if (changeOfState && connected) {
1103
+ this.flush();
1104
+ }
1105
+ this._connected = connected;
1106
+ if (!connected) {
1107
+ this._perfSignalData.signalsLost = 0;
1108
+ this._perfSignalData.signalTimestamp = 0;
1109
+ this._perfSignalData.trackingSignalSequenceNumber = undefined;
1110
+ }
1111
+ else {
1112
+ (0, core_utils_1.assert)(this.attachState === container_definitions_1.AttachState.Attached, 0x3cd /* Connection is possible only if container exists in storage */);
1113
+ }
1114
+ // Fail while disconnected
1115
+ if (reconnection) {
1116
+ this.consecutiveReconnects++;
1117
+ if (!this.shouldContinueReconnecting()) {
1118
+ this.closeFn(telemetry_utils_1.DataProcessingError.create("Runtime detected too many reconnects with no progress syncing local ops.", "setConnectionState", undefined, {
1119
+ dataLoss: 1,
1120
+ attempts: this.consecutiveReconnects,
1121
+ pendingMessages: this.pendingMessagesCount,
1122
+ }));
1123
+ return;
1124
+ }
1125
+ }
1126
+ if (changeOfState) {
1127
+ this.replayPendingStates();
1128
+ }
1129
+ this.dataStores.setConnectionState(connected, clientId);
1130
+ this.garbageCollector.setConnectionState(connected, clientId);
1131
+ (0, telemetry_utils_1.raiseConnectedEvent)(this.mc.logger, this, connected, clientId);
1132
+ }
1133
+ async notifyOpReplay(message) {
1134
+ await this.pendingStateManager.applyStashedOpsAt(message.sequenceNumber);
1135
+ }
1136
+ process(messageArg, local) {
1137
+ this.verifyNotClosed();
1138
+ // Whether or not the message appears to be a runtime message from an up-to-date client.
1139
+ // It may be a legacy runtime message (ie already unpacked and ContainerMessageType)
1140
+ // or something different, like a system message.
1141
+ const modernRuntimeMessage = messageArg.type === protocol_definitions_1.MessageType.Operation;
1142
+ // Do shallow copy of message, as the processing flow will modify it.
1143
+ // There might be multiple container instances receiving the same message.
1144
+ // We do not need to make a deep copy. Each layer will just replace message.contents itself,
1145
+ // but will not modify the contents object (likely it will replace it on the message).
1146
+ const messageCopy = { ...messageArg };
1147
+ for (const message of this.remoteMessageProcessor.process(messageCopy)) {
1148
+ if (modernRuntimeMessage) {
1149
+ this.processCore({
1150
+ // Cast it since we expect it to be this based on modernRuntimeMessage computation above.
1151
+ // There is nothing really ensuring that anytime original message.type is Operation that
1152
+ // the result messages will be so. In the end modern bool being true only directs to
1153
+ // throw error if ultimately unrecognized without compat details saying otherwise.
1154
+ message: message,
1155
+ local,
1156
+ modernRuntimeMessage,
1157
+ });
1158
+ }
1159
+ else {
1160
+ // Unrecognized message will be ignored.
1161
+ this.processCore({ message, local, modernRuntimeMessage });
1162
+ }
1163
+ }
1164
+ }
1165
+ /**
1166
+ * Direct the message to the correct subsystem for processing, and implement other side effects
1167
+ */
1168
+ processCore(messageWithContext) {
1169
+ const { message, local } = messageWithContext;
1170
+ // Surround the actual processing of the operation with messages to the schedule manager indicating
1171
+ // the beginning and end. This allows it to emit appropriate events and/or pause the processing of new
1172
+ // messages once a batch has been fully processed.
1173
+ this.scheduleManager.beforeOpProcessing(message);
1174
+ this._processedClientSequenceNumber = message.clientSequenceNumber;
1175
+ try {
1176
+ let localOpMetadata;
1177
+ if (local &&
1178
+ messageWithContext.modernRuntimeMessage &&
1179
+ message.type !== messageTypes_1.ContainerMessageType.ChunkedOp) {
1180
+ localOpMetadata = this.pendingStateManager.processPendingLocalMessage(messageWithContext.message);
1181
+ }
1182
+ // If there are no more pending messages after processing a local message,
1183
+ // the document is no longer dirty.
1184
+ if (!this.hasPendingMessages()) {
1185
+ this.updateDocumentDirtyState(false);
1186
+ }
1187
+ this.validateAndProcessRuntimeMessage(messageWithContext, localOpMetadata);
1188
+ this.emit("op", message, messageWithContext.modernRuntimeMessage);
1189
+ this.scheduleManager.afterOpProcessing(undefined, message);
1190
+ if (local) {
1191
+ // If we have processed a local op, this means that the container is
1192
+ // making progress and we can reset the counter for how many times
1193
+ // we have consecutively replayed the pending states
1194
+ this.resetReconnectCount(message);
1195
+ }
1196
+ }
1197
+ catch (e) {
1198
+ this.scheduleManager.afterOpProcessing(e, message);
1199
+ throw e;
1200
+ }
1201
+ }
1202
+ /**
1203
+ * Assuming the given message is also a TypedContainerRuntimeMessage,
1204
+ * checks its type and dispatches the message to the appropriate handler in the runtime.
1205
+ * Throws a DataProcessingError if the message looks like but doesn't conform to a known TypedContainerRuntimeMessage type.
1206
+ */
1207
+ validateAndProcessRuntimeMessage(messageWithContext, localOpMetadata) {
1208
+ // TODO: destructure message and modernRuntimeMessage once using typescript 5.2.2+
1209
+ const { local } = messageWithContext;
1210
+ switch (messageWithContext.message.type) {
1211
+ case messageTypes_1.ContainerMessageType.Attach:
1212
+ this.dataStores.processAttachMessage(messageWithContext.message, local);
1213
+ break;
1214
+ case messageTypes_1.ContainerMessageType.Alias:
1215
+ this.dataStores.processAliasMessage(messageWithContext.message, localOpMetadata, local);
1216
+ break;
1217
+ case messageTypes_1.ContainerMessageType.FluidDataStoreOp:
1218
+ this.dataStores.processFluidDataStoreOp(messageWithContext.message, local, localOpMetadata);
1219
+ break;
1220
+ case messageTypes_1.ContainerMessageType.BlobAttach:
1221
+ this.blobManager.processBlobAttachOp(messageWithContext.message, local);
1222
+ break;
1223
+ case messageTypes_1.ContainerMessageType.IdAllocation:
1224
+ (0, core_utils_1.assert)(this.idCompressor !== undefined, 0x67c /* IdCompressor should be defined if enabled */);
1225
+ // Don't re-finalize the range if we're processing a "savedOp" in
1226
+ // stashed ops flow. The compressor is stashed with these ops already processed.
1227
+ if (messageWithContext.message.metadata?.savedOp !== true) {
1228
+ this.idCompressor.finalizeCreationRange(messageWithContext.message.contents);
1229
+ }
1230
+ break;
1231
+ case messageTypes_1.ContainerMessageType.GC:
1232
+ this.garbageCollector.processMessage(messageWithContext.message, local);
1233
+ break;
1234
+ case messageTypes_1.ContainerMessageType.ChunkedOp:
1235
+ case messageTypes_1.ContainerMessageType.Rejoin:
1236
+ break;
1237
+ default: {
1238
+ // If we didn't necessarily expect a runtime message type, then no worries - just return
1239
+ // e.g. this case applies to system ops, or legacy ops that would have fallen into the above cases anyway.
1240
+ if (!messageWithContext.modernRuntimeMessage) {
1241
+ return;
1242
+ }
1243
+ const compatBehavior = messageWithContext.message.compatDetails?.behavior;
1244
+ if (!compatBehaviorAllowsMessageType(messageWithContext.message.type, compatBehavior)) {
1245
+ const { message } = messageWithContext;
1246
+ const error = telemetry_utils_1.DataProcessingError.create(
1247
+ // Former assert 0x3ce
1248
+ "Runtime message of unknown type", "OpProcessing", message, {
1249
+ local,
1250
+ messageDetails: JSON.stringify({
1251
+ type: message.type,
1252
+ contentType: typeof message.contents,
1253
+ compatBehavior,
1254
+ batch: message.metadata?.batch,
1255
+ compression: message.compression,
1256
+ }),
1257
+ });
1258
+ this.closeFn(error);
1259
+ throw error;
1260
+ }
1261
+ }
1262
+ }
1263
+ }
1264
+ /**
1265
+ * Emits the Signal event and update the perf signal data.
1266
+ * @param clientSignalSequenceNumber - is the client signal sequence number to be uploaded.
1267
+ */
1268
+ sendSignalTelemetryEvent(clientSignalSequenceNumber) {
1269
+ const duration = Date.now() - this._perfSignalData.signalTimestamp;
1270
+ this.mc.logger.sendPerformanceEvent({
1271
+ eventName: "SignalLatency",
1272
+ duration,
1273
+ signalsLost: this._perfSignalData.signalsLost,
1274
+ });
1275
+ this._perfSignalData.signalsLost = 0;
1276
+ this._perfSignalData.signalTimestamp = 0;
1277
+ }
1278
+ processSignal(message, local) {
1279
+ const envelope = message.content;
1280
+ const transformed = {
1281
+ clientId: message.clientId,
1282
+ content: envelope.contents.content,
1283
+ type: envelope.contents.type,
1284
+ };
1285
+ // Only collect signal telemetry for messages sent by the current client.
1286
+ if (message.clientId === this.clientId && this.connected) {
1287
+ // Check to see if the signal was lost.
1288
+ if (this._perfSignalData.trackingSignalSequenceNumber !== undefined &&
1289
+ envelope.clientSignalSequenceNumber >
1290
+ this._perfSignalData.trackingSignalSequenceNumber) {
1291
+ this._perfSignalData.signalsLost++;
1292
+ this._perfSignalData.trackingSignalSequenceNumber = undefined;
1293
+ this.mc.logger.sendErrorEvent({
1294
+ eventName: "SignalLost",
1295
+ type: envelope.contents.type,
1296
+ signalsLost: this._perfSignalData.signalsLost,
1297
+ trackingSequenceNumber: this._perfSignalData.trackingSignalSequenceNumber,
1298
+ clientSignalSequenceNumber: envelope.clientSignalSequenceNumber,
1299
+ });
1300
+ }
1301
+ else if (envelope.clientSignalSequenceNumber ===
1302
+ this._perfSignalData.trackingSignalSequenceNumber) {
1303
+ // only logging for the first connection and the trackingSignalSequenceNUmber.
1304
+ if (this.consecutiveReconnects === 0) {
1305
+ this.sendSignalTelemetryEvent(envelope.clientSignalSequenceNumber);
1306
+ }
1307
+ this._perfSignalData.trackingSignalSequenceNumber = undefined;
1308
+ }
1309
+ }
1310
+ if (envelope.address === undefined) {
1311
+ // No address indicates a container signal message.
1312
+ this.emit("signal", transformed, local);
1313
+ return;
1314
+ }
1315
+ this.dataStores.processSignal(envelope.address, transformed, local);
1316
+ }
1317
+ /**
1318
+ * Flush the pending ops manually.
1319
+ * This method is expected to be called at the end of a batch.
1320
+ */
1321
+ flush() {
1322
+ (0, core_utils_1.assert)(this._orderSequentiallyCalls === 0, 0x24c /* "Cannot call `flush()` from `orderSequentially`'s callback" */);
1323
+ this.outbox.flush();
1324
+ (0, core_utils_1.assert)(this.outbox.isEmpty, 0x3cf /* reentrancy */);
1325
+ }
1326
+ orderSequentially(callback) {
1327
+ let checkpoint;
1328
+ let result;
1329
+ if (this.mc.config.getBoolean("Fluid.ContainerRuntime.EnableRollback")) {
1330
+ // Note: we are not touching this.pendingAttachBatch here, for two reasons:
1331
+ // 1. It would not help, as we flush attach ops as they become available.
1332
+ // 2. There is no way to undo process of data store creation.
1333
+ checkpoint = this.outbox.checkpoint().mainBatch;
1334
+ }
1335
+ try {
1336
+ this._orderSequentiallyCalls++;
1337
+ result = callback();
1338
+ }
1339
+ catch (error) {
1340
+ if (checkpoint) {
1341
+ // This will throw and close the container if rollback fails
1342
+ try {
1343
+ checkpoint.rollback((message) => this.rollback(message.contents, message.localOpMetadata));
1344
+ }
1345
+ catch (err) {
1346
+ const error2 = (0, telemetry_utils_1.wrapError)(err, (message) => {
1347
+ return telemetry_utils_1.DataProcessingError.create(`RollbackError: ${message}`, "checkpointRollback", undefined);
1348
+ });
1349
+ this.closeFn(error2);
1350
+ throw error2;
1351
+ }
1352
+ }
1353
+ else {
1354
+ // pre-0.58 error message: orderSequentiallyCallbackException
1355
+ this.closeFn(new telemetry_utils_1.GenericError("orderSequentially callback exception", error));
1356
+ }
1357
+ throw error; // throw the original error for the consumer of the runtime
1358
+ }
1359
+ finally {
1360
+ this._orderSequentiallyCalls--;
1361
+ }
1362
+ // We don't flush on TurnBased since we expect all messages in the same JS turn to be part of the same batch
1363
+ if (this.flushMode !== runtime_definitions_1.FlushMode.TurnBased && this._orderSequentiallyCalls === 0) {
1364
+ this.flush();
1365
+ }
1366
+ return result;
1367
+ }
1368
+ /**
1369
+ * Returns the aliased data store's entryPoint, given the alias.
1370
+ * @param alias - The alias for the data store.
1371
+ * @returns The data store's entry point ({@link @fluidframework/core-interfaces#IFluidHandle}) if it exists and is aliased.
1372
+ * Returns undefined if no data store has been assigned the given alias.
1373
+ */
1374
+ async getAliasedDataStoreEntryPoint(alias) {
1375
+ await this.dataStores.waitIfPendingAlias(alias);
1376
+ const internalId = this.internalId(alias);
1377
+ const context = await this.dataStores.getDataStoreIfAvailable(internalId, { wait: false });
1378
+ // If the data store is not available or not an alias, return undefined.
1379
+ if (context === undefined || !(await context.isRoot())) {
1380
+ return undefined;
1381
+ }
1382
+ const channel = await context.realize();
1383
+ if (channel.entryPoint === undefined) {
1384
+ throw new telemetry_utils_1.UsageError("entryPoint must be defined on data store runtime for using getAliasedDataStoreEntryPoint");
1385
+ }
1386
+ this.garbageCollector.nodeUpdated(`/${internalId}`, "Loaded", undefined /* timestampMs */, context.packagePath);
1387
+ return channel.entryPoint;
1388
+ }
1389
+ createDetachedRootDataStore(pkg, rootDataStoreId) {
1390
+ if (rootDataStoreId.includes("/")) {
1391
+ throw new telemetry_utils_1.UsageError(`Id cannot contain slashes: '${rootDataStoreId}'`);
1392
+ }
1393
+ return this.dataStores.createDetachedDataStoreCore(pkg, true, rootDataStoreId);
1394
+ }
1395
+ createDetachedDataStore(pkg) {
1396
+ return this.dataStores.createDetachedDataStoreCore(pkg, false);
1397
+ }
1398
+ async createDataStore(pkg) {
1399
+ const id = (0, uuid_1.v4)();
1400
+ return (0, dataStore_1.channelToDataStore)(await this.dataStores
1401
+ ._createFluidDataStoreContext(Array.isArray(pkg) ? pkg : [pkg], id)
1402
+ .realize(), id, this, this.dataStores, this.mc.logger);
1403
+ }
1404
+ /**
1405
+ * @deprecated 0.16 Issue #1537, #3631
1406
+ */
1407
+ async _createDataStoreWithProps(pkg, props, id = (0, uuid_1.v4)()) {
1408
+ return (0, dataStore_1.channelToDataStore)(await this.dataStores
1409
+ ._createFluidDataStoreContext(Array.isArray(pkg) ? pkg : [pkg], id, props)
1410
+ .realize(), id, this, this.dataStores, this.mc.logger);
1411
+ }
1412
+ canSendOps() {
1413
+ // Note that the real (non-proxy) delta manager is needed here to get the readonly info. This is because
1414
+ // container runtime's ability to send ops depend on the actual readonly state of the delta manager.
1415
+ return (this.connected && !this.innerDeltaManager.readOnlyInfo.readonly && !this.imminentClosure);
1416
+ }
1417
+ /**
1418
+ * Are we in the middle of batching ops together?
1419
+ */
1420
+ currentlyBatching() {
1421
+ return this.flushMode !== runtime_definitions_1.FlushMode.Immediate || this._orderSequentiallyCalls !== 0;
1422
+ }
1423
+ getQuorum() {
1424
+ return this._quorum;
1425
+ }
1426
+ getAudience() {
1427
+ return this._audience;
1428
+ }
1429
+ /**
1430
+ * Returns true of container is dirty, i.e. there are some pending local changes that
1431
+ * either were not sent out to delta stream or were not yet acknowledged.
1432
+ */
1433
+ get isDirty() {
1434
+ return this.dirtyContainer;
1435
+ }
1436
+ isContainerMessageDirtyable({ type, contents }) {
1437
+ // Certain container runtime messages should not mark the container dirty such as the old built-in
1438
+ // AgentScheduler and Garbage collector messages.
1439
+ switch (type) {
1440
+ case messageTypes_1.ContainerMessageType.Attach: {
1441
+ const attachMessage = contents;
1442
+ if (attachMessage.id === exports.agentSchedulerId) {
1443
+ return false;
1444
+ }
1445
+ break;
1446
+ }
1447
+ case messageTypes_1.ContainerMessageType.FluidDataStoreOp: {
1448
+ const envelope = contents;
1449
+ if (envelope.address === exports.agentSchedulerId) {
1450
+ return false;
1451
+ }
1452
+ break;
1453
+ }
1454
+ case messageTypes_1.ContainerMessageType.GC: {
1455
+ return false;
1456
+ }
1457
+ default:
1458
+ break;
1459
+ }
1460
+ return true;
1461
+ }
1462
+ createNewSignalEnvelope(address, type, content) {
1463
+ const newSequenceNumber = ++this._perfSignalData.signalSequenceNumber;
1464
+ const newEnvelope = {
1465
+ address,
1466
+ clientSignalSequenceNumber: newSequenceNumber,
1467
+ contents: { type, content },
1468
+ };
1469
+ // We should not track any signals in case we already have a tracking number.
1470
+ if (newSequenceNumber % this.defaultTelemetrySignalSampleCount === 1 &&
1471
+ this._perfSignalData.trackingSignalSequenceNumber === undefined) {
1472
+ this._perfSignalData.signalTimestamp = Date.now();
1473
+ this._perfSignalData.trackingSignalSequenceNumber = newSequenceNumber;
1474
+ }
1475
+ return newEnvelope;
1476
+ }
1477
+ /**
1478
+ * Submits the signal to be sent to other clients.
1479
+ * @param type - Type of the signal.
1480
+ * @param content - Content of the signal.
1481
+ * @param targetClientId - When specified, the signal is only sent to the provided client id.
1482
+ */
1483
+ submitSignal(type, content, targetClientId) {
1484
+ this.verifyNotClosed();
1485
+ const envelope = this.createNewSignalEnvelope(undefined /* address */, type, content);
1486
+ return this.submitSignalFn(envelope, targetClientId);
1487
+ }
1488
+ /**
1489
+ * Submits the signal to be sent to other clients.
1490
+ * @param type - Type of the signal.
1491
+ * @param content - Content of the signal.
1492
+ * @param targetClientId - When specified, the signal is only sent to the provided client id.
1493
+ */
1494
+ submitDataStoreSignal(address, type, content, targetClientId) {
1495
+ const envelope = this.createNewSignalEnvelope(address, type, content);
1496
+ return this.submitSignalFn(envelope, targetClientId);
1497
+ }
1498
+ setAttachState(attachState) {
1499
+ if (attachState === container_definitions_1.AttachState.Attaching) {
1500
+ (0, core_utils_1.assert)(this.attachState === container_definitions_1.AttachState.Attaching, 0x12d /* "Container Context should already be in attaching state" */);
1501
+ }
1502
+ else {
1503
+ (0, core_utils_1.assert)(this.attachState === container_definitions_1.AttachState.Attached, 0x12e /* "Container Context should already be in attached state" */);
1504
+ this.emit("attached");
1505
+ }
1506
+ if (attachState === container_definitions_1.AttachState.Attached && !this.hasPendingMessages()) {
1507
+ this.updateDocumentDirtyState(false);
1508
+ }
1509
+ this.dataStores.setAttachState(attachState);
1510
+ }
1511
+ /**
1512
+ * Create a summary. Used when attaching or serializing a detached container.
1513
+ *
1514
+ * @param blobRedirectTable - A table passed during the attach process. While detached, blob upload is supported
1515
+ * using IDs generated locally. After attach, these IDs cannot be used, so this table maps the old local IDs to the
1516
+ * new storage IDs so requests can be redirected.
1517
+ * @param telemetryContext - summary data passed through the layers for telemetry purposes
1518
+ */
1519
+ createSummary(blobRedirectTable, telemetryContext) {
1520
+ if (blobRedirectTable) {
1521
+ this.blobManager.setRedirectTable(blobRedirectTable);
1522
+ }
1523
+ const summarizeResult = this.dataStores.createSummary(telemetryContext);
1524
+ // Wrap data store summaries in .channels subtree.
1525
+ (0, summary_1.wrapSummaryInChannelsTree)(summarizeResult);
1526
+ this.addContainerStateToSummary(summarizeResult, true /* fullTree */, false /* trackState */, telemetryContext);
1527
+ return summarizeResult.summary;
1528
+ }
1529
+ async summarizeInternal(fullTree, trackState, telemetryContext) {
1530
+ const summarizeResult = await this.dataStores.summarize(fullTree, trackState, telemetryContext);
1531
+ // Wrap data store summaries in .channels subtree.
1532
+ (0, summary_1.wrapSummaryInChannelsTree)(summarizeResult);
1533
+ const pathPartsForChildren = [runtime_definitions_1.channelsTreeName];
1534
+ this.addContainerStateToSummary(summarizeResult, fullTree, trackState, telemetryContext);
1535
+ return {
1536
+ ...summarizeResult,
1537
+ id: "",
1538
+ pathPartsForChildren,
1539
+ };
1540
+ }
1541
+ /**
1542
+ * Returns a summary of the runtime at the current sequence number.
1543
+ */
1544
+ async summarize(options) {
1545
+ this.verifyNotClosed();
1546
+ const { fullTree = false, trackState = true, summaryLogger = this.mc.logger, runGC = this.garbageCollector.shouldRunGC, runSweep, fullGC, } = options;
1547
+ const telemetryContext = new runtime_utils_1.TelemetryContext();
1548
+ // Add the options that are used to generate this summary to the telemetry context.
1549
+ telemetryContext.setMultiple("fluid_Summarize", "Options", {
1550
+ fullTree,
1551
+ trackState,
1552
+ runGC,
1553
+ fullGC,
1554
+ runSweep,
1555
+ });
1556
+ try {
1557
+ if (runGC) {
1558
+ await this.collectGarbage({ logger: summaryLogger, runSweep, fullGC }, telemetryContext);
1559
+ }
1560
+ const { stats, summary } = await this.summarizerNode.summarize(fullTree, trackState, telemetryContext);
1561
+ (0, core_utils_1.assert)(summary.type === protocol_definitions_1.SummaryType.Tree, 0x12f /* "Container Runtime's summarize should always return a tree" */);
1562
+ return { stats, summary };
1563
+ }
1564
+ finally {
1565
+ this.mc.logger.sendTelemetryEvent({
1566
+ eventName: "SummarizeTelemetry",
1567
+ details: telemetryContext.serialize(),
1568
+ });
1569
+ }
1570
+ }
1571
+ /**
1572
+ * Before GC runs, called by the garbage collector to update any pending GC state. This is mainly used to notify
1573
+ * the garbage collector of references detected since the last GC run. Most references are notified immediately
1574
+ * but there can be some for which async operation is required (such as detecting new root data stores).
1575
+ * @see IGarbageCollectionRuntime.updateStateBeforeGC
1576
+ */
1577
+ async updateStateBeforeGC() {
1578
+ return this.dataStores.updateStateBeforeGC();
1579
+ }
1580
+ async getGCDataInternal(fullGC) {
1581
+ return this.dataStores.getGCData(fullGC);
1582
+ }
1583
+ /**
1584
+ * Generates and returns the GC data for this container.
1585
+ * @param fullGC - true to bypass optimizations and force full generation of GC data.
1586
+ * @see IGarbageCollectionRuntime.getGCData
1587
+ */
1588
+ async getGCData(fullGC) {
1589
+ const builder = new runtime_utils_1.GCDataBuilder();
1590
+ const dsGCData = await this.summarizerNode.getGCData(fullGC);
1591
+ builder.addNodes(dsGCData.gcNodes);
1592
+ const blobsGCData = this.blobManager.getGCData(fullGC);
1593
+ builder.addNodes(blobsGCData.gcNodes);
1594
+ return builder.getGCData();
1595
+ }
1596
+ /**
1597
+ * After GC has run, called to notify this container's nodes of routes that are used in it.
1598
+ * @param usedRoutes - The routes that are used in all nodes in this Container.
1599
+ * @see IGarbageCollectionRuntime.updateUsedRoutes
1600
+ */
1601
+ updateUsedRoutes(usedRoutes) {
1602
+ // Update our summarizer node's used routes. Updating used routes in summarizer node before
1603
+ // summarizing is required and asserted by the the summarizer node. We are the root and are
1604
+ // always referenced, so the used routes is only self-route (empty string).
1605
+ this.summarizerNode.updateUsedRoutes([""]);
1606
+ const { dataStoreRoutes } = this.getDataStoreAndBlobManagerRoutes(usedRoutes);
1607
+ this.dataStores.updateUsedRoutes(dataStoreRoutes);
1608
+ }
1609
+ /**
1610
+ * This is called to update objects whose routes are unused.
1611
+ * @param unusedRoutes - Data store and attachment blob routes that are unused in this Container.
1612
+ */
1613
+ updateUnusedRoutes(unusedRoutes) {
1614
+ const { blobManagerRoutes, dataStoreRoutes } = this.getDataStoreAndBlobManagerRoutes(unusedRoutes);
1615
+ this.blobManager.updateUnusedRoutes(blobManagerRoutes);
1616
+ this.dataStores.updateUnusedRoutes(dataStoreRoutes);
1617
+ }
1618
+ /**
1619
+ * @deprecated Replaced by deleteSweepReadyNodes.
1620
+ */
1621
+ deleteUnusedNodes(unusedRoutes) {
1622
+ throw new Error("deleteUnusedRoutes should not be called");
1623
+ }
1624
+ /**
1625
+ * After GC has run and identified nodes that are sweep ready, this is called to delete the sweep ready nodes.
1626
+ * @param sweepReadyRoutes - The routes of nodes that are sweep ready and should be deleted.
1627
+ * @returns The routes of nodes that were deleted.
1628
+ */
1629
+ deleteSweepReadyNodes(sweepReadyRoutes) {
1630
+ const { dataStoreRoutes, blobManagerRoutes } = this.getDataStoreAndBlobManagerRoutes(sweepReadyRoutes);
1631
+ const deletedRoutes = this.dataStores.deleteSweepReadyNodes(dataStoreRoutes);
1632
+ return deletedRoutes.concat(this.blobManager.deleteSweepReadyNodes(blobManagerRoutes));
1633
+ }
1634
+ /**
1635
+ * This is called to update objects that are tombstones.
1636
+ * @param tombstonedRoutes - Data store and attachment blob routes that are tombstones in this Container.
1637
+ */
1638
+ updateTombstonedRoutes(tombstonedRoutes) {
1639
+ const { blobManagerRoutes, dataStoreRoutes } = this.getDataStoreAndBlobManagerRoutes(tombstonedRoutes);
1640
+ this.blobManager.updateTombstonedRoutes(blobManagerRoutes);
1641
+ this.dataStores.updateTombstonedRoutes(dataStoreRoutes);
1642
+ }
1643
+ /**
1644
+ * Returns a server generated referenced timestamp to be used to track unreferenced nodes by GC.
1645
+ */
1646
+ getCurrentReferenceTimestampMs() {
1647
+ // Use the timestamp of the last message seen by this client as that is server generated. If no messages have
1648
+ // been processed, use the timestamp of the message from the last summary.
1649
+ return this.deltaManager.lastMessage?.timestamp ?? this.messageAtLastSummary?.timestamp;
1650
+ }
1651
+ /**
1652
+ * Returns the type of the GC node. Currently, there are nodes that belong to the root ("/"), data stores or
1653
+ * blob manager.
1654
+ */
1655
+ getNodeType(nodePath) {
1656
+ if (this.isBlobPath(nodePath)) {
1657
+ return gc_1.GCNodeType.Blob;
1658
+ }
1659
+ return this.dataStores.getGCNodeType(nodePath) ?? gc_1.GCNodeType.Other;
1660
+ }
1661
+ /**
1662
+ * Called by GC to retrieve the package path of the node with the given path. The node should belong to a
1663
+ * data store or an attachment blob.
1664
+ */
1665
+ async getGCNodePackagePath(nodePath) {
1666
+ switch (this.getNodeType(nodePath)) {
1667
+ case gc_1.GCNodeType.Blob:
1668
+ return [blobManager_1.BlobManager.basePath];
1669
+ case gc_1.GCNodeType.DataStore:
1670
+ case gc_1.GCNodeType.SubDataStore:
1671
+ return this.dataStores.getDataStorePackagePath(nodePath);
1672
+ default:
1673
+ (0, core_utils_1.assert)(false, 0x2de /* "Package path requested for unsupported node type." */);
1674
+ }
1675
+ }
1676
+ /**
1677
+ * Returns whether a given path is for attachment blobs that are in the format - "/BlobManager.basePath/...".
1678
+ */
1679
+ isBlobPath(path) {
1680
+ const pathParts = path.split("/");
1681
+ if (pathParts.length < 2 || pathParts[1] !== blobManager_1.BlobManager.basePath) {
1682
+ return false;
1683
+ }
1684
+ return true;
1685
+ }
1686
+ /**
1687
+ * From a given list of routes, separate and return routes that belong to blob manager and data stores.
1688
+ * @param routes - A list of routes that can belong to data stores or blob manager.
1689
+ * @returns Two route lists - One that contains routes for blob manager and another one that contains routes
1690
+ * for data stores.
1691
+ */
1692
+ getDataStoreAndBlobManagerRoutes(routes) {
1693
+ const blobManagerRoutes = [];
1694
+ const dataStoreRoutes = [];
1695
+ for (const route of routes) {
1696
+ if (this.isBlobPath(route)) {
1697
+ blobManagerRoutes.push(route);
1698
+ }
1699
+ else {
1700
+ dataStoreRoutes.push(route);
1701
+ }
1702
+ }
1703
+ return { blobManagerRoutes, dataStoreRoutes };
1704
+ }
1705
+ /**
1706
+ * Runs garbage collection and updates the reference / used state of the nodes in the container.
1707
+ * @returns the statistics of the garbage collection run; undefined if GC did not run.
1708
+ */
1709
+ async collectGarbage(options, telemetryContext) {
1710
+ return this.garbageCollector.collectGarbage(options, telemetryContext);
1711
+ }
1712
+ /**
1713
+ * Called when a new outbound reference is added to another node. This is used by garbage collection to identify
1714
+ * all references added in the system.
1715
+ * @param srcHandle - The handle of the node that added the reference.
1716
+ * @param outboundHandle - The handle of the outbound node that is referenced.
1717
+ */
1718
+ addedGCOutboundReference(srcHandle, outboundHandle) {
1719
+ this.garbageCollector.addedOutboundReference(srcHandle.absolutePath, outboundHandle.absolutePath);
1720
+ }
1721
+ /**
1722
+ * Generates the summary tree, uploads it to storage, and then submits the summarize op.
1723
+ * This is intended to be called by the summarizer, since it is the implementation of
1724
+ * ISummarizerInternalsProvider.submitSummary.
1725
+ * It takes care of state management at the container level, including pausing inbound
1726
+ * op processing, updating SummarizerNode state tracking, and garbage collection.
1727
+ * @param options - options controlling how the summary is generated or submitted
1728
+ */
1729
+ async submitSummary(options) {
1730
+ const { fullTree = false, finalAttempt = false, refreshLatestAck, summaryLogger } = options;
1731
+ // The summary number for this summary. This will be updated during the summary process, so get it now and
1732
+ // use it for all events logged during this summary.
1733
+ const summaryNumber = this.nextSummaryNumber;
1734
+ const summaryNumberLogger = (0, telemetry_utils_1.createChildLogger)({
1735
+ logger: summaryLogger,
1736
+ properties: {
1737
+ all: { summaryNumber },
1738
+ },
1739
+ });
1740
+ (0, core_utils_1.assert)(this.outbox.isEmpty, 0x3d1 /* Can't trigger summary in the middle of a batch */);
1741
+ // We close the summarizer and download a new snapshot and reload the container
1742
+ let latestSnapshotVersionId;
1743
+ if (refreshLatestAck === true) {
1744
+ return this.prefetchLatestSummaryThenClose((0, telemetry_utils_1.createChildLogger)({
1745
+ logger: summaryNumberLogger,
1746
+ properties: { all: { safeSummary: true } },
1747
+ }));
1748
+ }
1749
+ // If the container is dirty, i.e., there are pending unacked ops, the summary will not be eventual consistent
1750
+ // and it may even be incorrect. So, wait for the container to be saved with a timeout. If the container is not
1751
+ // saved within the timeout, check if it should be failed or can continue.
1752
+ if (this.validateSummaryBeforeUpload && this.isDirty) {
1753
+ const countBefore = this.pendingMessagesCount;
1754
+ // The timeout for waiting for pending ops can be overridden via configurations.
1755
+ const pendingOpsTimeout = this.mc.config.getNumber("Fluid.Summarizer.waitForPendingOpsTimeoutMs") ??
1756
+ exports.defaultPendingOpsWaitTimeoutMs;
1757
+ await new Promise((resolve, reject) => {
1758
+ const timeoutId = setTimeout(() => resolve(), pendingOpsTimeout);
1759
+ this.once("saved", () => {
1760
+ clearTimeout(timeoutId);
1761
+ resolve();
1762
+ });
1763
+ this.once("dispose", () => {
1764
+ clearTimeout(timeoutId);
1765
+ reject(new Error("Runtime is disposed while summarizing"));
1766
+ });
1767
+ });
1768
+ // Log that there are pending ops while summarizing. This will help us gather data on how often this
1769
+ // happens, whether we attempted to wait for these ops to be acked and what was the result.
1770
+ summaryNumberLogger.sendTelemetryEvent({
1771
+ eventName: "PendingOpsWhileSummarizing",
1772
+ saved: !this.isDirty,
1773
+ timeout: pendingOpsTimeout,
1774
+ countBefore,
1775
+ countAfter: this.pendingMessagesCount,
1776
+ });
1777
+ // There could still be pending ops. Check if summary should fail or continue.
1778
+ const pendingMessagesFailResult = await this.shouldFailSummaryOnPendingOps(summaryNumberLogger, this.deltaManager.lastSequenceNumber, this.deltaManager.minimumSequenceNumber, finalAttempt, true /* beforeSummaryGeneration */);
1779
+ if (pendingMessagesFailResult !== undefined) {
1780
+ return pendingMessagesFailResult;
1781
+ }
1782
+ }
1783
+ const shouldPauseInboundSignal = this.mc.config.getBoolean("Fluid.ContainerRuntime.SubmitSummary.disableInboundSignalPause") !== true;
1784
+ let summaryRefSeqNum;
1785
+ try {
1786
+ await this.deltaManager.inbound.pause();
1787
+ if (shouldPauseInboundSignal) {
1788
+ await this.deltaManager.inboundSignal.pause();
1789
+ }
1790
+ summaryRefSeqNum = this.deltaManager.lastSequenceNumber;
1791
+ const minimumSequenceNumber = this.deltaManager.minimumSequenceNumber;
1792
+ const message = `Summary @${summaryRefSeqNum}:${this.deltaManager.minimumSequenceNumber}`;
1793
+ const lastAck = this.summaryCollection.latestAck;
1794
+ this.summarizerNode.startSummary(summaryRefSeqNum, summaryNumberLogger);
1795
+ // Helper function to check whether we should still continue between each async step.
1796
+ const checkContinue = () => {
1797
+ // Do not check for loss of connectivity directly! Instead leave it up to
1798
+ // RunWhileConnectedCoordinator to control policy in a single place.
1799
+ // This will allow easier change of design if we chose to. For example, we may chose to allow
1800
+ // summarizer to reconnect in the future.
1801
+ // Also checking for cancellation is a must as summary process may be abandoned for other reasons,
1802
+ // like loss of connectivity for main (interactive) client.
1803
+ if (options.cancellationToken.cancelled) {
1804
+ return { continue: false, error: "disconnected" };
1805
+ }
1806
+ // That said, we rely on submitSystemMessage() that today only works in connected state.
1807
+ // So if we fail here, it either means that RunWhileConnectedCoordinator does not work correctly,
1808
+ // OR that design changed and we need to remove this check and fix submitSystemMessage.
1809
+ (0, core_utils_1.assert)(this.connected, 0x258 /* "connected" */);
1810
+ // Ensure that lastSequenceNumber has not changed after pausing.
1811
+ // We need the summary op's reference sequence number to match our summary sequence number,
1812
+ // otherwise we'll get the wrong sequence number stamped on the summary's .protocol attributes.
1813
+ if (this.deltaManager.lastSequenceNumber !== summaryRefSeqNum) {
1814
+ return {
1815
+ continue: false,
1816
+ error: `lastSequenceNumber changed before uploading to storage. ${this.deltaManager.lastSequenceNumber} !== ${summaryRefSeqNum}`,
1817
+ };
1818
+ }
1819
+ (0, core_utils_1.assert)(summaryRefSeqNum === this.deltaManager.lastMessage?.sequenceNumber, 0x395 /* it's one and the same thing */);
1820
+ if (lastAck !== this.summaryCollection.latestAck) {
1821
+ return {
1822
+ continue: false,
1823
+ error: `Last summary changed while summarizing. ${this.summaryCollection.latestAck} !== ${lastAck}`,
1824
+ };
1825
+ }
1826
+ return { continue: true };
1827
+ };
1828
+ let continueResult = checkContinue();
1829
+ if (!continueResult.continue) {
1830
+ return {
1831
+ stage: "base",
1832
+ referenceSequenceNumber: summaryRefSeqNum,
1833
+ minimumSequenceNumber,
1834
+ error: continueResult.error,
1835
+ };
1836
+ }
1837
+ const trace = client_utils_1.Trace.start();
1838
+ let summarizeResult;
1839
+ // If the GC state needs to be reset, we need to force a full tree summary and update the unreferenced
1840
+ // state of all the nodes.
1841
+ const forcedFullTree = this.garbageCollector.summaryStateNeedsReset;
1842
+ try {
1843
+ summarizeResult = await this.summarize({
1844
+ fullTree: fullTree || forcedFullTree,
1845
+ trackState: true,
1846
+ summaryLogger: summaryNumberLogger,
1847
+ runGC: this.garbageCollector.shouldRunGC,
1848
+ });
1849
+ }
1850
+ catch (error) {
1851
+ return {
1852
+ stage: "base",
1853
+ referenceSequenceNumber: summaryRefSeqNum,
1854
+ minimumSequenceNumber,
1855
+ error,
1856
+ };
1857
+ }
1858
+ // If validateSummaryBeforeUpload is true, validate that the summary generated is correct before uploading.
1859
+ if (this.validateSummaryBeforeUpload) {
1860
+ // Validate that the summaries generated by summarize nodes is correct.
1861
+ const validateResult = this.summarizerNode.validateSummary();
1862
+ if (!validateResult.success) {
1863
+ const { success, ...loggingProps } = validateResult;
1864
+ const error = new summary_1.RetriableSummaryError(validateResult.reason, validateResult.retryAfterSeconds, { ...loggingProps });
1865
+ return {
1866
+ stage: "base",
1867
+ referenceSequenceNumber: summaryRefSeqNum,
1868
+ minimumSequenceNumber,
1869
+ error,
1870
+ };
1871
+ }
1872
+ const pendingMessagesFailResult = await this.shouldFailSummaryOnPendingOps(summaryNumberLogger, summaryRefSeqNum, minimumSequenceNumber, finalAttempt, false /* beforeSummaryGeneration */);
1873
+ if (pendingMessagesFailResult !== undefined) {
1874
+ return pendingMessagesFailResult;
1875
+ }
1876
+ }
1877
+ const { summary: summaryTree, stats: partialStats } = summarizeResult;
1878
+ // Now that we have generated the summary, update the message at last summary to the last message processed.
1879
+ this.messageAtLastSummary = this.deltaManager.lastMessage;
1880
+ // Counting dataStores and handles
1881
+ // Because handles are unchanged dataStores in the current logic,
1882
+ // summarized dataStore count is total dataStore count minus handle count
1883
+ const dataStoreTree = summaryTree.tree[runtime_definitions_1.channelsTreeName];
1884
+ (0, core_utils_1.assert)(dataStoreTree.type === protocol_definitions_1.SummaryType.Tree, 0x1fc /* "summary is not a tree" */);
1885
+ const handleCount = Object.values(dataStoreTree.tree).filter((value) => value.type === protocol_definitions_1.SummaryType.Handle).length;
1886
+ const gcSummaryTreeStats = summaryTree.tree[runtime_definitions_1.gcTreeKey]
1887
+ ? (0, runtime_utils_1.calculateStats)(summaryTree.tree[runtime_definitions_1.gcTreeKey])
1888
+ : undefined;
1889
+ const summaryStats = {
1890
+ dataStoreCount: this.dataStores.size,
1891
+ summarizedDataStoreCount: this.dataStores.size - handleCount,
1892
+ gcStateUpdatedDataStoreCount: this.garbageCollector.updatedDSCountSinceLastSummary,
1893
+ gcBlobNodeCount: gcSummaryTreeStats?.blobNodeCount,
1894
+ gcTotalBlobsSize: gcSummaryTreeStats?.totalBlobSize,
1895
+ summaryNumber,
1896
+ ...partialStats,
1897
+ };
1898
+ const generateSummaryData = {
1899
+ referenceSequenceNumber: summaryRefSeqNum,
1900
+ minimumSequenceNumber,
1901
+ summaryTree,
1902
+ summaryStats,
1903
+ generateDuration: trace.trace().duration,
1904
+ forcedFullTree,
1905
+ };
1906
+ continueResult = checkContinue();
1907
+ if (!continueResult.continue) {
1908
+ return { stage: "generate", ...generateSummaryData, error: continueResult.error };
1909
+ }
1910
+ // It may happen that the lastAck it not correct due to missing summaryAck in case of single commit
1911
+ // summary. So if the previous summarizer closes just after submitting the summary and before
1912
+ // submitting the summaryOp then we can't rely on summaryAck. So in case we have
1913
+ // latestSnapshotVersionId from storage and it does not match with the lastAck ackHandle, then use
1914
+ // the one fetched from storage as parent as that is the latest.
1915
+ let summaryContext;
1916
+ if (lastAck?.summaryAck.contents.handle !== latestSnapshotVersionId &&
1917
+ latestSnapshotVersionId !== undefined) {
1918
+ summaryContext = {
1919
+ proposalHandle: undefined,
1920
+ ackHandle: latestSnapshotVersionId,
1921
+ referenceSequenceNumber: summaryRefSeqNum,
1922
+ };
1923
+ }
1924
+ else if (lastAck === undefined) {
1925
+ summaryContext = {
1926
+ proposalHandle: undefined,
1927
+ ackHandle: this.loadedFromVersionId,
1928
+ referenceSequenceNumber: summaryRefSeqNum,
1929
+ };
1930
+ }
1931
+ else {
1932
+ summaryContext = {
1933
+ proposalHandle: lastAck.summaryOp.contents.handle,
1934
+ ackHandle: lastAck.summaryAck.contents.handle,
1935
+ referenceSequenceNumber: summaryRefSeqNum,
1936
+ };
1937
+ }
1938
+ let handle;
1939
+ try {
1940
+ handle = await this.storage.uploadSummaryWithContext(summarizeResult.summary, summaryContext);
1941
+ }
1942
+ catch (error) {
1943
+ return { stage: "generate", ...generateSummaryData, error };
1944
+ }
1945
+ const parent = summaryContext.ackHandle;
1946
+ const summaryMessage = {
1947
+ handle,
1948
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
1949
+ head: parent,
1950
+ message,
1951
+ parents: parent ? [parent] : [],
1952
+ };
1953
+ const uploadData = {
1954
+ ...generateSummaryData,
1955
+ handle,
1956
+ uploadDuration: trace.trace().duration,
1957
+ };
1958
+ continueResult = checkContinue();
1959
+ if (!continueResult.continue) {
1960
+ return { stage: "upload", ...uploadData, error: continueResult.error };
1961
+ }
1962
+ let clientSequenceNumber;
1963
+ try {
1964
+ clientSequenceNumber = this.submitSummaryMessage(summaryMessage, summaryRefSeqNum);
1965
+ }
1966
+ catch (error) {
1967
+ return { stage: "upload", ...uploadData, error };
1968
+ }
1969
+ const submitData = {
1970
+ stage: "submit",
1971
+ ...uploadData,
1972
+ clientSequenceNumber,
1973
+ submitOpDuration: trace.trace().duration,
1974
+ };
1975
+ try {
1976
+ // If validateSummaryBeforeUpload is false, the summary should be validated in this step.
1977
+ this.summarizerNode.completeSummary(handle, !this.validateSummaryBeforeUpload /* validate */);
1978
+ }
1979
+ catch (error) {
1980
+ return { stage: "upload", ...uploadData, error };
1981
+ }
1982
+ return submitData;
1983
+ }
1984
+ finally {
1985
+ // Cleanup wip summary in case of failure
1986
+ this.summarizerNode.clearSummary();
1987
+ // ! This needs to happen before we resume inbound queues to ensure heuristics are tracked correctly
1988
+ this._summarizer?.recordSummaryAttempt?.(summaryRefSeqNum);
1989
+ // Restart the delta manager
1990
+ this.deltaManager.inbound.resume();
1991
+ if (shouldPauseInboundSignal) {
1992
+ this.deltaManager.inboundSignal.resume();
1993
+ }
1994
+ }
1995
+ }
1996
+ /**
1997
+ * This helper is called during summarization. If the container is dirty, it will return a failed summarize result
1998
+ * (IBaseSummarizeResult) unless this is the final summarize attempt and SkipFailingIncorrectSummary option is set.
1999
+ * @param logger - The logger to be used for sending telemetry.
2000
+ * @param referenceSequenceNumber - The reference sequence number of the summary attempt.
2001
+ * @param minimumSequenceNumber - The minimum sequence number of the summary attempt.
2002
+ * @param finalAttempt - Whether this is the final summary attempt.
2003
+ * @param beforeSummaryGeneration - Whether this is called before summary generation or after.
2004
+ * @returns failed summarize result (IBaseSummarizeResult) if summary should be failed, undefined otherwise.
2005
+ */
2006
+ async shouldFailSummaryOnPendingOps(logger, referenceSequenceNumber, minimumSequenceNumber, finalAttempt, beforeSummaryGeneration) {
2007
+ if (!this.isDirty) {
2008
+ return;
2009
+ }
2010
+ // If "SkipFailingIncorrectSummary" option is true, don't fail the summary in the last attempt.
2011
+ // This is a fallback to make progress in documents where there are consistently pending ops in
2012
+ // the summarizer.
2013
+ if (finalAttempt &&
2014
+ this.mc.config.getBoolean("Fluid.Summarizer.SkipFailingIncorrectSummary")) {
2015
+ const error = telemetry_utils_1.DataProcessingError.create("Pending ops during summarization", "submitSummary", undefined, { pendingMessages: this.pendingMessagesCount });
2016
+ logger.sendErrorEvent({
2017
+ eventName: "SkipFailingIncorrectSummary",
2018
+ referenceSequenceNumber,
2019
+ minimumSequenceNumber,
2020
+ beforeGenerate: beforeSummaryGeneration,
2021
+ }, error);
2022
+ }
2023
+ else {
2024
+ // The retry delay when there are pending ops can be overridden via config so that we can adjust it
2025
+ // based on telemetry while we decide on a stable number.
2026
+ const retryDelayMs = this.mc.config.getNumber("Fluid.Summarizer.PendingOpsRetryDelayMs") ??
2027
+ exports.defaultPendingOpsRetryDelayMs;
2028
+ const error = new summary_1.RetriableSummaryError("PendingOpsWhileSummarizing", retryDelayMs / 1000, {
2029
+ count: this.pendingMessagesCount,
2030
+ beforeGenerate: beforeSummaryGeneration,
2031
+ });
2032
+ return {
2033
+ stage: "base",
2034
+ referenceSequenceNumber,
2035
+ minimumSequenceNumber,
2036
+ error,
2037
+ };
2038
+ }
2039
+ }
2040
+ get pendingMessagesCount() {
2041
+ return this.pendingStateManager.pendingMessagesCount + this.outbox.messageCount;
2042
+ }
2043
+ hasPendingMessages() {
2044
+ return this.pendingMessagesCount !== 0;
2045
+ }
2046
+ updateDocumentDirtyState(dirty) {
2047
+ if (this.attachState !== container_definitions_1.AttachState.Attached) {
2048
+ (0, core_utils_1.assert)(dirty, 0x3d2 /* Non-attached container is dirty */);
2049
+ }
2050
+ else {
2051
+ // Other way is not true = see this.isContainerMessageDirtyable()
2052
+ (0, core_utils_1.assert)(!dirty || this.hasPendingMessages(), 0x3d3 /* if doc is dirty, there has to be pending ops */);
2053
+ }
2054
+ if (this.dirtyContainer === dirty) {
2055
+ return;
2056
+ }
2057
+ this.dirtyContainer = dirty;
2058
+ if (this.emitDirtyDocumentEvent) {
2059
+ this.emit(dirty ? "dirty" : "saved");
2060
+ }
2061
+ }
2062
+ submitDataStoreOp(id, contents, localOpMetadata = undefined) {
2063
+ const envelope = {
2064
+ address: id,
2065
+ contents,
2066
+ };
2067
+ this.submit({ type: messageTypes_1.ContainerMessageType.FluidDataStoreOp, contents: envelope }, localOpMetadata);
2068
+ }
2069
+ submitDataStoreAliasOp(contents, localOpMetadata) {
2070
+ const aliasMessage = contents;
2071
+ if (!(0, dataStore_1.isDataStoreAliasMessage)(aliasMessage)) {
2072
+ throw new telemetry_utils_1.UsageError("malformedDataStoreAliasMessage");
2073
+ }
2074
+ this.submit({ type: messageTypes_1.ContainerMessageType.Alias, contents }, localOpMetadata);
2075
+ }
2076
+ async uploadBlob(blob, signal) {
2077
+ this.verifyNotClosed();
2078
+ return this.blobManager.createBlob(blob, signal);
2079
+ }
2080
+ maybeSubmitIdAllocationOp(type) {
2081
+ if (type !== messageTypes_1.ContainerMessageType.IdAllocation) {
2082
+ let idAllocationBatchMessage;
2083
+ let idRange;
2084
+ if (this.idCompressorEnabled) {
2085
+ (0, core_utils_1.assert)(this.idCompressor !== undefined, 0x67d /* IdCompressor should be defined if enabled */);
2086
+ idRange = this.idCompressor.takeNextCreationRange();
2087
+ // Don't include the idRange if there weren't any Ids allocated
2088
+ idRange = idRange?.ids !== undefined ? idRange : undefined;
2089
+ }
2090
+ if (idRange !== undefined) {
2091
+ const idAllocationMessage = {
2092
+ type: messageTypes_1.ContainerMessageType.IdAllocation,
2093
+ contents: idRange,
2094
+ };
2095
+ idAllocationBatchMessage = {
2096
+ contents: JSON.stringify(idAllocationMessage),
2097
+ referenceSequenceNumber: this.deltaManager.lastSequenceNumber,
2098
+ metadata: undefined,
2099
+ localOpMetadata: undefined,
2100
+ type: messageTypes_1.ContainerMessageType.IdAllocation,
2101
+ };
2102
+ }
2103
+ if (idAllocationBatchMessage !== undefined) {
2104
+ this.outbox.submitIdAllocation(idAllocationBatchMessage);
2105
+ }
2106
+ }
2107
+ }
2108
+ submit(containerRuntimeMessage, localOpMetadata = undefined, metadata = undefined) {
2109
+ this.verifyNotClosed();
2110
+ this.verifyCanSubmitOps();
2111
+ // There should be no ops in detached container state!
2112
+ (0, core_utils_1.assert)(this.attachState !== container_definitions_1.AttachState.Detached, 0x132 /* "sending ops in detached container" */);
2113
+ const serializedContent = JSON.stringify(containerRuntimeMessage);
2114
+ // Note that the real (non-proxy) delta manager is used here to get the readonly info. This is because
2115
+ // container runtime's ability to submit ops depend on the actual readonly state of the delta manager.
2116
+ if (this.innerDeltaManager.readOnlyInfo.readonly) {
2117
+ this.mc.logger.sendTelemetryEvent({
2118
+ eventName: "SubmitOpInReadonly",
2119
+ connected: this.connected,
2120
+ });
2121
+ }
2122
+ const type = containerRuntimeMessage.type;
2123
+ const message = {
2124
+ contents: serializedContent,
2125
+ type,
2126
+ metadata,
2127
+ localOpMetadata,
2128
+ referenceSequenceNumber: this.deltaManager.lastSequenceNumber,
2129
+ };
2130
+ try {
2131
+ // Submit an IdAllocation op if any Ids have been generated since
2132
+ // the last op was submitted. Don't submit another if it's an IdAllocation
2133
+ // op as that means we're in resubmission flow and we don't want to send
2134
+ // IdRanges out of order.
2135
+ this.maybeSubmitIdAllocationOp(type);
2136
+ // If this is attach message for new data store, and we are in a batch, send this op out of order
2137
+ // Is it safe:
2138
+ // Yes, this should be safe reordering. Newly created data stores are not visible through API surface.
2139
+ // They become visible only when aliased, or handle to some sub-element of newly created datastore
2140
+ // is stored in some DDS, i.e. only after some other op.
2141
+ // Why:
2142
+ // Attach ops are large, and expensive to process. Plus there are scenarios where a lot of new data
2143
+ // stores are created, causing issues like relay service throttling (too many ops) and catastrophic
2144
+ // failure (batch is too large). Pushing them earlier and outside of main batch should alleviate
2145
+ // these issues.
2146
+ // Cons:
2147
+ // 1. With large batches, relay service may throttle clients. Clients may disconnect while throttled.
2148
+ // This change creates new possibility of a lot of newly created data stores never being referenced
2149
+ // because client died before it had a change to submit the rest of the ops. This will create more
2150
+ // garbage that needs to be collected leveraging GC (Garbage Collection) feature.
2151
+ // 2. Sending ops out of order means they are excluded from rollback functionality. This is not an issue
2152
+ // today as rollback can't undo creation of data store. To some extent not sending them is a bigger
2153
+ // issue than sending.
2154
+ // Please note that this does not change file format, so it can be disabled in the future if this
2155
+ // optimization no longer makes sense (for example, batch compression may make it less appealing).
2156
+ if (this.currentlyBatching() &&
2157
+ type === messageTypes_1.ContainerMessageType.Attach &&
2158
+ this.disableAttachReorder !== true) {
2159
+ this.outbox.submitAttach(message);
2160
+ }
2161
+ else if (type === messageTypes_1.ContainerMessageType.BlobAttach) {
2162
+ // BlobAttach ops must have their metadata visible and cannot be grouped (see opGroupingManager.ts)
2163
+ this.outbox.submitBlobAttach(message);
2164
+ }
2165
+ else {
2166
+ this.outbox.submit(message);
2167
+ }
2168
+ if (!this.currentlyBatching()) {
2169
+ this.flush();
2170
+ }
2171
+ else {
2172
+ this.scheduleFlush();
2173
+ }
2174
+ }
2175
+ catch (error) {
2176
+ this.closeFn(error);
2177
+ throw error;
2178
+ }
2179
+ if (this.isContainerMessageDirtyable(containerRuntimeMessage)) {
2180
+ this.updateDocumentDirtyState(true);
2181
+ }
2182
+ }
2183
+ scheduleFlush() {
2184
+ if (this.flushTaskExists) {
2185
+ return;
2186
+ }
2187
+ this.flushTaskExists = true;
2188
+ const flush = () => {
2189
+ this.flushTaskExists = false;
2190
+ try {
2191
+ this.flush();
2192
+ }
2193
+ catch (error) {
2194
+ this.closeFn(error);
2195
+ }
2196
+ };
2197
+ switch (this.flushMode) {
2198
+ case runtime_definitions_1.FlushMode.TurnBased:
2199
+ // When in TurnBased flush mode the runtime will buffer operations in the current turn and send them as a single
2200
+ // batch at the end of the turn
2201
+ // eslint-disable-next-line @typescript-eslint/no-floating-promises
2202
+ Promise.resolve().then(flush);
2203
+ break;
2204
+ // FlushModeExperimental is experimental and not exposed directly in the runtime APIs
2205
+ case runtime_definitions_1.FlushModeExperimental.Async:
2206
+ // When in Async flush mode, the runtime will accumulate all operations across JS turns and send them as a single
2207
+ // batch when all micro-tasks are complete.
2208
+ // Compared to TurnBased, this flush mode will capture more ops into the same batch.
2209
+ setTimeout(flush, 0);
2210
+ break;
2211
+ default:
2212
+ (0, core_utils_1.assert)(this._orderSequentiallyCalls > 0, 0x587 /* Unreachable unless running under orderSequentially */);
2213
+ break;
2214
+ }
2215
+ }
2216
+ submitSummaryMessage(contents, referenceSequenceNumber) {
2217
+ this.verifyNotClosed();
2218
+ (0, core_utils_1.assert)(this.connected, 0x133 /* "Container disconnected when trying to submit system message" */);
2219
+ // System message should not be sent in the middle of the batch.
2220
+ (0, core_utils_1.assert)(this.outbox.isEmpty, 0x3d4 /* System op in the middle of a batch */);
2221
+ // back-compat: ADO #1385: Make this call unconditional in the future
2222
+ return this.submitSummaryFn !== undefined
2223
+ ? this.submitSummaryFn(contents, referenceSequenceNumber)
2224
+ : this.submitFn(protocol_definitions_1.MessageType.Summarize, contents, false);
2225
+ }
2226
+ /**
2227
+ * Throw an error if the runtime is closed. Methods that are expected to potentially
2228
+ * be called after dispose due to asynchrony should not call this.
2229
+ */
2230
+ verifyNotClosed() {
2231
+ if (this._disposed) {
2232
+ throw new Error("Runtime is closed");
2233
+ }
2234
+ }
2235
+ verifyCanSubmitOps() {
2236
+ if (this.ensureNoDataModelChangesCalls > 0) {
2237
+ const errorMessage = "Op was submitted from within a `ensureNoDataModelChanges` callback";
2238
+ if (this.opReentryCallsToReport > 0) {
2239
+ this.mc.logger.sendTelemetryEvent({ eventName: "OpReentry" },
2240
+ // We need to capture the call stack in order to inspect the source of this usage pattern
2241
+ (0, opLifecycle_1.getLongStack)(() => new telemetry_utils_1.UsageError(errorMessage)));
2242
+ this.opReentryCallsToReport--;
2243
+ }
2244
+ // Creating ops while processing ops can lead
2245
+ // to undefined behavior and events observed in the wrong order.
2246
+ // For example, we have two callbacks registered for a DDS, A and B.
2247
+ // Then if on change #1 callback A creates change #2, the invocation flow will be:
2248
+ //
2249
+ // A because of #1
2250
+ // A because of #2
2251
+ // B because of #2
2252
+ // B because of #1
2253
+ //
2254
+ // The runtime must enforce op coherence by not allowing ops to be submitted
2255
+ // while ops are being processed.
2256
+ if (this.enableOpReentryCheck) {
2257
+ throw new telemetry_utils_1.UsageError(errorMessage);
2258
+ }
2259
+ }
2260
+ }
2261
+ reSubmitBatch(batch) {
2262
+ this.orderSequentially(() => {
2263
+ for (const message of batch) {
2264
+ this.reSubmit(message);
2265
+ }
2266
+ });
2267
+ this.flush();
2268
+ }
2269
+ reSubmit(message) {
2270
+ // Need to parse from string for back-compat
2271
+ const containerRuntimeMessage = this.parseLocalOpContent(message.content);
2272
+ this.reSubmitCore(containerRuntimeMessage, message.localOpMetadata, message.opMetadata);
2273
+ }
2274
+ /**
2275
+ * Finds the right store and asks it to resubmit the message. This typically happens when we
2276
+ * reconnect and there are pending messages.
2277
+ * ! Note: successfully resubmitting an op that has been successfully sequenced is not possible due to checks in the ConnectionStateHandler (Loader layer)
2278
+ * @param message - The original LocalContainerRuntimeMessage.
2279
+ * @param localOpMetadata - The local metadata associated with the original message.
2280
+ */
2281
+ reSubmitCore(message, localOpMetadata, opMetadata) {
2282
+ switch (message.type) {
2283
+ case messageTypes_1.ContainerMessageType.FluidDataStoreOp:
2284
+ // For Operations, call resubmitDataStoreOp which will find the right store
2285
+ // and trigger resubmission on it.
2286
+ this.dataStores.resubmitDataStoreOp(message.contents, localOpMetadata);
2287
+ break;
2288
+ case messageTypes_1.ContainerMessageType.Attach:
2289
+ case messageTypes_1.ContainerMessageType.Alias:
2290
+ case messageTypes_1.ContainerMessageType.IdAllocation: {
2291
+ this.submit(message, localOpMetadata);
2292
+ break;
2293
+ }
2294
+ case messageTypes_1.ContainerMessageType.ChunkedOp:
2295
+ throw new Error(`chunkedOp not expected here`);
2296
+ case messageTypes_1.ContainerMessageType.BlobAttach:
2297
+ this.blobManager.reSubmit(opMetadata);
2298
+ break;
2299
+ case messageTypes_1.ContainerMessageType.Rejoin:
2300
+ this.submit(message);
2301
+ break;
2302
+ case messageTypes_1.ContainerMessageType.GC:
2303
+ // GC op is only sent in summarizer which should never reconnect.
2304
+ throw new telemetry_utils_1.LoggingError("GC op not expected to be resubmitted in summarizer");
2305
+ default: {
2306
+ // This case should be very rare - it would imply an op was stashed from a
2307
+ // future version of runtime code and now is being applied on an older version
2308
+ const compatBehavior = message.compatDetails?.behavior;
2309
+ if (compatBehaviorAllowsMessageType(message.type, compatBehavior)) {
2310
+ this.logger.sendTelemetryEvent({
2311
+ eventName: "resubmitUnrecognizedMessageTypeAllowed",
2312
+ messageDetails: { type: message.type, compatBehavior },
2313
+ });
2314
+ }
2315
+ else {
2316
+ const error = telemetry_utils_1.DataProcessingError.create("Resubmitting runtime message of unknown type", "reSubmitCore", undefined /* sequencedMessage */, {
2317
+ messageDetails: JSON.stringify({
2318
+ type: message.type,
2319
+ compatBehavior,
2320
+ }),
2321
+ });
2322
+ this.closeFn(error);
2323
+ throw error;
2324
+ }
2325
+ }
2326
+ }
2327
+ }
2328
+ rollback(content, localOpMetadata) {
2329
+ // Need to parse from string for back-compat
2330
+ const { type, contents } = this.parseLocalOpContent(content);
2331
+ switch (type) {
2332
+ case messageTypes_1.ContainerMessageType.FluidDataStoreOp:
2333
+ // For operations, call rollbackDataStoreOp which will find the right store
2334
+ // and trigger rollback on it.
2335
+ this.dataStores.rollbackDataStoreOp(contents, localOpMetadata);
2336
+ break;
2337
+ default:
2338
+ // Don't check message.compatDetails because this is for rolling back a local op so the type will be known
2339
+ throw new Error(`Can't rollback ${type}`);
2340
+ }
2341
+ }
2342
+ /** Implementation of ISummarizerInternalsProvider.refreshLatestSummaryAck */
2343
+ async refreshLatestSummaryAck(options) {
2344
+ const { proposalHandle, ackHandle, summaryRefSeq, summaryLogger } = options;
2345
+ // proposalHandle is always passed from RunningSummarizer.
2346
+ (0, core_utils_1.assert)(proposalHandle !== undefined, 0x766 /* proposalHandle should be available */);
2347
+ const readAndParseBlob = async (id) => (0, driver_utils_1.readAndParse)(this.storage, id);
2348
+ const result = await this.summarizerNode.refreshLatestSummary(proposalHandle, summaryRefSeq);
2349
+ /**
2350
+ * When refreshing a summary ack, this check indicates a new ack of a summary that is newer than the
2351
+ * current summary that is tracked, but this summarizer runtime did not produce/track that summary. Thus
2352
+ * it needs to refresh its state. Today refresh is done by fetching the latest snapshot to update the cache
2353
+ * and then close as the current main client is likely to be re-elected as the parent summarizer again.
2354
+ */
2355
+ if (!result.isSummaryTracked && result.isSummaryNewer) {
2356
+ const fetchResult = await this.fetchLatestSnapshotFromStorage(summaryLogger, {
2357
+ eventName: "RefreshLatestSummaryAckFetch",
2358
+ ackHandle,
2359
+ targetSequenceNumber: summaryRefSeq,
2360
+ }, readAndParseBlob);
2361
+ /**
2362
+ * If the fetched snapshot is older than the one for which the ack was received, close the container.
2363
+ * This should never happen because an ack should be sent after the latest summary is updated in the server.
2364
+ * However, there are couple of scenarios where it's possible:
2365
+ * 1. A file was modified externally resulting in modifying the snapshot's sequence number. This can lead to
2366
+ * the document being unusable and we should not proceed.
2367
+ * 2. The server DB failed after the ack was sent which may delete the corresponding snapshot. Ideally, in
2368
+ * such cases, the file will be rolled back along with the ack and we will eventually reach a consistent
2369
+ * state.
2370
+ */
2371
+ if (fetchResult.latestSnapshotRefSeq < summaryRefSeq) {
2372
+ const error = telemetry_utils_1.DataProcessingError.create("Fetched snapshot is older than the received ack", "RefreshLatestSummaryAck", undefined /* sequencedMessage */, {
2373
+ ackHandle,
2374
+ summaryRefSeq,
2375
+ fetchedSnapshotRefSeq: fetchResult.latestSnapshotRefSeq,
2376
+ });
2377
+ this.disposeFn(error);
2378
+ throw error;
2379
+ }
2380
+ await this.closeStaleSummarizer("RefreshLatestSummaryAckFetch");
2381
+ return;
2382
+ }
2383
+ // Notify the garbage collector so it can update its latest summary state.
2384
+ await this.garbageCollector.refreshLatestSummary(result);
2385
+ }
2386
+ /**
2387
+ * Fetches the latest snapshot from storage to refresh the cache as a performance optimization and closes the
2388
+ * summarizer to reload from new state.
2389
+ * @param summaryLogger - logger to use when fetching snapshot from storage
2390
+ * @returns a generic summarization error
2391
+ */
2392
+ async prefetchLatestSummaryThenClose(summaryLogger) {
2393
+ const readAndParseBlob = async (id) => (0, driver_utils_1.readAndParse)(this.storage, id);
2394
+ // This is a performance optimization as the same parent is likely to be elected again, and would use its
2395
+ // cache to fetch the snapshot instead of the network.
2396
+ await this.fetchLatestSnapshotFromStorage(summaryLogger, {
2397
+ eventName: "RefreshLatestSummaryFromServerFetch",
2398
+ }, readAndParseBlob);
2399
+ await this.closeStaleSummarizer("RefreshLatestSummaryFromServerFetch");
2400
+ return {
2401
+ stage: "base",
2402
+ error: "summary state stale - Unsupported option 'refreshLatestAck'",
2403
+ referenceSequenceNumber: this.deltaManager.lastSequenceNumber,
2404
+ minimumSequenceNumber: this.deltaManager.minimumSequenceNumber,
2405
+ };
2406
+ }
2407
+ async closeStaleSummarizer(codePath) {
2408
+ // Delay before restarting summarizer to prevent the summarizer from restarting too frequently.
2409
+ await (0, core_utils_1.delay)(this.closeSummarizerDelayMs);
2410
+ this._summarizer?.stop("latestSummaryStateStale");
2411
+ this.disposeFn();
2412
+ }
2413
+ /**
2414
+ * Downloads the latest snapshot from storage.
2415
+ * By default, it also closes the container after downloading the snapshot. However, this may be
2416
+ * overridden via options.
2417
+ */
2418
+ async fetchLatestSnapshotFromStorage(logger, event, readAndParseBlob) {
2419
+ return telemetry_utils_1.PerformanceEvent.timedExecAsync(logger, event, async (perfEvent) => {
2420
+ const stats = {};
2421
+ const trace = client_utils_1.Trace.start();
2422
+ const versions = await this.storage.getVersions(null, 1, "prefetchLatestSummaryBeforeClose", driver_definitions_1.FetchSource.noCache);
2423
+ (0, core_utils_1.assert)(!!versions && !!versions[0], 0x137 /* "Failed to get version from storage" */);
2424
+ stats.getVersionDuration = trace.trace().duration;
2425
+ const maybeSnapshot = await this.storage.getSnapshotTree(versions[0]);
2426
+ (0, core_utils_1.assert)(!!maybeSnapshot, 0x138 /* "Failed to get snapshot from storage" */);
2427
+ stats.getSnapshotDuration = trace.trace().duration;
2428
+ const latestSnapshotRefSeq = await (0, runtime_utils_1.seqFromTree)(maybeSnapshot, readAndParseBlob);
2429
+ stats.snapshotRefSeq = latestSnapshotRefSeq;
2430
+ stats.snapshotVersion = versions[0].id;
2431
+ perfEvent.end(stats);
2432
+ return {
2433
+ snapshotTree: maybeSnapshot,
2434
+ versionId: versions[0].id,
2435
+ latestSnapshotRefSeq,
2436
+ };
2437
+ });
2438
+ }
2439
+ async getPendingLocalState(props) {
2440
+ return telemetry_utils_1.PerformanceEvent.timedExecAsync(this.mc.logger, {
2441
+ eventName: "getPendingLocalState",
2442
+ notifyImminentClosure: props?.notifyImminentClosure,
2443
+ }, async (event) => {
2444
+ this.verifyNotClosed();
2445
+ // in case imminentClosure is set to true by future code, we don't
2446
+ // try to change its value
2447
+ if (!this.imminentClosure) {
2448
+ this.imminentClosure = props?.notifyImminentClosure ?? this.imminentClosure;
2449
+ }
2450
+ const stopBlobAttachingSignal = props?.stopBlobAttachingSignal;
2451
+ if (this._orderSequentiallyCalls !== 0) {
2452
+ throw new telemetry_utils_1.UsageError("can't get state during orderSequentially");
2453
+ }
2454
+ // Flush pending batch.
2455
+ // getPendingLocalState() is only exposed through Container.closeAndGetPendingLocalState(), so it's safe
2456
+ // to close current batch.
2457
+ this.flush();
2458
+ const pendingAttachmentBlobs = this.imminentClosure
2459
+ ? await this.blobManager.attachAndGetPendingBlobs(stopBlobAttachingSignal)
2460
+ : undefined;
2461
+ const pending = this.pendingStateManager.getLocalState();
2462
+ if (!pendingAttachmentBlobs && !this.hasPendingMessages()) {
2463
+ return; // no pending state to save
2464
+ }
2465
+ const pendingIdCompressorState = this.idCompressor?.serialize(true);
2466
+ const pendingState = {
2467
+ pending,
2468
+ pendingAttachmentBlobs,
2469
+ pendingIdCompressorState,
2470
+ };
2471
+ event.end({
2472
+ attachmentBlobsSize: Object.keys(pendingAttachmentBlobs ?? {}).length,
2473
+ pendingOpsSize: pending?.pendingStates.length,
2474
+ });
2475
+ return pendingState;
2476
+ });
2477
+ }
2478
+ summarizeOnDemand(options) {
2479
+ if (this.isSummarizerClient) {
2480
+ return this.summarizer.summarizeOnDemand(options);
2481
+ }
2482
+ else if (this.summaryManager !== undefined) {
2483
+ return this.summaryManager.summarizeOnDemand(options);
2484
+ }
2485
+ else {
2486
+ // If we're not the summarizer, and we don't have a summaryManager, we expect that
2487
+ // disableSummaries is turned on. We are throwing instead of returning a failure here,
2488
+ // because it is a misuse of the API rather than an expected failure.
2489
+ throw new telemetry_utils_1.UsageError(`Can't summarize, disableSummaries: ${this.summariesDisabled}`);
2490
+ }
2491
+ }
2492
+ enqueueSummarize(options) {
2493
+ if (this.isSummarizerClient) {
2494
+ return this.summarizer.enqueueSummarize(options);
2495
+ }
2496
+ else if (this.summaryManager !== undefined) {
2497
+ return this.summaryManager.enqueueSummarize(options);
2498
+ }
2499
+ else {
2500
+ // If we're not the summarizer, and we don't have a summaryManager, we expect that
2501
+ // generateSummaries is turned off. We are throwing instead of returning a failure here,
2502
+ // because it is a misuse of the API rather than an expected failure.
2503
+ throw new telemetry_utils_1.UsageError(`Can't summarize, disableSummaries: ${this.summariesDisabled}`);
2504
+ }
2505
+ }
2506
+ /**
2507
+ * Forms a function that will create and retrieve a Summarizer.
2508
+ */
2509
+ formCreateSummarizerFn(loader) {
2510
+ return async () => {
2511
+ return createSummarizer(loader, `/${summarizerRequestUrl}`);
2512
+ };
2513
+ }
2514
+ validateSummaryHeuristicConfiguration(configuration) {
2515
+ // eslint-disable-next-line no-restricted-syntax
2516
+ for (const prop in configuration) {
2517
+ if (typeof configuration[prop] === "number" && configuration[prop] < 0) {
2518
+ throw new telemetry_utils_1.UsageError(`Summary heuristic configuration property "${prop}" cannot be less than 0`);
2519
+ }
2520
+ }
2521
+ if (configuration.minIdleTime > configuration.maxIdleTime) {
2522
+ throw new telemetry_utils_1.UsageError(`"minIdleTime" [${configuration.minIdleTime}] cannot be greater than "maxIdleTime" [${configuration.maxIdleTime}]`);
2523
+ }
2524
+ }
2525
+ get groupedBatchingEnabled() {
2526
+ const killSwitch = this.mc.config.getBoolean("Fluid.ContainerRuntime.DisableGroupedBatching");
2527
+ return killSwitch !== true && this.runtimeOptions.enableGroupedBatching;
2528
+ }
2529
+ }
2530
+ exports.ContainerRuntime = ContainerRuntime;
2531
+ //# sourceMappingURL=containerRuntime.cjs.map