@fluidframework/container-runtime 2.20.0 → 2.21.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (333) hide show
  1. package/.eslintrc.cjs +36 -6
  2. package/CHANGELOG.md +38 -0
  3. package/api-report/container-runtime.legacy.alpha.api.md +31 -31
  4. package/dist/batchTracker.d.ts +1 -2
  5. package/dist/batchTracker.d.ts.map +1 -1
  6. package/dist/batchTracker.js +1 -1
  7. package/dist/batchTracker.js.map +1 -1
  8. package/dist/blobManager/blobManager.d.ts.map +1 -1
  9. package/dist/blobManager/blobManager.js +14 -11
  10. package/dist/blobManager/blobManager.js.map +1 -1
  11. package/dist/blobManager/blobManagerSnapSum.d.ts +1 -0
  12. package/dist/blobManager/blobManagerSnapSum.d.ts.map +1 -1
  13. package/dist/blobManager/blobManagerSnapSum.js +7 -5
  14. package/dist/blobManager/blobManagerSnapSum.js.map +1 -1
  15. package/dist/channelCollection.d.ts.map +1 -1
  16. package/dist/channelCollection.js +63 -41
  17. package/dist/channelCollection.js.map +1 -1
  18. package/dist/connectionTelemetry.d.ts +2 -2
  19. package/dist/connectionTelemetry.d.ts.map +1 -1
  20. package/dist/connectionTelemetry.js +4 -4
  21. package/dist/connectionTelemetry.js.map +1 -1
  22. package/dist/containerRuntime.d.ts +14 -30
  23. package/dist/containerRuntime.d.ts.map +1 -1
  24. package/dist/containerRuntime.js +264 -194
  25. package/dist/containerRuntime.js.map +1 -1
  26. package/dist/dataStore.js +6 -3
  27. package/dist/dataStore.js.map +1 -1
  28. package/dist/dataStoreContext.d.ts.map +1 -1
  29. package/dist/dataStoreContext.js +16 -11
  30. package/dist/dataStoreContext.js.map +1 -1
  31. package/dist/dataStoreContexts.d.ts.map +1 -1
  32. package/dist/dataStoreContexts.js +1 -0
  33. package/dist/dataStoreContexts.js.map +1 -1
  34. package/dist/deltaScheduler.d.ts.map +1 -1
  35. package/dist/deltaScheduler.js +5 -5
  36. package/dist/deltaScheduler.js.map +1 -1
  37. package/dist/gc/garbageCollection.d.ts.map +1 -1
  38. package/dist/gc/garbageCollection.js +36 -14
  39. package/dist/gc/garbageCollection.js.map +1 -1
  40. package/dist/gc/gcConfigs.d.ts.map +1 -1
  41. package/dist/gc/gcConfigs.js +2 -0
  42. package/dist/gc/gcConfigs.js.map +1 -1
  43. package/dist/gc/gcDefinitions.d.ts +8 -0
  44. package/dist/gc/gcDefinitions.d.ts.map +1 -1
  45. package/dist/gc/gcDefinitions.js +1 -0
  46. package/dist/gc/gcDefinitions.js.map +1 -1
  47. package/dist/gc/gcHelpers.d.ts.map +1 -1
  48. package/dist/gc/gcHelpers.js +8 -5
  49. package/dist/gc/gcHelpers.js.map +1 -1
  50. package/dist/gc/gcSummaryStateTracker.d.ts.map +1 -1
  51. package/dist/gc/gcSummaryStateTracker.js +2 -1
  52. package/dist/gc/gcSummaryStateTracker.js.map +1 -1
  53. package/dist/gc/gcTelemetry.d.ts.map +1 -1
  54. package/dist/gc/gcTelemetry.js +29 -15
  55. package/dist/gc/gcTelemetry.js.map +1 -1
  56. package/dist/inboundBatchAggregator.js +3 -3
  57. package/dist/inboundBatchAggregator.js.map +1 -1
  58. package/dist/layerCompatState.d.ts +19 -0
  59. package/dist/layerCompatState.d.ts.map +1 -0
  60. package/dist/layerCompatState.js +64 -0
  61. package/dist/layerCompatState.js.map +1 -0
  62. package/dist/messageTypes.d.ts.map +1 -1
  63. package/dist/messageTypes.js.map +1 -1
  64. package/dist/opLifecycle/duplicateBatchDetector.js +2 -2
  65. package/dist/opLifecycle/duplicateBatchDetector.js.map +1 -1
  66. package/dist/opLifecycle/opCompressor.d.ts +3 -2
  67. package/dist/opLifecycle/opCompressor.d.ts.map +1 -1
  68. package/dist/opLifecycle/opCompressor.js +13 -19
  69. package/dist/opLifecycle/opCompressor.js.map +1 -1
  70. package/dist/opLifecycle/opDecompressor.d.ts +3 -0
  71. package/dist/opLifecycle/opDecompressor.d.ts.map +1 -1
  72. package/dist/opLifecycle/opDecompressor.js +4 -1
  73. package/dist/opLifecycle/opDecompressor.js.map +1 -1
  74. package/dist/opLifecycle/opGroupingManager.d.ts +1 -1
  75. package/dist/opLifecycle/opGroupingManager.d.ts.map +1 -1
  76. package/dist/opLifecycle/opGroupingManager.js +5 -3
  77. package/dist/opLifecycle/opGroupingManager.js.map +1 -1
  78. package/dist/opLifecycle/opSplitter.d.ts +13 -10
  79. package/dist/opLifecycle/opSplitter.d.ts.map +1 -1
  80. package/dist/opLifecycle/opSplitter.js +14 -11
  81. package/dist/opLifecycle/opSplitter.js.map +1 -1
  82. package/dist/opLifecycle/outbox.d.ts +3 -3
  83. package/dist/opLifecycle/outbox.d.ts.map +1 -1
  84. package/dist/opLifecycle/outbox.js +11 -15
  85. package/dist/opLifecycle/outbox.js.map +1 -1
  86. package/dist/opLifecycle/remoteMessageProcessor.d.ts.map +1 -1
  87. package/dist/opLifecycle/remoteMessageProcessor.js +3 -1
  88. package/dist/opLifecycle/remoteMessageProcessor.js.map +1 -1
  89. package/dist/packageVersion.d.ts +1 -1
  90. package/dist/packageVersion.js +1 -1
  91. package/dist/packageVersion.js.map +1 -1
  92. package/dist/pendingStateManager.d.ts +3 -4
  93. package/dist/pendingStateManager.d.ts.map +1 -1
  94. package/dist/pendingStateManager.js +11 -10
  95. package/dist/pendingStateManager.js.map +1 -1
  96. package/dist/summary/documentSchema.d.ts +7 -0
  97. package/dist/summary/documentSchema.d.ts.map +1 -1
  98. package/dist/summary/documentSchema.js +6 -4
  99. package/dist/summary/documentSchema.js.map +1 -1
  100. package/dist/summary/orderedClientElection.d.ts +1 -0
  101. package/dist/summary/orderedClientElection.d.ts.map +1 -1
  102. package/dist/summary/orderedClientElection.js +13 -11
  103. package/dist/summary/orderedClientElection.js.map +1 -1
  104. package/dist/summary/runWhileConnectedCoordinator.d.ts +1 -0
  105. package/dist/summary/runWhileConnectedCoordinator.d.ts.map +1 -1
  106. package/dist/summary/runWhileConnectedCoordinator.js +7 -2
  107. package/dist/summary/runWhileConnectedCoordinator.js.map +1 -1
  108. package/dist/summary/runningSummarizer.d.ts +2 -2
  109. package/dist/summary/runningSummarizer.d.ts.map +1 -1
  110. package/dist/summary/runningSummarizer.js +38 -17
  111. package/dist/summary/runningSummarizer.js.map +1 -1
  112. package/dist/summary/summarizer.d.ts +1 -0
  113. package/dist/summary/summarizer.d.ts.map +1 -1
  114. package/dist/summary/summarizer.js +18 -9
  115. package/dist/summary/summarizer.js.map +1 -1
  116. package/dist/summary/summarizerClientElection.d.ts.map +1 -1
  117. package/dist/summary/summarizerClientElection.js +1 -0
  118. package/dist/summary/summarizerClientElection.js.map +1 -1
  119. package/dist/summary/summarizerHeuristics.js +1 -1
  120. package/dist/summary/summarizerHeuristics.js.map +1 -1
  121. package/dist/summary/summarizerNode/index.d.ts.map +1 -1
  122. package/dist/summary/summarizerNode/index.js.map +1 -1
  123. package/dist/summary/summarizerNode/summarizerNode.d.ts.map +1 -1
  124. package/dist/summary/summarizerNode/summarizerNode.js +30 -31
  125. package/dist/summary/summarizerNode/summarizerNode.js.map +1 -1
  126. package/dist/summary/summarizerNode/summarizerNodeWithGc.d.ts +1 -1
  127. package/dist/summary/summarizerNode/summarizerNodeWithGc.js +3 -3
  128. package/dist/summary/summarizerNode/summarizerNodeWithGc.js.map +1 -1
  129. package/dist/summary/summarizerTypes.d.ts +7 -0
  130. package/dist/summary/summarizerTypes.d.ts.map +1 -1
  131. package/dist/summary/summarizerTypes.js.map +1 -1
  132. package/dist/summary/summaryCollection.d.ts +3 -4
  133. package/dist/summary/summaryCollection.d.ts.map +1 -1
  134. package/dist/summary/summaryCollection.js +9 -6
  135. package/dist/summary/summaryCollection.js.map +1 -1
  136. package/dist/summary/summaryFormat.d.ts +4 -1
  137. package/dist/summary/summaryFormat.d.ts.map +1 -1
  138. package/dist/summary/summaryFormat.js +3 -2
  139. package/dist/summary/summaryFormat.js.map +1 -1
  140. package/dist/summary/summaryGenerator.d.ts.map +1 -1
  141. package/dist/summary/summaryGenerator.js +19 -8
  142. package/dist/summary/summaryGenerator.js.map +1 -1
  143. package/dist/summary/summaryManager.d.ts.map +1 -1
  144. package/dist/summary/summaryManager.js +12 -9
  145. package/dist/summary/summaryManager.js.map +1 -1
  146. package/lib/batchTracker.d.ts +1 -2
  147. package/lib/batchTracker.d.ts.map +1 -1
  148. package/lib/batchTracker.js +2 -2
  149. package/lib/batchTracker.js.map +1 -1
  150. package/lib/blobManager/blobManager.d.ts.map +1 -1
  151. package/lib/blobManager/blobManager.js +14 -11
  152. package/lib/blobManager/blobManager.js.map +1 -1
  153. package/lib/blobManager/blobManagerSnapSum.d.ts +1 -0
  154. package/lib/blobManager/blobManagerSnapSum.d.ts.map +1 -1
  155. package/lib/blobManager/blobManagerSnapSum.js +7 -5
  156. package/lib/blobManager/blobManagerSnapSum.js.map +1 -1
  157. package/lib/channelCollection.d.ts.map +1 -1
  158. package/lib/channelCollection.js +66 -42
  159. package/lib/channelCollection.js.map +1 -1
  160. package/lib/connectionTelemetry.d.ts +2 -2
  161. package/lib/connectionTelemetry.d.ts.map +1 -1
  162. package/lib/connectionTelemetry.js +5 -5
  163. package/lib/connectionTelemetry.js.map +1 -1
  164. package/lib/containerRuntime.d.ts +14 -30
  165. package/lib/containerRuntime.d.ts.map +1 -1
  166. package/lib/containerRuntime.js +271 -196
  167. package/lib/containerRuntime.js.map +1 -1
  168. package/lib/dataStore.js +6 -3
  169. package/lib/dataStore.js.map +1 -1
  170. package/lib/dataStoreContext.d.ts.map +1 -1
  171. package/lib/dataStoreContext.js +16 -11
  172. package/lib/dataStoreContext.js.map +1 -1
  173. package/lib/dataStoreContexts.d.ts.map +1 -1
  174. package/lib/dataStoreContexts.js +1 -0
  175. package/lib/dataStoreContexts.js.map +1 -1
  176. package/lib/deltaScheduler.d.ts.map +1 -1
  177. package/lib/deltaScheduler.js +6 -6
  178. package/lib/deltaScheduler.js.map +1 -1
  179. package/lib/gc/garbageCollection.d.ts.map +1 -1
  180. package/lib/gc/garbageCollection.js +39 -15
  181. package/lib/gc/garbageCollection.js.map +1 -1
  182. package/lib/gc/gcConfigs.d.ts.map +1 -1
  183. package/lib/gc/gcConfigs.js +2 -0
  184. package/lib/gc/gcConfigs.js.map +1 -1
  185. package/lib/gc/gcDefinitions.d.ts +8 -0
  186. package/lib/gc/gcDefinitions.d.ts.map +1 -1
  187. package/lib/gc/gcDefinitions.js +1 -0
  188. package/lib/gc/gcDefinitions.js.map +1 -1
  189. package/lib/gc/gcHelpers.d.ts.map +1 -1
  190. package/lib/gc/gcHelpers.js +8 -5
  191. package/lib/gc/gcHelpers.js.map +1 -1
  192. package/lib/gc/gcSummaryStateTracker.d.ts.map +1 -1
  193. package/lib/gc/gcSummaryStateTracker.js +2 -1
  194. package/lib/gc/gcSummaryStateTracker.js.map +1 -1
  195. package/lib/gc/gcTelemetry.d.ts.map +1 -1
  196. package/lib/gc/gcTelemetry.js +32 -16
  197. package/lib/gc/gcTelemetry.js.map +1 -1
  198. package/lib/inboundBatchAggregator.js +4 -4
  199. package/lib/inboundBatchAggregator.js.map +1 -1
  200. package/lib/layerCompatState.d.ts +19 -0
  201. package/lib/layerCompatState.d.ts.map +1 -0
  202. package/lib/layerCompatState.js +60 -0
  203. package/lib/layerCompatState.js.map +1 -0
  204. package/lib/messageTypes.d.ts.map +1 -1
  205. package/lib/messageTypes.js.map +1 -1
  206. package/lib/opLifecycle/duplicateBatchDetector.js +2 -2
  207. package/lib/opLifecycle/duplicateBatchDetector.js.map +1 -1
  208. package/lib/opLifecycle/opCompressor.d.ts +3 -2
  209. package/lib/opLifecycle/opCompressor.d.ts.map +1 -1
  210. package/lib/opLifecycle/opCompressor.js +13 -19
  211. package/lib/opLifecycle/opCompressor.js.map +1 -1
  212. package/lib/opLifecycle/opDecompressor.d.ts +3 -0
  213. package/lib/opLifecycle/opDecompressor.d.ts.map +1 -1
  214. package/lib/opLifecycle/opDecompressor.js +4 -1
  215. package/lib/opLifecycle/opDecompressor.js.map +1 -1
  216. package/lib/opLifecycle/opGroupingManager.d.ts +1 -1
  217. package/lib/opLifecycle/opGroupingManager.d.ts.map +1 -1
  218. package/lib/opLifecycle/opGroupingManager.js +5 -3
  219. package/lib/opLifecycle/opGroupingManager.js.map +1 -1
  220. package/lib/opLifecycle/opSplitter.d.ts +13 -10
  221. package/lib/opLifecycle/opSplitter.d.ts.map +1 -1
  222. package/lib/opLifecycle/opSplitter.js +14 -11
  223. package/lib/opLifecycle/opSplitter.js.map +1 -1
  224. package/lib/opLifecycle/outbox.d.ts +3 -3
  225. package/lib/opLifecycle/outbox.d.ts.map +1 -1
  226. package/lib/opLifecycle/outbox.js +11 -15
  227. package/lib/opLifecycle/outbox.js.map +1 -1
  228. package/lib/opLifecycle/remoteMessageProcessor.d.ts.map +1 -1
  229. package/lib/opLifecycle/remoteMessageProcessor.js +3 -1
  230. package/lib/opLifecycle/remoteMessageProcessor.js.map +1 -1
  231. package/lib/packageVersion.d.ts +1 -1
  232. package/lib/packageVersion.js +1 -1
  233. package/lib/packageVersion.js.map +1 -1
  234. package/lib/pendingStateManager.d.ts +3 -4
  235. package/lib/pendingStateManager.d.ts.map +1 -1
  236. package/lib/pendingStateManager.js +12 -11
  237. package/lib/pendingStateManager.js.map +1 -1
  238. package/lib/summary/documentSchema.d.ts +7 -0
  239. package/lib/summary/documentSchema.d.ts.map +1 -1
  240. package/lib/summary/documentSchema.js +6 -4
  241. package/lib/summary/documentSchema.js.map +1 -1
  242. package/lib/summary/orderedClientElection.d.ts +1 -0
  243. package/lib/summary/orderedClientElection.d.ts.map +1 -1
  244. package/lib/summary/orderedClientElection.js +13 -11
  245. package/lib/summary/orderedClientElection.js.map +1 -1
  246. package/lib/summary/runWhileConnectedCoordinator.d.ts +1 -0
  247. package/lib/summary/runWhileConnectedCoordinator.d.ts.map +1 -1
  248. package/lib/summary/runWhileConnectedCoordinator.js +7 -2
  249. package/lib/summary/runWhileConnectedCoordinator.js.map +1 -1
  250. package/lib/summary/runningSummarizer.d.ts +2 -2
  251. package/lib/summary/runningSummarizer.d.ts.map +1 -1
  252. package/lib/summary/runningSummarizer.js +38 -17
  253. package/lib/summary/runningSummarizer.js.map +1 -1
  254. package/lib/summary/summarizer.d.ts +1 -0
  255. package/lib/summary/summarizer.d.ts.map +1 -1
  256. package/lib/summary/summarizer.js +18 -9
  257. package/lib/summary/summarizer.js.map +1 -1
  258. package/lib/summary/summarizerClientElection.d.ts.map +1 -1
  259. package/lib/summary/summarizerClientElection.js +1 -0
  260. package/lib/summary/summarizerClientElection.js.map +1 -1
  261. package/lib/summary/summarizerHeuristics.js +1 -1
  262. package/lib/summary/summarizerHeuristics.js.map +1 -1
  263. package/lib/summary/summarizerNode/index.d.ts.map +1 -1
  264. package/lib/summary/summarizerNode/index.js.map +1 -1
  265. package/lib/summary/summarizerNode/summarizerNode.d.ts.map +1 -1
  266. package/lib/summary/summarizerNode/summarizerNode.js +30 -31
  267. package/lib/summary/summarizerNode/summarizerNode.js.map +1 -1
  268. package/lib/summary/summarizerNode/summarizerNodeWithGc.d.ts +1 -1
  269. package/lib/summary/summarizerNode/summarizerNodeWithGc.js +3 -3
  270. package/lib/summary/summarizerNode/summarizerNodeWithGc.js.map +1 -1
  271. package/lib/summary/summarizerTypes.d.ts +7 -0
  272. package/lib/summary/summarizerTypes.d.ts.map +1 -1
  273. package/lib/summary/summarizerTypes.js.map +1 -1
  274. package/lib/summary/summaryCollection.d.ts +3 -4
  275. package/lib/summary/summaryCollection.d.ts.map +1 -1
  276. package/lib/summary/summaryCollection.js +9 -6
  277. package/lib/summary/summaryCollection.js.map +1 -1
  278. package/lib/summary/summaryFormat.d.ts +4 -1
  279. package/lib/summary/summaryFormat.d.ts.map +1 -1
  280. package/lib/summary/summaryFormat.js +2 -2
  281. package/lib/summary/summaryFormat.js.map +1 -1
  282. package/lib/summary/summaryGenerator.d.ts.map +1 -1
  283. package/lib/summary/summaryGenerator.js +19 -8
  284. package/lib/summary/summaryGenerator.js.map +1 -1
  285. package/lib/summary/summaryManager.d.ts.map +1 -1
  286. package/lib/summary/summaryManager.js +12 -9
  287. package/lib/summary/summaryManager.js.map +1 -1
  288. package/package.json +21 -43
  289. package/src/batchTracker.ts +3 -3
  290. package/src/blobManager/blobManager.ts +16 -14
  291. package/src/blobManager/blobManagerSnapSum.ts +8 -8
  292. package/src/channelCollection.ts +63 -44
  293. package/src/connectionTelemetry.ts +12 -6
  294. package/src/containerRuntime.ts +306 -235
  295. package/src/dataStore.ts +6 -3
  296. package/src/dataStoreContext.ts +16 -16
  297. package/src/dataStoreContexts.ts +1 -0
  298. package/src/deltaScheduler.ts +6 -6
  299. package/src/gc/garbageCollection.ts +47 -20
  300. package/src/gc/gcConfigs.ts +9 -1
  301. package/src/gc/gcDefinitions.ts +12 -0
  302. package/src/gc/gcHelpers.ts +9 -4
  303. package/src/gc/gcSummaryStateTracker.ts +3 -1
  304. package/src/gc/gcTelemetry.ts +26 -11
  305. package/src/inboundBatchAggregator.ts +4 -4
  306. package/src/layerCompatState.ts +75 -0
  307. package/src/messageTypes.ts +2 -0
  308. package/src/opLifecycle/README.md +43 -34
  309. package/src/opLifecycle/duplicateBatchDetector.ts +2 -2
  310. package/src/opLifecycle/opCompressor.ts +16 -23
  311. package/src/opLifecycle/opDecompressor.ts +4 -1
  312. package/src/opLifecycle/opGroupingManager.ts +5 -4
  313. package/src/opLifecycle/opSplitter.ts +14 -11
  314. package/src/opLifecycle/outbox.ts +13 -20
  315. package/src/opLifecycle/remoteMessageProcessor.ts +3 -1
  316. package/src/packageVersion.ts +1 -1
  317. package/src/pendingStateManager.ts +15 -10
  318. package/src/summary/documentSchema.ts +11 -4
  319. package/src/summary/orderedClientElection.ts +14 -11
  320. package/src/summary/runWhileConnectedCoordinator.ts +6 -0
  321. package/src/summary/runningSummarizer.ts +43 -19
  322. package/src/summary/summarizer.ts +24 -11
  323. package/src/summary/summarizerClientElection.ts +2 -0
  324. package/src/summary/summarizerHeuristics.ts +1 -1
  325. package/src/summary/summarizerNode/index.ts +1 -0
  326. package/src/summary/summarizerNode/summarizerNode.ts +32 -31
  327. package/src/summary/summarizerNode/summarizerNodeWithGc.ts +4 -4
  328. package/src/summary/summarizerTypes.ts +7 -0
  329. package/src/summary/summaryCollection.ts +19 -8
  330. package/src/summary/summaryFormat.ts +10 -5
  331. package/src/summary/summaryGenerator.ts +25 -10
  332. package/src/summary/summaryManager.ts +14 -12
  333. package/container-runtime.test-files.tar +0 -0
@@ -14,10 +14,13 @@ import {
14
14
  } from "@fluidframework/telemetry-utils/internal";
15
15
 
16
16
  import { RuntimeHeaderData } from "../containerRuntime.js";
17
+ // eslint-disable-next-line import/no-deprecated
17
18
  import { ICreateContainerMetadata } from "../summary/index.js";
18
19
 
19
20
  import {
21
+ // eslint-disable-next-line import/no-deprecated
20
22
  GCFeatureMatrix,
23
+ // eslint-disable-next-line import/no-deprecated
21
24
  GCNodeType,
22
25
  IGarbageCollectorConfigs,
23
26
  UnreferencedState,
@@ -41,6 +44,7 @@ interface ICommonProps {
41
44
  /**
42
45
  * The event that is logged when unreferenced node is used after a certain time.
43
46
  */
47
+ // eslint-disable-next-line import/no-deprecated
44
48
  interface IUnreferencedEventProps extends ICreateContainerMetadata, ICommonProps {
45
49
  /**
46
50
  * The id that GC uses to track the node. May or may not match id
@@ -52,11 +56,13 @@ interface IUnreferencedEventProps extends ICreateContainerMetadata, ICommonProps
52
56
  */
53
57
  id: Tagged<string>;
54
58
  fromId?: Tagged<string>;
59
+ // eslint-disable-next-line import/no-deprecated
55
60
  type: GCNodeType;
56
61
  unrefTime: number;
57
62
  age: number;
58
63
  // Expanding GC feature matrix. Without doing this, the configs cannot be logged in telemetry directly.
59
64
  gcConfigs: Omit<IGarbageCollectorConfigs, "persistedGcFeatureMatrix"> & {
65
+ // eslint-disable-next-line import/no-deprecated
60
66
  [K in keyof GCFeatureMatrix]: GCFeatureMatrix[K];
61
67
  };
62
68
  timeout?: number;
@@ -123,7 +129,9 @@ export class GCTelemetryTracker {
123
129
  private readonly mc: MonitoringContext,
124
130
  private readonly configs: IGarbageCollectorConfigs,
125
131
  private readonly isSummarizerClient: boolean,
132
+ // eslint-disable-next-line import/no-deprecated
126
133
  private readonly createContainerMetadata: ICreateContainerMetadata,
134
+ // eslint-disable-next-line import/no-deprecated
127
135
  private readonly getNodeType: (nodeId: string) => GCNodeType,
128
136
  private readonly getNodeStateTracker: (
129
137
  nodeId: string,
@@ -144,6 +152,7 @@ export class GCTelemetryTracker {
144
152
  * 2. An event is logged only once per container instance per event per node.
145
153
  */
146
154
  private shouldLogNonActiveEvent(
155
+ // eslint-disable-next-line import/no-deprecated
147
156
  nodeType: GCNodeType,
148
157
  usageType: NodeUsageType,
149
158
  nodeStateTracker: UnreferencedStateTracker,
@@ -153,12 +162,14 @@ export class GCTelemetryTracker {
153
162
  return false;
154
163
  }
155
164
 
165
+ // eslint-disable-next-line import/no-deprecated
156
166
  if (nodeType === GCNodeType.Other) {
157
167
  return false;
158
168
  }
159
169
 
160
170
  // For sub data store (DDS) nodes, if they are changed, its data store will also be changed,
161
171
  // so skip logging to make the telemetry less noisy.
172
+ // eslint-disable-next-line import/no-deprecated
162
173
  if (nodeType === GCNodeType.SubDataStore && usageType === "Changed") {
163
174
  return false;
164
175
  }
@@ -196,17 +207,21 @@ export class GCTelemetryTracker {
196
207
 
197
208
  const timeout = (() => {
198
209
  switch (nodeStateTracker?.state) {
199
- case UnreferencedState.Inactive:
210
+ case UnreferencedState.Inactive: {
200
211
  return this.configs.inactiveTimeoutMs;
201
- case UnreferencedState.TombstoneReady:
212
+ }
213
+ case UnreferencedState.TombstoneReady: {
202
214
  return this.configs.tombstoneTimeoutMs;
203
- case UnreferencedState.SweepReady:
215
+ }
216
+ case UnreferencedState.SweepReady: {
204
217
  return (
205
218
  this.configs.tombstoneTimeoutMs &&
206
219
  this.configs.tombstoneTimeoutMs + this.configs.sweepGracePeriodMs
207
220
  );
208
- default:
221
+ }
222
+ default: {
209
223
  return undefined;
224
+ }
210
225
  }
211
226
  })();
212
227
  const { persistedGcFeatureMatrix, ...configs } = this.configs;
@@ -215,9 +230,9 @@ export class GCTelemetryTracker {
215
230
  type: nodeType,
216
231
  unrefTime: nodeStateTracker?.unreferencedTimestampMs ?? -1,
217
232
  age:
218
- nodeStateTracker !== undefined
219
- ? currentReferenceTimestampMs - nodeStateTracker.unreferencedTimestampMs
220
- : -1,
233
+ nodeStateTracker === undefined
234
+ ? -1
235
+ : currentReferenceTimestampMs - nodeStateTracker.unreferencedTimestampMs,
221
236
  timeout,
222
237
  isTombstoned,
223
238
  ...tagCodeArtifacts({ id: untaggedId, fromId: untaggedFromId }),
@@ -290,6 +305,7 @@ export class GCTelemetryTracker {
290
305
  */
291
306
  private logTombstoneUsageTelemetry(
292
307
  unrefEventProps: Omit<IUnreferencedEventProps, "state" | "usageType">,
308
+ // eslint-disable-next-line import/no-deprecated
293
309
  nodeType: GCNodeType,
294
310
  usageType: NodeUsageType,
295
311
  packagePath?: readonly string[],
@@ -357,6 +373,7 @@ export class GCTelemetryTracker {
357
373
  for (const route of currentOutboundRoutes) {
358
374
  const nodeType = this.getNodeType(route);
359
375
  if (
376
+ // eslint-disable-next-line import/no-deprecated
360
377
  (nodeType === GCNodeType.DataStore || nodeType === GCNodeType.Blob) &&
361
378
  !nodeId.startsWith(route) &&
362
379
  !previousRoutes.includes(route) &&
@@ -411,10 +428,8 @@ export class GCTelemetryTracker {
411
428
  const active =
412
429
  nodeStateTracker === undefined || nodeStateTracker.state === UnreferencedState.Active;
413
430
  if ((usageType === "Revived") === active) {
414
- const pkg = await this.getNodePackagePath(eventProps.id.value);
415
- const fromPkg = eventProps.fromId
416
- ? await this.getNodePackagePath(eventProps.fromId.value)
417
- : undefined;
431
+ const pkg = await this.getNodePackagePath(id.value);
432
+ const fromPkg = fromId ? await this.getNodePackagePath(fromId.value) : undefined;
418
433
  const event = {
419
434
  eventName: `${state}Object_${usageType}`,
420
435
  id,
@@ -3,7 +3,7 @@
3
3
  * Licensed under the MIT License.
4
4
  */
5
5
 
6
- import { performance } from "@fluid-internal/client-utils";
6
+ import { performanceNow } from "@fluid-internal/client-utils";
7
7
  import { IDeltaManagerFull } from "@fluidframework/container-definitions/internal";
8
8
  import { assert } from "@fluidframework/core-utils/internal";
9
9
  import { ISequencedDocumentMessage } from "@fluidframework/driver-definitions/internal";
@@ -114,7 +114,7 @@ export class InboundBatchAggregator {
114
114
  */
115
115
  private readonly trackPending = (message: ISequencedDocumentMessage): void => {
116
116
  assert(
117
- this.deltaManager.inbound.length !== 0,
117
+ this.deltaManager.inbound.length > 0,
118
118
  0x298 /* "we have something in the queue that generates this event" */,
119
119
  );
120
120
 
@@ -227,14 +227,14 @@ export class InboundBatchAggregator {
227
227
  private pauseQueue(): void {
228
228
  assert(!this.localPaused, 0x297 /* "always called from resumed state" */);
229
229
  this.localPaused = true;
230
- this.timePaused = performance.now();
230
+ this.timePaused = performanceNow();
231
231
  // eslint-disable-next-line @typescript-eslint/no-floating-promises
232
232
  this.deltaManager.inbound.pause();
233
233
  }
234
234
 
235
235
  private resumeQueue(startBatch: number, messageEndBatch: ISequencedDocumentMessage): void {
236
236
  const endBatch = messageEndBatch.sequenceNumber;
237
- const duration = this.localPaused ? performance.now() - this.timePaused : undefined;
237
+ const duration = this.localPaused ? performanceNow() - this.timePaused : undefined;
238
238
 
239
239
  this.batchCount++;
240
240
  if (this.batchCount % 1000 === 1) {
@@ -0,0 +1,75 @@
1
+ /*!
2
+ * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
+ * Licensed under the MIT License.
4
+ */
5
+
6
+ import {
7
+ checkLayerCompatibility,
8
+ type ILayerCompatDetails,
9
+ type ILayerCompatSupportRequirements,
10
+ } from "@fluid-internal/client-utils";
11
+ import type { ICriticalContainerError } from "@fluidframework/container-definitions";
12
+ import { UsageError } from "@fluidframework/telemetry-utils/internal";
13
+
14
+ import { pkgVersion } from "./packageVersion.js";
15
+
16
+ /**
17
+ * Runtime's compatibility details that is exposed to the Loader layer.
18
+ */
19
+ export const RuntimeCompatDetails: ILayerCompatDetails = {
20
+ /**
21
+ * The package version of the Runtime layer.
22
+ */
23
+ pkgVersion,
24
+ /**
25
+ * The current generation of the Runtime layer.
26
+ */
27
+ generation: 1,
28
+ /**
29
+ * The features supported by the Runtime layer across the Runtime / Loader boundary.
30
+ */
31
+ supportedFeatures: new Set<string>(),
32
+ };
33
+
34
+ /**
35
+ * The requirements that the Loader layer must meet to be compatible with this Runtime.
36
+ */
37
+ export const LoaderSupportRequirements: ILayerCompatSupportRequirements = {
38
+ /**
39
+ * Minimum generation that Loader must be at to be compatible with Runtime.
40
+ */
41
+ minSupportedGeneration: 0,
42
+ /**
43
+ * The features that the Loader must support to be compatible with Runtime. Note that 0 is used here for
44
+ * Loader layers before the introduction of the layer compatibility enforcement.
45
+ */
46
+ requiredFeatures: [],
47
+ };
48
+
49
+ /**
50
+ * Validates that the Loader layer is compatible with this Runtime.
51
+ */
52
+ export function validateLoaderCompatibility(
53
+ maybeLoaderCompatDetails: ILayerCompatDetails | undefined,
54
+ disposeFn: (error?: ICriticalContainerError) => void,
55
+ ): void {
56
+ const layerCheckResult = checkLayerCompatibility(
57
+ LoaderSupportRequirements,
58
+ maybeLoaderCompatDetails,
59
+ );
60
+ if (!layerCheckResult.isCompatible) {
61
+ const error = new UsageError("Runtime is not compatible with Loader", {
62
+ errorDetails: JSON.stringify({
63
+ runtimeVersion: RuntimeCompatDetails.pkgVersion,
64
+ loaderVersion: maybeLoaderCompatDetails?.pkgVersion,
65
+ runtimeGeneration: RuntimeCompatDetails.generation,
66
+ loaderGeneration: maybeLoaderCompatDetails?.generation,
67
+ minSupportedGeneration: LoaderSupportRequirements.minSupportedGeneration,
68
+ isGenerationCompatible: layerCheckResult.isGenerationCompatible,
69
+ unsupportedFeatures: layerCheckResult.unsupportedFeatures,
70
+ }),
71
+ });
72
+ disposeFn(error);
73
+ throw error;
74
+ }
75
+ }
@@ -14,6 +14,7 @@ import {
14
14
  import { IDataStoreAliasMessage } from "./dataStore.js";
15
15
  import { GarbageCollectionMessage } from "./gc/index.js";
16
16
  import { IChunkedOp } from "./opLifecycle/index.js";
17
+ // eslint-disable-next-line import/no-deprecated
17
18
  import { IDocumentSchemaChangeMessage } from "./summary/index.js";
18
19
 
19
20
  /**
@@ -114,6 +115,7 @@ export type ContainerRuntimeGCMessage = TypedContainerRuntimeMessage<
114
115
  >;
115
116
  export type ContainerRuntimeDocumentSchemaMessage = TypedContainerRuntimeMessage<
116
117
  ContainerMessageType.DocumentSchemaChange,
118
+ // eslint-disable-next-line import/no-deprecated
117
119
  IDocumentSchemaChangeMessage
118
120
  >;
119
121
 
@@ -2,21 +2,23 @@
2
2
 
3
3
  ## Table of contents
4
4
 
5
- - [Configs and feature gates for solving the 1MB limit.](#configs-and-feature-gates-for-solving-the-1mb-limit)
6
- - [Table of contents](#table-of-contents)
7
- - [Introduction](#introduction)
8
- - [How batching works](#how-batching-works)
9
- - [Compression](#compression)
10
- - [Grouped batching](#grouped-batching)
11
- - [Changes in op semantics](#changes-in-op-semantics)
12
- - [Chunking for compression](#chunking-for-compression)
13
- - [Configuration](#configuration)
14
- - [Note about performance and latency](#note-about-performance-and-latency)
15
- - [How it works](#how-it-works)
16
- - [How it works (Grouped Batching disabled)](#how-it-works-grouped-batching-disabled)
17
- - [How the overall op flow works](#how-the-overall-op-flow-works)
18
- - [Outbound](#outbound)
19
- - [Inbound](#inbound)
5
+ - [Configs and feature gates for solving the 1MB limit.](#configs-and-feature-gates-for-solving-the-1mb-limit)
6
+ - [Table of contents](#table-of-contents)
7
+ - [Introduction](#introduction)
8
+ - [How batching works](#how-batching-works)
9
+ - [Grouped batching](#grouped-batching)
10
+ - [Changes in op semantics](#changes-in-op-semantics)
11
+ - [Compression](#compression)
12
+ - [Only single-message batches are compressed](#only-single-message-batches-are-compressed)
13
+ - [Chunking for compression](#chunking-for-compression)
14
+ - [Configuration](#configuration)
15
+ - [Note about performance and latency](#note-about-performance-and-latency)
16
+ - [How it works](#how-it-works)
17
+ - [Legacy behavior - How it used to work (Compression+Chunking without Grouped Batching)](#legacy-behavior---how-it-used-to-work-compressionchunking-without-grouped-batching)
18
+ - [IMPORTANT - As of 2.20.0, we no longer compress ungrouped batches, but we do need to read such ops - read on to learn what these legacy ops look like](#important---as-of-2200-we-no-longer-compress-ungrouped-batches-but-we-do-need-to-read-such-ops---read-on-to-learn-what-these-legacy-ops-look-like)
19
+ - [How the overall op flow works](#how-the-overall-op-flow-works)
20
+ - [Outbound](#outbound)
21
+ - [Inbound](#inbound)
20
22
 
21
23
  ## Introduction
22
24
 
@@ -56,20 +58,6 @@ What this means is that `FlushMode.Immediate` will send each op in its own paylo
56
58
 
57
59
  As `FlushMode.TurnBased` accumulates ops, it is the most vulnerable to run into the 1MB socket limit.
58
60
 
59
- ## Compression
60
-
61
- **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.
62
-
63
- `ICompressionRuntimeOptions` has two properties:
64
-
65
- - `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.
66
- - `compressionAlgorithm` – currently, only `lz4` is supported.
67
-
68
- Compression is relevant for both `FlushMode.TurnBased` and `FlushMode.Immediate` as it only targets the contents of the ops and not the number of ops in a batch. Compression is opaque to the server and implementations of the Fluid protocol do not need to alter their behavior to support this client feature.
69
-
70
- Compressing a batch yields a batch with the same number of messages. It compresses all the content, shifting the compressed payload into the first op,
71
- leaving the rest of the batch's messages as empty placeholders to reserve sequence numbers for the compressed messages.
72
-
73
61
  ## Grouped batching
74
62
 
75
63
  With Grouped Batching enabled (it's on by default), all batch messages are combined under a single "grouped" message _before compression_. Upon receiving this new "grouped" message, the batch messages will be extracted, and they each will be given the same sequence number - that of the parent "grouped" message.
@@ -78,9 +66,7 @@ The purpose for enabling grouped batching before compression is to eliminate the
78
66
 
79
67
  Grouped batching is only relevant for `FlushMode.TurnBased`, since `OpGroupingManagerConfig.opCountThreshold` defaults to 2. Grouped batching is opaque to the server and implementations of the Fluid protocol do not need to alter their behavior to support this client feature.
80
68
 
81
- Grouped Batching can be disabled by setting `IContainerRuntimeOptions.enableGroupedBatching` to `false`.
82
-
83
- See [below](#how-grouped-batching-works) for an example.
69
+ See [below](#how-it-works) for an example.
84
70
 
85
71
  ### Changes in op semantics
86
72
 
@@ -92,6 +78,27 @@ Grouped Batching changed a couple of expectations around message structure and r
92
78
  - All ops in a batch must also have the same reference sequence number to ensure eventualy consistency of the model. The runtime will "rebase" ops in a batch with different ref sequence number to satisfy that requirement.
93
79
  - What causes ops in a single JS turn (and thus in a batch) to have different reference sequence number? "Op reentrancy", where changes are made to a DDS inside a DDS 'onChanged' event handler.
94
80
 
81
+ ## Compression
82
+
83
+ **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.
84
+
85
+ `ICompressionRuntimeOptions` has two properties:
86
+
87
+ - `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.
88
+ - `compressionAlgorithm` – currently, only `lz4` is supported.
89
+
90
+ Compression is relevant for both `FlushMode.TurnBased` and `FlushMode.Immediate` as it only targets the contents of the ops and not the number of ops in a batch. Compression is opaque to the server and implementations of the Fluid protocol do not need to alter their behavior to support this client feature.
91
+
92
+ ### Only single-message batches are compressed
93
+
94
+ The batch to compress has to have only one message and it yields a batch with a single message. It compresses all the content, replacing the op's original contents with the compressed payload and the appropriate metadata to indicate it's compressed.
95
+
96
+ Compression is only enabled if Grouped Batching is enabled.
97
+
98
+ Legacy compressed batches could contain multiple messages, compressing all the content and shifting the compressed payload into the first op, leaving the rest of the batch's messages as empty placeholders to reserve sequence numbers for the compressed messages.
99
+
100
+ While it remains possible to read compressed batches with empty ops, the system will no longer create said type of batches.
101
+
95
102
  ## Chunking for compression
96
103
 
97
104
  **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.
@@ -200,7 +207,9 @@ Ungrouped batch:
200
207
  +-----------------+-----------------+-----------------+-----------------+-----------------+
201
208
  ```
202
209
 
203
- ## How it works (Grouped Batching disabled)
210
+ ## Legacy behavior - How it used to work (Compression+Chunking without Grouped Batching)
211
+
212
+ ### IMPORTANT - As of 2.20.0, we no longer compress ungrouped batches, but we do need to read such ops - read on to learn what these legacy ops look like
204
213
 
205
214
  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:
206
215
 
@@ -311,7 +320,7 @@ stateDiagram-v2
311
320
  groupBatch --> if_compression
312
321
  flushInternal --> if_compression
313
322
  if_compression --> post
314
- if_compression --> compress: if compression is enabled
323
+ if_compression --> compress: if compression and grouped batching are enabled
315
324
  compress --> post
316
325
  compress --> opSplitter.split: if the compressed payload is larger than the chunk size
317
326
  opSplitter.split --> post
@@ -85,12 +85,12 @@ export class DuplicateBatchDetector {
85
85
  * since the batch start has been processed by all clients, and local batches are deduped and the forked client would close.
86
86
  */
87
87
  private clearOldBatchIds(msn: number): void {
88
- this.batchIdsBySeqNum.forEach((batchId, sequenceNumber) => {
88
+ for (const [sequenceNumber, batchId] of this.batchIdsBySeqNum) {
89
89
  if (sequenceNumber < msn) {
90
90
  this.batchIdsBySeqNum.delete(sequenceNumber);
91
91
  this.batchIdsAll.delete(batchId);
92
92
  }
93
- });
93
+ }
94
94
  }
95
95
 
96
96
  /**
@@ -34,13 +34,14 @@ export class OpCompressor {
34
34
  * Combines the contents of the batch into a single JSON string and compresses it, putting
35
35
  * the resulting string as the first message of the batch. The rest of the messages are
36
36
  * empty placeholders to reserve sequence numbers.
37
+ * This should only take a single message batch and compress it.
37
38
  * @param batch - The batch to compress
38
39
  * @returns A batch of the same length as the input batch, containing a single compressed message followed by empty placeholders
39
40
  */
40
- public compressBatch(batch: IBatch): IBatch {
41
+ public compressBatch(batch: IBatch): IBatch<[BatchMessage]> {
41
42
  assert(
42
- batch.contentSizeInBytes > 0 && batch.messages.length > 0,
43
- 0x5a4 /* Batch should not be empty */,
43
+ batch.contentSizeInBytes > 0 && batch.messages.length === 1,
44
+ 0x5a4 /* Batch should not be empty and should contain a single message */,
44
45
  );
45
46
 
46
47
  const compressionStart = Date.now();
@@ -49,24 +50,16 @@ export class OpCompressor {
49
50
  const compressedContent = IsoBuffer.from(compressedContents).toString("base64");
50
51
  const duration = Date.now() - compressionStart;
51
52
 
52
- const messages: BatchMessage[] = [];
53
- messages.push({
54
- ...batch.messages[0],
55
- contents: JSON.stringify({ packedContents: compressedContent }),
56
- metadata: batch.messages[0].metadata,
57
- compression: CompressionAlgorithms.lz4,
58
- });
53
+ const messages: [BatchMessage] = [
54
+ {
55
+ ...batch.messages[0],
56
+ contents: JSON.stringify({ packedContents: compressedContent }),
57
+ metadata: batch.messages[0].metadata,
58
+ compression: CompressionAlgorithms.lz4,
59
+ },
60
+ ];
59
61
 
60
- // Add empty placeholder messages to reserve the sequence numbers
61
- for (const message of batch.messages.slice(1)) {
62
- messages.push({
63
- localOpMetadata: message.localOpMetadata,
64
- metadata: message.metadata,
65
- referenceSequenceNumber: message.referenceSequenceNumber,
66
- });
67
- }
68
-
69
- const compressedBatch: IBatch = {
62
+ const compressedBatch = {
70
63
  contentSizeInBytes: compressedContent.length,
71
64
  messages,
72
65
  referenceSequenceNumber: batch.referenceSequenceNumber,
@@ -93,8 +86,8 @@ export class OpCompressor {
93
86
  try {
94
87
  // Yields a valid JSON array, since each message.contents is already serialized to JSON
95
88
  return `[${batch.messages.map(({ contents }) => contents).join(",")}]`;
96
- } catch (e: unknown) {
97
- if ((e as Partial<Error>).message === "Invalid string length") {
89
+ } catch (newError: unknown) {
90
+ if ((newError as Partial<Error>).message === "Invalid string length") {
98
91
  // This is how JSON.stringify signals that
99
92
  // the content size exceeds its capacity
100
93
  const error = new UsageError("Payload too large");
@@ -109,7 +102,7 @@ export class OpCompressor {
109
102
  throw error;
110
103
  }
111
104
 
112
- throw e;
105
+ throw newError;
113
106
  }
114
107
  }
115
108
  }
@@ -30,6 +30,9 @@ interface IPackedContentsContents {
30
30
  * 2. Messages in the middle of a compressed batch will have neither batch metadata nor the compression property set
31
31
  * 3. The final message of a batch will have batch metadata set to false
32
32
  * 4. An individually compressed op will have undefined batch metadata and compression set to true
33
+ *
34
+ * Compressed batches from current code are always a single message but this class needs to handle a legacy compressed batch with multiple messages
35
+ * because we need that functionality for back compat.
33
36
  */
34
37
  export class OpDecompressor {
35
38
  private activeBatch = false;
@@ -79,7 +82,7 @@ export class OpDecompressor {
79
82
  });
80
83
  return true;
81
84
  }
82
- } catch (err) {
85
+ } catch {
83
86
  return false;
84
87
  }
85
88
 
@@ -40,7 +40,6 @@ export function isGroupedBatch(op: ISequencedDocumentMessage): boolean {
40
40
 
41
41
  export interface OpGroupingManagerConfig {
42
42
  readonly groupedBatchingEnabled: boolean;
43
- readonly opCountThreshold: number;
44
43
  }
45
44
 
46
45
  export class OpGroupingManager {
@@ -102,7 +101,6 @@ export class OpGroupingManager {
102
101
  this.logger.sendTelemetryEvent({
103
102
  eventName: "GroupLargeBatch",
104
103
  length: batch.messages.length,
105
- threshold: this.config.opCountThreshold,
106
104
  reentrant: batch.hasReentrantOps,
107
105
  referenceSequenceNumber: batch.messages[0].referenceSequenceNumber,
108
106
  });
@@ -159,10 +157,13 @@ export class OpGroupingManager {
159
157
  return (
160
158
  // Grouped batching must be enabled
161
159
  this.config.groupedBatchingEnabled &&
162
- // The number of ops in the batch must surpass the configured threshold
160
+ // The number of ops in the batch must be 2 or more
163
161
  // or be empty (to allow for empty batches to be grouped)
164
- (batch.messages.length === 0 || batch.messages.length >= this.config.opCountThreshold)
162
+ batch.messages.length !== 1
165
163
  // Support for reentrant batches will be on by default
166
164
  );
167
165
  }
166
+ public groupedBatchingEnabled(): boolean {
167
+ return this.config.groupedBatchingEnabled;
168
+ }
168
169
  }
@@ -99,25 +99,28 @@ export class OpSplitter {
99
99
  * Splits the first op of a compressed batch in chunks, sends the chunks separately and
100
100
  * returns a new batch composed of the last chunk and the rest of the ops in the original batch.
101
101
  *
102
- * A compressed batch is formed by one large op at the first position, followed by a series of placeholder ops
103
- * which are used in order to reserve the sequence numbers for when the first op gets unrolled into the original
104
- * uncompressed ops at ingestion in the runtime.
102
+ * A compressed batch is formed by one large op at the first position.
105
103
  *
106
- * If the first op is too large, it can be chunked (split into smaller op) which can be sent individually over the wire
104
+ * If the op is too large, it can be chunked (split into smaller op) which can be sent individually over the wire
107
105
  * and accumulate at ingestion, until the last op in the chunk is processed, when the original op is unrolled.
108
106
  *
109
- * This method will send the first N - 1 chunks separately and use the last chunk as the first message in the result batch
110
- * and then appends the original placeholder ops. This will ensure that the batch semantics of the original (non-compressed) batch
111
- * are preserved, as the original chunked op will be unrolled by the runtime when the first message in the batch is processed
112
- * (as it is the last chunk).
107
+ * This method will send the first N - 1 chunks separately and use the last chunk as the first message in the result batch.
108
+ * This will ensure that the batch semantics of the original (non-compressed) batch are preserved, as the original chunked op
109
+ * will be unrolled by the runtime when the first message in the batch is processed (as it is the last chunk).
113
110
  *
114
- * To illustrate, if the input is `[largeOp, emptyOp, emptyOp]`, `largeOp` will be split into `[chunk1, chunk2, chunk3, chunk4]`.
111
+ * To handle legacy compressed batches with empty placeholders this method can attach the empty placeholder ops at the end
112
+ * of the result batch, ensuring that the batch semantics are preserved.
113
+ *
114
+ * To illustrate the current functionality, if the input is `[largeOp]`, `largeOp` will be split into `[chunk1, chunk2, chunk3, chunk4]`.
115
+ * `chunk1`, `chunk2` and `chunk3` will be sent individually and `[chunk4]` will be returned.
116
+ *
117
+ * With the legacy code, if the input is `[largeOp, emptyOp, emptyOp]`, `largeOp` will be split into `[chunk1, chunk2, chunk3, chunk4]`.
115
118
  * `chunk1`, `chunk2` and `chunk3` will be sent individually and `[chunk4, emptyOp, emptyOp]` will be returned.
116
119
  *
117
120
  * @remarks - A side effect here is that 1 or more chunks are queued immediately for sending in next JS turn.
118
121
  *
119
122
  * @param batch - the compressed batch which needs to be processed
120
- * @returns A new adjusted batch (last chunk + empty placeholders) which can be sent over the wire
123
+ * @returns A batch with the last chunk of the original message
121
124
  */
122
125
  public splitFirstBatchMessage(batch: IBatch): IBatch {
123
126
  assert(this.isBatchChunkingEnabled, 0x513 /* Chunking needs to be enabled */);
@@ -282,7 +285,7 @@ export const splitOp = (
282
285
  for (let chunkId = 1; chunkId <= chunkCount; chunkId++) {
283
286
  const chunk: IChunkedOp = {
284
287
  chunkId,
285
- contents: op.contents.substr(offset, chunkSizeInBytes),
288
+ contents: op.contents.slice(offset, offset + chunkSizeInBytes),
286
289
  totalChunks: chunkCount,
287
290
  };
288
291
 
@@ -135,7 +135,9 @@ export class Outbox {
135
135
  this.params.config.compressionOptions.minimumBatchSizeInBytes !==
136
136
  Number.POSITIVE_INFINITY;
137
137
  // We need to allow infinite size batches if we enable compression
138
- const hardLimit = isCompressionEnabled ? Infinity : this.params.config.maxBatchSizeInBytes;
138
+ const hardLimit = isCompressionEnabled
139
+ ? Number.POSITIVE_INFINITY
140
+ : this.params.config.maxBatchSizeInBytes;
139
141
 
140
142
  this.mainBatch = new BatchManager({ hardLimit, canRebase: true });
141
143
  this.blobAttachBatch = new BatchManager({ hardLimit, canRebase: true });
@@ -229,18 +231,6 @@ export class Outbox {
229
231
  this.maybeFlushPartialBatch();
230
232
 
231
233
  this.addMessageToBatchManager(this.blobAttachBatch, message);
232
-
233
- // If compression is enabled, we will always successfully receive
234
- // blobAttach ops and compress then send them at the next JS turn, regardless
235
- // of the overall size of the accumulated ops in the batch.
236
- // However, it is more efficient to flush these ops faster, preferably
237
- // after they reach a size which would benefit from compression.
238
- if (
239
- this.blobAttachBatch.contentSizeInBytes >=
240
- this.params.config.compressionOptions.minimumBatchSizeInBytes
241
- ) {
242
- this.flushInternal(this.blobAttachBatch);
243
- }
244
234
  }
245
235
 
246
236
  public submitIdAllocation(message: BatchMessage): void {
@@ -357,9 +347,11 @@ export class Outbox {
357
347
  // If so, do nothing, as pending state manager will resubmit it correctly on reconnect.
358
348
  // Because flush() is a task that executes async (on clean stack), we can get here in disconnected state.
359
349
  if (this.params.shouldSend()) {
360
- const processedBatch = this.compressBatch(
361
- shouldGroup ? this.params.groupingManager.groupBatch(rawBatch) : rawBatch,
362
- );
350
+ const processedBatch = disableGroupedBatching
351
+ ? rawBatch
352
+ : this.compressAndChunkBatch(
353
+ shouldGroup ? this.params.groupingManager.groupBatch(rawBatch) : rawBatch,
354
+ );
363
355
  clientSequenceNumber = this.sendBatch(processedBatch);
364
356
  assert(
365
357
  clientSequenceNumber === undefined || clientSequenceNumber >= 0,
@@ -420,16 +412,17 @@ export class Outbox {
420
412
  * @remarks - If chunking happens, a side effect here is that 1 or more chunks are queued immediately for sending in next JS turn.
421
413
  *
422
414
  * @param batch - Raw or Grouped batch to consider for compression/chunking
423
- * @returns Either (A) the original batch, (B) a compressed batch (same length as original),
424
- * or (C) a batch containing the last chunk (plus empty placeholders from compression if applicable).
415
+ * @returns Either (A) the original batch, (B) a compressed batch (same length as original)
416
+ * or (C) a batch containing the last chunk.
425
417
  */
426
- private compressBatch(batch: IBatch): IBatch {
418
+ private compressAndChunkBatch(batch: IBatch): IBatch {
427
419
  if (
428
420
  batch.messages.length === 0 ||
429
421
  this.params.config.compressionOptions === undefined ||
430
422
  this.params.config.compressionOptions.minimumBatchSizeInBytes >
431
423
  batch.contentSizeInBytes ||
432
- this.params.submitBatchFn === undefined
424
+ this.params.submitBatchFn === undefined ||
425
+ !this.params.groupingManager.groupedBatchingEnabled()
433
426
  ) {
434
427
  // Nothing to do if the batch is empty or if compression is disabled or not supported, or if we don't need to compress
435
428
  return batch;
@@ -165,7 +165,9 @@ export class RemoteMessageProcessor {
165
165
  // We should be awaiting a new batch (batchInProgress false)
166
166
  assert(!this.batchInProgress, 0x9d3 /* Grouped batch interrupting another batch */);
167
167
  const batchId = asBatchMetadata(message.metadata)?.batchId;
168
- const groupedMessages = this.opGroupingManager.ungroupOp(message).map(unpack);
168
+ const groupedMessages = this.opGroupingManager
169
+ .ungroupOp(message)
170
+ .map((innerMessage) => unpack(innerMessage));
169
171
 
170
172
  return {
171
173
  type: "fullBatch",
@@ -6,4 +6,4 @@
6
6
  */
7
7
 
8
8
  export const pkgName = "@fluidframework/container-runtime";
9
- export const pkgVersion = "2.20.0";
9
+ export const pkgVersion = "2.21.0";