@fluidframework/container-runtime 2.0.0-rc.2.0.1 → 2.0.0-rc.3.0.0

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 (554) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/api-report/container-runtime.api.md +471 -52
  3. package/dist/batchTracker.d.ts +1 -1
  4. package/dist/batchTracker.d.ts.map +1 -1
  5. package/dist/batchTracker.js +4 -4
  6. package/dist/batchTracker.js.map +1 -1
  7. package/dist/blobManager.d.ts +33 -30
  8. package/dist/blobManager.d.ts.map +1 -1
  9. package/dist/blobManager.js +82 -107
  10. package/dist/blobManager.js.map +1 -1
  11. package/dist/channelCollection.d.ts +27 -22
  12. package/dist/channelCollection.d.ts.map +1 -1
  13. package/dist/channelCollection.js +189 -165
  14. package/dist/channelCollection.js.map +1 -1
  15. package/dist/connectionTelemetry.d.ts +3 -3
  16. package/dist/connectionTelemetry.d.ts.map +1 -1
  17. package/dist/connectionTelemetry.js +17 -17
  18. package/dist/connectionTelemetry.js.map +1 -1
  19. package/dist/containerHandleContext.d.ts.map +1 -1
  20. package/dist/containerHandleContext.js +2 -2
  21. package/dist/containerHandleContext.js.map +1 -1
  22. package/dist/containerRuntime.d.ts +42 -39
  23. package/dist/containerRuntime.d.ts.map +1 -1
  24. package/dist/containerRuntime.js +425 -292
  25. package/dist/containerRuntime.js.map +1 -1
  26. package/dist/dataStore.d.ts +1 -1
  27. package/dist/dataStore.d.ts.map +1 -1
  28. package/dist/dataStore.js +8 -8
  29. package/dist/dataStore.js.map +1 -1
  30. package/dist/dataStoreContext.d.ts +58 -19
  31. package/dist/dataStoreContext.d.ts.map +1 -1
  32. package/dist/dataStoreContext.js +171 -114
  33. package/dist/dataStoreContext.js.map +1 -1
  34. package/dist/dataStoreContexts.d.ts +1 -0
  35. package/dist/dataStoreContexts.d.ts.map +1 -1
  36. package/dist/dataStoreContexts.js +12 -11
  37. package/dist/dataStoreContexts.js.map +1 -1
  38. package/dist/dataStoreRegistry.d.ts +5 -1
  39. package/dist/dataStoreRegistry.d.ts.map +1 -1
  40. package/dist/dataStoreRegistry.js +4 -4
  41. package/dist/dataStoreRegistry.js.map +1 -1
  42. package/dist/deltaManagerSummarizerProxy.d.ts +1 -1
  43. package/dist/deltaManagerSummarizerProxy.d.ts.map +1 -1
  44. package/dist/deltaManagerSummarizerProxy.js.map +1 -1
  45. package/dist/deltaScheduler.d.ts +1 -1
  46. package/dist/deltaScheduler.d.ts.map +1 -1
  47. package/dist/deltaScheduler.js +6 -6
  48. package/dist/deltaScheduler.js.map +1 -1
  49. package/dist/error.d.ts +1 -1
  50. package/dist/error.d.ts.map +1 -1
  51. package/dist/error.js +4 -4
  52. package/dist/error.js.map +1 -1
  53. package/dist/gc/garbageCollection.d.ts +3 -2
  54. package/dist/gc/garbageCollection.d.ts.map +1 -1
  55. package/dist/gc/garbageCollection.js +23 -23
  56. package/dist/gc/garbageCollection.js.map +1 -1
  57. package/dist/gc/gcConfigs.d.ts +2 -2
  58. package/dist/gc/gcConfigs.d.ts.map +1 -1
  59. package/dist/gc/gcConfigs.js +4 -5
  60. package/dist/gc/gcConfigs.js.map +1 -1
  61. package/dist/gc/gcDefinitions.d.ts +4 -5
  62. package/dist/gc/gcDefinitions.d.ts.map +1 -1
  63. package/dist/gc/gcDefinitions.js.map +1 -1
  64. package/dist/gc/gcHelpers.d.ts +5 -1
  65. package/dist/gc/gcHelpers.d.ts.map +1 -1
  66. package/dist/gc/gcHelpers.js +21 -12
  67. package/dist/gc/gcHelpers.js.map +1 -1
  68. package/dist/gc/gcSummaryStateTracker.d.ts +2 -2
  69. package/dist/gc/gcSummaryStateTracker.d.ts.map +1 -1
  70. package/dist/gc/gcSummaryStateTracker.js +11 -11
  71. package/dist/gc/gcSummaryStateTracker.js.map +1 -1
  72. package/dist/gc/gcTelemetry.d.ts +2 -1
  73. package/dist/gc/gcTelemetry.d.ts.map +1 -1
  74. package/dist/gc/gcTelemetry.js +11 -9
  75. package/dist/gc/gcTelemetry.js.map +1 -1
  76. package/dist/gc/gcUnreferencedStateTracker.d.ts.map +1 -1
  77. package/dist/gc/gcUnreferencedStateTracker.js +6 -6
  78. package/dist/gc/gcUnreferencedStateTracker.js.map +1 -1
  79. package/dist/gc/index.d.ts +1 -1
  80. package/dist/gc/index.d.ts.map +1 -1
  81. package/dist/gc/index.js +2 -1
  82. package/dist/gc/index.js.map +1 -1
  83. package/dist/index.d.ts +5 -2
  84. package/dist/index.d.ts.map +1 -1
  85. package/dist/index.js +12 -2
  86. package/dist/index.js.map +1 -1
  87. package/dist/legacy.d.ts +91 -0
  88. package/dist/messageTypes.d.ts +11 -5
  89. package/dist/messageTypes.d.ts.map +1 -1
  90. package/dist/messageTypes.js +4 -0
  91. package/dist/messageTypes.js.map +1 -1
  92. package/dist/opLifecycle/batchManager.d.ts.map +1 -1
  93. package/dist/opLifecycle/batchManager.js.map +1 -1
  94. package/dist/opLifecycle/definitions.d.ts +2 -20
  95. package/dist/opLifecycle/definitions.d.ts.map +1 -1
  96. package/dist/opLifecycle/definitions.js.map +1 -1
  97. package/dist/opLifecycle/index.d.ts +3 -3
  98. package/dist/opLifecycle/index.d.ts.map +1 -1
  99. package/dist/opLifecycle/index.js +3 -1
  100. package/dist/opLifecycle/index.js.map +1 -1
  101. package/dist/opLifecycle/opCompressor.d.ts.map +1 -1
  102. package/dist/opLifecycle/opCompressor.js +5 -6
  103. package/dist/opLifecycle/opCompressor.js.map +1 -1
  104. package/dist/opLifecycle/opDecompressor.d.ts +15 -4
  105. package/dist/opLifecycle/opDecompressor.d.ts.map +1 -1
  106. package/dist/opLifecycle/opDecompressor.js +62 -63
  107. package/dist/opLifecycle/opDecompressor.js.map +1 -1
  108. package/dist/opLifecycle/opGroupingManager.d.ts +2 -1
  109. package/dist/opLifecycle/opGroupingManager.d.ts.map +1 -1
  110. package/dist/opLifecycle/opGroupingManager.js +14 -16
  111. package/dist/opLifecycle/opGroupingManager.js.map +1 -1
  112. package/dist/opLifecycle/opSplitter.d.ts +12 -4
  113. package/dist/opLifecycle/opSplitter.d.ts.map +1 -1
  114. package/dist/opLifecycle/opSplitter.js +63 -53
  115. package/dist/opLifecycle/opSplitter.js.map +1 -1
  116. package/dist/opLifecycle/outbox.d.ts +2 -1
  117. package/dist/opLifecycle/outbox.d.ts.map +1 -1
  118. package/dist/opLifecycle/outbox.js +30 -29
  119. package/dist/opLifecycle/outbox.js.map +1 -1
  120. package/dist/opLifecycle/remoteMessageProcessor.d.ts +8 -0
  121. package/dist/opLifecycle/remoteMessageProcessor.d.ts.map +1 -1
  122. package/dist/opLifecycle/remoteMessageProcessor.js +36 -32
  123. package/dist/opLifecycle/remoteMessageProcessor.js.map +1 -1
  124. package/dist/packageVersion.d.ts +1 -1
  125. package/dist/packageVersion.js +1 -1
  126. package/dist/packageVersion.js.map +1 -1
  127. package/dist/pendingStateManager.d.ts +1 -1
  128. package/dist/pendingStateManager.d.ts.map +1 -1
  129. package/dist/pendingStateManager.js +18 -18
  130. package/dist/pendingStateManager.js.map +1 -1
  131. package/dist/public.d.ts +12 -0
  132. package/dist/scheduleManager.d.ts +1 -1
  133. package/dist/scheduleManager.d.ts.map +1 -1
  134. package/dist/scheduleManager.js +28 -24
  135. package/dist/scheduleManager.js.map +1 -1
  136. package/dist/storageServiceWithAttachBlobs.d.ts +2 -2
  137. package/dist/storageServiceWithAttachBlobs.d.ts.map +1 -1
  138. package/dist/storageServiceWithAttachBlobs.js +2 -2
  139. package/dist/storageServiceWithAttachBlobs.js.map +1 -1
  140. package/dist/summary/documentSchema.d.ts +209 -0
  141. package/dist/summary/documentSchema.d.ts.map +1 -0
  142. package/dist/summary/documentSchema.js +390 -0
  143. package/dist/summary/documentSchema.js.map +1 -0
  144. package/dist/summary/index.d.ts +2 -1
  145. package/dist/summary/index.d.ts.map +1 -1
  146. package/dist/summary/index.js +4 -1
  147. package/dist/summary/index.js.map +1 -1
  148. package/dist/summary/orderedClientElection.d.ts +2 -2
  149. package/dist/summary/orderedClientElection.d.ts.map +1 -1
  150. package/dist/summary/orderedClientElection.js +12 -7
  151. package/dist/summary/orderedClientElection.js.map +1 -1
  152. package/dist/summary/runWhileConnectedCoordinator.d.ts +1 -1
  153. package/dist/summary/runWhileConnectedCoordinator.d.ts.map +1 -1
  154. package/dist/summary/runWhileConnectedCoordinator.js +3 -3
  155. package/dist/summary/runWhileConnectedCoordinator.js.map +1 -1
  156. package/dist/summary/runningSummarizer.d.ts +3 -3
  157. package/dist/summary/runningSummarizer.d.ts.map +1 -1
  158. package/dist/summary/runningSummarizer.js +16 -16
  159. package/dist/summary/runningSummarizer.js.map +1 -1
  160. package/dist/summary/summarizer.d.ts +3 -2
  161. package/dist/summary/summarizer.d.ts.map +1 -1
  162. package/dist/summary/summarizer.js +13 -13
  163. package/dist/summary/summarizer.js.map +1 -1
  164. package/dist/summary/summarizerClientElection.d.ts +2 -2
  165. package/dist/summary/summarizerClientElection.d.ts.map +1 -1
  166. package/dist/summary/summarizerClientElection.js.map +1 -1
  167. package/dist/summary/summarizerHeuristics.d.ts +1 -1
  168. package/dist/summary/summarizerHeuristics.d.ts.map +1 -1
  169. package/dist/summary/summarizerHeuristics.js +2 -2
  170. package/dist/summary/summarizerHeuristics.js.map +1 -1
  171. package/dist/summary/summarizerNode/summarizerNode.d.ts +3 -2
  172. package/dist/summary/summarizerNode/summarizerNode.d.ts.map +1 -1
  173. package/dist/summary/summarizerNode/summarizerNode.js +28 -28
  174. package/dist/summary/summarizerNode/summarizerNode.js.map +1 -1
  175. package/dist/summary/summarizerNode/summarizerNodeUtils.d.ts +2 -1
  176. package/dist/summary/summarizerNode/summarizerNodeUtils.d.ts.map +1 -1
  177. package/dist/summary/summarizerNode/summarizerNodeUtils.js +3 -3
  178. package/dist/summary/summarizerNode/summarizerNodeUtils.js.map +1 -1
  179. package/dist/summary/summarizerNode/summarizerNodeWithGc.d.ts +2 -1
  180. package/dist/summary/summarizerNode/summarizerNodeWithGc.d.ts.map +1 -1
  181. package/dist/summary/summarizerNode/summarizerNodeWithGc.js +14 -14
  182. package/dist/summary/summarizerNode/summarizerNodeWithGc.js.map +1 -1
  183. package/dist/summary/summarizerTypes.d.ts +5 -3
  184. package/dist/summary/summarizerTypes.d.ts.map +1 -1
  185. package/dist/summary/summarizerTypes.js.map +1 -1
  186. package/dist/summary/summaryCollection.d.ts +2 -2
  187. package/dist/summary/summaryCollection.d.ts.map +1 -1
  188. package/dist/summary/summaryCollection.js +7 -7
  189. package/dist/summary/summaryCollection.js.map +1 -1
  190. package/dist/summary/summaryFormat.d.ts +6 -17
  191. package/dist/summary/summaryFormat.d.ts.map +1 -1
  192. package/dist/summary/summaryFormat.js +8 -8
  193. package/dist/summary/summaryFormat.js.map +1 -1
  194. package/dist/summary/summaryGenerator.d.ts +4 -3
  195. package/dist/summary/summaryGenerator.d.ts.map +1 -1
  196. package/dist/summary/summaryGenerator.js +17 -17
  197. package/dist/summary/summaryGenerator.js.map +1 -1
  198. package/dist/summary/summaryManager.d.ts +1 -1
  199. package/dist/summary/summaryManager.d.ts.map +1 -1
  200. package/dist/summary/summaryManager.js +15 -14
  201. package/dist/summary/summaryManager.js.map +1 -1
  202. package/internal.d.ts +11 -0
  203. package/legacy.d.ts +11 -0
  204. package/lib/batchTracker.d.ts +1 -1
  205. package/lib/batchTracker.d.ts.map +1 -1
  206. package/lib/batchTracker.js +2 -2
  207. package/lib/batchTracker.js.map +1 -1
  208. package/lib/blobManager.d.ts +33 -30
  209. package/lib/blobManager.d.ts.map +1 -1
  210. package/lib/blobManager.js +48 -73
  211. package/lib/blobManager.js.map +1 -1
  212. package/lib/channelCollection.d.ts +27 -22
  213. package/lib/channelCollection.d.ts.map +1 -1
  214. package/lib/channelCollection.js +132 -108
  215. package/lib/channelCollection.js.map +1 -1
  216. package/lib/connectionTelemetry.d.ts +3 -3
  217. package/lib/connectionTelemetry.d.ts.map +1 -1
  218. package/lib/connectionTelemetry.js +3 -3
  219. package/lib/connectionTelemetry.js.map +1 -1
  220. package/lib/containerHandleContext.d.ts.map +1 -1
  221. package/lib/containerHandleContext.js +1 -1
  222. package/lib/containerHandleContext.js.map +1 -1
  223. package/lib/containerRuntime.d.ts +42 -39
  224. package/lib/containerRuntime.d.ts.map +1 -1
  225. package/lib/containerRuntime.js +276 -141
  226. package/lib/containerRuntime.js.map +1 -1
  227. package/lib/dataStore.d.ts +1 -1
  228. package/lib/dataStore.d.ts.map +1 -1
  229. package/lib/dataStore.js +3 -3
  230. package/lib/dataStore.js.map +1 -1
  231. package/lib/dataStoreContext.d.ts +58 -19
  232. package/lib/dataStoreContext.d.ts.map +1 -1
  233. package/lib/dataStoreContext.js +110 -53
  234. package/lib/dataStoreContext.js.map +1 -1
  235. package/lib/dataStoreContexts.d.ts +1 -0
  236. package/lib/dataStoreContexts.d.ts.map +1 -1
  237. package/lib/dataStoreContexts.js +3 -2
  238. package/lib/dataStoreContexts.js.map +1 -1
  239. package/lib/dataStoreRegistry.d.ts +5 -1
  240. package/lib/dataStoreRegistry.d.ts.map +1 -1
  241. package/lib/dataStoreRegistry.js +1 -1
  242. package/lib/dataStoreRegistry.js.map +1 -1
  243. package/lib/deltaManagerSummarizerProxy.d.ts +1 -1
  244. package/lib/deltaManagerSummarizerProxy.d.ts.map +1 -1
  245. package/lib/deltaManagerSummarizerProxy.js.map +1 -1
  246. package/lib/deltaScheduler.d.ts +1 -1
  247. package/lib/deltaScheduler.d.ts.map +1 -1
  248. package/lib/deltaScheduler.js +1 -1
  249. package/lib/deltaScheduler.js.map +1 -1
  250. package/lib/error.d.ts +1 -1
  251. package/lib/error.d.ts.map +1 -1
  252. package/lib/error.js +2 -2
  253. package/lib/error.js.map +1 -1
  254. package/lib/gc/garbageCollection.d.ts +3 -2
  255. package/lib/gc/garbageCollection.d.ts.map +1 -1
  256. package/lib/gc/garbageCollection.js +8 -8
  257. package/lib/gc/garbageCollection.js.map +1 -1
  258. package/lib/gc/gcConfigs.d.ts +2 -2
  259. package/lib/gc/gcConfigs.d.ts.map +1 -1
  260. package/lib/gc/gcConfigs.js +4 -5
  261. package/lib/gc/gcConfigs.js.map +1 -1
  262. package/lib/gc/gcDefinitions.d.ts +4 -5
  263. package/lib/gc/gcDefinitions.d.ts.map +1 -1
  264. package/lib/gc/gcDefinitions.js.map +1 -1
  265. package/lib/gc/gcHelpers.d.ts +5 -1
  266. package/lib/gc/gcHelpers.d.ts.map +1 -1
  267. package/lib/gc/gcHelpers.js +10 -2
  268. package/lib/gc/gcHelpers.js.map +1 -1
  269. package/lib/gc/gcSummaryStateTracker.d.ts +2 -2
  270. package/lib/gc/gcSummaryStateTracker.d.ts.map +1 -1
  271. package/lib/gc/gcSummaryStateTracker.js +2 -2
  272. package/lib/gc/gcSummaryStateTracker.js.map +1 -1
  273. package/lib/gc/gcTelemetry.d.ts +2 -1
  274. package/lib/gc/gcTelemetry.d.ts.map +1 -1
  275. package/lib/gc/gcTelemetry.js +4 -2
  276. package/lib/gc/gcTelemetry.js.map +1 -1
  277. package/lib/gc/gcUnreferencedStateTracker.d.ts.map +1 -1
  278. package/lib/gc/gcUnreferencedStateTracker.js +2 -2
  279. package/lib/gc/gcUnreferencedStateTracker.js.map +1 -1
  280. package/lib/gc/index.d.ts +1 -1
  281. package/lib/gc/index.d.ts.map +1 -1
  282. package/lib/gc/index.js +1 -1
  283. package/lib/gc/index.js.map +1 -1
  284. package/lib/index.d.ts +5 -2
  285. package/lib/index.d.ts.map +1 -1
  286. package/lib/index.js +5 -2
  287. package/lib/index.js.map +1 -1
  288. package/lib/legacy.d.ts +91 -0
  289. package/lib/messageTypes.d.ts +11 -5
  290. package/lib/messageTypes.d.ts.map +1 -1
  291. package/lib/messageTypes.js +4 -0
  292. package/lib/messageTypes.js.map +1 -1
  293. package/lib/opLifecycle/batchManager.d.ts.map +1 -1
  294. package/lib/opLifecycle/batchManager.js.map +1 -1
  295. package/lib/opLifecycle/definitions.d.ts +2 -20
  296. package/lib/opLifecycle/definitions.d.ts.map +1 -1
  297. package/lib/opLifecycle/definitions.js.map +1 -1
  298. package/lib/opLifecycle/index.d.ts +3 -3
  299. package/lib/opLifecycle/index.d.ts.map +1 -1
  300. package/lib/opLifecycle/index.js +2 -2
  301. package/lib/opLifecycle/index.js.map +1 -1
  302. package/lib/opLifecycle/opCompressor.d.ts.map +1 -1
  303. package/lib/opLifecycle/opCompressor.js +2 -3
  304. package/lib/opLifecycle/opCompressor.js.map +1 -1
  305. package/lib/opLifecycle/opDecompressor.d.ts +15 -4
  306. package/lib/opLifecycle/opDecompressor.d.ts.map +1 -1
  307. package/lib/opLifecycle/opDecompressor.js +61 -62
  308. package/lib/opLifecycle/opDecompressor.js.map +1 -1
  309. package/lib/opLifecycle/opGroupingManager.d.ts +2 -1
  310. package/lib/opLifecycle/opGroupingManager.d.ts.map +1 -1
  311. package/lib/opLifecycle/opGroupingManager.js +9 -12
  312. package/lib/opLifecycle/opGroupingManager.js.map +1 -1
  313. package/lib/opLifecycle/opSplitter.d.ts +12 -4
  314. package/lib/opLifecycle/opSplitter.d.ts.map +1 -1
  315. package/lib/opLifecycle/opSplitter.js +47 -38
  316. package/lib/opLifecycle/opSplitter.js.map +1 -1
  317. package/lib/opLifecycle/outbox.d.ts +2 -1
  318. package/lib/opLifecycle/outbox.d.ts.map +1 -1
  319. package/lib/opLifecycle/outbox.js +19 -18
  320. package/lib/opLifecycle/outbox.js.map +1 -1
  321. package/lib/opLifecycle/remoteMessageProcessor.d.ts +8 -0
  322. package/lib/opLifecycle/remoteMessageProcessor.d.ts.map +1 -1
  323. package/lib/opLifecycle/remoteMessageProcessor.js +36 -32
  324. package/lib/opLifecycle/remoteMessageProcessor.js.map +1 -1
  325. package/lib/packageVersion.d.ts +1 -1
  326. package/lib/packageVersion.js +1 -1
  327. package/lib/packageVersion.js.map +1 -1
  328. package/lib/pendingStateManager.d.ts +1 -1
  329. package/lib/pendingStateManager.d.ts.map +1 -1
  330. package/lib/pendingStateManager.js +2 -2
  331. package/lib/pendingStateManager.js.map +1 -1
  332. package/lib/public.d.ts +12 -0
  333. package/lib/scheduleManager.d.ts +1 -1
  334. package/lib/scheduleManager.d.ts.map +1 -1
  335. package/lib/scheduleManager.js +7 -3
  336. package/lib/scheduleManager.js.map +1 -1
  337. package/lib/storageServiceWithAttachBlobs.d.ts +2 -2
  338. package/lib/storageServiceWithAttachBlobs.d.ts.map +1 -1
  339. package/lib/storageServiceWithAttachBlobs.js +1 -1
  340. package/lib/storageServiceWithAttachBlobs.js.map +1 -1
  341. package/lib/summary/documentSchema.d.ts +209 -0
  342. package/lib/summary/documentSchema.d.ts.map +1 -0
  343. package/lib/summary/documentSchema.js +386 -0
  344. package/lib/summary/documentSchema.js.map +1 -0
  345. package/lib/summary/index.d.ts +2 -1
  346. package/lib/summary/index.d.ts.map +1 -1
  347. package/lib/summary/index.js +1 -0
  348. package/lib/summary/index.js.map +1 -1
  349. package/lib/summary/orderedClientElection.d.ts +2 -2
  350. package/lib/summary/orderedClientElection.d.ts.map +1 -1
  351. package/lib/summary/orderedClientElection.js +7 -2
  352. package/lib/summary/orderedClientElection.js.map +1 -1
  353. package/lib/summary/runWhileConnectedCoordinator.d.ts +1 -1
  354. package/lib/summary/runWhileConnectedCoordinator.d.ts.map +1 -1
  355. package/lib/summary/runWhileConnectedCoordinator.js +1 -1
  356. package/lib/summary/runWhileConnectedCoordinator.js.map +1 -1
  357. package/lib/summary/runningSummarizer.d.ts +3 -3
  358. package/lib/summary/runningSummarizer.d.ts.map +1 -1
  359. package/lib/summary/runningSummarizer.js +3 -3
  360. package/lib/summary/runningSummarizer.js.map +1 -1
  361. package/lib/summary/summarizer.d.ts +3 -2
  362. package/lib/summary/summarizer.d.ts.map +1 -1
  363. package/lib/summary/summarizer.js +3 -3
  364. package/lib/summary/summarizer.js.map +1 -1
  365. package/lib/summary/summarizerClientElection.d.ts +2 -2
  366. package/lib/summary/summarizerClientElection.d.ts.map +1 -1
  367. package/lib/summary/summarizerClientElection.js.map +1 -1
  368. package/lib/summary/summarizerHeuristics.d.ts +1 -1
  369. package/lib/summary/summarizerHeuristics.d.ts.map +1 -1
  370. package/lib/summary/summarizerHeuristics.js +1 -1
  371. package/lib/summary/summarizerHeuristics.js.map +1 -1
  372. package/lib/summary/summarizerNode/summarizerNode.d.ts +3 -2
  373. package/lib/summary/summarizerNode/summarizerNode.d.ts.map +1 -1
  374. package/lib/summary/summarizerNode/summarizerNode.js +5 -5
  375. package/lib/summary/summarizerNode/summarizerNode.js.map +1 -1
  376. package/lib/summary/summarizerNode/summarizerNodeUtils.d.ts +2 -1
  377. package/lib/summary/summarizerNode/summarizerNodeUtils.d.ts.map +1 -1
  378. package/lib/summary/summarizerNode/summarizerNodeUtils.js +1 -1
  379. package/lib/summary/summarizerNode/summarizerNodeUtils.js.map +1 -1
  380. package/lib/summary/summarizerNode/summarizerNodeWithGc.d.ts +2 -1
  381. package/lib/summary/summarizerNode/summarizerNodeWithGc.d.ts.map +1 -1
  382. package/lib/summary/summarizerNode/summarizerNodeWithGc.js +3 -3
  383. package/lib/summary/summarizerNode/summarizerNodeWithGc.js.map +1 -1
  384. package/lib/summary/summarizerTypes.d.ts +5 -3
  385. package/lib/summary/summarizerTypes.d.ts.map +1 -1
  386. package/lib/summary/summarizerTypes.js.map +1 -1
  387. package/lib/summary/summaryCollection.d.ts +2 -2
  388. package/lib/summary/summaryCollection.d.ts.map +1 -1
  389. package/lib/summary/summaryCollection.js +1 -1
  390. package/lib/summary/summaryCollection.js.map +1 -1
  391. package/lib/summary/summaryFormat.d.ts +6 -17
  392. package/lib/summary/summaryFormat.d.ts.map +1 -1
  393. package/lib/summary/summaryFormat.js +3 -3
  394. package/lib/summary/summaryFormat.js.map +1 -1
  395. package/lib/summary/summaryGenerator.d.ts +4 -3
  396. package/lib/summary/summaryGenerator.d.ts.map +1 -1
  397. package/lib/summary/summaryGenerator.js +4 -4
  398. package/lib/summary/summaryGenerator.js.map +1 -1
  399. package/lib/summary/summaryManager.d.ts +1 -1
  400. package/lib/summary/summaryManager.d.ts.map +1 -1
  401. package/lib/summary/summaryManager.js +9 -8
  402. package/lib/summary/summaryManager.js.map +1 -1
  403. package/package.json +57 -65
  404. package/src/batchTracker.ts +4 -3
  405. package/src/blobManager.ts +100 -77
  406. package/src/channelCollection.ts +223 -167
  407. package/src/connectionTelemetry.ts +12 -12
  408. package/src/containerHandleContext.ts +3 -2
  409. package/src/containerRuntime.ts +481 -277
  410. package/src/dataStore.ts +9 -4
  411. package/src/dataStoreContext.ts +201 -97
  412. package/src/dataStoreContexts.ts +5 -2
  413. package/src/dataStoreRegistry.ts +3 -2
  414. package/src/deltaManagerSummarizerProxy.ts +1 -1
  415. package/src/deltaScheduler.ts +2 -1
  416. package/src/error.ts +2 -2
  417. package/src/gc/garbageCollection.ts +21 -20
  418. package/src/gc/gcConfigs.ts +15 -18
  419. package/src/gc/gcDefinitions.ts +6 -8
  420. package/src/gc/gcHelpers.ts +22 -5
  421. package/src/gc/gcSummaryStateTracker.ts +7 -5
  422. package/src/gc/gcTelemetry.ts +13 -7
  423. package/src/gc/gcUnreferencedStateTracker.ts +3 -2
  424. package/src/gc/index.ts +1 -0
  425. package/src/index.ts +22 -1
  426. package/src/messageTypes.ts +20 -6
  427. package/src/opLifecycle/README.md +89 -0
  428. package/src/opLifecycle/batchManager.ts +1 -0
  429. package/src/opLifecycle/definitions.ts +3 -21
  430. package/src/opLifecycle/index.ts +3 -9
  431. package/src/opLifecycle/opCompressor.ts +6 -5
  432. package/src/opLifecycle/opDecompressor.ts +90 -100
  433. package/src/opLifecycle/opGroupingManager.ts +12 -14
  434. package/src/opLifecycle/opSplitter.ts +76 -48
  435. package/src/opLifecycle/outbox.ts +30 -38
  436. package/src/opLifecycle/remoteMessageProcessor.ts +43 -55
  437. package/src/packageVersion.ts +1 -1
  438. package/src/pendingStateManager.ts +6 -6
  439. package/src/scheduleManager.ts +10 -8
  440. package/src/storageServiceWithAttachBlobs.ts +2 -2
  441. package/src/summary/documentSchema.ts +631 -0
  442. package/src/summary/index.ts +10 -1
  443. package/src/summary/orderedClientElection.ts +7 -7
  444. package/src/summary/runWhileConnectedCoordinator.ts +3 -2
  445. package/src/summary/runningSummarizer.ts +22 -20
  446. package/src/summary/summarizer.ts +17 -15
  447. package/src/summary/summarizerClientElection.ts +3 -2
  448. package/src/summary/summarizerHeuristics.ts +4 -2
  449. package/src/summary/summarizerNode/summarizerNode.ts +20 -18
  450. package/src/summary/summarizerNode/summarizerNodeUtils.ts +3 -2
  451. package/src/summary/summarizerNode/summarizerNodeWithGc.ts +16 -8
  452. package/src/summary/summarizerTypes.ts +7 -3
  453. package/src/summary/summaryCollection.ts +3 -3
  454. package/src/summary/summaryFormat.ts +14 -26
  455. package/src/summary/summaryGenerator.ts +12 -15
  456. package/src/summary/summaryManager.ts +16 -13
  457. package/api-extractor-cjs.json +0 -8
  458. package/dist/container-runtime-alpha.d.ts +0 -1753
  459. package/dist/container-runtime-beta.d.ts +0 -268
  460. package/dist/container-runtime-public.d.ts +0 -268
  461. package/dist/container-runtime-untrimmed.d.ts +0 -1893
  462. package/lib/container-runtime-alpha.d.ts +0 -1753
  463. package/lib/container-runtime-beta.d.ts +0 -268
  464. package/lib/container-runtime-public.d.ts +0 -268
  465. package/lib/container-runtime-untrimmed.d.ts +0 -1893
  466. package/lib/test/batchTracker.spec.js +0 -88
  467. package/lib/test/batchTracker.spec.js.map +0 -1
  468. package/lib/test/blobManager.spec.js +0 -835
  469. package/lib/test/blobManager.spec.js.map +0 -1
  470. package/lib/test/channelCollection.spec.js +0 -141
  471. package/lib/test/channelCollection.spec.js.map +0 -1
  472. package/lib/test/containerRuntime.spec.js +0 -1748
  473. package/lib/test/containerRuntime.spec.js.map +0 -1
  474. package/lib/test/dataStoreContext.spec.js +0 -801
  475. package/lib/test/dataStoreContext.spec.js.map +0 -1
  476. package/lib/test/dataStoreCreation.spec.js +0 -312
  477. package/lib/test/dataStoreCreation.spec.js.map +0 -1
  478. package/lib/test/dataStoreRegistry.spec.js +0 -26
  479. package/lib/test/dataStoreRegistry.spec.js.map +0 -1
  480. package/lib/test/fuzz/fuzzUtils.js +0 -66
  481. package/lib/test/fuzz/fuzzUtils.js.map +0 -1
  482. package/lib/test/fuzz/summarizer.fuzz.spec.js +0 -31
  483. package/lib/test/fuzz/summarizer.fuzz.spec.js.map +0 -1
  484. package/lib/test/fuzz/summarizerFuzzMocks.js +0 -162
  485. package/lib/test/fuzz/summarizerFuzzMocks.js.map +0 -1
  486. package/lib/test/fuzz/summarizerFuzzSuite.js +0 -106
  487. package/lib/test/fuzz/summarizerFuzzSuite.js.map +0 -1
  488. package/lib/test/gc/garbageCollection.spec.js +0 -1465
  489. package/lib/test/gc/garbageCollection.spec.js.map +0 -1
  490. package/lib/test/gc/gcConfigs.spec.js +0 -690
  491. package/lib/test/gc/gcConfigs.spec.js.map +0 -1
  492. package/lib/test/gc/gcHelpers.spec.js +0 -110
  493. package/lib/test/gc/gcHelpers.spec.js.map +0 -1
  494. package/lib/test/gc/gcReferenceGraphAlgorithm.spec.js +0 -68
  495. package/lib/test/gc/gcReferenceGraphAlgorithm.spec.js.map +0 -1
  496. package/lib/test/gc/gcStats.spec.js +0 -391
  497. package/lib/test/gc/gcStats.spec.js.map +0 -1
  498. package/lib/test/gc/gcSummaryStateTracker.spec.js +0 -228
  499. package/lib/test/gc/gcSummaryStateTracker.spec.js.map +0 -1
  500. package/lib/test/gc/gcTelemetry.spec.js +0 -530
  501. package/lib/test/gc/gcTelemetry.spec.js.map +0 -1
  502. package/lib/test/gc/gcUnitTestHelpers.js +0 -29
  503. package/lib/test/gc/gcUnitTestHelpers.js.map +0 -1
  504. package/lib/test/gc/gcUnreferencedStateTracker.spec.js +0 -192
  505. package/lib/test/gc/gcUnreferencedStateTracker.spec.js.map +0 -1
  506. package/lib/test/getPendingBlobs.spec.js +0 -193
  507. package/lib/test/getPendingBlobs.spec.js.map +0 -1
  508. package/lib/test/hardwareStats.spec.js +0 -93
  509. package/lib/test/hardwareStats.spec.js.map +0 -1
  510. package/lib/test/index.js +0 -6
  511. package/lib/test/index.js.map +0 -1
  512. package/lib/test/opLifecycle/OpGroupingManager.spec.js +0 -225
  513. package/lib/test/opLifecycle/OpGroupingManager.spec.js.map +0 -1
  514. package/lib/test/opLifecycle/batchManager.spec.js +0 -189
  515. package/lib/test/opLifecycle/batchManager.spec.js.map +0 -1
  516. package/lib/test/opLifecycle/opCompressor.spec.js +0 -74
  517. package/lib/test/opLifecycle/opCompressor.spec.js.map +0 -1
  518. package/lib/test/opLifecycle/opDecompressor.spec.js +0 -218
  519. package/lib/test/opLifecycle/opDecompressor.spec.js.map +0 -1
  520. package/lib/test/opLifecycle/opSplitter.spec.js +0 -272
  521. package/lib/test/opLifecycle/opSplitter.spec.js.map +0 -1
  522. package/lib/test/opLifecycle/outbox.spec.js +0 -675
  523. package/lib/test/opLifecycle/outbox.spec.js.map +0 -1
  524. package/lib/test/opLifecycle/remoteMessageProcessor.spec.js +0 -196
  525. package/lib/test/opLifecycle/remoteMessageProcessor.spec.js.map +0 -1
  526. package/lib/test/pendingStateManager.spec.js +0 -329
  527. package/lib/test/pendingStateManager.spec.js.map +0 -1
  528. package/lib/test/scheduleManager.spec.js +0 -270
  529. package/lib/test/scheduleManager.spec.js.map +0 -1
  530. package/lib/test/summarizerNode.spec.js +0 -326
  531. package/lib/test/summarizerNode.spec.js.map +0 -1
  532. package/lib/test/summarizerNodeWithGc.spec.js +0 -318
  533. package/lib/test/summarizerNodeWithGc.spec.js.map +0 -1
  534. package/lib/test/summary/orderedClientElection.spec.js +0 -535
  535. package/lib/test/summary/orderedClientElection.spec.js.map +0 -1
  536. package/lib/test/summary/runningSummarizer.spec.js +0 -1349
  537. package/lib/test/summary/runningSummarizer.spec.js.map +0 -1
  538. package/lib/test/summary/summarizer.spec.js +0 -29
  539. package/lib/test/summary/summarizer.spec.js.map +0 -1
  540. package/lib/test/summary/summarizerClientElection.spec.js +0 -436
  541. package/lib/test/summary/summarizerClientElection.spec.js.map +0 -1
  542. package/lib/test/summary/summarizerHeuristics.spec.js +0 -289
  543. package/lib/test/summary/summarizerHeuristics.spec.js.map +0 -1
  544. package/lib/test/summary/summaryCollection.spec.js +0 -200
  545. package/lib/test/summary/summaryCollection.spec.js.map +0 -1
  546. package/lib/test/summary/summaryManager.spec.js +0 -430
  547. package/lib/test/summary/summaryManager.spec.js.map +0 -1
  548. package/lib/test/summary/testQuorumClients.js +0 -34
  549. package/lib/test/summary/testQuorumClients.js.map +0 -1
  550. package/lib/test/throttler.spec.js +0 -175
  551. package/lib/test/throttler.spec.js.map +0 -1
  552. package/lib/test/types/validateContainerRuntimePrevious.generated.js +0 -180
  553. package/lib/test/types/validateContainerRuntimePrevious.generated.js.map +0 -1
  554. /package/{dist → lib}/tsdoc-metadata.json +0 -0
@@ -1,1748 +0,0 @@
1
- /*!
2
- * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
- * Licensed under the MIT License.
4
- */
5
- import { strict as assert } from "assert";
6
- import { createSandbox, useFakeTimers } from "sinon";
7
- import { AttachState, ContainerErrorTypes, } from "@fluidframework/container-definitions";
8
- import { MessageType, } from "@fluidframework/protocol-definitions";
9
- import { FlushMode, FlushModeExperimental, } from "@fluidframework/runtime-definitions";
10
- import { createChildLogger, isFluidError, isILoggingError, mixinMonitoringContext, MockLogger, } from "@fluidframework/telemetry-utils";
11
- import { MockDeltaManager, MockFluidDataStoreRuntime, MockQuorumClients, validateAssertionError, } from "@fluidframework/test-runtime-utils";
12
- import { stringToBuffer } from "@fluid-internal/client-utils";
13
- import { CompressionAlgorithms, ContainerRuntime, defaultPendingOpsWaitTimeoutMs, } from "../containerRuntime.js";
14
- import { ContainerMessageType, } from "../messageTypes.js";
15
- import { neverCancelledSummaryToken } from "../summary/index.js";
16
- function submitDataStoreOp(runtime, id, contents, localOpMetadata) {
17
- runtime.submitMessage(ContainerMessageType.FluidDataStoreOp, {
18
- address: id,
19
- contents,
20
- }, localOpMetadata);
21
- }
22
- describe("Runtime", () => {
23
- const configProvider = (settings) => ({
24
- getRawConfig: (name) => settings[name],
25
- });
26
- let submittedOps = [];
27
- let opFakeSequenceNumber = 1;
28
- let clock;
29
- before(() => {
30
- clock = useFakeTimers();
31
- });
32
- beforeEach(() => {
33
- submittedOps = [];
34
- opFakeSequenceNumber = 1;
35
- });
36
- afterEach(() => {
37
- clock.reset();
38
- });
39
- after(() => {
40
- clock.restore();
41
- });
42
- const getMockContext = (settings = {}, logger = new MockLogger()) => {
43
- const mockClientId = "mockClientId";
44
- // Mock the storage layer so "submitSummary" works.
45
- const mockStorage = {
46
- uploadSummaryWithContext: async (summary, context) => {
47
- return "fakeHandle";
48
- },
49
- };
50
- const mockContext = {
51
- attachState: AttachState.Attached,
52
- deltaManager: new MockDeltaManager(),
53
- quorum: new MockQuorumClients(),
54
- taggedLogger: mixinMonitoringContext(logger, configProvider(settings)).logger,
55
- clientDetails: { capabilities: { interactive: true } },
56
- closeFn: (_error) => { },
57
- updateDirtyContainerState: (_dirty) => { },
58
- getLoadedFromVersion: () => undefined,
59
- submitFn: (_type, contents, _batch, appData) => {
60
- submittedOps.push(contents);
61
- return opFakeSequenceNumber++;
62
- },
63
- clientId: mockClientId,
64
- connected: true,
65
- storage: mockStorage,
66
- };
67
- // Update the delta manager's last message which is used for validation during summarization.
68
- mockContext.deltaManager.lastMessage = {
69
- clientId: mockClientId,
70
- type: MessageType.Operation,
71
- sequenceNumber: 0,
72
- timestamp: Date.now(),
73
- minimumSequenceNumber: 0,
74
- referenceSequenceNumber: 0,
75
- clientSequenceNumber: 0,
76
- contents: undefined,
77
- };
78
- return mockContext;
79
- };
80
- const mockProvideEntryPoint = async () => ({
81
- myProp: "myValue",
82
- });
83
- describe("Container Runtime", () => {
84
- describe("IdCompressor", () => {
85
- it("finalizes idRange on attach", async () => {
86
- const logger = new MockLogger();
87
- const containerRuntime = await ContainerRuntime.loadRuntime({
88
- context: getMockContext({}, logger),
89
- registryEntries: [],
90
- existing: false,
91
- runtimeOptions: {
92
- flushMode: FlushMode.TurnBased,
93
- enableRuntimeIdCompressor: "on",
94
- },
95
- provideEntryPoint: mockProvideEntryPoint,
96
- });
97
- logger.clear();
98
- const compressor = containerRuntime.idCompressor;
99
- assert(compressor !== undefined);
100
- compressor.generateCompressedId();
101
- containerRuntime.createSummary();
102
- const range = compressor.takeNextCreationRange();
103
- assert.equal(range.ids, undefined, "All Ids should have been finalized after calling createSummary()");
104
- });
105
- });
106
- describe("flushMode setting", () => {
107
- it("Default flush mode", async () => {
108
- const containerRuntime = await ContainerRuntime.loadRuntime({
109
- context: getMockContext(),
110
- registryEntries: [],
111
- existing: false,
112
- runtimeOptions: {},
113
- provideEntryPoint: mockProvideEntryPoint,
114
- });
115
- assert.strictEqual(containerRuntime.flushMode, FlushMode.TurnBased);
116
- });
117
- it("Override default flush mode using options", async () => {
118
- const containerRuntime = await ContainerRuntime.loadRuntime({
119
- context: getMockContext(),
120
- registryEntries: [],
121
- existing: false,
122
- runtimeOptions: {
123
- flushMode: FlushMode.Immediate,
124
- },
125
- provideEntryPoint: mockProvideEntryPoint,
126
- });
127
- assert.strictEqual(containerRuntime.flushMode, FlushMode.Immediate);
128
- });
129
- it("Replaying ops should resend in correct order", async () => {
130
- const containerRuntime = await ContainerRuntime.loadRuntime({
131
- context: getMockContext(),
132
- registryEntries: [],
133
- existing: false,
134
- runtimeOptions: {
135
- flushMode: FlushMode.TurnBased,
136
- },
137
- provideEntryPoint: mockProvideEntryPoint,
138
- });
139
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
140
- containerRuntime.channelCollection = {
141
- setConnectionState: (_connected, _clientId) => { },
142
- // Pass data store op right back to ContainerRuntime
143
- reSubmit: (type, envelope, localOpMetadata) => {
144
- submitDataStoreOp(containerRuntime, envelope.address, envelope.contents, localOpMetadata);
145
- },
146
- };
147
- containerRuntime.setConnectionState(false);
148
- submitDataStoreOp(containerRuntime, "1", "test");
149
- containerRuntime.flush();
150
- submitDataStoreOp(containerRuntime, "2", "test");
151
- containerRuntime.setConnectionState(true);
152
- containerRuntime.flush();
153
- assert.strictEqual(submittedOps.length, 2);
154
- assert.strictEqual(submittedOps[0].contents.address, "1");
155
- assert.strictEqual(submittedOps[1].contents.address, "2");
156
- });
157
- });
158
- describe("orderSequentially", () => [
159
- FlushMode.TurnBased,
160
- FlushMode.Immediate,
161
- FlushModeExperimental.Async,
162
- ].forEach((flushMode) => {
163
- describe(`orderSequentially with flush mode: ${FlushMode[flushMode] ?? FlushModeExperimental[flushMode]}`, () => {
164
- let containerRuntime;
165
- let mockContext;
166
- const submittedOpsMetadata = [];
167
- const containerErrors = [];
168
- const getMockContextForOrderSequentially = () => {
169
- return {
170
- attachState: AttachState.Attached,
171
- deltaManager: new MockDeltaManager(),
172
- quorum: new MockQuorumClients(),
173
- taggedLogger: new MockLogger(),
174
- supportedFeatures: new Map([["referenceSequenceNumbers", true]]),
175
- clientDetails: { capabilities: { interactive: true } },
176
- closeFn: (error) => {
177
- if (error !== undefined) {
178
- containerErrors.push(error);
179
- }
180
- },
181
- updateDirtyContainerState: (_dirty) => { },
182
- submitFn: (_type, contents, _batch, appData) => {
183
- if (contents.type === "groupedBatch") {
184
- for (const subMessage of contents.contents) {
185
- submittedOpsMetadata.push(subMessage.metadata);
186
- }
187
- }
188
- else {
189
- submittedOpsMetadata.push(appData);
190
- }
191
- return opFakeSequenceNumber++;
192
- },
193
- connected: true,
194
- clientId: "fakeClientId",
195
- getLoadedFromVersion: () => undefined,
196
- };
197
- };
198
- const getFirstContainerError = () => {
199
- assert.ok(containerErrors.length > 0, "Container should have errors");
200
- return containerErrors[0];
201
- };
202
- const expectedOrderSequentiallyErrorMessage = "orderSequentially callback exception";
203
- beforeEach(async () => {
204
- mockContext = getMockContextForOrderSequentially();
205
- containerRuntime = await ContainerRuntime.loadRuntime({
206
- context: mockContext,
207
- registryEntries: [],
208
- existing: false,
209
- runtimeOptions: {
210
- summaryOptions: {
211
- summaryConfigOverrides: {
212
- state: "disabled",
213
- },
214
- },
215
- flushMode,
216
- },
217
- provideEntryPoint: mockProvideEntryPoint,
218
- });
219
- containerErrors.length = 0;
220
- submittedOpsMetadata.length = 0;
221
- });
222
- it("Can't call flush() inside orderSequentially's callback", () => {
223
- assert.throws(() => containerRuntime.orderSequentially(() => {
224
- containerRuntime.flush();
225
- }));
226
- const error = getFirstContainerError();
227
- assert(isFluidError(error));
228
- assert.strictEqual(error.errorType, ContainerErrorTypes.genericError);
229
- assert.strictEqual(error.message, `${expectedOrderSequentiallyErrorMessage}: 0x24c`);
230
- assert.strictEqual(error.getTelemetryProperties().orderSequentiallyCalls, 1);
231
- });
232
- it("Can't call flush() inside orderSequentially's callback when nested", () => {
233
- assert.throws(() => containerRuntime.orderSequentially(() => containerRuntime.orderSequentially(() => {
234
- containerRuntime.flush();
235
- })));
236
- const error = getFirstContainerError();
237
- assert(isFluidError(error));
238
- assert.strictEqual(error.errorType, ContainerErrorTypes.genericError);
239
- assert.strictEqual(error.message, `${expectedOrderSequentiallyErrorMessage}: 0x24c`);
240
- assert.strictEqual(error.getTelemetryProperties().orderSequentiallyCalls, 2);
241
- });
242
- it("Can't call flush() inside orderSequentially's callback when nested ignoring exceptions", () => {
243
- containerRuntime.orderSequentially(() => {
244
- try {
245
- containerRuntime.orderSequentially(() => {
246
- containerRuntime.flush();
247
- });
248
- }
249
- catch (e) {
250
- // ignore
251
- }
252
- });
253
- const error = getFirstContainerError();
254
- assert(isFluidError(error));
255
- assert.strictEqual(error.errorType, ContainerErrorTypes.genericError);
256
- assert.strictEqual(error.message, `${expectedOrderSequentiallyErrorMessage}: 0x24c`);
257
- assert.strictEqual(error.getTelemetryProperties().orderSequentiallyCalls, 2);
258
- });
259
- it("Errors propagate to the container", () => {
260
- assert.throws(() => containerRuntime.orderSequentially(() => {
261
- throw new Error("Any");
262
- }));
263
- const error = getFirstContainerError();
264
- assert(isFluidError(error));
265
- assert.strictEqual(error.errorType, ContainerErrorTypes.genericError);
266
- assert.strictEqual(error.message, `${expectedOrderSequentiallyErrorMessage}: Any`);
267
- assert.strictEqual(error.getTelemetryProperties().orderSequentiallyCalls, 1);
268
- });
269
- it("Errors propagate to the container when nested", () => {
270
- assert.throws(() => containerRuntime.orderSequentially(() => containerRuntime.orderSequentially(() => {
271
- throw new Error("Any");
272
- })));
273
- const error = getFirstContainerError();
274
- assert(isFluidError(error));
275
- assert.strictEqual(error.errorType, ContainerErrorTypes.genericError);
276
- assert.strictEqual(error.message, `${expectedOrderSequentiallyErrorMessage}: Any`);
277
- assert.strictEqual(error.getTelemetryProperties().orderSequentiallyCalls, 2);
278
- });
279
- it("Batching property set properly", () => {
280
- containerRuntime.orderSequentially(() => {
281
- submitDataStoreOp(containerRuntime, "1", "test");
282
- submitDataStoreOp(containerRuntime, "2", "test");
283
- submitDataStoreOp(containerRuntime, "3", "test");
284
- });
285
- containerRuntime.flush();
286
- assert.strictEqual(submittedOpsMetadata.length, 3, "3 messages should be sent");
287
- assert.strictEqual(submittedOpsMetadata[0].batch, true, "first message should be the batch start");
288
- assert.strictEqual(submittedOpsMetadata[1], undefined, "second message should not hold batch info");
289
- assert.strictEqual(submittedOpsMetadata[2].batch, false, "third message should be the batch end");
290
- });
291
- it("Resubmitting batch preserves original batches", async () => {
292
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
293
- containerRuntime.channelCollection = {
294
- setConnectionState: (_connected, _clientId) => { },
295
- // Pass data store op right back to ContainerRuntime
296
- reSubmit: (type, envelope, localOpMetadata) => {
297
- submitDataStoreOp(containerRuntime, envelope.address, envelope.contents, localOpMetadata);
298
- },
299
- };
300
- containerRuntime.setConnectionState(false);
301
- containerRuntime.orderSequentially(() => {
302
- submitDataStoreOp(containerRuntime, "1", "test");
303
- submitDataStoreOp(containerRuntime, "2", "test");
304
- submitDataStoreOp(containerRuntime, "3", "test");
305
- });
306
- containerRuntime.flush();
307
- containerRuntime.orderSequentially(() => {
308
- submitDataStoreOp(containerRuntime, "4", "test");
309
- submitDataStoreOp(containerRuntime, "5", "test");
310
- submitDataStoreOp(containerRuntime, "6", "test");
311
- });
312
- containerRuntime.flush();
313
- assert.strictEqual(submittedOpsMetadata.length, 0, "no messages should be sent");
314
- containerRuntime.setConnectionState(true);
315
- assert.strictEqual(submittedOpsMetadata.length, 6, "6 messages should be sent");
316
- const expectedBatchMetadata = [
317
- { batch: true },
318
- undefined,
319
- { batch: false },
320
- { batch: true },
321
- undefined,
322
- { batch: false },
323
- ];
324
- assert.deepStrictEqual(submittedOpsMetadata, expectedBatchMetadata, "batch metadata does not match");
325
- });
326
- });
327
- }));
328
- describe("Op reentry enforcement", () => {
329
- let containerRuntime;
330
- it("By default, don't enforce the op reentry check", async () => {
331
- containerRuntime = await ContainerRuntime.loadRuntime({
332
- context: getMockContext(),
333
- registryEntries: [],
334
- provideEntryPoint: mockProvideEntryPoint,
335
- existing: false,
336
- });
337
- assert.ok(containerRuntime.ensureNoDataModelChanges(() => {
338
- submitDataStoreOp(containerRuntime, "id", "test");
339
- return true;
340
- }));
341
- assert.ok(containerRuntime.ensureNoDataModelChanges(() => containerRuntime.ensureNoDataModelChanges(() => containerRuntime.ensureNoDataModelChanges(() => {
342
- submitDataStoreOp(containerRuntime, "id", "test");
343
- return true;
344
- }))));
345
- });
346
- it("If option enabled, enforce the op reentry check", async () => {
347
- containerRuntime = await ContainerRuntime.loadRuntime({
348
- context: getMockContext(),
349
- registryEntries: [],
350
- runtimeOptions: {
351
- enableOpReentryCheck: true,
352
- },
353
- provideEntryPoint: mockProvideEntryPoint,
354
- existing: false,
355
- });
356
- assert.throws(() => containerRuntime.ensureNoDataModelChanges(() => submitDataStoreOp(containerRuntime, "id", "test")));
357
- assert.throws(() => containerRuntime.ensureNoDataModelChanges(() => containerRuntime.ensureNoDataModelChanges(() => containerRuntime.ensureNoDataModelChanges(() => submitDataStoreOp(containerRuntime, "id", "test")))));
358
- });
359
- it("If option enabled but disabled via feature gate, don't enforce the op reentry check", async () => {
360
- containerRuntime = await ContainerRuntime.loadRuntime({
361
- context: getMockContext({
362
- "Fluid.ContainerRuntime.DisableOpReentryCheck": true,
363
- }),
364
- registryEntries: [],
365
- runtimeOptions: {
366
- enableOpReentryCheck: true,
367
- },
368
- provideEntryPoint: mockProvideEntryPoint,
369
- existing: false,
370
- });
371
- containerRuntime.ensureNoDataModelChanges(() => submitDataStoreOp(containerRuntime, "id", "test"));
372
- containerRuntime.ensureNoDataModelChanges(() => containerRuntime.ensureNoDataModelChanges(() => containerRuntime.ensureNoDataModelChanges(() => submitDataStoreOp(containerRuntime, "id", "test"))));
373
- });
374
- it("Report at most 5 reentrant ops", async () => {
375
- const mockLogger = new MockLogger();
376
- containerRuntime = await ContainerRuntime.loadRuntime({
377
- context: getMockContext({}, mockLogger),
378
- registryEntries: [],
379
- provideEntryPoint: mockProvideEntryPoint,
380
- existing: false,
381
- });
382
- mockLogger.clear();
383
- containerRuntime.ensureNoDataModelChanges(() => {
384
- for (let i = 0; i < 10; i++) {
385
- submitDataStoreOp(containerRuntime, "id", "test");
386
- }
387
- });
388
- // We expect only 5 events
389
- mockLogger.assertMatchStrict(Array.from(Array(5).keys()).map(() => ({
390
- eventName: "ContainerRuntime:OpReentry",
391
- error: "Op was submitted from within a `ensureNoDataModelChanges` callback",
392
- })));
393
- });
394
- it("Can't call flush() inside ensureNoDataModelChanges's callback", async () => {
395
- containerRuntime = await ContainerRuntime.loadRuntime({
396
- context: getMockContext(),
397
- registryEntries: [],
398
- runtimeOptions: {
399
- flushMode: FlushMode.Immediate,
400
- },
401
- provideEntryPoint: mockProvideEntryPoint,
402
- existing: false,
403
- });
404
- assert.throws(() => containerRuntime.ensureNoDataModelChanges(() => {
405
- containerRuntime.orderSequentially(() => { });
406
- }));
407
- });
408
- it("Can't create an infinite ensureNoDataModelChanges recursive call ", async () => {
409
- containerRuntime = await ContainerRuntime.loadRuntime({
410
- context: getMockContext(),
411
- registryEntries: [],
412
- provideEntryPoint: mockProvideEntryPoint,
413
- existing: false,
414
- });
415
- const callback = () => {
416
- containerRuntime.ensureNoDataModelChanges(() => {
417
- submitDataStoreOp(containerRuntime, "id", "test");
418
- callback();
419
- });
420
- };
421
- assert.throws(() => callback());
422
- });
423
- });
424
- describe("orderSequentially with rollback", () => [
425
- FlushMode.TurnBased,
426
- FlushMode.Immediate,
427
- FlushModeExperimental.Async,
428
- ].forEach((flushMode) => {
429
- describe(`orderSequentially with flush mode: ${FlushMode[flushMode] ?? FlushModeExperimental[flushMode]}`, () => {
430
- let containerRuntime;
431
- const containerErrors = [];
432
- const getMockContextForOrderSequentially = () => ({
433
- attachState: AttachState.Attached,
434
- deltaManager: new MockDeltaManager(),
435
- quorum: new MockQuorumClients(),
436
- taggedLogger: mixinMonitoringContext(new MockLogger(), configProvider({
437
- "Fluid.ContainerRuntime.EnableRollback": true,
438
- })),
439
- clientDetails: { capabilities: { interactive: true } },
440
- closeFn: (error) => {
441
- if (error !== undefined) {
442
- containerErrors.push(error);
443
- }
444
- },
445
- updateDirtyContainerState: (dirty) => { },
446
- getLoadedFromVersion: () => undefined,
447
- });
448
- beforeEach(async () => {
449
- containerRuntime = await ContainerRuntime.loadRuntime({
450
- context: getMockContextForOrderSequentially(),
451
- registryEntries: [],
452
- existing: false,
453
- runtimeOptions: {
454
- summaryOptions: {
455
- summaryConfigOverrides: { state: "disabled" },
456
- },
457
- flushMode,
458
- },
459
- provideEntryPoint: mockProvideEntryPoint,
460
- });
461
- containerErrors.length = 0;
462
- });
463
- it("No errors propagate to the container on rollback", () => {
464
- assert.throws(() => containerRuntime.orderSequentially(() => {
465
- throw new Error("Any");
466
- }));
467
- assert.strictEqual(containerErrors.length, 0);
468
- });
469
- it("No errors on successful callback with rollback set", () => {
470
- containerRuntime.orderSequentially(() => { });
471
- assert.strictEqual(containerErrors.length, 0);
472
- });
473
- });
474
- }));
475
- describe("Dirty flag", () => {
476
- const sandbox = createSandbox();
477
- const createMockContext = (attachState, addPendingMsg) => {
478
- const pendingState = {
479
- pending: {
480
- pendingStates: [
481
- {
482
- type: "message",
483
- content: `{"type": "${ContainerMessageType.BlobAttach}", "contents": {}}`,
484
- },
485
- ],
486
- },
487
- savedOps: [],
488
- };
489
- return {
490
- deltaManager: new MockDeltaManager(),
491
- quorum: new MockQuorumClients(),
492
- taggedLogger: new MockLogger(),
493
- clientDetails: { capabilities: { interactive: true } },
494
- updateDirtyContainerState: (_dirty) => { },
495
- attachState,
496
- pendingLocalState: addPendingMsg ? pendingState : undefined,
497
- getLoadedFromVersion: () => undefined,
498
- };
499
- };
500
- it("should NOT be set to dirty if context is attached with no pending ops", async () => {
501
- const mockContext = createMockContext(AttachState.Attached, false);
502
- const updateDirtyStateStub = sandbox.stub(mockContext, "updateDirtyContainerState");
503
- await ContainerRuntime.loadRuntime({
504
- context: mockContext,
505
- registryEntries: [],
506
- existing: false,
507
- runtimeOptions: undefined,
508
- containerScope: {},
509
- provideEntryPoint: mockProvideEntryPoint,
510
- });
511
- assert.deepStrictEqual(updateDirtyStateStub.calledOnce, true);
512
- assert.deepStrictEqual(updateDirtyStateStub.args, [[false]]);
513
- });
514
- it("should be set to dirty if context is attached with pending ops", async () => {
515
- const mockContext = createMockContext(AttachState.Attached, true);
516
- const updateDirtyStateStub = sandbox.stub(mockContext, "updateDirtyContainerState");
517
- await ContainerRuntime.loadRuntime({
518
- context: mockContext,
519
- registryEntries: [],
520
- existing: false,
521
- requestHandler: undefined,
522
- runtimeOptions: {},
523
- provideEntryPoint: mockProvideEntryPoint,
524
- });
525
- assert.deepStrictEqual(updateDirtyStateStub.calledOnce, true);
526
- assert.deepStrictEqual(updateDirtyStateStub.args, [[true]]);
527
- });
528
- it("should be set to dirty if context is attaching", async () => {
529
- const mockContext = createMockContext(AttachState.Attaching, false);
530
- const updateDirtyStateStub = sandbox.stub(mockContext, "updateDirtyContainerState");
531
- await ContainerRuntime.loadRuntime({
532
- context: mockContext,
533
- registryEntries: [],
534
- existing: false,
535
- requestHandler: undefined,
536
- runtimeOptions: {},
537
- provideEntryPoint: mockProvideEntryPoint,
538
- });
539
- assert.deepStrictEqual(updateDirtyStateStub.calledOnce, true);
540
- assert.deepStrictEqual(updateDirtyStateStub.args, [[true]]);
541
- });
542
- it("should be set to dirty if context is detached", async () => {
543
- const mockContext = createMockContext(AttachState.Detached, false);
544
- const updateDirtyStateStub = sandbox.stub(mockContext, "updateDirtyContainerState");
545
- await ContainerRuntime.loadRuntime({
546
- context: mockContext,
547
- registryEntries: [],
548
- existing: false,
549
- requestHandler: undefined,
550
- runtimeOptions: {},
551
- provideEntryPoint: mockProvideEntryPoint,
552
- });
553
- assert.deepStrictEqual(updateDirtyStateStub.calledOnce, true);
554
- assert.deepStrictEqual(updateDirtyStateStub.args, [[true]]);
555
- });
556
- });
557
- describe("Pending state progress tracking", () => {
558
- const maxReconnects = 7; // 7 is the default used by ContainerRuntime for the max reconnection attempts
559
- let containerRuntime;
560
- const mockLogger = new MockLogger();
561
- const containerErrors = [];
562
- const getMockContextForPendingStateProgressTracking = () => {
563
- return {
564
- clientId: "fakeClientId",
565
- attachState: AttachState.Attached,
566
- deltaManager: new MockDeltaManager(),
567
- quorum: new MockQuorumClients(),
568
- taggedLogger: mockLogger,
569
- clientDetails: { capabilities: { interactive: true } },
570
- closeFn: (error) => {
571
- if (error !== undefined) {
572
- containerErrors.push(error);
573
- }
574
- },
575
- updateDirtyContainerState: (_dirty) => { },
576
- getLoadedFromVersion: () => undefined,
577
- };
578
- };
579
- const getMockPendingStateManager = () => {
580
- let pendingMessages = 0;
581
- return {
582
- replayPendingStates: () => { },
583
- hasPendingMessages: () => pendingMessages > 0,
584
- processMessage: (_message, _local) => {
585
- return { localAck: false, localOpMetadata: undefined };
586
- },
587
- processPendingLocalMessage: (_message) => {
588
- return undefined;
589
- },
590
- get pendingMessagesCount() {
591
- return pendingMessages;
592
- },
593
- onSubmitMessage: (_type, _clientSequenceNumber, _referenceSequenceNumber, _content, _localOpMetadata, _opMetadata) => pendingMessages++,
594
- };
595
- };
596
- const getMockChannelCollection = () => {
597
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
598
- return {
599
- process: (..._args) => { },
600
- setConnectionState: (..._args) => { },
601
- };
602
- };
603
- const getFirstContainerError = () => {
604
- assert.ok(containerErrors.length > 0, "Container should have errors");
605
- return containerErrors[0];
606
- };
607
- beforeEach(async () => {
608
- containerErrors.length = 0;
609
- containerRuntime = await ContainerRuntime.loadRuntime({
610
- context: getMockContextForPendingStateProgressTracking(),
611
- registryEntries: [],
612
- existing: false,
613
- requestHandler: undefined,
614
- runtimeOptions: {
615
- summaryOptions: {
616
- summaryConfigOverrides: {
617
- state: "disabled",
618
- },
619
- },
620
- },
621
- provideEntryPoint: mockProvideEntryPoint,
622
- });
623
- });
624
- function patchRuntime(pendingStateManager, _maxReconnects = undefined) {
625
- const runtime = containerRuntime;
626
- runtime.pendingStateManager = pendingStateManager;
627
- runtime.channelCollection = getMockChannelCollection();
628
- runtime.maxConsecutiveReconnects =
629
- _maxReconnects ?? runtime.maxConsecutiveReconnects;
630
- return runtime;
631
- }
632
- const toggleConnection = (runtime) => {
633
- runtime.setConnectionState(false);
634
- runtime.setConnectionState(true);
635
- };
636
- const addPendingMessage = (pendingStateManager) => pendingStateManager.onSubmitMessage("", 0, "", undefined);
637
- it(`No progress for ${maxReconnects} connection state changes, with pending state, should ` +
638
- "generate telemetry event and throw an error that closes the container", async () => {
639
- const pendingStateManager = getMockPendingStateManager();
640
- patchRuntime(pendingStateManager);
641
- for (let i = 0; i < maxReconnects; i++) {
642
- addPendingMessage(pendingStateManager);
643
- toggleConnection(containerRuntime);
644
- }
645
- // NOTE: any errors returned by getFirstContainerError() are from a variable set in a mock closeFn function passed
646
- // around during test setup, which executes when the container runtime causes the context (container) to close.
647
- const error = getFirstContainerError();
648
- assert.strictEqual(error.errorType, ContainerErrorTypes.dataProcessingError);
649
- assert.strictEqual(error.message, "Runtime detected too many reconnects with no progress syncing local ops.");
650
- assert(isILoggingError(error));
651
- assert.strictEqual(error.getTelemetryProperties().attempts, maxReconnects);
652
- assert.strictEqual(error.getTelemetryProperties().pendingMessages, maxReconnects);
653
- mockLogger.assertMatchAny([
654
- {
655
- eventName: "ContainerRuntime:ReconnectsWithNoProgress",
656
- attempts: 3,
657
- pendingMessages: 3,
658
- },
659
- ]);
660
- });
661
- it(`No progress for ${maxReconnects} / 2 connection state changes, with pending state, should ` +
662
- "generate telemetry event but not throw an error that closes the container", async () => {
663
- const pendingStateManager = getMockPendingStateManager();
664
- patchRuntime(pendingStateManager);
665
- addPendingMessage(pendingStateManager);
666
- for (let i = 0; i < maxReconnects / 2; i++) {
667
- toggleConnection(containerRuntime);
668
- }
669
- // The particulars of the setup for this test mean that no errors here indicate the container did not close.
670
- assert.equal(containerErrors.length, 0);
671
- mockLogger.assertMatchAny([
672
- {
673
- eventName: "ContainerRuntime:ReconnectsWithNoProgress",
674
- attempts: 3,
675
- pendingMessages: 1,
676
- },
677
- ]);
678
- });
679
- it(`No progress for ${maxReconnects} connection state changes, with pending state, with ` +
680
- "feature disabled, should not generate telemetry event nor throw an error that closes the container", async () => {
681
- const pendingStateManager = getMockPendingStateManager();
682
- patchRuntime(pendingStateManager, -1 /* maxConsecutiveReconnects */);
683
- for (let i = 0; i < maxReconnects; i++) {
684
- addPendingMessage(pendingStateManager);
685
- toggleConnection(containerRuntime);
686
- }
687
- // The particulars of the setup for this test mean that no errors here indicate the container did not close.
688
- assert.equal(containerErrors.length, 0);
689
- mockLogger.assertMatchNone([
690
- {
691
- eventName: "ContainerRuntime:ReconnectsWithNoProgress",
692
- },
693
- ]);
694
- });
695
- it(`No progress for ${maxReconnects} connection state changes, with no pending state, should ` +
696
- "not generate telemetry event nor throw an error that closes the container", async () => {
697
- const pendingStateManager = getMockPendingStateManager();
698
- patchRuntime(pendingStateManager);
699
- for (let i = 0; i < maxReconnects; i++) {
700
- toggleConnection(containerRuntime);
701
- }
702
- // The particulars of the setup for this test mean that no errors here indicate the container did not close.
703
- assert.equal(containerErrors.length, 0);
704
- mockLogger.assertMatchNone([
705
- {
706
- eventName: "ContainerRuntime:ReconnectsWithNoProgress",
707
- },
708
- ]);
709
- });
710
- it(`No progress for ${maxReconnects} connection state changes, with pending state, successfully ` +
711
- "processing local op, should not generate telemetry event nor throw an error that closes the container", async () => {
712
- const pendingStateManager = getMockPendingStateManager();
713
- patchRuntime(pendingStateManager);
714
- addPendingMessage(pendingStateManager);
715
- for (let i = 0; i < maxReconnects; i++) {
716
- containerRuntime.setConnectionState(!containerRuntime.connected);
717
- containerRuntime.process({
718
- type: "op",
719
- clientId: "clientId",
720
- sequenceNumber: 0,
721
- contents: {
722
- address: "address",
723
- },
724
- }, true /* local */);
725
- }
726
- // The particulars of the setup for this test mean that no errors here indicate the container did not close.
727
- assert.equal(containerErrors.length, 0);
728
- mockLogger.assertMatchNone([
729
- {
730
- eventName: "ContainerRuntime:ReconnectsWithNoProgress",
731
- },
732
- ]);
733
- });
734
- it(`No progress for ${maxReconnects} connection state changes, with pending state, successfully ` +
735
- "processing remote op and local chunked op, should generate telemetry event and throw an error that closes the container", async () => {
736
- const pendingStateManager = getMockPendingStateManager();
737
- patchRuntime(pendingStateManager);
738
- for (let i = 0; i < maxReconnects; i++) {
739
- addPendingMessage(pendingStateManager);
740
- toggleConnection(containerRuntime);
741
- containerRuntime.process({
742
- type: "op",
743
- clientId: "a unique, remote clientId",
744
- sequenceNumber: 0,
745
- contents: {
746
- address: "address",
747
- },
748
- }, false /* local */);
749
- containerRuntime.process({
750
- type: "op",
751
- clientId: "clientId",
752
- sequenceNumber: 0,
753
- contents: {
754
- address: "address",
755
- contents: {
756
- chunkId: i + 1,
757
- totalChunks: maxReconnects + 1,
758
- },
759
- type: "chunkedOp",
760
- },
761
- }, true /* local */);
762
- }
763
- // NOTE: any errors returned by getFirstContainerError() are from a variable set in a mock closeFn function passed
764
- // around during test setup, which executes when the container runtime causes the context (container) to close.
765
- const error = getFirstContainerError();
766
- assert.strictEqual(error.errorType, ContainerErrorTypes.dataProcessingError);
767
- assert.strictEqual(error.message, "Runtime detected too many reconnects with no progress syncing local ops.");
768
- assert(isILoggingError(error));
769
- assert.strictEqual(error.getTelemetryProperties().attempts, maxReconnects);
770
- assert.strictEqual(error.getTelemetryProperties().pendingMessages, maxReconnects);
771
- mockLogger.assertMatchAny([
772
- {
773
- eventName: "ContainerRuntime:ReconnectsWithNoProgress",
774
- attempts: 3,
775
- pendingMessages: 3,
776
- },
777
- ]);
778
- });
779
- });
780
- describe("Future op type compatibility", () => {
781
- let containerRuntime;
782
- beforeEach(async () => {
783
- containerRuntime = await ContainerRuntime.loadRuntime({
784
- context: getMockContext(),
785
- registryEntries: [],
786
- existing: false,
787
- requestHandler: undefined,
788
- runtimeOptions: {},
789
- provideEntryPoint: mockProvideEntryPoint,
790
- });
791
- });
792
- it("can submit op compat behavior", async () => {
793
- const containerRuntimeWithSubmit = containerRuntime;
794
- const runtimeCompatMessage = {
795
- type: "NEW",
796
- contents: "Hello",
797
- compatDetails: { behavior: "Ignore" },
798
- };
799
- assert.doesNotThrow(() => containerRuntimeWithSubmit.submit(runtimeCompatMessage, undefined, undefined), "Cannot submit container runtime message with compatDetails");
800
- });
801
- /** Overwrites channelCollection property and exposes private submit function with modified typing */
802
- function patchContainerRuntime() {
803
- const patched = containerRuntime;
804
- patched.channelCollection = {
805
- setConnectionState: (_connected, _clientId) => { },
806
- // Pass data store op right back to ContainerRuntime
807
- reSubmit: (type, envelope, localOpMetadata) => {
808
- submitDataStoreOp(containerRuntime, envelope.address, envelope.contents, localOpMetadata);
809
- },
810
- };
811
- return patched;
812
- }
813
- it("Op with unrecognized type and 'Ignore' compat behavior is ignored by resubmit", async () => {
814
- const patchedContainerRuntime = patchContainerRuntime();
815
- patchedContainerRuntime.setConnectionState(false);
816
- submitDataStoreOp(patchedContainerRuntime, "1", "test");
817
- submitDataStoreOp(patchedContainerRuntime, "2", "test");
818
- patchedContainerRuntime.submit({
819
- type: "FUTURE_TYPE",
820
- contents: "3",
821
- compatDetails: { behavior: "Ignore" }, // This op should be ignored by resubmit
822
- });
823
- submitDataStoreOp(patchedContainerRuntime, "4", "test");
824
- assert.strictEqual(submittedOps.length, 0, "no messages should be sent while disconnected");
825
- // Connect, which will trigger resubmit
826
- patchedContainerRuntime.setConnectionState(true);
827
- assert.strictEqual(submittedOps.length, 3, "Only 3 messages should be sent - Do not resubmit the future/unknown op");
828
- });
829
- it("Op with unrecognized type and no compat behavior causes resubmit to throw", async () => {
830
- const patchedContainerRuntime = patchContainerRuntime();
831
- patchedContainerRuntime.setConnectionState(false);
832
- patchedContainerRuntime.submit({
833
- type: "FUTURE_TYPE",
834
- contents: "3",
835
- // No compatDetails so it will throw on resubmit.
836
- });
837
- assert.strictEqual(submittedOps.length, 0, "no messages should be sent while disconnected");
838
- // Note: hitting this error case in practice would require a new op type to be deployed,
839
- // one such op to be stashed, then a new session loads on older code that is unaware
840
- // of the new op type.
841
- assert.throws(() => {
842
- // Connect, which will trigger resubmit
843
- patchedContainerRuntime.setConnectionState(true);
844
- }, "Expected resubmit to throw");
845
- });
846
- it("process remote op with unrecognized type and 'Ignore' compat behavior", async () => {
847
- const futureRuntimeMessage = {
848
- type: "FROM_THE_FUTURE",
849
- contents: "Hello",
850
- compatDetails: { behavior: "Ignore" },
851
- };
852
- const packedOp = {
853
- contents: JSON.stringify(futureRuntimeMessage),
854
- type: MessageType.Operation,
855
- sequenceNumber: 123,
856
- clientId: "someClientId",
857
- };
858
- containerRuntime.process(packedOp, false /* local */);
859
- });
860
- it("process remote op with unrecognized type and 'FailToProcess' compat behavior", async () => {
861
- const futureRuntimeMessage = {
862
- type: "FROM THE FUTURE",
863
- contents: "Hello",
864
- compatDetails: { behavior: "FailToProcess" },
865
- };
866
- const packedOp = {
867
- type: MessageType.Operation,
868
- contents: JSON.stringify(futureRuntimeMessage),
869
- sequenceNumber: 123,
870
- clientId: "someClientId",
871
- };
872
- assert.throws(() => containerRuntime.process(packedOp, false /* local */), (error) => error.errorType === ContainerErrorTypes.dataProcessingError, "Ops with unrecognized type and 'FailToProcess' compat behavior should fail to process");
873
- });
874
- it("process remote op with unrecognized type and no compat behavior", async () => {
875
- const futureRuntimeMessage = {
876
- type: "FROM_THE_FUTURE",
877
- contents: "Hello",
878
- };
879
- const packedOp = {
880
- contents: JSON.stringify(futureRuntimeMessage),
881
- type: MessageType.Operation,
882
- sequenceNumber: 123,
883
- clientId: "someClientId",
884
- };
885
- assert.throws(() => containerRuntime.process(packedOp, false /* local */), (error) => error.errorType === ContainerErrorTypes.dataProcessingError, "Ops with unrecognized type and no specified compat behavior should fail to process");
886
- });
887
- });
888
- describe("Supports mixin classes", () => {
889
- it("new loadRuntime method works", async () => {
890
- const makeMixin = (Base, methodName, methodReturn) => class MixinContainerRuntime extends Base {
891
- static async loadRuntime(params) {
892
- // Note: we're mutating the parameter object here, normally a no-no, but shouldn't be
893
- // an issue in our tests.
894
- params.containerRuntimeCtor =
895
- params.containerRuntimeCtor ?? MixinContainerRuntime;
896
- params.containerScope = params.containerScope ?? params.context.scope;
897
- return Base.loadRuntime(params);
898
- }
899
- [methodName]() {
900
- return methodReturn;
901
- }
902
- };
903
- const myEntryPoint = {
904
- myProp: "myValue",
905
- };
906
- const runtime = await makeMixin(makeMixin(ContainerRuntime, "method1", "mixed in return"), "method2", 42).loadRuntime({
907
- context: getMockContext(),
908
- provideEntryPoint: async (containerRuntime) => myEntryPoint,
909
- existing: false,
910
- registryEntries: [],
911
- });
912
- assert.equal(runtime.method1(), "mixed in return");
913
- assert.equal(runtime.method2(), 42);
914
- });
915
- });
916
- describe("EntryPoint initialized correctly", () => {
917
- it("when using new loadRuntime method", async () => {
918
- const myEntryPoint = {
919
- myProp: "myValue",
920
- };
921
- const containerRuntime = await ContainerRuntime.loadRuntime({
922
- context: getMockContext(),
923
- provideEntryPoint: async (ctrRuntime) => myEntryPoint,
924
- existing: false,
925
- registryEntries: [],
926
- });
927
- // The entryPoint should come from the provided initialization function.
928
- const actualEntryPoint = await containerRuntime.getEntryPoint();
929
- assert(actualEntryPoint !== undefined, "entryPoint was not initialized");
930
- assert.deepEqual(actualEntryPoint, myEntryPoint, "entryPoint does not match expected object");
931
- });
932
- it("loadRuntime accepts both requestHandlers and entryPoint", async () => {
933
- const myResponse = {
934
- mimeType: "fluid/object",
935
- value: "hello!",
936
- status: 200,
937
- };
938
- const myEntryPoint = {
939
- myProp: "myValue",
940
- };
941
- const containerRuntime = await ContainerRuntime.loadRuntime({
942
- context: getMockContext(),
943
- requestHandler: async (req, ctrRuntime) => myResponse,
944
- provideEntryPoint: async (ctrRuntime) => myEntryPoint,
945
- existing: false,
946
- registryEntries: [],
947
- });
948
- // Calling request on the runtime should use the request handler we passed in the runtime's constructor.
949
- const responseFromRequestMethod = await containerRuntime.request({
950
- url: "/",
951
- });
952
- assert.deepEqual(responseFromRequestMethod, myResponse, "request method in runtime did not return the expected object");
953
- // The entryPoint should come from the provided initialization function.
954
- const actualEntryPoint = await containerRuntime.getEntryPoint();
955
- assert(actualEntryPoint !== undefined, "entryPoint was not initialized");
956
- assert.deepEqual(actualEntryPoint, myEntryPoint, "entryPoint does not match expected object");
957
- });
958
- });
959
- describe("Op content modification", () => {
960
- let containerRuntime;
961
- let pendingStateManager;
962
- beforeEach(async () => {
963
- containerRuntime = await ContainerRuntime.loadRuntime({
964
- context: getMockContext(),
965
- registryEntries: [],
966
- existing: false,
967
- runtimeOptions: {},
968
- provideEntryPoint: mockProvideEntryPoint,
969
- });
970
- pendingStateManager = containerRuntime.pendingStateManager;
971
- });
972
- it("modifying op content after submit does not reflect in PendingStateManager", () => {
973
- const content = { prop1: 1 };
974
- submitDataStoreOp(containerRuntime, "1", content);
975
- containerRuntime.flush();
976
- content.prop1 = 2;
977
- const state = pendingStateManager.getLocalState();
978
- assert.notStrictEqual(state, undefined, "expect pending local state");
979
- assert.strictEqual(state?.pendingStates.length, 1, "expect 1 pending message");
980
- assert.deepStrictEqual(JSON.parse(state?.pendingStates?.[0].content).contents.contents, {
981
- prop1: 1,
982
- }, "content of pending local message has changed");
983
- });
984
- });
985
- describe("Container logging when loaded", () => {
986
- let mockLogger;
987
- const localGetMockContext = (featureGates = {}) => {
988
- return {
989
- attachState: AttachState.Attached,
990
- deltaManager: new MockDeltaManager(),
991
- quorum: new MockQuorumClients(),
992
- taggedLogger: mixinMonitoringContext(mockLogger, configProvider(featureGates)),
993
- supportedFeatures: new Map([["referenceSequenceNumbers", true]]),
994
- clientDetails: { capabilities: { interactive: true } },
995
- closeFn: (_error) => { },
996
- updateDirtyContainerState: (_dirty) => { },
997
- getLoadedFromVersion: () => undefined,
998
- };
999
- };
1000
- beforeEach(async () => {
1001
- mockLogger = new MockLogger();
1002
- });
1003
- const runtimeOptions = {
1004
- compressionOptions: {
1005
- minimumBatchSizeInBytes: 1024 * 1024,
1006
- compressionAlgorithm: CompressionAlgorithms.lz4,
1007
- },
1008
- chunkSizeInBytes: 800 * 1024,
1009
- gcOptions: {
1010
- gcAllowed: true,
1011
- },
1012
- flushMode: FlushModeExperimental.Async,
1013
- enableGroupedBatching: true,
1014
- };
1015
- const defaultRuntimeOptions = {
1016
- summaryOptions: {},
1017
- gcOptions: {},
1018
- loadSequenceNumberVerification: "close",
1019
- flushMode: FlushMode.TurnBased,
1020
- compressionOptions: {
1021
- minimumBatchSizeInBytes: 614400,
1022
- compressionAlgorithm: CompressionAlgorithms.lz4,
1023
- },
1024
- maxBatchSizeInBytes: 700 * 1024,
1025
- chunkSizeInBytes: 204800,
1026
- enableRuntimeIdCompressor: "off",
1027
- enableOpReentryCheck: false,
1028
- enableGroupedBatching: false,
1029
- };
1030
- const mergedRuntimeOptions = { ...defaultRuntimeOptions, ...runtimeOptions };
1031
- it("Container load stats", async () => {
1032
- await ContainerRuntime.loadRuntime({
1033
- context: localGetMockContext({}),
1034
- registryEntries: [],
1035
- existing: false,
1036
- runtimeOptions,
1037
- provideEntryPoint: mockProvideEntryPoint,
1038
- });
1039
- mockLogger.assertMatchAny([
1040
- {
1041
- eventName: "ContainerRuntime:ContainerLoadStats",
1042
- category: "generic",
1043
- options: JSON.stringify(mergedRuntimeOptions),
1044
- idCompressorMode: defaultRuntimeOptions.enableRuntimeIdCompressor,
1045
- },
1046
- ]);
1047
- });
1048
- it("Container load stats with feature gate overrides", async () => {
1049
- const featureGates = {
1050
- "Fluid.ContainerRuntime.CompressionDisabled": true,
1051
- "Fluid.ContainerRuntime.CompressionChunkingDisabled": true,
1052
- "Fluid.ContainerRuntime.DisableOpReentryCheck": false,
1053
- "Fluid.ContainerRuntime.IdCompressorEnabled": true,
1054
- "Fluid.ContainerRuntime.DisableGroupedBatching": true,
1055
- };
1056
- await ContainerRuntime.loadRuntime({
1057
- context: localGetMockContext(featureGates),
1058
- registryEntries: [],
1059
- existing: false,
1060
- runtimeOptions,
1061
- provideEntryPoint: mockProvideEntryPoint,
1062
- });
1063
- mockLogger.assertMatchAny([
1064
- {
1065
- eventName: "ContainerRuntime:ContainerLoadStats",
1066
- category: "generic",
1067
- options: JSON.stringify(mergedRuntimeOptions),
1068
- idCompressorMode: "on",
1069
- featureGates: JSON.stringify({
1070
- disableCompression: true,
1071
- disableOpReentryCheck: false,
1072
- disableChunking: true,
1073
- }),
1074
- groupedBatchingEnabled: false,
1075
- },
1076
- ]);
1077
- });
1078
- });
1079
- describe("Container feature detection", () => {
1080
- const mockLogger = new MockLogger();
1081
- beforeEach(() => {
1082
- mockLogger.clear();
1083
- });
1084
- const localGetMockContext = (features) => {
1085
- return {
1086
- attachState: AttachState.Attached,
1087
- deltaManager: new MockDeltaManager(),
1088
- quorum: new MockQuorumClients(),
1089
- taggedLogger: mockLogger,
1090
- supportedFeatures: features,
1091
- clientDetails: { capabilities: { interactive: true } },
1092
- closeFn: (_error) => { },
1093
- updateDirtyContainerState: (_dirty) => { },
1094
- getLoadedFromVersion: () => undefined,
1095
- };
1096
- };
1097
- [
1098
- undefined,
1099
- new Map([["referenceSequenceNumbers", false]]),
1100
- new Map([
1101
- ["other", true],
1102
- ["feature", true],
1103
- ]),
1104
- ].forEach((features) => {
1105
- it("Loader not supported for async FlushMode, fallback to TurnBased", async () => {
1106
- const runtime = await ContainerRuntime.loadRuntime({
1107
- context: localGetMockContext(features),
1108
- registryEntries: [],
1109
- existing: false,
1110
- runtimeOptions: {
1111
- flushMode: FlushModeExperimental.Async,
1112
- },
1113
- provideEntryPoint: mockProvideEntryPoint,
1114
- });
1115
- assert.equal(runtime.flushMode, FlushMode.TurnBased);
1116
- mockLogger.assertMatchAny([
1117
- {
1118
- eventName: "ContainerRuntime:FlushModeFallback",
1119
- category: "error",
1120
- },
1121
- ]);
1122
- });
1123
- });
1124
- it("Loader supported for async FlushMode", async () => {
1125
- const runtime = await ContainerRuntime.loadRuntime({
1126
- context: localGetMockContext(new Map([["referenceSequenceNumbers", true]])),
1127
- registryEntries: [],
1128
- existing: false,
1129
- runtimeOptions: {
1130
- flushMode: FlushModeExperimental.Async,
1131
- },
1132
- provideEntryPoint: mockProvideEntryPoint,
1133
- });
1134
- assert.equal(runtime.flushMode, FlushModeExperimental.Async);
1135
- mockLogger.assertMatchNone([
1136
- {
1137
- eventName: "ContainerRuntime:FlushModeFallback",
1138
- category: "error",
1139
- },
1140
- ]);
1141
- });
1142
- });
1143
- describe("Summarization", () => {
1144
- let containerRuntime;
1145
- async function yieldEventLoop() {
1146
- const yieldP = new Promise((resolve) => {
1147
- setTimeout(resolve);
1148
- });
1149
- clock.tick(1);
1150
- await yieldP;
1151
- }
1152
- beforeEach(async () => {
1153
- const settings = {};
1154
- settings["Fluid.Summarizer.ValidateSummaryBeforeUpload"] = true;
1155
- containerRuntime = await ContainerRuntime.loadRuntime({
1156
- context: getMockContext(settings),
1157
- registryEntries: [],
1158
- existing: false,
1159
- provideEntryPoint: mockProvideEntryPoint,
1160
- });
1161
- });
1162
- it("summary is submitted successfully", async () => {
1163
- const summarizeResult = await containerRuntime.submitSummary({
1164
- summaryLogger: createChildLogger(),
1165
- cancellationToken: neverCancelledSummaryToken,
1166
- latestSummaryRefSeqNum: 0,
1167
- });
1168
- assert(summarizeResult.stage === "submit", "Summary did not succeed");
1169
- });
1170
- it("summary fails if summary token is canceled", async () => {
1171
- const cancelledSummaryToken = {
1172
- cancelled: true,
1173
- waitCancelled: new Promise(() => { }),
1174
- };
1175
- const summarizeResult = await containerRuntime.submitSummary({
1176
- summaryLogger: createChildLogger(),
1177
- cancellationToken: cancelledSummaryToken,
1178
- latestSummaryRefSeqNum: 0,
1179
- });
1180
- assert(summarizeResult.stage === "base", "Summary did not fail");
1181
- assert.strictEqual(summarizeResult.error, "disconnected", "Summary was not canceled");
1182
- });
1183
- it("summary fails before generate if there are pending ops", async () => {
1184
- // Submit an op and yield for it to be flushed from outbox to pending state manager.
1185
- submitDataStoreOp(containerRuntime, "fakeId", "fakeContents");
1186
- await yieldEventLoop();
1187
- const summarizeResultP = containerRuntime.submitSummary({
1188
- summaryLogger: createChildLogger(),
1189
- cancellationToken: neverCancelledSummaryToken,
1190
- latestSummaryRefSeqNum: 0,
1191
- });
1192
- // Advance the clock by the time that container runtime would wait for pending ops to be processed.
1193
- clock.tick(defaultPendingOpsWaitTimeoutMs);
1194
- const summarizeResult = await summarizeResultP;
1195
- assert(summarizeResult.stage === "base", "Summary did not fail");
1196
- assert.strictEqual(summarizeResult.error.message, "PendingOpsWhileSummarizing", "Summary did not fail with the right error");
1197
- assert.strictEqual(summarizeResult.error.beforeGenerate, true, "It should have failed before generating summary");
1198
- });
1199
- it("summary fails after generate if there are pending ops", async () => {
1200
- // Patch the summarize function to submit messages during it. This way there will be pending
1201
- // messages after generating the summary.
1202
- const patch = (fn) => {
1203
- const boundFn = fn.bind(containerRuntime);
1204
- return async (...args) => {
1205
- // Submit an op and yield for it to be flushed from outbox to pending state manager.
1206
- submitDataStoreOp(containerRuntime, "fakeId", "fakeContents");
1207
- await yieldEventLoop();
1208
- return boundFn(...args);
1209
- };
1210
- };
1211
- containerRuntime.summarize = patch(containerRuntime.summarize);
1212
- const summarizeResult = await containerRuntime.submitSummary({
1213
- summaryLogger: createChildLogger(),
1214
- cancellationToken: neverCancelledSummaryToken,
1215
- latestSummaryRefSeqNum: 0,
1216
- });
1217
- assert(summarizeResult.stage === "base", "Summary did not fail");
1218
- assert.strictEqual(summarizeResult.error.message, "PendingOpsWhileSummarizing", "Summary did not fail with the right error");
1219
- assert.strictEqual(summarizeResult.error.beforeGenerate, false, "It should have failed after generating summary");
1220
- });
1221
- it("summary passes if pending ops are processed during pending op processing timeout", async () => {
1222
- const containerRuntimeWithSubmit = containerRuntime;
1223
- // Submit a rejoin op and yield for it to be flushed from outbox to pending state manager.
1224
- containerRuntimeWithSubmit.submit({
1225
- type: ContainerMessageType.Rejoin,
1226
- contents: undefined,
1227
- }, undefined, undefined);
1228
- await yieldEventLoop();
1229
- // Create a mock logger to validate that pending ops event is generated with correct params.
1230
- const mockLogger = new MockLogger();
1231
- const summaryLogger = createChildLogger({ logger: mockLogger });
1232
- const summarizeResultP = containerRuntime.submitSummary({
1233
- summaryLogger,
1234
- cancellationToken: neverCancelledSummaryToken,
1235
- latestSummaryRefSeqNum: 0,
1236
- });
1237
- // Advance the clock by 1 ms less than the time waited for pending ops to be processed. This will allow
1238
- // summarization to proceed far enough to wait for pending ops.
1239
- clock.tick(defaultPendingOpsWaitTimeoutMs - 1);
1240
- // Process the rejoin op so that there are no pending ops.
1241
- containerRuntime.process({
1242
- type: "op",
1243
- clientId: "fakeClientId",
1244
- sequenceNumber: 0,
1245
- contents: {
1246
- type: ContainerMessageType.Rejoin,
1247
- contents: "something",
1248
- },
1249
- }, true /* local */);
1250
- // Advance the clock by the remaining time so that pending ops wait is completed.
1251
- clock.tick(1);
1252
- const summarizeResult = await summarizeResultP;
1253
- assert(summarizeResult.stage === "submit", "Summary did not succeed");
1254
- mockLogger.assertMatch([
1255
- {
1256
- eventName: "PendingOpsWhileSummarizing",
1257
- countBefore: 1,
1258
- countAfter: 0,
1259
- saved: true,
1260
- },
1261
- ]);
1262
- });
1263
- });
1264
- describe("GetPendingState", () => {
1265
- it("No Props. No pending state", async () => {
1266
- const logger = new MockLogger();
1267
- const containerRuntime = await ContainerRuntime.loadRuntime({
1268
- context: getMockContext({}, logger),
1269
- registryEntries: [],
1270
- existing: false,
1271
- runtimeOptions: {
1272
- flushMode: FlushMode.TurnBased,
1273
- enableRuntimeIdCompressor: "on",
1274
- },
1275
- provideEntryPoint: mockProvideEntryPoint,
1276
- });
1277
- const mockPendingStateManager = new Proxy({}, {
1278
- get: (_t, p, _r) => {
1279
- switch (p) {
1280
- case "getLocalState":
1281
- return () => undefined;
1282
- case "pendingMessagesCount":
1283
- return 0;
1284
- default:
1285
- assert.fail(`unexpected access to pendingStateManager.${p}`);
1286
- }
1287
- },
1288
- });
1289
- containerRuntime.pendingStateManager = mockPendingStateManager;
1290
- const state = containerRuntime.getPendingLocalState();
1291
- assert.strictEqual(state, undefined);
1292
- });
1293
- it("No Props. Some pending state", async () => {
1294
- const logger = new MockLogger();
1295
- const containerRuntime = await ContainerRuntime.loadRuntime({
1296
- context: getMockContext({}, logger),
1297
- registryEntries: [],
1298
- existing: false,
1299
- runtimeOptions: {
1300
- flushMode: FlushMode.TurnBased,
1301
- enableRuntimeIdCompressor: "on",
1302
- },
1303
- provideEntryPoint: mockProvideEntryPoint,
1304
- });
1305
- const pendingStates = Array.from({ length: 5 }).map((_, i) => ({
1306
- content: i.toString(),
1307
- type: "message",
1308
- referenceSequenceNumber: 0,
1309
- localOpMetadata: undefined,
1310
- opMetadata: undefined,
1311
- }));
1312
- const mockPendingStateManager = new Proxy({}, {
1313
- get: (_t, p, _r) => {
1314
- switch (p) {
1315
- case "getLocalState":
1316
- return () => ({
1317
- pendingStates,
1318
- });
1319
- case "pendingMessagesCount":
1320
- return 5;
1321
- default:
1322
- assert.fail(`unexpected access to pendingStateManager.${p}`);
1323
- }
1324
- },
1325
- });
1326
- containerRuntime.pendingStateManager = mockPendingStateManager;
1327
- const state = containerRuntime.getPendingLocalState();
1328
- assert.strictEqual(typeof state, "object");
1329
- assert.strictEqual(state.pending?.pendingStates, pendingStates);
1330
- });
1331
- it("notifyImminentClosure. Some pending state", async () => {
1332
- const logger = new MockLogger();
1333
- const containerRuntime = await ContainerRuntime.loadRuntime({
1334
- context: getMockContext({}, logger),
1335
- registryEntries: [],
1336
- existing: false,
1337
- runtimeOptions: {
1338
- flushMode: FlushMode.TurnBased,
1339
- enableRuntimeIdCompressor: "on",
1340
- },
1341
- provideEntryPoint: mockProvideEntryPoint,
1342
- });
1343
- const pendingStates = Array.from({ length: 5 }).map((_, i) => ({
1344
- content: i.toString(),
1345
- type: "message",
1346
- referenceSequenceNumber: 0,
1347
- localOpMetadata: undefined,
1348
- opMetadata: undefined,
1349
- }));
1350
- const mockPendingStateManager = new Proxy({}, {
1351
- get: (_t, p, _r) => {
1352
- switch (p) {
1353
- case "getLocalState":
1354
- return () => ({
1355
- pendingStates,
1356
- });
1357
- case "pendingMessagesCount":
1358
- return 5;
1359
- default:
1360
- assert.fail(`unexpected access to pendingStateManager.${p}`);
1361
- }
1362
- },
1363
- });
1364
- containerRuntime.pendingStateManager = mockPendingStateManager;
1365
- const stateP = containerRuntime.getPendingLocalState({
1366
- notifyImminentClosure: true,
1367
- });
1368
- assert("then" in stateP, "should be a promise like");
1369
- const state = await stateP;
1370
- assert.strictEqual(typeof state, "object");
1371
- assert.strictEqual(state.pending?.pendingStates, pendingStates);
1372
- });
1373
- });
1374
- describe("Load Partial Snapshot with datastores with GroupId", () => {
1375
- let snapshotWithContents;
1376
- let blobContents;
1377
- let ops;
1378
- let containerRuntime;
1379
- let containerContext;
1380
- let entryDefault;
1381
- let snapshotTree;
1382
- let missingDataStoreRuntime;
1383
- beforeEach(async () => {
1384
- snapshotTree = {
1385
- id: "SnapshotId",
1386
- blobs: { ".metadata": "bARD4RKvW4LL1KmaUKp6hUMSp" },
1387
- trees: {
1388
- ".channels": {
1389
- blobs: {},
1390
- trees: {
1391
- default: {
1392
- blobs: {
1393
- ".component": "bARC6dCXlcrPxQHw3PeROtmKc",
1394
- },
1395
- trees: {
1396
- ".channels": {
1397
- blobs: {},
1398
- trees: {
1399
- root: { blobs: {}, trees: {} },
1400
- },
1401
- },
1402
- },
1403
- },
1404
- },
1405
- unreferenced: true,
1406
- },
1407
- ".blobs": { blobs: {}, trees: {} },
1408
- "gc": {
1409
- id: "e8ed0760ac37fd8042020559779ce80b1d88f266",
1410
- blobs: {
1411
- __gc_root: "018d97818f8b519f99c418cb3c33ce5cc4e38e3f",
1412
- },
1413
- trees: {},
1414
- },
1415
- },
1416
- };
1417
- blobContents = new Map([
1418
- [
1419
- "bARD4RKvW4LL1KmaUKp6hUMSp",
1420
- stringToBuffer(JSON.stringify({ summaryFormatVersion: 1, gcFeature: 3 }), "utf8"),
1421
- ],
1422
- [
1423
- "bARC6dCXlcrPxQHw3PeROtmKc",
1424
- stringToBuffer(JSON.stringify({
1425
- pkg: '["@fluid-example/smde"]',
1426
- summaryFormatVersion: 2,
1427
- isRootDataStore: true,
1428
- }), "utf8"),
1429
- ],
1430
- [
1431
- "018d97818f8b519f99c418cb3c33ce5cc4e38e3f",
1432
- stringToBuffer(JSON.parse(JSON.stringify('{"gcNodes":{"/":{"outboundRoutes":["/rootDOId"]},"/rootDOId":{"outboundRoutes":["/rootDOId/de68ca53-be31-479e-8d34-a267958997e4","/rootDOId/root"]},"/rootDOId/de68ca53-be31-479e-8d34-a267958997e4":{"outboundRoutes":["/rootDOId"]},"/rootDOId/root":{"outboundRoutes":["/rootDOId","/rootDOId/de68ca53-be31-479e-8d34-a267958997e4"]}}}')), "utf8"),
1433
- ],
1434
- ]);
1435
- ops = [
1436
- {
1437
- clientId: "X",
1438
- clientSequenceNumber: -1,
1439
- contents: null,
1440
- minimumSequenceNumber: 0,
1441
- referenceSequenceNumber: -1,
1442
- sequenceNumber: 1,
1443
- timestamp: 1623883807452,
1444
- type: "join",
1445
- },
1446
- {
1447
- clientId: "Y",
1448
- clientSequenceNumber: -1,
1449
- contents: null,
1450
- minimumSequenceNumber: 0,
1451
- referenceSequenceNumber: -1,
1452
- sequenceNumber: 2,
1453
- timestamp: 1623883811928,
1454
- type: "join",
1455
- },
1456
- ];
1457
- snapshotWithContents = {
1458
- blobContents,
1459
- ops,
1460
- snapshotTree,
1461
- sequenceNumber: 0,
1462
- snapshotFormatV: 1,
1463
- latestSequenceNumber: 2,
1464
- };
1465
- const logger = new MockLogger();
1466
- containerContext = getMockContext({}, logger);
1467
- containerContext.snapshotWithContents = snapshotWithContents;
1468
- containerContext.baseSnapshot = snapshotWithContents.snapshotTree;
1469
- containerContext.storage.readBlob = async (id) => {
1470
- return blobContents.get(id);
1471
- };
1472
- missingDataStoreRuntime = new MockFluidDataStoreRuntime();
1473
- const entryA = createDataStoreRegistryEntry([]);
1474
- const entryB = createDataStoreRegistryEntry([]);
1475
- entryDefault = createDataStoreRegistryEntry([
1476
- ["default", Promise.resolve(entryA)],
1477
- ["missingDataStore", Promise.resolve(entryB)],
1478
- ]);
1479
- logger.clear();
1480
- });
1481
- function createSnapshot(addMissindDatasore, setGroupId = true) {
1482
- if (addMissindDatasore) {
1483
- snapshotTree.trees[".channels"].trees.missingDataStore = {
1484
- blobs: {},
1485
- trees: {},
1486
- groupId: setGroupId ? "G1" : undefined,
1487
- omitted: true,
1488
- };
1489
- }
1490
- }
1491
- // Helper function that creates a FluidDataStoreRegistryEntry with the registry entries
1492
- // provided to it.
1493
- function createDataStoreRegistryEntry(entries) {
1494
- const registryEntries = new Map(entries);
1495
- const factory = {
1496
- type: "store-type",
1497
- get IFluidDataStoreFactory() {
1498
- return factory;
1499
- },
1500
- instantiateDataStore: async (context) => {
1501
- if (context.id === "missingDataStore") {
1502
- return missingDataStoreRuntime;
1503
- }
1504
- return new MockFluidDataStoreRuntime();
1505
- },
1506
- };
1507
- const registry = {
1508
- get IFluidDataStoreRegistry() {
1509
- return registry;
1510
- },
1511
- // Returns the registry entry as per the entries provided in the param.
1512
- get: async (pkg) => registryEntries.get(pkg),
1513
- };
1514
- const entry = {
1515
- get IFluidDataStoreFactory() {
1516
- return factory;
1517
- },
1518
- get IFluidDataStoreRegistry() {
1519
- return registry;
1520
- },
1521
- };
1522
- return entry;
1523
- }
1524
- it("Load snapshot with missing snapshot contents for datastores should fail when groupId not specified", async () => {
1525
- // In this test we will try to load the container runtime with a snapshot which has 2 datastores. However,
1526
- // snapshot for datastore "missingDataStore" is omitted and we will check that the container runtime loads fine
1527
- // but the "missingDataStore" is aliased, it fails if the snapshot for it does not have loadingGroupId to fetch
1528
- // the omitted snapshot contents.
1529
- createSnapshot(true /* addMissingDatastore */, false /* Don't set groupId property */);
1530
- containerRuntime = await ContainerRuntime.loadRuntime({
1531
- context: containerContext,
1532
- registryEntries: [["@fluid-example/smde", Promise.resolve(entryDefault)]],
1533
- existing: true,
1534
- runtimeOptions: {
1535
- flushMode: FlushMode.TurnBased,
1536
- enableRuntimeIdCompressor: "on",
1537
- },
1538
- provideEntryPoint: mockProvideEntryPoint,
1539
- });
1540
- const defaultDataStore = await containerRuntime.getAliasedDataStoreEntryPoint("default");
1541
- assert(defaultDataStore !== undefined, "data store should load and is attached");
1542
- await assert.rejects(async () => {
1543
- await containerRuntime.getAliasedDataStoreEntryPoint("missingDataStore");
1544
- }, (err) => {
1545
- validateAssertionError(err, "groupId should be present to fetch snapshot");
1546
- return true;
1547
- });
1548
- });
1549
- it("Load snapshot with missing snapshot contents for datastores should fail for summarizer in case group snapshot is ahead of initial snapshot seq number", async () => {
1550
- // In this test we will try to load the container runtime with a snapshot which has 2 datastores. However,
1551
- // snapshot for datastore "missingDataStore" is omitted and we will check that the container runtime loads fine
1552
- // but the "missingDataStore" is requested/aliased, it fails to because for summarizer the fetched snapshot could
1553
- // not be ahead of the base snapshot as that means that a snapshot is missing and the summarizer is not up to date.
1554
- containerContext.storage.getSnapshot = async (snapshotFetchOptions) => {
1555
- snapshotTree.trees[".channels"].trees.missingDataStore = {
1556
- blobs: {
1557
- ".component": "bARC6dCXlcrPxQHw3PeROtmKc",
1558
- },
1559
- trees: {
1560
- ".channels": {
1561
- blobs: {},
1562
- trees: {
1563
- root: { blobs: {}, trees: {} },
1564
- },
1565
- },
1566
- },
1567
- };
1568
- snapshotWithContents.sequenceNumber = 10;
1569
- return snapshotWithContents;
1570
- };
1571
- createSnapshot(true /* addMissingDatastore */);
1572
- containerContext.clientDetails.type = "summarizer";
1573
- containerRuntime = await ContainerRuntime.loadRuntime({
1574
- context: containerContext,
1575
- registryEntries: [["@fluid-example/smde", Promise.resolve(entryDefault)]],
1576
- existing: true,
1577
- runtimeOptions: {
1578
- flushMode: FlushMode.TurnBased,
1579
- enableRuntimeIdCompressor: "on",
1580
- },
1581
- provideEntryPoint: mockProvideEntryPoint,
1582
- });
1583
- const defaultDataStore = await containerRuntime.getAliasedDataStoreEntryPoint("default");
1584
- assert(defaultDataStore !== undefined, "data store should load and is attached");
1585
- await assert.rejects(async () => {
1586
- await containerRuntime.getAliasedDataStoreEntryPoint("missingDataStore");
1587
- }, (err) => {
1588
- assert(err.message ===
1589
- "Summarizer client behind, loaded newer snapshot with loadingGroupId", "summarizer client is behind");
1590
- return true;
1591
- });
1592
- });
1593
- it("Load snapshot with missing snapshot contents for datastores should load properly", async () => {
1594
- // In this test we will try to load the container runtime with a snapshot which has 2 datastores. However,
1595
- // snapshot for datastore "missingDataStore" is omitted and we will check that the container runtime loads fine
1596
- // container runtime loads fine and when the "missingDataStore" is requested/aliased, it does that successfully.
1597
- let getSnapshotCalledTimes = 0;
1598
- containerContext.storage.getSnapshot = async (snapshotFetchOptions) => {
1599
- getSnapshotCalledTimes++;
1600
- snapshotTree.trees[".channels"].trees.missingDataStore = {
1601
- blobs: {
1602
- ".component": "bARC6dCXlcrPxQHw3PeROtmKc",
1603
- },
1604
- trees: {
1605
- ".channels": {
1606
- blobs: {},
1607
- trees: {
1608
- root: { blobs: {}, trees: {} },
1609
- },
1610
- },
1611
- },
1612
- };
1613
- return snapshotWithContents;
1614
- };
1615
- createSnapshot(true /* addMissingDatastore */);
1616
- containerRuntime = await ContainerRuntime.loadRuntime({
1617
- context: containerContext,
1618
- registryEntries: [["@fluid-example/smde", Promise.resolve(entryDefault)]],
1619
- existing: true,
1620
- runtimeOptions: {
1621
- flushMode: FlushMode.TurnBased,
1622
- enableRuntimeIdCompressor: "on",
1623
- },
1624
- provideEntryPoint: mockProvideEntryPoint,
1625
- });
1626
- const defaultDataStore = await containerRuntime.getAliasedDataStoreEntryPoint("default");
1627
- assert(defaultDataStore !== undefined, "data store should load and is attached");
1628
- await assert.doesNotReject(async () => {
1629
- await containerRuntime.resolveHandle({ url: "/missingDataStore" });
1630
- }, "resolveHandle should work fine");
1631
- // Now try to get snapshot for missing data store again from container runtime. It should be returned
1632
- // from cache.
1633
- const snapshot = await containerRuntime.getSnapshotForLoadingGroupId(["G1"], ["missingDataStore"]);
1634
- assert.deepStrictEqual(snapshotTree.trees[".channels"].trees.missingDataStore, snapshot.snapshotTree, "snapshot should be equal");
1635
- assert(getSnapshotCalledTimes === 1, "second time should be from cache");
1636
- // Set api to undefined to see that it should not be called again.
1637
- containerContext.storage.getSnapshot = undefined;
1638
- await assert.doesNotReject(async () => {
1639
- await containerRuntime.resolveHandle({ url: "/missingDataStore" });
1640
- }, "resolveHandle should work fine");
1641
- });
1642
- it("Load snapshot with missing snapshot contents for datastores should work in case group snapshot is ahead of initial snapshot seq number", async () => {
1643
- // In this test we will try to load the container runtime with a snapshot which has 2 datastores. However,
1644
- // snapshot for datastore "missingDataStore" is omitted and we will check that the container runtime loads fine
1645
- // and the container runtime waits for delta manager to reach snapshot seq number before returning the snapshot.
1646
- containerContext.storage.getSnapshot = async (snapshotFetchOptions) => {
1647
- snapshotTree.trees[".channels"].trees.missingDataStore = {
1648
- blobs: {
1649
- ".component": "bARC6dCXlcrPxQHw3PeROtmKc",
1650
- },
1651
- trees: {
1652
- ".channels": {
1653
- blobs: {},
1654
- trees: {
1655
- root: { blobs: {}, trees: {} },
1656
- },
1657
- },
1658
- },
1659
- };
1660
- snapshotWithContents.sequenceNumber = 5;
1661
- return snapshotWithContents;
1662
- };
1663
- createSnapshot(true /* addMissingDatastore */);
1664
- containerRuntime = await ContainerRuntime.loadRuntime({
1665
- context: containerContext,
1666
- registryEntries: [["@fluid-example/smde", Promise.resolve(entryDefault)]],
1667
- existing: true,
1668
- runtimeOptions: {
1669
- flushMode: FlushMode.TurnBased,
1670
- enableRuntimeIdCompressor: "on",
1671
- },
1672
- provideEntryPoint: mockProvideEntryPoint,
1673
- });
1674
- const defaultDataStore = await containerRuntime.getAliasedDataStoreEntryPoint("default");
1675
- assert(defaultDataStore !== undefined, "data store should load and is attached");
1676
- // Set it to seq number of partial fetched snapshot so that it is returned successfully by container runtime.
1677
- containerContext.deltaManager.lastSequenceNumber = 5;
1678
- await assert.doesNotReject(async () => {
1679
- await containerRuntime.resolveHandle({ url: "/missingDataStore" });
1680
- }, "resolveHandle should work fine");
1681
- });
1682
- it("Load snapshot with missing snapshot contents for datastores should only process ops in datastore context which are after the snapshot seq number", async () => {
1683
- // In this test we will try to load the container runtime with a snapshot which has 2 datastores. However,
1684
- // snapshot for datastore "missingDataStore" is omitted and we will check that the container runtime loads fine
1685
- // and the data store context only process ops which are after the snapshot seq number.
1686
- containerContext.storage.getSnapshot = async (snapshotFetchOptions) => {
1687
- snapshotTree.trees[".channels"].trees.missingDataStore = {
1688
- blobs: {
1689
- ".component": "bARC6dCXlcrPxQHw3PeROtmKc",
1690
- },
1691
- trees: {
1692
- ".channels": {
1693
- blobs: {},
1694
- trees: {
1695
- root: { blobs: {}, trees: {} },
1696
- },
1697
- },
1698
- },
1699
- };
1700
- snapshotWithContents.sequenceNumber = 2;
1701
- return snapshotWithContents;
1702
- };
1703
- createSnapshot(true /* addMissingDatastore */);
1704
- containerRuntime = await ContainerRuntime.loadRuntime({
1705
- context: containerContext,
1706
- registryEntries: [["@fluid-example/smde", Promise.resolve(entryDefault)]],
1707
- existing: true,
1708
- runtimeOptions: {
1709
- flushMode: FlushMode.TurnBased,
1710
- enableRuntimeIdCompressor: "on",
1711
- },
1712
- provideEntryPoint: mockProvideEntryPoint,
1713
- });
1714
- const defaultDataStore = await containerRuntime.getAliasedDataStoreEntryPoint("default");
1715
- assert(defaultDataStore !== undefined, "data store should load and is attached");
1716
- const missingDataStoreContext =
1717
- // eslint-disable-next-line @typescript-eslint/dot-notation
1718
- containerRuntime["channelCollection"]["contexts"].get("missingDataStore");
1719
- assert(missingDataStoreContext !== undefined, "context should be there");
1720
- // Add ops to this context.
1721
- const messages = [
1722
- { sequenceNumber: 1 },
1723
- { sequenceNumber: 2 },
1724
- { sequenceNumber: 3 },
1725
- { sequenceNumber: 4 },
1726
- ];
1727
- // eslint-disable-next-line @typescript-eslint/dot-notation
1728
- missingDataStoreContext["pending"] = messages;
1729
- // Set it to seq number of partial fetched snapshot so that it is returned successfully by container runtime.
1730
- containerContext.deltaManager.lastSequenceNumber = 2;
1731
- let opsProcessed = 0;
1732
- let opsStart;
1733
- missingDataStoreRuntime.process = (message, local, localOpMetadata) => {
1734
- if (opsProcessed === 0) {
1735
- opsStart = message.sequenceNumber;
1736
- }
1737
- opsProcessed++;
1738
- };
1739
- await assert.doesNotReject(async () => {
1740
- await containerRuntime.resolveHandle({ url: "/missingDataStore" });
1741
- }, "resolveHandle should work fine");
1742
- assert(opsProcessed === 2, "only 2 ops should be processed with seq number 3 and 4");
1743
- assert(opsStart === 3, "first op processed should have seq number 3");
1744
- });
1745
- });
1746
- });
1747
- });
1748
- //# sourceMappingURL=containerRuntime.spec.js.map