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

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 (465) hide show
  1. package/dist/blobManager.d.ts +24 -11
  2. package/dist/blobManager.d.ts.map +1 -1
  3. package/dist/blobManager.js +112 -55
  4. package/dist/blobManager.js.map +1 -1
  5. package/dist/containerRuntime.d.ts +60 -75
  6. package/dist/containerRuntime.d.ts.map +1 -1
  7. package/dist/containerRuntime.js +295 -256
  8. package/dist/containerRuntime.js.map +1 -1
  9. package/dist/dataStoreContext.d.ts +39 -13
  10. package/dist/dataStoreContext.d.ts.map +1 -1
  11. package/dist/dataStoreContext.js +112 -49
  12. package/dist/dataStoreContext.js.map +1 -1
  13. package/dist/dataStores.d.ts +28 -4
  14. package/dist/dataStores.d.ts.map +1 -1
  15. package/dist/dataStores.js +107 -41
  16. package/dist/dataStores.js.map +1 -1
  17. package/dist/deltaManagerSummarizerProxy.d.ts +19 -0
  18. package/dist/deltaManagerSummarizerProxy.d.ts.map +1 -0
  19. package/dist/deltaManagerSummarizerProxy.js +40 -0
  20. package/dist/deltaManagerSummarizerProxy.js.map +1 -0
  21. package/dist/gc/garbageCollection.d.ts +204 -0
  22. package/dist/gc/garbageCollection.d.ts.map +1 -0
  23. package/dist/{garbageCollection.js → gc/garbageCollection.js} +190 -554
  24. package/dist/gc/garbageCollection.js.map +1 -0
  25. package/dist/gc/gcConfigs.d.ts +22 -0
  26. package/dist/gc/gcConfigs.d.ts.map +1 -0
  27. package/dist/gc/gcConfigs.js +143 -0
  28. package/dist/gc/gcConfigs.js.map +1 -0
  29. package/dist/gc/gcDefinitions.d.ts +320 -0
  30. package/dist/gc/gcDefinitions.d.ts.map +1 -0
  31. package/dist/gc/gcDefinitions.js +81 -0
  32. package/dist/gc/gcDefinitions.js.map +1 -0
  33. package/dist/gc/gcHelpers.d.ts +86 -0
  34. package/dist/gc/gcHelpers.d.ts.map +1 -0
  35. package/dist/gc/gcHelpers.js +268 -0
  36. package/dist/gc/gcHelpers.js.map +1 -0
  37. package/dist/gc/gcReferenceGraphAlgorithm.d.ts +16 -0
  38. package/dist/gc/gcReferenceGraphAlgorithm.d.ts.map +1 -0
  39. package/dist/gc/gcReferenceGraphAlgorithm.js +49 -0
  40. package/dist/gc/gcReferenceGraphAlgorithm.js.map +1 -0
  41. package/dist/gc/gcSummaryDefinitions.d.ts +52 -0
  42. package/dist/gc/gcSummaryDefinitions.d.ts.map +1 -0
  43. package/dist/gc/gcSummaryDefinitions.js +7 -0
  44. package/dist/gc/gcSummaryDefinitions.js.map +1 -0
  45. package/dist/gc/gcSummaryStateTracker.d.ts +93 -0
  46. package/dist/gc/gcSummaryStateTracker.d.ts.map +1 -0
  47. package/dist/gc/gcSummaryStateTracker.js +239 -0
  48. package/dist/gc/gcSummaryStateTracker.js.map +1 -0
  49. package/dist/gc/gcSweepReadyUsageDetection.d.ts.map +1 -0
  50. package/dist/{gcSweepReadyUsageDetection.js → gc/gcSweepReadyUsageDetection.js} +2 -2
  51. package/dist/gc/gcSweepReadyUsageDetection.js.map +1 -0
  52. package/dist/gc/gcUnreferencedStateTracker.d.ts +34 -0
  53. package/dist/gc/gcUnreferencedStateTracker.d.ts.map +1 -0
  54. package/dist/gc/gcUnreferencedStateTracker.js +94 -0
  55. package/dist/gc/gcUnreferencedStateTracker.js.map +1 -0
  56. package/dist/gc/index.d.ts +13 -0
  57. package/dist/gc/index.d.ts.map +1 -0
  58. package/dist/gc/index.js +50 -0
  59. package/dist/gc/index.js.map +1 -0
  60. package/dist/index.d.ts +3 -7
  61. package/dist/index.d.ts.map +1 -1
  62. package/dist/index.js +5 -9
  63. package/dist/index.js.map +1 -1
  64. package/dist/opLifecycle/batchManager.d.ts +11 -13
  65. package/dist/opLifecycle/batchManager.d.ts.map +1 -1
  66. package/dist/opLifecycle/batchManager.js +26 -38
  67. package/dist/opLifecycle/batchManager.js.map +1 -1
  68. package/dist/opLifecycle/definitions.d.ts +4 -0
  69. package/dist/opLifecycle/definitions.d.ts.map +1 -1
  70. package/dist/opLifecycle/definitions.js.map +1 -1
  71. package/dist/opLifecycle/index.d.ts +1 -1
  72. package/dist/opLifecycle/index.d.ts.map +1 -1
  73. package/dist/opLifecycle/index.js +2 -1
  74. package/dist/opLifecycle/index.js.map +1 -1
  75. package/dist/opLifecycle/opCompressor.d.ts.map +1 -1
  76. package/dist/opLifecycle/opCompressor.js +25 -10
  77. package/dist/opLifecycle/opCompressor.js.map +1 -1
  78. package/dist/opLifecycle/opDecompressor.d.ts +4 -0
  79. package/dist/opLifecycle/opDecompressor.d.ts.map +1 -1
  80. package/dist/opLifecycle/opDecompressor.js +42 -4
  81. package/dist/opLifecycle/opDecompressor.js.map +1 -1
  82. package/dist/opLifecycle/opSplitter.d.ts +15 -3
  83. package/dist/opLifecycle/opSplitter.d.ts.map +1 -1
  84. package/dist/opLifecycle/opSplitter.js +35 -10
  85. package/dist/opLifecycle/opSplitter.js.map +1 -1
  86. package/dist/opLifecycle/outbox.d.ts +19 -3
  87. package/dist/opLifecycle/outbox.d.ts.map +1 -1
  88. package/dist/opLifecycle/outbox.js +88 -49
  89. package/dist/opLifecycle/outbox.js.map +1 -1
  90. package/dist/packageVersion.d.ts +1 -1
  91. package/dist/packageVersion.js +1 -1
  92. package/dist/packageVersion.js.map +1 -1
  93. package/dist/pendingStateManager.d.ts +3 -3
  94. package/dist/pendingStateManager.d.ts.map +1 -1
  95. package/dist/pendingStateManager.js +20 -21
  96. package/dist/pendingStateManager.js.map +1 -1
  97. package/dist/storageServiceWithAttachBlobs.d.ts +17 -0
  98. package/dist/storageServiceWithAttachBlobs.d.ts.map +1 -0
  99. package/dist/storageServiceWithAttachBlobs.js +32 -0
  100. package/dist/storageServiceWithAttachBlobs.js.map +1 -0
  101. package/dist/summary/index.d.ts +17 -0
  102. package/dist/summary/index.d.ts.map +1 -0
  103. package/dist/summary/index.js +46 -0
  104. package/dist/summary/index.js.map +1 -0
  105. package/dist/summary/orderedClientElection.d.ts.map +1 -0
  106. package/dist/summary/orderedClientElection.js.map +1 -0
  107. package/dist/{runWhileConnectedCoordinator.d.ts → summary/runWhileConnectedCoordinator.d.ts} +3 -2
  108. package/dist/summary/runWhileConnectedCoordinator.d.ts.map +1 -0
  109. package/dist/{runWhileConnectedCoordinator.js → summary/runWhileConnectedCoordinator.js} +5 -4
  110. package/dist/summary/runWhileConnectedCoordinator.js.map +1 -0
  111. package/{lib → dist/summary}/runningSummarizer.d.ts +19 -18
  112. package/dist/summary/runningSummarizer.d.ts.map +1 -0
  113. package/dist/{runningSummarizer.js → summary/runningSummarizer.js} +158 -56
  114. package/dist/summary/runningSummarizer.js.map +1 -0
  115. package/dist/{summarizer.d.ts → summary/summarizer.d.ts} +4 -9
  116. package/dist/summary/summarizer.d.ts.map +1 -0
  117. package/dist/{summarizer.js → summary/summarizer.js} +9 -74
  118. package/dist/summary/summarizer.js.map +1 -0
  119. package/dist/summary/summarizerClientElection.d.ts.map +1 -0
  120. package/dist/summary/summarizerClientElection.js.map +1 -0
  121. package/{lib → dist/summary}/summarizerHeuristics.d.ts +1 -1
  122. package/dist/summary/summarizerHeuristics.d.ts.map +1 -0
  123. package/dist/{summarizerHeuristics.js → summary/summarizerHeuristics.js} +3 -3
  124. package/dist/summary/summarizerHeuristics.js.map +1 -0
  125. package/dist/summary/summarizerNode/index.d.ts +8 -0
  126. package/dist/summary/summarizerNode/index.d.ts.map +1 -0
  127. package/dist/summary/summarizerNode/index.js +12 -0
  128. package/dist/summary/summarizerNode/index.js.map +1 -0
  129. package/dist/summary/summarizerNode/summarizerNode.d.ts +149 -0
  130. package/dist/summary/summarizerNode/summarizerNode.d.ts.map +1 -0
  131. package/dist/summary/summarizerNode/summarizerNode.js +531 -0
  132. package/dist/summary/summarizerNode/summarizerNode.js.map +1 -0
  133. package/dist/summary/summarizerNode/summarizerNodeUtils.d.ts +125 -0
  134. package/dist/summary/summarizerNode/summarizerNodeUtils.d.ts.map +1 -0
  135. package/dist/summary/summarizerNode/summarizerNodeUtils.js +132 -0
  136. package/dist/summary/summarizerNode/summarizerNodeUtils.js.map +1 -0
  137. package/dist/summary/summarizerNode/summarizerNodeWithGc.d.ts +22 -0
  138. package/dist/summary/summarizerNode/summarizerNodeWithGc.d.ts.map +1 -0
  139. package/dist/summary/summarizerNode/summarizerNodeWithGc.js +423 -0
  140. package/dist/summary/summarizerNode/summarizerNodeWithGc.js.map +1 -0
  141. package/{lib → dist/summary}/summarizerTypes.d.ts +7 -17
  142. package/dist/summary/summarizerTypes.d.ts.map +1 -0
  143. package/dist/{summarizerTypes.js → summary/summarizerTypes.js} +0 -5
  144. package/dist/summary/summarizerTypes.js.map +1 -0
  145. package/dist/summary/summaryCollection.d.ts.map +1 -0
  146. package/dist/summary/summaryCollection.js.map +1 -0
  147. package/{lib → dist/summary}/summaryFormat.d.ts +3 -21
  148. package/dist/summary/summaryFormat.d.ts.map +1 -0
  149. package/dist/{summaryFormat.js → summary/summaryFormat.js} +1 -10
  150. package/dist/summary/summaryFormat.js.map +1 -0
  151. package/dist/summary/summaryGenerator.d.ts.map +1 -0
  152. package/dist/{summaryGenerator.js → summary/summaryGenerator.js} +4 -4
  153. package/dist/summary/summaryGenerator.js.map +1 -0
  154. package/dist/{summaryManager.d.ts → summary/summaryManager.d.ts} +1 -1
  155. package/dist/summary/summaryManager.d.ts.map +1 -0
  156. package/dist/summary/summaryManager.js.map +1 -0
  157. package/lib/blobManager.d.ts +24 -11
  158. package/lib/blobManager.d.ts.map +1 -1
  159. package/lib/blobManager.js +109 -52
  160. package/lib/blobManager.js.map +1 -1
  161. package/lib/containerRuntime.d.ts +60 -75
  162. package/lib/containerRuntime.d.ts.map +1 -1
  163. package/lib/containerRuntime.js +267 -228
  164. package/lib/containerRuntime.js.map +1 -1
  165. package/lib/dataStoreContext.d.ts +39 -13
  166. package/lib/dataStoreContext.d.ts.map +1 -1
  167. package/lib/dataStoreContext.js +101 -38
  168. package/lib/dataStoreContext.js.map +1 -1
  169. package/lib/dataStores.d.ts +28 -4
  170. package/lib/dataStores.d.ts.map +1 -1
  171. package/lib/dataStores.js +100 -34
  172. package/lib/dataStores.js.map +1 -1
  173. package/lib/deltaManagerSummarizerProxy.d.ts +19 -0
  174. package/lib/deltaManagerSummarizerProxy.d.ts.map +1 -0
  175. package/lib/deltaManagerSummarizerProxy.js +36 -0
  176. package/lib/deltaManagerSummarizerProxy.js.map +1 -0
  177. package/lib/gc/garbageCollection.d.ts +204 -0
  178. package/lib/gc/garbageCollection.d.ts.map +1 -0
  179. package/lib/{garbageCollection.js → gc/garbageCollection.js} +172 -535
  180. package/lib/gc/garbageCollection.js.map +1 -0
  181. package/lib/gc/gcConfigs.d.ts +22 -0
  182. package/lib/gc/gcConfigs.d.ts.map +1 -0
  183. package/lib/gc/gcConfigs.js +139 -0
  184. package/lib/gc/gcConfigs.js.map +1 -0
  185. package/lib/gc/gcDefinitions.d.ts +320 -0
  186. package/lib/gc/gcDefinitions.d.ts.map +1 -0
  187. package/lib/gc/gcDefinitions.js +78 -0
  188. package/lib/gc/gcDefinitions.js.map +1 -0
  189. package/lib/gc/gcHelpers.d.ts +86 -0
  190. package/lib/gc/gcHelpers.d.ts.map +1 -0
  191. package/lib/gc/gcHelpers.js +254 -0
  192. package/lib/gc/gcHelpers.js.map +1 -0
  193. package/lib/gc/gcReferenceGraphAlgorithm.d.ts +16 -0
  194. package/lib/gc/gcReferenceGraphAlgorithm.d.ts.map +1 -0
  195. package/lib/gc/gcReferenceGraphAlgorithm.js +45 -0
  196. package/lib/gc/gcReferenceGraphAlgorithm.js.map +1 -0
  197. package/lib/gc/gcSummaryDefinitions.d.ts +52 -0
  198. package/lib/gc/gcSummaryDefinitions.d.ts.map +1 -0
  199. package/lib/gc/gcSummaryDefinitions.js +6 -0
  200. package/lib/gc/gcSummaryDefinitions.js.map +1 -0
  201. package/lib/gc/gcSummaryStateTracker.d.ts +93 -0
  202. package/lib/gc/gcSummaryStateTracker.d.ts.map +1 -0
  203. package/lib/gc/gcSummaryStateTracker.js +235 -0
  204. package/lib/gc/gcSummaryStateTracker.js.map +1 -0
  205. package/lib/gc/gcSweepReadyUsageDetection.d.ts.map +1 -0
  206. package/lib/{gcSweepReadyUsageDetection.js → gc/gcSweepReadyUsageDetection.js} +1 -1
  207. package/lib/gc/gcSweepReadyUsageDetection.js.map +1 -0
  208. package/lib/gc/gcUnreferencedStateTracker.d.ts +34 -0
  209. package/lib/gc/gcUnreferencedStateTracker.d.ts.map +1 -0
  210. package/lib/gc/gcUnreferencedStateTracker.js +90 -0
  211. package/lib/gc/gcUnreferencedStateTracker.js.map +1 -0
  212. package/lib/gc/index.d.ts +13 -0
  213. package/lib/gc/index.d.ts.map +1 -0
  214. package/lib/gc/index.js +12 -0
  215. package/lib/gc/index.js.map +1 -0
  216. package/lib/index.d.ts +3 -7
  217. package/lib/index.d.ts.map +1 -1
  218. package/lib/index.js +1 -4
  219. package/lib/index.js.map +1 -1
  220. package/lib/opLifecycle/batchManager.d.ts +11 -13
  221. package/lib/opLifecycle/batchManager.d.ts.map +1 -1
  222. package/lib/opLifecycle/batchManager.js +24 -37
  223. package/lib/opLifecycle/batchManager.js.map +1 -1
  224. package/lib/opLifecycle/definitions.d.ts +4 -0
  225. package/lib/opLifecycle/definitions.d.ts.map +1 -1
  226. package/lib/opLifecycle/definitions.js.map +1 -1
  227. package/lib/opLifecycle/index.d.ts +1 -1
  228. package/lib/opLifecycle/index.d.ts.map +1 -1
  229. package/lib/opLifecycle/index.js +1 -1
  230. package/lib/opLifecycle/index.js.map +1 -1
  231. package/lib/opLifecycle/opCompressor.d.ts.map +1 -1
  232. package/lib/opLifecycle/opCompressor.js +26 -11
  233. package/lib/opLifecycle/opCompressor.js.map +1 -1
  234. package/lib/opLifecycle/opDecompressor.d.ts +4 -0
  235. package/lib/opLifecycle/opDecompressor.d.ts.map +1 -1
  236. package/lib/opLifecycle/opDecompressor.js +42 -4
  237. package/lib/opLifecycle/opDecompressor.js.map +1 -1
  238. package/lib/opLifecycle/opSplitter.d.ts +15 -3
  239. package/lib/opLifecycle/opSplitter.d.ts.map +1 -1
  240. package/lib/opLifecycle/opSplitter.js +35 -10
  241. package/lib/opLifecycle/opSplitter.js.map +1 -1
  242. package/lib/opLifecycle/outbox.d.ts +19 -3
  243. package/lib/opLifecycle/outbox.d.ts.map +1 -1
  244. package/lib/opLifecycle/outbox.js +90 -51
  245. package/lib/opLifecycle/outbox.js.map +1 -1
  246. package/lib/packageVersion.d.ts +1 -1
  247. package/lib/packageVersion.js +1 -1
  248. package/lib/packageVersion.js.map +1 -1
  249. package/lib/pendingStateManager.d.ts +3 -3
  250. package/lib/pendingStateManager.d.ts.map +1 -1
  251. package/lib/pendingStateManager.js +20 -21
  252. package/lib/pendingStateManager.js.map +1 -1
  253. package/lib/storageServiceWithAttachBlobs.d.ts +17 -0
  254. package/lib/storageServiceWithAttachBlobs.d.ts.map +1 -0
  255. package/lib/storageServiceWithAttachBlobs.js +28 -0
  256. package/lib/storageServiceWithAttachBlobs.js.map +1 -0
  257. package/lib/summary/index.d.ts +17 -0
  258. package/lib/summary/index.d.ts.map +1 -0
  259. package/lib/summary/index.js +15 -0
  260. package/lib/summary/index.js.map +1 -0
  261. package/lib/summary/orderedClientElection.d.ts.map +1 -0
  262. package/lib/summary/orderedClientElection.js.map +1 -0
  263. package/lib/{runWhileConnectedCoordinator.d.ts → summary/runWhileConnectedCoordinator.d.ts} +3 -2
  264. package/lib/summary/runWhileConnectedCoordinator.d.ts.map +1 -0
  265. package/lib/{runWhileConnectedCoordinator.js → summary/runWhileConnectedCoordinator.js} +5 -4
  266. package/lib/summary/runWhileConnectedCoordinator.js.map +1 -0
  267. package/{dist → lib/summary}/runningSummarizer.d.ts +19 -18
  268. package/lib/summary/runningSummarizer.d.ts.map +1 -0
  269. package/lib/{runningSummarizer.js → summary/runningSummarizer.js} +159 -57
  270. package/lib/summary/runningSummarizer.js.map +1 -0
  271. package/lib/{summarizer.d.ts → summary/summarizer.d.ts} +4 -9
  272. package/lib/summary/summarizer.d.ts.map +1 -0
  273. package/lib/{summarizer.js → summary/summarizer.js} +11 -76
  274. package/lib/summary/summarizer.js.map +1 -0
  275. package/lib/summary/summarizerClientElection.d.ts.map +1 -0
  276. package/lib/summary/summarizerClientElection.js.map +1 -0
  277. package/{dist → lib/summary}/summarizerHeuristics.d.ts +1 -1
  278. package/lib/summary/summarizerHeuristics.d.ts.map +1 -0
  279. package/lib/{summarizerHeuristics.js → summary/summarizerHeuristics.js} +3 -3
  280. package/lib/summary/summarizerHeuristics.js.map +1 -0
  281. package/lib/summary/summarizerNode/index.d.ts +8 -0
  282. package/lib/summary/summarizerNode/index.d.ts.map +1 -0
  283. package/lib/summary/summarizerNode/index.js +7 -0
  284. package/lib/summary/summarizerNode/index.js.map +1 -0
  285. package/lib/summary/summarizerNode/summarizerNode.d.ts +149 -0
  286. package/lib/summary/summarizerNode/summarizerNode.d.ts.map +1 -0
  287. package/lib/summary/summarizerNode/summarizerNode.js +526 -0
  288. package/lib/summary/summarizerNode/summarizerNode.js.map +1 -0
  289. package/lib/summary/summarizerNode/summarizerNodeUtils.d.ts +125 -0
  290. package/lib/summary/summarizerNode/summarizerNodeUtils.d.ts.map +1 -0
  291. package/lib/summary/summarizerNode/summarizerNodeUtils.js +125 -0
  292. package/lib/summary/summarizerNode/summarizerNodeUtils.js.map +1 -0
  293. package/lib/summary/summarizerNode/summarizerNodeWithGc.d.ts +22 -0
  294. package/lib/summary/summarizerNode/summarizerNodeWithGc.d.ts.map +1 -0
  295. package/lib/summary/summarizerNode/summarizerNodeWithGc.js +419 -0
  296. package/lib/summary/summarizerNode/summarizerNodeWithGc.js.map +1 -0
  297. package/{dist → lib/summary}/summarizerTypes.d.ts +7 -17
  298. package/lib/summary/summarizerTypes.d.ts.map +1 -0
  299. package/lib/summary/summarizerTypes.js +6 -0
  300. package/{dist → lib/summary}/summarizerTypes.js.map +1 -1
  301. package/lib/summary/summaryCollection.d.ts.map +1 -0
  302. package/lib/summary/summaryCollection.js.map +1 -0
  303. package/{dist → lib/summary}/summaryFormat.d.ts +3 -21
  304. package/lib/summary/summaryFormat.d.ts.map +1 -0
  305. package/lib/{summaryFormat.js → summary/summaryFormat.js} +0 -8
  306. package/lib/summary/summaryFormat.js.map +1 -0
  307. package/lib/summary/summaryGenerator.d.ts.map +1 -0
  308. package/lib/{summaryGenerator.js → summary/summaryGenerator.js} +4 -4
  309. package/lib/summary/summaryGenerator.js.map +1 -0
  310. package/lib/{summaryManager.d.ts → summary/summaryManager.d.ts} +1 -1
  311. package/lib/summary/summaryManager.d.ts.map +1 -0
  312. package/lib/summary/summaryManager.js.map +1 -0
  313. package/package.json +63 -60
  314. package/src/blobManager.ts +132 -69
  315. package/src/containerRuntime.ts +421 -382
  316. package/src/dataStoreContext.ts +140 -49
  317. package/src/dataStores.ts +139 -41
  318. package/src/deltaManagerSummarizerProxy.ts +46 -0
  319. package/{garbageCollection.md → src/gc/garbageCollection.md} +2 -2
  320. package/src/{garbageCollection.ts → gc/garbageCollection.ts} +245 -890
  321. package/src/gc/gcConfigs.ts +193 -0
  322. package/src/gc/gcDefinitions.ts +387 -0
  323. package/src/gc/gcHelpers.ts +332 -0
  324. package/src/gc/gcReferenceGraphAlgorithm.ts +52 -0
  325. package/src/gc/gcSummaryDefinitions.ts +54 -0
  326. package/src/gc/gcSummaryStateTracker.ts +329 -0
  327. package/src/{gcSweepReadyUsageDetection.ts → gc/gcSweepReadyUsageDetection.ts} +1 -1
  328. package/src/gc/gcUnreferencedStateTracker.ts +114 -0
  329. package/src/gc/index.ts +65 -0
  330. package/src/index.ts +10 -22
  331. package/src/opLifecycle/README.md +157 -0
  332. package/src/opLifecycle/batchManager.ts +26 -55
  333. package/src/opLifecycle/definitions.ts +4 -0
  334. package/src/opLifecycle/index.ts +1 -1
  335. package/src/opLifecycle/opCompressor.ts +32 -12
  336. package/src/opLifecycle/opDecompressor.ts +49 -5
  337. package/src/opLifecycle/opSplitter.ts +55 -12
  338. package/src/opLifecycle/outbox.ts +120 -60
  339. package/src/packageVersion.ts +1 -1
  340. package/src/pendingStateManager.ts +34 -27
  341. package/src/storageServiceWithAttachBlobs.ts +38 -0
  342. package/src/summary/index.ts +105 -0
  343. package/src/{runWhileConnectedCoordinator.ts → summary/runWhileConnectedCoordinator.ts} +7 -7
  344. package/src/{runningSummarizer.ts → summary/runningSummarizer.ts} +279 -139
  345. package/src/{summarizer.ts → summary/summarizer.ts} +12 -97
  346. package/src/{summarizerHeuristics.ts → summary/summarizerHeuristics.ts} +9 -4
  347. package/src/summary/summarizerNode/index.ts +12 -0
  348. package/src/summary/summarizerNode/summarizerNode.ts +766 -0
  349. package/src/summary/summarizerNode/summarizerNodeUtils.ts +214 -0
  350. package/src/summary/summarizerNode/summarizerNodeWithGc.ts +644 -0
  351. package/src/{summarizerTypes.ts → summary/summarizerTypes.ts} +8 -22
  352. package/src/{summaryFormat.ts → summary/summaryFormat.ts} +3 -29
  353. package/src/{summaryGenerator.ts → summary/summaryGenerator.ts} +12 -11
  354. package/src/{summaryManager.ts → summary/summaryManager.ts} +1 -1
  355. package/dist/garbageCollection.d.ts +0 -411
  356. package/dist/garbageCollection.d.ts.map +0 -1
  357. package/dist/garbageCollection.js.map +0 -1
  358. package/dist/garbageCollectionConstants.d.ts +0 -23
  359. package/dist/garbageCollectionConstants.d.ts.map +0 -1
  360. package/dist/garbageCollectionConstants.js +0 -36
  361. package/dist/garbageCollectionConstants.js.map +0 -1
  362. package/dist/garbageCollectionHelpers.d.ts +0 -15
  363. package/dist/garbageCollectionHelpers.d.ts.map +0 -1
  364. package/dist/garbageCollectionHelpers.js +0 -27
  365. package/dist/garbageCollectionHelpers.js.map +0 -1
  366. package/dist/gcSweepReadyUsageDetection.d.ts.map +0 -1
  367. package/dist/gcSweepReadyUsageDetection.js.map +0 -1
  368. package/dist/orderedClientElection.d.ts.map +0 -1
  369. package/dist/orderedClientElection.js.map +0 -1
  370. package/dist/runWhileConnectedCoordinator.d.ts.map +0 -1
  371. package/dist/runWhileConnectedCoordinator.js.map +0 -1
  372. package/dist/runningSummarizer.d.ts.map +0 -1
  373. package/dist/runningSummarizer.js.map +0 -1
  374. package/dist/serializedSnapshotStorage.d.ts +0 -58
  375. package/dist/serializedSnapshotStorage.d.ts.map +0 -1
  376. package/dist/serializedSnapshotStorage.js +0 -110
  377. package/dist/serializedSnapshotStorage.js.map +0 -1
  378. package/dist/summarizer.d.ts.map +0 -1
  379. package/dist/summarizer.js.map +0 -1
  380. package/dist/summarizerClientElection.d.ts.map +0 -1
  381. package/dist/summarizerClientElection.js.map +0 -1
  382. package/dist/summarizerHandle.d.ts +0 -12
  383. package/dist/summarizerHandle.d.ts.map +0 -1
  384. package/dist/summarizerHandle.js +0 -22
  385. package/dist/summarizerHandle.js.map +0 -1
  386. package/dist/summarizerHeuristics.d.ts.map +0 -1
  387. package/dist/summarizerHeuristics.js.map +0 -1
  388. package/dist/summarizerTypes.d.ts.map +0 -1
  389. package/dist/summaryCollection.d.ts.map +0 -1
  390. package/dist/summaryCollection.js.map +0 -1
  391. package/dist/summaryFormat.d.ts.map +0 -1
  392. package/dist/summaryFormat.js.map +0 -1
  393. package/dist/summaryGenerator.d.ts.map +0 -1
  394. package/dist/summaryGenerator.js.map +0 -1
  395. package/dist/summaryManager.d.ts.map +0 -1
  396. package/dist/summaryManager.js.map +0 -1
  397. package/lib/garbageCollection.d.ts +0 -411
  398. package/lib/garbageCollection.d.ts.map +0 -1
  399. package/lib/garbageCollection.js.map +0 -1
  400. package/lib/garbageCollectionConstants.d.ts +0 -23
  401. package/lib/garbageCollectionConstants.d.ts.map +0 -1
  402. package/lib/garbageCollectionConstants.js +0 -33
  403. package/lib/garbageCollectionConstants.js.map +0 -1
  404. package/lib/garbageCollectionHelpers.d.ts +0 -15
  405. package/lib/garbageCollectionHelpers.d.ts.map +0 -1
  406. package/lib/garbageCollectionHelpers.js +0 -23
  407. package/lib/garbageCollectionHelpers.js.map +0 -1
  408. package/lib/gcSweepReadyUsageDetection.d.ts.map +0 -1
  409. package/lib/gcSweepReadyUsageDetection.js.map +0 -1
  410. package/lib/orderedClientElection.d.ts.map +0 -1
  411. package/lib/orderedClientElection.js.map +0 -1
  412. package/lib/runWhileConnectedCoordinator.d.ts.map +0 -1
  413. package/lib/runWhileConnectedCoordinator.js.map +0 -1
  414. package/lib/runningSummarizer.d.ts.map +0 -1
  415. package/lib/runningSummarizer.js.map +0 -1
  416. package/lib/serializedSnapshotStorage.d.ts +0 -58
  417. package/lib/serializedSnapshotStorage.d.ts.map +0 -1
  418. package/lib/serializedSnapshotStorage.js +0 -106
  419. package/lib/serializedSnapshotStorage.js.map +0 -1
  420. package/lib/summarizer.d.ts.map +0 -1
  421. package/lib/summarizer.js.map +0 -1
  422. package/lib/summarizerClientElection.d.ts.map +0 -1
  423. package/lib/summarizerClientElection.js.map +0 -1
  424. package/lib/summarizerHandle.d.ts +0 -12
  425. package/lib/summarizerHandle.d.ts.map +0 -1
  426. package/lib/summarizerHandle.js +0 -18
  427. package/lib/summarizerHandle.js.map +0 -1
  428. package/lib/summarizerHeuristics.d.ts.map +0 -1
  429. package/lib/summarizerHeuristics.js.map +0 -1
  430. package/lib/summarizerTypes.d.ts.map +0 -1
  431. package/lib/summarizerTypes.js +0 -9
  432. package/lib/summarizerTypes.js.map +0 -1
  433. package/lib/summaryCollection.d.ts.map +0 -1
  434. package/lib/summaryCollection.js.map +0 -1
  435. package/lib/summaryFormat.d.ts.map +0 -1
  436. package/lib/summaryFormat.js.map +0 -1
  437. package/lib/summaryGenerator.d.ts.map +0 -1
  438. package/lib/summaryGenerator.js.map +0 -1
  439. package/lib/summaryManager.d.ts.map +0 -1
  440. package/lib/summaryManager.js.map +0 -1
  441. package/src/garbageCollectionConstants.ts +0 -38
  442. package/src/garbageCollectionHelpers.ts +0 -37
  443. package/src/serializedSnapshotStorage.ts +0 -151
  444. package/src/summarizerHandle.ts +0 -23
  445. /package/dist/{gcSweepReadyUsageDetection.d.ts → gc/gcSweepReadyUsageDetection.d.ts} +0 -0
  446. /package/dist/{orderedClientElection.d.ts → summary/orderedClientElection.d.ts} +0 -0
  447. /package/dist/{orderedClientElection.js → summary/orderedClientElection.js} +0 -0
  448. /package/dist/{summarizerClientElection.d.ts → summary/summarizerClientElection.d.ts} +0 -0
  449. /package/dist/{summarizerClientElection.js → summary/summarizerClientElection.js} +0 -0
  450. /package/dist/{summaryCollection.d.ts → summary/summaryCollection.d.ts} +0 -0
  451. /package/dist/{summaryCollection.js → summary/summaryCollection.js} +0 -0
  452. /package/dist/{summaryGenerator.d.ts → summary/summaryGenerator.d.ts} +0 -0
  453. /package/dist/{summaryManager.js → summary/summaryManager.js} +0 -0
  454. /package/lib/{gcSweepReadyUsageDetection.d.ts → gc/gcSweepReadyUsageDetection.d.ts} +0 -0
  455. /package/lib/{orderedClientElection.d.ts → summary/orderedClientElection.d.ts} +0 -0
  456. /package/lib/{orderedClientElection.js → summary/orderedClientElection.js} +0 -0
  457. /package/lib/{summarizerClientElection.d.ts → summary/summarizerClientElection.d.ts} +0 -0
  458. /package/lib/{summarizerClientElection.js → summary/summarizerClientElection.js} +0 -0
  459. /package/lib/{summaryCollection.d.ts → summary/summaryCollection.d.ts} +0 -0
  460. /package/lib/{summaryCollection.js → summary/summaryCollection.js} +0 -0
  461. /package/lib/{summaryGenerator.d.ts → summary/summaryGenerator.d.ts} +0 -0
  462. /package/lib/{summaryManager.js → summary/summaryManager.js} +0 -0
  463. /package/src/{orderedClientElection.ts → summary/orderedClientElection.ts} +0 -0
  464. /package/src/{summarizerClientElection.ts → summary/summarizerClientElection.ts} +0 -0
  465. /package/src/{summaryCollection.ts → summary/summaryCollection.ts} +0 -0
@@ -0,0 +1,157 @@
1
+ # Configs and feature gates for solving the 1MB limit.
2
+
3
+ ## Introduction
4
+
5
+ There is a current limitation regarding the size of the payload a Fluid client can send and receive. [The limit is 1MB per payload](https://github.com/microsoft/FluidFramework/issues/9023) and it is currently enforced explicitly with the `BatchTooLarge` error which closes the container.
6
+
7
+ There are two features which can be used to work around this size limit, batch compression and compressed batch chunking. This document describes how to enable/disable them, along with a brief description of how they work. The features are enabled by default.
8
+
9
+ By default, the runtime is configured with a max batch size of `716800` bytes, which is lower than the 1MB limit. The reason for the lower value is to account for possible overhead from the op envelope and metadata.
10
+
11
+ ## Table of contents
12
+
13
+ - [Introduction](#introduction)
14
+ - [Compression](#compression)
15
+ - [Chunking for compression](#chunking-for-compression)
16
+ - [Disabling in case of emergency](#disabling-in-case-of-emergency)
17
+ - [Example configs](#example-configs)
18
+ - [How it works](#how-it-works)
19
+
20
+ ## Compression
21
+
22
+ **Compression targets payloads which exceed the max batch size and it is enabled by default.**. The `IContainerRuntimeOptions.compressionOptions` property, of type `ICompressionRuntimeOptions` is the configuration governing how compression works.
23
+
24
+ `ICompressionRuntimeOptions` has two properties:
25
+
26
+ - `minimumBatchSizeInBytes` – the minimum size of the batch for which compression should kick in. If the payload is too small, compression may not yield too many benefits. To target the original 1MB issue, a good value here would be to match the default maxBatchSizeInBytes (972800), however, experimentally, a good lower value could be at around 614400 bytes. Setting this value to `Number.POSITIVE_INFINITY` will disable compression.
27
+ - `compressionAlgorithm` – currently, only `lz4` is supported.
28
+
29
+ ## Chunking for compression
30
+
31
+ **Op chunking for compression targets payloads which exceed the max batch size after compression.** So, only payloads which are already compressed. By default, the feature is enabled.
32
+
33
+ The `IContainerRuntimeOptions.chunkSizeInBytes` property is the only configuration for chunking and it represents the size of the chunked ops, when chunking is necessary. When chunking is performed, the large op is split into smaller ops (chunks). This config represents both the size of the chunks and the threshold for the feature to activate. The value enables a trade-off between large chunks / few ops and small chunks / many ops. A good value for this would be at around 204800. Setting this value to `Number.POSITIVE_INFINITY` will disable chunking.
34
+
35
+ This config would govern chunking compressed batches only. We will not be enabling chunking across all types of ops/batches but **only when compression is enabled and when the batch is compressed**, and its payload size is more than `IContainerRuntimeOptions.chunkSizeInBytes`.
36
+
37
+ ## Disabling in case of emergency
38
+
39
+ If the features are enabled using the configs, they can be disabled at runtime via feature gates as following:
40
+
41
+ - `Fluid.ContainerRuntime.CompressionDisabled` - if set to true, will disable compression (this has a side effect of also disabling chunking, as chunking is invoked only for compressed payloads).
42
+ - `Fluid.ContainerRuntime.CompressionChunkingDisabled` - if set to true, will disable chunking for compression.
43
+
44
+ ## Example configs
45
+
46
+ By default, the runtime is configured with the following values related to compression and chunking:
47
+
48
+ ```
49
+ const runtimeOptions: IContainerRuntimeOptions = {
50
+ compressionOptions: {
51
+ minimumBatchSizeInBytes: 614400,
52
+ compressionAlgorithm: CompressionAlgorithms.lz4,
53
+ },
54
+ chunkSizeInBytes: 204800,
55
+ maxBatchSizeInBytes: 716800,
56
+     }
57
+ ```
58
+
59
+ To use compression but disable chunking:
60
+
61
+ ```
62
+ const runtimeOptions: IContainerRuntimeOptions = {
63
+ chunkSizeInBytes: Number.POSITIVE_INFINITY,
64
+     }
65
+ ```
66
+
67
+ To disable compression (will also disable chunking, as chunking works only for compressed batches):
68
+
69
+ ```
70
+ const runtimeOptions: IContainerRuntimeOptions = {
71
+ compressionOptions: {
72
+ minimumBatchSizeInBytes: Number.POSITIVE_INFINITY,
73
+ compressionAlgorithm: CompressionAlgorithms.lz4,
74
+ },
75
+     }
76
+ ```
77
+
78
+ ## How it works
79
+
80
+ Compression currently works as a runtime layer over the regular op sending/receiving pipeline.
81
+
82
+ If we have a batch with a size larger than the configured minimum required for compression (in the example let’s say it’s 850 bytes), as following:
83
+
84
+ ```
85
+ +-----------+-----------+-----------+-----------+
86
+ | Op 1 | Op 2 | Op 3 | Op 4 |
87
+ | SeqNum: 1 | SeqNum: 2 | SeqNum: 3 | SeqNum: 4 |
88
+ | Size: 100 | Size: 150 | Size: 200 | Size: 400 |
89
+ +-----------+-----------+-----------+-----------+
90
+ ```
91
+
92
+ The total size of the batch is 850 bytes. The client which needs to send the batch would compress the batch to a smaller size (200 bytes) and will send a new batch like the following:
93
+
94
+ ```
95
+ +--------------------+-----------+-----------+-----------+
96
+ | Op 1 | Op 2 | Op 3 | Op 4 |
97
+ | SeqNum: 1 | SeqNum: 2 | SeqNum: 3 | SeqNum: 4 |
98
+ | Size: 200 | Size: 0 | Size: 0 | Size: 0 |
99
+ | Compression: 'lz4' | | | |
100
+ +--------------------+-----------+-----------+-----------+
101
+ ```
102
+
103
+ The first op in the batch is the only one with content (which is opaque due to it being compressed), the rest of the ops serve only to reserve the sequence numbers so that the state machine which rebuilds the original batch on the receiving client can reconstruct the original batch.
104
+
105
+ When the batch is received by a client, it will detect the first op as being compressed, it will decompress it and store it in memory. For each empty op subsequently received, it will fetch the uncompressed content from memory and rebuild the original ops. The original ops are then processed by the runtime and applied accordingly.
106
+ So, compression virtualizes the batch.
107
+
108
+ After compression, the first op in the batch can exceed 1MB, therefore it would still be rejected. In this case, another layer of virtualization is added after compression (and before decompression, symmetrically on the receiving end).
109
+
110
+ The first op in the compressed batch can be chunked into smaller ops which can be sent outside the original batch. However, to conveniently maintain the batch semantics, the last chunk (the chunk which triggers rebuilding the original op) is the first op in the new batch.
111
+
112
+ To illustrate, let’s take the large batch below:
113
+
114
+ ```
115
+ +--------------------+-----------+-----------+-----------+
116
+ | Op 1 | Op 2 | Op 3 | Op 4 |
117
+ | SeqNum: 1 | SeqNum: 2 | SeqNum: 3 | SeqNum: 4 |
118
+ | Size: 900 | Size: 0 | Size: 0 | Size: 0 |
119
+ | Compression: 'lz4' | | | |
120
+ +--------------------+-----------+-----------+-----------+
121
+ ```
122
+
123
+ This will produce the following batches:
124
+
125
+ ```
126
+ +-----------+
127
+ | Chunk 1/3 |
128
+ | SeqNum: 1 |
129
+ | Size: 300 |
130
+ +-----------+
131
+
132
+ ```
133
+
134
+ ```
135
+ +-----------+
136
+ | Chunk 2/3 |
137
+ | SeqNum: 2 |
138
+ | Size: 300 |
139
+ +-----------+
140
+
141
+ ```
142
+
143
+ ```
144
+ +-----------+-----------+-----------+-----------+
145
+ | Chunk 3/3 | Op 2 | Op 3 | Op 4 |
146
+ | SeqNum: 3 | SeqNum: 4 | SeqNum: 5 | SeqNum: 6 |
147
+ | Size: 300 | Size: 0 | Size: 0 | Size: 0 |
148
+ +-----------+-----------+-----------+-----------+
149
+ ```
150
+
151
+ The first 2 chunks are sent in their own batches, while the last chunk is the first op in the last batch which contains the ops reserving the required sequence numbers.
152
+
153
+ Notice that the sequence numbers don’t matter here, as all ops will be based off the same reference sequence number, so the sequence number will be recalculated for all, without additional work.
154
+
155
+ Additionally, as compression preserves the original uncompressed batch layout in terms of the number of ops by using empty ops to reserve the sequence numbers, this ensures that the clients will always receive the exact count of ops to rebuild the uncompressed batch sequentially.
156
+
157
+ On the receiving end, the client will accumulate chunks 1 and 2 and keep them in memory. When chunk 3 is received, the original large, decompressed op will be rebuilt, and the runtime will then process the batch as if it is a compressed batch.
@@ -3,34 +3,27 @@
3
3
  * Licensed under the MIT License.
4
4
  */
5
5
 
6
- import { ITelemetryLogger } from "@fluidframework/common-definitions";
7
- import { UsageError } from "@fluidframework/driver-utils";
8
- import { ChildLogger } from "@fluidframework/telemetry-utils";
9
6
  import { ICompressionRuntimeOptions } from "../containerRuntime";
10
7
  import { BatchMessage, IBatch, IBatchCheckpoint } from "./definitions";
11
8
 
12
9
  export interface IBatchManagerOptions {
13
- readonly enableOpReentryCheck?: boolean;
14
10
  readonly hardLimit: number;
15
11
  readonly softLimit?: number;
16
12
  readonly compressionOptions?: ICompressionRuntimeOptions;
17
13
  }
18
14
 
15
+ /**
16
+ * Estimated size of the stringification overhead for an op accumulated
17
+ * from runtime to loader to the service.
18
+ */
19
+ const opOverhead = 200;
20
+
19
21
  /**
20
22
  * Helper class that manages partial batch & rollback.
21
23
  */
22
24
  export class BatchManager {
23
- private readonly logger;
24
25
  private pendingBatch: BatchMessage[] = [];
25
26
  private batchContentSize = 0;
26
- /**
27
- * Track the number of ops which were detected to have a mismatched
28
- * reference sequence number, in order to self-throttle the telemetry events.
29
- *
30
- * This should be removed as part of ADO:2322
31
- */
32
- private readonly maxMismatchedOpsToReport = 5;
33
- private mismatchedOpsReported = 0;
34
27
 
35
28
  public get length() {
36
29
  return this.pendingBatch.length;
@@ -39,13 +32,15 @@ export class BatchManager {
39
32
  return this.batchContentSize;
40
33
  }
41
34
 
42
- constructor(public readonly options: IBatchManagerOptions, logger: ITelemetryLogger) {
43
- this.logger = ChildLogger.create(logger, "BatchManager");
35
+ public get referenceSequenceNumber(): number | undefined {
36
+ return this.pendingBatch.length === 0
37
+ ? undefined
38
+ : this.pendingBatch[this.pendingBatch.length - 1].referenceSequenceNumber;
44
39
  }
45
40
 
46
- public push(message: BatchMessage): boolean {
47
- this.checkReferenceSequenceNumber(message);
41
+ constructor(public readonly options: IBatchManagerOptions) {}
48
42
 
43
+ public push(message: BatchMessage): boolean {
49
44
  const contentSize = this.batchContentSize + (message.contents?.length ?? 0);
50
45
  const opCount = this.pendingBatch.length;
51
46
 
@@ -54,7 +49,7 @@ export class BatchManager {
54
49
  // Also content will be strigified, and that adds a lot of overhead due to a lot of escape characters.
55
50
  // Not taking it into account, as compression work should help there - compressed payload will be
56
51
  // initially stored as base64, and that requires only 2 extra escape characters.
57
- const socketMessageSize = contentSize + 200 * opCount;
52
+ const socketMessageSize = contentSize + opOverhead * opCount;
58
53
 
59
54
  // If we were provided soft limit, check for exceeding it.
60
55
  // But only if we have any ops, as the intention here is to flush existing ops (on exceeding this limit)
@@ -86,6 +81,7 @@ export class BatchManager {
86
81
  const batch: IBatch = {
87
82
  content: this.pendingBatch,
88
83
  contentSizeInBytes: this.batchContentSize,
84
+ referenceSequenceNumber: this.referenceSequenceNumber,
89
85
  };
90
86
 
91
87
  this.pendingBatch = [];
@@ -112,43 +108,6 @@ export class BatchManager {
112
108
  },
113
109
  };
114
110
  }
115
-
116
- private checkReferenceSequenceNumber(message: BatchMessage) {
117
- if (
118
- this.pendingBatch.length === 0 ||
119
- message.referenceSequenceNumber === this.pendingBatch[0].referenceSequenceNumber
120
- ) {
121
- // The reference sequence numbers are stable
122
- return;
123
- }
124
-
125
- const telemetryProperties = {
126
- referenceSequenceNumber: this.pendingBatch[0].referenceSequenceNumber,
127
- messageReferenceSequenceNumber: message.referenceSequenceNumber,
128
- type: message.deserializedContent.type,
129
- length: this.pendingBatch.length,
130
- enableOpReentryCheck: this.options.enableOpReentryCheck === true,
131
- };
132
- const error = new UsageError("Submission of an out of order message");
133
- const eventName = "ReferenceSequenceNumberMismatch";
134
-
135
- if (this.options.enableOpReentryCheck === true) {
136
- this.logger.sendErrorEvent({ eventName, ...telemetryProperties }, error);
137
- throw error;
138
- }
139
-
140
- if (++this.mismatchedOpsReported <= this.maxMismatchedOpsToReport) {
141
- this.logger.sendErrorEvent(
142
- {
143
- eventName,
144
- ...telemetryProperties,
145
- ops: this.mismatchedOpsReported,
146
- maxOps: this.maxMismatchedOpsToReport,
147
- },
148
- error,
149
- );
150
- }
151
- }
152
111
  }
153
112
 
154
113
  const addBatchMetadata = (batch: IBatch): IBatch => {
@@ -165,3 +124,15 @@ const addBatchMetadata = (batch: IBatch): IBatch => {
165
124
 
166
125
  return batch;
167
126
  };
127
+
128
+ /**
129
+ * Estimates the real size in bytes on the socket for a given batch. It assumes that
130
+ * the envelope size (and the size of an empty op) is 200 bytes, taking into account
131
+ * extra overhead from stringification.
132
+ *
133
+ * @param batch - the batch to inspect
134
+ * @returns An estimate of the payload size in bytes which will be produced when the batch is sent over the wire
135
+ */
136
+ export const estimateSocketSize = (batch: IBatch): number => {
137
+ return batch.contentSizeInBytes + opOverhead * batch.content.length;
138
+ };
@@ -30,6 +30,10 @@ export interface IBatch {
30
30
  * All the messages in the batch
31
31
  */
32
32
  readonly content: BatchMessage[];
33
+ /**
34
+ * The reference sequence number for the batch
35
+ */
36
+ readonly referenceSequenceNumber: number | undefined;
33
37
  }
34
38
 
35
39
  export interface IBatchCheckpoint {
@@ -3,7 +3,7 @@
3
3
  * Licensed under the MIT License.
4
4
  */
5
5
 
6
- export { BatchManager } from "./batchManager";
6
+ export { BatchManager, estimateSocketSize } from "./batchManager";
7
7
  export {
8
8
  BatchMessage,
9
9
  IBatch,
@@ -4,11 +4,12 @@
4
4
  */
5
5
 
6
6
  import { ITelemetryLogger } from "@fluidframework/common-definitions";
7
- import { IsoBuffer } from "@fluidframework/common-utils";
7
+ import { assert, IsoBuffer } from "@fluidframework/common-utils";
8
8
  import { UsageError } from "@fluidframework/container-utils";
9
9
  import { ChildLogger } from "@fluidframework/telemetry-utils";
10
10
  import { compress } from "lz4js";
11
11
  import { CompressionAlgorithms } from "../containerRuntime";
12
+ import { estimateSocketSize } from "./batchManager";
12
13
  import { IBatch, BatchMessage } from "./definitions";
13
14
 
14
15
  /**
@@ -24,21 +25,17 @@ export class OpCompressor {
24
25
  }
25
26
 
26
27
  public compressBatch(batch: IBatch): IBatch {
28
+ assert(
29
+ batch.contentSizeInBytes > 0 && batch.content.length > 0,
30
+ 0x5a4 /* Batch should not be empty */,
31
+ );
32
+
27
33
  const compressionStart = Date.now();
28
34
  const contentsAsBuffer = new TextEncoder().encode(this.serializeBatch(batch));
29
35
  const compressedContents = compress(contentsAsBuffer);
30
36
  const compressedContent = IsoBuffer.from(compressedContents).toString("base64");
31
37
  const duration = Date.now() - compressionStart;
32
38
 
33
- if (batch.contentSizeInBytes > 200000) {
34
- this.logger.sendPerformanceEvent({
35
- eventName: "CompressedBatch",
36
- duration,
37
- sizeBeforeCompression: batch.contentSizeInBytes,
38
- sizeAfterCompression: compressedContent.length,
39
- });
40
- }
41
-
42
39
  const messages: BatchMessage[] = [];
43
40
  messages.push({
44
41
  ...batch.content[0],
@@ -47,14 +44,37 @@ export class OpCompressor {
47
44
  compression: CompressionAlgorithms.lz4,
48
45
  });
49
46
 
47
+ // Add empty placeholder messages to reserve the sequence numbers
50
48
  for (const message of batch.content.slice(1)) {
51
- messages.push({ ...message, contents: undefined });
49
+ messages.push({
50
+ deserializedContent: {
51
+ contents: undefined,
52
+ type: message.deserializedContent.type,
53
+ },
54
+ localOpMetadata: message.localOpMetadata,
55
+ metadata: message.metadata,
56
+ referenceSequenceNumber: message.referenceSequenceNumber,
57
+ });
52
58
  }
53
59
 
54
- return {
60
+ const compressedBatch: IBatch = {
55
61
  contentSizeInBytes: compressedContent.length,
56
62
  content: messages,
63
+ referenceSequenceNumber: batch.referenceSequenceNumber,
57
64
  };
65
+
66
+ if (batch.contentSizeInBytes > 200000) {
67
+ this.logger.sendPerformanceEvent({
68
+ eventName: "CompressedBatch",
69
+ duration,
70
+ sizeBeforeCompression: batch.contentSizeInBytes,
71
+ sizeAfterCompression: compressedBatch.contentSizeInBytes,
72
+ opCount: compressedBatch.content.length,
73
+ socketSize: estimateSocketSize(compressedBatch),
74
+ });
75
+ }
76
+
77
+ return compressedBatch;
58
78
  }
59
79
 
60
80
  private serializeBatch(batch: IBatch): string {
@@ -6,6 +6,8 @@
6
6
  import { decompress } from "lz4js";
7
7
  import { ISequencedDocumentMessage } from "@fluidframework/protocol-definitions";
8
8
  import { assert, IsoBuffer, Uint8ArrayToString } from "@fluidframework/common-utils";
9
+ import { ChildLogger } from "@fluidframework/telemetry-utils";
10
+ import { ITelemetryLogger } from "@fluidframework/common-definitions";
9
11
  import { CompressionAlgorithms } from "../containerRuntime";
10
12
  import { IMessageProcessingResult } from "./definitions";
11
13
 
@@ -21,6 +23,11 @@ export class OpDecompressor {
21
23
  private activeBatch = false;
22
24
  private rootMessageContents: any | undefined;
23
25
  private processedCount = 0;
26
+ private readonly logger;
27
+
28
+ constructor(logger: ITelemetryLogger) {
29
+ this.logger = ChildLogger.create(logger, "OpDecompressor");
30
+ }
24
31
 
25
32
  public processMessage(message: ISequencedDocumentMessage): IMessageProcessingResult {
26
33
  assert(
@@ -28,7 +35,7 @@ export class OpDecompressor {
28
35
  0x511 /* Only lz4 compression is supported */,
29
36
  );
30
37
 
31
- if (message.metadata?.batch === true && message.compression === CompressionAlgorithms.lz4) {
38
+ if (message.metadata?.batch === true && this.isCompressed(message)) {
32
39
  // Beginning of a compressed batch
33
40
  assert(this.activeBatch === false, 0x4b8 /* shouldn't have multiple active batches */);
34
41
  if (message.compression) {
@@ -84,10 +91,7 @@ export class OpDecompressor {
84
91
  };
85
92
  }
86
93
 
87
- if (
88
- message.metadata?.batch === undefined &&
89
- message.compression === CompressionAlgorithms.lz4
90
- ) {
94
+ if (message.metadata?.batch === undefined && this.isCompressed(message)) {
91
95
  // Single compressed message
92
96
  assert(
93
97
  this.activeBatch === false,
@@ -110,6 +114,46 @@ export class OpDecompressor {
110
114
  state: "Skipped",
111
115
  };
112
116
  }
117
+
118
+ private isCompressed(message: ISequencedDocumentMessage) {
119
+ if (message.compression === CompressionAlgorithms.lz4) {
120
+ return true;
121
+ }
122
+
123
+ /**
124
+ * Back-compat self healing mechanism for ADO:3538, as loaders from
125
+ * version client_v2.0.0-internal.1.2.0 to client_v2.0.0-internal.2.2.0 do not
126
+ * support adding the proper compression metadata to compressed messages submitted
127
+ * by the runtime. Should be removed after the loader reaches sufficient saturation
128
+ * for a version greater or equal than client_v2.0.0-internal.2.2.0.
129
+ *
130
+ * The condition holds true for compressed messages, regardless of metadata. We are ultimately
131
+ * looking for a message with a single property `packedContents` inside `contents`, of type 'string'
132
+ * with a base64 encoded value.
133
+ */
134
+ try {
135
+ if (
136
+ typeof message.contents === "object" &&
137
+ Object.keys(message.contents).length === 1 &&
138
+ message.contents?.packedContents !== undefined &&
139
+ typeof message.contents?.packedContents === "string" &&
140
+ message.contents.packedContents.length > 0 &&
141
+ IsoBuffer.from(message.contents.packedContents, "base64").toString("base64") ===
142
+ message.contents.packedContents
143
+ ) {
144
+ this.logger.sendTelemetryEvent({
145
+ eventName: "LegacyCompression",
146
+ type: message.type,
147
+ batch: message.metadata?.batch,
148
+ });
149
+ return true;
150
+ }
151
+ } catch (err) {
152
+ return false;
153
+ }
154
+
155
+ return false;
156
+ }
113
157
  }
114
158
 
115
159
  // We should not be mutating the input message nor its metadata
@@ -13,6 +13,7 @@ import {
13
13
  import { ISequencedDocumentMessage } from "@fluidframework/protocol-definitions";
14
14
  import { ChildLogger } from "@fluidframework/telemetry-utils";
15
15
  import { ContainerMessageType, ContainerRuntimeMessage } from "../containerRuntime";
16
+ import { estimateSocketSize } from "./batchManager";
16
17
  import { BatchMessage, IBatch, IChunkedOp, IMessageProcessingResult } from "./definitions";
17
18
 
18
19
  /**
@@ -25,8 +26,10 @@ export class OpSplitter {
25
26
 
26
27
  constructor(
27
28
  chunks: [string, string[]][],
28
- private readonly submitBatchFn: ((batch: IBatchMessage[]) => number) | undefined,
29
- private readonly chunkSizeInBytes: number,
29
+ private readonly submitBatchFn:
30
+ | ((batch: IBatchMessage[], referenceSequenceNumber?: number) => number)
31
+ | undefined,
32
+ public readonly chunkSizeInBytes: number,
30
33
  private readonly maxBatchSizeInBytes: number,
31
34
  logger: ITelemetryLogger,
32
35
  ) {
@@ -138,6 +141,10 @@ export class OpSplitter {
138
141
  batch.contentSizeInBytes > 0 && batch.content.length > 0,
139
142
  0x514 /* Batch needs to be non-empty */,
140
143
  );
144
+ assert(
145
+ batch.referenceSequenceNumber !== undefined,
146
+ 0x58a /* Batch must have a reference sequence number if non-empty */,
147
+ );
141
148
  assert(this.chunkSizeInBytes !== 0, 0x515 /* Chunk size needs to be non-zero */);
142
149
  assert(
143
150
  this.chunkSizeInBytes < this.maxBatchSizeInBytes,
@@ -155,33 +162,47 @@ export class OpSplitter {
155
162
  );
156
163
 
157
164
  const restOfMessages = batch.content.slice(1); // we expect these to be empty ops, created to reserve sequence numbers
158
- const chunks = splitOp(firstMessage, this.chunkSizeInBytes);
165
+ const socketSize = estimateSocketSize(batch);
166
+ const chunks = splitOp(
167
+ firstMessage,
168
+ this.chunkSizeInBytes,
169
+ // If we estimate that the socket batch size will exceed the batch limit
170
+ // we will inject an empty op to minimize the risk of the payload failing due to
171
+ // the overhead from the trailing empty ops in the batch.
172
+ socketSize >= this.maxBatchSizeInBytes,
173
+ );
159
174
 
160
175
  assert(this.submitBatchFn !== undefined, 0x519 /* We don't support old loaders */);
161
176
  // Send the first N-1 chunks immediately
162
177
  for (const chunk of chunks.slice(0, -1)) {
163
- this.submitBatchFn([chunkToBatchMessage(chunk, firstMessage.referenceSequenceNumber)]);
178
+ this.submitBatchFn(
179
+ [chunkToBatchMessage(chunk, batch.referenceSequenceNumber)],
180
+ batch.referenceSequenceNumber,
181
+ );
164
182
  }
165
183
 
166
184
  // The last chunk will be part of the new batch and needs to
167
185
  // preserve the batch metadata of the original batch
168
186
  const lastChunk = chunkToBatchMessage(
169
187
  chunks[chunks.length - 1],
170
- firstMessage.referenceSequenceNumber,
188
+ batch.referenceSequenceNumber,
171
189
  { batch: firstMessage.metadata?.batch },
172
190
  );
173
191
 
174
192
  this.logger.sendPerformanceEvent({
175
- eventName: "Chunked compressed batch",
193
+ // Used to be "Chunked compressed batch"
194
+ eventName: "CompressedChunkedBatch",
176
195
  length: batch.content.length,
177
196
  sizeInBytes: batch.contentSizeInBytes,
178
197
  chunks: chunks.length,
179
198
  chunkSizeInBytes: this.chunkSizeInBytes,
199
+ socketSize,
180
200
  });
181
201
 
182
202
  return {
183
203
  content: [lastChunk, ...restOfMessages],
184
204
  contentSizeInBytes: lastChunk.contents?.length ?? 0,
205
+ referenceSequenceNumber: batch.referenceSequenceNumber,
185
206
  };
186
207
  }
187
208
  }
@@ -204,7 +225,23 @@ const chunkToBatchMessage = (
204
225
  };
205
226
  };
206
227
 
207
- export const splitOp = (op: BatchMessage, chunkSizeInBytes: number): IChunkedOp[] => {
228
+ /**
229
+ * Splits an op into smaller ops (chunks), based on the size of the op and the `chunkSizeInBytes` parameter.
230
+ *
231
+ * The last op of the result will be bundled with empty ops in the same batch. There is a risk of the batch payload
232
+ * exceeding the 1MB limit due to the overhead from the empty ops. If the last op is large, the risk is even higher.
233
+ * To minimize the odds, an extra empty op can be added to the result using the `extraOp` parameter.
234
+ *
235
+ * @param op - the op to be split
236
+ * @param chunkSizeInBytes - how large should the chunks be
237
+ * @param extraOp - should an extra empty op be added to the result
238
+ * @returns an array of chunked ops
239
+ */
240
+ export const splitOp = (
241
+ op: BatchMessage,
242
+ chunkSizeInBytes: number,
243
+ extraOp: boolean = false,
244
+ ): IChunkedOp[] => {
208
245
  const chunks: IChunkedOp[] = [];
209
246
  assert(
210
247
  op.contents !== undefined && op.contents !== null,
@@ -212,17 +249,17 @@ export const splitOp = (op: BatchMessage, chunkSizeInBytes: number): IChunkedOp[
212
249
  );
213
250
 
214
251
  const contentLength = op.contents.length;
215
- const chunkN = Math.floor((contentLength - 1) / chunkSizeInBytes) + 1;
252
+ const chunkCount = Math.floor((contentLength - 1) / chunkSizeInBytes) + 1 + (extraOp ? 1 : 0);
216
253
  let offset = 0;
217
- for (let i = 1; i <= chunkN; i++) {
254
+ for (let chunkId = 1; chunkId <= chunkCount; chunkId++) {
218
255
  const chunk: IChunkedOp = {
219
- chunkId: i,
256
+ chunkId,
220
257
  contents: op.contents.substr(offset, chunkSizeInBytes),
221
258
  originalType: op.deserializedContent.type,
222
- totalChunks: chunkN,
259
+ totalChunks: chunkCount,
223
260
  };
224
261
 
225
- if (i === chunkN) {
262
+ if (chunkId === chunkCount) {
226
263
  // We don't need to port these to all the chunks,
227
264
  // as we rebuild the original op when we process the
228
265
  // last chunk, therefore it is the only one that needs it.
@@ -232,7 +269,13 @@ export const splitOp = (op: BatchMessage, chunkSizeInBytes: number): IChunkedOp[
232
269
 
233
270
  chunks.push(chunk);
234
271
  offset += chunkSizeInBytes;
272
+ assert(
273
+ chunkId >= chunkCount - 1 || offset <= contentLength,
274
+ 0x58b /* Content offset within bounds */,
275
+ );
235
276
  }
236
277
 
278
+ assert(offset >= contentLength, 0x58c /* Content offset equal or larger than content length */);
279
+ assert(chunks.length === chunkCount, 0x5a5 /* Expected number of chunks */);
237
280
  return chunks;
238
281
  };