@fluidframework/container-runtime 2.0.0-internal.3.0.5 → 2.0.0-internal.3.1.1

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 (329) hide show
  1. package/.eslintrc.js +19 -19
  2. package/.mocharc.js +2 -2
  3. package/api-extractor.json +2 -2
  4. package/dist/batchTracker.d.ts.map +1 -1
  5. package/dist/batchTracker.js +2 -1
  6. package/dist/batchTracker.js.map +1 -1
  7. package/dist/blobManager.d.ts +9 -2
  8. package/dist/blobManager.d.ts.map +1 -1
  9. package/dist/blobManager.js +80 -33
  10. package/dist/blobManager.js.map +1 -1
  11. package/dist/connectionTelemetry.d.ts.map +1 -1
  12. package/dist/connectionTelemetry.js +11 -9
  13. package/dist/connectionTelemetry.js.map +1 -1
  14. package/dist/containerHandleContext.d.ts.map +1 -1
  15. package/dist/containerHandleContext.js +3 -1
  16. package/dist/containerHandleContext.js.map +1 -1
  17. package/dist/containerRuntime.d.ts +11 -1
  18. package/dist/containerRuntime.d.ts.map +1 -1
  19. package/dist/containerRuntime.js +116 -72
  20. package/dist/containerRuntime.js.map +1 -1
  21. package/dist/dataStore.d.ts.map +1 -1
  22. package/dist/dataStore.js +11 -9
  23. package/dist/dataStore.js.map +1 -1
  24. package/dist/dataStoreContext.d.ts +18 -13
  25. package/dist/dataStoreContext.d.ts.map +1 -1
  26. package/dist/dataStoreContext.js +68 -55
  27. package/dist/dataStoreContext.js.map +1 -1
  28. package/dist/dataStoreContexts.d.ts.map +1 -1
  29. package/dist/dataStoreContexts.js +7 -3
  30. package/dist/dataStoreContexts.js.map +1 -1
  31. package/dist/dataStoreRegistry.d.ts.map +1 -1
  32. package/dist/dataStoreRegistry.js +3 -1
  33. package/dist/dataStoreRegistry.js.map +1 -1
  34. package/dist/dataStores.d.ts +26 -1
  35. package/dist/dataStores.d.ts.map +1 -1
  36. package/dist/dataStores.js +103 -18
  37. package/dist/dataStores.js.map +1 -1
  38. package/dist/deltaScheduler.d.ts.map +1 -1
  39. package/dist/deltaScheduler.js +8 -3
  40. package/dist/deltaScheduler.js.map +1 -1
  41. package/dist/garbageCollection.d.ts +33 -14
  42. package/dist/garbageCollection.d.ts.map +1 -1
  43. package/dist/garbageCollection.js +178 -92
  44. package/dist/garbageCollection.js.map +1 -1
  45. package/dist/garbageCollectionConstants.d.ts +1 -0
  46. package/dist/garbageCollectionConstants.d.ts.map +1 -1
  47. package/dist/garbageCollectionConstants.js +4 -1
  48. package/dist/garbageCollectionConstants.js.map +1 -1
  49. package/dist/garbageCollectionHelpers.d.ts +26 -0
  50. package/dist/garbageCollectionHelpers.d.ts.map +1 -0
  51. package/dist/garbageCollectionHelpers.js +45 -0
  52. package/dist/garbageCollectionHelpers.js.map +1 -0
  53. package/dist/gcSweepReadyUsageDetection.d.ts +5 -5
  54. package/dist/gcSweepReadyUsageDetection.d.ts.map +1 -1
  55. package/dist/gcSweepReadyUsageDetection.js +14 -10
  56. package/dist/gcSweepReadyUsageDetection.js.map +1 -1
  57. package/dist/index.d.ts +2 -2
  58. package/dist/index.d.ts.map +1 -1
  59. package/dist/index.js.map +1 -1
  60. package/dist/opLifecycle/batchManager.d.ts +5 -5
  61. package/dist/opLifecycle/batchManager.d.ts.map +1 -1
  62. package/dist/opLifecycle/batchManager.js +19 -12
  63. package/dist/opLifecycle/batchManager.js.map +1 -1
  64. package/dist/opLifecycle/definitions.d.ts.map +1 -1
  65. package/dist/opLifecycle/definitions.js.map +1 -1
  66. package/dist/opLifecycle/index.d.ts.map +1 -1
  67. package/dist/opLifecycle/index.js.map +1 -1
  68. package/dist/opLifecycle/opCompressor.d.ts.map +1 -1
  69. package/dist/opLifecycle/opCompressor.js.map +1 -1
  70. package/dist/opLifecycle/opDecompressor.d.ts +0 -4
  71. package/dist/opLifecycle/opDecompressor.d.ts.map +1 -1
  72. package/dist/opLifecycle/opDecompressor.js +7 -43
  73. package/dist/opLifecycle/opDecompressor.js.map +1 -1
  74. package/dist/opLifecycle/opSplitter.d.ts.map +1 -1
  75. package/dist/opLifecycle/opSplitter.js +4 -1
  76. package/dist/opLifecycle/opSplitter.js.map +1 -1
  77. package/dist/opLifecycle/outbox.d.ts.map +1 -1
  78. package/dist/opLifecycle/outbox.js +20 -19
  79. package/dist/opLifecycle/outbox.js.map +1 -1
  80. package/dist/opLifecycle/remoteMessageProcessor.d.ts.map +1 -1
  81. package/dist/opLifecycle/remoteMessageProcessor.js.map +1 -1
  82. package/dist/opProperties.d.ts.map +1 -1
  83. package/dist/opProperties.js +1 -3
  84. package/dist/opProperties.js.map +1 -1
  85. package/dist/orderedClientElection.d.ts.map +1 -1
  86. package/dist/orderedClientElection.js +10 -4
  87. package/dist/orderedClientElection.js.map +1 -1
  88. package/dist/packageVersion.d.ts +1 -1
  89. package/dist/packageVersion.js +1 -1
  90. package/dist/packageVersion.js.map +1 -1
  91. package/dist/pendingStateManager.d.ts +7 -0
  92. package/dist/pendingStateManager.d.ts.map +1 -1
  93. package/dist/pendingStateManager.js +7 -4
  94. package/dist/pendingStateManager.js.map +1 -1
  95. package/dist/runWhileConnectedCoordinator.d.ts.map +1 -1
  96. package/dist/runWhileConnectedCoordinator.js.map +1 -1
  97. package/dist/runningSummarizer.d.ts.map +1 -1
  98. package/dist/runningSummarizer.js +34 -21
  99. package/dist/runningSummarizer.js.map +1 -1
  100. package/dist/scheduleManager.d.ts.map +1 -1
  101. package/dist/scheduleManager.js +3 -2
  102. package/dist/scheduleManager.js.map +1 -1
  103. package/dist/serializedSnapshotStorage.d.ts +2 -2
  104. package/dist/serializedSnapshotStorage.d.ts.map +1 -1
  105. package/dist/serializedSnapshotStorage.js +5 -3
  106. package/dist/serializedSnapshotStorage.js.map +1 -1
  107. package/dist/summarizer.d.ts +2 -2
  108. package/dist/summarizer.d.ts.map +1 -1
  109. package/dist/summarizer.js +37 -17
  110. package/dist/summarizer.js.map +1 -1
  111. package/dist/summarizerClientElection.d.ts.map +1 -1
  112. package/dist/summarizerClientElection.js.map +1 -1
  113. package/dist/summarizerHandle.d.ts.map +1 -1
  114. package/dist/summarizerHandle.js.map +1 -1
  115. package/dist/summarizerHeuristics.d.ts.map +1 -1
  116. package/dist/summarizerHeuristics.js +6 -9
  117. package/dist/summarizerHeuristics.js.map +1 -1
  118. package/dist/summarizerTypes.d.ts +21 -21
  119. package/dist/summarizerTypes.d.ts.map +1 -1
  120. package/dist/summarizerTypes.js.map +1 -1
  121. package/dist/summaryCollection.d.ts.map +1 -1
  122. package/dist/summaryCollection.js +18 -8
  123. package/dist/summaryCollection.js.map +1 -1
  124. package/dist/summaryFormat.d.ts +5 -2
  125. package/dist/summaryFormat.d.ts.map +1 -1
  126. package/dist/summaryFormat.js +18 -10
  127. package/dist/summaryFormat.js.map +1 -1
  128. package/dist/summaryGenerator.d.ts.map +1 -1
  129. package/dist/summaryGenerator.js +35 -16
  130. package/dist/summaryGenerator.js.map +1 -1
  131. package/dist/summaryManager.d.ts.map +1 -1
  132. package/dist/summaryManager.js +21 -9
  133. package/dist/summaryManager.js.map +1 -1
  134. package/dist/throttler.d.ts +2 -2
  135. package/dist/throttler.d.ts.map +1 -1
  136. package/dist/throttler.js +4 -4
  137. package/dist/throttler.js.map +1 -1
  138. package/garbageCollection.md +15 -2
  139. package/lib/batchTracker.d.ts.map +1 -1
  140. package/lib/batchTracker.js +2 -1
  141. package/lib/batchTracker.js.map +1 -1
  142. package/lib/blobManager.d.ts +9 -2
  143. package/lib/blobManager.d.ts.map +1 -1
  144. package/lib/blobManager.js +82 -35
  145. package/lib/blobManager.js.map +1 -1
  146. package/lib/connectionTelemetry.d.ts.map +1 -1
  147. package/lib/connectionTelemetry.js +11 -9
  148. package/lib/connectionTelemetry.js.map +1 -1
  149. package/lib/containerHandleContext.d.ts.map +1 -1
  150. package/lib/containerHandleContext.js +3 -1
  151. package/lib/containerHandleContext.js.map +1 -1
  152. package/lib/containerRuntime.d.ts +11 -1
  153. package/lib/containerRuntime.d.ts.map +1 -1
  154. package/lib/containerRuntime.js +122 -78
  155. package/lib/containerRuntime.js.map +1 -1
  156. package/lib/dataStore.d.ts.map +1 -1
  157. package/lib/dataStore.js +11 -9
  158. package/lib/dataStore.js.map +1 -1
  159. package/lib/dataStoreContext.d.ts +18 -13
  160. package/lib/dataStoreContext.d.ts.map +1 -1
  161. package/lib/dataStoreContext.js +71 -58
  162. package/lib/dataStoreContext.js.map +1 -1
  163. package/lib/dataStoreContexts.d.ts.map +1 -1
  164. package/lib/dataStoreContexts.js +7 -3
  165. package/lib/dataStoreContexts.js.map +1 -1
  166. package/lib/dataStoreRegistry.d.ts.map +1 -1
  167. package/lib/dataStoreRegistry.js +3 -1
  168. package/lib/dataStoreRegistry.js.map +1 -1
  169. package/lib/dataStores.d.ts +26 -1
  170. package/lib/dataStores.d.ts.map +1 -1
  171. package/lib/dataStores.js +109 -24
  172. package/lib/dataStores.js.map +1 -1
  173. package/lib/deltaScheduler.d.ts.map +1 -1
  174. package/lib/deltaScheduler.js +9 -4
  175. package/lib/deltaScheduler.js.map +1 -1
  176. package/lib/garbageCollection.d.ts +33 -14
  177. package/lib/garbageCollection.d.ts.map +1 -1
  178. package/lib/garbageCollection.js +180 -94
  179. package/lib/garbageCollection.js.map +1 -1
  180. package/lib/garbageCollectionConstants.d.ts +1 -0
  181. package/lib/garbageCollectionConstants.d.ts.map +1 -1
  182. package/lib/garbageCollectionConstants.js +3 -0
  183. package/lib/garbageCollectionConstants.js.map +1 -1
  184. package/lib/garbageCollectionHelpers.d.ts +26 -0
  185. package/lib/garbageCollectionHelpers.d.ts.map +1 -0
  186. package/lib/garbageCollectionHelpers.js +40 -0
  187. package/lib/garbageCollectionHelpers.js.map +1 -0
  188. package/lib/gcSweepReadyUsageDetection.d.ts +5 -5
  189. package/lib/gcSweepReadyUsageDetection.d.ts.map +1 -1
  190. package/lib/gcSweepReadyUsageDetection.js +14 -10
  191. package/lib/gcSweepReadyUsageDetection.js.map +1 -1
  192. package/lib/index.d.ts +2 -2
  193. package/lib/index.d.ts.map +1 -1
  194. package/lib/index.js +1 -1
  195. package/lib/index.js.map +1 -1
  196. package/lib/opLifecycle/batchManager.d.ts +5 -5
  197. package/lib/opLifecycle/batchManager.d.ts.map +1 -1
  198. package/lib/opLifecycle/batchManager.js +19 -12
  199. package/lib/opLifecycle/batchManager.js.map +1 -1
  200. package/lib/opLifecycle/definitions.d.ts.map +1 -1
  201. package/lib/opLifecycle/definitions.js.map +1 -1
  202. package/lib/opLifecycle/index.d.ts.map +1 -1
  203. package/lib/opLifecycle/index.js.map +1 -1
  204. package/lib/opLifecycle/opCompressor.d.ts.map +1 -1
  205. package/lib/opLifecycle/opCompressor.js.map +1 -1
  206. package/lib/opLifecycle/opDecompressor.d.ts +0 -4
  207. package/lib/opLifecycle/opDecompressor.d.ts.map +1 -1
  208. package/lib/opLifecycle/opDecompressor.js +7 -43
  209. package/lib/opLifecycle/opDecompressor.js.map +1 -1
  210. package/lib/opLifecycle/opSplitter.d.ts.map +1 -1
  211. package/lib/opLifecycle/opSplitter.js +5 -2
  212. package/lib/opLifecycle/opSplitter.js.map +1 -1
  213. package/lib/opLifecycle/outbox.d.ts.map +1 -1
  214. package/lib/opLifecycle/outbox.js +20 -19
  215. package/lib/opLifecycle/outbox.js.map +1 -1
  216. package/lib/opLifecycle/remoteMessageProcessor.d.ts.map +1 -1
  217. package/lib/opLifecycle/remoteMessageProcessor.js.map +1 -1
  218. package/lib/opProperties.d.ts.map +1 -1
  219. package/lib/opProperties.js +1 -3
  220. package/lib/opProperties.js.map +1 -1
  221. package/lib/orderedClientElection.d.ts.map +1 -1
  222. package/lib/orderedClientElection.js +10 -4
  223. package/lib/orderedClientElection.js.map +1 -1
  224. package/lib/packageVersion.d.ts +1 -1
  225. package/lib/packageVersion.js +1 -1
  226. package/lib/packageVersion.js.map +1 -1
  227. package/lib/pendingStateManager.d.ts +7 -0
  228. package/lib/pendingStateManager.d.ts.map +1 -1
  229. package/lib/pendingStateManager.js +7 -4
  230. package/lib/pendingStateManager.js.map +1 -1
  231. package/lib/runWhileConnectedCoordinator.d.ts.map +1 -1
  232. package/lib/runWhileConnectedCoordinator.js.map +1 -1
  233. package/lib/runningSummarizer.d.ts.map +1 -1
  234. package/lib/runningSummarizer.js +35 -22
  235. package/lib/runningSummarizer.js.map +1 -1
  236. package/lib/scheduleManager.d.ts.map +1 -1
  237. package/lib/scheduleManager.js +3 -2
  238. package/lib/scheduleManager.js.map +1 -1
  239. package/lib/serializedSnapshotStorage.d.ts +2 -2
  240. package/lib/serializedSnapshotStorage.d.ts.map +1 -1
  241. package/lib/serializedSnapshotStorage.js +5 -3
  242. package/lib/serializedSnapshotStorage.js.map +1 -1
  243. package/lib/summarizer.d.ts +2 -2
  244. package/lib/summarizer.d.ts.map +1 -1
  245. package/lib/summarizer.js +37 -17
  246. package/lib/summarizer.js.map +1 -1
  247. package/lib/summarizerClientElection.d.ts.map +1 -1
  248. package/lib/summarizerClientElection.js.map +1 -1
  249. package/lib/summarizerHandle.d.ts.map +1 -1
  250. package/lib/summarizerHandle.js.map +1 -1
  251. package/lib/summarizerHeuristics.d.ts.map +1 -1
  252. package/lib/summarizerHeuristics.js +6 -9
  253. package/lib/summarizerHeuristics.js.map +1 -1
  254. package/lib/summarizerTypes.d.ts +21 -21
  255. package/lib/summarizerTypes.d.ts.map +1 -1
  256. package/lib/summarizerTypes.js.map +1 -1
  257. package/lib/summaryCollection.d.ts.map +1 -1
  258. package/lib/summaryCollection.js +18 -8
  259. package/lib/summaryCollection.js.map +1 -1
  260. package/lib/summaryFormat.d.ts +5 -2
  261. package/lib/summaryFormat.d.ts.map +1 -1
  262. package/lib/summaryFormat.js +20 -12
  263. package/lib/summaryFormat.js.map +1 -1
  264. package/lib/summaryGenerator.d.ts.map +1 -1
  265. package/lib/summaryGenerator.js +35 -16
  266. package/lib/summaryGenerator.js.map +1 -1
  267. package/lib/summaryManager.d.ts.map +1 -1
  268. package/lib/summaryManager.js +21 -9
  269. package/lib/summaryManager.js.map +1 -1
  270. package/lib/throttler.d.ts +2 -2
  271. package/lib/throttler.d.ts.map +1 -1
  272. package/lib/throttler.js +4 -4
  273. package/lib/throttler.js.map +1 -1
  274. package/package.json +121 -115
  275. package/prettier.config.cjs +1 -1
  276. package/src/batchTracker.ts +54 -49
  277. package/src/blobManager.ts +793 -672
  278. package/src/connectionTelemetry.ts +280 -249
  279. package/src/containerHandleContext.ts +27 -29
  280. package/src/containerRuntime.ts +3168 -2988
  281. package/src/dataStore.ts +172 -159
  282. package/src/dataStoreContext.ts +1098 -1055
  283. package/src/dataStoreContexts.ts +178 -161
  284. package/src/dataStoreRegistry.ts +25 -20
  285. package/src/dataStores.ts +884 -728
  286. package/src/deltaScheduler.ts +158 -150
  287. package/src/garbageCollection.ts +1860 -1688
  288. package/src/garbageCollectionConstants.ts +3 -0
  289. package/src/garbageCollectionHelpers.ts +61 -0
  290. package/src/gcSweepReadyUsageDetection.ts +89 -83
  291. package/src/index.ts +67 -66
  292. package/src/opLifecycle/README.md +152 -0
  293. package/src/opLifecycle/batchManager.ts +145 -141
  294. package/src/opLifecycle/definitions.ts +29 -29
  295. package/src/opLifecycle/index.ts +5 -5
  296. package/src/opLifecycle/opCompressor.ts +54 -53
  297. package/src/opLifecycle/opDecompressor.ts +100 -128
  298. package/src/opLifecycle/opSplitter.ts +214 -188
  299. package/src/opLifecycle/outbox.ts +204 -195
  300. package/src/opLifecycle/remoteMessageProcessor.ts +62 -62
  301. package/src/opProperties.ts +11 -9
  302. package/src/orderedClientElection.ts +489 -457
  303. package/src/packageVersion.ts +1 -1
  304. package/src/pendingStateManager.ts +384 -338
  305. package/src/runWhileConnectedCoordinator.ts +78 -71
  306. package/src/runningSummarizer.ts +619 -581
  307. package/src/scheduleManager.ts +299 -269
  308. package/src/serializedSnapshotStorage.ts +126 -112
  309. package/src/summarizer.ts +417 -381
  310. package/src/summarizerClientElection.ts +107 -100
  311. package/src/summarizerHandle.ts +11 -9
  312. package/src/summarizerHeuristics.ts +183 -186
  313. package/src/summarizerTypes.ts +344 -330
  314. package/src/summaryCollection.ts +378 -349
  315. package/src/summaryFormat.ts +165 -143
  316. package/src/summaryGenerator.ts +465 -410
  317. package/src/summaryManager.ts +377 -348
  318. package/src/throttler.ts +131 -122
  319. package/tsconfig.esnext.json +6 -6
  320. package/tsconfig.json +9 -13
  321. package/dist/garbageCollectionTombstoneUtils.d.ts +0 -14
  322. package/dist/garbageCollectionTombstoneUtils.d.ts.map +0 -1
  323. package/dist/garbageCollectionTombstoneUtils.js +0 -23
  324. package/dist/garbageCollectionTombstoneUtils.js.map +0 -1
  325. package/lib/garbageCollectionTombstoneUtils.d.ts +0 -14
  326. package/lib/garbageCollectionTombstoneUtils.d.ts.map +0 -1
  327. package/lib/garbageCollectionTombstoneUtils.js +0 -19
  328. package/lib/garbageCollectionTombstoneUtils.js.map +0 -1
  329. package/src/garbageCollectionTombstoneUtils.ts +0 -28
@@ -6,7 +6,10 @@
6
6
  import { ITelemetryLogger } from "@fluidframework/common-definitions";
7
7
  import { assert } from "@fluidframework/common-utils";
8
8
  import { IBatchMessage } from "@fluidframework/container-definitions";
9
- import { DataCorruptionError, extractSafePropertiesFromMessage } from "@fluidframework/container-utils";
9
+ import {
10
+ DataCorruptionError,
11
+ extractSafePropertiesFromMessage,
12
+ } from "@fluidframework/container-utils";
10
13
  import { ISequencedDocumentMessage } from "@fluidframework/protocol-definitions";
11
14
  import { ChildLogger } from "@fluidframework/telemetry-utils";
12
15
  import { ContainerMessageType, ContainerRuntimeMessage } from "../containerRuntime";
@@ -16,197 +19,220 @@ import { BatchMessage, IBatch, IChunkedOp, IMessageProcessingResult } from "./de
16
19
  * Responsible for creating and reconstructing chunked messages.
17
20
  */
18
21
  export class OpSplitter {
19
- // Local copy of incomplete received chunks.
20
- private readonly chunkMap: Map<string, string[]>;
21
- private readonly logger;
22
-
23
- constructor(
24
- chunks: [string, string[]][],
25
- private readonly submitBatchFn: ((batch: IBatchMessage[]) => number) | undefined,
26
- private readonly chunkSizeInBytes: number,
27
- private readonly maxBatchSizeInBytes: number,
28
- logger: ITelemetryLogger,
29
- ) {
30
- this.chunkMap = new Map<string, string[]>(chunks);
31
- this.logger = ChildLogger.create(logger, "OpSplitter");
32
- }
33
-
34
- public get isBatchChunkingEnabled(): boolean {
35
- return this.chunkSizeInBytes < Number.POSITIVE_INFINITY && this.submitBatchFn !== undefined;
36
- }
37
-
38
- public get chunks(): ReadonlyMap<string, string[]> {
39
- return this.chunkMap;
40
- }
41
-
42
- public processRemoteMessage(message: ISequencedDocumentMessage): IMessageProcessingResult {
43
- if (message.type !== ContainerMessageType.ChunkedOp) {
44
- return {
45
- message,
46
- state: "Skipped",
47
- };
48
- }
49
-
50
- const clientId = message.clientId;
51
- const chunkedContent = message.contents as IChunkedOp;
52
- this.addChunk(clientId, chunkedContent, message);
53
-
54
- if (chunkedContent.chunkId < chunkedContent.totalChunks) {
55
- // We are processing the op in chunks but haven't reached
56
- // the last chunk yet in order to reconstruct the original op
57
- return {
58
- message,
59
- state: "Accepted",
60
- };
61
- }
62
-
63
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
64
- const serializedContent = this.chunkMap.get(clientId)!.join("");
65
- this.clearPartialChunks(clientId);
66
-
67
- const newMessage = { ...message };
68
- newMessage.contents = serializedContent === "" ? undefined : JSON.parse(serializedContent);
69
- newMessage.type = chunkedContent.originalType;
70
- newMessage.metadata = chunkedContent.originalMetadata;
71
- newMessage.compression = chunkedContent.originalCompression;
72
- return {
73
- message: newMessage,
74
- state: "Processed",
75
- };
76
- }
77
-
78
- public clearPartialChunks(clientId: string) {
79
- if (this.chunkMap.has(clientId)) {
80
- this.chunkMap.delete(clientId);
81
- }
82
- }
83
-
84
- private addChunk(clientId: string, chunkedContent: IChunkedOp, originalMessage: ISequencedDocumentMessage) {
85
- let map = this.chunkMap.get(clientId);
86
- if (map === undefined) {
87
- map = [];
88
- this.chunkMap.set(clientId, map);
89
- }
90
-
91
- if (chunkedContent.chunkId !== map.length + 1) {
92
- // We are expecting the chunks to be processed sequentially, in the same order as they are sent.
93
- // Therefore, the chunkId of the incoming op needs to match the length of the array (1-based indexing)
94
- // holding the existing chunks for that particular clientId.
95
- throw new DataCorruptionError("Chunk Id mismatch", {
96
- ...extractSafePropertiesFromMessage(originalMessage),
97
- chunkMapLength: map.length,
98
- chunkId: chunkedContent.chunkId,
99
- totalChunks: chunkedContent.totalChunks,
100
- });
101
- }
102
-
103
- map.push(chunkedContent.contents);
104
- }
105
-
106
- /**
107
- * Splits the first op of a compressed batch in chunks, sends the chunks separately and
108
- * returns a new batch composed of the last chunk and the rest of the ops in the original batch.
109
- *
110
- * A compressed batch is formed by one large op at the first position, followed by a series of placeholder ops
111
- * which are used in order to reserve the sequence numbers for when the first op gets unrolled into the original
112
- * uncompressed ops at ingestion in the runtime.
113
- *
114
- * If the first op is too large, it can be chunked (split into smaller op) which can be sent individually over the wire
115
- * and accumulate at ingestion, until the last op in the chunk is processed, when the original op is unrolled.
116
- *
117
- * This method will send the first N - 1 chunks separately and use the last chunk as the first message in the result batch
118
- * and then appends the original placeholder ops. This will ensure that the batch semantics of the original (non-compressed) batch
119
- * are preserved, as the original chunked op will be unrolled by the runtime when the first message in the batch is processed
120
- * (as it is the last chunk).
121
- *
122
- * To illustrate, if the input is `[largeOp, emptyOp, emptyOp]`, `largeOp` will be split into `[chunk1, chunk2, chunk3, chunk4]`.
123
- * `chunk1`, `chunk2` and `chunk3` will be sent individually and `[chunk4, emptyOp, emptyOp]` will be returned.
124
- *
125
- * @param batch - the compressed batch which needs to be processed
126
- * @returns A new adjusted batch which can be sent over the wire
127
- */
128
- public splitCompressedBatch(batch: IBatch): IBatch {
129
- assert(this.isBatchChunkingEnabled, 0x513 /* Chunking needs to be enabled */);
130
- assert(batch.contentSizeInBytes > 0 && batch.content.length > 0, 0x514 /* Batch needs to be non-empty */);
131
- assert(this.chunkSizeInBytes !== 0, 0x515 /* Chunk size needs to be non-zero */);
132
- assert(this.chunkSizeInBytes < this.maxBatchSizeInBytes, 0x516 /* Chunk size needs to be smaller than the max batch size */);
133
-
134
- const firstMessage = batch.content[0]; // we expect this to be the large compressed op, which needs to be split
135
- assert(firstMessage.metadata?.compressed === true || firstMessage.compression !== undefined, 0x517 /* Batch needs to be compressed */);
136
- assert((firstMessage.contents?.length ?? 0) >= this.chunkSizeInBytes, 0x518 /* First message in the batch needs to be chunkable */);
137
-
138
- const restOfMessages = batch.content.slice(1); // we expect these to be empty ops, created to reserve sequence numbers
139
- const chunks = splitOp(firstMessage, this.chunkSizeInBytes);
140
-
141
- assert(this.submitBatchFn !== undefined, 0x519 /* We don't support old loaders */);
142
- // Send the first N-1 chunks immediately
143
- for (const chunk of chunks.slice(0, -1)) {
144
- this.submitBatchFn([chunkToBatchMessage(chunk, firstMessage.referenceSequenceNumber)]);
145
- }
146
-
147
- // The last chunk will be part of the new batch and needs to
148
- // preserve the batch metadata of the original batch
149
- const lastChunk = chunkToBatchMessage(
150
- chunks[chunks.length - 1],
151
- firstMessage.referenceSequenceNumber,
152
- { batch: firstMessage.metadata?.batch });
153
-
154
- this.logger.sendPerformanceEvent({
155
- eventName: "Chunked compressed batch",
156
- length: batch.content.length,
157
- sizeInBytes: batch.contentSizeInBytes,
158
- chunks: chunks.length,
159
- chunkSizeInBytes: this.chunkSizeInBytes,
160
- });
161
-
162
- return {
163
- content: [lastChunk, ...restOfMessages],
164
- contentSizeInBytes: lastChunk.contents?.length ?? 0,
165
- };
166
- }
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
+ }
167
187
  }
168
188
 
169
189
  const chunkToBatchMessage = (
170
- chunk: IChunkedOp,
171
- referenceSequenceNumber: number,
172
- metadata: Record<string, unknown> | undefined = undefined,
190
+ chunk: IChunkedOp,
191
+ referenceSequenceNumber: number,
192
+ metadata: Record<string, unknown> | undefined = undefined,
173
193
  ): BatchMessage => {
174
- const payload: ContainerRuntimeMessage = { type: ContainerMessageType.ChunkedOp, contents: chunk };
175
- return {
176
- contents: JSON.stringify(payload),
177
- deserializedContent: payload,
178
- metadata,
179
- localOpMetadata: undefined,
180
- referenceSequenceNumber,
181
- };
182
- }
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
+ };
183
206
 
184
207
  export const splitOp = (op: BatchMessage, chunkSizeInBytes: number): IChunkedOp[] => {
185
- const chunks: IChunkedOp[] = [];
186
- assert(op.contents !== undefined && op.contents !== null, 0x51a /* We should have something to chunk */);
187
-
188
- const contentLength = op.contents.length;
189
- const chunkN = Math.floor((contentLength - 1) / chunkSizeInBytes) + 1;
190
- let offset = 0;
191
- for (let i = 1; i <= chunkN; i++) {
192
- const chunk: IChunkedOp = {
193
- chunkId: i,
194
- contents: op.contents.substr(offset, chunkSizeInBytes),
195
- originalType: op.deserializedContent.type,
196
- totalChunks: chunkN,
197
- }
198
-
199
- if (i === chunkN) {
200
- // We don't need to port these to all the chunks,
201
- // as we rebuild the original op when we process the
202
- // last chunk, therefore it is the only one that needs it.
203
- chunk.originalMetadata = op.metadata;
204
- chunk.originalCompression = op.compression;
205
- }
206
-
207
- chunks.push(chunk);
208
- offset += chunkSizeInBytes;
209
- }
210
-
211
- return chunks;
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;
212
238
  };