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

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 (365) hide show
  1. package/.eslintrc.js +21 -10
  2. package/.mocharc.js +2 -2
  3. package/api-extractor.json +2 -2
  4. package/dist/batchTracker.d.ts +1 -2
  5. package/dist/batchTracker.d.ts.map +1 -1
  6. package/dist/batchTracker.js +2 -1
  7. package/dist/batchTracker.js.map +1 -1
  8. package/dist/blobManager.d.ts +62 -28
  9. package/dist/blobManager.d.ts.map +1 -1
  10. package/dist/blobManager.js +256 -102
  11. package/dist/blobManager.js.map +1 -1
  12. package/dist/connectionTelemetry.d.ts.map +1 -1
  13. package/dist/connectionTelemetry.js +11 -9
  14. package/dist/connectionTelemetry.js.map +1 -1
  15. package/dist/containerHandleContext.d.ts.map +1 -1
  16. package/dist/containerHandleContext.js +3 -1
  17. package/dist/containerHandleContext.js.map +1 -1
  18. package/dist/containerRuntime.d.ts +110 -78
  19. package/dist/containerRuntime.d.ts.map +1 -1
  20. package/dist/containerRuntime.js +336 -331
  21. package/dist/containerRuntime.js.map +1 -1
  22. package/dist/dataStore.d.ts.map +1 -1
  23. package/dist/dataStore.js +11 -9
  24. package/dist/dataStore.js.map +1 -1
  25. package/dist/dataStoreContext.d.ts +2 -1
  26. package/dist/dataStoreContext.d.ts.map +1 -1
  27. package/dist/dataStoreContext.js +40 -23
  28. package/dist/dataStoreContext.js.map +1 -1
  29. package/dist/dataStoreContexts.d.ts.map +1 -1
  30. package/dist/dataStoreContexts.js +7 -3
  31. package/dist/dataStoreContexts.js.map +1 -1
  32. package/dist/dataStoreRegistry.d.ts.map +1 -1
  33. package/dist/dataStoreRegistry.js +3 -1
  34. package/dist/dataStoreRegistry.js.map +1 -1
  35. package/dist/dataStores.d.ts +12 -9
  36. package/dist/dataStores.d.ts.map +1 -1
  37. package/dist/dataStores.js +69 -46
  38. package/dist/dataStores.js.map +1 -1
  39. package/dist/deltaScheduler.d.ts.map +1 -1
  40. package/dist/deltaScheduler.js +8 -3
  41. package/dist/deltaScheduler.js.map +1 -1
  42. package/dist/garbageCollection.d.ts +57 -42
  43. package/dist/garbageCollection.d.ts.map +1 -1
  44. package/dist/garbageCollection.js +371 -239
  45. package/dist/garbageCollection.js.map +1 -1
  46. package/dist/garbageCollectionConstants.d.ts +23 -0
  47. package/dist/garbageCollectionConstants.d.ts.map +1 -0
  48. package/dist/garbageCollectionConstants.js +36 -0
  49. package/dist/garbageCollectionConstants.js.map +1 -0
  50. package/dist/garbageCollectionHelpers.d.ts +15 -0
  51. package/dist/garbageCollectionHelpers.d.ts.map +1 -0
  52. package/dist/garbageCollectionHelpers.js +27 -0
  53. package/dist/garbageCollectionHelpers.js.map +1 -0
  54. package/dist/gcSweepReadyUsageDetection.d.ts +5 -5
  55. package/dist/gcSweepReadyUsageDetection.d.ts.map +1 -1
  56. package/dist/gcSweepReadyUsageDetection.js +15 -11
  57. package/dist/gcSweepReadyUsageDetection.js.map +1 -1
  58. package/dist/index.d.ts +4 -3
  59. package/dist/index.d.ts.map +1 -1
  60. package/dist/index.js +5 -6
  61. package/dist/index.js.map +1 -1
  62. package/dist/opLifecycle/batchManager.d.ts +42 -0
  63. package/dist/opLifecycle/batchManager.d.ts.map +1 -0
  64. package/dist/opLifecycle/batchManager.js +124 -0
  65. package/dist/opLifecycle/batchManager.js.map +1 -0
  66. package/dist/opLifecycle/definitions.d.ts +64 -0
  67. package/dist/opLifecycle/definitions.d.ts.map +1 -0
  68. package/dist/opLifecycle/definitions.js +7 -0
  69. package/dist/opLifecycle/definitions.js.map +1 -0
  70. package/dist/opLifecycle/index.d.ts +12 -0
  71. package/dist/opLifecycle/index.d.ts.map +1 -0
  72. package/dist/opLifecycle/index.js +22 -0
  73. package/dist/opLifecycle/index.js.map +1 -0
  74. package/dist/{opCompressor.d.ts → opLifecycle/opCompressor.d.ts} +3 -3
  75. package/dist/opLifecycle/opCompressor.d.ts.map +1 -0
  76. package/dist/opLifecycle/opCompressor.js +67 -0
  77. package/dist/opLifecycle/opCompressor.js.map +1 -0
  78. package/dist/{opDecompressor.d.ts → opLifecycle/opDecompressor.d.ts} +2 -1
  79. package/dist/opLifecycle/opDecompressor.d.ts.map +1 -0
  80. package/dist/{opDecompressor.js → opLifecycle/opDecompressor.js} +37 -21
  81. package/dist/opLifecycle/opDecompressor.js.map +1 -0
  82. package/dist/opLifecycle/opSplitter.d.ts +49 -0
  83. package/dist/opLifecycle/opSplitter.d.ts.map +1 -0
  84. package/dist/opLifecycle/opSplitter.js +173 -0
  85. package/dist/opLifecycle/opSplitter.js.map +1 -0
  86. package/dist/opLifecycle/outbox.d.ts +52 -0
  87. package/dist/opLifecycle/outbox.d.ts.map +1 -0
  88. package/dist/opLifecycle/outbox.js +164 -0
  89. package/dist/opLifecycle/outbox.js.map +1 -0
  90. package/dist/opLifecycle/remoteMessageProcessor.d.ts +26 -0
  91. package/dist/opLifecycle/remoteMessageProcessor.d.ts.map +1 -0
  92. package/dist/opLifecycle/remoteMessageProcessor.js +96 -0
  93. package/dist/opLifecycle/remoteMessageProcessor.js.map +1 -0
  94. package/dist/opProperties.d.ts.map +1 -1
  95. package/dist/opProperties.js +1 -3
  96. package/dist/opProperties.js.map +1 -1
  97. package/dist/orderedClientElection.d.ts.map +1 -1
  98. package/dist/orderedClientElection.js +10 -4
  99. package/dist/orderedClientElection.js.map +1 -1
  100. package/dist/packageVersion.d.ts +1 -1
  101. package/dist/packageVersion.js +1 -1
  102. package/dist/packageVersion.js.map +1 -1
  103. package/dist/pendingStateManager.d.ts +4 -13
  104. package/dist/pendingStateManager.d.ts.map +1 -1
  105. package/dist/pendingStateManager.js +134 -161
  106. package/dist/pendingStateManager.js.map +1 -1
  107. package/dist/runWhileConnectedCoordinator.d.ts.map +1 -1
  108. package/dist/runWhileConnectedCoordinator.js.map +1 -1
  109. package/dist/runningSummarizer.d.ts.map +1 -1
  110. package/dist/runningSummarizer.js +34 -22
  111. package/dist/runningSummarizer.js.map +1 -1
  112. package/dist/scheduleManager.d.ts +0 -1
  113. package/dist/scheduleManager.d.ts.map +1 -1
  114. package/dist/scheduleManager.js +11 -21
  115. package/dist/scheduleManager.js.map +1 -1
  116. package/dist/serializedSnapshotStorage.d.ts.map +1 -1
  117. package/dist/serializedSnapshotStorage.js +3 -1
  118. package/dist/serializedSnapshotStorage.js.map +1 -1
  119. package/dist/summarizer.d.ts +2 -3
  120. package/dist/summarizer.d.ts.map +1 -1
  121. package/dist/summarizer.js +39 -18
  122. package/dist/summarizer.js.map +1 -1
  123. package/dist/summarizerClientElection.d.ts +1 -2
  124. package/dist/summarizerClientElection.d.ts.map +1 -1
  125. package/dist/summarizerClientElection.js +3 -30
  126. package/dist/summarizerClientElection.js.map +1 -1
  127. package/dist/summarizerHandle.d.ts.map +1 -1
  128. package/dist/summarizerHandle.js.map +1 -1
  129. package/dist/summarizerHeuristics.d.ts.map +1 -1
  130. package/dist/summarizerHeuristics.js +6 -9
  131. package/dist/summarizerHeuristics.js.map +1 -1
  132. package/dist/summarizerTypes.d.ts +22 -25
  133. package/dist/summarizerTypes.d.ts.map +1 -1
  134. package/dist/summarizerTypes.js.map +1 -1
  135. package/dist/summaryCollection.d.ts.map +1 -1
  136. package/dist/summaryCollection.js +18 -8
  137. package/dist/summaryCollection.js.map +1 -1
  138. package/dist/summaryFormat.d.ts.map +1 -1
  139. package/dist/summaryFormat.js +18 -11
  140. package/dist/summaryFormat.js.map +1 -1
  141. package/dist/summaryGenerator.d.ts.map +1 -1
  142. package/dist/summaryGenerator.js +32 -14
  143. package/dist/summaryGenerator.js.map +1 -1
  144. package/dist/summaryManager.d.ts.map +1 -1
  145. package/dist/summaryManager.js +21 -9
  146. package/dist/summaryManager.js.map +1 -1
  147. package/dist/throttler.d.ts +2 -2
  148. package/dist/throttler.d.ts.map +1 -1
  149. package/dist/throttler.js +4 -4
  150. package/dist/throttler.js.map +1 -1
  151. package/garbageCollection.md +15 -2
  152. package/lib/batchTracker.d.ts +1 -2
  153. package/lib/batchTracker.d.ts.map +1 -1
  154. package/lib/batchTracker.js +2 -1
  155. package/lib/batchTracker.js.map +1 -1
  156. package/lib/blobManager.d.ts +62 -28
  157. package/lib/blobManager.d.ts.map +1 -1
  158. package/lib/blobManager.js +259 -105
  159. package/lib/blobManager.js.map +1 -1
  160. package/lib/connectionTelemetry.d.ts.map +1 -1
  161. package/lib/connectionTelemetry.js +11 -9
  162. package/lib/connectionTelemetry.js.map +1 -1
  163. package/lib/containerHandleContext.d.ts.map +1 -1
  164. package/lib/containerHandleContext.js +3 -1
  165. package/lib/containerHandleContext.js.map +1 -1
  166. package/lib/containerRuntime.d.ts +110 -78
  167. package/lib/containerRuntime.d.ts.map +1 -1
  168. package/lib/containerRuntime.js +340 -334
  169. package/lib/containerRuntime.js.map +1 -1
  170. package/lib/dataStore.d.ts.map +1 -1
  171. package/lib/dataStore.js +11 -9
  172. package/lib/dataStore.js.map +1 -1
  173. package/lib/dataStoreContext.d.ts +2 -1
  174. package/lib/dataStoreContext.d.ts.map +1 -1
  175. package/lib/dataStoreContext.js +41 -24
  176. package/lib/dataStoreContext.js.map +1 -1
  177. package/lib/dataStoreContexts.d.ts.map +1 -1
  178. package/lib/dataStoreContexts.js +7 -3
  179. package/lib/dataStoreContexts.js.map +1 -1
  180. package/lib/dataStoreRegistry.d.ts.map +1 -1
  181. package/lib/dataStoreRegistry.js +3 -1
  182. package/lib/dataStoreRegistry.js.map +1 -1
  183. package/lib/dataStores.d.ts +12 -9
  184. package/lib/dataStores.d.ts.map +1 -1
  185. package/lib/dataStores.js +75 -52
  186. package/lib/dataStores.js.map +1 -1
  187. package/lib/deltaScheduler.d.ts.map +1 -1
  188. package/lib/deltaScheduler.js +9 -4
  189. package/lib/deltaScheduler.js.map +1 -1
  190. package/lib/garbageCollection.d.ts +57 -42
  191. package/lib/garbageCollection.d.ts.map +1 -1
  192. package/lib/garbageCollection.js +364 -232
  193. package/lib/garbageCollection.js.map +1 -1
  194. package/lib/garbageCollectionConstants.d.ts +23 -0
  195. package/lib/garbageCollectionConstants.d.ts.map +1 -0
  196. package/lib/garbageCollectionConstants.js +33 -0
  197. package/lib/garbageCollectionConstants.js.map +1 -0
  198. package/lib/garbageCollectionHelpers.d.ts +15 -0
  199. package/lib/garbageCollectionHelpers.d.ts.map +1 -0
  200. package/lib/garbageCollectionHelpers.js +23 -0
  201. package/lib/garbageCollectionHelpers.js.map +1 -0
  202. package/lib/gcSweepReadyUsageDetection.d.ts +5 -5
  203. package/lib/gcSweepReadyUsageDetection.d.ts.map +1 -1
  204. package/lib/gcSweepReadyUsageDetection.js +15 -11
  205. package/lib/gcSweepReadyUsageDetection.js.map +1 -1
  206. package/lib/index.d.ts +4 -3
  207. package/lib/index.d.ts.map +1 -1
  208. package/lib/index.js +3 -3
  209. package/lib/index.js.map +1 -1
  210. package/lib/opLifecycle/batchManager.d.ts +42 -0
  211. package/lib/opLifecycle/batchManager.d.ts.map +1 -0
  212. package/lib/opLifecycle/batchManager.js +120 -0
  213. package/lib/opLifecycle/batchManager.js.map +1 -0
  214. package/lib/opLifecycle/definitions.d.ts +64 -0
  215. package/lib/opLifecycle/definitions.d.ts.map +1 -0
  216. package/lib/opLifecycle/definitions.js +6 -0
  217. package/lib/opLifecycle/definitions.js.map +1 -0
  218. package/lib/opLifecycle/index.d.ts +12 -0
  219. package/lib/opLifecycle/index.d.ts.map +1 -0
  220. package/lib/opLifecycle/index.js +11 -0
  221. package/lib/opLifecycle/index.js.map +1 -0
  222. package/lib/{opCompressor.d.ts → opLifecycle/opCompressor.d.ts} +3 -3
  223. package/lib/opLifecycle/opCompressor.d.ts.map +1 -0
  224. package/lib/opLifecycle/opCompressor.js +63 -0
  225. package/lib/opLifecycle/opCompressor.js.map +1 -0
  226. package/lib/{opDecompressor.d.ts → opLifecycle/opDecompressor.d.ts} +2 -1
  227. package/lib/opLifecycle/opDecompressor.d.ts.map +1 -0
  228. package/lib/{opDecompressor.js → opLifecycle/opDecompressor.js} +37 -21
  229. package/lib/opLifecycle/opDecompressor.js.map +1 -0
  230. package/lib/opLifecycle/opSplitter.d.ts +49 -0
  231. package/lib/opLifecycle/opSplitter.d.ts.map +1 -0
  232. package/lib/opLifecycle/opSplitter.js +168 -0
  233. package/lib/opLifecycle/opSplitter.js.map +1 -0
  234. package/lib/opLifecycle/outbox.d.ts +52 -0
  235. package/lib/opLifecycle/outbox.d.ts.map +1 -0
  236. package/lib/opLifecycle/outbox.js +160 -0
  237. package/lib/opLifecycle/outbox.js.map +1 -0
  238. package/lib/opLifecycle/remoteMessageProcessor.d.ts +26 -0
  239. package/lib/opLifecycle/remoteMessageProcessor.d.ts.map +1 -0
  240. package/lib/opLifecycle/remoteMessageProcessor.js +91 -0
  241. package/lib/opLifecycle/remoteMessageProcessor.js.map +1 -0
  242. package/lib/opProperties.d.ts.map +1 -1
  243. package/lib/opProperties.js +1 -3
  244. package/lib/opProperties.js.map +1 -1
  245. package/lib/orderedClientElection.d.ts.map +1 -1
  246. package/lib/orderedClientElection.js +10 -4
  247. package/lib/orderedClientElection.js.map +1 -1
  248. package/lib/packageVersion.d.ts +1 -1
  249. package/lib/packageVersion.js +1 -1
  250. package/lib/packageVersion.js.map +1 -1
  251. package/lib/pendingStateManager.d.ts +4 -13
  252. package/lib/pendingStateManager.d.ts.map +1 -1
  253. package/lib/pendingStateManager.js +134 -161
  254. package/lib/pendingStateManager.js.map +1 -1
  255. package/lib/runWhileConnectedCoordinator.d.ts.map +1 -1
  256. package/lib/runWhileConnectedCoordinator.js.map +1 -1
  257. package/lib/runningSummarizer.d.ts.map +1 -1
  258. package/lib/runningSummarizer.js +35 -23
  259. package/lib/runningSummarizer.js.map +1 -1
  260. package/lib/scheduleManager.d.ts +0 -1
  261. package/lib/scheduleManager.d.ts.map +1 -1
  262. package/lib/scheduleManager.js +11 -21
  263. package/lib/scheduleManager.js.map +1 -1
  264. package/lib/serializedSnapshotStorage.d.ts.map +1 -1
  265. package/lib/serializedSnapshotStorage.js +3 -1
  266. package/lib/serializedSnapshotStorage.js.map +1 -1
  267. package/lib/summarizer.d.ts +2 -3
  268. package/lib/summarizer.d.ts.map +1 -1
  269. package/lib/summarizer.js +39 -18
  270. package/lib/summarizer.js.map +1 -1
  271. package/lib/summarizerClientElection.d.ts +1 -2
  272. package/lib/summarizerClientElection.d.ts.map +1 -1
  273. package/lib/summarizerClientElection.js +3 -30
  274. package/lib/summarizerClientElection.js.map +1 -1
  275. package/lib/summarizerHandle.d.ts.map +1 -1
  276. package/lib/summarizerHandle.js.map +1 -1
  277. package/lib/summarizerHeuristics.d.ts.map +1 -1
  278. package/lib/summarizerHeuristics.js +6 -9
  279. package/lib/summarizerHeuristics.js.map +1 -1
  280. package/lib/summarizerTypes.d.ts +22 -25
  281. package/lib/summarizerTypes.d.ts.map +1 -1
  282. package/lib/summarizerTypes.js.map +1 -1
  283. package/lib/summaryCollection.d.ts.map +1 -1
  284. package/lib/summaryCollection.js +18 -8
  285. package/lib/summaryCollection.js.map +1 -1
  286. package/lib/summaryFormat.d.ts.map +1 -1
  287. package/lib/summaryFormat.js +20 -13
  288. package/lib/summaryFormat.js.map +1 -1
  289. package/lib/summaryGenerator.d.ts.map +1 -1
  290. package/lib/summaryGenerator.js +32 -14
  291. package/lib/summaryGenerator.js.map +1 -1
  292. package/lib/summaryManager.d.ts.map +1 -1
  293. package/lib/summaryManager.js +21 -9
  294. package/lib/summaryManager.js.map +1 -1
  295. package/lib/throttler.d.ts +2 -2
  296. package/lib/throttler.d.ts.map +1 -1
  297. package/lib/throttler.js +4 -4
  298. package/lib/throttler.js.map +1 -1
  299. package/package.json +28 -38
  300. package/prettier.config.cjs +1 -1
  301. package/src/batchTracker.ts +55 -50
  302. package/src/blobManager.ts +802 -541
  303. package/src/connectionTelemetry.ts +280 -249
  304. package/src/containerHandleContext.ts +27 -29
  305. package/src/containerRuntime.ts +3125 -2982
  306. package/src/dataStore.ts +172 -159
  307. package/src/dataStoreContext.ts +1049 -992
  308. package/src/dataStoreContexts.ts +178 -161
  309. package/src/dataStoreRegistry.ts +25 -20
  310. package/src/dataStores.ts +785 -711
  311. package/src/deltaScheduler.ts +158 -150
  312. package/src/garbageCollection.ts +1797 -1558
  313. package/src/garbageCollectionConstants.ts +38 -0
  314. package/src/garbageCollectionHelpers.ts +37 -0
  315. package/src/gcSweepReadyUsageDetection.ts +90 -84
  316. package/src/index.ts +68 -69
  317. package/src/opLifecycle/batchManager.ts +167 -0
  318. package/src/opLifecycle/definitions.ts +70 -0
  319. package/src/opLifecycle/index.ts +18 -0
  320. package/src/opLifecycle/opCompressor.ts +82 -0
  321. package/src/opLifecycle/opDecompressor.ts +124 -0
  322. package/src/opLifecycle/opSplitter.ts +238 -0
  323. package/src/opLifecycle/outbox.ts +228 -0
  324. package/src/opLifecycle/remoteMessageProcessor.ts +106 -0
  325. package/src/opProperties.ts +11 -9
  326. package/src/orderedClientElection.ts +489 -457
  327. package/src/packageVersion.ts +1 -1
  328. package/src/pendingStateManager.ts +379 -381
  329. package/src/runWhileConnectedCoordinator.ts +78 -71
  330. package/src/runningSummarizer.ts +619 -582
  331. package/src/scheduleManager.ts +299 -280
  332. package/src/serializedSnapshotStorage.ts +116 -111
  333. package/src/summarizer.ts +417 -381
  334. package/src/summarizerClientElection.ts +107 -129
  335. package/src/summarizerHandle.ts +11 -9
  336. package/src/summarizerHeuristics.ts +183 -186
  337. package/src/summarizerTypes.ts +344 -333
  338. package/src/summaryCollection.ts +378 -349
  339. package/src/summaryFormat.ts +146 -127
  340. package/src/summaryGenerator.ts +464 -406
  341. package/src/summaryManager.ts +377 -348
  342. package/src/throttler.ts +131 -122
  343. package/tsconfig.esnext.json +6 -6
  344. package/tsconfig.json +9 -13
  345. package/dist/batchManager.d.ts +0 -42
  346. package/dist/batchManager.d.ts.map +0 -1
  347. package/dist/batchManager.js +0 -83
  348. package/dist/batchManager.js.map +0 -1
  349. package/dist/opCompressor.d.ts.map +0 -1
  350. package/dist/opCompressor.js +0 -50
  351. package/dist/opCompressor.js.map +0 -1
  352. package/dist/opDecompressor.d.ts.map +0 -1
  353. package/dist/opDecompressor.js.map +0 -1
  354. package/lib/batchManager.d.ts +0 -42
  355. package/lib/batchManager.d.ts.map +0 -1
  356. package/lib/batchManager.js +0 -79
  357. package/lib/batchManager.js.map +0 -1
  358. package/lib/opCompressor.d.ts.map +0 -1
  359. package/lib/opCompressor.js +0 -46
  360. package/lib/opCompressor.js.map +0 -1
  361. package/lib/opDecompressor.d.ts.map +0 -1
  362. package/lib/opDecompressor.js.map +0 -1
  363. package/src/batchManager.ts +0 -108
  364. package/src/opCompressor.ts +0 -59
  365. package/src/opDecompressor.ts +0 -82
@@ -0,0 +1,70 @@
1
+ /*!
2
+ * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
+ * Licensed under the MIT License.
4
+ */
5
+
6
+ import { IBatchMessage } from "@fluidframework/container-definitions";
7
+ import { ISequencedDocumentMessage, MessageType } from "@fluidframework/protocol-definitions";
8
+ import { CompressionAlgorithms, ContainerMessageType, ContainerRuntimeMessage } from "..";
9
+
10
+ /**
11
+ * Batch message type used internally by the runtime
12
+ */
13
+ export type BatchMessage = IBatchMessage & {
14
+ localOpMetadata: unknown;
15
+ deserializedContent: ContainerRuntimeMessage;
16
+ referenceSequenceNumber: number;
17
+ compression?: CompressionAlgorithms;
18
+ };
19
+
20
+ /**
21
+ * Batch interface used internally by the runtime.
22
+ */
23
+ export interface IBatch {
24
+ /**
25
+ * Sum of the in-memory content sizes of all messages in the batch.
26
+ * If the batch is compressed, this number reflects the post-compression size.
27
+ */
28
+ readonly contentSizeInBytes: number;
29
+ /**
30
+ * All the messages in the batch
31
+ */
32
+ readonly content: BatchMessage[];
33
+ }
34
+
35
+ export interface IBatchCheckpoint {
36
+ rollback: (action: (message: BatchMessage) => void) => void;
37
+ }
38
+
39
+ export interface IChunkedOp {
40
+ chunkId: number;
41
+ totalChunks: number;
42
+ contents: string;
43
+ originalType: MessageType | ContainerMessageType;
44
+ originalMetadata?: Record<string, unknown>;
45
+ originalCompression?: string;
46
+ }
47
+
48
+ /**
49
+ * The state of remote message processing:
50
+ * `Processed` - the message can be considered processed
51
+ * `Skipped` - the message was ignored by the processor
52
+ * `Accepted` - the message was processed partially. Eventually, a message
53
+ * will make the processor return `Processed`.
54
+ */
55
+ export type ProcessingState = "Processed" | "Skipped" | "Accepted";
56
+
57
+ /**
58
+ * Return type for functions which process remote messages
59
+ */
60
+ export interface IMessageProcessingResult {
61
+ /**
62
+ * A shallow copy of the input message if processing happened, or
63
+ * the original message otherwise
64
+ */
65
+ readonly message: ISequencedDocumentMessage;
66
+ /**
67
+ * Processing result of the input message.
68
+ */
69
+ readonly state: ProcessingState;
70
+ }
@@ -0,0 +1,18 @@
1
+ /*!
2
+ * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
+ * Licensed under the MIT License.
4
+ */
5
+
6
+ export { BatchManager } from "./batchManager";
7
+ export {
8
+ BatchMessage,
9
+ IBatch,
10
+ IBatchCheckpoint,
11
+ IChunkedOp,
12
+ IMessageProcessingResult,
13
+ } from "./definitions";
14
+ export { Outbox } from "./outbox";
15
+ export { OpCompressor } from "./opCompressor";
16
+ export { OpDecompressor } from "./opDecompressor";
17
+ export { OpSplitter, splitOp } from "./opSplitter";
18
+ export { RemoteMessageProcessor, unpackRuntimeMessage } from "./remoteMessageProcessor";
@@ -0,0 +1,82 @@
1
+ /*!
2
+ * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
+ * Licensed under the MIT License.
4
+ */
5
+
6
+ import { ITelemetryLogger } from "@fluidframework/common-definitions";
7
+ import { IsoBuffer } from "@fluidframework/common-utils";
8
+ import { UsageError } from "@fluidframework/container-utils";
9
+ import { ChildLogger } from "@fluidframework/telemetry-utils";
10
+ import { compress } from "lz4js";
11
+ import { CompressionAlgorithms } from "../containerRuntime";
12
+ import { IBatch, BatchMessage } from "./definitions";
13
+
14
+ /**
15
+ * Compresses batches of ops. It generates a single compressed op that contains
16
+ * the contents of each op in the batch. It then submits empty ops for each original
17
+ * op to reserve sequence numbers.
18
+ */
19
+ export class OpCompressor {
20
+ private readonly logger;
21
+
22
+ constructor(logger: ITelemetryLogger) {
23
+ this.logger = ChildLogger.create(logger, "OpCompressor");
24
+ }
25
+
26
+ public compressBatch(batch: IBatch): IBatch {
27
+ const compressionStart = Date.now();
28
+ const contentsAsBuffer = new TextEncoder().encode(this.serializeBatch(batch));
29
+ const compressedContents = compress(contentsAsBuffer);
30
+ const compressedContent = IsoBuffer.from(compressedContents).toString("base64");
31
+ const duration = Date.now() - compressionStart;
32
+
33
+ if (batch.contentSizeInBytes > 200000) {
34
+ this.logger.sendPerformanceEvent({
35
+ eventName: "CompressedBatch",
36
+ duration,
37
+ sizeBeforeCompression: batch.contentSizeInBytes,
38
+ sizeAfterCompression: compressedContent.length,
39
+ });
40
+ }
41
+
42
+ const messages: BatchMessage[] = [];
43
+ messages.push({
44
+ ...batch.content[0],
45
+ contents: JSON.stringify({ packedContents: compressedContent }),
46
+ metadata: batch.content[0].metadata,
47
+ compression: CompressionAlgorithms.lz4,
48
+ });
49
+
50
+ for (const message of batch.content.slice(1)) {
51
+ messages.push({ ...message, contents: undefined });
52
+ }
53
+
54
+ return {
55
+ contentSizeInBytes: compressedContent.length,
56
+ content: messages,
57
+ };
58
+ }
59
+
60
+ private serializeBatch(batch: IBatch): string {
61
+ try {
62
+ return JSON.stringify(batch.content.map((message) => message.deserializedContent));
63
+ } catch (e: any) {
64
+ if (e.message === "Invalid string length") {
65
+ // This is how JSON.stringify signals that
66
+ // the content size exceeds its capacity
67
+ const error = new UsageError("Payload too large");
68
+ this.logger.sendErrorEvent(
69
+ {
70
+ eventName: "BatchTooLarge",
71
+ size: batch.contentSizeInBytes,
72
+ length: batch.content.length,
73
+ },
74
+ error,
75
+ );
76
+ throw error;
77
+ }
78
+
79
+ throw e;
80
+ }
81
+ }
82
+ }
@@ -0,0 +1,124 @@
1
+ /*!
2
+ * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
+ * Licensed under the MIT License.
4
+ */
5
+
6
+ import { decompress } from "lz4js";
7
+ import { ISequencedDocumentMessage } from "@fluidframework/protocol-definitions";
8
+ import { assert, IsoBuffer, Uint8ArrayToString } from "@fluidframework/common-utils";
9
+ import { CompressionAlgorithms } from "../containerRuntime";
10
+ import { IMessageProcessingResult } from "./definitions";
11
+
12
+ /**
13
+ * State machine that "unrolls" contents of compressed batches of ops after decompressing them.
14
+ * This class relies on some implicit contracts defined below:
15
+ * 1. A compressed batch will have its first message with batch metadata set to true and compressed set to true
16
+ * 2. Messages in the middle of a compressed batch will have neither batch metadata nor the compression property set
17
+ * 3. The final message of a batch will have batch metadata set to false
18
+ * 4. An individually compressed op will have undefined batch metadata and compression set to true
19
+ */
20
+ export class OpDecompressor {
21
+ private activeBatch = false;
22
+ private rootMessageContents: any | undefined;
23
+ private processedCount = 0;
24
+
25
+ public processMessage(message: ISequencedDocumentMessage): IMessageProcessingResult {
26
+ assert(
27
+ message.compression === undefined || message.compression === CompressionAlgorithms.lz4,
28
+ 0x511 /* Only lz4 compression is supported */,
29
+ );
30
+
31
+ if (message.metadata?.batch === true && message.compression === CompressionAlgorithms.lz4) {
32
+ // Beginning of a compressed batch
33
+ assert(this.activeBatch === false, 0x4b8 /* shouldn't have multiple active batches */);
34
+ if (message.compression) {
35
+ // lz4 is the only supported compression algorithm for now
36
+ assert(
37
+ message.compression === CompressionAlgorithms.lz4,
38
+ 0x4b9 /* lz4 is currently the only supported compression algorithm */,
39
+ );
40
+ }
41
+
42
+ this.activeBatch = true;
43
+
44
+ const contents = IsoBuffer.from(message.contents.packedContents, "base64");
45
+ const decompressedMessage = decompress(contents);
46
+ const intoString = Uint8ArrayToString(decompressedMessage);
47
+ const asObj = JSON.parse(intoString);
48
+ this.rootMessageContents = asObj;
49
+
50
+ return {
51
+ message: newMessage(message, this.rootMessageContents[this.processedCount++]),
52
+ state: "Accepted",
53
+ };
54
+ }
55
+
56
+ if (
57
+ this.rootMessageContents !== undefined &&
58
+ message.metadata?.batch === undefined &&
59
+ this.activeBatch
60
+ ) {
61
+ assert(message.contents === undefined, 0x512 /* Expecting empty message */);
62
+
63
+ // Continuation of compressed batch
64
+ return {
65
+ message: newMessage(message, this.rootMessageContents[this.processedCount++]),
66
+ state: "Accepted",
67
+ };
68
+ }
69
+
70
+ if (this.rootMessageContents !== undefined && message.metadata?.batch === false) {
71
+ // End of compressed batch
72
+ const returnMessage = newMessage(
73
+ message,
74
+ this.rootMessageContents[this.processedCount++],
75
+ );
76
+
77
+ this.activeBatch = false;
78
+ this.rootMessageContents = undefined;
79
+ this.processedCount = 0;
80
+
81
+ return {
82
+ message: returnMessage,
83
+ state: "Processed",
84
+ };
85
+ }
86
+
87
+ if (
88
+ message.metadata?.batch === undefined &&
89
+ message.compression === CompressionAlgorithms.lz4
90
+ ) {
91
+ // Single compressed message
92
+ assert(
93
+ this.activeBatch === false,
94
+ 0x4ba /* shouldn't receive compressed message in middle of a batch */,
95
+ );
96
+
97
+ const contents = IsoBuffer.from(message.contents.packedContents, "base64");
98
+ const decompressedMessage = decompress(contents);
99
+ const intoString = new TextDecoder().decode(decompressedMessage);
100
+ const asObj = JSON.parse(intoString);
101
+
102
+ return {
103
+ message: newMessage(message, asObj[0]),
104
+ state: "Processed",
105
+ };
106
+ }
107
+
108
+ return {
109
+ message,
110
+ state: "Skipped",
111
+ };
112
+ }
113
+ }
114
+
115
+ // We should not be mutating the input message nor its metadata
116
+ const newMessage = (
117
+ originalMessage: ISequencedDocumentMessage,
118
+ contents: any,
119
+ ): ISequencedDocumentMessage => ({
120
+ ...originalMessage,
121
+ contents,
122
+ compression: undefined,
123
+ metadata: { ...originalMessage.metadata },
124
+ });
@@ -0,0 +1,238 @@
1
+ /*!
2
+ * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
+ * Licensed under the MIT License.
4
+ */
5
+
6
+ import { ITelemetryLogger } from "@fluidframework/common-definitions";
7
+ import { assert } from "@fluidframework/common-utils";
8
+ import { IBatchMessage } from "@fluidframework/container-definitions";
9
+ import {
10
+ DataCorruptionError,
11
+ extractSafePropertiesFromMessage,
12
+ } from "@fluidframework/container-utils";
13
+ import { ISequencedDocumentMessage } from "@fluidframework/protocol-definitions";
14
+ import { ChildLogger } from "@fluidframework/telemetry-utils";
15
+ import { ContainerMessageType, ContainerRuntimeMessage } from "../containerRuntime";
16
+ import { BatchMessage, IBatch, IChunkedOp, IMessageProcessingResult } from "./definitions";
17
+
18
+ /**
19
+ * Responsible for creating and reconstructing chunked messages.
20
+ */
21
+ export class OpSplitter {
22
+ // Local copy of incomplete received chunks.
23
+ private readonly chunkMap: Map<string, string[]>;
24
+ private readonly logger;
25
+
26
+ constructor(
27
+ chunks: [string, string[]][],
28
+ private readonly submitBatchFn: ((batch: IBatchMessage[]) => number) | undefined,
29
+ private readonly chunkSizeInBytes: number,
30
+ private readonly maxBatchSizeInBytes: number,
31
+ logger: ITelemetryLogger,
32
+ ) {
33
+ this.chunkMap = new Map<string, string[]>(chunks);
34
+ this.logger = ChildLogger.create(logger, "OpSplitter");
35
+ }
36
+
37
+ public get isBatchChunkingEnabled(): boolean {
38
+ return this.chunkSizeInBytes < Number.POSITIVE_INFINITY && this.submitBatchFn !== undefined;
39
+ }
40
+
41
+ public get chunks(): ReadonlyMap<string, string[]> {
42
+ return this.chunkMap;
43
+ }
44
+
45
+ public processRemoteMessage(message: ISequencedDocumentMessage): IMessageProcessingResult {
46
+ if (message.type !== ContainerMessageType.ChunkedOp) {
47
+ return {
48
+ message,
49
+ state: "Skipped",
50
+ };
51
+ }
52
+
53
+ const clientId = message.clientId;
54
+ const chunkedContent = message.contents as IChunkedOp;
55
+ this.addChunk(clientId, chunkedContent, message);
56
+
57
+ if (chunkedContent.chunkId < chunkedContent.totalChunks) {
58
+ // We are processing the op in chunks but haven't reached
59
+ // the last chunk yet in order to reconstruct the original op
60
+ return {
61
+ message,
62
+ state: "Accepted",
63
+ };
64
+ }
65
+
66
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
67
+ const serializedContent = this.chunkMap.get(clientId)!.join("");
68
+ this.clearPartialChunks(clientId);
69
+
70
+ const newMessage = { ...message };
71
+ newMessage.contents = serializedContent === "" ? undefined : JSON.parse(serializedContent);
72
+ newMessage.type = chunkedContent.originalType;
73
+ newMessage.metadata = chunkedContent.originalMetadata;
74
+ newMessage.compression = chunkedContent.originalCompression;
75
+ return {
76
+ message: newMessage,
77
+ state: "Processed",
78
+ };
79
+ }
80
+
81
+ public clearPartialChunks(clientId: string) {
82
+ if (this.chunkMap.has(clientId)) {
83
+ this.chunkMap.delete(clientId);
84
+ }
85
+ }
86
+
87
+ private addChunk(
88
+ clientId: string,
89
+ chunkedContent: IChunkedOp,
90
+ originalMessage: ISequencedDocumentMessage,
91
+ ) {
92
+ let map = this.chunkMap.get(clientId);
93
+ if (map === undefined) {
94
+ map = [];
95
+ this.chunkMap.set(clientId, map);
96
+ }
97
+
98
+ if (chunkedContent.chunkId !== map.length + 1) {
99
+ // We are expecting the chunks to be processed sequentially, in the same order as they are sent.
100
+ // Therefore, the chunkId of the incoming op needs to match the length of the array (1-based indexing)
101
+ // holding the existing chunks for that particular clientId.
102
+ throw new DataCorruptionError("Chunk Id mismatch", {
103
+ ...extractSafePropertiesFromMessage(originalMessage),
104
+ chunkMapLength: map.length,
105
+ chunkId: chunkedContent.chunkId,
106
+ totalChunks: chunkedContent.totalChunks,
107
+ });
108
+ }
109
+
110
+ map.push(chunkedContent.contents);
111
+ }
112
+
113
+ /**
114
+ * Splits the first op of a compressed batch in chunks, sends the chunks separately and
115
+ * returns a new batch composed of the last chunk and the rest of the ops in the original batch.
116
+ *
117
+ * A compressed batch is formed by one large op at the first position, followed by a series of placeholder ops
118
+ * which are used in order to reserve the sequence numbers for when the first op gets unrolled into the original
119
+ * uncompressed ops at ingestion in the runtime.
120
+ *
121
+ * If the first op is too large, it can be chunked (split into smaller op) which can be sent individually over the wire
122
+ * and accumulate at ingestion, until the last op in the chunk is processed, when the original op is unrolled.
123
+ *
124
+ * This method will send the first N - 1 chunks separately and use the last chunk as the first message in the result batch
125
+ * and then appends the original placeholder ops. This will ensure that the batch semantics of the original (non-compressed) batch
126
+ * are preserved, as the original chunked op will be unrolled by the runtime when the first message in the batch is processed
127
+ * (as it is the last chunk).
128
+ *
129
+ * To illustrate, if the input is `[largeOp, emptyOp, emptyOp]`, `largeOp` will be split into `[chunk1, chunk2, chunk3, chunk4]`.
130
+ * `chunk1`, `chunk2` and `chunk3` will be sent individually and `[chunk4, emptyOp, emptyOp]` will be returned.
131
+ *
132
+ * @param batch - the compressed batch which needs to be processed
133
+ * @returns A new adjusted batch which can be sent over the wire
134
+ */
135
+ public splitCompressedBatch(batch: IBatch): IBatch {
136
+ assert(this.isBatchChunkingEnabled, 0x513 /* Chunking needs to be enabled */);
137
+ assert(
138
+ batch.contentSizeInBytes > 0 && batch.content.length > 0,
139
+ 0x514 /* Batch needs to be non-empty */,
140
+ );
141
+ assert(this.chunkSizeInBytes !== 0, 0x515 /* Chunk size needs to be non-zero */);
142
+ assert(
143
+ this.chunkSizeInBytes < this.maxBatchSizeInBytes,
144
+ 0x516 /* Chunk size needs to be smaller than the max batch size */,
145
+ );
146
+
147
+ const firstMessage = batch.content[0]; // we expect this to be the large compressed op, which needs to be split
148
+ assert(
149
+ firstMessage.metadata?.compressed === true || firstMessage.compression !== undefined,
150
+ 0x517 /* Batch needs to be compressed */,
151
+ );
152
+ assert(
153
+ (firstMessage.contents?.length ?? 0) >= this.chunkSizeInBytes,
154
+ 0x518 /* First message in the batch needs to be chunkable */,
155
+ );
156
+
157
+ const restOfMessages = batch.content.slice(1); // we expect these to be empty ops, created to reserve sequence numbers
158
+ const chunks = splitOp(firstMessage, this.chunkSizeInBytes);
159
+
160
+ assert(this.submitBatchFn !== undefined, 0x519 /* We don't support old loaders */);
161
+ // Send the first N-1 chunks immediately
162
+ for (const chunk of chunks.slice(0, -1)) {
163
+ this.submitBatchFn([chunkToBatchMessage(chunk, firstMessage.referenceSequenceNumber)]);
164
+ }
165
+
166
+ // The last chunk will be part of the new batch and needs to
167
+ // preserve the batch metadata of the original batch
168
+ const lastChunk = chunkToBatchMessage(
169
+ chunks[chunks.length - 1],
170
+ firstMessage.referenceSequenceNumber,
171
+ { batch: firstMessage.metadata?.batch },
172
+ );
173
+
174
+ this.logger.sendPerformanceEvent({
175
+ eventName: "Chunked compressed batch",
176
+ length: batch.content.length,
177
+ sizeInBytes: batch.contentSizeInBytes,
178
+ chunks: chunks.length,
179
+ chunkSizeInBytes: this.chunkSizeInBytes,
180
+ });
181
+
182
+ return {
183
+ content: [lastChunk, ...restOfMessages],
184
+ contentSizeInBytes: lastChunk.contents?.length ?? 0,
185
+ };
186
+ }
187
+ }
188
+
189
+ const chunkToBatchMessage = (
190
+ chunk: IChunkedOp,
191
+ referenceSequenceNumber: number,
192
+ metadata: Record<string, unknown> | undefined = undefined,
193
+ ): BatchMessage => {
194
+ const payload: ContainerRuntimeMessage = {
195
+ type: ContainerMessageType.ChunkedOp,
196
+ contents: chunk,
197
+ };
198
+ return {
199
+ contents: JSON.stringify(payload),
200
+ deserializedContent: payload,
201
+ metadata,
202
+ localOpMetadata: undefined,
203
+ referenceSequenceNumber,
204
+ };
205
+ };
206
+
207
+ export const splitOp = (op: BatchMessage, chunkSizeInBytes: number): IChunkedOp[] => {
208
+ const chunks: IChunkedOp[] = [];
209
+ assert(
210
+ op.contents !== undefined && op.contents !== null,
211
+ 0x51a /* We should have something to chunk */,
212
+ );
213
+
214
+ const contentLength = op.contents.length;
215
+ const chunkN = Math.floor((contentLength - 1) / chunkSizeInBytes) + 1;
216
+ let offset = 0;
217
+ for (let i = 1; i <= chunkN; i++) {
218
+ const chunk: IChunkedOp = {
219
+ chunkId: i,
220
+ contents: op.contents.substr(offset, chunkSizeInBytes),
221
+ originalType: op.deserializedContent.type,
222
+ totalChunks: chunkN,
223
+ };
224
+
225
+ if (i === chunkN) {
226
+ // We don't need to port these to all the chunks,
227
+ // as we rebuild the original op when we process the
228
+ // last chunk, therefore it is the only one that needs it.
229
+ chunk.originalMetadata = op.metadata;
230
+ chunk.originalCompression = op.compression;
231
+ }
232
+
233
+ chunks.push(chunk);
234
+ offset += chunkSizeInBytes;
235
+ }
236
+
237
+ return chunks;
238
+ };