@fluidframework/container-runtime 2.1.0-276985 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (199) hide show
  1. package/CHANGELOG.md +4 -0
  2. package/README.md +71 -18
  3. package/api-extractor/api-extractor.current.json +5 -0
  4. package/api-extractor/api-extractor.legacy.json +1 -1
  5. package/api-extractor.json +1 -1
  6. package/api-report/container-runtime.legacy.public.api.md +9 -0
  7. package/container-runtime.test-files.tar +0 -0
  8. package/dist/blobManager/blobManager.d.ts +10 -0
  9. package/dist/blobManager/blobManager.d.ts.map +1 -1
  10. package/dist/blobManager/blobManager.js +19 -0
  11. package/dist/blobManager/blobManager.js.map +1 -1
  12. package/dist/channelCollection.d.ts +1 -1
  13. package/dist/channelCollection.d.ts.map +1 -1
  14. package/dist/channelCollection.js +40 -8
  15. package/dist/channelCollection.js.map +1 -1
  16. package/dist/containerRuntime.d.ts +14 -5
  17. package/dist/containerRuntime.d.ts.map +1 -1
  18. package/dist/containerRuntime.js +151 -99
  19. package/dist/containerRuntime.js.map +1 -1
  20. package/dist/dataStoreContext.d.ts +4 -0
  21. package/dist/dataStoreContext.d.ts.map +1 -1
  22. package/dist/dataStoreContext.js +9 -3
  23. package/dist/dataStoreContext.js.map +1 -1
  24. package/dist/gc/garbageCollection.d.ts +1 -1
  25. package/dist/gc/garbageCollection.d.ts.map +1 -1
  26. package/dist/gc/garbageCollection.js +14 -8
  27. package/dist/gc/garbageCollection.js.map +1 -1
  28. package/dist/gc/gcDefinitions.d.ts +4 -2
  29. package/dist/gc/gcDefinitions.d.ts.map +1 -1
  30. package/dist/gc/gcDefinitions.js.map +1 -1
  31. package/dist/gc/gcHelpers.d.ts.map +1 -1
  32. package/dist/gc/gcHelpers.js +12 -0
  33. package/dist/gc/gcHelpers.js.map +1 -1
  34. package/dist/gc/gcTelemetry.d.ts +3 -2
  35. package/dist/gc/gcTelemetry.d.ts.map +1 -1
  36. package/dist/gc/gcTelemetry.js +6 -6
  37. package/dist/gc/gcTelemetry.js.map +1 -1
  38. package/dist/legacy.d.ts +1 -1
  39. package/dist/metadata.d.ts +7 -1
  40. package/dist/metadata.d.ts.map +1 -1
  41. package/dist/metadata.js +6 -0
  42. package/dist/metadata.js.map +1 -1
  43. package/dist/opLifecycle/batchManager.d.ts +8 -1
  44. package/dist/opLifecycle/batchManager.d.ts.map +1 -1
  45. package/dist/opLifecycle/batchManager.js +37 -16
  46. package/dist/opLifecycle/batchManager.js.map +1 -1
  47. package/dist/opLifecycle/definitions.d.ts +1 -1
  48. package/dist/opLifecycle/definitions.d.ts.map +1 -1
  49. package/dist/opLifecycle/definitions.js.map +1 -1
  50. package/dist/opLifecycle/index.d.ts +2 -2
  51. package/dist/opLifecycle/index.d.ts.map +1 -1
  52. package/dist/opLifecycle/index.js +3 -1
  53. package/dist/opLifecycle/index.js.map +1 -1
  54. package/dist/opLifecycle/opCompressor.d.ts.map +1 -1
  55. package/dist/opLifecycle/opCompressor.js +12 -8
  56. package/dist/opLifecycle/opCompressor.js.map +1 -1
  57. package/dist/opLifecycle/opGroupingManager.d.ts.map +1 -1
  58. package/dist/opLifecycle/opGroupingManager.js +14 -11
  59. package/dist/opLifecycle/opGroupingManager.js.map +1 -1
  60. package/dist/opLifecycle/opSplitter.d.ts.map +1 -1
  61. package/dist/opLifecycle/opSplitter.js +11 -6
  62. package/dist/opLifecycle/opSplitter.js.map +1 -1
  63. package/dist/opLifecycle/outbox.d.ts +22 -6
  64. package/dist/opLifecycle/outbox.d.ts.map +1 -1
  65. package/dist/opLifecycle/outbox.js +43 -21
  66. package/dist/opLifecycle/outbox.js.map +1 -1
  67. package/dist/opLifecycle/remoteMessageProcessor.d.ts +10 -8
  68. package/dist/opLifecycle/remoteMessageProcessor.d.ts.map +1 -1
  69. package/dist/opLifecycle/remoteMessageProcessor.js +39 -15
  70. package/dist/opLifecycle/remoteMessageProcessor.js.map +1 -1
  71. package/dist/packageVersion.d.ts +1 -1
  72. package/dist/packageVersion.d.ts.map +1 -1
  73. package/dist/packageVersion.js +1 -1
  74. package/dist/packageVersion.js.map +1 -1
  75. package/dist/pendingStateManager.d.ts +37 -13
  76. package/dist/pendingStateManager.d.ts.map +1 -1
  77. package/dist/pendingStateManager.js +95 -45
  78. package/dist/pendingStateManager.js.map +1 -1
  79. package/dist/public.d.ts +1 -1
  80. package/dist/scheduleManager.js +4 -0
  81. package/dist/scheduleManager.js.map +1 -1
  82. package/dist/summary/summarizerNode/summarizerNodeUtils.d.ts.map +1 -1
  83. package/dist/summary/summarizerNode/summarizerNodeUtils.js +2 -0
  84. package/dist/summary/summarizerNode/summarizerNodeUtils.js.map +1 -1
  85. package/dist/summary/summaryFormat.d.ts.map +1 -1
  86. package/dist/summary/summaryFormat.js +4 -1
  87. package/dist/summary/summaryFormat.js.map +1 -1
  88. package/internal.d.ts +1 -1
  89. package/legacy.d.ts +1 -1
  90. package/lib/blobManager/blobManager.d.ts +10 -0
  91. package/lib/blobManager/blobManager.d.ts.map +1 -1
  92. package/lib/blobManager/blobManager.js +19 -0
  93. package/lib/blobManager/blobManager.js.map +1 -1
  94. package/lib/channelCollection.d.ts +1 -1
  95. package/lib/channelCollection.d.ts.map +1 -1
  96. package/lib/channelCollection.js +40 -8
  97. package/lib/channelCollection.js.map +1 -1
  98. package/lib/containerRuntime.d.ts +14 -5
  99. package/lib/containerRuntime.d.ts.map +1 -1
  100. package/lib/containerRuntime.js +152 -100
  101. package/lib/containerRuntime.js.map +1 -1
  102. package/lib/dataStoreContext.d.ts +4 -0
  103. package/lib/dataStoreContext.d.ts.map +1 -1
  104. package/lib/dataStoreContext.js +10 -4
  105. package/lib/dataStoreContext.js.map +1 -1
  106. package/lib/gc/garbageCollection.d.ts +1 -1
  107. package/lib/gc/garbageCollection.d.ts.map +1 -1
  108. package/lib/gc/garbageCollection.js +14 -8
  109. package/lib/gc/garbageCollection.js.map +1 -1
  110. package/lib/gc/gcDefinitions.d.ts +4 -2
  111. package/lib/gc/gcDefinitions.d.ts.map +1 -1
  112. package/lib/gc/gcDefinitions.js.map +1 -1
  113. package/lib/gc/gcHelpers.d.ts.map +1 -1
  114. package/lib/gc/gcHelpers.js +12 -0
  115. package/lib/gc/gcHelpers.js.map +1 -1
  116. package/lib/gc/gcTelemetry.d.ts +3 -2
  117. package/lib/gc/gcTelemetry.d.ts.map +1 -1
  118. package/lib/gc/gcTelemetry.js +6 -6
  119. package/lib/gc/gcTelemetry.js.map +1 -1
  120. package/lib/legacy.d.ts +1 -1
  121. package/lib/metadata.d.ts +7 -1
  122. package/lib/metadata.d.ts.map +1 -1
  123. package/lib/metadata.js +4 -1
  124. package/lib/metadata.js.map +1 -1
  125. package/lib/opLifecycle/batchManager.d.ts +8 -1
  126. package/lib/opLifecycle/batchManager.d.ts.map +1 -1
  127. package/lib/opLifecycle/batchManager.js +35 -15
  128. package/lib/opLifecycle/batchManager.js.map +1 -1
  129. package/lib/opLifecycle/definitions.d.ts +1 -1
  130. package/lib/opLifecycle/definitions.d.ts.map +1 -1
  131. package/lib/opLifecycle/definitions.js.map +1 -1
  132. package/lib/opLifecycle/index.d.ts +2 -2
  133. package/lib/opLifecycle/index.d.ts.map +1 -1
  134. package/lib/opLifecycle/index.js +2 -2
  135. package/lib/opLifecycle/index.js.map +1 -1
  136. package/lib/opLifecycle/opCompressor.d.ts.map +1 -1
  137. package/lib/opLifecycle/opCompressor.js +12 -8
  138. package/lib/opLifecycle/opCompressor.js.map +1 -1
  139. package/lib/opLifecycle/opGroupingManager.d.ts.map +1 -1
  140. package/lib/opLifecycle/opGroupingManager.js +14 -11
  141. package/lib/opLifecycle/opGroupingManager.js.map +1 -1
  142. package/lib/opLifecycle/opSplitter.d.ts.map +1 -1
  143. package/lib/opLifecycle/opSplitter.js +11 -6
  144. package/lib/opLifecycle/opSplitter.js.map +1 -1
  145. package/lib/opLifecycle/outbox.d.ts +22 -6
  146. package/lib/opLifecycle/outbox.d.ts.map +1 -1
  147. package/lib/opLifecycle/outbox.js +44 -22
  148. package/lib/opLifecycle/outbox.js.map +1 -1
  149. package/lib/opLifecycle/remoteMessageProcessor.d.ts +10 -8
  150. package/lib/opLifecycle/remoteMessageProcessor.d.ts.map +1 -1
  151. package/lib/opLifecycle/remoteMessageProcessor.js +37 -14
  152. package/lib/opLifecycle/remoteMessageProcessor.js.map +1 -1
  153. package/lib/packageVersion.d.ts +1 -1
  154. package/lib/packageVersion.d.ts.map +1 -1
  155. package/lib/packageVersion.js +1 -1
  156. package/lib/packageVersion.js.map +1 -1
  157. package/lib/pendingStateManager.d.ts +37 -13
  158. package/lib/pendingStateManager.d.ts.map +1 -1
  159. package/lib/pendingStateManager.js +95 -45
  160. package/lib/pendingStateManager.js.map +1 -1
  161. package/lib/public.d.ts +1 -1
  162. package/lib/scheduleManager.js +4 -0
  163. package/lib/scheduleManager.js.map +1 -1
  164. package/lib/summary/summarizerNode/summarizerNodeUtils.d.ts.map +1 -1
  165. package/lib/summary/summarizerNode/summarizerNodeUtils.js +2 -0
  166. package/lib/summary/summarizerNode/summarizerNodeUtils.js.map +1 -1
  167. package/lib/summary/summaryFormat.d.ts.map +1 -1
  168. package/lib/summary/summaryFormat.js +4 -1
  169. package/lib/summary/summaryFormat.js.map +1 -1
  170. package/package.json +46 -31
  171. package/src/blobManager/blobManager.ts +19 -0
  172. package/src/channelCollection.ts +48 -11
  173. package/src/containerRuntime.ts +203 -133
  174. package/src/dataStoreContext.ts +22 -4
  175. package/src/gc/garbageCollection.ts +15 -10
  176. package/src/gc/gcDefinitions.ts +7 -2
  177. package/src/gc/gcHelpers.ts +18 -6
  178. package/src/gc/gcTelemetry.ts +20 -8
  179. package/src/metadata.ts +11 -1
  180. package/src/opLifecycle/README.md +0 -8
  181. package/src/opLifecycle/batchManager.ts +49 -16
  182. package/src/opLifecycle/definitions.ts +1 -1
  183. package/src/opLifecycle/index.ts +13 -2
  184. package/src/opLifecycle/opCompressor.ts +12 -8
  185. package/src/opLifecycle/opGroupingManager.ts +14 -11
  186. package/src/opLifecycle/opSplitter.ts +10 -6
  187. package/src/opLifecycle/outbox.ts +64 -26
  188. package/src/opLifecycle/remoteMessageProcessor.ts +56 -17
  189. package/src/packageVersion.ts +1 -1
  190. package/src/pendingStateManager.ts +173 -74
  191. package/src/scheduleManager.ts +6 -2
  192. package/src/summary/README.md +81 -0
  193. package/src/summary/summarizerNode/summarizerNodeUtils.ts +3 -1
  194. package/src/summary/summaryFormat.ts +3 -1
  195. package/src/summary/summaryFormats.md +69 -8
  196. package/tsconfig.json +0 -1
  197. package/src/summary/images/appTree.png +0 -0
  198. package/src/summary/images/protocolAndAppTree.png +0 -0
  199. package/src/summary/images/summaryTree.png +0 -0
@@ -24,7 +24,7 @@ export interface IBatch<TMessages extends BatchMessage[] = BatchMessage[]> {
24
24
  /**
25
25
  * All the messages in the batch
26
26
  */
27
- readonly content: TMessages;
27
+ readonly messages: TMessages;
28
28
  /**
29
29
  * The reference sequence number for the batch
30
30
  */
@@ -1 +1 @@
1
- {"version":3,"file":"definitions.d.ts","sourceRoot":"","sources":["../../src/opLifecycle/definitions.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,gDAAgD,CAAC;AAE/E,OAAO,EAAE,qBAAqB,EAAE,MAAM,wBAAwB,CAAC;AAE/D;;GAEG;AACH,MAAM,MAAM,YAAY,GAAG,aAAa,GAAG;IAC1C,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,uBAAuB,EAAE,MAAM,CAAC;IAChC,WAAW,CAAC,EAAE,qBAAqB,CAAC;CACpC,CAAC;AAEF;;GAEG;AACH,MAAM,WAAW,MAAM,CAAC,SAAS,SAAS,YAAY,EAAE,GAAG,YAAY,EAAE;IACxE;;;OAGG;IACH,QAAQ,CAAC,kBAAkB,EAAE,MAAM,CAAC;IACpC;;OAEG;IACH,QAAQ,CAAC,OAAO,EAAE,SAAS,CAAC;IAC5B;;OAEG;IACH,QAAQ,CAAC,uBAAuB,EAAE,MAAM,GAAG,SAAS,CAAC;IACrD;;;;;;;;;OASG;IACH,QAAQ,CAAC,eAAe,CAAC,EAAE,OAAO,CAAC;CACnC;AAED,MAAM,WAAW,gBAAgB;IAChC,QAAQ,EAAE,CAAC,MAAM,EAAE,CAAC,OAAO,EAAE,YAAY,KAAK,IAAI,KAAK,IAAI,CAAC;CAC5D;AAED;;GAEG;AACH,MAAM,WAAW,UAAU;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,gBAAgB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC3C,mBAAmB,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED;;;;;;GAMG;AACH,MAAM,MAAM,eAAe,GAAG,WAAW,GAAG,SAAS,GAAG,UAAU,CAAC"}
1
+ {"version":3,"file":"definitions.d.ts","sourceRoot":"","sources":["../../src/opLifecycle/definitions.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,gDAAgD,CAAC;AAE/E,OAAO,EAAE,qBAAqB,EAAE,MAAM,wBAAwB,CAAC;AAE/D;;GAEG;AACH,MAAM,MAAM,YAAY,GAAG,aAAa,GAAG;IAC1C,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,uBAAuB,EAAE,MAAM,CAAC;IAChC,WAAW,CAAC,EAAE,qBAAqB,CAAC;CACpC,CAAC;AAEF;;GAEG;AACH,MAAM,WAAW,MAAM,CAAC,SAAS,SAAS,YAAY,EAAE,GAAG,YAAY,EAAE;IACxE;;;OAGG;IACH,QAAQ,CAAC,kBAAkB,EAAE,MAAM,CAAC;IACpC;;OAEG;IACH,QAAQ,CAAC,QAAQ,EAAE,SAAS,CAAC;IAC7B;;OAEG;IACH,QAAQ,CAAC,uBAAuB,EAAE,MAAM,GAAG,SAAS,CAAC;IACrD;;;;;;;;;OASG;IACH,QAAQ,CAAC,eAAe,CAAC,EAAE,OAAO,CAAC;CACnC;AAED,MAAM,WAAW,gBAAgB;IAChC,QAAQ,EAAE,CAAC,MAAM,EAAE,CAAC,OAAO,EAAE,YAAY,KAAK,IAAI,KAAK,IAAI,CAAC;CAC5D;AAED;;GAEG;AACH,MAAM,WAAW,UAAU;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,gBAAgB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC3C,mBAAmB,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED;;;;;;GAMG;AACH,MAAM,MAAM,eAAe,GAAG,WAAW,GAAG,SAAS,GAAG,UAAU,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"definitions.js","sourceRoot":"","sources":["../../src/opLifecycle/definitions.ts"],"names":[],"mappings":"AAAA;;;GAGG","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { IBatchMessage } from \"@fluidframework/container-definitions/internal\";\n\nimport { CompressionAlgorithms } from \"../containerRuntime.js\";\n\n/**\n * Batch message type used internally by the runtime\n */\nexport type BatchMessage = IBatchMessage & {\n\tlocalOpMetadata?: unknown;\n\treferenceSequenceNumber: number;\n\tcompression?: CompressionAlgorithms;\n};\n\n/**\n * Batch interface used internally by the runtime.\n */\nexport interface IBatch<TMessages extends BatchMessage[] = BatchMessage[]> {\n\t/**\n\t * Sum of the in-memory content sizes of all messages in the batch.\n\t * If the batch is compressed, this number reflects the post-compression size.\n\t */\n\treadonly contentSizeInBytes: number;\n\t/**\n\t * All the messages in the batch\n\t */\n\treadonly content: TMessages;\n\t/**\n\t * The reference sequence number for the batch\n\t */\n\treadonly referenceSequenceNumber: number | undefined;\n\t/**\n\t * Wether or not the batch contains at least one op which was produced as the result\n\t * of processing another op. This means that the batch must be rebased before\n\t * submitted, to ensure that all ops have the same reference sequence numbers and a\n\t * consistent view of the data model. This happens when the op is created within a\n\t * 'changed' event handler of a DDS and will have a different reference sequence number\n\t * than the rest of the ops in the batch, meaning that it has a different view of the\n\t * state of the data model, therefore all ops must be resubmitted and rebased to the current\n\t * reference sequence number to be in agreement about the data model state.\n\t */\n\treadonly hasReentrantOps?: boolean;\n}\n\nexport interface IBatchCheckpoint {\n\trollback: (action: (message: BatchMessage) => void) => void;\n}\n\n/**\n * @internal\n */\nexport interface IChunkedOp {\n\tchunkId: number;\n\ttotalChunks: number;\n\tcontents: string;\n\toriginalMetadata?: Record<string, unknown>;\n\toriginalCompression?: string;\n}\n\n/**\n * The state of remote message processing:\n * `Processed` - the message can be considered processed\n * `Skipped` - the message was ignored by the processor\n * `Accepted` - the message was processed partially. Eventually, a message\n * will make the processor return `Processed`.\n */\nexport type ProcessingState = \"Processed\" | \"Skipped\" | \"Accepted\";\n"]}
1
+ {"version":3,"file":"definitions.js","sourceRoot":"","sources":["../../src/opLifecycle/definitions.ts"],"names":[],"mappings":"AAAA;;;GAGG","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { IBatchMessage } from \"@fluidframework/container-definitions/internal\";\n\nimport { CompressionAlgorithms } from \"../containerRuntime.js\";\n\n/**\n * Batch message type used internally by the runtime\n */\nexport type BatchMessage = IBatchMessage & {\n\tlocalOpMetadata?: unknown;\n\treferenceSequenceNumber: number;\n\tcompression?: CompressionAlgorithms;\n};\n\n/**\n * Batch interface used internally by the runtime.\n */\nexport interface IBatch<TMessages extends BatchMessage[] = BatchMessage[]> {\n\t/**\n\t * Sum of the in-memory content sizes of all messages in the batch.\n\t * If the batch is compressed, this number reflects the post-compression size.\n\t */\n\treadonly contentSizeInBytes: number;\n\t/**\n\t * All the messages in the batch\n\t */\n\treadonly messages: TMessages;\n\t/**\n\t * The reference sequence number for the batch\n\t */\n\treadonly referenceSequenceNumber: number | undefined;\n\t/**\n\t * Wether or not the batch contains at least one op which was produced as the result\n\t * of processing another op. This means that the batch must be rebased before\n\t * submitted, to ensure that all ops have the same reference sequence numbers and a\n\t * consistent view of the data model. This happens when the op is created within a\n\t * 'changed' event handler of a DDS and will have a different reference sequence number\n\t * than the rest of the ops in the batch, meaning that it has a different view of the\n\t * state of the data model, therefore all ops must be resubmitted and rebased to the current\n\t * reference sequence number to be in agreement about the data model state.\n\t */\n\treadonly hasReentrantOps?: boolean;\n}\n\nexport interface IBatchCheckpoint {\n\trollback: (action: (message: BatchMessage) => void) => void;\n}\n\n/**\n * @internal\n */\nexport interface IChunkedOp {\n\tchunkId: number;\n\ttotalChunks: number;\n\tcontents: string;\n\toriginalMetadata?: Record<string, unknown>;\n\toriginalCompression?: string;\n}\n\n/**\n * The state of remote message processing:\n * `Processed` - the message can be considered processed\n * `Skipped` - the message was ignored by the processor\n * `Accepted` - the message was processed partially. Eventually, a message\n * will make the processor return `Processed`.\n */\nexport type ProcessingState = \"Processed\" | \"Skipped\" | \"Accepted\";\n"]}
@@ -2,12 +2,12 @@
2
2
  * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
3
  * Licensed under the MIT License.
4
4
  */
5
- export { BatchManager, estimateSocketSize, BatchSequenceNumbers } from "./batchManager.js";
5
+ export { BatchId, BatchManager, BatchSequenceNumbers, estimateSocketSize, generateBatchId, IBatchManagerOptions, } from "./batchManager.js";
6
6
  export { BatchMessage, IBatch, IBatchCheckpoint, IChunkedOp } from "./definitions.js";
7
7
  export { Outbox, getLongStack } from "./outbox.js";
8
8
  export { OpCompressor } from "./opCompressor.js";
9
9
  export { OpDecompressor } from "./opDecompressor.js";
10
10
  export { OpSplitter, splitOp, isChunkedMessage } from "./opSplitter.js";
11
- export { RemoteMessageProcessor, unpackRuntimeMessage } from "./remoteMessageProcessor.js";
11
+ export { ensureContentsDeserialized, RemoteMessageProcessor, unpackRuntimeMessage, } from "./remoteMessageProcessor.js";
12
12
  export { OpGroupingManager, OpGroupingManagerConfig, isGroupedBatch, } from "./opGroupingManager.js";
13
13
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/opLifecycle/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,YAAY,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AAC3F,OAAO,EAAE,YAAY,EAAE,MAAM,EAAE,gBAAgB,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AACtF,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACxE,OAAO,EAAE,sBAAsB,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AAC3F,OAAO,EACN,iBAAiB,EACjB,uBAAuB,EACvB,cAAc,GACd,MAAM,wBAAwB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/opLifecycle/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EACN,OAAO,EACP,YAAY,EACZ,oBAAoB,EACpB,kBAAkB,EAClB,eAAe,EACf,oBAAoB,GACpB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,YAAY,EAAE,MAAM,EAAE,gBAAgB,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AACtF,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACxE,OAAO,EACN,0BAA0B,EAC1B,sBAAsB,EACtB,oBAAoB,GACpB,MAAM,6BAA6B,CAAC;AACrC,OAAO,EACN,iBAAiB,EACjB,uBAAuB,EACvB,cAAc,GACd,MAAM,wBAAwB,CAAC"}
@@ -2,11 +2,11 @@
2
2
  * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
3
  * Licensed under the MIT License.
4
4
  */
5
- export { BatchManager, estimateSocketSize } from "./batchManager.js";
5
+ export { BatchManager, estimateSocketSize, generateBatchId, } from "./batchManager.js";
6
6
  export { Outbox, getLongStack } from "./outbox.js";
7
7
  export { OpCompressor } from "./opCompressor.js";
8
8
  export { OpDecompressor } from "./opDecompressor.js";
9
9
  export { OpSplitter, splitOp, isChunkedMessage } from "./opSplitter.js";
10
- export { RemoteMessageProcessor, unpackRuntimeMessage } from "./remoteMessageProcessor.js";
10
+ export { ensureContentsDeserialized, RemoteMessageProcessor, unpackRuntimeMessage, } from "./remoteMessageProcessor.js";
11
11
  export { OpGroupingManager, isGroupedBatch, } from "./opGroupingManager.js";
12
12
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/opLifecycle/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,YAAY,EAAE,kBAAkB,EAAwB,MAAM,mBAAmB,CAAC;AAE3F,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACxE,OAAO,EAAE,sBAAsB,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AAC3F,OAAO,EACN,iBAAiB,EAEjB,cAAc,GACd,MAAM,wBAAwB,CAAC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nexport { BatchManager, estimateSocketSize, BatchSequenceNumbers } from \"./batchManager.js\";\nexport { BatchMessage, IBatch, IBatchCheckpoint, IChunkedOp } from \"./definitions.js\";\nexport { Outbox, getLongStack } from \"./outbox.js\";\nexport { OpCompressor } from \"./opCompressor.js\";\nexport { OpDecompressor } from \"./opDecompressor.js\";\nexport { OpSplitter, splitOp, isChunkedMessage } from \"./opSplitter.js\";\nexport { RemoteMessageProcessor, unpackRuntimeMessage } from \"./remoteMessageProcessor.js\";\nexport {\n\tOpGroupingManager,\n\tOpGroupingManagerConfig,\n\tisGroupedBatch,\n} from \"./opGroupingManager.js\";\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/opLifecycle/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAEN,YAAY,EAEZ,kBAAkB,EAClB,eAAe,GAEf,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACxE,OAAO,EACN,0BAA0B,EAC1B,sBAAsB,EACtB,oBAAoB,GACpB,MAAM,6BAA6B,CAAC;AACrC,OAAO,EACN,iBAAiB,EAEjB,cAAc,GACd,MAAM,wBAAwB,CAAC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nexport {\n\tBatchId,\n\tBatchManager,\n\tBatchSequenceNumbers,\n\testimateSocketSize,\n\tgenerateBatchId,\n\tIBatchManagerOptions,\n} from \"./batchManager.js\";\nexport { BatchMessage, IBatch, IBatchCheckpoint, IChunkedOp } from \"./definitions.js\";\nexport { Outbox, getLongStack } from \"./outbox.js\";\nexport { OpCompressor } from \"./opCompressor.js\";\nexport { OpDecompressor } from \"./opDecompressor.js\";\nexport { OpSplitter, splitOp, isChunkedMessage } from \"./opSplitter.js\";\nexport {\n\tensureContentsDeserialized,\n\tRemoteMessageProcessor,\n\tunpackRuntimeMessage,\n} from \"./remoteMessageProcessor.js\";\nexport {\n\tOpGroupingManager,\n\tOpGroupingManagerConfig,\n\tisGroupedBatch,\n} from \"./opGroupingManager.js\";\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"opCompressor.d.ts","sourceRoot":"","sources":["../../src/opLifecycle/opCompressor.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAE,oBAAoB,EAAE,MAAM,iCAAiC,CAAC;AAQvE,OAAO,EAAgB,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAExD;;;;GAIG;AACH,qBAAa,YAAY;IACxB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC;gBAEZ,MAAM,EAAE,oBAAoB;IAIxC;;;;;;OAMG;IACI,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM;IAiD3C;;OAEG;IACH,OAAO,CAAC,sBAAsB;CAuB9B"}
1
+ {"version":3,"file":"opCompressor.d.ts","sourceRoot":"","sources":["../../src/opLifecycle/opCompressor.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAE,oBAAoB,EAAE,MAAM,iCAAiC,CAAC;AAQvE,OAAO,EAAgB,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAExD;;;;GAIG;AACH,qBAAa,YAAY;IACxB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC;gBAEZ,MAAM,EAAE,oBAAoB;IAIxC;;;;;;OAMG;IACI,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM;IAqD3C;;OAEG;IACH,OAAO,CAAC,sBAAsB;CAuB9B"}
@@ -25,7 +25,7 @@ export class OpCompressor {
25
25
  * @returns A batch of the same length as the input batch, containing a single compressed message followed by empty placeholders
26
26
  */
27
27
  compressBatch(batch) {
28
- assert(batch.contentSizeInBytes > 0 && batch.content.length > 0, 0x5a4 /* Batch should not be empty */);
28
+ assert(batch.contentSizeInBytes > 0 && batch.messages.length > 0, 0x5a4 /* Batch should not be empty */);
29
29
  const compressionStart = Date.now();
30
30
  const contentsAsBuffer = new TextEncoder().encode(this.serializeBatchContents(batch));
31
31
  const compressedContents = compress(contentsAsBuffer);
@@ -33,13 +33,17 @@ export class OpCompressor {
33
33
  const duration = Date.now() - compressionStart;
34
34
  const messages = [];
35
35
  messages.push({
36
- ...batch.content[0],
36
+ // Non null asserting here because of the length assert above
37
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
38
+ ...batch.messages[0],
37
39
  contents: JSON.stringify({ packedContents: compressedContent }),
38
- metadata: batch.content[0].metadata,
40
+ // Non null asserting here because of the length assert above
41
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
42
+ metadata: batch.messages[0].metadata,
39
43
  compression: CompressionAlgorithms.lz4,
40
44
  });
41
45
  // Add empty placeholder messages to reserve the sequence numbers
42
- for (const message of batch.content.slice(1)) {
46
+ for (const message of batch.messages.slice(1)) {
43
47
  messages.push({
44
48
  localOpMetadata: message.localOpMetadata,
45
49
  metadata: message.metadata,
@@ -48,7 +52,7 @@ export class OpCompressor {
48
52
  }
49
53
  const compressedBatch = {
50
54
  contentSizeInBytes: compressedContent.length,
51
- content: messages,
55
+ messages,
52
56
  referenceSequenceNumber: batch.referenceSequenceNumber,
53
57
  };
54
58
  if (batch.contentSizeInBytes > 200000) {
@@ -57,7 +61,7 @@ export class OpCompressor {
57
61
  duration,
58
62
  sizeBeforeCompression: batch.contentSizeInBytes,
59
63
  sizeAfterCompression: compressedBatch.contentSizeInBytes,
60
- opCount: compressedBatch.content.length,
64
+ opCount: compressedBatch.messages.length,
61
65
  socketSize: estimateSocketSize(compressedBatch),
62
66
  });
63
67
  }
@@ -69,7 +73,7 @@ export class OpCompressor {
69
73
  serializeBatchContents(batch) {
70
74
  try {
71
75
  // Yields a valid JSON array, since each message.contents is already serialized to JSON
72
- return `[${batch.content.map((message) => message.contents).join(",")}]`;
76
+ return `[${batch.messages.map(({ contents }) => contents).join(",")}]`;
73
77
  }
74
78
  catch (e) {
75
79
  if (e.message === "Invalid string length") {
@@ -79,7 +83,7 @@ export class OpCompressor {
79
83
  this.logger.sendErrorEvent({
80
84
  eventName: "BatchTooLarge",
81
85
  size: batch.contentSizeInBytes,
82
- length: batch.content.length,
86
+ length: batch.messages.length,
83
87
  }, error);
84
88
  throw error;
85
89
  }
@@ -1 +1 @@
1
- {"version":3,"file":"opCompressor.js","sourceRoot":"","sources":["../../src/opLifecycle/opCompressor.ts"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { IsoBuffer } from \"@fluid-internal/client-utils\";\nimport { ITelemetryBaseLogger } from \"@fluidframework/core-interfaces\";\nimport { assert } from \"@fluidframework/core-utils/internal\";\nimport { UsageError, createChildLogger } from \"@fluidframework/telemetry-utils/internal\";\nimport { compress } from \"lz4js\";\n\nimport { CompressionAlgorithms } from \"../containerRuntime.js\";\n\nimport { estimateSocketSize } from \"./batchManager.js\";\nimport { BatchMessage, IBatch } from \"./definitions.js\";\n\n/**\n * Compresses batches of ops. It generates a single compressed op that contains\n * the contents of each op in the batch. It then submits empty ops for each original\n * op to reserve sequence numbers.\n */\nexport class OpCompressor {\n\tprivate readonly logger;\n\n\tconstructor(logger: ITelemetryBaseLogger) {\n\t\tthis.logger = createChildLogger({ logger, namespace: \"OpCompressor\" });\n\t}\n\n\t/**\n\t * Combines the contents of the batch into a single JSON string and compresses it, putting\n\t * the resulting string as the first message of the batch. The rest of the messages are\n\t * empty placeholders to reserve sequence numbers.\n\t * @param batch - The batch to compress\n\t * @returns A batch of the same length as the input batch, containing a single compressed message followed by empty placeholders\n\t */\n\tpublic compressBatch(batch: IBatch): IBatch {\n\t\tassert(\n\t\t\tbatch.contentSizeInBytes > 0 && batch.content.length > 0,\n\t\t\t0x5a4 /* Batch should not be empty */,\n\t\t);\n\n\t\tconst compressionStart = Date.now();\n\t\tconst contentsAsBuffer = new TextEncoder().encode(this.serializeBatchContents(batch));\n\t\tconst compressedContents = compress(contentsAsBuffer);\n\t\tconst compressedContent = IsoBuffer.from(compressedContents).toString(\"base64\");\n\t\tconst duration = Date.now() - compressionStart;\n\n\t\tconst messages: BatchMessage[] = [];\n\t\tmessages.push({\n\t\t\t...batch.content[0],\n\t\t\tcontents: JSON.stringify({ packedContents: compressedContent }),\n\t\t\tmetadata: batch.content[0].metadata,\n\t\t\tcompression: CompressionAlgorithms.lz4,\n\t\t});\n\n\t\t// Add empty placeholder messages to reserve the sequence numbers\n\t\tfor (const message of batch.content.slice(1)) {\n\t\t\tmessages.push({\n\t\t\t\tlocalOpMetadata: message.localOpMetadata,\n\t\t\t\tmetadata: message.metadata,\n\t\t\t\treferenceSequenceNumber: message.referenceSequenceNumber,\n\t\t\t});\n\t\t}\n\n\t\tconst compressedBatch: IBatch = {\n\t\t\tcontentSizeInBytes: compressedContent.length,\n\t\t\tcontent: messages,\n\t\t\treferenceSequenceNumber: batch.referenceSequenceNumber,\n\t\t};\n\n\t\tif (batch.contentSizeInBytes > 200000) {\n\t\t\tthis.logger.sendPerformanceEvent({\n\t\t\t\teventName: \"CompressedBatch\",\n\t\t\t\tduration,\n\t\t\t\tsizeBeforeCompression: batch.contentSizeInBytes,\n\t\t\t\tsizeAfterCompression: compressedBatch.contentSizeInBytes,\n\t\t\t\topCount: compressedBatch.content.length,\n\t\t\t\tsocketSize: estimateSocketSize(compressedBatch),\n\t\t\t});\n\t\t}\n\n\t\treturn compressedBatch;\n\t}\n\n\t/**\n\t * Combine the batch's content strings into a single JSON string (a serialized array)\n\t */\n\tprivate serializeBatchContents(batch: IBatch): string {\n\t\ttry {\n\t\t\t// Yields a valid JSON array, since each message.contents is already serialized to JSON\n\t\t\treturn `[${batch.content.map((message) => message.contents).join(\",\")}]`;\n\t\t} catch (e: any) {\n\t\t\tif (e.message === \"Invalid string length\") {\n\t\t\t\t// This is how JSON.stringify signals that\n\t\t\t\t// the content size exceeds its capacity\n\t\t\t\tconst error = new UsageError(\"Payload too large\");\n\t\t\t\tthis.logger.sendErrorEvent(\n\t\t\t\t\t{\n\t\t\t\t\t\teventName: \"BatchTooLarge\",\n\t\t\t\t\t\tsize: batch.contentSizeInBytes,\n\t\t\t\t\t\tlength: batch.content.length,\n\t\t\t\t\t},\n\t\t\t\t\terror,\n\t\t\t\t);\n\t\t\t\tthrow error;\n\t\t\t}\n\n\t\t\tthrow e;\n\t\t}\n\t}\n}\n"]}
1
+ {"version":3,"file":"opCompressor.js","sourceRoot":"","sources":["../../src/opLifecycle/opCompressor.ts"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { IsoBuffer } from \"@fluid-internal/client-utils\";\nimport { ITelemetryBaseLogger } from \"@fluidframework/core-interfaces\";\nimport { assert } from \"@fluidframework/core-utils/internal\";\nimport { UsageError, createChildLogger } from \"@fluidframework/telemetry-utils/internal\";\nimport { compress } from \"lz4js\";\n\nimport { CompressionAlgorithms } from \"../containerRuntime.js\";\n\nimport { estimateSocketSize } from \"./batchManager.js\";\nimport { BatchMessage, IBatch } from \"./definitions.js\";\n\n/**\n * Compresses batches of ops. It generates a single compressed op that contains\n * the contents of each op in the batch. It then submits empty ops for each original\n * op to reserve sequence numbers.\n */\nexport class OpCompressor {\n\tprivate readonly logger;\n\n\tconstructor(logger: ITelemetryBaseLogger) {\n\t\tthis.logger = createChildLogger({ logger, namespace: \"OpCompressor\" });\n\t}\n\n\t/**\n\t * Combines the contents of the batch into a single JSON string and compresses it, putting\n\t * the resulting string as the first message of the batch. The rest of the messages are\n\t * empty placeholders to reserve sequence numbers.\n\t * @param batch - The batch to compress\n\t * @returns A batch of the same length as the input batch, containing a single compressed message followed by empty placeholders\n\t */\n\tpublic compressBatch(batch: IBatch): IBatch {\n\t\tassert(\n\t\t\tbatch.contentSizeInBytes > 0 && batch.messages.length > 0,\n\t\t\t0x5a4 /* Batch should not be empty */,\n\t\t);\n\n\t\tconst compressionStart = Date.now();\n\t\tconst contentsAsBuffer = new TextEncoder().encode(this.serializeBatchContents(batch));\n\t\tconst compressedContents = compress(contentsAsBuffer);\n\t\tconst compressedContent = IsoBuffer.from(compressedContents).toString(\"base64\");\n\t\tconst duration = Date.now() - compressionStart;\n\n\t\tconst messages: BatchMessage[] = [];\n\t\tmessages.push({\n\t\t\t// Non null asserting here because of the length assert above\n\t\t\t// eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n\t\t\t...batch.messages[0]!,\n\t\t\tcontents: JSON.stringify({ packedContents: compressedContent }),\n\t\t\t// Non null asserting here because of the length assert above\n\t\t\t// eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n\t\t\tmetadata: batch.messages[0]!.metadata,\n\t\t\tcompression: CompressionAlgorithms.lz4,\n\t\t});\n\n\t\t// Add empty placeholder messages to reserve the sequence numbers\n\t\tfor (const message of batch.messages.slice(1)) {\n\t\t\tmessages.push({\n\t\t\t\tlocalOpMetadata: message.localOpMetadata,\n\t\t\t\tmetadata: message.metadata,\n\t\t\t\treferenceSequenceNumber: message.referenceSequenceNumber,\n\t\t\t});\n\t\t}\n\n\t\tconst compressedBatch: IBatch = {\n\t\t\tcontentSizeInBytes: compressedContent.length,\n\t\t\tmessages,\n\t\t\treferenceSequenceNumber: batch.referenceSequenceNumber,\n\t\t};\n\n\t\tif (batch.contentSizeInBytes > 200000) {\n\t\t\tthis.logger.sendPerformanceEvent({\n\t\t\t\teventName: \"CompressedBatch\",\n\t\t\t\tduration,\n\t\t\t\tsizeBeforeCompression: batch.contentSizeInBytes,\n\t\t\t\tsizeAfterCompression: compressedBatch.contentSizeInBytes,\n\t\t\t\topCount: compressedBatch.messages.length,\n\t\t\t\tsocketSize: estimateSocketSize(compressedBatch),\n\t\t\t});\n\t\t}\n\n\t\treturn compressedBatch;\n\t}\n\n\t/**\n\t * Combine the batch's content strings into a single JSON string (a serialized array)\n\t */\n\tprivate serializeBatchContents(batch: IBatch): string {\n\t\ttry {\n\t\t\t// Yields a valid JSON array, since each message.contents is already serialized to JSON\n\t\t\treturn `[${batch.messages.map(({ contents }) => contents).join(\",\")}]`;\n\t\t} catch (e: any) {\n\t\t\tif (e.message === \"Invalid string length\") {\n\t\t\t\t// This is how JSON.stringify signals that\n\t\t\t\t// the content size exceeds its capacity\n\t\t\t\tconst error = new UsageError(\"Payload too large\");\n\t\t\t\tthis.logger.sendErrorEvent(\n\t\t\t\t\t{\n\t\t\t\t\t\teventName: \"BatchTooLarge\",\n\t\t\t\t\t\tsize: batch.contentSizeInBytes,\n\t\t\t\t\t\tlength: batch.messages.length,\n\t\t\t\t\t},\n\t\t\t\t\terror,\n\t\t\t\t);\n\t\t\t\tthrow error;\n\t\t\t}\n\n\t\t\tthrow e;\n\t\t}\n\t}\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"opGroupingManager.d.ts","sourceRoot":"","sources":["../../src/opLifecycle/opGroupingManager.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,oBAAoB,EAAE,MAAM,iCAAiC,CAAC;AAEvE,OAAO,EAAE,yBAAyB,EAAE,MAAM,6CAA6C,CAAC;AAGxF,OAAO,EAAE,MAAM,EAAE,KAAK,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAoB7D,wBAAgB,cAAc,CAAC,EAAE,EAAE,yBAAyB,GAAG,OAAO,CAErE;AAED,MAAM,WAAW,uBAAuB;IACvC,QAAQ,CAAC,sBAAsB,EAAE,OAAO,CAAC;IACzC,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,6BAA6B,EAAE,OAAO,CAAC;CAChD;AAED,qBAAa,iBAAiB;IAK5B,OAAO,CAAC,QAAQ,CAAC,MAAM;IAJxB,MAAM,CAAC,QAAQ,CAAC,cAAc,kBAAkB;IAChD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC;gBAGN,MAAM,EAAE,uBAAuB,EAChD,MAAM,EAAE,oBAAoB;IAK7B;;;;;;OAMG;IACI,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAAC,CAAC,YAAY,CAAC,CAAC;IA2CjD,SAAS,CAAC,EAAE,EAAE,yBAAyB,GAAG,yBAAyB,EAAE;IAcrE,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO;CAU1C"}
1
+ {"version":3,"file":"opGroupingManager.d.ts","sourceRoot":"","sources":["../../src/opLifecycle/opGroupingManager.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,oBAAoB,EAAE,MAAM,iCAAiC,CAAC;AAEvE,OAAO,EAAE,yBAAyB,EAAE,MAAM,6CAA6C,CAAC;AAGxF,OAAO,EAAE,MAAM,EAAE,KAAK,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAoB7D,wBAAgB,cAAc,CAAC,EAAE,EAAE,yBAAyB,GAAG,OAAO,CAErE;AAED,MAAM,WAAW,uBAAuB;IACvC,QAAQ,CAAC,sBAAsB,EAAE,OAAO,CAAC;IACzC,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,6BAA6B,EAAE,OAAO,CAAC;CAChD;AAED,qBAAa,iBAAiB;IAK5B,OAAO,CAAC,QAAQ,CAAC,MAAM;IAJxB,MAAM,CAAC,QAAQ,CAAC,cAAc,kBAAkB;IAChD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC;gBAGN,MAAM,EAAE,uBAAuB,EAChD,MAAM,EAAE,oBAAoB;IAK7B;;;;;;OAMG;IACI,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAAC,CAAC,YAAY,CAAC,CAAC;IA8CjD,SAAS,CAAC,EAAE,EAAE,yBAAyB,GAAG,yBAAyB,EAAE;IAcrE,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO;CAU1C"}
@@ -24,25 +24,26 @@ export class OpGroupingManager {
24
24
  */
25
25
  groupBatch(batch) {
26
26
  assert(this.shouldGroup(batch), 0x946 /* cannot group the provided batch */);
27
- if (batch.content.length >= 1000) {
27
+ if (batch.messages.length >= 1000) {
28
28
  this.logger.sendTelemetryEvent({
29
29
  eventName: "GroupLargeBatch",
30
- length: batch.content.length,
30
+ length: batch.messages.length,
31
31
  threshold: this.config.opCountThreshold,
32
32
  reentrant: batch.hasReentrantOps,
33
- referenceSequenceNumber: batch.content[0].referenceSequenceNumber,
33
+ // Non null asserting here because of the length check above
34
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
35
+ referenceSequenceNumber: batch.messages[0].referenceSequenceNumber,
34
36
  });
35
37
  }
36
- for (const message of batch.content) {
38
+ for (const message of batch.messages) {
37
39
  if (message.metadata) {
38
- const keys = Object.keys(message.metadata);
39
- assert(keys.length < 2, 0x5dd /* cannot group ops with metadata */);
40
- assert(keys.length === 0 || keys[0] === "batch", 0x5de /* unexpected op metadata */);
40
+ const { batch: _batch, batchId, ...rest } = message.metadata;
41
+ assert(Object.keys(rest).length === 0, 0x5dd /* cannot group ops with metadata */);
41
42
  }
42
43
  }
43
44
  const serializedContent = JSON.stringify({
44
45
  type: OpGroupingManager.groupedBatchOp,
45
- contents: batch.content.map((message) => ({
46
+ contents: batch.messages.map((message) => ({
46
47
  contents: message.contents === undefined ? undefined : JSON.parse(message.contents),
47
48
  metadata: message.metadata,
48
49
  compression: message.compression,
@@ -50,10 +51,12 @@ export class OpGroupingManager {
50
51
  });
51
52
  const groupedBatch = {
52
53
  ...batch,
53
- content: [
54
+ messages: [
54
55
  {
55
56
  metadata: undefined,
56
- referenceSequenceNumber: batch.content[0].referenceSequenceNumber,
57
+ // TODO why are we non null asserting here?
58
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
59
+ referenceSequenceNumber: batch.messages[0].referenceSequenceNumber,
57
60
  contents: serializedContent,
58
61
  },
59
62
  ],
@@ -77,7 +80,7 @@ export class OpGroupingManager {
77
80
  // Grouped batching must be enabled
78
81
  this.config.groupedBatchingEnabled &&
79
82
  // The number of ops in the batch must surpass the configured threshold
80
- batch.content.length >= this.config.opCountThreshold &&
83
+ batch.messages.length >= this.config.opCountThreshold &&
81
84
  // Support for reentrant batches must be explicitly enabled
82
85
  (this.config.reentrantBatchGroupingEnabled || batch.hasReentrantOps !== true));
83
86
  }
@@ -1 +1 @@
1
- {"version":3,"file":"opGroupingManager.js","sourceRoot":"","sources":["../../src/opLifecycle/opGroupingManager.ts"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { ITelemetryBaseLogger } from \"@fluidframework/core-interfaces\";\nimport { assert } from \"@fluidframework/core-utils/internal\";\nimport { ISequencedDocumentMessage } from \"@fluidframework/driver-definitions/internal\";\nimport { createChildLogger } from \"@fluidframework/telemetry-utils/internal\";\n\nimport { IBatch, type BatchMessage } from \"./definitions.js\";\n\n/**\n * Grouping makes assumptions about the shape of message contents. This interface codifies those assumptions, but does not validate them.\n */\ninterface IGroupedBatchMessageContents {\n\ttype: typeof OpGroupingManager.groupedBatchOp;\n\tcontents: IGroupedMessage[];\n}\n\ninterface IGroupedMessage {\n\tcontents?: unknown;\n\tmetadata?: Record<string, unknown>;\n\tcompression?: string;\n}\n\nfunction isGroupContents(opContents: any): opContents is IGroupedBatchMessageContents {\n\treturn opContents?.type === OpGroupingManager.groupedBatchOp;\n}\n\nexport function isGroupedBatch(op: ISequencedDocumentMessage): boolean {\n\treturn isGroupContents(op.contents);\n}\n\nexport interface OpGroupingManagerConfig {\n\treadonly groupedBatchingEnabled: boolean;\n\treadonly opCountThreshold: number;\n\treadonly reentrantBatchGroupingEnabled: boolean;\n}\n\nexport class OpGroupingManager {\n\tstatic readonly groupedBatchOp = \"groupedBatch\";\n\tprivate readonly logger;\n\n\tconstructor(\n\t\tprivate readonly config: OpGroupingManagerConfig,\n\t\tlogger: ITelemetryBaseLogger,\n\t) {\n\t\tthis.logger = createChildLogger({ logger, namespace: \"OpGroupingManager\" });\n\t}\n\n\t/**\n\t * Converts the given batch into a \"grouped batch\" - a batch with a single message of type \"groupedBatch\",\n\t * with contents being an array of the original batch's messages.\n\t *\n\t * @remarks - Remember that a BatchMessage has its content JSON serialized, so the incoming batch message contents\n\t * must be parsed first, and then the type and contents mentioned above are hidden in that JSON serialization.\n\t */\n\tpublic groupBatch(batch: IBatch): IBatch<[BatchMessage]> {\n\t\tassert(this.shouldGroup(batch), 0x946 /* cannot group the provided batch */);\n\n\t\tif (batch.content.length >= 1000) {\n\t\t\tthis.logger.sendTelemetryEvent({\n\t\t\t\teventName: \"GroupLargeBatch\",\n\t\t\t\tlength: batch.content.length,\n\t\t\t\tthreshold: this.config.opCountThreshold,\n\t\t\t\treentrant: batch.hasReentrantOps,\n\t\t\t\treferenceSequenceNumber: batch.content[0].referenceSequenceNumber,\n\t\t\t});\n\t\t}\n\n\t\tfor (const message of batch.content) {\n\t\t\tif (message.metadata) {\n\t\t\t\tconst keys = Object.keys(message.metadata);\n\t\t\t\tassert(keys.length < 2, 0x5dd /* cannot group ops with metadata */);\n\t\t\t\tassert(keys.length === 0 || keys[0] === \"batch\", 0x5de /* unexpected op metadata */);\n\t\t\t}\n\t\t}\n\n\t\tconst serializedContent = JSON.stringify({\n\t\t\ttype: OpGroupingManager.groupedBatchOp,\n\t\t\tcontents: batch.content.map<IGroupedMessage>((message) => ({\n\t\t\t\tcontents: message.contents === undefined ? undefined : JSON.parse(message.contents),\n\t\t\t\tmetadata: message.metadata,\n\t\t\t\tcompression: message.compression,\n\t\t\t})),\n\t\t});\n\n\t\tconst groupedBatch: IBatch<[BatchMessage]> = {\n\t\t\t...batch,\n\t\t\tcontent: [\n\t\t\t\t{\n\t\t\t\t\tmetadata: undefined,\n\t\t\t\t\treferenceSequenceNumber: batch.content[0].referenceSequenceNumber,\n\t\t\t\t\tcontents: serializedContent,\n\t\t\t\t},\n\t\t\t],\n\t\t};\n\t\treturn groupedBatch;\n\t}\n\n\tpublic ungroupOp(op: ISequencedDocumentMessage): ISequencedDocumentMessage[] {\n\t\tassert(isGroupContents(op.contents), 0x947 /* can only ungroup a grouped batch */);\n\t\tconst contents: IGroupedBatchMessageContents = op.contents;\n\n\t\tlet fakeCsn = 1;\n\t\treturn contents.contents.map((subMessage) => ({\n\t\t\t...op,\n\t\t\tclientSequenceNumber: fakeCsn++,\n\t\t\tcontents: subMessage.contents,\n\t\t\tmetadata: subMessage.metadata,\n\t\t\tcompression: subMessage.compression,\n\t\t}));\n\t}\n\n\tpublic shouldGroup(batch: IBatch): boolean {\n\t\treturn (\n\t\t\t// Grouped batching must be enabled\n\t\t\tthis.config.groupedBatchingEnabled &&\n\t\t\t// The number of ops in the batch must surpass the configured threshold\n\t\t\tbatch.content.length >= this.config.opCountThreshold &&\n\t\t\t// Support for reentrant batches must be explicitly enabled\n\t\t\t(this.config.reentrantBatchGroupingEnabled || batch.hasReentrantOps !== true)\n\t\t);\n\t}\n}\n"]}
1
+ {"version":3,"file":"opGroupingManager.js","sourceRoot":"","sources":["../../src/opLifecycle/opGroupingManager.ts"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { ITelemetryBaseLogger } from \"@fluidframework/core-interfaces\";\nimport { assert } from \"@fluidframework/core-utils/internal\";\nimport { ISequencedDocumentMessage } from \"@fluidframework/driver-definitions/internal\";\nimport { createChildLogger } from \"@fluidframework/telemetry-utils/internal\";\n\nimport { IBatch, type BatchMessage } from \"./definitions.js\";\n\n/**\n * Grouping makes assumptions about the shape of message contents. This interface codifies those assumptions, but does not validate them.\n */\ninterface IGroupedBatchMessageContents {\n\ttype: typeof OpGroupingManager.groupedBatchOp;\n\tcontents: IGroupedMessage[];\n}\n\ninterface IGroupedMessage {\n\tcontents?: unknown;\n\tmetadata?: Record<string, unknown>;\n\tcompression?: string;\n}\n\nfunction isGroupContents(opContents: any): opContents is IGroupedBatchMessageContents {\n\treturn opContents?.type === OpGroupingManager.groupedBatchOp;\n}\n\nexport function isGroupedBatch(op: ISequencedDocumentMessage): boolean {\n\treturn isGroupContents(op.contents);\n}\n\nexport interface OpGroupingManagerConfig {\n\treadonly groupedBatchingEnabled: boolean;\n\treadonly opCountThreshold: number;\n\treadonly reentrantBatchGroupingEnabled: boolean;\n}\n\nexport class OpGroupingManager {\n\tstatic readonly groupedBatchOp = \"groupedBatch\";\n\tprivate readonly logger;\n\n\tconstructor(\n\t\tprivate readonly config: OpGroupingManagerConfig,\n\t\tlogger: ITelemetryBaseLogger,\n\t) {\n\t\tthis.logger = createChildLogger({ logger, namespace: \"OpGroupingManager\" });\n\t}\n\n\t/**\n\t * Converts the given batch into a \"grouped batch\" - a batch with a single message of type \"groupedBatch\",\n\t * with contents being an array of the original batch's messages.\n\t *\n\t * @remarks - Remember that a BatchMessage has its content JSON serialized, so the incoming batch message contents\n\t * must be parsed first, and then the type and contents mentioned above are hidden in that JSON serialization.\n\t */\n\tpublic groupBatch(batch: IBatch): IBatch<[BatchMessage]> {\n\t\tassert(this.shouldGroup(batch), 0x946 /* cannot group the provided batch */);\n\n\t\tif (batch.messages.length >= 1000) {\n\t\t\tthis.logger.sendTelemetryEvent({\n\t\t\t\teventName: \"GroupLargeBatch\",\n\t\t\t\tlength: batch.messages.length,\n\t\t\t\tthreshold: this.config.opCountThreshold,\n\t\t\t\treentrant: batch.hasReentrantOps,\n\t\t\t\t// Non null asserting here because of the length check above\n\t\t\t\t// eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n\t\t\t\treferenceSequenceNumber: batch.messages[0]!.referenceSequenceNumber,\n\t\t\t});\n\t\t}\n\n\t\tfor (const message of batch.messages) {\n\t\t\tif (message.metadata) {\n\t\t\t\tconst { batch: _batch, batchId, ...rest } = message.metadata;\n\t\t\t\tassert(Object.keys(rest).length === 0, 0x5dd /* cannot group ops with metadata */);\n\t\t\t}\n\t\t}\n\n\t\tconst serializedContent = JSON.stringify({\n\t\t\ttype: OpGroupingManager.groupedBatchOp,\n\t\t\tcontents: batch.messages.map<IGroupedMessage>((message) => ({\n\t\t\t\tcontents: message.contents === undefined ? undefined : JSON.parse(message.contents),\n\t\t\t\tmetadata: message.metadata,\n\t\t\t\tcompression: message.compression,\n\t\t\t})),\n\t\t});\n\n\t\tconst groupedBatch: IBatch<[BatchMessage]> = {\n\t\t\t...batch,\n\t\t\tmessages: [\n\t\t\t\t{\n\t\t\t\t\tmetadata: undefined,\n\t\t\t\t\t// TODO why are we non null asserting here?\n\t\t\t\t\t// eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n\t\t\t\t\treferenceSequenceNumber: batch.messages[0]!.referenceSequenceNumber,\n\t\t\t\t\tcontents: serializedContent,\n\t\t\t\t},\n\t\t\t],\n\t\t};\n\t\treturn groupedBatch;\n\t}\n\n\tpublic ungroupOp(op: ISequencedDocumentMessage): ISequencedDocumentMessage[] {\n\t\tassert(isGroupContents(op.contents), 0x947 /* can only ungroup a grouped batch */);\n\t\tconst contents: IGroupedBatchMessageContents = op.contents;\n\n\t\tlet fakeCsn = 1;\n\t\treturn contents.contents.map((subMessage) => ({\n\t\t\t...op,\n\t\t\tclientSequenceNumber: fakeCsn++,\n\t\t\tcontents: subMessage.contents,\n\t\t\tmetadata: subMessage.metadata,\n\t\t\tcompression: subMessage.compression,\n\t\t}));\n\t}\n\n\tpublic shouldGroup(batch: IBatch): boolean {\n\t\treturn (\n\t\t\t// Grouped batching must be enabled\n\t\t\tthis.config.groupedBatchingEnabled &&\n\t\t\t// The number of ops in the batch must surpass the configured threshold\n\t\t\tbatch.messages.length >= this.config.opCountThreshold &&\n\t\t\t// Support for reentrant batches must be explicitly enabled\n\t\t\t(this.config.reentrantBatchGroupingEnabled || batch.hasReentrantOps !== true)\n\t\t);\n\t}\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"opSplitter.d.ts","sourceRoot":"","sources":["../../src/opLifecycle/opSplitter.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,gDAAgD,CAAC;AAC/E,OAAO,EAAE,oBAAoB,EAAE,MAAM,iCAAiC,CAAC;AAEvE,OAAO,EAAE,yBAAyB,EAAE,MAAM,6CAA6C,CAAC;AAUxF,OAAO,EAAE,YAAY,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAEpE,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,yBAAyB,GAAG,OAAO,CAE5E;AAWD;;GAEG;AACH,qBAAa,UAAU;IAOrB,OAAO,CAAC,QAAQ,CAAC,aAAa;aAGd,gBAAgB,EAAE,MAAM;IACxC,OAAO,CAAC,QAAQ,CAAC,mBAAmB;IATrC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAwB;IACjD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC;gBAGvB,MAAM,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,EAAE,EACX,aAAa,EAC3B,CAAC,CAAC,KAAK,EAAE,aAAa,EAAE,EAAE,uBAAuB,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC,GACtE,SAAS,EACI,gBAAgB,EAAE,MAAM,EACvB,mBAAmB,EAAE,MAAM,EAC5C,MAAM,EAAE,oBAAoB;IAM7B,IAAW,sBAAsB,IAAI,OAAO,CAI3C;IAED,IAAW,MAAM,IAAI,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAEjD;IAEM,kBAAkB,CAAC,QAAQ,EAAE,MAAM;IAM1C,OAAO,CAAC,QAAQ;IA0BhB;;;;;;;;;;;;;;;;;;;;;;;OAuBG;IACI,sBAAsB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM;IAmE7C,YAAY,CAAC,OAAO,EAAE,yBAAyB,GAAG,kBAAkB;CAsC3E;AAED,KAAK,kBAAkB,GACpB;IACA,QAAQ,CAAC,YAAY,EAAE,KAAK,CAAC;CAC5B,GACD;IACA,QAAQ,CAAC,YAAY,EAAE,IAAI,CAAC;IAC5B,QAAQ,CAAC,OAAO,EAAE,yBAAyB,CAAC;CAC3C,CAAC;AAkBL;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,OAAO,OACf,YAAY,oBACE,MAAM,YACf,OAAO,KACd,UAAU,EAiDZ,CAAC"}
1
+ {"version":3,"file":"opSplitter.d.ts","sourceRoot":"","sources":["../../src/opLifecycle/opSplitter.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,gDAAgD,CAAC;AAC/E,OAAO,EAAE,oBAAoB,EAAE,MAAM,iCAAiC,CAAC;AAEvE,OAAO,EAAE,yBAAyB,EAAE,MAAM,6CAA6C,CAAC;AAUxF,OAAO,EAAE,YAAY,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAEpE,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,yBAAyB,GAAG,OAAO,CAE5E;AAWD;;GAEG;AACH,qBAAa,UAAU;IAOrB,OAAO,CAAC,QAAQ,CAAC,aAAa;aAGd,gBAAgB,EAAE,MAAM;IACxC,OAAO,CAAC,QAAQ,CAAC,mBAAmB;IATrC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAwB;IACjD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC;gBAGvB,MAAM,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,EAAE,EACX,aAAa,EAC3B,CAAC,CAAC,KAAK,EAAE,aAAa,EAAE,EAAE,uBAAuB,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC,GACtE,SAAS,EACI,gBAAgB,EAAE,MAAM,EACvB,mBAAmB,EAAE,MAAM,EAC5C,MAAM,EAAE,oBAAoB;IAM7B,IAAW,sBAAsB,IAAI,OAAO,CAI3C;IAED,IAAW,MAAM,IAAI,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAEjD;IAEM,kBAAkB,CAAC,QAAQ,EAAE,MAAM;IAM1C,OAAO,CAAC,QAAQ;IA0BhB;;;;;;;;;;;;;;;;;;;;;;;OAuBG;IACI,sBAAsB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM;IAuE7C,YAAY,CAAC,OAAO,EAAE,yBAAyB,GAAG,kBAAkB;CAsC3E;AAED,KAAK,kBAAkB,GACpB;IACA,QAAQ,CAAC,YAAY,EAAE,KAAK,CAAC;CAC5B,GACD;IACA,QAAQ,CAAC,YAAY,EAAE,IAAI,CAAC;IAC5B,QAAQ,CAAC,OAAO,EAAE,yBAAyB,CAAC;CAC3C,CAAC;AAkBL;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,OAAO,OACf,YAAY,oBACE,MAAM,YACf,OAAO,KACd,UAAU,EAiDZ,CAAC"}
@@ -79,13 +79,15 @@ export class OpSplitter {
79
79
  */
80
80
  splitFirstBatchMessage(batch) {
81
81
  assert(this.isBatchChunkingEnabled, 0x513 /* Chunking needs to be enabled */);
82
- assert(batch.contentSizeInBytes > 0 && batch.content.length > 0, 0x514 /* Batch needs to be non-empty */);
82
+ assert(batch.contentSizeInBytes > 0 && batch.messages.length > 0, 0x514 /* Batch needs to be non-empty */);
83
83
  assert(batch.referenceSequenceNumber !== undefined, 0x58a /* Batch must have a reference sequence number if non-empty */);
84
84
  assert(this.chunkSizeInBytes !== 0, 0x515 /* Chunk size needs to be non-zero */);
85
85
  assert(this.chunkSizeInBytes < this.maxBatchSizeInBytes, 0x516 /* Chunk size needs to be smaller than the max batch size */);
86
- const firstMessage = batch.content[0]; // we expect this to be the large compressed op, which needs to be split
86
+ // Non null asserting here because of the length check above
87
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
88
+ const firstMessage = batch.messages[0]; // we expect this to be the large compressed op, which needs to be split
87
89
  assert((firstMessage.contents?.length ?? 0) >= this.chunkSizeInBytes, 0x518 /* First message in the batch needs to be chunkable */);
88
- const restOfMessages = batch.content.slice(1); // we expect these to be empty ops, created to reserve sequence numbers
90
+ const restOfMessages = batch.messages.slice(1); // we expect these to be empty ops, created to reserve sequence numbers
89
91
  const socketSize = estimateSocketSize(batch);
90
92
  const chunks = splitOp(firstMessage, this.chunkSizeInBytes,
91
93
  // If we estimate that the socket batch size will exceed the batch limit
@@ -99,18 +101,21 @@ export class OpSplitter {
99
101
  }
100
102
  // The last chunk will be part of the new batch and needs to
101
103
  // preserve the batch metadata of the original batch
102
- const lastChunk = chunkToBatchMessage(chunks[chunks.length - 1], batch.referenceSequenceNumber, { batch: firstMessage.metadata?.batch });
104
+ const lastChunk = chunkToBatchMessage(
105
+ // Non null asserting here because of the length assert above
106
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
107
+ chunks[chunks.length - 1], batch.referenceSequenceNumber, { batch: firstMessage.metadata?.batch });
103
108
  this.logger.sendPerformanceEvent({
104
109
  // Used to be "Chunked compressed batch"
105
110
  eventName: "CompressedChunkedBatch",
106
- length: batch.content.length,
111
+ length: batch.messages.length,
107
112
  sizeInBytes: batch.contentSizeInBytes,
108
113
  chunks: chunks.length,
109
114
  chunkSizeInBytes: this.chunkSizeInBytes,
110
115
  socketSize,
111
116
  });
112
117
  return {
113
- content: [lastChunk, ...restOfMessages],
118
+ messages: [lastChunk, ...restOfMessages],
114
119
  contentSizeInBytes: lastChunk.contents?.length ?? 0,
115
120
  referenceSequenceNumber: batch.referenceSequenceNumber,
116
121
  };
@@ -1 +1 @@
1
- {"version":3,"file":"opSplitter.js","sourceRoot":"","sources":["../../src/opLifecycle/opSplitter.ts"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { IBatchMessage } from \"@fluidframework/container-definitions/internal\";\nimport { ITelemetryBaseLogger } from \"@fluidframework/core-interfaces\";\nimport { assert } from \"@fluidframework/core-utils/internal\";\nimport { ISequencedDocumentMessage } from \"@fluidframework/driver-definitions/internal\";\nimport {\n\tDataCorruptionError,\n\tcreateChildLogger,\n\textractSafePropertiesFromMessage,\n} from \"@fluidframework/telemetry-utils/internal\";\n\nimport { ContainerMessageType, ContainerRuntimeChunkedOpMessage } from \"../messageTypes.js\";\n\nimport { estimateSocketSize } from \"./batchManager.js\";\nimport { BatchMessage, IBatch, IChunkedOp } from \"./definitions.js\";\n\nexport function isChunkedMessage(message: ISequencedDocumentMessage): boolean {\n\treturn isChunkedContents(message.contents);\n}\n\ninterface IChunkedContents {\n\ttype: typeof ContainerMessageType.ChunkedOp;\n\tcontents: IChunkedOp;\n}\n\nfunction isChunkedContents(contents: any): contents is IChunkedContents {\n\treturn contents?.type === ContainerMessageType.ChunkedOp;\n}\n\n/**\n * Responsible for creating and reconstructing chunked messages.\n */\nexport class OpSplitter {\n\t// Local copy of incomplete received chunks.\n\tprivate readonly chunkMap: Map<string, string[]>;\n\tprivate readonly logger;\n\n\tconstructor(\n\t\tchunks: [string, string[]][],\n\t\tprivate readonly submitBatchFn:\n\t\t\t| ((batch: IBatchMessage[], referenceSequenceNumber?: number) => number)\n\t\t\t| undefined,\n\t\tpublic readonly chunkSizeInBytes: number,\n\t\tprivate readonly maxBatchSizeInBytes: number,\n\t\tlogger: ITelemetryBaseLogger,\n\t) {\n\t\tthis.chunkMap = new Map<string, string[]>(chunks);\n\t\tthis.logger = createChildLogger({ logger, namespace: \"OpSplitter\" });\n\t}\n\n\tpublic get isBatchChunkingEnabled(): boolean {\n\t\treturn (\n\t\t\tthis.chunkSizeInBytes < Number.POSITIVE_INFINITY && this.submitBatchFn !== undefined\n\t\t);\n\t}\n\n\tpublic get chunks(): ReadonlyMap<string, string[]> {\n\t\treturn this.chunkMap;\n\t}\n\n\tpublic clearPartialChunks(clientId: string) {\n\t\tif (this.chunkMap.has(clientId)) {\n\t\t\tthis.chunkMap.delete(clientId);\n\t\t}\n\t}\n\n\tprivate addChunk(\n\t\tclientId: string,\n\t\tchunkedContent: IChunkedOp,\n\t\toriginalMessage: ISequencedDocumentMessage,\n\t) {\n\t\tlet map = this.chunkMap.get(clientId);\n\t\tif (map === undefined) {\n\t\t\tmap = [];\n\t\t\tthis.chunkMap.set(clientId, map);\n\t\t}\n\n\t\tif (chunkedContent.chunkId !== map.length + 1) {\n\t\t\t// We are expecting the chunks to be processed sequentially, in the same order as they are sent.\n\t\t\t// Therefore, the chunkId of the incoming op needs to match the length of the array (1-based indexing)\n\t\t\t// holding the existing chunks for that particular clientId.\n\t\t\tthrow new DataCorruptionError(\"Chunk Id mismatch\", {\n\t\t\t\t...extractSafePropertiesFromMessage(originalMessage),\n\t\t\t\tchunkMapLength: map.length,\n\t\t\t\tchunkId: chunkedContent.chunkId,\n\t\t\t\ttotalChunks: chunkedContent.totalChunks,\n\t\t\t});\n\t\t}\n\n\t\tmap.push(chunkedContent.contents);\n\t}\n\n\t/**\n\t * Splits the first op of a compressed batch in chunks, sends the chunks separately and\n\t * returns a new batch composed of the last chunk and the rest of the ops in the original batch.\n\t *\n\t * A compressed batch is formed by one large op at the first position, followed by a series of placeholder ops\n\t * which are used in order to reserve the sequence numbers for when the first op gets unrolled into the original\n\t * uncompressed ops at ingestion in the runtime.\n\t *\n\t * If the first op is too large, it can be chunked (split into smaller op) which can be sent individually over the wire\n\t * and accumulate at ingestion, until the last op in the chunk is processed, when the original op is unrolled.\n\t *\n\t * This method will send the first N - 1 chunks separately and use the last chunk as the first message in the result batch\n\t * and then appends the original placeholder ops. This will ensure that the batch semantics of the original (non-compressed) batch\n\t * are preserved, as the original chunked op will be unrolled by the runtime when the first message in the batch is processed\n\t * (as it is the last chunk).\n\t *\n\t * To illustrate, if the input is `[largeOp, emptyOp, emptyOp]`, `largeOp` will be split into `[chunk1, chunk2, chunk3, chunk4]`.\n\t * `chunk1`, `chunk2` and `chunk3` will be sent individually and `[chunk4, emptyOp, emptyOp]` will be returned.\n\t *\n\t * @remarks - A side effect here is that 1 or more chunks are queued immediately for sending in next JS turn.\n\t *\n\t * @param batch - the compressed batch which needs to be processed\n\t * @returns A new adjusted batch (last chunk + empty placeholders) which can be sent over the wire\n\t */\n\tpublic splitFirstBatchMessage(batch: IBatch): IBatch {\n\t\tassert(this.isBatchChunkingEnabled, 0x513 /* Chunking needs to be enabled */);\n\t\tassert(\n\t\t\tbatch.contentSizeInBytes > 0 && batch.content.length > 0,\n\t\t\t0x514 /* Batch needs to be non-empty */,\n\t\t);\n\t\tassert(\n\t\t\tbatch.referenceSequenceNumber !== undefined,\n\t\t\t0x58a /* Batch must have a reference sequence number if non-empty */,\n\t\t);\n\t\tassert(this.chunkSizeInBytes !== 0, 0x515 /* Chunk size needs to be non-zero */);\n\t\tassert(\n\t\t\tthis.chunkSizeInBytes < this.maxBatchSizeInBytes,\n\t\t\t0x516 /* Chunk size needs to be smaller than the max batch size */,\n\t\t);\n\n\t\tconst firstMessage = batch.content[0]; // we expect this to be the large compressed op, which needs to be split\n\t\tassert(\n\t\t\t(firstMessage.contents?.length ?? 0) >= this.chunkSizeInBytes,\n\t\t\t0x518 /* First message in the batch needs to be chunkable */,\n\t\t);\n\n\t\tconst restOfMessages = batch.content.slice(1); // we expect these to be empty ops, created to reserve sequence numbers\n\t\tconst socketSize = estimateSocketSize(batch);\n\t\tconst chunks = splitOp(\n\t\t\tfirstMessage,\n\t\t\tthis.chunkSizeInBytes,\n\t\t\t// If we estimate that the socket batch size will exceed the batch limit\n\t\t\t// we will inject an empty op to minimize the risk of the payload failing due to\n\t\t\t// the overhead from the trailing empty ops in the batch.\n\t\t\tsocketSize >= this.maxBatchSizeInBytes,\n\t\t);\n\n\t\tassert(this.submitBatchFn !== undefined, 0x519 /* We don't support old loaders */);\n\t\t// Send the first N-1 chunks immediately\n\t\tfor (const chunk of chunks.slice(0, -1)) {\n\t\t\tthis.submitBatchFn(\n\t\t\t\t[chunkToBatchMessage(chunk, batch.referenceSequenceNumber)],\n\t\t\t\tbatch.referenceSequenceNumber,\n\t\t\t);\n\t\t}\n\n\t\t// The last chunk will be part of the new batch and needs to\n\t\t// preserve the batch metadata of the original batch\n\t\tconst lastChunk = chunkToBatchMessage(\n\t\t\tchunks[chunks.length - 1],\n\t\t\tbatch.referenceSequenceNumber,\n\t\t\t{ batch: firstMessage.metadata?.batch },\n\t\t);\n\n\t\tthis.logger.sendPerformanceEvent({\n\t\t\t// Used to be \"Chunked compressed batch\"\n\t\t\teventName: \"CompressedChunkedBatch\",\n\t\t\tlength: batch.content.length,\n\t\t\tsizeInBytes: batch.contentSizeInBytes,\n\t\t\tchunks: chunks.length,\n\t\t\tchunkSizeInBytes: this.chunkSizeInBytes,\n\t\t\tsocketSize,\n\t\t});\n\n\t\treturn {\n\t\t\tcontent: [lastChunk, ...restOfMessages],\n\t\t\tcontentSizeInBytes: lastChunk.contents?.length ?? 0,\n\t\t\treferenceSequenceNumber: batch.referenceSequenceNumber,\n\t\t};\n\t}\n\n\tpublic processChunk(message: ISequencedDocumentMessage): ProcessChunkResult {\n\t\tassert(isChunkedContents(message.contents), 0x948 /* message not of type ChunkedOp */);\n\t\tconst contents: IChunkedContents = message.contents;\n\n\t\t// TODO: Verify whether this should be able to handle server-generated ops (with null clientId)\n\t\t// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion\n\t\tconst clientId = message.clientId as string;\n\t\tconst chunkedContent = contents.contents;\n\t\tthis.addChunk(clientId, chunkedContent, message);\n\n\t\tif (chunkedContent.chunkId < chunkedContent.totalChunks) {\n\t\t\t// We are processing the op in chunks but haven't reached\n\t\t\t// the last chunk yet in order to reconstruct the original op\n\t\t\treturn {\n\t\t\t\tisFinalChunk: false,\n\t\t\t};\n\t\t}\n\n\t\t// eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n\t\tconst serializedContent = this.chunkMap.get(clientId)!.join(\"\");\n\t\tthis.clearPartialChunks(clientId);\n\n\t\t// The final/complete message will contain the data from all the chunks.\n\t\t// It will have the sequenceNumber of the last chunk\n\t\tconst completeMessage = { ...message };\n\t\tcompleteMessage.contents =\n\t\t\tserializedContent === \"\" ? undefined : JSON.parse(serializedContent);\n\t\t// back-compat with 1.x builds\n\t\t// This is only required / present for non-compressed, chunked ops\n\t\t// For compressed ops, we have op grouping enabled, and type of each op is preserved within compressed content.\n\t\tcompleteMessage.type = (chunkedContent as any).originalType;\n\t\tcompleteMessage.metadata = chunkedContent.originalMetadata;\n\t\tcompleteMessage.compression = chunkedContent.originalCompression;\n\t\treturn {\n\t\t\tmessage: completeMessage,\n\t\t\tisFinalChunk: true,\n\t\t};\n\t}\n}\n\ntype ProcessChunkResult =\n\t| {\n\t\t\treadonly isFinalChunk: false;\n\t }\n\t| {\n\t\t\treadonly isFinalChunk: true;\n\t\t\treadonly message: ISequencedDocumentMessage;\n\t };\n\nconst chunkToBatchMessage = (\n\tchunk: IChunkedOp,\n\treferenceSequenceNumber: number,\n\tmetadata: Record<string, unknown> | undefined = undefined,\n): BatchMessage => {\n\tconst payload: ContainerRuntimeChunkedOpMessage = {\n\t\ttype: ContainerMessageType.ChunkedOp,\n\t\tcontents: chunk,\n\t};\n\treturn {\n\t\tcontents: JSON.stringify(payload),\n\t\tmetadata,\n\t\treferenceSequenceNumber,\n\t};\n};\n\n/**\n * Splits an op into smaller ops (chunks), based on the size of the op and the `chunkSizeInBytes` parameter.\n *\n * The last op of the result will be bundled with empty ops in the same batch. There is a risk of the batch payload\n * exceeding the 1MB limit due to the overhead from the empty ops. If the last op is large, the risk is even higher.\n * To minimize the odds, an extra empty op can be added to the result using the `extraOp` parameter.\n *\n * @param op - the op to be split\n * @param chunkSizeInBytes - how large should the chunks be\n * @param extraOp - should an extra empty op be added to the result\n * @returns an array of chunked ops\n */\nexport const splitOp = (\n\top: BatchMessage,\n\tchunkSizeInBytes: number,\n\textraOp: boolean = false,\n): IChunkedOp[] => {\n\tconst chunks: IChunkedOp[] = [];\n\tassert(\n\t\top.contents !== undefined && op.contents !== null,\n\t\t0x51a /* We should have something to chunk */,\n\t);\n\n\tconst contentLength = op.contents.length;\n\tconst chunkCount =\n\t\tMath.floor((contentLength - 1) / chunkSizeInBytes) + 1 + (extraOp ? 1 : 0);\n\tlet offset = 0;\n\tfor (let chunkId = 1; chunkId <= chunkCount; chunkId++) {\n\t\tconst chunk: IChunkedOp = {\n\t\t\tchunkId,\n\t\t\tcontents: op.contents.substr(offset, chunkSizeInBytes),\n\t\t\ttotalChunks: chunkCount,\n\t\t};\n\n\t\tif (chunkId === chunkCount) {\n\t\t\t// We don't need to port these to all the chunks,\n\t\t\t// as we rebuild the original op when we process the\n\t\t\t// last chunk, therefore it is the only one that needs it.\n\t\t\tchunk.originalMetadata = op.metadata;\n\t\t\tchunk.originalCompression = op.compression;\n\n\t\t\t// back-compat with 1.x builds\n\t\t\t// 2.x builds only do chunking for compressed ops.\n\t\t\t// originalType is no longer used in such cases, as each op preserves its type within compressed payload.\n\t\t\t// But, if 1.x builds see this op, and there is no type on the message, then it will ignore this message silently.\n\t\t\t// This is really bad, as we will crash on later ops and it's very hard to debug these cases.\n\t\t\t// If we put some known type here, then we will crash on it (as 1.x does not understand compression, and thus will not\n\t\t\t// find info on the op like address of the channel to deliver the op)\n\t\t\t(chunk as any).originalType = \"component\";\n\t\t}\n\n\t\tchunks.push(chunk);\n\t\toffset += chunkSizeInBytes;\n\t\tassert(\n\t\t\tchunkId >= chunkCount - 1 || offset <= contentLength,\n\t\t\t0x58b /* Content offset within bounds */,\n\t\t);\n\t}\n\n\tassert(\n\t\toffset >= contentLength,\n\t\t0x58c /* Content offset equal or larger than content length */,\n\t);\n\tassert(chunks.length === chunkCount, 0x5a5 /* Expected number of chunks */);\n\treturn chunks;\n};\n"]}
1
+ {"version":3,"file":"opSplitter.js","sourceRoot":"","sources":["../../src/opLifecycle/opSplitter.ts"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { IBatchMessage } from \"@fluidframework/container-definitions/internal\";\nimport { ITelemetryBaseLogger } from \"@fluidframework/core-interfaces\";\nimport { assert } from \"@fluidframework/core-utils/internal\";\nimport { ISequencedDocumentMessage } from \"@fluidframework/driver-definitions/internal\";\nimport {\n\tDataCorruptionError,\n\tcreateChildLogger,\n\textractSafePropertiesFromMessage,\n} from \"@fluidframework/telemetry-utils/internal\";\n\nimport { ContainerMessageType, ContainerRuntimeChunkedOpMessage } from \"../messageTypes.js\";\n\nimport { estimateSocketSize } from \"./batchManager.js\";\nimport { BatchMessage, IBatch, IChunkedOp } from \"./definitions.js\";\n\nexport function isChunkedMessage(message: ISequencedDocumentMessage): boolean {\n\treturn isChunkedContents(message.contents);\n}\n\ninterface IChunkedContents {\n\ttype: typeof ContainerMessageType.ChunkedOp;\n\tcontents: IChunkedOp;\n}\n\nfunction isChunkedContents(contents: any): contents is IChunkedContents {\n\treturn contents?.type === ContainerMessageType.ChunkedOp;\n}\n\n/**\n * Responsible for creating and reconstructing chunked messages.\n */\nexport class OpSplitter {\n\t// Local copy of incomplete received chunks.\n\tprivate readonly chunkMap: Map<string, string[]>;\n\tprivate readonly logger;\n\n\tconstructor(\n\t\tchunks: [string, string[]][],\n\t\tprivate readonly submitBatchFn:\n\t\t\t| ((batch: IBatchMessage[], referenceSequenceNumber?: number) => number)\n\t\t\t| undefined,\n\t\tpublic readonly chunkSizeInBytes: number,\n\t\tprivate readonly maxBatchSizeInBytes: number,\n\t\tlogger: ITelemetryBaseLogger,\n\t) {\n\t\tthis.chunkMap = new Map<string, string[]>(chunks);\n\t\tthis.logger = createChildLogger({ logger, namespace: \"OpSplitter\" });\n\t}\n\n\tpublic get isBatchChunkingEnabled(): boolean {\n\t\treturn (\n\t\t\tthis.chunkSizeInBytes < Number.POSITIVE_INFINITY && this.submitBatchFn !== undefined\n\t\t);\n\t}\n\n\tpublic get chunks(): ReadonlyMap<string, string[]> {\n\t\treturn this.chunkMap;\n\t}\n\n\tpublic clearPartialChunks(clientId: string) {\n\t\tif (this.chunkMap.has(clientId)) {\n\t\t\tthis.chunkMap.delete(clientId);\n\t\t}\n\t}\n\n\tprivate addChunk(\n\t\tclientId: string,\n\t\tchunkedContent: IChunkedOp,\n\t\toriginalMessage: ISequencedDocumentMessage,\n\t) {\n\t\tlet map = this.chunkMap.get(clientId);\n\t\tif (map === undefined) {\n\t\t\tmap = [];\n\t\t\tthis.chunkMap.set(clientId, map);\n\t\t}\n\n\t\tif (chunkedContent.chunkId !== map.length + 1) {\n\t\t\t// We are expecting the chunks to be processed sequentially, in the same order as they are sent.\n\t\t\t// Therefore, the chunkId of the incoming op needs to match the length of the array (1-based indexing)\n\t\t\t// holding the existing chunks for that particular clientId.\n\t\t\tthrow new DataCorruptionError(\"Chunk Id mismatch\", {\n\t\t\t\t...extractSafePropertiesFromMessage(originalMessage),\n\t\t\t\tchunkMapLength: map.length,\n\t\t\t\tchunkId: chunkedContent.chunkId,\n\t\t\t\ttotalChunks: chunkedContent.totalChunks,\n\t\t\t});\n\t\t}\n\n\t\tmap.push(chunkedContent.contents);\n\t}\n\n\t/**\n\t * Splits the first op of a compressed batch in chunks, sends the chunks separately and\n\t * returns a new batch composed of the last chunk and the rest of the ops in the original batch.\n\t *\n\t * A compressed batch is formed by one large op at the first position, followed by a series of placeholder ops\n\t * which are used in order to reserve the sequence numbers for when the first op gets unrolled into the original\n\t * uncompressed ops at ingestion in the runtime.\n\t *\n\t * If the first op is too large, it can be chunked (split into smaller op) which can be sent individually over the wire\n\t * and accumulate at ingestion, until the last op in the chunk is processed, when the original op is unrolled.\n\t *\n\t * This method will send the first N - 1 chunks separately and use the last chunk as the first message in the result batch\n\t * and then appends the original placeholder ops. This will ensure that the batch semantics of the original (non-compressed) batch\n\t * are preserved, as the original chunked op will be unrolled by the runtime when the first message in the batch is processed\n\t * (as it is the last chunk).\n\t *\n\t * To illustrate, if the input is `[largeOp, emptyOp, emptyOp]`, `largeOp` will be split into `[chunk1, chunk2, chunk3, chunk4]`.\n\t * `chunk1`, `chunk2` and `chunk3` will be sent individually and `[chunk4, emptyOp, emptyOp]` will be returned.\n\t *\n\t * @remarks - A side effect here is that 1 or more chunks are queued immediately for sending in next JS turn.\n\t *\n\t * @param batch - the compressed batch which needs to be processed\n\t * @returns A new adjusted batch (last chunk + empty placeholders) which can be sent over the wire\n\t */\n\tpublic splitFirstBatchMessage(batch: IBatch): IBatch {\n\t\tassert(this.isBatchChunkingEnabled, 0x513 /* Chunking needs to be enabled */);\n\t\tassert(\n\t\t\tbatch.contentSizeInBytes > 0 && batch.messages.length > 0,\n\t\t\t0x514 /* Batch needs to be non-empty */,\n\t\t);\n\t\tassert(\n\t\t\tbatch.referenceSequenceNumber !== undefined,\n\t\t\t0x58a /* Batch must have a reference sequence number if non-empty */,\n\t\t);\n\t\tassert(this.chunkSizeInBytes !== 0, 0x515 /* Chunk size needs to be non-zero */);\n\t\tassert(\n\t\t\tthis.chunkSizeInBytes < this.maxBatchSizeInBytes,\n\t\t\t0x516 /* Chunk size needs to be smaller than the max batch size */,\n\t\t);\n\n\t\t// Non null asserting here because of the length check above\n\t\t// eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n\t\tconst firstMessage = batch.messages[0]!; // we expect this to be the large compressed op, which needs to be split\n\t\tassert(\n\t\t\t(firstMessage.contents?.length ?? 0) >= this.chunkSizeInBytes,\n\t\t\t0x518 /* First message in the batch needs to be chunkable */,\n\t\t);\n\n\t\tconst restOfMessages = batch.messages.slice(1); // we expect these to be empty ops, created to reserve sequence numbers\n\t\tconst socketSize = estimateSocketSize(batch);\n\t\tconst chunks = splitOp(\n\t\t\tfirstMessage,\n\t\t\tthis.chunkSizeInBytes,\n\t\t\t// If we estimate that the socket batch size will exceed the batch limit\n\t\t\t// we will inject an empty op to minimize the risk of the payload failing due to\n\t\t\t// the overhead from the trailing empty ops in the batch.\n\t\t\tsocketSize >= this.maxBatchSizeInBytes,\n\t\t);\n\n\t\tassert(this.submitBatchFn !== undefined, 0x519 /* We don't support old loaders */);\n\t\t// Send the first N-1 chunks immediately\n\t\tfor (const chunk of chunks.slice(0, -1)) {\n\t\t\tthis.submitBatchFn(\n\t\t\t\t[chunkToBatchMessage(chunk, batch.referenceSequenceNumber)],\n\t\t\t\tbatch.referenceSequenceNumber,\n\t\t\t);\n\t\t}\n\n\t\t// The last chunk will be part of the new batch and needs to\n\t\t// preserve the batch metadata of the original batch\n\t\tconst lastChunk = chunkToBatchMessage(\n\t\t\t// Non null asserting here because of the length assert above\n\t\t\t// eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n\t\t\tchunks[chunks.length - 1]!,\n\t\t\tbatch.referenceSequenceNumber,\n\t\t\t{ batch: firstMessage.metadata?.batch },\n\t\t);\n\n\t\tthis.logger.sendPerformanceEvent({\n\t\t\t// Used to be \"Chunked compressed batch\"\n\t\t\teventName: \"CompressedChunkedBatch\",\n\t\t\tlength: batch.messages.length,\n\t\t\tsizeInBytes: batch.contentSizeInBytes,\n\t\t\tchunks: chunks.length,\n\t\t\tchunkSizeInBytes: this.chunkSizeInBytes,\n\t\t\tsocketSize,\n\t\t});\n\n\t\treturn {\n\t\t\tmessages: [lastChunk, ...restOfMessages],\n\t\t\tcontentSizeInBytes: lastChunk.contents?.length ?? 0,\n\t\t\treferenceSequenceNumber: batch.referenceSequenceNumber,\n\t\t};\n\t}\n\n\tpublic processChunk(message: ISequencedDocumentMessage): ProcessChunkResult {\n\t\tassert(isChunkedContents(message.contents), 0x948 /* message not of type ChunkedOp */);\n\t\tconst contents: IChunkedContents = message.contents;\n\n\t\t// TODO: Verify whether this should be able to handle server-generated ops (with null clientId)\n\t\t// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion\n\t\tconst clientId = message.clientId as string;\n\t\tconst chunkedContent = contents.contents;\n\t\tthis.addChunk(clientId, chunkedContent, message);\n\n\t\tif (chunkedContent.chunkId < chunkedContent.totalChunks) {\n\t\t\t// We are processing the op in chunks but haven't reached\n\t\t\t// the last chunk yet in order to reconstruct the original op\n\t\t\treturn {\n\t\t\t\tisFinalChunk: false,\n\t\t\t};\n\t\t}\n\n\t\t// eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n\t\tconst serializedContent = this.chunkMap.get(clientId)!.join(\"\");\n\t\tthis.clearPartialChunks(clientId);\n\n\t\t// The final/complete message will contain the data from all the chunks.\n\t\t// It will have the sequenceNumber of the last chunk\n\t\tconst completeMessage = { ...message };\n\t\tcompleteMessage.contents =\n\t\t\tserializedContent === \"\" ? undefined : JSON.parse(serializedContent);\n\t\t// back-compat with 1.x builds\n\t\t// This is only required / present for non-compressed, chunked ops\n\t\t// For compressed ops, we have op grouping enabled, and type of each op is preserved within compressed content.\n\t\tcompleteMessage.type = (chunkedContent as any).originalType;\n\t\tcompleteMessage.metadata = chunkedContent.originalMetadata;\n\t\tcompleteMessage.compression = chunkedContent.originalCompression;\n\t\treturn {\n\t\t\tmessage: completeMessage,\n\t\t\tisFinalChunk: true,\n\t\t};\n\t}\n}\n\ntype ProcessChunkResult =\n\t| {\n\t\t\treadonly isFinalChunk: false;\n\t }\n\t| {\n\t\t\treadonly isFinalChunk: true;\n\t\t\treadonly message: ISequencedDocumentMessage;\n\t };\n\nconst chunkToBatchMessage = (\n\tchunk: IChunkedOp,\n\treferenceSequenceNumber: number,\n\tmetadata: Record<string, unknown> | undefined = undefined,\n): BatchMessage => {\n\tconst payload: ContainerRuntimeChunkedOpMessage = {\n\t\ttype: ContainerMessageType.ChunkedOp,\n\t\tcontents: chunk,\n\t};\n\treturn {\n\t\tcontents: JSON.stringify(payload),\n\t\tmetadata,\n\t\treferenceSequenceNumber,\n\t};\n};\n\n/**\n * Splits an op into smaller ops (chunks), based on the size of the op and the `chunkSizeInBytes` parameter.\n *\n * The last op of the result will be bundled with empty ops in the same batch. There is a risk of the batch payload\n * exceeding the 1MB limit due to the overhead from the empty ops. If the last op is large, the risk is even higher.\n * To minimize the odds, an extra empty op can be added to the result using the `extraOp` parameter.\n *\n * @param op - the op to be split\n * @param chunkSizeInBytes - how large should the chunks be\n * @param extraOp - should an extra empty op be added to the result\n * @returns an array of chunked ops\n */\nexport const splitOp = (\n\top: BatchMessage,\n\tchunkSizeInBytes: number,\n\textraOp: boolean = false,\n): IChunkedOp[] => {\n\tconst chunks: IChunkedOp[] = [];\n\tassert(\n\t\top.contents !== undefined && op.contents !== null,\n\t\t0x51a /* We should have something to chunk */,\n\t);\n\n\tconst contentLength = op.contents.length;\n\tconst chunkCount =\n\t\tMath.floor((contentLength - 1) / chunkSizeInBytes) + 1 + (extraOp ? 1 : 0);\n\tlet offset = 0;\n\tfor (let chunkId = 1; chunkId <= chunkCount; chunkId++) {\n\t\tconst chunk: IChunkedOp = {\n\t\t\tchunkId,\n\t\t\tcontents: op.contents.substr(offset, chunkSizeInBytes),\n\t\t\ttotalChunks: chunkCount,\n\t\t};\n\n\t\tif (chunkId === chunkCount) {\n\t\t\t// We don't need to port these to all the chunks,\n\t\t\t// as we rebuild the original op when we process the\n\t\t\t// last chunk, therefore it is the only one that needs it.\n\t\t\tchunk.originalMetadata = op.metadata;\n\t\t\tchunk.originalCompression = op.compression;\n\n\t\t\t// back-compat with 1.x builds\n\t\t\t// 2.x builds only do chunking for compressed ops.\n\t\t\t// originalType is no longer used in such cases, as each op preserves its type within compressed payload.\n\t\t\t// But, if 1.x builds see this op, and there is no type on the message, then it will ignore this message silently.\n\t\t\t// This is really bad, as we will crash on later ops and it's very hard to debug these cases.\n\t\t\t// If we put some known type here, then we will crash on it (as 1.x does not understand compression, and thus will not\n\t\t\t// find info on the op like address of the channel to deliver the op)\n\t\t\t(chunk as any).originalType = \"component\";\n\t\t}\n\n\t\tchunks.push(chunk);\n\t\toffset += chunkSizeInBytes;\n\t\tassert(\n\t\t\tchunkId >= chunkCount - 1 || offset <= contentLength,\n\t\t\t0x58b /* Content offset within bounds */,\n\t\t);\n\t}\n\n\tassert(\n\t\toffset >= contentLength,\n\t\t0x58c /* Content offset equal or larger than content length */,\n\t);\n\tassert(chunks.length === chunkCount, 0x5a5 /* Expected number of chunks */);\n\treturn chunks;\n};\n"]}
@@ -6,8 +6,8 @@ import { ICriticalContainerError } from "@fluidframework/container-definitions";
6
6
  import { IBatchMessage } from "@fluidframework/container-definitions/internal";
7
7
  import { ITelemetryBaseLogger } from "@fluidframework/core-interfaces";
8
8
  import { ICompressionRuntimeOptions } from "../containerRuntime.js";
9
- import { IPendingBatchMessage, PendingStateManager } from "../pendingStateManager.js";
10
- import { BatchSequenceNumbers } from "./batchManager.js";
9
+ import { PendingMessageResubmitData, PendingStateManager } from "../pendingStateManager.js";
10
+ import { BatchSequenceNumbers, type BatchId } from "./batchManager.js";
11
11
  import { BatchMessage, IBatch, IBatchCheckpoint } from "./definitions.js";
12
12
  import { OpCompressor } from "./opCompressor.js";
13
13
  import { OpGroupingManager } from "./opGroupingManager.js";
@@ -28,7 +28,7 @@ export interface IOutboxParameters {
28
28
  readonly logger: ITelemetryBaseLogger;
29
29
  readonly groupingManager: OpGroupingManager;
30
30
  readonly getCurrentSequenceNumbers: () => BatchSequenceNumbers;
31
- readonly reSubmit: (message: IPendingBatchMessage) => void;
31
+ readonly reSubmit: (message: PendingMessageResubmitData) => void;
32
32
  readonly opReentrancy: () => boolean;
33
33
  readonly closeContainer: (error?: ICriticalContainerError) => void;
34
34
  }
@@ -44,9 +44,16 @@ export interface IOutboxParameters {
44
44
  * @returns the result of the action provided
45
45
  */
46
46
  export declare function getLongStack<T>(action: () => T, length?: number): T;
47
+ /**
48
+ * The Outbox collects messages submitted by the ContainerRuntime into a batch,
49
+ * and then flushes the batch when requested.
50
+ *
51
+ * @remarks There are actually multiple independent batches (some are for a specific message type),
52
+ * to support slight variation in semantics for each batch (e.g. support for rebasing or grouping).
53
+ */
47
54
  export declare class Outbox {
48
55
  private readonly params;
49
- private readonly mc;
56
+ private readonly logger;
50
57
  private readonly mainBatch;
51
58
  private readonly blobAttachBatch;
52
59
  private readonly idAllocationBatch;
@@ -78,7 +85,13 @@ export declare class Outbox {
78
85
  submitBlobAttach(message: BatchMessage): void;
79
86
  submitIdAllocation(message: BatchMessage): void;
80
87
  private addMessageToBatchManager;
81
- flush(): void;
88
+ /**
89
+ * Flush all the batches to the ordering service.
90
+ * This method is expected to be called at the end of a batch.
91
+ * @param resubmittingBatchId - If defined, indicates this is a resubmission of a batch
92
+ * with the given Batch ID, which must be preserved
93
+ */
94
+ flush(resubmittingBatchId?: BatchId): void;
82
95
  private flushAll;
83
96
  private flushInternal;
84
97
  /**
@@ -106,7 +119,10 @@ export declare class Outbox {
106
119
  * @returns the clientSequenceNumber of the start of the batch, or undefined if nothing was sent
107
120
  */
108
121
  private sendBatch;
109
- checkpoint(): {
122
+ /**
123
+ * @returns A checkpoint object per batch that facilitates iterating over the batch messages when rolling back.
124
+ */
125
+ getBatchCheckpoints(): {
110
126
  mainBatch: IBatchCheckpoint;
111
127
  idAllocationBatch: IBatchCheckpoint;
112
128
  blobAttachBatch: IBatchCheckpoint;
@@ -1 +1 @@
1
- {"version":3,"file":"outbox.d.ts","sourceRoot":"","sources":["../../src/opLifecycle/outbox.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"outbox.d.ts","sourceRoot":"","sources":["../../src/opLifecycle/outbox.ts"],"names":[],"mappings":"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"}