@fluidframework/container-runtime 2.0.0-dev.4.4.0.162574 → 2.0.0-dev.5.3.2.178189

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 (367) hide show
  1. package/CHANGELOG.md +69 -0
  2. package/dist/batchTracker.d.ts +4 -4
  3. package/dist/batchTracker.d.ts.map +1 -1
  4. package/dist/batchTracker.js +2 -2
  5. package/dist/batchTracker.js.map +1 -1
  6. package/dist/blobManager.d.ts +5 -2
  7. package/dist/blobManager.d.ts.map +1 -1
  8. package/dist/blobManager.js +53 -24
  9. package/dist/blobManager.js.map +1 -1
  10. package/dist/connectionTelemetry.d.ts +2 -2
  11. package/dist/connectionTelemetry.d.ts.map +1 -1
  12. package/dist/connectionTelemetry.js +8 -1
  13. package/dist/connectionTelemetry.js.map +1 -1
  14. package/dist/containerRuntime.d.ts +19 -7
  15. package/dist/containerRuntime.d.ts.map +1 -1
  16. package/dist/containerRuntime.js +98 -22
  17. package/dist/containerRuntime.js.map +1 -1
  18. package/dist/dataStore.d.ts +2 -2
  19. package/dist/dataStore.d.ts.map +1 -1
  20. package/dist/dataStore.js +1 -1
  21. package/dist/dataStore.js.map +1 -1
  22. package/dist/dataStoreContext.d.ts +3 -4
  23. package/dist/dataStoreContext.d.ts.map +1 -1
  24. package/dist/dataStoreContext.js +5 -5
  25. package/dist/dataStoreContext.js.map +1 -1
  26. package/dist/dataStoreContexts.d.ts +2 -1
  27. package/dist/dataStoreContexts.d.ts.map +1 -1
  28. package/dist/dataStoreContexts.js +2 -1
  29. package/dist/dataStoreContexts.js.map +1 -1
  30. package/dist/dataStores.d.ts +1 -1
  31. package/dist/dataStores.d.ts.map +1 -1
  32. package/dist/dataStores.js +2 -1
  33. package/dist/dataStores.js.map +1 -1
  34. package/dist/deltaScheduler.d.ts +2 -2
  35. package/dist/deltaScheduler.d.ts.map +1 -1
  36. package/dist/deltaScheduler.js +1 -1
  37. package/dist/deltaScheduler.js.map +1 -1
  38. package/dist/gc/garbageCollection.d.ts +2 -2
  39. package/dist/gc/garbageCollection.d.ts.map +1 -1
  40. package/dist/gc/garbageCollection.js +4 -3
  41. package/dist/gc/garbageCollection.js.map +1 -1
  42. package/dist/gc/gcDefinitions.d.ts +3 -4
  43. package/dist/gc/gcDefinitions.d.ts.map +1 -1
  44. package/dist/gc/gcDefinitions.js.map +1 -1
  45. package/dist/gc/gcTelemetry.d.ts +5 -5
  46. package/dist/gc/gcTelemetry.d.ts.map +1 -1
  47. package/dist/gc/gcTelemetry.js.map +1 -1
  48. package/dist/id-compressor/idCompressor.d.ts +2 -2
  49. package/dist/id-compressor/idCompressor.d.ts.map +1 -1
  50. package/dist/id-compressor/idCompressor.js.map +1 -1
  51. package/dist/id-compressor/uuidUtilities.d.ts +0 -2
  52. package/dist/id-compressor/uuidUtilities.d.ts.map +1 -1
  53. package/dist/id-compressor/uuidUtilities.js +1 -3
  54. package/dist/id-compressor/uuidUtilities.js.map +1 -1
  55. package/dist/index.d.ts +1 -1
  56. package/dist/index.d.ts.map +1 -1
  57. package/dist/index.js.map +1 -1
  58. package/dist/metadata.d.ts +18 -0
  59. package/dist/metadata.d.ts.map +1 -0
  60. package/dist/metadata.js +7 -0
  61. package/dist/metadata.js.map +1 -0
  62. package/dist/opLifecycle/batchManager.d.ts +2 -1
  63. package/dist/opLifecycle/batchManager.d.ts.map +1 -1
  64. package/dist/opLifecycle/batchManager.js +5 -1
  65. package/dist/opLifecycle/batchManager.js.map +1 -1
  66. package/dist/opLifecycle/definitions.d.ts +13 -2
  67. package/dist/opLifecycle/definitions.d.ts.map +1 -1
  68. package/dist/opLifecycle/definitions.js.map +1 -1
  69. package/dist/opLifecycle/index.d.ts +1 -1
  70. package/dist/opLifecycle/index.d.ts.map +1 -1
  71. package/dist/opLifecycle/index.js +2 -1
  72. package/dist/opLifecycle/index.js.map +1 -1
  73. package/dist/opLifecycle/opCompressor.d.ts +2 -2
  74. package/dist/opLifecycle/opCompressor.d.ts.map +1 -1
  75. package/dist/opLifecycle/opCompressor.js +3 -6
  76. package/dist/opLifecycle/opCompressor.js.map +1 -1
  77. package/dist/opLifecycle/opDecompressor.d.ts +2 -2
  78. package/dist/opLifecycle/opDecompressor.d.ts.map +1 -1
  79. package/dist/opLifecycle/opDecompressor.js +14 -8
  80. package/dist/opLifecycle/opDecompressor.js.map +1 -1
  81. package/dist/opLifecycle/opGroupingManager.d.ts +1 -1
  82. package/dist/opLifecycle/opGroupingManager.d.ts.map +1 -1
  83. package/dist/opLifecycle/opGroupingManager.js +6 -11
  84. package/dist/opLifecycle/opGroupingManager.js.map +1 -1
  85. package/dist/opLifecycle/opSplitter.d.ts +2 -2
  86. package/dist/opLifecycle/opSplitter.d.ts.map +1 -1
  87. package/dist/opLifecycle/opSplitter.js +5 -3
  88. package/dist/opLifecycle/opSplitter.js.map +1 -1
  89. package/dist/opLifecycle/outbox.d.ts +35 -4
  90. package/dist/opLifecycle/outbox.d.ts.map +1 -1
  91. package/dist/opLifecycle/outbox.js +135 -45
  92. package/dist/opLifecycle/outbox.js.map +1 -1
  93. package/dist/opLifecycle/remoteMessageProcessor.d.ts.map +1 -1
  94. package/dist/opLifecycle/remoteMessageProcessor.js +3 -1
  95. package/dist/opLifecycle/remoteMessageProcessor.js.map +1 -1
  96. package/dist/packageVersion.d.ts +1 -1
  97. package/dist/packageVersion.js +1 -1
  98. package/dist/packageVersion.js.map +1 -1
  99. package/dist/pendingStateManager.d.ts +24 -15
  100. package/dist/pendingStateManager.d.ts.map +1 -1
  101. package/dist/pendingStateManager.js +67 -72
  102. package/dist/pendingStateManager.js.map +1 -1
  103. package/dist/scheduleManager.d.ts +2 -2
  104. package/dist/scheduleManager.d.ts.map +1 -1
  105. package/dist/scheduleManager.js +8 -2
  106. package/dist/scheduleManager.js.map +1 -1
  107. package/dist/summary/index.d.ts +2 -2
  108. package/dist/summary/index.d.ts.map +1 -1
  109. package/dist/summary/index.js +2 -1
  110. package/dist/summary/index.js.map +1 -1
  111. package/dist/summary/orderedClientElection.d.ts +4 -3
  112. package/dist/summary/orderedClientElection.d.ts.map +1 -1
  113. package/dist/summary/orderedClientElection.js +3 -18
  114. package/dist/summary/orderedClientElection.js.map +1 -1
  115. package/dist/summary/runningSummarizer.d.ts +4 -3
  116. package/dist/summary/runningSummarizer.d.ts.map +1 -1
  117. package/dist/summary/runningSummarizer.js +5 -6
  118. package/dist/summary/runningSummarizer.js.map +1 -1
  119. package/dist/summary/summarizer.d.ts +2 -3
  120. package/dist/summary/summarizer.d.ts.map +1 -1
  121. package/dist/summary/summarizer.js +2 -3
  122. package/dist/summary/summarizer.js.map +1 -1
  123. package/dist/summary/summarizerClientElection.d.ts +3 -2
  124. package/dist/summary/summarizerClientElection.d.ts.map +1 -1
  125. package/dist/summary/summarizerClientElection.js.map +1 -1
  126. package/dist/summary/summarizerHeuristics.d.ts +2 -2
  127. package/dist/summary/summarizerHeuristics.d.ts.map +1 -1
  128. package/dist/summary/summarizerHeuristics.js.map +1 -1
  129. package/dist/summary/summarizerNode/index.d.ts +1 -1
  130. package/dist/summary/summarizerNode/index.d.ts.map +1 -1
  131. package/dist/summary/summarizerNode/index.js.map +1 -1
  132. package/dist/summary/summarizerNode/summarizerNode.d.ts +41 -14
  133. package/dist/summary/summarizerNode/summarizerNode.d.ts.map +1 -1
  134. package/dist/summary/summarizerNode/summarizerNode.js +91 -23
  135. package/dist/summary/summarizerNode/summarizerNode.js.map +1 -1
  136. package/dist/summary/summarizerNode/summarizerNodeUtils.d.ts +24 -4
  137. package/dist/summary/summarizerNode/summarizerNodeUtils.d.ts.map +1 -1
  138. package/dist/summary/summarizerNode/summarizerNodeUtils.js.map +1 -1
  139. package/dist/summary/summarizerNode/summarizerNodeWithGc.d.ts +23 -8
  140. package/dist/summary/summarizerNode/summarizerNodeWithGc.d.ts.map +1 -1
  141. package/dist/summary/summarizerNode/summarizerNodeWithGc.js +60 -23
  142. package/dist/summary/summarizerNode/summarizerNodeWithGc.js.map +1 -1
  143. package/dist/summary/summarizerTypes.d.ts +16 -9
  144. package/dist/summary/summarizerTypes.d.ts.map +1 -1
  145. package/dist/summary/summarizerTypes.js.map +1 -1
  146. package/dist/summary/summaryCollection.d.ts +4 -2
  147. package/dist/summary/summaryCollection.d.ts.map +1 -1
  148. package/dist/summary/summaryCollection.js +4 -0
  149. package/dist/summary/summaryCollection.js.map +1 -1
  150. package/dist/summary/summaryFormat.d.ts +1 -0
  151. package/dist/summary/summaryFormat.d.ts.map +1 -1
  152. package/dist/summary/summaryFormat.js +2 -1
  153. package/dist/summary/summaryFormat.js.map +1 -1
  154. package/dist/summary/summaryGenerator.d.ts +14 -5
  155. package/dist/summary/summaryGenerator.d.ts.map +1 -1
  156. package/dist/summary/summaryGenerator.js +23 -9
  157. package/dist/summary/summaryGenerator.js.map +1 -1
  158. package/dist/summary/summaryManager.d.ts +4 -2
  159. package/dist/summary/summaryManager.d.ts.map +1 -1
  160. package/dist/summary/summaryManager.js.map +1 -1
  161. package/dist/tsdoc-metadata.json +11 -0
  162. package/lib/batchTracker.d.ts +4 -4
  163. package/lib/batchTracker.d.ts.map +1 -1
  164. package/lib/batchTracker.js +2 -2
  165. package/lib/batchTracker.js.map +1 -1
  166. package/lib/blobManager.d.ts +5 -2
  167. package/lib/blobManager.d.ts.map +1 -1
  168. package/lib/blobManager.js +53 -24
  169. package/lib/blobManager.js.map +1 -1
  170. package/lib/connectionTelemetry.d.ts +2 -2
  171. package/lib/connectionTelemetry.d.ts.map +1 -1
  172. package/lib/connectionTelemetry.js +8 -1
  173. package/lib/connectionTelemetry.js.map +1 -1
  174. package/lib/containerRuntime.d.ts +19 -7
  175. package/lib/containerRuntime.d.ts.map +1 -1
  176. package/lib/containerRuntime.js +101 -25
  177. package/lib/containerRuntime.js.map +1 -1
  178. package/lib/dataStore.d.ts +2 -2
  179. package/lib/dataStore.d.ts.map +1 -1
  180. package/lib/dataStore.js +1 -1
  181. package/lib/dataStore.js.map +1 -1
  182. package/lib/dataStoreContext.d.ts +3 -4
  183. package/lib/dataStoreContext.d.ts.map +1 -1
  184. package/lib/dataStoreContext.js +5 -5
  185. package/lib/dataStoreContext.js.map +1 -1
  186. package/lib/dataStoreContexts.d.ts +2 -1
  187. package/lib/dataStoreContexts.d.ts.map +1 -1
  188. package/lib/dataStoreContexts.js +2 -1
  189. package/lib/dataStoreContexts.js.map +1 -1
  190. package/lib/dataStores.d.ts +1 -1
  191. package/lib/dataStores.d.ts.map +1 -1
  192. package/lib/dataStores.js +2 -1
  193. package/lib/dataStores.js.map +1 -1
  194. package/lib/deltaScheduler.d.ts +2 -2
  195. package/lib/deltaScheduler.d.ts.map +1 -1
  196. package/lib/deltaScheduler.js +1 -1
  197. package/lib/deltaScheduler.js.map +1 -1
  198. package/lib/gc/garbageCollection.d.ts +2 -2
  199. package/lib/gc/garbageCollection.d.ts.map +1 -1
  200. package/lib/gc/garbageCollection.js +2 -1
  201. package/lib/gc/garbageCollection.js.map +1 -1
  202. package/lib/gc/gcDefinitions.d.ts +3 -4
  203. package/lib/gc/gcDefinitions.d.ts.map +1 -1
  204. package/lib/gc/gcDefinitions.js.map +1 -1
  205. package/lib/gc/gcTelemetry.d.ts +5 -5
  206. package/lib/gc/gcTelemetry.d.ts.map +1 -1
  207. package/lib/gc/gcTelemetry.js +1 -1
  208. package/lib/gc/gcTelemetry.js.map +1 -1
  209. package/lib/id-compressor/idCompressor.d.ts +2 -2
  210. package/lib/id-compressor/idCompressor.d.ts.map +1 -1
  211. package/lib/id-compressor/idCompressor.js.map +1 -1
  212. package/lib/id-compressor/uuidUtilities.d.ts +0 -2
  213. package/lib/id-compressor/uuidUtilities.d.ts.map +1 -1
  214. package/lib/id-compressor/uuidUtilities.js +1 -3
  215. package/lib/id-compressor/uuidUtilities.js.map +1 -1
  216. package/lib/index.d.ts +1 -1
  217. package/lib/index.d.ts.map +1 -1
  218. package/lib/index.js.map +1 -1
  219. package/lib/metadata.d.ts +18 -0
  220. package/lib/metadata.d.ts.map +1 -0
  221. package/lib/metadata.js +6 -0
  222. package/lib/metadata.js.map +1 -0
  223. package/lib/opLifecycle/batchManager.d.ts +2 -1
  224. package/lib/opLifecycle/batchManager.d.ts.map +1 -1
  225. package/lib/opLifecycle/batchManager.js +5 -1
  226. package/lib/opLifecycle/batchManager.js.map +1 -1
  227. package/lib/opLifecycle/definitions.d.ts +13 -2
  228. package/lib/opLifecycle/definitions.d.ts.map +1 -1
  229. package/lib/opLifecycle/definitions.js.map +1 -1
  230. package/lib/opLifecycle/index.d.ts +1 -1
  231. package/lib/opLifecycle/index.d.ts.map +1 -1
  232. package/lib/opLifecycle/index.js +1 -1
  233. package/lib/opLifecycle/index.js.map +1 -1
  234. package/lib/opLifecycle/opCompressor.d.ts +2 -2
  235. package/lib/opLifecycle/opCompressor.d.ts.map +1 -1
  236. package/lib/opLifecycle/opCompressor.js +3 -6
  237. package/lib/opLifecycle/opCompressor.js.map +1 -1
  238. package/lib/opLifecycle/opDecompressor.d.ts +2 -2
  239. package/lib/opLifecycle/opDecompressor.d.ts.map +1 -1
  240. package/lib/opLifecycle/opDecompressor.js +14 -8
  241. package/lib/opLifecycle/opDecompressor.js.map +1 -1
  242. package/lib/opLifecycle/opGroupingManager.d.ts +1 -1
  243. package/lib/opLifecycle/opGroupingManager.d.ts.map +1 -1
  244. package/lib/opLifecycle/opGroupingManager.js +6 -11
  245. package/lib/opLifecycle/opGroupingManager.js.map +1 -1
  246. package/lib/opLifecycle/opSplitter.d.ts +2 -2
  247. package/lib/opLifecycle/opSplitter.d.ts.map +1 -1
  248. package/lib/opLifecycle/opSplitter.js +5 -3
  249. package/lib/opLifecycle/opSplitter.js.map +1 -1
  250. package/lib/opLifecycle/outbox.d.ts +35 -4
  251. package/lib/opLifecycle/outbox.d.ts.map +1 -1
  252. package/lib/opLifecycle/outbox.js +133 -44
  253. package/lib/opLifecycle/outbox.js.map +1 -1
  254. package/lib/opLifecycle/remoteMessageProcessor.d.ts.map +1 -1
  255. package/lib/opLifecycle/remoteMessageProcessor.js +3 -1
  256. package/lib/opLifecycle/remoteMessageProcessor.js.map +1 -1
  257. package/lib/packageVersion.d.ts +1 -1
  258. package/lib/packageVersion.js +1 -1
  259. package/lib/packageVersion.js.map +1 -1
  260. package/lib/pendingStateManager.d.ts +24 -15
  261. package/lib/pendingStateManager.d.ts.map +1 -1
  262. package/lib/pendingStateManager.js +67 -72
  263. package/lib/pendingStateManager.js.map +1 -1
  264. package/lib/scheduleManager.d.ts +2 -2
  265. package/lib/scheduleManager.d.ts.map +1 -1
  266. package/lib/scheduleManager.js +8 -2
  267. package/lib/scheduleManager.js.map +1 -1
  268. package/lib/summary/index.d.ts +2 -2
  269. package/lib/summary/index.d.ts.map +1 -1
  270. package/lib/summary/index.js +1 -1
  271. package/lib/summary/index.js.map +1 -1
  272. package/lib/summary/orderedClientElection.d.ts +4 -3
  273. package/lib/summary/orderedClientElection.d.ts.map +1 -1
  274. package/lib/summary/orderedClientElection.js +3 -18
  275. package/lib/summary/orderedClientElection.js.map +1 -1
  276. package/lib/summary/runningSummarizer.d.ts +4 -3
  277. package/lib/summary/runningSummarizer.d.ts.map +1 -1
  278. package/lib/summary/runningSummarizer.js +5 -6
  279. package/lib/summary/runningSummarizer.js.map +1 -1
  280. package/lib/summary/summarizer.d.ts +2 -3
  281. package/lib/summary/summarizer.d.ts.map +1 -1
  282. package/lib/summary/summarizer.js +2 -3
  283. package/lib/summary/summarizer.js.map +1 -1
  284. package/lib/summary/summarizerClientElection.d.ts +3 -2
  285. package/lib/summary/summarizerClientElection.d.ts.map +1 -1
  286. package/lib/summary/summarizerClientElection.js.map +1 -1
  287. package/lib/summary/summarizerHeuristics.d.ts +2 -2
  288. package/lib/summary/summarizerHeuristics.d.ts.map +1 -1
  289. package/lib/summary/summarizerHeuristics.js.map +1 -1
  290. package/lib/summary/summarizerNode/index.d.ts +1 -1
  291. package/lib/summary/summarizerNode/index.d.ts.map +1 -1
  292. package/lib/summary/summarizerNode/index.js.map +1 -1
  293. package/lib/summary/summarizerNode/summarizerNode.d.ts +41 -14
  294. package/lib/summary/summarizerNode/summarizerNode.d.ts.map +1 -1
  295. package/lib/summary/summarizerNode/summarizerNode.js +91 -23
  296. package/lib/summary/summarizerNode/summarizerNode.js.map +1 -1
  297. package/lib/summary/summarizerNode/summarizerNodeUtils.d.ts +24 -4
  298. package/lib/summary/summarizerNode/summarizerNodeUtils.d.ts.map +1 -1
  299. package/lib/summary/summarizerNode/summarizerNodeUtils.js.map +1 -1
  300. package/lib/summary/summarizerNode/summarizerNodeWithGc.d.ts +23 -8
  301. package/lib/summary/summarizerNode/summarizerNodeWithGc.d.ts.map +1 -1
  302. package/lib/summary/summarizerNode/summarizerNodeWithGc.js +59 -22
  303. package/lib/summary/summarizerNode/summarizerNodeWithGc.js.map +1 -1
  304. package/lib/summary/summarizerTypes.d.ts +16 -9
  305. package/lib/summary/summarizerTypes.d.ts.map +1 -1
  306. package/lib/summary/summarizerTypes.js.map +1 -1
  307. package/lib/summary/summaryCollection.d.ts +4 -2
  308. package/lib/summary/summaryCollection.d.ts.map +1 -1
  309. package/lib/summary/summaryCollection.js +4 -0
  310. package/lib/summary/summaryCollection.js.map +1 -1
  311. package/lib/summary/summaryFormat.d.ts +1 -0
  312. package/lib/summary/summaryFormat.d.ts.map +1 -1
  313. package/lib/summary/summaryFormat.js +2 -1
  314. package/lib/summary/summaryFormat.js.map +1 -1
  315. package/lib/summary/summaryGenerator.d.ts +14 -5
  316. package/lib/summary/summaryGenerator.d.ts.map +1 -1
  317. package/lib/summary/summaryGenerator.js +21 -8
  318. package/lib/summary/summaryGenerator.js.map +1 -1
  319. package/lib/summary/summaryManager.d.ts +4 -2
  320. package/lib/summary/summaryManager.d.ts.map +1 -1
  321. package/lib/summary/summaryManager.js +1 -1
  322. package/lib/summary/summaryManager.js.map +1 -1
  323. package/package.json +25 -41
  324. package/src/batchTracker.ts +5 -6
  325. package/src/blobManager.ts +70 -29
  326. package/src/connectionTelemetry.ts +14 -6
  327. package/src/containerRuntime.ts +124 -38
  328. package/src/dataStore.ts +3 -4
  329. package/src/dataStoreContext.ts +12 -9
  330. package/src/dataStoreContexts.ts +6 -8
  331. package/src/dataStores.ts +8 -3
  332. package/src/deltaScheduler.ts +2 -3
  333. package/src/gc/garbageCollection.ts +7 -6
  334. package/src/gc/gcDefinitions.ts +3 -4
  335. package/src/gc/gcTelemetry.ts +9 -5
  336. package/src/id-compressor/idCompressor.ts +2 -2
  337. package/src/id-compressor/uuidUtilities.ts +1 -4
  338. package/src/index.ts +2 -0
  339. package/src/metadata.ts +19 -0
  340. package/src/opLifecycle/README.md +20 -0
  341. package/src/opLifecycle/batchManager.ts +9 -1
  342. package/src/opLifecycle/definitions.ts +13 -2
  343. package/src/opLifecycle/index.ts +1 -1
  344. package/src/opLifecycle/opCompressor.ts +4 -8
  345. package/src/opLifecycle/opDecompressor.ts +43 -16
  346. package/src/opLifecycle/opGroupingManager.ts +19 -13
  347. package/src/opLifecycle/opSplitter.ts +7 -6
  348. package/src/opLifecycle/outbox.ts +172 -57
  349. package/src/opLifecycle/remoteMessageProcessor.ts +5 -1
  350. package/src/packageVersion.ts +1 -1
  351. package/src/pendingStateManager.ts +113 -129
  352. package/src/scheduleManager.ts +18 -10
  353. package/src/summary/index.ts +3 -1
  354. package/src/summary/orderedClientElection.ts +7 -20
  355. package/src/summary/runningSummarizer.ts +11 -10
  356. package/src/summary/summarizer.ts +8 -8
  357. package/src/summary/summarizerClientElection.ts +3 -2
  358. package/src/summary/summarizerHeuristics.ts +2 -2
  359. package/src/summary/summarizerNode/index.ts +1 -0
  360. package/src/summary/summarizerNode/summarizerNode.ts +121 -38
  361. package/src/summary/summarizerNode/summarizerNodeUtils.ts +27 -4
  362. package/src/summary/summarizerNode/summarizerNodeWithGc.ts +73 -27
  363. package/src/summary/summarizerTypes.ts +19 -14
  364. package/src/summary/summaryCollection.ts +10 -4
  365. package/src/summary/summaryFormat.ts +5 -1
  366. package/src/summary/summaryGenerator.ts +38 -11
  367. package/src/summary/summaryManager.ts +9 -9
@@ -1 +1 @@
1
- {"version":3,"file":"opGroupingManager.js","sourceRoot":"","sources":["../../src/opLifecycle/opGroupingManager.ts"],"names":[],"mappings":";AAAA;;;GAGG;;;AAEH,+DAAsD;AAEtD,0BAAmE;AASnE,MAAa,iBAAiB;IAG7B,YAA6B,sBAA+B;QAA/B,2BAAsB,GAAtB,sBAAsB,CAAS;IAAG,CAAC;IAEzD,UAAU,CAAC,KAAa;QAC9B,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,sBAAsB,EAAE;YAC7D,OAAO,KAAK,CAAC;SACb;QAED,KAAK,MAAM,OAAO,IAAI,KAAK,CAAC,OAAO,EAAE;YACpC,yEAAyE;YACzE,IAAI,OAAO,CAAC,mBAAmB,CAAC,IAAI,KAAK,wBAAoB,CAAC,UAAU,EAAE;gBACzE,OAAO,KAAK,CAAC;aACb;YACD,IAAI,OAAO,CAAC,QAAQ,EAAE;gBACrB,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;gBAC3C,IAAA,qBAAM,EAAC,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,KAAK,CAAC,oCAAoC,CAAC,CAAC;gBACpE,IAAA,qBAAM,EACL,IAAI,CAAC,MAAM,KAAK,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,OAAO,EACxC,KAAK,CAAC,4BAA4B,CAClC,CAAC;aACF;SACD;QAED,MAAM,mBAAmB,GAAG;YAC3B,IAAI,EAAE,iBAAiB,CAAC,cAAc;YACtC,QAAQ,EAAE,KAAK,CAAC,OAAO,CAAC,GAAG,CAAkB,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;gBAC1D,QAAQ,EAAE,OAAO,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC;gBACnF,QAAQ,EAAE,OAAO,CAAC,QAAQ;gBAC1B,WAAW,EAAE,OAAO,CAAC,WAAW;aAChC,CAAC,CAAC;SACH,CAAC;QAEF,MAAM,YAAY,mCACd,KAAK,KACR,OAAO,EAAE;gBACR;oBACC,eAAe,EAAE,SAAS;oBAC1B,QAAQ,EAAE,SAAS;oBACnB,uBAAuB,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,uBAAuB;oBACjE,2CAA2C;oBAC3C,mBAAmB,EAAE,mBAA8C;oBACnE,QAAQ,EAAE,IAAI,CAAC,SAAS,CAAC,mBAAmB,CAAC;iBAC7C;aACD,GACD,CAAC;QACF,OAAO,YAAY,CAAC;IACrB,CAAC;IAEM,SAAS,CAAC,EAA6B;;QAC7C,IAAI,CAAA,MAAA,EAAE,CAAC,QAAQ,0CAAE,IAAI,MAAK,iBAAiB,CAAC,cAAc,EAAE;YAC3D,OAAO,CAAC,EAAE,CAAC,CAAC;SACZ;QAED,MAAM,QAAQ,GAAG,EAAE,CAAC,QAAQ,CAAC,QAA6B,CAAC;QAC3D,IAAI,OAAO,GAAG,CAAC,CAAC;QAChB,OAAO,QAAQ,CAAC,GAAG,CAAC,CAAC,UAAU,EAAE,EAAE,CAAC,iCAChC,EAAE,KACL,oBAAoB,EAAE,OAAO,EAAE,EAC/B,QAAQ,EAAE,UAAU,CAAC,QAAQ,EAC7B,QAAQ,EAAE,UAAU,CAAC,QAAQ,EAC7B,WAAW,EAAE,UAAU,CAAC,WAAW,IAClC,CAAC,CAAC;IACL,CAAC;;AAhEF,8CAiEC;AAhEO,gCAAc,GAAG,cAAc,CAAC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { assert } from \"@fluidframework/common-utils\";\nimport { ISequencedDocumentMessage } from \"@fluidframework/protocol-definitions\";\nimport { ContainerMessageType, ContainerRuntimeMessage } from \"..\";\nimport { IBatch } from \"./definitions\";\n\ninterface IGroupedMessage {\n\tcontents?: unknown;\n\tmetadata?: Record<string, unknown>;\n\tcompression?: string;\n}\n\nexport class OpGroupingManager {\n\tstatic groupedBatchOp = \"groupedBatch\";\n\n\tconstructor(private readonly groupedBatchingEnabled: boolean) {}\n\n\tpublic groupBatch(batch: IBatch): IBatch {\n\t\tif (batch.content.length < 2 || !this.groupedBatchingEnabled) {\n\t\t\treturn batch;\n\t\t}\n\n\t\tfor (const message of batch.content) {\n\t\t\t// Blob attaches cannot be grouped (grouped batching would hide metadata)\n\t\t\tif (message.deserializedContent.type === ContainerMessageType.BlobAttach) {\n\t\t\t\treturn batch;\n\t\t\t}\n\t\t\tif (message.metadata) {\n\t\t\t\tconst keys = Object.keys(message.metadata);\n\t\t\t\tassert(keys.length < 2, 0x5dd /* cannot group ops with metadata */);\n\t\t\t\tassert(\n\t\t\t\t\tkeys.length === 0 || keys[0] === \"batch\",\n\t\t\t\t\t0x5de /* unexpected op metadata */,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\tconst deserializedContent = {\n\t\t\ttype: OpGroupingManager.groupedBatchOp,\n\t\t\tcontents: batch.content.map<IGroupedMessage>((message) => ({\n\t\t\t\tcontents: message.contents === undefined ? undefined : JSON.parse(message.contents),\n\t\t\t\tmetadata: message.metadata,\n\t\t\t\tcompression: message.compression,\n\t\t\t})),\n\t\t};\n\n\t\tconst groupedBatch: IBatch = {\n\t\t\t...batch,\n\t\t\tcontent: [\n\t\t\t\t{\n\t\t\t\t\tlocalOpMetadata: undefined,\n\t\t\t\t\tmetadata: undefined,\n\t\t\t\t\treferenceSequenceNumber: batch.content[0].referenceSequenceNumber,\n\t\t\t\t\t// Need deserializedContent for back-compat\n\t\t\t\t\tdeserializedContent: deserializedContent as ContainerRuntimeMessage,\n\t\t\t\t\tcontents: JSON.stringify(deserializedContent),\n\t\t\t\t},\n\t\t\t],\n\t\t};\n\t\treturn groupedBatch;\n\t}\n\n\tpublic ungroupOp(op: ISequencedDocumentMessage): ISequencedDocumentMessage[] {\n\t\tif (op.contents?.type !== OpGroupingManager.groupedBatchOp) {\n\t\t\treturn [op];\n\t\t}\n\n\t\tconst messages = op.contents.contents as IGroupedMessage[];\n\t\tlet fakeCsn = 1;\n\t\treturn messages.map((subMessage) => ({\n\t\t\t...op,\n\t\t\tclientSequenceNumber: fakeCsn++,\n\t\t\tcontents: subMessage.contents,\n\t\t\tmetadata: subMessage.metadata,\n\t\t\tcompression: subMessage.compression,\n\t\t}));\n\t}\n}\n"]}
1
+ {"version":3,"file":"opGroupingManager.js","sourceRoot":"","sources":["../../src/opLifecycle/opGroupingManager.ts"],"names":[],"mappings":";AAAA;;;GAGG;;;AAEH,+DAAsD;AAmBtD,MAAa,iBAAiB;IAG7B,YAA6B,sBAA+B;QAA/B,2BAAsB,GAAtB,sBAAsB,CAAS;IAAG,CAAC;IAEzD,UAAU,CAAC,KAAa;QAC9B,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,sBAAsB,EAAE;YAC7D,OAAO,KAAK,CAAC;SACb;QAED,KAAK,MAAM,OAAO,IAAI,KAAK,CAAC,OAAO,EAAE;YACpC,IAAI,OAAO,CAAC,QAAQ,EAAE;gBACrB,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;gBAC3C,IAAA,qBAAM,EAAC,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,KAAK,CAAC,oCAAoC,CAAC,CAAC;gBACpE,IAAA,qBAAM,EACL,IAAI,CAAC,MAAM,KAAK,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,OAAO,EACxC,KAAK,CAAC,4BAA4B,CAClC,CAAC;aACF;SACD;QAED,MAAM,iBAAiB,GAAG,IAAI,CAAC,SAAS,CAAC;YACxC,IAAI,EAAE,iBAAiB,CAAC,cAAc;YACtC,QAAQ,EAAE,KAAK,CAAC,OAAO,CAAC,GAAG,CAAkB,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;gBAC1D,QAAQ,EAAE,OAAO,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC;gBACnF,QAAQ,EAAE,OAAO,CAAC,QAAQ;gBAC1B,WAAW,EAAE,OAAO,CAAC,WAAW;aAChC,CAAC,CAAC;SACH,CAAC,CAAC;QAEH,MAAM,YAAY,mCACd,KAAK,KACR,OAAO,EAAE;gBACR;oBACC,eAAe,EAAE,SAAS;oBAC1B,QAAQ,EAAE,SAAS;oBACnB,uBAAuB,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,uBAAuB;oBACjE,QAAQ,EAAE,iBAAiB;oBAC3B,IAAI,EAAE,iBAAiB,CAAC,cAAsC;iBAC9D;aACD,GACD,CAAC;QACF,OAAO,YAAY,CAAC;IACrB,CAAC;IAEM,SAAS,CAAC,EAA6B;;QAC7C,IACC,CAAA,MAAC,EAAE,CAAC,QAA2C,0CAAE,IAAI;YACrD,iBAAiB,CAAC,cAAc,EAC/B;YACD,OAAO,CAAC,EAAE,CAAC,CAAC;SACZ;QAED,MAAM,QAAQ,GAAI,EAAE,CAAC,QAAyC,CAAC,QAAQ,CAAC;QACxE,IAAI,OAAO,GAAG,CAAC,CAAC;QAChB,OAAO,QAAQ,CAAC,GAAG,CAAC,CAAC,UAAU,EAAE,EAAE,CAAC,iCAChC,EAAE,KACL,oBAAoB,EAAE,OAAO,EAAE,EAC/B,QAAQ,EAAE,UAAU,CAAC,QAAQ,EAC7B,QAAQ,EAAE,UAAU,CAAC,QAAQ,EAC7B,WAAW,EAAE,UAAU,CAAC,WAAW,IAClC,CAAC,CAAC;IACL,CAAC;;AA9DF,8CA+DC;AA9DgB,gCAAc,GAAG,cAAc,CAAC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { assert } from \"@fluidframework/common-utils\";\nimport { ISequencedDocumentMessage } from \"@fluidframework/protocol-definitions\";\nimport { ContainerMessageType } from \"..\";\nimport { IBatch } from \"./definitions\";\n\n/**\n * Grouping makes assumptions about the shape of message contents. This interface codifies those assumptions, but does not validate them.\n */\ninterface IGroupedBatchMessageContents {\n\ttype: typeof OpGroupingManager.groupedBatchOp;\n\tcontents: IGroupedMessage[];\n}\n\ninterface IGroupedMessage {\n\tcontents?: unknown;\n\tmetadata?: Record<string, unknown>;\n\tcompression?: string;\n}\n\nexport class OpGroupingManager {\n\tstatic readonly groupedBatchOp = \"groupedBatch\";\n\n\tconstructor(private readonly groupedBatchingEnabled: boolean) {}\n\n\tpublic groupBatch(batch: IBatch): IBatch {\n\t\tif (batch.content.length < 2 || !this.groupedBatchingEnabled) {\n\t\t\treturn batch;\n\t\t}\n\n\t\tfor (const message of batch.content) {\n\t\t\tif (message.metadata) {\n\t\t\t\tconst keys = Object.keys(message.metadata);\n\t\t\t\tassert(keys.length < 2, 0x5dd /* cannot group ops with metadata */);\n\t\t\t\tassert(\n\t\t\t\t\tkeys.length === 0 || keys[0] === \"batch\",\n\t\t\t\t\t0x5de /* unexpected op metadata */,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\tconst serializedContent = JSON.stringify({\n\t\t\ttype: OpGroupingManager.groupedBatchOp,\n\t\t\tcontents: batch.content.map<IGroupedMessage>((message) => ({\n\t\t\t\tcontents: message.contents === undefined ? undefined : JSON.parse(message.contents),\n\t\t\t\tmetadata: message.metadata,\n\t\t\t\tcompression: message.compression,\n\t\t\t})),\n\t\t});\n\n\t\tconst groupedBatch: IBatch = {\n\t\t\t...batch,\n\t\t\tcontent: [\n\t\t\t\t{\n\t\t\t\t\tlocalOpMetadata: undefined,\n\t\t\t\t\tmetadata: undefined,\n\t\t\t\t\treferenceSequenceNumber: batch.content[0].referenceSequenceNumber,\n\t\t\t\t\tcontents: serializedContent,\n\t\t\t\t\ttype: OpGroupingManager.groupedBatchOp as ContainerMessageType,\n\t\t\t\t},\n\t\t\t],\n\t\t};\n\t\treturn groupedBatch;\n\t}\n\n\tpublic ungroupOp(op: ISequencedDocumentMessage): ISequencedDocumentMessage[] {\n\t\tif (\n\t\t\t(op.contents as { type?: unknown } | undefined)?.type !==\n\t\t\tOpGroupingManager.groupedBatchOp\n\t\t) {\n\t\t\treturn [op];\n\t\t}\n\n\t\tconst messages = (op.contents as IGroupedBatchMessageContents).contents;\n\t\tlet fakeCsn = 1;\n\t\treturn messages.map((subMessage) => ({\n\t\t\t...op,\n\t\t\tclientSequenceNumber: fakeCsn++,\n\t\t\tcontents: subMessage.contents,\n\t\t\tmetadata: subMessage.metadata,\n\t\t\tcompression: subMessage.compression,\n\t\t}));\n\t}\n}\n"]}
@@ -2,7 +2,7 @@
2
2
  * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
3
  * Licensed under the MIT License.
4
4
  */
5
- import { ITelemetryLogger } from "@fluidframework/common-definitions";
5
+ import { ITelemetryLoggerExt } from "@fluidframework/telemetry-utils";
6
6
  import { IBatchMessage } from "@fluidframework/container-definitions";
7
7
  import { ISequencedDocumentMessage } from "@fluidframework/protocol-definitions";
8
8
  import { BatchMessage, IBatch, IChunkedOp, IMessageProcessingResult } from "./definitions";
@@ -15,7 +15,7 @@ export declare class OpSplitter {
15
15
  private readonly maxBatchSizeInBytes;
16
16
  private readonly chunkMap;
17
17
  private readonly logger;
18
- constructor(chunks: [string, string[]][], submitBatchFn: ((batch: IBatchMessage[], referenceSequenceNumber?: number) => number) | undefined, chunkSizeInBytes: number, maxBatchSizeInBytes: number, logger: ITelemetryLogger);
18
+ constructor(chunks: [string, string[]][], submitBatchFn: ((batch: IBatchMessage[], referenceSequenceNumber?: number) => number) | undefined, chunkSizeInBytes: number, maxBatchSizeInBytes: number, logger: ITelemetryLoggerExt);
19
19
  get isBatchChunkingEnabled(): boolean;
20
20
  get chunks(): ReadonlyMap<string, string[]>;
21
21
  processRemoteMessage(message: ISequencedDocumentMessage): IMessageProcessingResult;
@@ -1 +1 @@
1
- {"version":3,"file":"opSplitter.d.ts","sourceRoot":"","sources":["../../src/opLifecycle/opSplitter.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,gBAAgB,EAAE,MAAM,oCAAoC,CAAC;AAEtE,OAAO,EAAE,aAAa,EAAE,MAAM,uCAAuC,CAAC;AAKtE,OAAO,EAAE,yBAAyB,EAAE,MAAM,sCAAsC,CAAC;AAIjF,OAAO,EAAE,YAAY,EAAE,MAAM,EAAE,UAAU,EAAE,wBAAwB,EAAE,MAAM,eAAe,CAAC;AAE3F;;GAEG;AACH,qBAAa,UAAU;IAOrB,OAAO,CAAC,QAAQ,CAAC,aAAa;aAGd,gBAAgB,EAAE,MAAM;IACxC,OAAO,CAAC,QAAQ,CAAC,mBAAmB;IATrC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAwB;IACjD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC;gBAGvB,MAAM,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,EAAE,EACX,aAAa,EAC3B,CAAC,CAAC,KAAK,EAAE,aAAa,EAAE,EAAE,uBAAuB,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC,GACtE,SAAS,EACI,gBAAgB,EAAE,MAAM,EACvB,mBAAmB,EAAE,MAAM,EAC5C,MAAM,EAAE,gBAAgB;IAMzB,IAAW,sBAAsB,IAAI,OAAO,CAE3C;IAED,IAAW,MAAM,IAAI,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAEjD;IAEM,oBAAoB,CAAC,OAAO,EAAE,yBAAyB,GAAG,wBAAwB;IAoClF,kBAAkB,CAAC,QAAQ,EAAE,MAAM;IAM1C,OAAO,CAAC,QAAQ;IA0BhB;;;;;;;;;;;;;;;;;;;;;OAqBG;IACI,sBAAsB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM;CAkEpD;AAoBD;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,OAAO,OACf,YAAY,oBACE,MAAM,YACf,OAAO,KACd,UAAU,EAqCZ,CAAC"}
1
+ {"version":3,"file":"opSplitter.d.ts","sourceRoot":"","sources":["../../src/opLifecycle/opSplitter.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,mBAAmB,EAAe,MAAM,iCAAiC,CAAC;AAEnF,OAAO,EAAE,aAAa,EAAE,MAAM,uCAAuC,CAAC;AAKtE,OAAO,EAAE,yBAAyB,EAAE,MAAM,sCAAsC,CAAC;AAGjF,OAAO,EAAE,YAAY,EAAE,MAAM,EAAE,UAAU,EAAE,wBAAwB,EAAE,MAAM,eAAe,CAAC;AAE3F;;GAEG;AACH,qBAAa,UAAU;IAOrB,OAAO,CAAC,QAAQ,CAAC,aAAa;aAGd,gBAAgB,EAAE,MAAM;IACxC,OAAO,CAAC,QAAQ,CAAC,mBAAmB;IATrC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAwB;IACjD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC;gBAGvB,MAAM,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,EAAE,EACX,aAAa,EAC3B,CAAC,CAAC,KAAK,EAAE,aAAa,EAAE,EAAE,uBAAuB,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC,GACtE,SAAS,EACI,gBAAgB,EAAE,MAAM,EACvB,mBAAmB,EAAE,MAAM,EAC5C,MAAM,EAAE,mBAAmB;IAM5B,IAAW,sBAAsB,IAAI,OAAO,CAE3C;IAED,IAAW,MAAM,IAAI,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAEjD;IAEM,oBAAoB,CAAC,OAAO,EAAE,yBAAyB,GAAG,wBAAwB;IAsClF,kBAAkB,CAAC,QAAQ,EAAE,MAAM;IAM1C,OAAO,CAAC,QAAQ;IA0BhB;;;;;;;;;;;;;;;;;;;;;OAqBG;IACI,sBAAsB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM;CAkEpD;AAoBD;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,OAAO,OACf,YAAY,oBACE,MAAM,YACf,OAAO,KACd,UAAU,EAqCZ,CAAC"}
@@ -5,9 +5,9 @@
5
5
  */
6
6
  Object.defineProperty(exports, "__esModule", { value: true });
7
7
  exports.splitOp = exports.OpSplitter = void 0;
8
+ const telemetry_utils_1 = require("@fluidframework/telemetry-utils");
8
9
  const common_utils_1 = require("@fluidframework/common-utils");
9
10
  const container_utils_1 = require("@fluidframework/container-utils");
10
- const telemetry_utils_1 = require("@fluidframework/telemetry-utils");
11
11
  const containerRuntime_1 = require("../containerRuntime");
12
12
  const batchManager_1 = require("./batchManager");
13
13
  /**
@@ -34,6 +34,8 @@ class OpSplitter {
34
34
  state: "Skipped",
35
35
  };
36
36
  }
37
+ // TODO: Verify whether this should be able to handle server-generated ops (with null clientId)
38
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
37
39
  const clientId = message.clientId;
38
40
  const chunkedContent = message.contents;
39
41
  this.addChunk(clientId, chunkedContent, message);
@@ -147,7 +149,7 @@ const chunkToBatchMessage = (chunk, referenceSequenceNumber, metadata = undefine
147
149
  };
148
150
  return {
149
151
  contents: JSON.stringify(payload),
150
- deserializedContent: payload,
152
+ type: payload.type,
151
153
  metadata,
152
154
  localOpMetadata: undefined,
153
155
  referenceSequenceNumber,
@@ -175,7 +177,7 @@ const splitOp = (op, chunkSizeInBytes, extraOp = false) => {
175
177
  const chunk = {
176
178
  chunkId,
177
179
  contents: op.contents.substr(offset, chunkSizeInBytes),
178
- originalType: op.deserializedContent.type,
180
+ originalType: op.type,
179
181
  totalChunks: chunkCount,
180
182
  };
181
183
  if (chunkId === chunkCount) {
@@ -1 +1 @@
1
- {"version":3,"file":"opSplitter.js","sourceRoot":"","sources":["../../src/opLifecycle/opSplitter.ts"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { ITelemetryLogger } from \"@fluidframework/common-definitions\";\nimport { assert } from \"@fluidframework/common-utils\";\nimport { IBatchMessage } from \"@fluidframework/container-definitions\";\nimport {\n\tDataCorruptionError,\n\textractSafePropertiesFromMessage,\n} from \"@fluidframework/container-utils\";\nimport { ISequencedDocumentMessage } from \"@fluidframework/protocol-definitions\";\nimport { ChildLogger } from \"@fluidframework/telemetry-utils\";\nimport { ContainerMessageType, ContainerRuntimeMessage } from \"../containerRuntime\";\nimport { estimateSocketSize } from \"./batchManager\";\nimport { BatchMessage, IBatch, IChunkedOp, IMessageProcessingResult } from \"./definitions\";\n\n/**\n * Responsible for creating and reconstructing chunked messages.\n */\nexport class OpSplitter {\n\t// Local copy of incomplete received chunks.\n\tprivate readonly chunkMap: Map<string, string[]>;\n\tprivate readonly logger;\n\n\tconstructor(\n\t\tchunks: [string, string[]][],\n\t\tprivate readonly submitBatchFn:\n\t\t\t| ((batch: IBatchMessage[], referenceSequenceNumber?: number) => number)\n\t\t\t| undefined,\n\t\tpublic readonly chunkSizeInBytes: number,\n\t\tprivate readonly maxBatchSizeInBytes: number,\n\t\tlogger: ITelemetryLogger,\n\t) {\n\t\tthis.chunkMap = new Map<string, string[]>(chunks);\n\t\tthis.logger = ChildLogger.create(logger, \"OpSplitter\");\n\t}\n\n\tpublic get isBatchChunkingEnabled(): boolean {\n\t\treturn this.chunkSizeInBytes < Number.POSITIVE_INFINITY && this.submitBatchFn !== undefined;\n\t}\n\n\tpublic get chunks(): ReadonlyMap<string, string[]> {\n\t\treturn this.chunkMap;\n\t}\n\n\tpublic processRemoteMessage(message: ISequencedDocumentMessage): IMessageProcessingResult {\n\t\tif (message.type !== ContainerMessageType.ChunkedOp) {\n\t\t\treturn {\n\t\t\t\tmessage,\n\t\t\t\tstate: \"Skipped\",\n\t\t\t};\n\t\t}\n\n\t\tconst clientId = message.clientId;\n\t\tconst chunkedContent = message.contents as IChunkedOp;\n\t\tthis.addChunk(clientId, chunkedContent, message);\n\n\t\tif (chunkedContent.chunkId < chunkedContent.totalChunks) {\n\t\t\t// We are processing the op in chunks but haven't reached\n\t\t\t// the last chunk yet in order to reconstruct the original op\n\t\t\treturn {\n\t\t\t\tmessage,\n\t\t\t\tstate: \"Accepted\",\n\t\t\t};\n\t\t}\n\n\t\t// eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n\t\tconst serializedContent = this.chunkMap.get(clientId)!.join(\"\");\n\t\tthis.clearPartialChunks(clientId);\n\n\t\tconst newMessage = { ...message };\n\t\tnewMessage.contents = serializedContent === \"\" ? undefined : JSON.parse(serializedContent);\n\t\tnewMessage.type = chunkedContent.originalType;\n\t\tnewMessage.metadata = chunkedContent.originalMetadata;\n\t\tnewMessage.compression = chunkedContent.originalCompression;\n\t\treturn {\n\t\t\tmessage: newMessage,\n\t\t\tstate: \"Processed\",\n\t\t};\n\t}\n\n\tpublic clearPartialChunks(clientId: string) {\n\t\tif (this.chunkMap.has(clientId)) {\n\t\t\tthis.chunkMap.delete(clientId);\n\t\t}\n\t}\n\n\tprivate addChunk(\n\t\tclientId: string,\n\t\tchunkedContent: IChunkedOp,\n\t\toriginalMessage: ISequencedDocumentMessage,\n\t) {\n\t\tlet map = this.chunkMap.get(clientId);\n\t\tif (map === undefined) {\n\t\t\tmap = [];\n\t\t\tthis.chunkMap.set(clientId, map);\n\t\t}\n\n\t\tif (chunkedContent.chunkId !== map.length + 1) {\n\t\t\t// We are expecting the chunks to be processed sequentially, in the same order as they are sent.\n\t\t\t// Therefore, the chunkId of the incoming op needs to match the length of the array (1-based indexing)\n\t\t\t// holding the existing chunks for that particular clientId.\n\t\t\tthrow new DataCorruptionError(\"Chunk Id mismatch\", {\n\t\t\t\t...extractSafePropertiesFromMessage(originalMessage),\n\t\t\t\tchunkMapLength: map.length,\n\t\t\t\tchunkId: chunkedContent.chunkId,\n\t\t\t\ttotalChunks: chunkedContent.totalChunks,\n\t\t\t});\n\t\t}\n\n\t\tmap.push(chunkedContent.contents);\n\t}\n\n\t/**\n\t * Splits the first op of a compressed batch in chunks, sends the chunks separately and\n\t * returns a new batch composed of the last chunk and the rest of the ops in the original batch.\n\t *\n\t * A compressed batch is formed by one large op at the first position, followed by a series of placeholder ops\n\t * which are used in order to reserve the sequence numbers for when the first op gets unrolled into the original\n\t * uncompressed ops at ingestion in the runtime.\n\t *\n\t * If the first op is too large, it can be chunked (split into smaller op) which can be sent individually over the wire\n\t * and accumulate at ingestion, until the last op in the chunk is processed, when the original op is unrolled.\n\t *\n\t * This method will send the first N - 1 chunks separately and use the last chunk as the first message in the result batch\n\t * and then appends the original placeholder ops. This will ensure that the batch semantics of the original (non-compressed) batch\n\t * are preserved, as the original chunked op will be unrolled by the runtime when the first message in the batch is processed\n\t * (as it is the last chunk).\n\t *\n\t * To illustrate, if the input is `[largeOp, emptyOp, emptyOp]`, `largeOp` will be split into `[chunk1, chunk2, chunk3, chunk4]`.\n\t * `chunk1`, `chunk2` and `chunk3` will be sent individually and `[chunk4, emptyOp, emptyOp]` will be returned.\n\t *\n\t * @param batch - the compressed batch which needs to be processed\n\t * @returns A new adjusted batch which can be sent over the wire\n\t */\n\tpublic splitFirstBatchMessage(batch: IBatch): IBatch {\n\t\tassert(this.isBatchChunkingEnabled, 0x513 /* Chunking needs to be enabled */);\n\t\tassert(\n\t\t\tbatch.contentSizeInBytes > 0 && batch.content.length > 0,\n\t\t\t0x514 /* Batch needs to be non-empty */,\n\t\t);\n\t\tassert(\n\t\t\tbatch.referenceSequenceNumber !== undefined,\n\t\t\t0x58a /* Batch must have a reference sequence number if non-empty */,\n\t\t);\n\t\tassert(this.chunkSizeInBytes !== 0, 0x515 /* Chunk size needs to be non-zero */);\n\t\tassert(\n\t\t\tthis.chunkSizeInBytes < this.maxBatchSizeInBytes,\n\t\t\t0x516 /* Chunk size needs to be smaller than the max batch size */,\n\t\t);\n\n\t\tconst firstMessage = batch.content[0]; // we expect this to be the large compressed op, which needs to be split\n\t\tassert(\n\t\t\t(firstMessage.contents?.length ?? 0) >= this.chunkSizeInBytes,\n\t\t\t0x518 /* First message in the batch needs to be chunkable */,\n\t\t);\n\n\t\tconst restOfMessages = batch.content.slice(1); // we expect these to be empty ops, created to reserve sequence numbers\n\t\tconst socketSize = estimateSocketSize(batch);\n\t\tconst chunks = splitOp(\n\t\t\tfirstMessage,\n\t\t\tthis.chunkSizeInBytes,\n\t\t\t// If we estimate that the socket batch size will exceed the batch limit\n\t\t\t// we will inject an empty op to minimize the risk of the payload failing due to\n\t\t\t// the overhead from the trailing empty ops in the batch.\n\t\t\tsocketSize >= this.maxBatchSizeInBytes,\n\t\t);\n\n\t\tassert(this.submitBatchFn !== undefined, 0x519 /* We don't support old loaders */);\n\t\t// Send the first N-1 chunks immediately\n\t\tfor (const chunk of chunks.slice(0, -1)) {\n\t\t\tthis.submitBatchFn(\n\t\t\t\t[chunkToBatchMessage(chunk, batch.referenceSequenceNumber)],\n\t\t\t\tbatch.referenceSequenceNumber,\n\t\t\t);\n\t\t}\n\n\t\t// The last chunk will be part of the new batch and needs to\n\t\t// preserve the batch metadata of the original batch\n\t\tconst lastChunk = chunkToBatchMessage(\n\t\t\tchunks[chunks.length - 1],\n\t\t\tbatch.referenceSequenceNumber,\n\t\t\t{ batch: firstMessage.metadata?.batch },\n\t\t);\n\n\t\tthis.logger.sendPerformanceEvent({\n\t\t\t// Used to be \"Chunked compressed batch\"\n\t\t\teventName: \"CompressedChunkedBatch\",\n\t\t\tlength: batch.content.length,\n\t\t\tsizeInBytes: batch.contentSizeInBytes,\n\t\t\tchunks: chunks.length,\n\t\t\tchunkSizeInBytes: this.chunkSizeInBytes,\n\t\t\tsocketSize,\n\t\t});\n\n\t\treturn {\n\t\t\tcontent: [lastChunk, ...restOfMessages],\n\t\t\tcontentSizeInBytes: lastChunk.contents?.length ?? 0,\n\t\t\treferenceSequenceNumber: batch.referenceSequenceNumber,\n\t\t};\n\t}\n}\n\nconst chunkToBatchMessage = (\n\tchunk: IChunkedOp,\n\treferenceSequenceNumber: number,\n\tmetadata: Record<string, unknown> | undefined = undefined,\n): BatchMessage => {\n\tconst payload: ContainerRuntimeMessage = {\n\t\ttype: ContainerMessageType.ChunkedOp,\n\t\tcontents: chunk,\n\t};\n\treturn {\n\t\tcontents: JSON.stringify(payload),\n\t\tdeserializedContent: payload,\n\t\tmetadata,\n\t\tlocalOpMetadata: undefined,\n\t\treferenceSequenceNumber,\n\t};\n};\n\n/**\n * Splits an op into smaller ops (chunks), based on the size of the op and the `chunkSizeInBytes` parameter.\n *\n * The last op of the result will be bundled with empty ops in the same batch. There is a risk of the batch payload\n * exceeding the 1MB limit due to the overhead from the empty ops. If the last op is large, the risk is even higher.\n * To minimize the odds, an extra empty op can be added to the result using the `extraOp` parameter.\n *\n * @param op - the op to be split\n * @param chunkSizeInBytes - how large should the chunks be\n * @param extraOp - should an extra empty op be added to the result\n * @returns an array of chunked ops\n */\nexport const splitOp = (\n\top: BatchMessage,\n\tchunkSizeInBytes: number,\n\textraOp: boolean = false,\n): IChunkedOp[] => {\n\tconst chunks: IChunkedOp[] = [];\n\tassert(\n\t\top.contents !== undefined && op.contents !== null,\n\t\t0x51a /* We should have something to chunk */,\n\t);\n\n\tconst contentLength = op.contents.length;\n\tconst chunkCount = Math.floor((contentLength - 1) / chunkSizeInBytes) + 1 + (extraOp ? 1 : 0);\n\tlet offset = 0;\n\tfor (let chunkId = 1; chunkId <= chunkCount; chunkId++) {\n\t\tconst chunk: IChunkedOp = {\n\t\t\tchunkId,\n\t\t\tcontents: op.contents.substr(offset, chunkSizeInBytes),\n\t\t\toriginalType: op.deserializedContent.type,\n\t\t\ttotalChunks: chunkCount,\n\t\t};\n\n\t\tif (chunkId === chunkCount) {\n\t\t\t// We don't need to port these to all the chunks,\n\t\t\t// as we rebuild the original op when we process the\n\t\t\t// last chunk, therefore it is the only one that needs it.\n\t\t\tchunk.originalMetadata = op.metadata;\n\t\t\tchunk.originalCompression = op.compression;\n\t\t}\n\n\t\tchunks.push(chunk);\n\t\toffset += chunkSizeInBytes;\n\t\tassert(\n\t\t\tchunkId >= chunkCount - 1 || offset <= contentLength,\n\t\t\t0x58b /* Content offset within bounds */,\n\t\t);\n\t}\n\n\tassert(offset >= contentLength, 0x58c /* Content offset equal or larger than content length */);\n\tassert(chunks.length === chunkCount, 0x5a5 /* Expected number of chunks */);\n\treturn chunks;\n};\n"]}
1
+ {"version":3,"file":"opSplitter.js","sourceRoot":"","sources":["../../src/opLifecycle/opSplitter.ts"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { ITelemetryLoggerExt, ChildLogger } from \"@fluidframework/telemetry-utils\";\nimport { assert } from \"@fluidframework/common-utils\";\nimport { IBatchMessage } from \"@fluidframework/container-definitions\";\nimport {\n\tDataCorruptionError,\n\textractSafePropertiesFromMessage,\n} from \"@fluidframework/container-utils\";\nimport { ISequencedDocumentMessage } from \"@fluidframework/protocol-definitions\";\nimport { ContainerMessageType, ContainerRuntimeMessage } from \"../containerRuntime\";\nimport { estimateSocketSize } from \"./batchManager\";\nimport { BatchMessage, IBatch, IChunkedOp, IMessageProcessingResult } from \"./definitions\";\n\n/**\n * Responsible for creating and reconstructing chunked messages.\n */\nexport class OpSplitter {\n\t// Local copy of incomplete received chunks.\n\tprivate readonly chunkMap: Map<string, string[]>;\n\tprivate readonly logger;\n\n\tconstructor(\n\t\tchunks: [string, string[]][],\n\t\tprivate readonly submitBatchFn:\n\t\t\t| ((batch: IBatchMessage[], referenceSequenceNumber?: number) => number)\n\t\t\t| undefined,\n\t\tpublic readonly chunkSizeInBytes: number,\n\t\tprivate readonly maxBatchSizeInBytes: number,\n\t\tlogger: ITelemetryLoggerExt,\n\t) {\n\t\tthis.chunkMap = new Map<string, string[]>(chunks);\n\t\tthis.logger = ChildLogger.create(logger, \"OpSplitter\");\n\t}\n\n\tpublic get isBatchChunkingEnabled(): boolean {\n\t\treturn this.chunkSizeInBytes < Number.POSITIVE_INFINITY && this.submitBatchFn !== undefined;\n\t}\n\n\tpublic get chunks(): ReadonlyMap<string, string[]> {\n\t\treturn this.chunkMap;\n\t}\n\n\tpublic processRemoteMessage(message: ISequencedDocumentMessage): IMessageProcessingResult {\n\t\tif (message.type !== ContainerMessageType.ChunkedOp) {\n\t\t\treturn {\n\t\t\t\tmessage,\n\t\t\t\tstate: \"Skipped\",\n\t\t\t};\n\t\t}\n\n\t\t// TODO: Verify whether this should be able to handle server-generated ops (with null clientId)\n\t\t// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion\n\t\tconst clientId = message.clientId as string;\n\t\tconst chunkedContent = message.contents as IChunkedOp;\n\t\tthis.addChunk(clientId, chunkedContent, message);\n\n\t\tif (chunkedContent.chunkId < chunkedContent.totalChunks) {\n\t\t\t// We are processing the op in chunks but haven't reached\n\t\t\t// the last chunk yet in order to reconstruct the original op\n\t\t\treturn {\n\t\t\t\tmessage,\n\t\t\t\tstate: \"Accepted\",\n\t\t\t};\n\t\t}\n\n\t\t// eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n\t\tconst serializedContent = this.chunkMap.get(clientId)!.join(\"\");\n\t\tthis.clearPartialChunks(clientId);\n\n\t\tconst newMessage = { ...message };\n\t\tnewMessage.contents = serializedContent === \"\" ? undefined : JSON.parse(serializedContent);\n\t\tnewMessage.type = chunkedContent.originalType;\n\t\tnewMessage.metadata = chunkedContent.originalMetadata;\n\t\tnewMessage.compression = chunkedContent.originalCompression;\n\t\treturn {\n\t\t\tmessage: newMessage,\n\t\t\tstate: \"Processed\",\n\t\t};\n\t}\n\n\tpublic clearPartialChunks(clientId: string) {\n\t\tif (this.chunkMap.has(clientId)) {\n\t\t\tthis.chunkMap.delete(clientId);\n\t\t}\n\t}\n\n\tprivate addChunk(\n\t\tclientId: string,\n\t\tchunkedContent: IChunkedOp,\n\t\toriginalMessage: ISequencedDocumentMessage,\n\t) {\n\t\tlet map = this.chunkMap.get(clientId);\n\t\tif (map === undefined) {\n\t\t\tmap = [];\n\t\t\tthis.chunkMap.set(clientId, map);\n\t\t}\n\n\t\tif (chunkedContent.chunkId !== map.length + 1) {\n\t\t\t// We are expecting the chunks to be processed sequentially, in the same order as they are sent.\n\t\t\t// Therefore, the chunkId of the incoming op needs to match the length of the array (1-based indexing)\n\t\t\t// holding the existing chunks for that particular clientId.\n\t\t\tthrow new DataCorruptionError(\"Chunk Id mismatch\", {\n\t\t\t\t...extractSafePropertiesFromMessage(originalMessage),\n\t\t\t\tchunkMapLength: map.length,\n\t\t\t\tchunkId: chunkedContent.chunkId,\n\t\t\t\ttotalChunks: chunkedContent.totalChunks,\n\t\t\t});\n\t\t}\n\n\t\tmap.push(chunkedContent.contents);\n\t}\n\n\t/**\n\t * Splits the first op of a compressed batch in chunks, sends the chunks separately and\n\t * returns a new batch composed of the last chunk and the rest of the ops in the original batch.\n\t *\n\t * A compressed batch is formed by one large op at the first position, followed by a series of placeholder ops\n\t * which are used in order to reserve the sequence numbers for when the first op gets unrolled into the original\n\t * uncompressed ops at ingestion in the runtime.\n\t *\n\t * If the first op is too large, it can be chunked (split into smaller op) which can be sent individually over the wire\n\t * and accumulate at ingestion, until the last op in the chunk is processed, when the original op is unrolled.\n\t *\n\t * This method will send the first N - 1 chunks separately and use the last chunk as the first message in the result batch\n\t * and then appends the original placeholder ops. This will ensure that the batch semantics of the original (non-compressed) batch\n\t * are preserved, as the original chunked op will be unrolled by the runtime when the first message in the batch is processed\n\t * (as it is the last chunk).\n\t *\n\t * To illustrate, if the input is `[largeOp, emptyOp, emptyOp]`, `largeOp` will be split into `[chunk1, chunk2, chunk3, chunk4]`.\n\t * `chunk1`, `chunk2` and `chunk3` will be sent individually and `[chunk4, emptyOp, emptyOp]` will be returned.\n\t *\n\t * @param batch - the compressed batch which needs to be processed\n\t * @returns A new adjusted batch which can be sent over the wire\n\t */\n\tpublic splitFirstBatchMessage(batch: IBatch): IBatch {\n\t\tassert(this.isBatchChunkingEnabled, 0x513 /* Chunking needs to be enabled */);\n\t\tassert(\n\t\t\tbatch.contentSizeInBytes > 0 && batch.content.length > 0,\n\t\t\t0x514 /* Batch needs to be non-empty */,\n\t\t);\n\t\tassert(\n\t\t\tbatch.referenceSequenceNumber !== undefined,\n\t\t\t0x58a /* Batch must have a reference sequence number if non-empty */,\n\t\t);\n\t\tassert(this.chunkSizeInBytes !== 0, 0x515 /* Chunk size needs to be non-zero */);\n\t\tassert(\n\t\t\tthis.chunkSizeInBytes < this.maxBatchSizeInBytes,\n\t\t\t0x516 /* Chunk size needs to be smaller than the max batch size */,\n\t\t);\n\n\t\tconst firstMessage = batch.content[0]; // we expect this to be the large compressed op, which needs to be split\n\t\tassert(\n\t\t\t(firstMessage.contents?.length ?? 0) >= this.chunkSizeInBytes,\n\t\t\t0x518 /* First message in the batch needs to be chunkable */,\n\t\t);\n\n\t\tconst restOfMessages = batch.content.slice(1); // we expect these to be empty ops, created to reserve sequence numbers\n\t\tconst socketSize = estimateSocketSize(batch);\n\t\tconst chunks = splitOp(\n\t\t\tfirstMessage,\n\t\t\tthis.chunkSizeInBytes,\n\t\t\t// If we estimate that the socket batch size will exceed the batch limit\n\t\t\t// we will inject an empty op to minimize the risk of the payload failing due to\n\t\t\t// the overhead from the trailing empty ops in the batch.\n\t\t\tsocketSize >= this.maxBatchSizeInBytes,\n\t\t);\n\n\t\tassert(this.submitBatchFn !== undefined, 0x519 /* We don't support old loaders */);\n\t\t// Send the first N-1 chunks immediately\n\t\tfor (const chunk of chunks.slice(0, -1)) {\n\t\t\tthis.submitBatchFn(\n\t\t\t\t[chunkToBatchMessage(chunk, batch.referenceSequenceNumber)],\n\t\t\t\tbatch.referenceSequenceNumber,\n\t\t\t);\n\t\t}\n\n\t\t// The last chunk will be part of the new batch and needs to\n\t\t// preserve the batch metadata of the original batch\n\t\tconst lastChunk = chunkToBatchMessage(\n\t\t\tchunks[chunks.length - 1],\n\t\t\tbatch.referenceSequenceNumber,\n\t\t\t{ batch: firstMessage.metadata?.batch },\n\t\t);\n\n\t\tthis.logger.sendPerformanceEvent({\n\t\t\t// Used to be \"Chunked compressed batch\"\n\t\t\teventName: \"CompressedChunkedBatch\",\n\t\t\tlength: batch.content.length,\n\t\t\tsizeInBytes: batch.contentSizeInBytes,\n\t\t\tchunks: chunks.length,\n\t\t\tchunkSizeInBytes: this.chunkSizeInBytes,\n\t\t\tsocketSize,\n\t\t});\n\n\t\treturn {\n\t\t\tcontent: [lastChunk, ...restOfMessages],\n\t\t\tcontentSizeInBytes: lastChunk.contents?.length ?? 0,\n\t\t\treferenceSequenceNumber: batch.referenceSequenceNumber,\n\t\t};\n\t}\n}\n\nconst chunkToBatchMessage = (\n\tchunk: IChunkedOp,\n\treferenceSequenceNumber: number,\n\tmetadata: Record<string, unknown> | undefined = undefined,\n): BatchMessage => {\n\tconst payload: ContainerRuntimeMessage = {\n\t\ttype: ContainerMessageType.ChunkedOp,\n\t\tcontents: chunk,\n\t};\n\treturn {\n\t\tcontents: JSON.stringify(payload),\n\t\ttype: payload.type,\n\t\tmetadata,\n\t\tlocalOpMetadata: undefined,\n\t\treferenceSequenceNumber,\n\t};\n};\n\n/**\n * Splits an op into smaller ops (chunks), based on the size of the op and the `chunkSizeInBytes` parameter.\n *\n * The last op of the result will be bundled with empty ops in the same batch. There is a risk of the batch payload\n * exceeding the 1MB limit due to the overhead from the empty ops. If the last op is large, the risk is even higher.\n * To minimize the odds, an extra empty op can be added to the result using the `extraOp` parameter.\n *\n * @param op - the op to be split\n * @param chunkSizeInBytes - how large should the chunks be\n * @param extraOp - should an extra empty op be added to the result\n * @returns an array of chunked ops\n */\nexport const splitOp = (\n\top: BatchMessage,\n\tchunkSizeInBytes: number,\n\textraOp: boolean = false,\n): IChunkedOp[] => {\n\tconst chunks: IChunkedOp[] = [];\n\tassert(\n\t\top.contents !== undefined && op.contents !== null,\n\t\t0x51a /* We should have something to chunk */,\n\t);\n\n\tconst contentLength = op.contents.length;\n\tconst chunkCount = Math.floor((contentLength - 1) / chunkSizeInBytes) + 1 + (extraOp ? 1 : 0);\n\tlet offset = 0;\n\tfor (let chunkId = 1; chunkId <= chunkCount; chunkId++) {\n\t\tconst chunk: IChunkedOp = {\n\t\t\tchunkId,\n\t\t\tcontents: op.contents.substr(offset, chunkSizeInBytes),\n\t\t\toriginalType: op.type,\n\t\t\ttotalChunks: chunkCount,\n\t\t};\n\n\t\tif (chunkId === chunkCount) {\n\t\t\t// We don't need to port these to all the chunks,\n\t\t\t// as we rebuild the original op when we process the\n\t\t\t// last chunk, therefore it is the only one that needs it.\n\t\t\tchunk.originalMetadata = op.metadata;\n\t\t\tchunk.originalCompression = op.compression;\n\t\t}\n\n\t\tchunks.push(chunk);\n\t\toffset += chunkSizeInBytes;\n\t\tassert(\n\t\t\tchunkId >= chunkCount - 1 || offset <= contentLength,\n\t\t\t0x58b /* Content offset within bounds */,\n\t\t);\n\t}\n\n\tassert(offset >= contentLength, 0x58c /* Content offset equal or larger than content length */);\n\tassert(chunks.length === chunkCount, 0x5a5 /* Expected number of chunks */);\n\treturn chunks;\n};\n"]}
@@ -2,10 +2,10 @@
2
2
  * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
3
  * Licensed under the MIT License.
4
4
  */
5
- import { ITelemetryLogger } from "@fluidframework/common-definitions";
6
- import { IContainerContext } from "@fluidframework/container-definitions";
5
+ import { ITelemetryLoggerExt } from "@fluidframework/telemetry-utils";
6
+ import { IContainerContext, ICriticalContainerError } from "@fluidframework/container-definitions";
7
7
  import { ICompressionRuntimeOptions } from "../containerRuntime";
8
- import { PendingStateManager } from "../pendingStateManager";
8
+ import { IPendingBatchMessage, PendingStateManager } from "../pendingStateManager";
9
9
  import { BatchSequenceNumbers } from "./batchManager";
10
10
  import { BatchMessage } from "./definitions";
11
11
  import { OpCompressor } from "./opCompressor";
@@ -15,6 +15,7 @@ export interface IOutboxConfig {
15
15
  readonly compressionOptions: ICompressionRuntimeOptions;
16
16
  readonly maxBatchSizeInBytes: number;
17
17
  readonly disablePartialFlush: boolean;
18
+ readonly enableGroupedBatching: boolean;
18
19
  }
19
20
  export interface IOutboxParameters {
20
21
  readonly shouldSend: () => boolean;
@@ -23,16 +24,34 @@ export interface IOutboxParameters {
23
24
  readonly config: IOutboxConfig;
24
25
  readonly compressor: OpCompressor;
25
26
  readonly splitter: OpSplitter;
26
- readonly logger: ITelemetryLogger;
27
+ readonly logger: ITelemetryLoggerExt;
27
28
  readonly groupingManager: OpGroupingManager;
28
29
  readonly getCurrentSequenceNumbers: () => BatchSequenceNumbers;
30
+ readonly reSubmit: (message: IPendingBatchMessage) => void;
31
+ readonly opReentrancy: () => boolean;
32
+ readonly closeContainer: (error?: ICriticalContainerError) => void;
29
33
  }
34
+ /**
35
+ * Temporarily increase the stack limit while executing the provided action.
36
+ * If a negative value is provided for `length`, no stack frames will be collected.
37
+ * If Infinity is provided, all frames will be collected.
38
+ *
39
+ * ADO:4663 - add this to the common packages.
40
+ *
41
+ * @param action - action which returns an error
42
+ * @param length - number of stack frames to collect, 50 if unspecified.
43
+ * @returns the result of the action provided
44
+ */
45
+ export declare function getLongStack<T>(action: () => T, length?: number): T;
30
46
  export declare class Outbox {
31
47
  private readonly params;
32
48
  private readonly mc;
33
49
  private readonly attachFlowBatch;
34
50
  private readonly mainBatch;
51
+ private readonly blobAttachBatch;
35
52
  private readonly defaultAttachFlowSoftLimitInBytes;
53
+ private batchRebasesToReport;
54
+ private rebasing;
36
55
  /**
37
56
  * Track the number of ops which were detected to have a mismatched
38
57
  * reference sequence number, in order to self-throttle the telemetry events.
@@ -52,8 +71,19 @@ export declare class Outbox {
52
71
  private maybeFlushPartialBatch;
53
72
  submit(message: BatchMessage): void;
54
73
  submitAttach(message: BatchMessage): void;
74
+ submitBlobAttach(message: BatchMessage): void;
75
+ private addMessageToBatchManager;
55
76
  flush(): void;
77
+ private flushAll;
56
78
  private flushInternal;
79
+ /**
80
+ * Rebases a batch. All the ops in the batch are resubmitted to the runtime and
81
+ * they will end up back in the same batch manager they were flushed from and subsequently flushed.
82
+ *
83
+ * @param rawBatch - the batch to be rebased
84
+ */
85
+ private rebase;
86
+ private isContextReentrant;
57
87
  private compressBatch;
58
88
  /**
59
89
  * Sends the batch object to the container context to be sent over the wire.
@@ -65,6 +95,7 @@ export declare class Outbox {
65
95
  checkpoint(): {
66
96
  mainBatch: import("./definitions").IBatchCheckpoint;
67
97
  attachFlowBatch: import("./definitions").IBatchCheckpoint;
98
+ blobAttachBatch: import("./definitions").IBatchCheckpoint;
68
99
  };
69
100
  }
70
101
  //# sourceMappingURL=outbox.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"outbox.d.ts","sourceRoot":"","sources":["../../src/opLifecycle/outbox.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,gBAAgB,EAAE,MAAM,oCAAoC,CAAC;AAEtE,OAAO,EAAE,iBAAiB,EAAE,MAAM,uCAAuC,CAAC;AAQ1E,OAAO,EAAE,0BAA0B,EAAE,MAAM,qBAAqB,CAAC;AACjE,OAAO,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AAC7D,OAAO,EAEN,oBAAoB,EAGpB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,YAAY,EAAU,MAAM,eAAe,CAAC;AACrD,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAE1C,MAAM,WAAW,aAAa;IAC7B,QAAQ,CAAC,kBAAkB,EAAE,0BAA0B,CAAC;IAExD,QAAQ,CAAC,mBAAmB,EAAE,MAAM,CAAC;IACrC,QAAQ,CAAC,mBAAmB,EAAE,OAAO,CAAC;CACtC;AAED,MAAM,WAAW,iBAAiB;IACjC,QAAQ,CAAC,UAAU,EAAE,MAAM,OAAO,CAAC;IACnC,QAAQ,CAAC,mBAAmB,EAAE,mBAAmB,CAAC;IAClD,QAAQ,CAAC,gBAAgB,EAAE,iBAAiB,CAAC;IAC7C,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAC;IAC/B,QAAQ,CAAC,UAAU,EAAE,YAAY,CAAC;IAClC,QAAQ,CAAC,QAAQ,EAAE,UAAU,CAAC;IAC9B,QAAQ,CAAC,MAAM,EAAE,gBAAgB,CAAC;IAClC,QAAQ,CAAC,eAAe,EAAE,iBAAiB,CAAC;IAC5C,QAAQ,CAAC,yBAAyB,EAAE,MAAM,oBAAoB,CAAC;CAC/D;AAeD,qBAAa,MAAM;IAeN,OAAO,CAAC,QAAQ,CAAC,MAAM;IAdnC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAoB;IACvC,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAe;IAC/C,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAe;IACzC,OAAO,CAAC,QAAQ,CAAC,iCAAiC,CAAc;IAEhE;;;;;OAKG;IACH,OAAO,CAAC,QAAQ,CAAC,wBAAwB,CAAK;IAC9C,OAAO,CAAC,qBAAqB,CAAK;gBAEL,MAAM,EAAE,iBAAiB;IAatD,IAAW,OAAO,IAAI,OAAO,CAE5B;IAED;;;;;OAKG;IACH,OAAO,CAAC,sBAAsB;IAwCvB,MAAM,CAAC,OAAO,EAAE,YAAY;IAkB5B,YAAY,CAAC,OAAO,EAAE,YAAY;IAyClC,KAAK;IAKZ,OAAO,CAAC,aAAa;IAOrB,OAAO,CAAC,aAAa;IAqCrB;;;;OAIG;IACH,OAAO,CAAC,SAAS;IAsDjB,OAAO,CAAC,YAAY;IAcb,UAAU;;;;CAMjB"}
1
+ {"version":3,"file":"outbox.d.ts","sourceRoot":"","sources":["../../src/opLifecycle/outbox.ts"],"names":[],"mappings":"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"}
@@ -4,29 +4,46 @@
4
4
  * Licensed under the MIT License.
5
5
  */
6
6
  Object.defineProperty(exports, "__esModule", { value: true });
7
- exports.Outbox = void 0;
7
+ exports.Outbox = exports.getLongStack = void 0;
8
+ const telemetry_utils_1 = require("@fluidframework/telemetry-utils");
8
9
  const common_utils_1 = require("@fluidframework/common-utils");
9
10
  const container_utils_1 = require("@fluidframework/container-utils");
10
11
  const protocol_definitions_1 = require("@fluidframework/protocol-definitions");
11
- const telemetry_utils_1 = require("@fluidframework/telemetry-utils");
12
12
  const batchManager_1 = require("./batchManager");
13
- function getLongStack(action) {
14
- // Increase the stack trace limit temporarily, so as to debug better in case it occurs.
15
- try {
16
- const originalStackTraceLimit = Error.stackTraceLimit;
17
- Error.stackTraceLimit = 50;
18
- const result = action();
19
- Error.stackTraceLimit = originalStackTraceLimit;
20
- return result;
13
+ /**
14
+ * Temporarily increase the stack limit while executing the provided action.
15
+ * If a negative value is provided for `length`, no stack frames will be collected.
16
+ * If Infinity is provided, all frames will be collected.
17
+ *
18
+ * ADO:4663 - add this to the common packages.
19
+ *
20
+ * @param action - action which returns an error
21
+ * @param length - number of stack frames to collect, 50 if unspecified.
22
+ * @returns the result of the action provided
23
+ */
24
+ function getLongStack(action, length = 50) {
25
+ const errorObj = Error;
26
+ if ((Object.getOwnPropertyDescriptor(errorObj, "stackTraceLimit") ||
27
+ Object.getOwnPropertyDescriptor(Object.getPrototypeOf(errorObj), "stackTraceLimit") ||
28
+ {}).writable !== true) {
29
+ return action();
21
30
  }
22
- catch (error) {
31
+ const originalStackTraceLimit = errorObj.stackTraceLimit;
32
+ try {
33
+ errorObj.stackTraceLimit = length;
23
34
  return action();
24
35
  }
36
+ finally {
37
+ errorObj.stackTraceLimit = originalStackTraceLimit;
38
+ }
25
39
  }
40
+ exports.getLongStack = getLongStack;
26
41
  class Outbox {
27
42
  constructor(params) {
28
43
  this.params = params;
29
44
  this.defaultAttachFlowSoftLimitInBytes = 320 * 1024;
45
+ this.batchRebasesToReport = 5;
46
+ this.rebasing = false;
30
47
  /**
31
48
  * Track the number of ops which were detected to have a mismatched
32
49
  * reference sequence number, in order to self-throttle the telemetry events.
@@ -43,9 +60,12 @@ class Outbox {
43
60
  const softLimit = isCompressionEnabled ? Infinity : this.defaultAttachFlowSoftLimitInBytes;
44
61
  this.attachFlowBatch = new batchManager_1.BatchManager({ hardLimit, softLimit });
45
62
  this.mainBatch = new batchManager_1.BatchManager({ hardLimit });
63
+ this.blobAttachBatch = new batchManager_1.BatchManager({ hardLimit });
46
64
  }
47
65
  get isEmpty() {
48
- return this.attachFlowBatch.length === 0 && this.mainBatch.length === 0;
66
+ return (this.attachFlowBatch.length === 0 &&
67
+ this.mainBatch.length === 0 &&
68
+ this.blobAttachBatch.length === 0);
49
69
  }
50
70
  /**
51
71
  * If we detect that the reference sequence number of the incoming message does not match
@@ -56,11 +76,14 @@ class Outbox {
56
76
  maybeFlushPartialBatch() {
57
77
  const mainBatchSeqNums = this.mainBatch.sequenceNumbers;
58
78
  const attachFlowBatchSeqNums = this.attachFlowBatch.sequenceNumbers;
79
+ const blobAttachSeqNums = this.blobAttachBatch.sequenceNumbers;
59
80
  (0, common_utils_1.assert)(this.params.config.disablePartialFlush ||
60
- (0, batchManager_1.sequenceNumbersMatch)(mainBatchSeqNums, attachFlowBatchSeqNums), 0x58d /* Reference sequence numbers from both batches must be in sync */);
81
+ ((0, batchManager_1.sequenceNumbersMatch)(mainBatchSeqNums, attachFlowBatchSeqNums) &&
82
+ (0, batchManager_1.sequenceNumbersMatch)(mainBatchSeqNums, blobAttachSeqNums)), 0x58d /* Reference sequence numbers from both batches must be in sync */);
61
83
  const currentSequenceNumbers = this.params.getCurrentSequenceNumbers();
62
84
  if ((0, batchManager_1.sequenceNumbersMatch)(mainBatchSeqNums, currentSequenceNumbers) &&
63
- (0, batchManager_1.sequenceNumbersMatch)(attachFlowBatchSeqNums, currentSequenceNumbers)) {
85
+ (0, batchManager_1.sequenceNumbersMatch)(attachFlowBatchSeqNums, currentSequenceNumbers) &&
86
+ (0, batchManager_1.sequenceNumbersMatch)(blobAttachSeqNums, currentSequenceNumbers)) {
64
87
  // The reference sequence numbers are stable, there is nothing to do
65
88
  return;
66
89
  }
@@ -72,42 +95,28 @@ class Outbox {
72
95
  mainClientSequenceNumber: mainBatchSeqNums.clientSequenceNumber,
73
96
  attachReferenceSequenceNumber: attachFlowBatchSeqNums.referenceSequenceNumber,
74
97
  attachClientSequenceNumber: attachFlowBatchSeqNums.clientSequenceNumber,
98
+ blobAttachReferenceSequenceNumber: blobAttachSeqNums.referenceSequenceNumber,
99
+ blobAttachClientSequenceNumber: blobAttachSeqNums.clientSequenceNumber,
75
100
  currentReferenceSequenceNumber: currentSequenceNumbers.referenceSequenceNumber,
76
101
  currentClientSequenceNumber: currentSequenceNumbers.clientSequenceNumber,
77
102
  }, getLongStack(() => new container_utils_1.UsageError("Submission of an out of order message")));
78
103
  }
79
104
  if (!this.params.config.disablePartialFlush) {
80
- this.flush();
105
+ this.flushAll();
81
106
  }
82
107
  }
83
108
  submit(message) {
84
- var _a, _b;
85
109
  this.maybeFlushPartialBatch();
86
- if (!this.mainBatch.push(message, this.params.getCurrentSequenceNumbers().clientSequenceNumber)) {
87
- throw new container_utils_1.GenericError("BatchTooLarge", /* error */ undefined, {
88
- opSize: (_b = (_a = message.contents) === null || _a === void 0 ? void 0 : _a.length) !== null && _b !== void 0 ? _b : 0,
89
- batchSize: this.mainBatch.contentSizeInBytes,
90
- count: this.mainBatch.length,
91
- limit: this.mainBatch.options.hardLimit,
92
- });
93
- }
110
+ this.addMessageToBatchManager(this.mainBatch, message);
94
111
  }
95
112
  submitAttach(message) {
96
- var _a, _b;
97
113
  this.maybeFlushPartialBatch();
98
- if (!this.attachFlowBatch.push(message, this.params.getCurrentSequenceNumbers().clientSequenceNumber)) {
114
+ if (!this.attachFlowBatch.push(message, this.isContextReentrant(), this.params.getCurrentSequenceNumbers().clientSequenceNumber)) {
99
115
  // BatchManager has two limits - soft limit & hard limit. Soft limit is only engaged
100
116
  // when queue is not empty.
101
117
  // Flush queue & retry. Failure on retry would mean - single message is bigger than hard limit
102
- this.flushInternal(this.attachFlowBatch.popBatch());
103
- if (!this.attachFlowBatch.push(message, this.params.getCurrentSequenceNumbers().clientSequenceNumber)) {
104
- throw new container_utils_1.GenericError("BatchTooLarge", /* error */ undefined, {
105
- opSize: (_b = (_a = message.contents) === null || _a === void 0 ? void 0 : _a.length) !== null && _b !== void 0 ? _b : 0,
106
- batchSize: this.attachFlowBatch.contentSizeInBytes,
107
- count: this.attachFlowBatch.length,
108
- limit: this.attachFlowBatch.options.hardLimit,
109
- });
110
- }
118
+ this.flushInternal(this.attachFlowBatch);
119
+ this.addMessageToBatchManager(this.attachFlowBatch, message);
111
120
  }
112
121
  // If compression is enabled, we will always successfully receive
113
122
  // attach ops and compress then send them at the next JS turn, regardless
@@ -116,28 +125,104 @@ class Outbox {
116
125
  // after they reach a size which would benefit from compression.
117
126
  if (this.attachFlowBatch.contentSizeInBytes >=
118
127
  this.params.config.compressionOptions.minimumBatchSizeInBytes) {
119
- this.flushInternal(this.attachFlowBatch.popBatch());
128
+ this.flushInternal(this.attachFlowBatch);
129
+ }
130
+ }
131
+ submitBlobAttach(message) {
132
+ this.maybeFlushPartialBatch();
133
+ this.addMessageToBatchManager(this.blobAttachBatch, message);
134
+ // If compression is enabled, we will always successfully receive
135
+ // blobAttach ops and compress then send them at the next JS turn, regardless
136
+ // of the overall size of the accumulated ops in the batch.
137
+ // However, it is more efficient to flush these ops faster, preferably
138
+ // after they reach a size which would benefit from compression.
139
+ if (this.blobAttachBatch.contentSizeInBytes >=
140
+ this.params.config.compressionOptions.minimumBatchSizeInBytes) {
141
+ this.flushInternal(this.blobAttachBatch);
142
+ }
143
+ }
144
+ addMessageToBatchManager(batchManager, message) {
145
+ var _a, _b;
146
+ if (!batchManager.push(message, this.isContextReentrant(), this.params.getCurrentSequenceNumbers().clientSequenceNumber)) {
147
+ throw new container_utils_1.GenericError("BatchTooLarge", /* error */ undefined, {
148
+ opSize: (_b = (_a = message.contents) === null || _a === void 0 ? void 0 : _a.length) !== null && _b !== void 0 ? _b : 0,
149
+ batchSize: batchManager.contentSizeInBytes,
150
+ count: batchManager.length,
151
+ limit: batchManager.options.hardLimit,
152
+ });
120
153
  }
121
154
  }
122
155
  flush() {
123
- this.flushInternal(this.attachFlowBatch.popBatch());
124
- this.flushInternal(this.mainBatch.popBatch());
156
+ if (this.isContextReentrant()) {
157
+ const error = new container_utils_1.UsageError("Flushing is not supported inside DDS event handlers");
158
+ this.params.closeContainer(error);
159
+ throw error;
160
+ }
161
+ this.flushAll();
162
+ }
163
+ flushAll() {
164
+ this.flushInternal(this.attachFlowBatch);
165
+ this.flushInternal(this.blobAttachBatch, true /* disableGroupedBatching */);
166
+ this.flushInternal(this.mainBatch);
125
167
  }
126
- flushInternal(rawBatch) {
127
- const processedBatch = this.compressBatch(rawBatch);
168
+ flushInternal(batchManager, disableGroupedBatching = false) {
169
+ if (batchManager.empty) {
170
+ return;
171
+ }
172
+ const rawBatch = batchManager.popBatch();
173
+ if (rawBatch.hasReentrantOps === true && this.params.config.enableGroupedBatching) {
174
+ (0, common_utils_1.assert)(!this.rebasing, 0x6fa /* A rebased batch should never have reentrant ops */);
175
+ // If a batch contains reentrant ops (ops created as a result from processing another op)
176
+ // it needs to be rebased so that we can ensure consistent reference sequence numbers
177
+ // and eventual consistency at the DDS level.
178
+ this.rebase(rawBatch, batchManager);
179
+ return;
180
+ }
181
+ const processedBatch = this.compressBatch(rawBatch, disableGroupedBatching);
128
182
  this.sendBatch(processedBatch);
129
183
  this.persistBatch(rawBatch.content);
130
184
  }
131
- compressBatch(batch) {
185
+ /**
186
+ * Rebases a batch. All the ops in the batch are resubmitted to the runtime and
187
+ * they will end up back in the same batch manager they were flushed from and subsequently flushed.
188
+ *
189
+ * @param rawBatch - the batch to be rebased
190
+ */
191
+ rebase(rawBatch, batchManager) {
192
+ (0, common_utils_1.assert)(!this.rebasing, 0x6fb /* Reentrancy */);
193
+ this.rebasing = true;
194
+ for (const message of rawBatch.content) {
195
+ this.params.reSubmit({
196
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
197
+ content: message.contents,
198
+ localOpMetadata: message.localOpMetadata,
199
+ opMetadata: message.metadata,
200
+ });
201
+ }
202
+ if (this.batchRebasesToReport > 0) {
203
+ this.mc.logger.sendTelemetryEvent({
204
+ eventName: "BatchRebase",
205
+ length: rawBatch.content.length,
206
+ referenceSequenceNumber: rawBatch.referenceSequenceNumber,
207
+ }, new container_utils_1.UsageError("BatchRebase"));
208
+ this.batchRebasesToReport--;
209
+ }
210
+ this.flushInternal(batchManager);
211
+ this.rebasing = false;
212
+ }
213
+ isContextReentrant() {
214
+ return this.params.opReentrancy() && !this.rebasing;
215
+ }
216
+ compressBatch(batch, disableGroupedBatching) {
132
217
  if (batch.content.length === 0 ||
133
218
  this.params.config.compressionOptions === undefined ||
134
219
  this.params.config.compressionOptions.minimumBatchSizeInBytes >
135
220
  batch.contentSizeInBytes ||
136
221
  this.params.containerContext.submitBatchFn === undefined) {
137
222
  // Nothing to do if the batch is empty or if compression is disabled or not supported, or if we don't need to compress
138
- return this.params.groupingManager.groupBatch(batch);
223
+ return disableGroupedBatching ? batch : this.params.groupingManager.groupBatch(batch);
139
224
  }
140
- const compressedBatch = this.params.compressor.compressBatch(this.params.groupingManager.groupBatch(batch));
225
+ const compressedBatch = this.params.compressor.compressBatch(disableGroupedBatching ? batch : this.params.groupingManager.groupBatch(batch));
141
226
  if (this.params.splitter.isBatchChunkingEnabled) {
142
227
  return compressedBatch.contentSizeInBytes <= this.params.splitter.chunkSizeInBytes
143
228
  ? compressedBatch
@@ -182,7 +267,9 @@ class Outbox {
182
267
  // version that has support for batches (submitBatchFn)
183
268
  (0, common_utils_1.assert)(batch.content[0].compression === undefined, 0x5a6 /* Compression should not have happened if the loader does not support it */);
184
269
  for (const message of batch.content) {
185
- this.params.containerContext.submitFn(protocol_definitions_1.MessageType.Operation, message.deserializedContent, true, // batch
270
+ this.params.containerContext.submitFn(protocol_definitions_1.MessageType.Operation,
271
+ // For back-compat (submitFn only works on deserialized content)
272
+ message.contents === undefined ? undefined : JSON.parse(message.contents), true, // batch
186
273
  message.metadata);
187
274
  }
188
275
  this.params.containerContext.deltaManager.flush();
@@ -201,13 +288,16 @@ class Outbox {
201
288
  // Let the PendingStateManager know that a message was submitted.
202
289
  // In future, need to shift toward keeping batch as a whole!
203
290
  for (const message of batch) {
204
- this.params.pendingStateManager.onSubmitMessage(message.deserializedContent.type, message.referenceSequenceNumber, message.deserializedContent.contents, message.localOpMetadata, message.metadata);
291
+ this.params.pendingStateManager.onSubmitMessage(
292
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
293
+ message.contents, message.referenceSequenceNumber, message.localOpMetadata, message.metadata);
205
294
  }
206
295
  }
207
296
  checkpoint() {
208
297
  return {
209
298
  mainBatch: this.mainBatch.checkpoint(),
210
299
  attachFlowBatch: this.attachFlowBatch.checkpoint(),
300
+ blobAttachBatch: this.blobAttachBatch.checkpoint(),
211
301
  };
212
302
  }
213
303
  }