@fluidframework/container-runtime 1.4.0-115997 → 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 -6
  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 -257
  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 -34
  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 -6
  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 -36
  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 +174 -73
  481. package/prettier.config.cjs +8 -0
  482. package/src/batchTracker.ts +60 -55
  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 -616
  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
@@ -3,995 +3,1168 @@
3
3
  * Licensed under the MIT License.
4
4
  */
5
5
 
6
- import { IDisposable, ITelemetryLogger } from "@fluidframework/common-definitions";
7
6
  import {
8
- FluidObject,
9
- IRequest,
10
- IResponse,
11
- IFluidHandle,
7
+ IDisposable,
8
+ FluidObject,
9
+ IRequest,
10
+ IResponse,
11
+ IFluidHandle,
12
+ ITelemetryProperties,
12
13
  } from "@fluidframework/core-interfaces";
13
14
  import {
14
- IAudience,
15
- IDeltaManager,
16
- BindState,
17
- AttachState,
18
- ILoaderOptions,
15
+ IAudience,
16
+ IDeltaManager,
17
+ AttachState,
18
+ ILoaderOptions,
19
19
  } from "@fluidframework/container-definitions";
20
- import {
21
- assert,
22
- Deferred,
23
- LazyPromise,
24
- TypedEventEmitter,
25
- } from "@fluidframework/common-utils";
20
+ import { TypedEventEmitter } from "@fluid-internal/client-utils";
21
+ import { assert, Deferred, LazyPromise } from "@fluidframework/core-utils";
26
22
  import { IDocumentStorageService } from "@fluidframework/driver-definitions";
27
- import { readAndParse } from "@fluidframework/driver-utils";
28
- import { BlobTreeEntry } from "@fluidframework/protocol-base";
23
+ import { BlobTreeEntry, readAndParse } from "@fluidframework/driver-utils";
29
24
  import {
30
- IClientDetails,
31
- IDocumentMessage,
32
- IQuorumClients,
33
- ISequencedDocumentMessage,
34
- ISnapshotTree,
35
- ITreeEntry,
25
+ IClientDetails,
26
+ IDocumentMessage,
27
+ IQuorumClients,
28
+ ISequencedDocumentMessage,
29
+ ISnapshotTree,
30
+ ITreeEntry,
36
31
  } from "@fluidframework/protocol-definitions";
32
+ import { IContainerRuntime } from "@fluidframework/container-runtime-definitions";
37
33
  import {
38
- IContainerRuntime,
39
- } from "@fluidframework/container-runtime-definitions";
40
- import {
41
- channelsTreeName,
42
- CreateChildSummarizerNodeFn,
43
- CreateChildSummarizerNodeParam,
44
- FluidDataStoreRegistryEntry,
45
- gcBlobKey,
46
- IAttachMessage,
47
- IFluidDataStoreChannel,
48
- IFluidDataStoreContext,
49
- IFluidDataStoreContextDetached,
50
- IFluidDataStoreContextEvents,
51
- IFluidDataStoreRegistry,
52
- IGarbageCollectionData,
53
- IGarbageCollectionDetailsBase,
54
- IGarbageCollectionSummaryDetails,
55
- IInboundSignalMessage,
56
- IProvideFluidDataStoreFactory,
57
- ISummarizeInternalResult,
58
- ISummarizeResult,
59
- ISummarizerNodeWithGC,
60
- SummarizeInternalFn,
61
- ITelemetryContext,
34
+ channelsTreeName,
35
+ CreateChildSummarizerNodeFn,
36
+ CreateChildSummarizerNodeParam,
37
+ FluidDataStoreRegistryEntry,
38
+ IAttachMessage,
39
+ IFluidDataStoreChannel,
40
+ IFluidDataStoreContext,
41
+ IFluidDataStoreContextDetached,
42
+ IFluidDataStoreContextEvents,
43
+ IFluidDataStoreRegistry,
44
+ IGarbageCollectionData,
45
+ IGarbageCollectionDetailsBase,
46
+ IInboundSignalMessage,
47
+ IProvideFluidDataStoreFactory,
48
+ ISummarizeInternalResult,
49
+ ISummarizeResult,
50
+ ISummarizerNodeWithGC,
51
+ SummarizeInternalFn,
52
+ ITelemetryContext,
53
+ IIdCompressor,
54
+ IIdCompressorCore,
55
+ VisibilityState,
62
56
  } from "@fluidframework/runtime-definitions";
63
57
  import { addBlobToSummary, convertSummaryTreeToITree } from "@fluidframework/runtime-utils";
64
58
  import {
65
- ChildLogger,
66
- LoggingError,
67
- TelemetryDataTag,
68
- ThresholdCounter,
59
+ createChildMonitoringContext,
60
+ DataCorruptionError,
61
+ DataProcessingError,
62
+ extractSafePropertiesFromMessage,
63
+ generateStack,
64
+ ITelemetryLoggerExt,
65
+ LoggingError,
66
+ MonitoringContext,
67
+ tagCodeArtifacts,
68
+ ThresholdCounter,
69
69
  } from "@fluidframework/telemetry-utils";
70
- import { DataProcessingError } from "@fluidframework/container-utils";
71
-
72
- import { ContainerRuntime } from "./containerRuntime";
73
70
  import {
74
- dataStoreAttributesBlobName,
75
- hasIsolatedChannels,
76
- wrapSummaryInChannelsTree,
77
- ReadFluidDataStoreAttributes,
78
- WriteFluidDataStoreAttributes,
79
- getAttributesFormatVersion,
80
- getFluidDataStoreAttributes,
81
- } from "./summaryFormat";
71
+ dataStoreAttributesBlobName,
72
+ hasIsolatedChannels,
73
+ wrapSummaryInChannelsTree,
74
+ ReadFluidDataStoreAttributes,
75
+ WriteFluidDataStoreAttributes,
76
+ getAttributesFormatVersion,
77
+ getFluidDataStoreAttributes,
78
+ summarizerClientType,
79
+ } from "./summary";
80
+ import { ContainerRuntime } from "./containerRuntime";
81
+ import { sendGCUnexpectedUsageEvent } from "./gc";
82
82
 
83
83
  function createAttributes(
84
- pkg: readonly string[],
85
- isRootDataStore: boolean,
86
- disableIsolatedChannels: boolean,
84
+ pkg: readonly string[],
85
+ isRootDataStore: boolean,
87
86
  ): WriteFluidDataStoreAttributes {
88
- const stringifiedPkg = JSON.stringify(pkg);
89
- return disableIsolatedChannels ? {
90
- pkg: stringifiedPkg,
91
- snapshotFormatVersion: "0.1",
92
- isRootDataStore,
93
- } : {
94
- pkg: stringifiedPkg,
95
- summaryFormatVersion: 2,
96
- isRootDataStore,
97
- };
87
+ const stringifiedPkg = JSON.stringify(pkg);
88
+ return {
89
+ pkg: stringifiedPkg,
90
+ summaryFormatVersion: 2,
91
+ isRootDataStore,
92
+ };
98
93
  }
99
- export function createAttributesBlob(
100
- pkg: readonly string[],
101
- isRootDataStore: boolean,
102
- disableIsolatedChannels: boolean,
103
- ): ITreeEntry {
104
- const attributes = createAttributes(pkg, isRootDataStore, disableIsolatedChannels);
105
- return new BlobTreeEntry(dataStoreAttributesBlobName, JSON.stringify(attributes));
94
+ export function createAttributesBlob(pkg: readonly string[], isRootDataStore: boolean): ITreeEntry {
95
+ const attributes = createAttributes(pkg, isRootDataStore);
96
+ return new BlobTreeEntry(dataStoreAttributesBlobName, JSON.stringify(attributes));
106
97
  }
107
98
 
108
99
  interface ISnapshotDetails {
109
- pkg: readonly string[];
110
- isRootDataStore: boolean;
111
- snapshot?: ISnapshotTree;
100
+ pkg: readonly string[];
101
+ isRootDataStore: boolean;
102
+ snapshot?: ISnapshotTree;
112
103
  }
113
104
 
114
105
  interface FluidDataStoreMessage {
115
- content: any;
116
- type: string;
106
+ content: any;
107
+ type: string;
117
108
  }
118
109
 
119
110
  /** Properties necessary for creating a FluidDataStoreContext */
120
111
  export interface IFluidDataStoreContextProps {
121
- readonly id: string;
122
- readonly runtime: ContainerRuntime;
123
- readonly storage: IDocumentStorageService;
124
- readonly scope: FluidObject;
125
- readonly createSummarizerNodeFn: CreateChildSummarizerNodeFn;
126
- readonly writeGCDataAtRoot: boolean;
127
- readonly disableIsolatedChannels: boolean;
128
- readonly pkg?: Readonly<string[]>;
112
+ readonly id: string;
113
+ readonly runtime: ContainerRuntime;
114
+ readonly storage: IDocumentStorageService;
115
+ readonly scope: FluidObject;
116
+ readonly createSummarizerNodeFn: CreateChildSummarizerNodeFn;
117
+ readonly pkg?: Readonly<string[]>;
129
118
  }
130
119
 
131
120
  /** Properties necessary for creating a local FluidDataStoreContext */
132
121
  export interface ILocalFluidDataStoreContextProps extends IFluidDataStoreContextProps {
133
- readonly pkg: Readonly<string[]> | undefined;
134
- readonly snapshotTree: ISnapshotTree | undefined;
135
- readonly isRootDataStore: boolean | undefined;
136
- readonly makeLocallyVisibleFn: () => void;
137
- /**
138
- * @deprecated 0.16 Issue #1635, #3631
139
- */
140
- readonly createProps?: any;
122
+ readonly pkg: Readonly<string[]> | undefined;
123
+ readonly snapshotTree: ISnapshotTree | undefined;
124
+ readonly isRootDataStore: boolean | undefined;
125
+ readonly makeLocallyVisibleFn: () => void;
126
+ /**
127
+ * @deprecated 0.16 Issue #1635, #3631
128
+ */
129
+ readonly createProps?: any;
141
130
  }
142
131
 
143
132
  /** Properties necessary for creating a remote FluidDataStoreContext */
144
133
  export interface IRemoteFluidDataStoreContextProps extends IFluidDataStoreContextProps {
145
- readonly snapshotTree: ISnapshotTree | string | undefined;
146
- readonly getBaseGCDetails: () => Promise<IGarbageCollectionDetailsBase | undefined>;
134
+ readonly snapshotTree: ISnapshotTree | undefined;
147
135
  }
148
136
 
149
137
  /**
150
138
  * Represents the context for the store. This context is passed to the store runtime.
151
139
  */
152
- export abstract class FluidDataStoreContext extends TypedEventEmitter<IFluidDataStoreContextEvents> implements
153
- IFluidDataStoreContext,
154
- IDisposable {
155
- public get packagePath(): readonly string[] {
156
- assert(this.pkg !== undefined, 0x139 /* "Undefined package path" */);
157
- return this.pkg;
158
- }
159
-
160
- public get options(): ILoaderOptions {
161
- return this._containerRuntime.options;
162
- }
163
-
164
- public get clientId(): string | undefined {
165
- return this._containerRuntime.clientId;
166
- }
167
-
168
- public get clientDetails(): IClientDetails {
169
- return this._containerRuntime.clientDetails;
170
- }
171
-
172
- public get logger(): ITelemetryLogger {
173
- return this._containerRuntime.logger;
174
- }
175
-
176
- public get deltaManager(): IDeltaManager<ISequencedDocumentMessage, IDocumentMessage> {
177
- return this._containerRuntime.deltaManager;
178
- }
179
-
180
- public get connected(): boolean {
181
- return this._containerRuntime.connected;
182
- }
183
-
184
- public get IFluidHandleContext() {
185
- return this._containerRuntime.IFluidHandleContext;
186
- }
187
-
188
- public get containerRuntime(): IContainerRuntime {
189
- return this._containerRuntime;
190
- }
191
-
192
- public get isLoaded(): boolean {
193
- return this.loaded;
194
- }
195
-
196
- public get baseSnapshot(): ISnapshotTree | undefined {
197
- return this._baseSnapshot;
198
- }
199
-
200
- private _disposed = false;
201
- public get disposed() { return this._disposed; }
202
-
203
- public get attachState(): AttachState {
204
- return this._attachState;
205
- }
206
-
207
- public get IFluidDataStoreRegistry(): IFluidDataStoreRegistry | undefined {
208
- return this.registry;
209
- }
210
-
211
- /**
212
- * A datastore is considered as root if it
213
- * 1. is root in memory - see isInMemoryRoot
214
- * 2. is root as part of the base snapshot that the datastore loaded from
215
- * @returns whether a datastore is root
216
- */
217
- public async isRoot(): Promise<boolean> {
218
- return this.isInMemoryRoot() || (await this.getInitialSnapshotDetails()).isRootDataStore;
219
- }
220
-
221
- /**
222
- * There are 3 states where isInMemoryRoot needs to be true
223
- * 1. when a datastore becomes aliased. This can happen for both remote and local datastores
224
- * 2. when a datastore is created locally as root
225
- * 3. when a datastore is created locally as root and is rehydrated
226
- * @returns whether a datastore is root in memory
227
- */
228
- protected isInMemoryRoot(): boolean {
229
- return this._isInMemoryRoot;
230
- }
231
-
232
- protected registry: IFluidDataStoreRegistry | undefined;
233
-
234
- protected detachedRuntimeCreation = false;
235
- public readonly bindToContext: () => void;
236
- protected channel: IFluidDataStoreChannel | undefined;
237
- private loaded = false;
238
- protected pending: ISequencedDocumentMessage[] | undefined = [];
239
- protected channelDeferred: Deferred<IFluidDataStoreChannel> | undefined;
240
- private _baseSnapshot: ISnapshotTree | undefined;
241
- protected _attachState: AttachState;
242
- private _isInMemoryRoot: boolean = false;
243
- protected readonly summarizerNode: ISummarizerNodeWithGC;
244
- private readonly subLogger: ITelemetryLogger;
245
- private readonly thresholdOpsCounter: ThresholdCounter;
246
- private static readonly pendingOpsCountThreshold = 1000;
247
-
248
- // The used state of this node as per the last GC run. This is used to update the used state of the channel
249
- // if it realizes after GC is run.
250
- private lastUsedState: { usedRoutes: string[]; gcTimestamp?: number; } | undefined;
251
-
252
- public readonly id: string;
253
- private readonly _containerRuntime: ContainerRuntime;
254
- public readonly storage: IDocumentStorageService;
255
- public readonly scope: FluidObject;
256
- private readonly writeGCDataAtRoot: boolean;
257
- protected readonly disableIsolatedChannels: boolean;
258
- protected pkg?: readonly string[];
259
-
260
- constructor(
261
- props: IFluidDataStoreContextProps,
262
- private readonly existing: boolean,
263
- private bindState: BindState,
264
- public readonly isLocalDataStore: boolean,
265
- private readonly makeLocallyVisibleFn: () => void,
266
- ) {
267
- super();
268
-
269
- this._containerRuntime = props.runtime;
270
- this.id = props.id;
271
- this.storage = props.storage;
272
- this.scope = props.scope;
273
- this.writeGCDataAtRoot = props.writeGCDataAtRoot;
274
- this.disableIsolatedChannels = props.disableIsolatedChannels;
275
- this.pkg = props.pkg;
276
-
277
- // URIs use slashes as delimiters. Handles use URIs.
278
- // Thus having slashes in types almost guarantees trouble down the road!
279
- assert(!this.id.includes("/"), 0x13a /* Data store ID contains slash */);
280
-
281
- this._attachState = this.containerRuntime.attachState !== AttachState.Detached && this.existing ?
282
- this.containerRuntime.attachState : AttachState.Detached;
283
-
284
- this.bindToContext = () => {
285
- assert(this.bindState === BindState.NotBound, 0x13b /* "datastore context is already in bound state" */);
286
- this.bindState = BindState.Binding;
287
- assert(this.channel !== undefined, 0x13c /* "undefined channel on datastore context" */);
288
- this.makeLocallyVisible();
289
- this.bindState = BindState.Bound;
290
- };
291
-
292
- const thisSummarizeInternal =
293
- async (fullTree: boolean, trackState: boolean, telemetryContext?: ITelemetryContext) =>
294
- this.summarizeInternal(fullTree, trackState, telemetryContext);
295
-
296
- this.summarizerNode = props.createSummarizerNodeFn(
297
- thisSummarizeInternal,
298
- async (fullGC?: boolean) => this.getGCDataInternal(fullGC),
299
- async () => this.getBaseGCDetails(),
300
- );
301
-
302
- this.subLogger = ChildLogger.create(this.logger, "FluidDataStoreContext");
303
- this.thresholdOpsCounter = new ThresholdCounter(FluidDataStoreContext.pendingOpsCountThreshold, this.subLogger);
304
- }
305
-
306
- public dispose(): void {
307
- if (this._disposed) {
308
- return;
309
- }
310
- this._disposed = true;
311
-
312
- // Dispose any pending runtime after it gets fulfilled
313
- // Errors are logged where this.channelDeferred is consumed/generated (realizeCore(), bindRuntime())
314
- if (this.channelDeferred) {
315
- this.channelDeferred.promise.then((runtime) => {
316
- runtime.dispose();
317
- }).catch((error) => {});
318
- }
319
- }
320
-
321
- private rejectDeferredRealize(reason: string, packageName?: string): never {
322
- throw new LoggingError(reason, { packageName: { value: packageName, tag: TelemetryDataTag.PackageData } });
323
- }
324
-
325
- public async realize(): Promise<IFluidDataStoreChannel> {
326
- assert(!this.detachedRuntimeCreation, 0x13d /* "Detached runtime creation on realize()" */);
327
- if (!this.channelDeferred) {
328
- this.channelDeferred = new Deferred<IFluidDataStoreChannel>();
329
- this.realizeCore(this.existing).catch((error) => {
330
- const errorWrapped = DataProcessingError.wrapIfUnrecognized(error, "realizeFluidDataStoreContext");
331
- errorWrapped.addTelemetryProperties({ fluidDataStoreId: { value: this.id, tag: "PackageData" } });
332
- this.channelDeferred?.reject(errorWrapped);
333
- this.logger.sendErrorEvent({ eventName: "RealizeError" }, errorWrapped);
334
- });
335
- }
336
- return this.channelDeferred.promise;
337
- }
338
-
339
- protected async factoryFromPackagePath(packages?: readonly string[]) {
340
- assert(this.pkg === packages, 0x13e /* "Unexpected package path" */);
341
- if (packages === undefined) {
342
- this.rejectDeferredRealize("packages is undefined");
343
- }
344
-
345
- let entry: FluidDataStoreRegistryEntry | undefined;
346
- let registry: IFluidDataStoreRegistry | undefined = this._containerRuntime.IFluidDataStoreRegistry;
347
- let lastPkg: string | undefined;
348
- for (const pkg of packages) {
349
- if (!registry) {
350
- this.rejectDeferredRealize("No registry for package", lastPkg);
351
- }
352
- lastPkg = pkg;
353
- entry = await registry.get(pkg);
354
- if (!entry) {
355
- this.rejectDeferredRealize("Registry does not contain entry for the package", pkg);
356
- }
357
- registry = entry.IFluidDataStoreRegistry;
358
- }
359
- const factory = entry?.IFluidDataStoreFactory;
360
- if (factory === undefined) {
361
- this.rejectDeferredRealize("Can't find factory for package", lastPkg);
362
- }
363
-
364
- return { factory, registry };
365
- }
366
-
367
- private async realizeCore(existing: boolean): Promise<void> {
368
- const details = await this.getInitialSnapshotDetails();
369
- // Base snapshot is the baseline where pending ops are applied to.
370
- // It is important that this be in sync with the pending ops, and also
371
- // that it is set here, before bindRuntime is called.
372
- this._baseSnapshot = details.snapshot;
373
- const packages = details.pkg;
374
-
375
- const { factory, registry } = await this.factoryFromPackagePath(packages);
376
-
377
- assert(this.registry === undefined, 0x13f /* "datastore context registry is already set" */);
378
- this.registry = registry;
379
-
380
- const channel = await factory.instantiateDataStore(this, existing);
381
- assert(channel !== undefined, 0x140 /* "undefined channel on datastore context" */);
382
- this.bindRuntime(channel);
383
- }
384
-
385
- /**
386
- * Notifies this object about changes in the connection state.
387
- * @param value - New connection state.
388
- * @param clientId - ID of the client. It's old ID when in disconnected state and
389
- * it's new client ID when we are connecting or connected.
390
- */
391
- public setConnectionState(connected: boolean, clientId?: string) {
392
- this.verifyNotClosed();
393
-
394
- // Connection events are ignored if the store is not yet loaded
395
- if (!this.loaded) {
396
- return;
397
- }
398
-
399
- assert(this.connected === connected, 0x141 /* "Unexpected connected state" */);
400
-
401
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
402
- this.channel!.setConnectionState(connected, clientId);
403
- }
404
-
405
- public process(messageArg: ISequencedDocumentMessage, local: boolean, localOpMetadata: unknown): void {
406
- this.verifyNotClosed();
407
-
408
- const innerContents = messageArg.contents as FluidDataStoreMessage;
409
- const message = {
410
- ...messageArg,
411
- type: innerContents.type,
412
- contents: innerContents.content,
413
- };
414
-
415
- this.summarizerNode.recordChange(message);
416
-
417
- if (this.loaded) {
418
- return this.channel?.process(message, local, localOpMetadata);
419
- } else {
420
- assert(!local, 0x142 /* "local store channel is not loaded" */);
421
- assert(this.pending !== undefined, 0x23d /* "pending is undefined" */);
422
- this.pending.push(message);
423
- this.thresholdOpsCounter.sendIfMultiple("StorePendingOps", this.pending.length);
424
- }
425
- }
426
-
427
- public processSignal(message: IInboundSignalMessage, local: boolean): void {
428
- this.verifyNotClosed();
429
-
430
- // Signals are ignored if the store is not yet loaded
431
- if (!this.loaded) {
432
- return;
433
- }
434
-
435
- this.channel?.processSignal(message, local);
436
- }
437
-
438
- public getQuorum(): IQuorumClients {
439
- return this._containerRuntime.getQuorum();
440
- }
441
-
442
- public getAudience(): IAudience {
443
- return this._containerRuntime.getAudience();
444
- }
445
-
446
- /**
447
- * Returns a summary at the current sequence number.
448
- * @param fullTree - true to bypass optimizations and force a full summary tree
449
- * @param trackState - This tells whether we should track state from this summary.
450
- * @param telemetryContext - summary data passed through the layers for telemetry purposes
451
- */
452
- public async summarize(
453
- fullTree: boolean = false,
454
- trackState: boolean = true,
455
- telemetryContext?: ITelemetryContext,
456
- ): Promise<ISummarizeResult> {
457
- return this.summarizerNode.summarize(fullTree, trackState, telemetryContext);
458
- }
459
-
460
- private async summarizeInternal(
461
- fullTree: boolean,
462
- trackState: boolean,
463
- telemetryContext?: ITelemetryContext,
464
- ): Promise<ISummarizeInternalResult> {
465
- await this.realize();
466
-
467
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
468
- const summarizeResult = await this.channel!.summarize(fullTree, trackState, telemetryContext);
469
- let pathPartsForChildren: string[] | undefined;
470
-
471
- if (!this.disableIsolatedChannels) {
472
- // Wrap dds summaries in .channels subtree.
473
- wrapSummaryInChannelsTree(summarizeResult);
474
- pathPartsForChildren = [channelsTreeName];
475
- }
476
-
477
- // Add data store's attributes to the summary.
478
- const { pkg } = await this.getInitialSnapshotDetails();
479
- const isRoot = await this.isRoot();
480
- const attributes = createAttributes(pkg, isRoot, this.disableIsolatedChannels);
481
- addBlobToSummary(summarizeResult, dataStoreAttributesBlobName, JSON.stringify(attributes));
482
-
483
- // Add GC data to the summary if it's not written at the root.
484
- if (!this.writeGCDataAtRoot) {
485
- addBlobToSummary(summarizeResult, gcBlobKey, JSON.stringify(this.summarizerNode.getGCSummaryDetails()));
486
- }
487
-
488
- // If we are not referenced, mark the summary tree as unreferenced. Also, update unreferenced blob
489
- // size in the summary stats with the blobs size of this data store.
490
- if (!this.summarizerNode.isReferenced()) {
491
- summarizeResult.summary.unreferenced = true;
492
- summarizeResult.stats.unreferencedBlobSize = summarizeResult.stats.totalBlobSize;
493
- }
494
-
495
- return {
496
- ...summarizeResult,
497
- id: this.id,
498
- pathPartsForChildren,
499
- };
500
- }
501
-
502
- /**
503
- * Returns the data used for garbage collection. This includes a list of GC nodes that represent this data store
504
- * including any of its child channel contexts. Each node has a set of outbound routes to other GC nodes in the
505
- * document.
506
- * If there is no new data in this data store since the last summary, previous GC data is used.
507
- * If there is new data, the GC data is generated again (by calling getGCDataInternal).
508
- * @param fullGC - true to bypass optimizations and force full generation of GC data.
509
- */
510
- public async getGCData(fullGC: boolean = false): Promise<IGarbageCollectionData> {
511
- return this.summarizerNode.getGCData(fullGC);
512
- }
513
-
514
- /**
515
- * Generates data used for garbage collection. This is called when there is new data since last summary. It
516
- * realizes the data store and calls into each channel context to get its GC data.
517
- * @param fullGC - true to bypass optimizations and force full generation of GC data.
518
- */
519
- private async getGCDataInternal(fullGC: boolean = false): Promise<IGarbageCollectionData> {
520
- await this.realize();
521
- assert(this.channel !== undefined, 0x143 /* "Channel should not be undefined when running GC" */);
522
-
523
- return this.channel.getGCData(fullGC);
524
- }
525
-
526
- /**
527
- * After GC has run, called to notify the data store of routes used in it. These are used for the following:
528
- * 1. To identify if this data store is being referenced in the document or not.
529
- * 2. To determine if it needs to re-summarize in case used routes changed since last summary.
530
- * 3. These are added to the summary generated by the data store.
531
- * 4. To notify child contexts of their used routes. This is done immediately if the data store is loaded. Else,
532
- * it is done when realizing the data store.
533
- * 5. To update the timestamp when this data store or any children are marked as unreferenced.
534
- * @param usedRoutes - The routes that are used in this data store.
535
- * @param gcTimestamp - The time when GC was run that generated these used routes. If any node becomes unreferenced
536
- * as part of this GC run, this should be used to update the time when it happens.
537
- */
538
- public updateUsedRoutes(usedRoutes: string[], gcTimestamp?: number) {
539
- // Update the used routes in this data store's summarizer node.
540
- this.summarizerNode.updateUsedRoutes(usedRoutes, gcTimestamp);
541
-
542
- /**
543
- * If the data store has not been realized yet, we need this used state to update the used state of the channel
544
- * when it realizes. It's safe to keep only the last used state because if something changes because of this GC
545
- * run, the data store will be immediately realized as part of the summary that follows GC. For example, if a
546
- * child's reference state changes, the gcTimestamp has to be used to update its unreferencedTimestamp. Since
547
- * it will result in a change in this data store's used routes, it will be realized to regenerate its summary.
548
- */
549
- this.lastUsedState = { usedRoutes, gcTimestamp };
550
-
551
- // If we are loaded, call the channel so it can update the used routes of the child contexts.
552
- // If we are not loaded, we will update this when we are realized.
553
- if (this.loaded) {
554
- this.updateChannelUsedRoutes();
555
- }
556
- }
557
-
558
- /**
559
- * Called when a new outbound reference is added to another node. This is used by garbage collection to identify
560
- * all references added in the system.
561
- * @param srcHandle - The handle of the node that added the reference.
562
- * @param outboundHandle - The handle of the outbound node that is referenced.
563
- */
564
- public addedGCOutboundReference(srcHandle: IFluidHandle, outboundHandle: IFluidHandle) {
565
- this._containerRuntime.addedGCOutboundReference(srcHandle, outboundHandle);
566
- }
567
-
568
- /**
569
- * Updates the used routes of the channel and its child contexts. The channel must be loaded before calling this.
570
- * It is called in these two scenarios:
571
- * 1. When the used routes of the data store is updated and the data store is loaded.
572
- * 2. When the data store is realized. This updates the channel's used routes as per last GC run.
573
- */
574
- private updateChannelUsedRoutes() {
575
- assert(this.loaded, 0x144 /* "Channel should be loaded when updating used routes" */);
576
- assert(this.channel !== undefined, 0x145 /* "Channel should be present when data store is loaded" */);
577
-
578
- // If there is no lastUsedState, GC has not run up until this point.
579
- if (this.lastUsedState === undefined) {
580
- return;
581
- }
582
-
583
- // Remove the route to this data store, if it exists.
584
- const usedChannelRoutes = this.lastUsedState.usedRoutes.filter(
585
- (id: string) => { return id !== "/" && id !== ""; },
586
- );
587
- this.channel.updateUsedRoutes(usedChannelRoutes, this.lastUsedState.gcTimestamp);
588
- }
589
-
590
- /**
591
- * @deprecated 0.18.Should call request on the runtime directly
592
- */
593
- public async request(request: IRequest): Promise<IResponse> {
594
- const runtime = await this.realize();
595
- return runtime.request(request);
596
- }
597
-
598
- public submitMessage(type: string, content: any, localOpMetadata: unknown): void {
599
- this.verifyNotClosed();
600
- assert(!!this.channel, 0x146 /* "Channel must exist when submitting message" */);
601
- const fluidDataStoreContent: FluidDataStoreMessage = {
602
- content,
603
- type,
604
- };
605
- this._containerRuntime.submitDataStoreOp(
606
- this.id,
607
- fluidDataStoreContent,
608
- localOpMetadata);
609
- }
610
-
611
- /**
612
- * This is called from a SharedSummaryBlock that does not generate ops but only wants to be part of the summary.
613
- * It indicates that there is data in the object that needs to be summarized.
614
- * We will update the latestSequenceNumber of the summary tracker of this
615
- * store and of the object's channel.
616
- *
617
- * @param address - The address of the channel that is dirty.
618
- *
619
- */
620
- public setChannelDirty(address: string): void {
621
- this.verifyNotClosed();
622
-
623
- // Get the latest sequence number.
624
- const latestSequenceNumber = this.deltaManager.lastSequenceNumber;
625
-
626
- this.summarizerNode.invalidate(latestSequenceNumber);
627
-
628
- const channelSummarizerNode = this.summarizerNode.getChild(address);
629
-
630
- if (channelSummarizerNode) {
631
- channelSummarizerNode.invalidate(latestSequenceNumber); // TODO: lazy load problem?
632
- }
633
- }
634
-
635
- public submitSignal(type: string, content: any) {
636
- this.verifyNotClosed();
637
- assert(!!this.channel, 0x147 /* "Channel must exist on submitting signal" */);
638
- return this._containerRuntime.submitDataStoreSignal(this.id, type, content);
639
- }
640
-
641
- /**
642
- * This is called by the data store channel when it becomes locally visible indicating that it is ready to become
643
- * globally visible now.
644
- */
645
- public makeLocallyVisible() {
646
- assert(this.channel !== undefined, 0x2cf /* "undefined channel on datastore context" */);
647
- this.makeLocallyVisibleFn();
648
- }
649
-
650
- protected bindRuntime(channel: IFluidDataStoreChannel) {
651
- if (this.channel) {
652
- throw new Error("Runtime already bound");
653
- }
654
-
655
- try {
656
- assert(!this.detachedRuntimeCreation, 0x148 /* "Detached runtime creation on runtime bind" */);
657
- assert(this.channelDeferred !== undefined, 0x149 /* "Undefined channel deferral" */);
658
- assert(this.pkg !== undefined, 0x14a /* "Undefined package path" */);
659
-
660
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
661
- const pending = this.pending!;
662
-
663
- // Apply all pending ops
664
- for (const op of pending) {
665
- channel.process(op, false, undefined /* localOpMetadata */);
666
- }
667
-
668
- this.thresholdOpsCounter.send("ProcessPendingOps", pending.length);
669
- this.pending = undefined;
670
-
671
- // And now mark the runtime active
672
- this.loaded = true;
673
- this.channel = channel;
674
-
675
- // Freeze the package path to ensure that someone doesn't modify it when it is
676
- // returned in packagePath().
677
- Object.freeze(this.pkg);
678
-
679
- /**
680
- * Update the used routes of the channel. If GC has run before this data store was realized, we will have
681
- * the used routes saved. So, this will ensure that all the child contexts have up-to-date used routes as
682
- * per the last time GC was run.
683
- * Also, this data store may have been realized during summarize. In that case, the child contexts need to
684
- * have their used routes updated to determine if its needs to summarize again and to add it to the summary.
685
- */
686
- this.updateChannelUsedRoutes();
687
-
688
- // And notify the pending promise it is now available
689
- this.channelDeferred.resolve(this.channel);
690
- } catch (error) {
691
- this.channelDeferred?.reject(error);
692
- this.logger.sendErrorEvent(
693
- { eventName: "BindRuntimeError", fluidDataStoreId: { value: this.id, tag: "PackageData" } },
694
- error);
695
- }
696
- }
697
-
698
- public async getAbsoluteUrl(relativeUrl: string): Promise<string | undefined> {
699
- if (this.attachState !== AttachState.Attached) {
700
- return undefined;
701
- }
702
- return this._containerRuntime.getAbsoluteUrl(relativeUrl);
703
- }
704
-
705
- public abstract generateAttachMessage(): IAttachMessage;
706
-
707
- public abstract getInitialSnapshotDetails(): Promise<ISnapshotDetails>;
708
-
709
- /**
710
- * @deprecated - Sets the datastore as root, for aliasing purposes: #7948
711
- * This method should not be used outside of the aliasing context.
712
- * It will be removed, as the source of truth for this flag will be the aliasing blob.
713
- */
714
- public setInMemoryRoot(): void {
715
- this._isInMemoryRoot = true;
716
- }
717
-
718
- /**
719
- * @deprecated - Renamed to getBaseGCDetails().
720
- */
721
- public abstract getInitialGCSummaryDetails(): Promise<IGarbageCollectionSummaryDetails>;
722
-
723
- public abstract getBaseGCDetails(): Promise<IGarbageCollectionDetailsBase>;
724
-
725
- public reSubmit(contents: any, localOpMetadata: unknown) {
726
- assert(!!this.channel, 0x14b /* "Channel must exist when resubmitting ops" */);
727
- const innerContents = contents as FluidDataStoreMessage;
728
- this.channel.reSubmit(innerContents.type, innerContents.content, localOpMetadata);
729
- }
730
-
731
- public rollback(contents: any, localOpMetadata: unknown) {
732
- if (!this.channel) {
733
- throw new Error("Channel must exist when rolling back ops");
734
- }
735
- if (!this.channel.rollback) {
736
- throw new Error("Channel doesn't support rollback");
737
- }
738
- const innerContents = contents as FluidDataStoreMessage;
739
- this.channel.rollback(innerContents.type, innerContents.content, localOpMetadata);
740
- }
741
-
742
- public async applyStashedOp(contents: any): Promise<unknown> {
743
- if (!this.channel) {
744
- await this.realize();
745
- }
746
- assert(!!this.channel, 0x14c /* "Channel must exist when rebasing ops" */);
747
- const innerContents = contents as FluidDataStoreMessage;
748
- return this.channel.applyStashedOp(innerContents.content);
749
- }
750
-
751
- private verifyNotClosed() {
752
- if (this._disposed) {
753
- throw new Error("Context is closed");
754
- }
755
- }
756
-
757
- public getCreateChildSummarizerNodeFn(id: string, createParam: CreateChildSummarizerNodeParam) {
758
- return (
759
- summarizeInternal: SummarizeInternalFn,
760
- getGCDataFn: (fullGC?: boolean) => Promise<IGarbageCollectionData>,
761
- getBaseGCDetailsFn: () => Promise<IGarbageCollectionDetailsBase>,
762
- ) => this.summarizerNode.createChild(
763
- summarizeInternal,
764
- id,
765
- createParam,
766
- // DDS will not create failure summaries
767
- { throwOnFailure: true },
768
- getGCDataFn,
769
- getBaseGCDetailsFn,
770
- );
771
- }
772
-
773
- public async uploadBlob(blob: ArrayBufferLike): Promise<IFluidHandle<ArrayBufferLike>> {
774
- return this.containerRuntime.uploadBlob(blob);
775
- }
140
+ export abstract class FluidDataStoreContext
141
+ extends TypedEventEmitter<IFluidDataStoreContextEvents>
142
+ implements IFluidDataStoreContext, IDisposable
143
+ {
144
+ public get packagePath(): readonly string[] {
145
+ assert(this.pkg !== undefined, 0x139 /* "Undefined package path" */);
146
+ return this.pkg;
147
+ }
148
+
149
+ public get options(): ILoaderOptions {
150
+ return this._containerRuntime.options;
151
+ }
152
+
153
+ public get clientId(): string | undefined {
154
+ return this._containerRuntime.clientId;
155
+ }
156
+
157
+ public get clientDetails(): IClientDetails {
158
+ return this._containerRuntime.clientDetails;
159
+ }
160
+
161
+ public get logger(): ITelemetryLoggerExt {
162
+ return this._containerRuntime.logger;
163
+ }
164
+
165
+ public get deltaManager(): IDeltaManager<ISequencedDocumentMessage, IDocumentMessage> {
166
+ return this._containerRuntime.deltaManager;
167
+ }
168
+
169
+ public get connected(): boolean {
170
+ return this._containerRuntime.connected;
171
+ }
172
+
173
+ public get IFluidHandleContext() {
174
+ return this._containerRuntime.IFluidHandleContext;
175
+ }
176
+
177
+ public get containerRuntime(): IContainerRuntime {
178
+ return this._containerRuntime;
179
+ }
180
+
181
+ public ensureNoDataModelChanges<T>(callback: () => T): T {
182
+ return this._containerRuntime.ensureNoDataModelChanges(callback);
183
+ }
184
+
185
+ public get isLoaded(): boolean {
186
+ return this.loaded;
187
+ }
188
+
189
+ public get baseSnapshot(): ISnapshotTree | undefined {
190
+ return this._baseSnapshot;
191
+ }
192
+
193
+ public get idCompressor(): (IIdCompressorCore & IIdCompressor) | undefined {
194
+ return this._containerRuntime.idCompressor;
195
+ }
196
+
197
+ private _disposed = false;
198
+ public get disposed() {
199
+ return this._disposed;
200
+ }
201
+
202
+ /**
203
+ * Tombstone is a temporary feature that prevents a data store from sending / receiving ops, signals and from
204
+ * loading.
205
+ */
206
+ private _tombstoned = false;
207
+ public get tombstoned() {
208
+ return this._tombstoned;
209
+ }
210
+ /** If true, throw an error when a tombstone data store is used. */
211
+ private readonly throwOnTombstoneUsage: boolean;
212
+
213
+ /** If true, this means that this data store context and its children have been removed from the runtime */
214
+ private deleted: boolean = false;
215
+
216
+ public get attachState(): AttachState {
217
+ return this._attachState;
218
+ }
219
+
220
+ public get IFluidDataStoreRegistry(): IFluidDataStoreRegistry | undefined {
221
+ return this.registry;
222
+ }
223
+
224
+ /**
225
+ * A datastore is considered as root if it
226
+ * 1. is root in memory - see isInMemoryRoot
227
+ * 2. is root as part of the base snapshot that the datastore loaded from
228
+ * @returns whether a datastore is root
229
+ */
230
+ public async isRoot(): Promise<boolean> {
231
+ return this.isInMemoryRoot() || (await this.getInitialSnapshotDetails()).isRootDataStore;
232
+ }
233
+
234
+ /**
235
+ * There are 3 states where isInMemoryRoot needs to be true
236
+ * 1. when a datastore becomes aliased. This can happen for both remote and local datastores
237
+ * 2. when a datastore is created locally as root
238
+ * 3. when a datastore is created locally as root and is rehydrated
239
+ * @returns whether a datastore is root in memory
240
+ */
241
+ protected isInMemoryRoot(): boolean {
242
+ return this._isInMemoryRoot;
243
+ }
244
+
245
+ protected registry: IFluidDataStoreRegistry | undefined;
246
+
247
+ protected detachedRuntimeCreation = false;
248
+ protected channel: IFluidDataStoreChannel | undefined;
249
+ private loaded = false;
250
+ protected pending: ISequencedDocumentMessage[] | undefined = [];
251
+ protected channelDeferred: Deferred<IFluidDataStoreChannel> | undefined;
252
+ private _baseSnapshot: ISnapshotTree | undefined;
253
+ protected _attachState: AttachState;
254
+ private _isInMemoryRoot: boolean = false;
255
+ protected readonly summarizerNode: ISummarizerNodeWithGC;
256
+ protected readonly mc: MonitoringContext;
257
+ private readonly thresholdOpsCounter: ThresholdCounter;
258
+ private static readonly pendingOpsCountThreshold = 1000;
259
+
260
+ /**
261
+ * If the summarizer makes local changes, a telemetry event is logged. This has the potential to be very noisy.
262
+ * So, adding a count of how many telemetry events are logged per data store context. This can be
263
+ * controlled via feature flags.
264
+ */
265
+ private localChangesTelemetryCount: number;
266
+
267
+ // The used routes of this node as per the last GC run. This is used to update the used routes of the channel
268
+ // if it realizes after GC is run.
269
+ private lastUsedRoutes: string[] | undefined;
270
+
271
+ public readonly id: string;
272
+ private readonly _containerRuntime: ContainerRuntime;
273
+ public readonly storage: IDocumentStorageService;
274
+ public readonly scope: FluidObject;
275
+ protected pkg?: readonly string[];
276
+
277
+ constructor(
278
+ props: IFluidDataStoreContextProps,
279
+ private readonly existing: boolean,
280
+ public readonly isLocalDataStore: boolean,
281
+ private readonly makeLocallyVisibleFn: () => void,
282
+ ) {
283
+ super();
284
+
285
+ this._containerRuntime = props.runtime;
286
+ this.id = props.id;
287
+ this.storage = props.storage;
288
+ this.scope = props.scope;
289
+ this.pkg = props.pkg;
290
+
291
+ // URIs use slashes as delimiters. Handles use URIs.
292
+ // Thus having slashes in types almost guarantees trouble down the road!
293
+ assert(!this.id.includes("/"), 0x13a /* Data store ID contains slash */);
294
+
295
+ this._attachState =
296
+ this.containerRuntime.attachState !== AttachState.Detached && this.existing
297
+ ? this.containerRuntime.attachState
298
+ : AttachState.Detached;
299
+
300
+ const thisSummarizeInternal = async (
301
+ fullTree: boolean,
302
+ trackState: boolean,
303
+ telemetryContext?: ITelemetryContext,
304
+ ) => this.summarizeInternal(fullTree, trackState, telemetryContext);
305
+
306
+ this.summarizerNode = props.createSummarizerNodeFn(
307
+ thisSummarizeInternal,
308
+ async (fullGC?: boolean) => this.getGCDataInternal(fullGC),
309
+ );
310
+
311
+ this.mc = createChildMonitoringContext({
312
+ logger: this.logger,
313
+ namespace: "FluidDataStoreContext",
314
+ properties: {
315
+ all: tagCodeArtifacts({
316
+ fluidDataStoreId: this.id,
317
+ // The package name is a getter because `this.pkg` may not be initialized during construction.
318
+ // For data stores loaded from summary, it is initialized during data store realization.
319
+ fullPackageName: () => this.pkg?.join("/"),
320
+ }),
321
+ },
322
+ });
323
+ this.thresholdOpsCounter = new ThresholdCounter(
324
+ FluidDataStoreContext.pendingOpsCountThreshold,
325
+ this.mc.logger,
326
+ );
327
+
328
+ this.throwOnTombstoneUsage = this._containerRuntime.gcThrowOnTombstoneUsage;
329
+
330
+ // By default, a data store can log maximum 10 local changes telemetry in summarizer.
331
+ this.localChangesTelemetryCount =
332
+ this.mc.config.getNumber("Fluid.Telemetry.LocalChangesTelemetryCount") ?? 10;
333
+ }
334
+
335
+ public dispose(): void {
336
+ if (this._disposed) {
337
+ return;
338
+ }
339
+ this._disposed = true;
340
+
341
+ // Dispose any pending runtime after it gets fulfilled
342
+ // Errors are logged where this.channelDeferred is consumed/generated (realizeCore(), bindRuntime())
343
+ if (this.channelDeferred) {
344
+ this.channelDeferred.promise
345
+ .then((runtime) => {
346
+ runtime.dispose();
347
+ })
348
+ .catch((error) => {});
349
+ }
350
+ }
351
+
352
+ /**
353
+ * When delete is called, that means that the data store is permanently removed from the runtime, and will not show up in future summaries
354
+ * This function is called to prevent ops from being generated from this data store once it has been deleted. Furthermore, this data store
355
+ * should not receive any ops/signals.
356
+ */
357
+ public delete() {
358
+ this.deleted = true;
359
+ }
360
+
361
+ public setTombstone(tombstone: boolean) {
362
+ if (this.tombstoned === tombstone) {
363
+ return;
364
+ }
365
+
366
+ this._tombstoned = tombstone;
367
+ }
368
+
369
+ private rejectDeferredRealize(
370
+ reason: string,
371
+ failedPkgPath?: string,
372
+ fullPackageName?: readonly string[],
373
+ ): never {
374
+ throw new LoggingError(
375
+ reason,
376
+ tagCodeArtifacts({
377
+ failedPkgPath,
378
+ packagePath: fullPackageName?.join("/"),
379
+ }),
380
+ );
381
+ }
382
+
383
+ public async realize(): Promise<IFluidDataStoreChannel> {
384
+ assert(!this.detachedRuntimeCreation, 0x13d /* "Detached runtime creation on realize()" */);
385
+ if (!this.channelDeferred) {
386
+ this.channelDeferred = new Deferred<IFluidDataStoreChannel>();
387
+ this.realizeCore(this.existing).catch((error) => {
388
+ const errorWrapped = DataProcessingError.wrapIfUnrecognized(
389
+ error,
390
+ "realizeFluidDataStoreContext",
391
+ );
392
+ errorWrapped.addTelemetryProperties(
393
+ tagCodeArtifacts({
394
+ fullPackageName: this.pkg?.join("/"),
395
+ fluidDataStoreId: this.id,
396
+ }),
397
+ );
398
+ this.channelDeferred?.reject(errorWrapped);
399
+ this.mc.logger.sendErrorEvent({ eventName: "RealizeError" }, errorWrapped);
400
+ });
401
+ }
402
+ return this.channelDeferred.promise;
403
+ }
404
+
405
+ protected async factoryFromPackagePath(packages?: readonly string[]) {
406
+ assert(this.pkg === packages, 0x13e /* "Unexpected package path" */);
407
+ if (packages === undefined) {
408
+ this.rejectDeferredRealize("packages is undefined");
409
+ }
410
+
411
+ let entry: FluidDataStoreRegistryEntry | undefined;
412
+ let registry: IFluidDataStoreRegistry | undefined =
413
+ this._containerRuntime.IFluidDataStoreRegistry;
414
+ let lastPkg: string | undefined;
415
+ for (const pkg of packages) {
416
+ if (!registry) {
417
+ this.rejectDeferredRealize("No registry for package", lastPkg, packages);
418
+ }
419
+ lastPkg = pkg;
420
+ entry = await registry.get(pkg);
421
+ if (!entry) {
422
+ this.rejectDeferredRealize(
423
+ "Registry does not contain entry for the package",
424
+ pkg,
425
+ packages,
426
+ );
427
+ }
428
+ registry = entry.IFluidDataStoreRegistry;
429
+ }
430
+ const factory = entry?.IFluidDataStoreFactory;
431
+ if (factory === undefined) {
432
+ this.rejectDeferredRealize("Can't find factory for package", lastPkg, packages);
433
+ }
434
+
435
+ return { factory, registry };
436
+ }
437
+
438
+ private async realizeCore(existing: boolean): Promise<void> {
439
+ const details = await this.getInitialSnapshotDetails();
440
+ // Base snapshot is the baseline where pending ops are applied to.
441
+ // It is important that this be in sync with the pending ops, and also
442
+ // that it is set here, before bindRuntime is called.
443
+ this._baseSnapshot = details.snapshot;
444
+ const packages = details.pkg;
445
+
446
+ const { factory, registry } = await this.factoryFromPackagePath(packages);
447
+
448
+ assert(
449
+ this.registry === undefined,
450
+ 0x13f /* "datastore context registry is already set" */,
451
+ );
452
+ this.registry = registry;
453
+
454
+ const channel = await factory.instantiateDataStore(this, existing);
455
+ assert(channel !== undefined, 0x140 /* "undefined channel on datastore context" */);
456
+ this.bindRuntime(channel);
457
+ // This data store may have been disposed before the channel is created during realization. If so,
458
+ // dispose the channel now.
459
+ if (this.disposed) {
460
+ channel.dispose();
461
+ }
462
+ }
463
+
464
+ /**
465
+ * Notifies this object about changes in the connection state.
466
+ * @param value - New connection state.
467
+ * @param clientId - ID of the client. Its old ID when in disconnected state and
468
+ * its new client ID when we are connecting or connected.
469
+ */
470
+ public setConnectionState(connected: boolean, clientId?: string) {
471
+ // ConnectionState should not fail in tombstone mode as this is internally run
472
+ this.verifyNotClosed("setConnectionState", false /* checkTombstone */);
473
+
474
+ // Connection events are ignored if the store is not yet loaded
475
+ if (!this.loaded) {
476
+ return;
477
+ }
478
+
479
+ assert(this.connected === connected, 0x141 /* "Unexpected connected state" */);
480
+
481
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
482
+ this.channel!.setConnectionState(connected, clientId);
483
+ }
484
+
485
+ public process(
486
+ messageArg: ISequencedDocumentMessage,
487
+ local: boolean,
488
+ localOpMetadata: unknown,
489
+ ): void {
490
+ const safeTelemetryProps = extractSafePropertiesFromMessage(messageArg);
491
+ // On op process, tombstone error is logged in garbage collector. So, set "checkTombstone" to false when calling
492
+ // "verifyNotClosed" which logs tombstone errors. Throw error if tombstoned and throwing on load is configured.
493
+ this.verifyNotClosed("process", false /* checkTombstone */, safeTelemetryProps);
494
+ if (this.tombstoned && this.throwOnTombstoneUsage) {
495
+ throw new DataCorruptionError(
496
+ "Context is tombstoned! Call site [process]",
497
+ safeTelemetryProps,
498
+ );
499
+ }
500
+
501
+ const innerContents = messageArg.contents as FluidDataStoreMessage;
502
+ const message = {
503
+ ...messageArg,
504
+ type: innerContents.type,
505
+ contents: innerContents.content,
506
+ };
507
+
508
+ this.summarizerNode.recordChange(message);
509
+
510
+ if (this.loaded) {
511
+ return this.channel?.process(message, local, localOpMetadata);
512
+ } else {
513
+ assert(!local, 0x142 /* "local store channel is not loaded" */);
514
+ assert(this.pending !== undefined, 0x23d /* "pending is undefined" */);
515
+ this.pending.push(message);
516
+ this.thresholdOpsCounter.sendIfMultiple("StorePendingOps", this.pending.length);
517
+ }
518
+ }
519
+
520
+ public processSignal(message: IInboundSignalMessage, local: boolean): void {
521
+ this.verifyNotClosed("processSignal");
522
+
523
+ // Signals are ignored if the store is not yet loaded
524
+ if (!this.loaded) {
525
+ return;
526
+ }
527
+
528
+ this.channel?.processSignal(message, local);
529
+ }
530
+
531
+ public getQuorum(): IQuorumClients {
532
+ return this._containerRuntime.getQuorum();
533
+ }
534
+
535
+ public getAudience(): IAudience {
536
+ return this._containerRuntime.getAudience();
537
+ }
538
+
539
+ /**
540
+ * Returns a summary at the current sequence number.
541
+ * @param fullTree - true to bypass optimizations and force a full summary tree
542
+ * @param trackState - This tells whether we should track state from this summary.
543
+ * @param telemetryContext - summary data passed through the layers for telemetry purposes
544
+ */
545
+ public async summarize(
546
+ fullTree: boolean = false,
547
+ trackState: boolean = true,
548
+ telemetryContext?: ITelemetryContext,
549
+ ): Promise<ISummarizeResult> {
550
+ return this.summarizerNode.summarize(fullTree, trackState, telemetryContext);
551
+ }
552
+
553
+ private async summarizeInternal(
554
+ fullTree: boolean,
555
+ trackState: boolean,
556
+ telemetryContext?: ITelemetryContext,
557
+ ): Promise<ISummarizeInternalResult> {
558
+ await this.realize();
559
+
560
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
561
+ const summarizeResult = await this.channel!.summarize(
562
+ fullTree,
563
+ trackState,
564
+ telemetryContext,
565
+ );
566
+
567
+ // Wrap dds summaries in .channels subtree.
568
+ wrapSummaryInChannelsTree(summarizeResult);
569
+ const pathPartsForChildren = [channelsTreeName];
570
+
571
+ // Add data store's attributes to the summary.
572
+ const { pkg } = await this.getInitialSnapshotDetails();
573
+ const isRoot = await this.isRoot();
574
+ const attributes = createAttributes(pkg, isRoot);
575
+ addBlobToSummary(summarizeResult, dataStoreAttributesBlobName, JSON.stringify(attributes));
576
+
577
+ // If we are not referenced, mark the summary tree as unreferenced. Also, update unreferenced blob
578
+ // size in the summary stats with the blobs size of this data store.
579
+ if (!this.summarizerNode.isReferenced()) {
580
+ summarizeResult.summary.unreferenced = true;
581
+ summarizeResult.stats.unreferencedBlobSize = summarizeResult.stats.totalBlobSize;
582
+ }
583
+
584
+ return {
585
+ ...summarizeResult,
586
+ id: this.id,
587
+ pathPartsForChildren,
588
+ };
589
+ }
590
+
591
+ /**
592
+ * Returns the data used for garbage collection. This includes a list of GC nodes that represent this data store
593
+ * including any of its child channel contexts. Each node has a set of outbound routes to other GC nodes in the
594
+ * document.
595
+ * If there is no new data in this data store since the last summary, previous GC data is used.
596
+ * If there is new data, the GC data is generated again (by calling getGCDataInternal).
597
+ * @param fullGC - true to bypass optimizations and force full generation of GC data.
598
+ */
599
+ public async getGCData(fullGC: boolean = false): Promise<IGarbageCollectionData> {
600
+ return this.summarizerNode.getGCData(fullGC);
601
+ }
602
+
603
+ /**
604
+ * Generates data used for garbage collection. This is called when there is new data since last summary. It
605
+ * realizes the data store and calls into each channel context to get its GC data.
606
+ * @param fullGC - true to bypass optimizations and force full generation of GC data.
607
+ */
608
+ private async getGCDataInternal(fullGC: boolean = false): Promise<IGarbageCollectionData> {
609
+ await this.realize();
610
+ assert(
611
+ this.channel !== undefined,
612
+ 0x143 /* "Channel should not be undefined when running GC" */,
613
+ );
614
+
615
+ return this.channel.getGCData(fullGC);
616
+ }
617
+
618
+ /**
619
+ * After GC has run, called to notify the data store of routes used in it. These are used for the following:
620
+ *
621
+ * 1. To identify if this data store is being referenced in the document or not.
622
+ *
623
+ * 2. To determine if it needs to re-summarize in case used routes changed since last summary.
624
+ *
625
+ * 3. These are added to the summary generated by the data store.
626
+ *
627
+ * 4. To notify child contexts of their used routes. This is done immediately if the data store is loaded.
628
+ * Else, it is done when realizing the data store.
629
+ *
630
+ * 5. To update the timestamp when this data store or any children are marked as unreferenced.
631
+ *
632
+ * @param usedRoutes - The routes that are used in this data store.
633
+ */
634
+ public updateUsedRoutes(usedRoutes: string[]) {
635
+ // Update the used routes in this data store's summarizer node.
636
+ this.summarizerNode.updateUsedRoutes(usedRoutes);
637
+
638
+ /**
639
+ * Store the used routes to update the channel if the data store is not loaded yet. If the used routes changed
640
+ * since the previous run, the data store will be loaded during summarize since the used state changed. So, it's
641
+ * safe to only store the last used routes.
642
+ */
643
+ this.lastUsedRoutes = usedRoutes;
644
+
645
+ // If we are loaded, call the channel so it can update the used routes of the child contexts.
646
+ // If we are not loaded, we will update this when we are realized.
647
+ if (this.loaded) {
648
+ this.updateChannelUsedRoutes();
649
+ }
650
+ }
651
+
652
+ /**
653
+ * Called when a new outbound reference is added to another node. This is used by garbage collection to identify
654
+ * all references added in the system.
655
+ * @param srcHandle - The handle of the node that added the reference.
656
+ * @param outboundHandle - The handle of the outbound node that is referenced.
657
+ */
658
+ public addedGCOutboundReference(srcHandle: IFluidHandle, outboundHandle: IFluidHandle) {
659
+ this._containerRuntime.addedGCOutboundReference(srcHandle, outboundHandle);
660
+ }
661
+
662
+ /**
663
+ * Updates the used routes of the channel and its child contexts. The channel must be loaded before calling this.
664
+ * It is called in these two scenarios:
665
+ * 1. When the used routes of the data store is updated and the data store is loaded.
666
+ * 2. When the data store is realized. This updates the channel's used routes as per last GC run.
667
+ */
668
+ private updateChannelUsedRoutes() {
669
+ assert(this.loaded, 0x144 /* "Channel should be loaded when updating used routes" */);
670
+ assert(
671
+ this.channel !== undefined,
672
+ 0x145 /* "Channel should be present when data store is loaded" */,
673
+ );
674
+
675
+ // If there is no lastUsedRoutes, GC has not run up until this point.
676
+ if (this.lastUsedRoutes === undefined) {
677
+ return;
678
+ }
679
+
680
+ // Remove the route to this data store, if it exists.
681
+ const usedChannelRoutes = this.lastUsedRoutes.filter((id: string) => {
682
+ return id !== "/" && id !== "";
683
+ });
684
+ this.channel.updateUsedRoutes(usedChannelRoutes);
685
+ }
686
+
687
+ /**
688
+ * @deprecated 0.18.Should call request on the runtime directly
689
+ */
690
+ public async request(request: IRequest): Promise<IResponse> {
691
+ const runtime = await this.realize();
692
+ return runtime.request(request);
693
+ }
694
+
695
+ public submitMessage(type: string, content: any, localOpMetadata: unknown): void {
696
+ this.verifyNotClosed("submitMessage");
697
+ assert(!!this.channel, 0x146 /* "Channel must exist when submitting message" */);
698
+ const fluidDataStoreContent: FluidDataStoreMessage = {
699
+ content,
700
+ type,
701
+ };
702
+
703
+ // Summarizer clients should not submit messages.
704
+ this.identifyLocalChangeInSummarizer("DataStoreMessageSubmittedInSummarizer", type);
705
+
706
+ this._containerRuntime.submitDataStoreOp(this.id, fluidDataStoreContent, localOpMetadata);
707
+ }
708
+
709
+ /**
710
+ * This is called from a SharedSummaryBlock that does not generate ops but only wants to be part of the summary.
711
+ * It indicates that there is data in the object that needs to be summarized.
712
+ * We will update the latestSequenceNumber of the summary tracker of this
713
+ * store and of the object's channel.
714
+ *
715
+ * @param address - The address of the channel that is dirty.
716
+ *
717
+ */
718
+ public setChannelDirty(address: string): void {
719
+ this.verifyNotClosed("setChannelDirty");
720
+
721
+ // Get the latest sequence number.
722
+ const latestSequenceNumber = this.deltaManager.lastSequenceNumber;
723
+
724
+ this.summarizerNode.invalidate(latestSequenceNumber);
725
+
726
+ const channelSummarizerNode = this.summarizerNode.getChild(address);
727
+
728
+ if (channelSummarizerNode) {
729
+ channelSummarizerNode.invalidate(latestSequenceNumber); // TODO: lazy load problem?
730
+ }
731
+ }
732
+
733
+ /**
734
+ * Submits the signal to be sent to other clients.
735
+ * @param type - Type of the signal.
736
+ * @param content - Content of the signal.
737
+ * @param targetClientId - When specified, the signal is only sent to the provided client id.
738
+ */
739
+ public submitSignal(type: string, content: any, targetClientId?: string) {
740
+ this.verifyNotClosed("submitSignal");
741
+
742
+ assert(!!this.channel, 0x147 /* "Channel must exist on submitting signal" */);
743
+ return this._containerRuntime.submitDataStoreSignal(this.id, type, content, targetClientId);
744
+ }
745
+
746
+ /**
747
+ * This is called by the data store channel when it becomes locally visible indicating that it is ready to become
748
+ * globally visible now.
749
+ */
750
+ public makeLocallyVisible() {
751
+ assert(this.channel !== undefined, 0x2cf /* "undefined channel on datastore context" */);
752
+ assert(
753
+ this.channel.visibilityState === VisibilityState.LocallyVisible,
754
+ 0x590 /* Channel must be locally visible */,
755
+ );
756
+ this.makeLocallyVisibleFn();
757
+ }
758
+
759
+ protected bindRuntime(channel: IFluidDataStoreChannel) {
760
+ if (this.channel) {
761
+ throw new Error("Runtime already bound");
762
+ }
763
+
764
+ try {
765
+ assert(
766
+ !this.detachedRuntimeCreation,
767
+ 0x148 /* "Detached runtime creation on runtime bind" */,
768
+ );
769
+ assert(this.channelDeferred !== undefined, 0x149 /* "Undefined channel deferral" */);
770
+ assert(this.pkg !== undefined, 0x14a /* "Undefined package path" */);
771
+
772
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
773
+ const pending = this.pending!;
774
+
775
+ // Apply all pending ops
776
+ for (const op of pending) {
777
+ channel.process(op, false, undefined /* localOpMetadata */);
778
+ }
779
+
780
+ this.thresholdOpsCounter.send("ProcessPendingOps", pending.length);
781
+ this.pending = undefined;
782
+
783
+ // And now mark the runtime active
784
+ this.loaded = true;
785
+ this.channel = channel;
786
+
787
+ // Freeze the package path to ensure that someone doesn't modify it when it is
788
+ // returned in packagePath().
789
+ Object.freeze(this.pkg);
790
+
791
+ /**
792
+ * Update the used routes of the channel. If GC has run before this data store was realized, we will have
793
+ * the used routes saved. So, this will ensure that all the child contexts have up-to-date used routes as
794
+ * per the last time GC was run.
795
+ * Also, this data store may have been realized during summarize. In that case, the child contexts need to
796
+ * have their used routes updated to determine if its needs to summarize again and to add it to the summary.
797
+ */
798
+ this.updateChannelUsedRoutes();
799
+
800
+ // And notify the pending promise it is now available
801
+ this.channelDeferred.resolve(this.channel);
802
+ } catch (error) {
803
+ this.channelDeferred?.reject(error);
804
+ this.mc.logger.sendErrorEvent(
805
+ {
806
+ eventName: "BindRuntimeError",
807
+ },
808
+ error,
809
+ );
810
+ }
811
+ }
812
+
813
+ public async getAbsoluteUrl(relativeUrl: string): Promise<string | undefined> {
814
+ if (this.attachState !== AttachState.Attached) {
815
+ return undefined;
816
+ }
817
+ return this._containerRuntime.getAbsoluteUrl(relativeUrl);
818
+ }
819
+
820
+ public abstract generateAttachMessage(): IAttachMessage;
821
+
822
+ public abstract getInitialSnapshotDetails(): Promise<ISnapshotDetails>;
823
+
824
+ /**
825
+ * @deprecated Sets the datastore as root, for aliasing purposes: #7948
826
+ * This method should not be used outside of the aliasing context.
827
+ * It will be removed, as the source of truth for this flag will be the aliasing blob.
828
+ */
829
+ public setInMemoryRoot(): void {
830
+ this._isInMemoryRoot = true;
831
+ }
832
+
833
+ /**
834
+ * @deprecated The functionality to get base GC details has been moved to summarizer node.
835
+ */
836
+ public async getBaseGCDetails(): Promise<IGarbageCollectionDetailsBase> {
837
+ return {};
838
+ }
839
+
840
+ public reSubmit(contents: any, localOpMetadata: unknown) {
841
+ assert(!!this.channel, 0x14b /* "Channel must exist when resubmitting ops" */);
842
+ const innerContents = contents as FluidDataStoreMessage;
843
+ this.channel.reSubmit(innerContents.type, innerContents.content, localOpMetadata);
844
+ }
845
+
846
+ public rollback(contents: any, localOpMetadata: unknown) {
847
+ if (!this.channel) {
848
+ throw new Error("Channel must exist when rolling back ops");
849
+ }
850
+ if (!this.channel.rollback) {
851
+ throw new Error("Channel doesn't support rollback");
852
+ }
853
+ const innerContents = contents as FluidDataStoreMessage;
854
+ this.channel.rollback(innerContents.type, innerContents.content, localOpMetadata);
855
+ }
856
+
857
+ public async applyStashedOp(contents: any): Promise<unknown> {
858
+ if (!this.channel) {
859
+ await this.realize();
860
+ }
861
+ assert(!!this.channel, 0x14c /* "Channel must exist when rebasing ops" */);
862
+ return this.channel.applyStashedOp(contents);
863
+ }
864
+
865
+ private verifyNotClosed(
866
+ callSite: string,
867
+ checkTombstone = true,
868
+ safeTelemetryProps: ITelemetryProperties = {},
869
+ ) {
870
+ if (this.deleted) {
871
+ const messageString = `Context is deleted! Call site [${callSite}]`;
872
+ const error = new DataCorruptionError(messageString, safeTelemetryProps);
873
+ this.mc.logger.sendErrorEvent(
874
+ {
875
+ eventName: "GC_Deleted_DataStore_Changed",
876
+ callSite,
877
+ },
878
+ error,
879
+ );
880
+
881
+ throw error;
882
+ }
883
+
884
+ if (this._disposed) {
885
+ throw new Error(`Context is closed! Call site [${callSite}]`);
886
+ }
887
+
888
+ if (checkTombstone && this.tombstoned) {
889
+ const messageString = `Context is tombstoned! Call site [${callSite}]`;
890
+ const error = new DataCorruptionError(messageString, safeTelemetryProps);
891
+
892
+ sendGCUnexpectedUsageEvent(
893
+ this.mc,
894
+ {
895
+ eventName: "GC_Tombstone_DataStore_Changed",
896
+ category: this.throwOnTombstoneUsage ? "error" : "generic",
897
+ gcTombstoneEnforcementAllowed:
898
+ this._containerRuntime.gcTombstoneEnforcementAllowed,
899
+ callSite,
900
+ },
901
+ this.pkg,
902
+ error,
903
+ );
904
+ if (this.throwOnTombstoneUsage) {
905
+ throw error;
906
+ }
907
+ }
908
+ }
909
+
910
+ /**
911
+ * Summarizer client should not have local changes. These changes can become part of the summary and can break
912
+ * eventual consistency. For example, the next summary (say at ref seq# 100) may contain these changes whereas
913
+ * other clients that are up-to-date till seq# 100 may not have them yet.
914
+ */
915
+ protected identifyLocalChangeInSummarizer(eventName: string, type?: string) {
916
+ if (
917
+ this.clientDetails.type !== summarizerClientType ||
918
+ this.localChangesTelemetryCount <= 0
919
+ ) {
920
+ return;
921
+ }
922
+
923
+ // Log a telemetry if there are local changes in the summarizer. This will give us data on how often
924
+ // this is happening and which data stores do this. The eventual goal is to disallow local changes
925
+ // in the summarizer and the data will help us plan this.
926
+ this.mc.logger.sendTelemetryEvent({
927
+ eventName,
928
+ type,
929
+ isSummaryInProgress: this.summarizerNode.isSummaryInProgress?.(),
930
+ stack: generateStack(),
931
+ });
932
+ this.localChangesTelemetryCount--;
933
+ }
934
+
935
+ public getCreateChildSummarizerNodeFn(id: string, createParam: CreateChildSummarizerNodeParam) {
936
+ return (
937
+ summarizeInternal: SummarizeInternalFn,
938
+ getGCDataFn: (fullGC?: boolean) => Promise<IGarbageCollectionData>,
939
+ ) =>
940
+ this.summarizerNode.createChild(
941
+ summarizeInternal,
942
+ id,
943
+ createParam,
944
+ // DDS will not create failure summaries
945
+ { throwOnFailure: true },
946
+ getGCDataFn,
947
+ );
948
+ }
949
+
950
+ public async uploadBlob(
951
+ blob: ArrayBufferLike,
952
+ signal?: AbortSignal,
953
+ ): Promise<IFluidHandle<ArrayBufferLike>> {
954
+ return this.containerRuntime.uploadBlob(blob, signal);
955
+ }
776
956
  }
777
957
 
778
958
  export class RemoteFluidDataStoreContext extends FluidDataStoreContext {
779
- private readonly initSnapshotValue: ISnapshotTree | string | undefined;
780
- private readonly baseGCDetailsP: Promise<IGarbageCollectionDetailsBase>;
781
-
782
- constructor(props: IRemoteFluidDataStoreContextProps) {
783
- super(
784
- props,
785
- true /* existing */,
786
- BindState.Bound,
787
- false /* isLocalDataStore */,
788
- () => {
789
- throw new Error("Already attached");
790
- },
791
- );
792
-
793
- this.initSnapshotValue = props.snapshotTree;
794
- this.baseGCDetailsP = new LazyPromise<IGarbageCollectionDetailsBase>(async () => {
795
- return (await props.getBaseGCDetails()) ?? {};
796
- });
797
- }
798
-
799
- private readonly initialSnapshotDetailsP = new LazyPromise<ISnapshotDetails>(async () => {
800
- let tree: ISnapshotTree | undefined;
801
- let isRootDataStore = true;
802
-
803
- if (typeof this.initSnapshotValue === "string") {
804
- const commit = (await this.storage.getVersions(this.initSnapshotValue, 1))[0];
805
- tree = await this.storage.getSnapshotTree(commit) ?? undefined;
806
- } else {
807
- tree = this.initSnapshotValue;
808
- }
809
-
810
- const localReadAndParse = async <T>(id: string) => readAndParse<T>(this.storage, id);
811
- if (tree) {
812
- const loadedSummary = await this.summarizerNode.loadBaseSummary(tree, localReadAndParse);
813
- tree = loadedSummary.baseSummary;
814
- // Prepend outstanding ops to pending queue of ops to process.
815
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
816
- this.pending = loadedSummary.outstandingOps.concat(this.pending!);
817
- }
818
-
819
- if (!!tree && tree.blobs[dataStoreAttributesBlobName] !== undefined) {
820
- // Need to get through snapshot and use that to populate extraBlobs
821
- const attributes =
822
- await localReadAndParse<ReadFluidDataStoreAttributes>(tree.blobs[dataStoreAttributesBlobName]);
823
-
824
- let pkgFromSnapshot: string[];
825
- // Use the snapshotFormatVersion to determine how the pkg is encoded in the snapshot.
826
- // For snapshotFormatVersion = "0.1" (1) or above, pkg is jsonified, otherwise it is just a string.
827
- const formatVersion = getAttributesFormatVersion(attributes);
828
- if (formatVersion < 1) {
829
- if (attributes.pkg.startsWith("[\"") && attributes.pkg.endsWith("\"]")) {
830
- pkgFromSnapshot = JSON.parse(attributes.pkg) as string[];
831
- } else {
832
- pkgFromSnapshot = [attributes.pkg];
833
- }
834
- } else {
835
- pkgFromSnapshot = JSON.parse(attributes.pkg) as string[];
836
- }
837
- this.pkg = pkgFromSnapshot;
838
-
839
- /**
840
- * If there is no isRootDataStore in the attributes blob, set it to true. This will ensure that
841
- * data stores in older documents are not garbage collected incorrectly. This may lead to additional
842
- * roots in the document but they won't break.
843
- */
844
- isRootDataStore = attributes.isRootDataStore ?? true;
845
-
846
- if (hasIsolatedChannels(attributes)) {
847
- tree = tree.trees[channelsTreeName];
848
- assert(tree !== undefined,
849
- 0x1fe /* "isolated channels subtree should exist in remote datastore snapshot" */);
850
- }
851
- }
852
-
853
- return {
854
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
855
- pkg: this.pkg!,
856
- isRootDataStore,
857
- snapshot: tree,
858
- };
859
- });
860
-
861
- public async getInitialSnapshotDetails(): Promise<ISnapshotDetails> {
862
- return this.initialSnapshotDetailsP;
863
- }
864
-
865
- /**
866
- * @deprecated - Renamed to getBaseGCDetails.
867
- */
868
- public async getInitialGCSummaryDetails(): Promise<IGarbageCollectionSummaryDetails> {
869
- return this.getBaseGCDetails();
870
- }
871
-
872
- public async getBaseGCDetails(): Promise<IGarbageCollectionDetailsBase> {
873
- return this.baseGCDetailsP;
874
- }
875
-
876
- public generateAttachMessage(): IAttachMessage {
877
- throw new Error("Cannot attach remote store");
878
- }
959
+ private readonly initSnapshotValue: ISnapshotTree | undefined;
960
+
961
+ constructor(props: IRemoteFluidDataStoreContextProps) {
962
+ super(props, true /* existing */, false /* isLocalDataStore */, () => {
963
+ throw new Error("Already attached");
964
+ });
965
+
966
+ this.initSnapshotValue = props.snapshotTree;
967
+
968
+ if (props.snapshotTree !== undefined) {
969
+ this.summarizerNode.updateBaseSummaryState(props.snapshotTree);
970
+ }
971
+ }
972
+
973
+ private readonly initialSnapshotDetailsP = new LazyPromise<ISnapshotDetails>(async () => {
974
+ let tree = this.initSnapshotValue;
975
+ let isRootDataStore = true;
976
+
977
+ if (!!tree && tree.blobs[dataStoreAttributesBlobName] !== undefined) {
978
+ // Need to get through snapshot and use that to populate extraBlobs
979
+ const attributes = await readAndParse<ReadFluidDataStoreAttributes>(
980
+ this.storage,
981
+ tree.blobs[dataStoreAttributesBlobName],
982
+ );
983
+
984
+ let pkgFromSnapshot: string[];
985
+ // Use the snapshotFormatVersion to determine how the pkg is encoded in the snapshot.
986
+ // For snapshotFormatVersion = "0.1" (1) or above, pkg is jsonified, otherwise it is just a string.
987
+ const formatVersion = getAttributesFormatVersion(attributes);
988
+ if (formatVersion < 1) {
989
+ pkgFromSnapshot =
990
+ attributes.pkg.startsWith('["') && attributes.pkg.endsWith('"]')
991
+ ? (JSON.parse(attributes.pkg) as string[])
992
+ : [attributes.pkg];
993
+ } else {
994
+ pkgFromSnapshot = JSON.parse(attributes.pkg) as string[];
995
+ }
996
+ this.pkg = pkgFromSnapshot;
997
+
998
+ /**
999
+ * If there is no isRootDataStore in the attributes blob, set it to true. This will ensure that
1000
+ * data stores in older documents are not garbage collected incorrectly. This may lead to additional
1001
+ * roots in the document but they won't break.
1002
+ */
1003
+ isRootDataStore = attributes.isRootDataStore ?? true;
1004
+
1005
+ if (hasIsolatedChannels(attributes)) {
1006
+ tree = tree.trees[channelsTreeName];
1007
+ assert(
1008
+ tree !== undefined,
1009
+ 0x1fe /* "isolated channels subtree should exist in remote datastore snapshot" */,
1010
+ );
1011
+ }
1012
+ }
1013
+
1014
+ return {
1015
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
1016
+ pkg: this.pkg!,
1017
+ isRootDataStore,
1018
+ snapshot: tree,
1019
+ };
1020
+ });
1021
+
1022
+ public async getInitialSnapshotDetails(): Promise<ISnapshotDetails> {
1023
+ return this.initialSnapshotDetailsP;
1024
+ }
1025
+
1026
+ public generateAttachMessage(): IAttachMessage {
1027
+ throw new Error("Cannot attach remote store");
1028
+ }
879
1029
  }
880
1030
 
881
1031
  /**
882
1032
  * Base class for detached & attached context classes
883
1033
  */
884
1034
  export class LocalFluidDataStoreContextBase extends FluidDataStoreContext {
885
- private readonly snapshotTree: ISnapshotTree | undefined;
886
- /**
887
- * @deprecated 0.16 Issue #1635, #3631
888
- */
889
- public readonly createProps?: any;
890
-
891
- constructor(props: ILocalFluidDataStoreContextProps) {
892
- super(
893
- props,
894
- props.snapshotTree !== undefined ? true : false /* existing */,
895
- props.snapshotTree ? BindState.Bound : BindState.NotBound,
896
- true /* isLocalDataStore */,
897
- props.makeLocallyVisibleFn,
898
- );
899
-
900
- this.snapshotTree = props.snapshotTree;
901
- if (props.isRootDataStore === true) {
902
- this.setInMemoryRoot();
903
- }
904
- this.createProps = props.createProps;
905
- this.attachListeners();
906
- }
907
-
908
- private attachListeners(): void {
909
- this.once("attaching", () => {
910
- assert(this.attachState === AttachState.Detached, 0x14d /* "Should move from detached to attaching" */);
911
- this._attachState = AttachState.Attaching;
912
- });
913
- this.once("attached", () => {
914
- assert(this.attachState === AttachState.Attaching, 0x14e /* "Should move from attaching to attached" */);
915
- this._attachState = AttachState.Attached;
916
- });
917
- }
918
-
919
- public generateAttachMessage(): IAttachMessage {
920
- assert(this.channel !== undefined, 0x14f /* "There should be a channel when generating attach message" */);
921
- assert(this.pkg !== undefined, 0x150 /* "pkg should be available in local data store context" */);
922
-
923
- const summarizeResult = this.channel.getAttachSummary();
924
-
925
- if (!this.disableIsolatedChannels) {
926
- // Wrap dds summaries in .channels subtree.
927
- wrapSummaryInChannelsTree(summarizeResult);
928
- }
929
-
930
- // Add data store's attributes to the summary.
931
- const attributes = createAttributes(
932
- this.pkg,
933
- this.isInMemoryRoot(),
934
- this.disableIsolatedChannels,
935
- );
936
- addBlobToSummary(summarizeResult, dataStoreAttributesBlobName, JSON.stringify(attributes));
937
-
938
- // Attach message needs the summary in ITree format. Convert the ISummaryTree into an ITree.
939
- const snapshot = convertSummaryTreeToITree(summarizeResult.summary);
940
-
941
- const message: IAttachMessage = {
942
- id: this.id,
943
- snapshot,
944
- type: this.pkg[this.pkg.length - 1],
945
- };
946
-
947
- return message;
948
- }
949
-
950
- public async getInitialSnapshotDetails(): Promise<ISnapshotDetails> {
951
- let snapshot = this.snapshotTree;
952
- let attributes: ReadFluidDataStoreAttributes;
953
- let isRootDataStore = false;
954
- if (snapshot !== undefined) {
955
- // Get the dataStore attributes.
956
- // Note: storage can be undefined in special case while detached.
957
- attributes = await getFluidDataStoreAttributes(this.storage, snapshot);
958
- if (hasIsolatedChannels(attributes)) {
959
- snapshot = snapshot.trees[channelsTreeName];
960
- assert(snapshot !== undefined,
961
- 0x1ff /* "isolated channels subtree should exist in local datastore snapshot" */);
962
- }
963
- if (this.pkg === undefined) {
964
- this.pkg = JSON.parse(attributes.pkg) as string[];
965
- // If there is no isRootDataStore in the attributes blob, set it to true. This ensures that data
966
- // stores in older documents are not garbage collected incorrectly. This may lead to additional
967
- // roots in the document but they won't break.
968
- if (attributes.isRootDataStore ?? true) {
969
- isRootDataStore = true;
970
- this.setInMemoryRoot();
971
- }
972
- }
973
- }
974
- assert(this.pkg !== undefined, 0x152 /* "pkg should be available in local data store" */);
975
-
976
- return {
977
- pkg: this.pkg,
978
- isRootDataStore,
979
- snapshot,
980
- };
981
- }
982
-
983
- /**
984
- * @deprecated - Renamed to getBaseGCDetails.
985
- */
986
- public async getInitialGCSummaryDetails(): Promise<IGarbageCollectionSummaryDetails> {
987
- // Local data store does not have initial summary.
988
- return {};
989
- }
990
-
991
- public async getBaseGCDetails(): Promise<IGarbageCollectionDetailsBase> {
992
- // Local data store does not have initial summary.
993
- return {};
994
- }
1035
+ private readonly snapshotTree: ISnapshotTree | undefined;
1036
+ /**
1037
+ * @deprecated 0.16 Issue #1635, #3631
1038
+ */
1039
+ public readonly createProps?: any;
1040
+
1041
+ constructor(props: ILocalFluidDataStoreContextProps) {
1042
+ super(
1043
+ props,
1044
+ props.snapshotTree !== undefined ? true : false /* existing */,
1045
+ true /* isLocalDataStore */,
1046
+ props.makeLocallyVisibleFn,
1047
+ );
1048
+
1049
+ // Summarizer client should not create local data stores.
1050
+ this.identifyLocalChangeInSummarizer("DataStoreCreatedInSummarizer");
1051
+
1052
+ this.snapshotTree = props.snapshotTree;
1053
+ if (props.isRootDataStore === true) {
1054
+ this.setInMemoryRoot();
1055
+ }
1056
+ this.createProps = props.createProps;
1057
+ this.attachListeners();
1058
+ }
1059
+
1060
+ private attachListeners(): void {
1061
+ this.once("attaching", () => {
1062
+ assert(
1063
+ this.attachState === AttachState.Detached,
1064
+ 0x14d /* "Should move from detached to attaching" */,
1065
+ );
1066
+ this._attachState = AttachState.Attaching;
1067
+ });
1068
+ this.once("attached", () => {
1069
+ assert(
1070
+ this.attachState === AttachState.Attaching,
1071
+ 0x14e /* "Should move from attaching to attached" */,
1072
+ );
1073
+ this._attachState = AttachState.Attached;
1074
+ });
1075
+ }
1076
+
1077
+ public generateAttachMessage(): IAttachMessage {
1078
+ assert(
1079
+ this.channel !== undefined,
1080
+ 0x14f /* "There should be a channel when generating attach message" */,
1081
+ );
1082
+ assert(
1083
+ this.pkg !== undefined,
1084
+ 0x150 /* "pkg should be available in local data store context" */,
1085
+ );
1086
+
1087
+ const summarizeResult = this.channel.getAttachSummary();
1088
+
1089
+ // Wrap dds summaries in .channels subtree.
1090
+ wrapSummaryInChannelsTree(summarizeResult);
1091
+
1092
+ // Add data store's attributes to the summary.
1093
+ const attributes = createAttributes(this.pkg, this.isInMemoryRoot());
1094
+ addBlobToSummary(summarizeResult, dataStoreAttributesBlobName, JSON.stringify(attributes));
1095
+
1096
+ // Attach message needs the summary in ITree format. Convert the ISummaryTree into an ITree.
1097
+ const snapshot = convertSummaryTreeToITree(summarizeResult.summary);
1098
+
1099
+ const message: IAttachMessage = {
1100
+ id: this.id,
1101
+ snapshot,
1102
+ type: this.pkg[this.pkg.length - 1],
1103
+ };
1104
+
1105
+ return message;
1106
+ }
1107
+
1108
+ private readonly initialSnapshotDetailsP = new LazyPromise<ISnapshotDetails>(async () => {
1109
+ let snapshot = this.snapshotTree;
1110
+ let attributes: ReadFluidDataStoreAttributes;
1111
+ let isRootDataStore = false;
1112
+ if (snapshot !== undefined) {
1113
+ // Get the dataStore attributes.
1114
+ // Note: storage can be undefined in special case while detached.
1115
+ attributes = await getFluidDataStoreAttributes(this.storage, snapshot);
1116
+ if (hasIsolatedChannels(attributes)) {
1117
+ snapshot = snapshot.trees[channelsTreeName];
1118
+ assert(
1119
+ snapshot !== undefined,
1120
+ 0x1ff /* "isolated channels subtree should exist in local datastore snapshot" */,
1121
+ );
1122
+ }
1123
+ if (this.pkg === undefined) {
1124
+ this.pkg = JSON.parse(attributes.pkg) as string[];
1125
+ // If there is no isRootDataStore in the attributes blob, set it to true. This ensures that data
1126
+ // stores in older documents are not garbage collected incorrectly. This may lead to additional
1127
+ // roots in the document but they won't break.
1128
+ if (attributes.isRootDataStore ?? true) {
1129
+ isRootDataStore = true;
1130
+ this.setInMemoryRoot();
1131
+ }
1132
+ }
1133
+ }
1134
+ assert(this.pkg !== undefined, 0x152 /* "pkg should be available in local data store" */);
1135
+
1136
+ return {
1137
+ pkg: this.pkg,
1138
+ isRootDataStore,
1139
+ snapshot,
1140
+ };
1141
+ });
1142
+
1143
+ public async getInitialSnapshotDetails(): Promise<ISnapshotDetails> {
1144
+ return this.initialSnapshotDetailsP;
1145
+ }
1146
+
1147
+ /**
1148
+ * A context should only be marked as deleted when its a remote context.
1149
+ * Session Expiry at the runtime level should have closed the container creating the local data store context
1150
+ * before delete is even possible. Session Expiry is at 30 days, and sweep is done 36+ days later from the time
1151
+ * it was unreferenced. Thus the sweeping container should have loaded from a snapshot and thus creating a remote
1152
+ * context.
1153
+ */
1154
+ public delete() {
1155
+ // TODO: GC:Validation - potentially prevent this from happening or asserting. Maybe throw here.
1156
+ sendGCUnexpectedUsageEvent(
1157
+ this.mc,
1158
+ {
1159
+ eventName: "GC_Deleted_DataStore_Unexpected_Delete",
1160
+ message: "Unexpected deletion of a local data store context",
1161
+ category: "error",
1162
+ gcTombstoneEnforcementAllowed: undefined,
1163
+ },
1164
+ this.pkg,
1165
+ );
1166
+ super.delete();
1167
+ }
995
1168
  }
996
1169
 
997
1170
  /**
@@ -1001,9 +1174,9 @@ export class LocalFluidDataStoreContextBase extends FluidDataStoreContext {
1001
1174
  * Runtime is created using data store factory that is associated with this context.
1002
1175
  */
1003
1176
  export class LocalFluidDataStoreContext extends LocalFluidDataStoreContextBase {
1004
- constructor(props: ILocalFluidDataStoreContextProps) {
1005
- super(props);
1006
- }
1177
+ constructor(props: ILocalFluidDataStoreContextProps) {
1178
+ super(props);
1179
+ }
1007
1180
  }
1008
1181
 
1009
1182
  /**
@@ -1013,47 +1186,54 @@ export class LocalFluidDataStoreContext extends LocalFluidDataStoreContextBase {
1013
1186
  * Attachment process provides all missing parts - package path, data store runtime, and data store factory
1014
1187
  */
1015
1188
  export class LocalDetachedFluidDataStoreContext
1016
- extends LocalFluidDataStoreContextBase
1017
- implements IFluidDataStoreContextDetached {
1018
- constructor(props: ILocalFluidDataStoreContextProps) {
1019
- super(props);
1020
- this.detachedRuntimeCreation = true;
1021
- }
1022
-
1023
- public async attachRuntime(
1024
- registry: IProvideFluidDataStoreFactory,
1025
- dataStoreChannel: IFluidDataStoreChannel) {
1026
- assert(this.detachedRuntimeCreation, 0x154 /* "runtime creation is already attached" */);
1027
- assert(this.channelDeferred === undefined, 0x155 /* "channel deferral is already set" */);
1028
-
1029
- const factory = registry.IFluidDataStoreFactory;
1030
-
1031
- const entry = await this.factoryFromPackagePath(this.pkg);
1032
- assert(entry.factory === factory, 0x156 /* "Unexpected factory for package path" */);
1033
-
1034
- assert(this.registry === undefined, 0x157 /* "datastore registry already attached" */);
1035
- this.registry = entry.registry;
1036
-
1037
- this.detachedRuntimeCreation = false;
1038
- this.channelDeferred = new Deferred<IFluidDataStoreChannel>();
1039
-
1040
- super.bindRuntime(dataStoreChannel);
1041
-
1042
- if (await this.isRoot()) {
1043
- // back-compat 0.59.1000 - makeVisibleAndAttachGraph was added in this version to IFluidDataStoreChannel.
1044
- // For older versions, we still have to call bindToContext.
1045
- if (dataStoreChannel.makeVisibleAndAttachGraph !== undefined) {
1046
- dataStoreChannel.makeVisibleAndAttachGraph();
1047
- } else {
1048
- dataStoreChannel.bindToContext();
1049
- }
1050
- }
1051
- }
1052
-
1053
- public async getInitialSnapshotDetails(): Promise<ISnapshotDetails> {
1054
- if (this.detachedRuntimeCreation) {
1055
- throw new Error("Detached Fluid Data Store context can't be realized! Please attach runtime first!");
1056
- }
1057
- return super.getInitialSnapshotDetails();
1058
- }
1189
+ extends LocalFluidDataStoreContextBase
1190
+ implements IFluidDataStoreContextDetached
1191
+ {
1192
+ constructor(props: ILocalFluidDataStoreContextProps) {
1193
+ super(props);
1194
+ this.detachedRuntimeCreation = true;
1195
+ }
1196
+
1197
+ public async attachRuntime(
1198
+ registry: IProvideFluidDataStoreFactory,
1199
+ dataStoreChannel: IFluidDataStoreChannel,
1200
+ ) {
1201
+ assert(this.detachedRuntimeCreation, 0x154 /* "runtime creation is already attached" */);
1202
+ this.detachedRuntimeCreation = false;
1203
+
1204
+ assert(this.channelDeferred === undefined, 0x155 /* "channel deferral is already set" */);
1205
+ this.channelDeferred = new Deferred<IFluidDataStoreChannel>();
1206
+
1207
+ const factory = registry.IFluidDataStoreFactory;
1208
+
1209
+ const entry = await this.factoryFromPackagePath(this.pkg);
1210
+ assert(entry.factory === factory, 0x156 /* "Unexpected factory for package path" */);
1211
+
1212
+ assert(this.registry === undefined, 0x157 /* "datastore registry already attached" */);
1213
+ this.registry = entry.registry;
1214
+
1215
+ super.bindRuntime(dataStoreChannel);
1216
+
1217
+ // Load the handle to the data store's entryPoint to make sure that for a detached data store, the entryPoint
1218
+ // initialization function is called before the data store gets attached and potentially connected to the
1219
+ // delta stream, so it gets a chance to do things while the data store is still "purely local".
1220
+ // This preserves the behavior from before we introduced entryPoints, where the instantiateDataStore method
1221
+ // of data store factories tends to construct the data object (at least kick off an async method that returns
1222
+ // it); that code moved to the entryPoint initialization function, so we want to ensure it still executes
1223
+ // before the data store is attached.
1224
+ await dataStoreChannel.entryPoint.get();
1225
+
1226
+ if (await this.isRoot()) {
1227
+ dataStoreChannel.makeVisibleAndAttachGraph();
1228
+ }
1229
+ }
1230
+
1231
+ public async getInitialSnapshotDetails(): Promise<ISnapshotDetails> {
1232
+ if (this.detachedRuntimeCreation) {
1233
+ throw new Error(
1234
+ "Detached Fluid Data Store context can't be realized! Please attach runtime first!",
1235
+ );
1236
+ }
1237
+ return super.getInitialSnapshotDetails();
1238
+ }
1059
1239
  }