@fluidframework/container-runtime 2.0.0-dev.2.3.0.115467 → 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 (340) 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 +53 -34
  9. package/dist/blobManager.d.ts.map +1 -1
  10. package/dist/blobManager.js +236 -124
  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 +95 -46
  19. package/dist/containerRuntime.d.ts.map +1 -1
  20. package/dist/containerRuntime.js +288 -135
  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 +38 -21
  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 +68 -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 +50 -26
  43. package/dist/garbageCollection.d.ts.map +1 -1
  44. package/dist/garbageCollection.js +348 -196
  45. package/dist/garbageCollection.js.map +1 -1
  46. package/dist/garbageCollectionConstants.d.ts +7 -3
  47. package/dist/garbageCollectionConstants.d.ts.map +1 -1
  48. package/dist/garbageCollectionConstants.js +10 -8
  49. package/dist/garbageCollectionConstants.js.map +1 -1
  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 +14 -10
  57. package/dist/gcSweepReadyUsageDetection.js.map +1 -1
  58. package/dist/index.d.ts +3 -4
  59. package/dist/index.d.ts.map +1 -1
  60. package/dist/index.js +3 -5
  61. package/dist/index.js.map +1 -1
  62. package/dist/opLifecycle/batchManager.d.ts +13 -1
  63. package/dist/opLifecycle/batchManager.d.ts.map +1 -1
  64. package/dist/opLifecycle/batchManager.js +48 -7
  65. package/dist/opLifecycle/batchManager.js.map +1 -1
  66. package/dist/opLifecycle/definitions.d.ts +25 -1
  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 +2 -2
  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 +1 -1
  74. package/dist/opLifecycle/opCompressor.d.ts.map +1 -1
  75. package/dist/opLifecycle/opCompressor.js +24 -10
  76. package/dist/opLifecycle/opCompressor.js.map +1 -1
  77. package/dist/opLifecycle/opDecompressor.d.ts +2 -1
  78. package/dist/opLifecycle/opDecompressor.d.ts.map +1 -1
  79. package/dist/opLifecycle/opDecompressor.js +33 -17
  80. package/dist/opLifecycle/opDecompressor.js.map +1 -1
  81. package/dist/opLifecycle/opSplitter.d.ts +34 -2
  82. package/dist/opLifecycle/opSplitter.d.ts.map +1 -1
  83. package/dist/opLifecycle/opSplitter.js +117 -5
  84. package/dist/opLifecycle/opSplitter.js.map +1 -1
  85. package/dist/opLifecycle/outbox.d.ts +5 -0
  86. package/dist/opLifecycle/outbox.d.ts.map +1 -1
  87. package/dist/opLifecycle/outbox.js +38 -27
  88. package/dist/opLifecycle/outbox.js.map +1 -1
  89. package/dist/opLifecycle/remoteMessageProcessor.d.ts.map +1 -1
  90. package/dist/opLifecycle/remoteMessageProcessor.js +17 -2
  91. package/dist/opLifecycle/remoteMessageProcessor.js.map +1 -1
  92. package/dist/opProperties.d.ts.map +1 -1
  93. package/dist/opProperties.js +1 -3
  94. package/dist/opProperties.js.map +1 -1
  95. package/dist/orderedClientElection.d.ts.map +1 -1
  96. package/dist/orderedClientElection.js +10 -4
  97. package/dist/orderedClientElection.js.map +1 -1
  98. package/dist/packageVersion.d.ts +1 -1
  99. package/dist/packageVersion.js +1 -1
  100. package/dist/packageVersion.js.map +1 -1
  101. package/dist/pendingStateManager.d.ts +4 -13
  102. package/dist/pendingStateManager.d.ts.map +1 -1
  103. package/dist/pendingStateManager.js +134 -161
  104. package/dist/pendingStateManager.js.map +1 -1
  105. package/dist/runWhileConnectedCoordinator.d.ts.map +1 -1
  106. package/dist/runWhileConnectedCoordinator.js.map +1 -1
  107. package/dist/runningSummarizer.d.ts.map +1 -1
  108. package/dist/runningSummarizer.js +34 -22
  109. package/dist/runningSummarizer.js.map +1 -1
  110. package/dist/scheduleManager.d.ts +0 -1
  111. package/dist/scheduleManager.d.ts.map +1 -1
  112. package/dist/scheduleManager.js +11 -21
  113. package/dist/scheduleManager.js.map +1 -1
  114. package/dist/serializedSnapshotStorage.d.ts.map +1 -1
  115. package/dist/serializedSnapshotStorage.js +3 -1
  116. package/dist/serializedSnapshotStorage.js.map +1 -1
  117. package/dist/summarizer.d.ts +2 -3
  118. package/dist/summarizer.d.ts.map +1 -1
  119. package/dist/summarizer.js +39 -18
  120. package/dist/summarizer.js.map +1 -1
  121. package/dist/summarizerClientElection.d.ts +1 -2
  122. package/dist/summarizerClientElection.d.ts.map +1 -1
  123. package/dist/summarizerClientElection.js +3 -30
  124. package/dist/summarizerClientElection.js.map +1 -1
  125. package/dist/summarizerHandle.d.ts.map +1 -1
  126. package/dist/summarizerHandle.js.map +1 -1
  127. package/dist/summarizerHeuristics.d.ts.map +1 -1
  128. package/dist/summarizerHeuristics.js +6 -9
  129. package/dist/summarizerHeuristics.js.map +1 -1
  130. package/dist/summarizerTypes.d.ts +22 -25
  131. package/dist/summarizerTypes.d.ts.map +1 -1
  132. package/dist/summarizerTypes.js.map +1 -1
  133. package/dist/summaryCollection.d.ts.map +1 -1
  134. package/dist/summaryCollection.js +18 -8
  135. package/dist/summaryCollection.js.map +1 -1
  136. package/dist/summaryFormat.d.ts.map +1 -1
  137. package/dist/summaryFormat.js +18 -11
  138. package/dist/summaryFormat.js.map +1 -1
  139. package/dist/summaryGenerator.d.ts.map +1 -1
  140. package/dist/summaryGenerator.js +32 -14
  141. package/dist/summaryGenerator.js.map +1 -1
  142. package/dist/summaryManager.d.ts.map +1 -1
  143. package/dist/summaryManager.js +21 -9
  144. package/dist/summaryManager.js.map +1 -1
  145. package/dist/throttler.d.ts +2 -2
  146. package/dist/throttler.d.ts.map +1 -1
  147. package/dist/throttler.js +4 -4
  148. package/dist/throttler.js.map +1 -1
  149. package/garbageCollection.md +15 -2
  150. package/lib/batchTracker.d.ts +1 -2
  151. package/lib/batchTracker.d.ts.map +1 -1
  152. package/lib/batchTracker.js +2 -1
  153. package/lib/batchTracker.js.map +1 -1
  154. package/lib/blobManager.d.ts +53 -34
  155. package/lib/blobManager.d.ts.map +1 -1
  156. package/lib/blobManager.js +239 -127
  157. package/lib/blobManager.js.map +1 -1
  158. package/lib/connectionTelemetry.d.ts.map +1 -1
  159. package/lib/connectionTelemetry.js +11 -9
  160. package/lib/connectionTelemetry.js.map +1 -1
  161. package/lib/containerHandleContext.d.ts.map +1 -1
  162. package/lib/containerHandleContext.js +3 -1
  163. package/lib/containerHandleContext.js.map +1 -1
  164. package/lib/containerRuntime.d.ts +95 -46
  165. package/lib/containerRuntime.d.ts.map +1 -1
  166. package/lib/containerRuntime.js +291 -138
  167. package/lib/containerRuntime.js.map +1 -1
  168. package/lib/dataStore.d.ts.map +1 -1
  169. package/lib/dataStore.js +11 -9
  170. package/lib/dataStore.js.map +1 -1
  171. package/lib/dataStoreContext.d.ts +2 -1
  172. package/lib/dataStoreContext.d.ts.map +1 -1
  173. package/lib/dataStoreContext.js +40 -23
  174. package/lib/dataStoreContext.js.map +1 -1
  175. package/lib/dataStoreContexts.d.ts.map +1 -1
  176. package/lib/dataStoreContexts.js +7 -3
  177. package/lib/dataStoreContexts.js.map +1 -1
  178. package/lib/dataStoreRegistry.d.ts.map +1 -1
  179. package/lib/dataStoreRegistry.js +3 -1
  180. package/lib/dataStoreRegistry.js.map +1 -1
  181. package/lib/dataStores.d.ts +12 -9
  182. package/lib/dataStores.d.ts.map +1 -1
  183. package/lib/dataStores.js +74 -52
  184. package/lib/dataStores.js.map +1 -1
  185. package/lib/deltaScheduler.d.ts.map +1 -1
  186. package/lib/deltaScheduler.js +9 -4
  187. package/lib/deltaScheduler.js.map +1 -1
  188. package/lib/garbageCollection.d.ts +50 -26
  189. package/lib/garbageCollection.d.ts.map +1 -1
  190. package/lib/garbageCollection.js +347 -195
  191. package/lib/garbageCollection.js.map +1 -1
  192. package/lib/garbageCollectionConstants.d.ts +7 -3
  193. package/lib/garbageCollectionConstants.d.ts.map +1 -1
  194. package/lib/garbageCollectionConstants.js +9 -7
  195. package/lib/garbageCollectionConstants.js.map +1 -1
  196. package/lib/garbageCollectionHelpers.d.ts +15 -0
  197. package/lib/garbageCollectionHelpers.d.ts.map +1 -0
  198. package/lib/garbageCollectionHelpers.js +23 -0
  199. package/lib/garbageCollectionHelpers.js.map +1 -0
  200. package/lib/gcSweepReadyUsageDetection.d.ts +5 -5
  201. package/lib/gcSweepReadyUsageDetection.d.ts.map +1 -1
  202. package/lib/gcSweepReadyUsageDetection.js +14 -10
  203. package/lib/gcSweepReadyUsageDetection.js.map +1 -1
  204. package/lib/index.d.ts +3 -4
  205. package/lib/index.d.ts.map +1 -1
  206. package/lib/index.js +2 -3
  207. package/lib/index.js.map +1 -1
  208. package/lib/opLifecycle/batchManager.d.ts +13 -1
  209. package/lib/opLifecycle/batchManager.d.ts.map +1 -1
  210. package/lib/opLifecycle/batchManager.js +48 -7
  211. package/lib/opLifecycle/batchManager.js.map +1 -1
  212. package/lib/opLifecycle/definitions.d.ts +25 -1
  213. package/lib/opLifecycle/definitions.d.ts.map +1 -1
  214. package/lib/opLifecycle/definitions.js.map +1 -1
  215. package/lib/opLifecycle/index.d.ts +2 -2
  216. package/lib/opLifecycle/index.d.ts.map +1 -1
  217. package/lib/opLifecycle/index.js +1 -1
  218. package/lib/opLifecycle/index.js.map +1 -1
  219. package/lib/opLifecycle/opCompressor.d.ts +1 -1
  220. package/lib/opLifecycle/opCompressor.d.ts.map +1 -1
  221. package/lib/opLifecycle/opCompressor.js +24 -10
  222. package/lib/opLifecycle/opCompressor.js.map +1 -1
  223. package/lib/opLifecycle/opDecompressor.d.ts +2 -1
  224. package/lib/opLifecycle/opDecompressor.d.ts.map +1 -1
  225. package/lib/opLifecycle/opDecompressor.js +33 -17
  226. package/lib/opLifecycle/opDecompressor.js.map +1 -1
  227. package/lib/opLifecycle/opSplitter.d.ts +34 -2
  228. package/lib/opLifecycle/opSplitter.d.ts.map +1 -1
  229. package/lib/opLifecycle/opSplitter.js +116 -5
  230. package/lib/opLifecycle/opSplitter.js.map +1 -1
  231. package/lib/opLifecycle/outbox.d.ts +5 -0
  232. package/lib/opLifecycle/outbox.d.ts.map +1 -1
  233. package/lib/opLifecycle/outbox.js +38 -27
  234. package/lib/opLifecycle/outbox.js.map +1 -1
  235. package/lib/opLifecycle/remoteMessageProcessor.d.ts.map +1 -1
  236. package/lib/opLifecycle/remoteMessageProcessor.js +17 -2
  237. package/lib/opLifecycle/remoteMessageProcessor.js.map +1 -1
  238. package/lib/opProperties.d.ts.map +1 -1
  239. package/lib/opProperties.js +1 -3
  240. package/lib/opProperties.js.map +1 -1
  241. package/lib/orderedClientElection.d.ts.map +1 -1
  242. package/lib/orderedClientElection.js +10 -4
  243. package/lib/orderedClientElection.js.map +1 -1
  244. package/lib/packageVersion.d.ts +1 -1
  245. package/lib/packageVersion.js +1 -1
  246. package/lib/packageVersion.js.map +1 -1
  247. package/lib/pendingStateManager.d.ts +4 -13
  248. package/lib/pendingStateManager.d.ts.map +1 -1
  249. package/lib/pendingStateManager.js +134 -161
  250. package/lib/pendingStateManager.js.map +1 -1
  251. package/lib/runWhileConnectedCoordinator.d.ts.map +1 -1
  252. package/lib/runWhileConnectedCoordinator.js.map +1 -1
  253. package/lib/runningSummarizer.d.ts.map +1 -1
  254. package/lib/runningSummarizer.js +35 -23
  255. package/lib/runningSummarizer.js.map +1 -1
  256. package/lib/scheduleManager.d.ts +0 -1
  257. package/lib/scheduleManager.d.ts.map +1 -1
  258. package/lib/scheduleManager.js +11 -21
  259. package/lib/scheduleManager.js.map +1 -1
  260. package/lib/serializedSnapshotStorage.d.ts.map +1 -1
  261. package/lib/serializedSnapshotStorage.js +3 -1
  262. package/lib/serializedSnapshotStorage.js.map +1 -1
  263. package/lib/summarizer.d.ts +2 -3
  264. package/lib/summarizer.d.ts.map +1 -1
  265. package/lib/summarizer.js +39 -18
  266. package/lib/summarizer.js.map +1 -1
  267. package/lib/summarizerClientElection.d.ts +1 -2
  268. package/lib/summarizerClientElection.d.ts.map +1 -1
  269. package/lib/summarizerClientElection.js +3 -30
  270. package/lib/summarizerClientElection.js.map +1 -1
  271. package/lib/summarizerHandle.d.ts.map +1 -1
  272. package/lib/summarizerHandle.js.map +1 -1
  273. package/lib/summarizerHeuristics.d.ts.map +1 -1
  274. package/lib/summarizerHeuristics.js +6 -9
  275. package/lib/summarizerHeuristics.js.map +1 -1
  276. package/lib/summarizerTypes.d.ts +22 -25
  277. package/lib/summarizerTypes.d.ts.map +1 -1
  278. package/lib/summarizerTypes.js.map +1 -1
  279. package/lib/summaryCollection.d.ts.map +1 -1
  280. package/lib/summaryCollection.js +18 -8
  281. package/lib/summaryCollection.js.map +1 -1
  282. package/lib/summaryFormat.d.ts.map +1 -1
  283. package/lib/summaryFormat.js +20 -13
  284. package/lib/summaryFormat.js.map +1 -1
  285. package/lib/summaryGenerator.d.ts.map +1 -1
  286. package/lib/summaryGenerator.js +32 -14
  287. package/lib/summaryGenerator.js.map +1 -1
  288. package/lib/summaryManager.d.ts.map +1 -1
  289. package/lib/summaryManager.js +21 -9
  290. package/lib/summaryManager.js.map +1 -1
  291. package/lib/throttler.d.ts +2 -2
  292. package/lib/throttler.d.ts.map +1 -1
  293. package/lib/throttler.js +4 -4
  294. package/lib/throttler.js.map +1 -1
  295. package/package.json +27 -24
  296. package/prettier.config.cjs +1 -1
  297. package/src/batchTracker.ts +55 -50
  298. package/src/blobManager.ts +799 -593
  299. package/src/connectionTelemetry.ts +280 -249
  300. package/src/containerHandleContext.ts +27 -29
  301. package/src/containerRuntime.ts +3123 -2793
  302. package/src/dataStore.ts +172 -159
  303. package/src/dataStoreContext.ts +1048 -991
  304. package/src/dataStoreContexts.ts +178 -161
  305. package/src/dataStoreRegistry.ts +25 -20
  306. package/src/dataStores.ts +784 -711
  307. package/src/deltaScheduler.ts +158 -150
  308. package/src/garbageCollection.ts +1795 -1546
  309. package/src/garbageCollectionConstants.ts +10 -7
  310. package/src/garbageCollectionHelpers.ts +37 -0
  311. package/src/gcSweepReadyUsageDetection.ts +89 -83
  312. package/src/index.ts +67 -69
  313. package/src/opLifecycle/batchManager.ts +148 -86
  314. package/src/opLifecycle/definitions.ts +45 -19
  315. package/src/opLifecycle/index.ts +6 -5
  316. package/src/opLifecycle/opCompressor.ts +57 -39
  317. package/src/opLifecycle/opDecompressor.ts +104 -64
  318. package/src/opLifecycle/opSplitter.ts +226 -66
  319. package/src/opLifecycle/outbox.ts +206 -182
  320. package/src/opLifecycle/remoteMessageProcessor.ts +63 -47
  321. package/src/opProperties.ts +11 -9
  322. package/src/orderedClientElection.ts +489 -457
  323. package/src/packageVersion.ts +1 -1
  324. package/src/pendingStateManager.ts +379 -381
  325. package/src/runWhileConnectedCoordinator.ts +78 -71
  326. package/src/runningSummarizer.ts +619 -582
  327. package/src/scheduleManager.ts +299 -280
  328. package/src/serializedSnapshotStorage.ts +116 -111
  329. package/src/summarizer.ts +417 -381
  330. package/src/summarizerClientElection.ts +107 -129
  331. package/src/summarizerHandle.ts +11 -9
  332. package/src/summarizerHeuristics.ts +183 -186
  333. package/src/summarizerTypes.ts +344 -333
  334. package/src/summaryCollection.ts +378 -349
  335. package/src/summaryFormat.ts +146 -127
  336. package/src/summaryGenerator.ts +464 -406
  337. package/src/summaryManager.ts +377 -348
  338. package/src/throttler.ts +131 -122
  339. package/tsconfig.esnext.json +6 -6
  340. package/tsconfig.json +9 -13
@@ -3,103 +3,165 @@
3
3
  * Licensed under the MIT License.
4
4
  */
5
5
 
6
+ import { ITelemetryLogger } from "@fluidframework/common-definitions";
7
+ import { UsageError } from "@fluidframework/driver-utils";
8
+ import { ChildLogger } from "@fluidframework/telemetry-utils";
6
9
  import { ICompressionRuntimeOptions } from "../containerRuntime";
7
10
  import { BatchMessage, IBatch, IBatchCheckpoint } from "./definitions";
8
11
 
9
12
  export interface IBatchManagerOptions {
10
- readonly hardLimit: number;
11
- readonly softLimit?: number;
12
- readonly compressionOptions?: ICompressionRuntimeOptions;
13
+ readonly enableOpReentryCheck?: boolean;
14
+ readonly hardLimit: number;
15
+ readonly softLimit?: number;
16
+ readonly compressionOptions?: ICompressionRuntimeOptions;
13
17
  }
14
18
 
15
19
  /**
16
20
  * Helper class that manages partial batch & rollback.
17
21
  */
18
22
  export class BatchManager {
19
- private pendingBatch: BatchMessage[] = [];
20
- private batchContentSize = 0;
21
-
22
- public get length() { return this.pendingBatch.length; }
23
- public get contentSizeInBytes() { return this.batchContentSize; }
24
-
25
- constructor(public readonly options: IBatchManagerOptions) { }
26
-
27
- public push(message: BatchMessage): boolean {
28
- const contentSize = this.batchContentSize + (message.contents?.length ?? 0);
29
- const opCount = this.pendingBatch.length;
30
-
31
- // Attempt to estimate batch size, aka socket message size.
32
- // Each op has pretty large envelope, estimating to be 200 bytes.
33
- // Also content will be strigified, and that adds a lot of overhead due to a lot of escape characters.
34
- // Not taking it into account, as compression work should help there - compressed payload will be
35
- // initially stored as base64, and that requires only 2 extra escape characters.
36
- const socketMessageSize = contentSize + 200 * opCount;
37
-
38
- // If we were provided soft limit, check for exceeding it.
39
- // But only if we have any ops, as the intention here is to flush existing ops (on exceeding this limit)
40
- // and start over. That's not an option if we have no ops.
41
- // If compression is enabled, the soft and hard limit are ignored and the message will be pushed anyways.
42
- // Cases where the message is still too large will be handled by the maxConsecutiveReconnects path.
43
- if (this.options.softLimit !== undefined
44
- && this.length > 0
45
- && socketMessageSize >= this.options.softLimit) {
46
- return false;
47
- }
48
-
49
- if (socketMessageSize >= this.options.hardLimit) {
50
- return false;
51
- }
52
-
53
- this.batchContentSize = contentSize;
54
- this.pendingBatch.push(message);
55
- return true;
56
- }
57
-
58
- public get empty() { return this.pendingBatch.length === 0; }
59
-
60
- public popBatch(): IBatch {
61
- const batch: IBatch = {
62
- content: this.pendingBatch,
63
- contentSizeInBytes: this.batchContentSize,
64
- };
65
-
66
- this.pendingBatch = [];
67
- this.batchContentSize = 0;
68
-
69
- return addBatchMetadata(batch);
70
- }
71
-
72
- /**
73
- * Capture the pending state at this point
74
- */
75
- public checkpoint(): IBatchCheckpoint {
76
- const startPoint = this.pendingBatch.length;
77
- return {
78
- rollback: (process: (message: BatchMessage) => void) => {
79
- for (let i = this.pendingBatch.length; i > startPoint;) {
80
- i--;
81
- const message = this.pendingBatch[i];
82
- this.batchContentSize -= message.contents?.length ?? 0;
83
- process(message);
84
- }
85
-
86
- this.pendingBatch.length = startPoint;
87
- },
88
- };
89
- }
23
+ private readonly logger;
24
+ private pendingBatch: BatchMessage[] = [];
25
+ private batchContentSize = 0;
26
+ /**
27
+ * Track the number of ops which were detected to have a mismatched
28
+ * reference sequence number, in order to self-throttle the telemetry events.
29
+ *
30
+ * This should be removed as part of ADO:2322
31
+ */
32
+ private readonly maxMismatchedOpsToReport = 5;
33
+ private mismatchedOpsReported = 0;
34
+
35
+ public get length() {
36
+ return this.pendingBatch.length;
37
+ }
38
+ public get contentSizeInBytes() {
39
+ return this.batchContentSize;
40
+ }
41
+
42
+ constructor(public readonly options: IBatchManagerOptions, logger: ITelemetryLogger) {
43
+ this.logger = ChildLogger.create(logger, "BatchManager");
44
+ }
45
+
46
+ public push(message: BatchMessage): boolean {
47
+ this.checkReferenceSequenceNumber(message);
48
+
49
+ const contentSize = this.batchContentSize + (message.contents?.length ?? 0);
50
+ const opCount = this.pendingBatch.length;
51
+
52
+ // Attempt to estimate batch size, aka socket message size.
53
+ // Each op has pretty large envelope, estimating to be 200 bytes.
54
+ // Also content will be strigified, and that adds a lot of overhead due to a lot of escape characters.
55
+ // Not taking it into account, as compression work should help there - compressed payload will be
56
+ // initially stored as base64, and that requires only 2 extra escape characters.
57
+ const socketMessageSize = contentSize + 200 * opCount;
58
+
59
+ // If we were provided soft limit, check for exceeding it.
60
+ // But only if we have any ops, as the intention here is to flush existing ops (on exceeding this limit)
61
+ // and start over. That's not an option if we have no ops.
62
+ // If compression is enabled, the soft and hard limit are ignored and the message will be pushed anyways.
63
+ // Cases where the message is still too large will be handled by the maxConsecutiveReconnects path.
64
+ if (
65
+ this.options.softLimit !== undefined &&
66
+ this.length > 0 &&
67
+ socketMessageSize >= this.options.softLimit
68
+ ) {
69
+ return false;
70
+ }
71
+
72
+ if (socketMessageSize >= this.options.hardLimit) {
73
+ return false;
74
+ }
75
+
76
+ this.batchContentSize = contentSize;
77
+ this.pendingBatch.push(message);
78
+ return true;
79
+ }
80
+
81
+ public get empty() {
82
+ return this.pendingBatch.length === 0;
83
+ }
84
+
85
+ public popBatch(): IBatch {
86
+ const batch: IBatch = {
87
+ content: this.pendingBatch,
88
+ contentSizeInBytes: this.batchContentSize,
89
+ };
90
+
91
+ this.pendingBatch = [];
92
+ this.batchContentSize = 0;
93
+
94
+ return addBatchMetadata(batch);
95
+ }
96
+
97
+ /**
98
+ * Capture the pending state at this point
99
+ */
100
+ public checkpoint(): IBatchCheckpoint {
101
+ const startPoint = this.pendingBatch.length;
102
+ return {
103
+ rollback: (process: (message: BatchMessage) => void) => {
104
+ for (let i = this.pendingBatch.length; i > startPoint; ) {
105
+ i--;
106
+ const message = this.pendingBatch[i];
107
+ this.batchContentSize -= message.contents?.length ?? 0;
108
+ process(message);
109
+ }
110
+
111
+ this.pendingBatch.length = startPoint;
112
+ },
113
+ };
114
+ }
115
+
116
+ private checkReferenceSequenceNumber(message: BatchMessage) {
117
+ if (
118
+ this.pendingBatch.length === 0 ||
119
+ message.referenceSequenceNumber === this.pendingBatch[0].referenceSequenceNumber
120
+ ) {
121
+ // The reference sequence numbers are stable
122
+ return;
123
+ }
124
+
125
+ const telemetryProperties = {
126
+ referenceSequenceNumber: this.pendingBatch[0].referenceSequenceNumber,
127
+ messageReferenceSequenceNumber: message.referenceSequenceNumber,
128
+ type: message.deserializedContent.type,
129
+ length: this.pendingBatch.length,
130
+ enableOpReentryCheck: this.options.enableOpReentryCheck === true,
131
+ };
132
+ const error = new UsageError("Submission of an out of order message");
133
+ const eventName = "ReferenceSequenceNumberMismatch";
134
+
135
+ if (this.options.enableOpReentryCheck === true) {
136
+ this.logger.sendErrorEvent({ eventName, ...telemetryProperties }, error);
137
+ throw error;
138
+ }
139
+
140
+ if (++this.mismatchedOpsReported <= this.maxMismatchedOpsToReport) {
141
+ this.logger.sendErrorEvent(
142
+ {
143
+ eventName,
144
+ ...telemetryProperties,
145
+ ops: this.mismatchedOpsReported,
146
+ maxOps: this.maxMismatchedOpsToReport,
147
+ },
148
+ error,
149
+ );
150
+ }
151
+ }
90
152
  }
91
153
 
92
154
  const addBatchMetadata = (batch: IBatch): IBatch => {
93
- if (batch.content.length > 1) {
94
- batch.content[0].metadata = {
95
- ...batch.content[0].metadata,
96
- batch: true
97
- };
98
- batch.content[batch.content.length - 1].metadata = {
99
- ...batch.content[batch.content.length - 1].metadata,
100
- batch: false
101
- };
102
- }
103
-
104
- return batch;
155
+ if (batch.content.length > 1) {
156
+ batch.content[0].metadata = {
157
+ ...batch.content[0].metadata,
158
+ batch: true,
159
+ };
160
+ batch.content[batch.content.length - 1].metadata = {
161
+ ...batch.content[batch.content.length - 1].metadata,
162
+ batch: false,
163
+ };
164
+ }
165
+
166
+ return batch;
105
167
  };
@@ -4,41 +4,67 @@
4
4
  */
5
5
 
6
6
  import { IBatchMessage } from "@fluidframework/container-definitions";
7
- import { MessageType } from "@fluidframework/protocol-definitions";
7
+ import { ISequencedDocumentMessage, MessageType } from "@fluidframework/protocol-definitions";
8
8
  import { CompressionAlgorithms, ContainerMessageType, ContainerRuntimeMessage } from "..";
9
9
 
10
10
  /**
11
11
  * Batch message type used internally by the runtime
12
12
  */
13
13
  export type BatchMessage = IBatchMessage & {
14
- localOpMetadata: unknown;
15
- deserializedContent: ContainerRuntimeMessage;
16
- referenceSequenceNumber: number;
17
- compression?: CompressionAlgorithms;
14
+ localOpMetadata: unknown;
15
+ deserializedContent: ContainerRuntimeMessage;
16
+ referenceSequenceNumber: number;
17
+ compression?: CompressionAlgorithms;
18
18
  };
19
19
 
20
20
  /**
21
21
  * Batch interface used internally by the runtime.
22
22
  */
23
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[];
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
33
  }
34
34
 
35
35
  export interface IBatchCheckpoint {
36
- rollback: (action: (message: BatchMessage) => void) => void;
36
+ rollback: (action: (message: BatchMessage) => void) => void;
37
37
  }
38
38
 
39
39
  export interface IChunkedOp {
40
- chunkId: number;
41
- totalChunks: number;
42
- contents: string;
43
- originalType: MessageType | ContainerMessageType;
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;
44
70
  }
@@ -5,13 +5,14 @@
5
5
 
6
6
  export { BatchManager } from "./batchManager";
7
7
  export {
8
- BatchMessage,
9
- IBatch,
10
- IBatchCheckpoint,
11
- IChunkedOp,
8
+ BatchMessage,
9
+ IBatch,
10
+ IBatchCheckpoint,
11
+ IChunkedOp,
12
+ IMessageProcessingResult,
12
13
  } from "./definitions";
13
14
  export { Outbox } from "./outbox";
14
15
  export { OpCompressor } from "./opCompressor";
15
16
  export { OpDecompressor } from "./opDecompressor";
16
- export { OpSplitter } from "./opSplitter";
17
+ export { OpSplitter, splitOp } from "./opSplitter";
17
18
  export { RemoteMessageProcessor, unpackRuntimeMessage } from "./remoteMessageProcessor";
@@ -5,9 +5,10 @@
5
5
 
6
6
  import { ITelemetryLogger } from "@fluidframework/common-definitions";
7
7
  import { IsoBuffer } from "@fluidframework/common-utils";
8
+ import { UsageError } from "@fluidframework/container-utils";
8
9
  import { ChildLogger } from "@fluidframework/telemetry-utils";
9
10
  import { compress } from "lz4js";
10
- import { CompressionAlgorithms, ContainerRuntimeMessage } from "../containerRuntime";
11
+ import { CompressionAlgorithms } from "../containerRuntime";
11
12
  import { IBatch, BatchMessage } from "./definitions";
12
13
 
13
14
  /**
@@ -16,49 +17,66 @@ import { IBatch, BatchMessage } from "./definitions";
16
17
  * op to reserve sequence numbers.
17
18
  */
18
19
  export class OpCompressor {
19
- private readonly logger;
20
- private compressedBatchCount = 0;
20
+ private readonly logger;
21
21
 
22
- constructor(logger: ITelemetryLogger) {
23
- this.logger = ChildLogger.create(logger, "OpCompressor");
24
- }
22
+ constructor(logger: ITelemetryLogger) {
23
+ this.logger = ChildLogger.create(logger, "OpCompressor");
24
+ }
25
25
 
26
- public compressBatch(batch: IBatch): IBatch {
27
- const messages: BatchMessage[] = [];
28
- this.compressedBatchCount++;
29
- const contentToCompress: ContainerRuntimeMessage[] = [];
30
- for (const message of batch.content) {
31
- contentToCompress.push(message.deserializedContent);
32
- }
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;
33
32
 
34
- const compressionStart = Date.now();
35
- const contentsAsBuffer = new TextEncoder().encode(JSON.stringify(contentToCompress));
36
- const compressedContents = compress(contentsAsBuffer);
37
- const compressedContent = IsoBuffer.from(compressedContents).toString("base64");
38
- const duration = Date.now() - compressionStart;
33
+ if (batch.contentSizeInBytes > 200000) {
34
+ this.logger.sendPerformanceEvent({
35
+ eventName: "CompressedBatch",
36
+ duration,
37
+ sizeBeforeCompression: batch.contentSizeInBytes,
38
+ sizeAfterCompression: compressedContent.length,
39
+ });
40
+ }
39
41
 
40
- if (batch.contentSizeInBytes > 200000 || this.compressedBatchCount % 25) {
41
- this.logger.sendPerformanceEvent({
42
- eventName: "CompressedBatch",
43
- duration,
44
- sizeBeforeCompression: batch.contentSizeInBytes,
45
- sizeAfterCompression: compressedContent.length,
46
- });
47
- }
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
+ });
48
49
 
49
- messages.push({
50
- ...batch.content[0], contents: JSON.stringify({ packedContents: compressedContent }),
51
- metadata: { ...batch.content[0].metadata, compressed: true },
52
- compression: CompressionAlgorithms.lz4,
53
- });
50
+ for (const message of batch.content.slice(1)) {
51
+ messages.push({ ...message, contents: undefined });
52
+ }
54
53
 
55
- for (const message of batch.content.slice(1)) {
56
- messages.push({ ...message, contents: undefined });
57
- }
54
+ return {
55
+ contentSizeInBytes: compressedContent.length,
56
+ content: messages,
57
+ };
58
+ }
58
59
 
59
- return {
60
- contentSizeInBytes: compressedContent.length,
61
- content: messages,
62
- };
63
- }
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
+ }
64
82
  }
@@ -7,6 +7,7 @@ import { decompress } from "lz4js";
7
7
  import { ISequencedDocumentMessage } from "@fluidframework/protocol-definitions";
8
8
  import { assert, IsoBuffer, Uint8ArrayToString } from "@fluidframework/common-utils";
9
9
  import { CompressionAlgorithms } from "../containerRuntime";
10
+ import { IMessageProcessingResult } from "./definitions";
10
11
 
11
12
  /**
12
13
  * State machine that "unrolls" contents of compressed batches of ops after decompressing them.
@@ -17,68 +18,107 @@ import { CompressionAlgorithms } from "../containerRuntime";
17
18
  * 4. An individually compressed op will have undefined batch metadata and compression set to true
18
19
  */
19
20
  export class OpDecompressor {
20
- private activeBatch = false;
21
- private rootMessageContents: any | undefined;
22
- private processedCount = 0;
23
-
24
- public processMessage(message: ISequencedDocumentMessage): ISequencedDocumentMessage {
25
- // We're checking for compression = true or top level compression property so
26
- // that we can enable compression without waiting on all ordering services
27
- // to pick up protocol change. Eventually only the top level property should
28
- // be used.
29
- if (message.metadata?.batch === true
30
- && (message.metadata?.compressed || message.compression !== undefined)) {
31
- // Beginning of a compressed batch
32
- assert(this.activeBatch === false, 0x4b8 /* shouldn't have multiple active batches */);
33
- if (message.compression) {
34
- // lz4 is the only supported compression algorithm for now
35
- assert(message.compression === CompressionAlgorithms.lz4,
36
- 0x4b9 /* lz4 is currently the only supported compression algorithm */);
37
- }
38
-
39
- this.activeBatch = true;
40
-
41
- const contents = IsoBuffer.from(message.contents.packedContents, "base64");
42
- const decompressedMessage = decompress(contents);
43
- const intoString = Uint8ArrayToString(decompressedMessage);
44
- const asObj = JSON.parse(intoString);
45
- this.rootMessageContents = asObj;
46
-
47
- return { ...message, contents: this.rootMessageContents[this.processedCount++] };
48
- }
49
-
50
- if (this.rootMessageContents !== undefined && message.metadata?.batch === undefined && this.activeBatch) {
51
- // Continuation of compressed batch
52
- return { ...message, contents: this.rootMessageContents[this.processedCount++] };
53
- }
54
-
55
- if (this.rootMessageContents !== undefined && message.metadata?.batch === false) {
56
- // End of compressed batch
57
- const returnMessage = {
58
- ...message,
59
- contents: this.rootMessageContents[this.processedCount++]
60
- };
61
-
62
- this.activeBatch = false;
63
- this.rootMessageContents = undefined;
64
- this.processedCount = 0;
65
-
66
- return returnMessage;
67
- }
68
-
69
- if (message.metadata?.batch === undefined &&
70
- (message.metadata?.compressed || message.compression === CompressionAlgorithms.lz4)) {
71
- // Single compressed message
72
- assert(this.activeBatch === false, 0x4ba /* shouldn't receive compressed message in middle of a batch */);
73
-
74
- const contents = IsoBuffer.from(message.contents.packedContents, "base64");
75
- const decompressedMessage = decompress(contents);
76
- const intoString = new TextDecoder().decode(decompressedMessage);
77
- const asObj = JSON.parse(intoString);
78
-
79
- return { ...message, contents: asObj[0] };
80
- }
81
-
82
- return message;
83
- }
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
+ }
84
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
+ });