@fluidframework/container-runtime 2.0.0-internal.5.2.0 → 2.0.0-internal.5.3.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 (171) hide show
  1. package/CHANGELOG.md +4 -0
  2. package/dist/blobManager.d.ts +5 -2
  3. package/dist/blobManager.d.ts.map +1 -1
  4. package/dist/blobManager.js +51 -22
  5. package/dist/blobManager.js.map +1 -1
  6. package/dist/connectionTelemetry.d.ts.map +1 -1
  7. package/dist/connectionTelemetry.js +8 -1
  8. package/dist/connectionTelemetry.js.map +1 -1
  9. package/dist/containerRuntime.d.ts +4 -15
  10. package/dist/containerRuntime.d.ts.map +1 -1
  11. package/dist/containerRuntime.js +12 -7
  12. package/dist/containerRuntime.js.map +1 -1
  13. package/dist/dataStoreContext.d.ts.map +1 -1
  14. package/dist/dataStoreContext.js +4 -3
  15. package/dist/dataStoreContext.js.map +1 -1
  16. package/dist/dataStoreContexts.d.ts.map +1 -1
  17. package/dist/dataStoreContexts.js +2 -1
  18. package/dist/dataStoreContexts.js.map +1 -1
  19. package/dist/dataStores.d.ts +1 -2
  20. package/dist/dataStores.d.ts.map +1 -1
  21. package/dist/dataStores.js +2 -1
  22. package/dist/dataStores.js.map +1 -1
  23. package/dist/gc/garbageCollection.d.ts.map +1 -1
  24. package/dist/gc/garbageCollection.js +4 -3
  25. package/dist/gc/garbageCollection.js.map +1 -1
  26. package/dist/gc/gcTelemetry.d.ts +1 -1
  27. package/dist/gc/gcTelemetry.d.ts.map +1 -1
  28. package/dist/gc/gcTelemetry.js.map +1 -1
  29. package/dist/metadata.d.ts +18 -0
  30. package/dist/metadata.d.ts.map +1 -0
  31. package/dist/metadata.js +7 -0
  32. package/dist/metadata.js.map +1 -0
  33. package/dist/opLifecycle/opDecompressor.d.ts.map +1 -1
  34. package/dist/opLifecycle/opDecompressor.js +14 -8
  35. package/dist/opLifecycle/opDecompressor.js.map +1 -1
  36. package/dist/opLifecycle/opGroupingManager.d.ts +1 -1
  37. package/dist/opLifecycle/opGroupingManager.d.ts.map +1 -1
  38. package/dist/opLifecycle/opGroupingManager.js +2 -6
  39. package/dist/opLifecycle/opGroupingManager.js.map +1 -1
  40. package/dist/opLifecycle/opSplitter.d.ts.map +1 -1
  41. package/dist/opLifecycle/opSplitter.js +2 -0
  42. package/dist/opLifecycle/opSplitter.js.map +1 -1
  43. package/dist/opLifecycle/outbox.d.ts +5 -1
  44. package/dist/opLifecycle/outbox.d.ts.map +1 -1
  45. package/dist/opLifecycle/outbox.js +45 -27
  46. package/dist/opLifecycle/outbox.js.map +1 -1
  47. package/dist/opLifecycle/remoteMessageProcessor.d.ts.map +1 -1
  48. package/dist/opLifecycle/remoteMessageProcessor.js +3 -1
  49. package/dist/opLifecycle/remoteMessageProcessor.js.map +1 -1
  50. package/dist/packageVersion.d.ts +1 -1
  51. package/dist/packageVersion.js +1 -1
  52. package/dist/packageVersion.js.map +1 -1
  53. package/dist/pendingStateManager.d.ts +1 -1
  54. package/dist/pendingStateManager.d.ts.map +1 -1
  55. package/dist/pendingStateManager.js +13 -5
  56. package/dist/pendingStateManager.js.map +1 -1
  57. package/dist/scheduleManager.d.ts.map +1 -1
  58. package/dist/scheduleManager.js +8 -2
  59. package/dist/scheduleManager.js.map +1 -1
  60. package/dist/summary/summarizerNode/summarizerNode.d.ts +1 -1
  61. package/dist/summary/summarizerNode/summarizerNode.d.ts.map +1 -1
  62. package/dist/summary/summarizerNode/summarizerNode.js.map +1 -1
  63. package/dist/summary/summarizerNode/summarizerNodeWithGc.d.ts.map +1 -1
  64. package/dist/summary/summarizerNode/summarizerNodeWithGc.js +3 -2
  65. package/dist/summary/summarizerNode/summarizerNodeWithGc.js.map +1 -1
  66. package/dist/summary/summarizerTypes.d.ts +2 -1
  67. package/dist/summary/summarizerTypes.d.ts.map +1 -1
  68. package/dist/summary/summarizerTypes.js.map +1 -1
  69. package/dist/summary/summaryCollection.d.ts.map +1 -1
  70. package/dist/summary/summaryCollection.js +4 -0
  71. package/dist/summary/summaryCollection.js.map +1 -1
  72. package/dist/summary/summaryGenerator.d.ts +1 -1
  73. package/dist/summary/summaryGenerator.d.ts.map +1 -1
  74. package/dist/summary/summaryGenerator.js.map +1 -1
  75. package/lib/blobManager.d.ts +5 -2
  76. package/lib/blobManager.d.ts.map +1 -1
  77. package/lib/blobManager.js +51 -22
  78. package/lib/blobManager.js.map +1 -1
  79. package/lib/connectionTelemetry.d.ts.map +1 -1
  80. package/lib/connectionTelemetry.js +8 -1
  81. package/lib/connectionTelemetry.js.map +1 -1
  82. package/lib/containerRuntime.d.ts +4 -15
  83. package/lib/containerRuntime.d.ts.map +1 -1
  84. package/lib/containerRuntime.js +12 -7
  85. package/lib/containerRuntime.js.map +1 -1
  86. package/lib/dataStoreContext.d.ts.map +1 -1
  87. package/lib/dataStoreContext.js +4 -3
  88. package/lib/dataStoreContext.js.map +1 -1
  89. package/lib/dataStoreContexts.d.ts.map +1 -1
  90. package/lib/dataStoreContexts.js +2 -1
  91. package/lib/dataStoreContexts.js.map +1 -1
  92. package/lib/dataStores.d.ts +1 -2
  93. package/lib/dataStores.d.ts.map +1 -1
  94. package/lib/dataStores.js +2 -1
  95. package/lib/dataStores.js.map +1 -1
  96. package/lib/gc/garbageCollection.d.ts.map +1 -1
  97. package/lib/gc/garbageCollection.js +2 -1
  98. package/lib/gc/garbageCollection.js.map +1 -1
  99. package/lib/gc/gcTelemetry.d.ts +1 -1
  100. package/lib/gc/gcTelemetry.d.ts.map +1 -1
  101. package/lib/gc/gcTelemetry.js.map +1 -1
  102. package/lib/metadata.d.ts +18 -0
  103. package/lib/metadata.d.ts.map +1 -0
  104. package/lib/metadata.js +6 -0
  105. package/lib/metadata.js.map +1 -0
  106. package/lib/opLifecycle/opDecompressor.d.ts.map +1 -1
  107. package/lib/opLifecycle/opDecompressor.js +14 -8
  108. package/lib/opLifecycle/opDecompressor.js.map +1 -1
  109. package/lib/opLifecycle/opGroupingManager.d.ts +1 -1
  110. package/lib/opLifecycle/opGroupingManager.d.ts.map +1 -1
  111. package/lib/opLifecycle/opGroupingManager.js +2 -6
  112. package/lib/opLifecycle/opGroupingManager.js.map +1 -1
  113. package/lib/opLifecycle/opSplitter.d.ts.map +1 -1
  114. package/lib/opLifecycle/opSplitter.js +2 -0
  115. package/lib/opLifecycle/opSplitter.js.map +1 -1
  116. package/lib/opLifecycle/outbox.d.ts +5 -1
  117. package/lib/opLifecycle/outbox.d.ts.map +1 -1
  118. package/lib/opLifecycle/outbox.js +45 -27
  119. package/lib/opLifecycle/outbox.js.map +1 -1
  120. package/lib/opLifecycle/remoteMessageProcessor.d.ts.map +1 -1
  121. package/lib/opLifecycle/remoteMessageProcessor.js +3 -1
  122. package/lib/opLifecycle/remoteMessageProcessor.js.map +1 -1
  123. package/lib/packageVersion.d.ts +1 -1
  124. package/lib/packageVersion.js +1 -1
  125. package/lib/packageVersion.js.map +1 -1
  126. package/lib/pendingStateManager.d.ts +1 -1
  127. package/lib/pendingStateManager.d.ts.map +1 -1
  128. package/lib/pendingStateManager.js +13 -5
  129. package/lib/pendingStateManager.js.map +1 -1
  130. package/lib/scheduleManager.d.ts.map +1 -1
  131. package/lib/scheduleManager.js +8 -2
  132. package/lib/scheduleManager.js.map +1 -1
  133. package/lib/summary/summarizerNode/summarizerNode.d.ts +1 -1
  134. package/lib/summary/summarizerNode/summarizerNode.d.ts.map +1 -1
  135. package/lib/summary/summarizerNode/summarizerNode.js.map +1 -1
  136. package/lib/summary/summarizerNode/summarizerNodeWithGc.d.ts.map +1 -1
  137. package/lib/summary/summarizerNode/summarizerNodeWithGc.js +2 -1
  138. package/lib/summary/summarizerNode/summarizerNodeWithGc.js.map +1 -1
  139. package/lib/summary/summarizerTypes.d.ts +2 -1
  140. package/lib/summary/summarizerTypes.d.ts.map +1 -1
  141. package/lib/summary/summarizerTypes.js.map +1 -1
  142. package/lib/summary/summaryCollection.d.ts.map +1 -1
  143. package/lib/summary/summaryCollection.js +4 -0
  144. package/lib/summary/summaryCollection.js.map +1 -1
  145. package/lib/summary/summaryGenerator.d.ts +1 -1
  146. package/lib/summary/summaryGenerator.d.ts.map +1 -1
  147. package/lib/summary/summaryGenerator.js.map +1 -1
  148. package/package.json +19 -18
  149. package/src/blobManager.ts +68 -27
  150. package/src/connectionTelemetry.ts +10 -1
  151. package/src/containerRuntime.ts +14 -25
  152. package/src/dataStoreContext.ts +5 -4
  153. package/src/dataStoreContexts.ts +2 -1
  154. package/src/dataStores.ts +8 -3
  155. package/src/gc/garbageCollection.ts +2 -1
  156. package/src/gc/gcTelemetry.ts +1 -1
  157. package/src/metadata.ts +19 -0
  158. package/src/opLifecycle/README.md +20 -0
  159. package/src/opLifecycle/opDecompressor.ts +41 -13
  160. package/src/opLifecycle/opGroupingManager.ts +14 -7
  161. package/src/opLifecycle/opSplitter.ts +3 -1
  162. package/src/opLifecycle/outbox.ts +61 -38
  163. package/src/opLifecycle/remoteMessageProcessor.ts +5 -1
  164. package/src/packageVersion.ts +1 -1
  165. package/src/pendingStateManager.ts +20 -9
  166. package/src/scheduleManager.ts +15 -6
  167. package/src/summary/summarizerNode/summarizerNode.ts +1 -1
  168. package/src/summary/summarizerNode/summarizerNodeWithGc.ts +2 -1
  169. package/src/summary/summarizerTypes.ts +2 -1
  170. package/src/summary/summaryCollection.ts +6 -2
  171. package/src/summary/summaryGenerator.ts +1 -1
@@ -56,9 +56,12 @@ export class Outbox {
56
56
  const softLimit = isCompressionEnabled ? Infinity : this.defaultAttachFlowSoftLimitInBytes;
57
57
  this.attachFlowBatch = new BatchManager({ hardLimit, softLimit });
58
58
  this.mainBatch = new BatchManager({ hardLimit });
59
+ this.blobAttachBatch = new BatchManager({ hardLimit });
59
60
  }
60
61
  get isEmpty() {
61
- return this.attachFlowBatch.length === 0 && this.mainBatch.length === 0;
62
+ return (this.attachFlowBatch.length === 0 &&
63
+ this.mainBatch.length === 0 &&
64
+ this.blobAttachBatch.length === 0);
62
65
  }
63
66
  /**
64
67
  * If we detect that the reference sequence number of the incoming message does not match
@@ -69,11 +72,14 @@ export class Outbox {
69
72
  maybeFlushPartialBatch() {
70
73
  const mainBatchSeqNums = this.mainBatch.sequenceNumbers;
71
74
  const attachFlowBatchSeqNums = this.attachFlowBatch.sequenceNumbers;
75
+ const blobAttachSeqNums = this.blobAttachBatch.sequenceNumbers;
72
76
  assert(this.params.config.disablePartialFlush ||
73
- sequenceNumbersMatch(mainBatchSeqNums, attachFlowBatchSeqNums), 0x58d /* Reference sequence numbers from both batches must be in sync */);
77
+ (sequenceNumbersMatch(mainBatchSeqNums, attachFlowBatchSeqNums) &&
78
+ sequenceNumbersMatch(mainBatchSeqNums, blobAttachSeqNums)), 0x58d /* Reference sequence numbers from both batches must be in sync */);
74
79
  const currentSequenceNumbers = this.params.getCurrentSequenceNumbers();
75
80
  if (sequenceNumbersMatch(mainBatchSeqNums, currentSequenceNumbers) &&
76
- sequenceNumbersMatch(attachFlowBatchSeqNums, currentSequenceNumbers)) {
81
+ sequenceNumbersMatch(attachFlowBatchSeqNums, currentSequenceNumbers) &&
82
+ sequenceNumbersMatch(blobAttachSeqNums, currentSequenceNumbers)) {
77
83
  // The reference sequence numbers are stable, there is nothing to do
78
84
  return;
79
85
  }
@@ -85,6 +91,8 @@ export class Outbox {
85
91
  mainClientSequenceNumber: mainBatchSeqNums.clientSequenceNumber,
86
92
  attachReferenceSequenceNumber: attachFlowBatchSeqNums.referenceSequenceNumber,
87
93
  attachClientSequenceNumber: attachFlowBatchSeqNums.clientSequenceNumber,
94
+ blobAttachReferenceSequenceNumber: blobAttachSeqNums.referenceSequenceNumber,
95
+ blobAttachClientSequenceNumber: blobAttachSeqNums.clientSequenceNumber,
88
96
  currentReferenceSequenceNumber: currentSequenceNumbers.referenceSequenceNumber,
89
97
  currentClientSequenceNumber: currentSequenceNumbers.clientSequenceNumber,
90
98
  }, getLongStack(() => new UsageError("Submission of an out of order message")));
@@ -94,33 +102,17 @@ export class Outbox {
94
102
  }
95
103
  }
96
104
  submit(message) {
97
- var _a, _b;
98
105
  this.maybeFlushPartialBatch();
99
- if (!this.mainBatch.push(message, this.isContextReentrant(), this.params.getCurrentSequenceNumbers().clientSequenceNumber)) {
100
- throw new GenericError("BatchTooLarge", /* error */ undefined, {
101
- opSize: (_b = (_a = message.contents) === null || _a === void 0 ? void 0 : _a.length) !== null && _b !== void 0 ? _b : 0,
102
- batchSize: this.mainBatch.contentSizeInBytes,
103
- count: this.mainBatch.length,
104
- limit: this.mainBatch.options.hardLimit,
105
- });
106
- }
106
+ this.addMessageToBatchManager(this.mainBatch, message);
107
107
  }
108
108
  submitAttach(message) {
109
- var _a, _b;
110
109
  this.maybeFlushPartialBatch();
111
110
  if (!this.attachFlowBatch.push(message, this.isContextReentrant(), this.params.getCurrentSequenceNumbers().clientSequenceNumber)) {
112
111
  // BatchManager has two limits - soft limit & hard limit. Soft limit is only engaged
113
112
  // when queue is not empty.
114
113
  // Flush queue & retry. Failure on retry would mean - single message is bigger than hard limit
115
114
  this.flushInternal(this.attachFlowBatch);
116
- if (!this.attachFlowBatch.push(message, this.isContextReentrant(), this.params.getCurrentSequenceNumbers().clientSequenceNumber)) {
117
- throw new GenericError("BatchTooLarge", /* error */ undefined, {
118
- opSize: (_b = (_a = message.contents) === null || _a === void 0 ? void 0 : _a.length) !== null && _b !== void 0 ? _b : 0,
119
- batchSize: this.attachFlowBatch.contentSizeInBytes,
120
- count: this.attachFlowBatch.length,
121
- limit: this.attachFlowBatch.options.hardLimit,
122
- });
123
- }
115
+ this.addMessageToBatchManager(this.attachFlowBatch, message);
124
116
  }
125
117
  // If compression is enabled, we will always successfully receive
126
118
  // attach ops and compress then send them at the next JS turn, regardless
@@ -132,6 +124,30 @@ export class Outbox {
132
124
  this.flushInternal(this.attachFlowBatch);
133
125
  }
134
126
  }
127
+ submitBlobAttach(message) {
128
+ this.maybeFlushPartialBatch();
129
+ this.addMessageToBatchManager(this.blobAttachBatch, message);
130
+ // If compression is enabled, we will always successfully receive
131
+ // blobAttach ops and compress then send them at the next JS turn, regardless
132
+ // of the overall size of the accumulated ops in the batch.
133
+ // However, it is more efficient to flush these ops faster, preferably
134
+ // after they reach a size which would benefit from compression.
135
+ if (this.blobAttachBatch.contentSizeInBytes >=
136
+ this.params.config.compressionOptions.minimumBatchSizeInBytes) {
137
+ this.flushInternal(this.blobAttachBatch);
138
+ }
139
+ }
140
+ addMessageToBatchManager(batchManager, message) {
141
+ var _a, _b;
142
+ if (!batchManager.push(message, this.isContextReentrant(), this.params.getCurrentSequenceNumbers().clientSequenceNumber)) {
143
+ throw new GenericError("BatchTooLarge", /* error */ undefined, {
144
+ opSize: (_b = (_a = message.contents) === null || _a === void 0 ? void 0 : _a.length) !== null && _b !== void 0 ? _b : 0,
145
+ batchSize: batchManager.contentSizeInBytes,
146
+ count: batchManager.length,
147
+ limit: batchManager.options.hardLimit,
148
+ });
149
+ }
150
+ }
135
151
  flush() {
136
152
  if (this.isContextReentrant()) {
137
153
  const error = new UsageError("Flushing is not supported inside DDS event handlers");
@@ -142,14 +158,15 @@ export class Outbox {
142
158
  }
143
159
  flushAll() {
144
160
  this.flushInternal(this.attachFlowBatch);
161
+ this.flushInternal(this.blobAttachBatch, true /* disableGroupedBatching */);
145
162
  this.flushInternal(this.mainBatch);
146
163
  }
147
- flushInternal(batchManager) {
164
+ flushInternal(batchManager, disableGroupedBatching = false) {
148
165
  if (batchManager.empty) {
149
166
  return;
150
167
  }
151
168
  const rawBatch = batchManager.popBatch();
152
- if (rawBatch.hasReentrantOps === true && this.params.config.enableBatchRebasing) {
169
+ if (rawBatch.hasReentrantOps === true && this.params.config.enableGroupedBatching) {
153
170
  assert(!this.rebasing, 0x6fa /* A rebased batch should never have reentrant ops */);
154
171
  // If a batch contains reentrant ops (ops created as a result from processing another op)
155
172
  // it needs to be rebased so that we can ensure consistent reference sequence numbers
@@ -157,7 +174,7 @@ export class Outbox {
157
174
  this.rebase(rawBatch, batchManager);
158
175
  return;
159
176
  }
160
- const processedBatch = this.compressBatch(rawBatch);
177
+ const processedBatch = this.compressBatch(rawBatch, disableGroupedBatching);
161
178
  this.sendBatch(processedBatch);
162
179
  this.persistBatch(rawBatch.content);
163
180
  }
@@ -192,16 +209,16 @@ export class Outbox {
192
209
  isContextReentrant() {
193
210
  return this.params.opReentrancy() && !this.rebasing;
194
211
  }
195
- compressBatch(batch) {
212
+ compressBatch(batch, disableGroupedBatching) {
196
213
  if (batch.content.length === 0 ||
197
214
  this.params.config.compressionOptions === undefined ||
198
215
  this.params.config.compressionOptions.minimumBatchSizeInBytes >
199
216
  batch.contentSizeInBytes ||
200
217
  this.params.containerContext.submitBatchFn === undefined) {
201
218
  // Nothing to do if the batch is empty or if compression is disabled or not supported, or if we don't need to compress
202
- return this.params.groupingManager.groupBatch(batch);
219
+ return disableGroupedBatching ? batch : this.params.groupingManager.groupBatch(batch);
203
220
  }
204
- const compressedBatch = this.params.compressor.compressBatch(this.params.groupingManager.groupBatch(batch));
221
+ const compressedBatch = this.params.compressor.compressBatch(disableGroupedBatching ? batch : this.params.groupingManager.groupBatch(batch));
205
222
  if (this.params.splitter.isBatchChunkingEnabled) {
206
223
  return compressedBatch.contentSizeInBytes <= this.params.splitter.chunkSizeInBytes
207
224
  ? compressedBatch
@@ -276,6 +293,7 @@ export class Outbox {
276
293
  return {
277
294
  mainBatch: this.mainBatch.checkpoint(),
278
295
  attachFlowBatch: this.attachFlowBatch.checkpoint(),
296
+ blobAttachBatch: this.blobAttachBatch.checkpoint(),
279
297
  };
280
298
  }
281
299
  }
@@ -1 +1 @@
1
- {"version":3,"file":"outbox.js","sourceRoot":"","sources":["../../src/opLifecycle/outbox.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 {\n\tITelemetryLoggerExt,\n\tChildLogger,\n\tloggerToMonitoringContext,\n\tMonitoringContext,\n} from \"@fluidframework/telemetry-utils\";\nimport { assert } from \"@fluidframework/common-utils\";\nimport { IContainerContext, ICriticalContainerError } from \"@fluidframework/container-definitions\";\nimport { GenericError, UsageError } from \"@fluidframework/container-utils\";\nimport { MessageType } from \"@fluidframework/protocol-definitions\";\nimport { ICompressionRuntimeOptions } from \"../containerRuntime\";\nimport { IPendingBatchMessage, PendingStateManager } from \"../pendingStateManager\";\nimport {\n\tBatchManager,\n\tBatchSequenceNumbers,\n\testimateSocketSize,\n\tsequenceNumbersMatch,\n} from \"./batchManager\";\nimport { BatchMessage, IBatch } from \"./definitions\";\nimport { OpCompressor } from \"./opCompressor\";\nimport { OpGroupingManager } from \"./opGroupingManager\";\nimport { OpSplitter } from \"./opSplitter\";\n\nexport interface IOutboxConfig {\n\treadonly compressionOptions: ICompressionRuntimeOptions;\n\t// The maximum size of a batch that we can send over the wire.\n\treadonly maxBatchSizeInBytes: number;\n\treadonly disablePartialFlush: boolean;\n\treadonly enableBatchRebasing: boolean;\n}\n\nexport interface IOutboxParameters {\n\treadonly shouldSend: () => boolean;\n\treadonly pendingStateManager: PendingStateManager;\n\treadonly containerContext: IContainerContext;\n\treadonly config: IOutboxConfig;\n\treadonly compressor: OpCompressor;\n\treadonly splitter: OpSplitter;\n\treadonly logger: ITelemetryLoggerExt;\n\treadonly groupingManager: OpGroupingManager;\n\treadonly getCurrentSequenceNumbers: () => BatchSequenceNumbers;\n\treadonly reSubmit: (message: IPendingBatchMessage) => void;\n\treadonly opReentrancy: () => boolean;\n\treadonly closeContainer: (error?: ICriticalContainerError) => void;\n}\n\n/**\n * Temporarily increase the stack limit while executing the provided action.\n * If a negative value is provided for `length`, no stack frames will be collected.\n * If Infinity is provided, all frames will be collected.\n *\n * ADO:4663 - add this to the common packages.\n *\n * @param action - action which returns an error\n * @param length - number of stack frames to collect, 50 if unspecified.\n * @returns the result of the action provided\n */\nexport function getLongStack<T>(action: () => T, length: number = 50): T {\n\tconst errorObj = Error as any;\n\tif (\n\t\t(\n\t\t\tObject.getOwnPropertyDescriptor(errorObj, \"stackTraceLimit\") ||\n\t\t\tObject.getOwnPropertyDescriptor(Object.getPrototypeOf(errorObj), \"stackTraceLimit\") ||\n\t\t\t{}\n\t\t).writable !== true\n\t) {\n\t\treturn action();\n\t}\n\n\tconst originalStackTraceLimit = errorObj.stackTraceLimit;\n\ttry {\n\t\terrorObj.stackTraceLimit = length;\n\t\treturn action();\n\t} finally {\n\t\terrorObj.stackTraceLimit = originalStackTraceLimit;\n\t}\n}\n\nexport class Outbox {\n\tprivate readonly mc: MonitoringContext;\n\tprivate readonly attachFlowBatch: BatchManager;\n\tprivate readonly mainBatch: BatchManager;\n\tprivate readonly defaultAttachFlowSoftLimitInBytes = 320 * 1024;\n\tprivate batchRebasesToReport = 5;\n\tprivate rebasing = false;\n\n\t/**\n\t * Track the number of ops which were detected to have a mismatched\n\t * reference sequence number, in order to self-throttle the telemetry events.\n\t *\n\t * This should be removed as part of ADO:2322\n\t */\n\tprivate readonly maxMismatchedOpsToReport = 3;\n\tprivate mismatchedOpsReported = 0;\n\n\tconstructor(private readonly params: IOutboxParameters) {\n\t\tthis.mc = loggerToMonitoringContext(ChildLogger.create(params.logger, \"Outbox\"));\n\t\tconst isCompressionEnabled =\n\t\t\tthis.params.config.compressionOptions.minimumBatchSizeInBytes !==\n\t\t\tNumber.POSITIVE_INFINITY;\n\t\t// We need to allow infinite size batches if we enable compression\n\t\tconst hardLimit = isCompressionEnabled ? Infinity : this.params.config.maxBatchSizeInBytes;\n\t\tconst softLimit = isCompressionEnabled ? Infinity : this.defaultAttachFlowSoftLimitInBytes;\n\n\t\tthis.attachFlowBatch = new BatchManager({ hardLimit, softLimit });\n\t\tthis.mainBatch = new BatchManager({ hardLimit });\n\t}\n\n\tpublic get isEmpty(): boolean {\n\t\treturn this.attachFlowBatch.length === 0 && this.mainBatch.length === 0;\n\t}\n\n\t/**\n\t * If we detect that the reference sequence number of the incoming message does not match\n\t * what was already in the batch managers, this means that batching has been interrupted so\n\t * we will flush the accumulated messages to account for that and create a new batch with the new\n\t * message as the first message.\n\t */\n\tprivate maybeFlushPartialBatch() {\n\t\tconst mainBatchSeqNums = this.mainBatch.sequenceNumbers;\n\t\tconst attachFlowBatchSeqNums = this.attachFlowBatch.sequenceNumbers;\n\t\tassert(\n\t\t\tthis.params.config.disablePartialFlush ||\n\t\t\t\tsequenceNumbersMatch(mainBatchSeqNums, attachFlowBatchSeqNums),\n\t\t\t0x58d /* Reference sequence numbers from both batches must be in sync */,\n\t\t);\n\n\t\tconst currentSequenceNumbers = this.params.getCurrentSequenceNumbers();\n\n\t\tif (\n\t\t\tsequenceNumbersMatch(mainBatchSeqNums, currentSequenceNumbers) &&\n\t\t\tsequenceNumbersMatch(attachFlowBatchSeqNums, currentSequenceNumbers)\n\t\t) {\n\t\t\t// The reference sequence numbers are stable, there is nothing to do\n\t\t\treturn;\n\t\t}\n\n\t\tif (++this.mismatchedOpsReported <= this.maxMismatchedOpsToReport) {\n\t\t\tthis.mc.logger.sendTelemetryEvent(\n\t\t\t\t{\n\t\t\t\t\tcategory: this.params.config.disablePartialFlush ? \"error\" : \"generic\",\n\t\t\t\t\teventName: \"ReferenceSequenceNumberMismatch\",\n\t\t\t\t\tmainReferenceSequenceNumber: mainBatchSeqNums.referenceSequenceNumber,\n\t\t\t\t\tmainClientSequenceNumber: mainBatchSeqNums.clientSequenceNumber,\n\t\t\t\t\tattachReferenceSequenceNumber: attachFlowBatchSeqNums.referenceSequenceNumber,\n\t\t\t\t\tattachClientSequenceNumber: attachFlowBatchSeqNums.clientSequenceNumber,\n\t\t\t\t\tcurrentReferenceSequenceNumber: currentSequenceNumbers.referenceSequenceNumber,\n\t\t\t\t\tcurrentClientSequenceNumber: currentSequenceNumbers.clientSequenceNumber,\n\t\t\t\t},\n\t\t\t\tgetLongStack(() => new UsageError(\"Submission of an out of order message\")),\n\t\t\t);\n\t\t}\n\n\t\tif (!this.params.config.disablePartialFlush) {\n\t\t\tthis.flushAll();\n\t\t}\n\t}\n\n\tpublic submit(message: BatchMessage) {\n\t\tthis.maybeFlushPartialBatch();\n\n\t\tif (\n\t\t\t!this.mainBatch.push(\n\t\t\t\tmessage,\n\t\t\t\tthis.isContextReentrant(),\n\t\t\t\tthis.params.getCurrentSequenceNumbers().clientSequenceNumber,\n\t\t\t)\n\t\t) {\n\t\t\tthrow new GenericError(\"BatchTooLarge\", /* error */ undefined, {\n\t\t\t\topSize: message.contents?.length ?? 0,\n\t\t\t\tbatchSize: this.mainBatch.contentSizeInBytes,\n\t\t\t\tcount: this.mainBatch.length,\n\t\t\t\tlimit: this.mainBatch.options.hardLimit,\n\t\t\t});\n\t\t}\n\t}\n\n\tpublic submitAttach(message: BatchMessage) {\n\t\tthis.maybeFlushPartialBatch();\n\n\t\tif (\n\t\t\t!this.attachFlowBatch.push(\n\t\t\t\tmessage,\n\t\t\t\tthis.isContextReentrant(),\n\t\t\t\tthis.params.getCurrentSequenceNumbers().clientSequenceNumber,\n\t\t\t)\n\t\t) {\n\t\t\t// BatchManager has two limits - soft limit & hard limit. Soft limit is only engaged\n\t\t\t// when queue is not empty.\n\t\t\t// Flush queue & retry. Failure on retry would mean - single message is bigger than hard limit\n\t\t\tthis.flushInternal(this.attachFlowBatch);\n\t\t\tif (\n\t\t\t\t!this.attachFlowBatch.push(\n\t\t\t\t\tmessage,\n\t\t\t\t\tthis.isContextReentrant(),\n\t\t\t\t\tthis.params.getCurrentSequenceNumbers().clientSequenceNumber,\n\t\t\t\t)\n\t\t\t) {\n\t\t\t\tthrow new GenericError(\"BatchTooLarge\", /* error */ undefined, {\n\t\t\t\t\topSize: message.contents?.length ?? 0,\n\t\t\t\t\tbatchSize: this.attachFlowBatch.contentSizeInBytes,\n\t\t\t\t\tcount: this.attachFlowBatch.length,\n\t\t\t\t\tlimit: this.attachFlowBatch.options.hardLimit,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\t// If compression is enabled, we will always successfully receive\n\t\t// attach ops and compress then send them at the next JS turn, regardless\n\t\t// of the overall size of the accumulated ops in the batch.\n\t\t// However, it is more efficient to flush these ops faster, preferably\n\t\t// after they reach a size which would benefit from compression.\n\t\tif (\n\t\t\tthis.attachFlowBatch.contentSizeInBytes >=\n\t\t\tthis.params.config.compressionOptions.minimumBatchSizeInBytes\n\t\t) {\n\t\t\tthis.flushInternal(this.attachFlowBatch);\n\t\t}\n\t}\n\n\tpublic flush() {\n\t\tif (this.isContextReentrant()) {\n\t\t\tconst error = new UsageError(\"Flushing is not supported inside DDS event handlers\");\n\t\t\tthis.params.closeContainer(error);\n\t\t\tthrow error;\n\t\t}\n\n\t\tthis.flushAll();\n\t}\n\n\tprivate flushAll() {\n\t\tthis.flushInternal(this.attachFlowBatch);\n\t\tthis.flushInternal(this.mainBatch);\n\t}\n\n\tprivate flushInternal(batchManager: BatchManager) {\n\t\tif (batchManager.empty) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst rawBatch = batchManager.popBatch();\n\t\tif (rawBatch.hasReentrantOps === true && this.params.config.enableBatchRebasing) {\n\t\t\tassert(!this.rebasing, 0x6fa /* A rebased batch should never have reentrant ops */);\n\t\t\t// If a batch contains reentrant ops (ops created as a result from processing another op)\n\t\t\t// it needs to be rebased so that we can ensure consistent reference sequence numbers\n\t\t\t// and eventual consistency at the DDS level.\n\t\t\tthis.rebase(rawBatch, batchManager);\n\t\t\treturn;\n\t\t}\n\n\t\tconst processedBatch = this.compressBatch(rawBatch);\n\t\tthis.sendBatch(processedBatch);\n\n\t\tthis.persistBatch(rawBatch.content);\n\t}\n\n\t/**\n\t * Rebases a batch. All the ops in the batch are resubmitted to the runtime and\n\t * they will end up back in the same batch manager they were flushed from and subsequently flushed.\n\t *\n\t * @param rawBatch - the batch to be rebased\n\t */\n\tprivate rebase(rawBatch: IBatch, batchManager: BatchManager) {\n\t\tassert(!this.rebasing, 0x6fb /* Reentrancy */);\n\n\t\tthis.rebasing = true;\n\t\tfor (const message of rawBatch.content) {\n\t\t\tthis.params.reSubmit({\n\t\t\t\t// eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n\t\t\t\tcontent: message.contents!,\n\t\t\t\tlocalOpMetadata: message.localOpMetadata,\n\t\t\t\topMetadata: message.metadata,\n\t\t\t});\n\t\t}\n\n\t\tif (this.batchRebasesToReport > 0) {\n\t\t\tthis.mc.logger.sendTelemetryEvent(\n\t\t\t\t{\n\t\t\t\t\teventName: \"BatchRebase\",\n\t\t\t\t\tlength: rawBatch.content.length,\n\t\t\t\t\treferenceSequenceNumber: rawBatch.referenceSequenceNumber,\n\t\t\t\t},\n\t\t\t\tnew UsageError(\"BatchRebase\"),\n\t\t\t);\n\t\t\tthis.batchRebasesToReport--;\n\t\t}\n\n\t\tthis.flushInternal(batchManager);\n\t\tthis.rebasing = false;\n\t}\n\n\tprivate isContextReentrant(): boolean {\n\t\treturn this.params.opReentrancy() && !this.rebasing;\n\t}\n\n\tprivate compressBatch(batch: IBatch): IBatch {\n\t\tif (\n\t\t\tbatch.content.length === 0 ||\n\t\t\tthis.params.config.compressionOptions === undefined ||\n\t\t\tthis.params.config.compressionOptions.minimumBatchSizeInBytes >\n\t\t\t\tbatch.contentSizeInBytes ||\n\t\t\tthis.params.containerContext.submitBatchFn === undefined\n\t\t) {\n\t\t\t// Nothing to do if the batch is empty or if compression is disabled or not supported, or if we don't need to compress\n\t\t\treturn this.params.groupingManager.groupBatch(batch);\n\t\t}\n\n\t\tconst compressedBatch = this.params.compressor.compressBatch(\n\t\t\tthis.params.groupingManager.groupBatch(batch),\n\t\t);\n\n\t\tif (this.params.splitter.isBatchChunkingEnabled) {\n\t\t\treturn compressedBatch.contentSizeInBytes <= this.params.splitter.chunkSizeInBytes\n\t\t\t\t? compressedBatch\n\t\t\t\t: this.params.splitter.splitFirstBatchMessage(compressedBatch);\n\t\t}\n\n\t\tif (compressedBatch.contentSizeInBytes >= this.params.config.maxBatchSizeInBytes) {\n\t\t\tthrow new GenericError(\"BatchTooLarge\", /* error */ undefined, {\n\t\t\t\tbatchSize: batch.contentSizeInBytes,\n\t\t\t\tcompressedBatchSize: compressedBatch.contentSizeInBytes,\n\t\t\t\tcount: compressedBatch.content.length,\n\t\t\t\tlimit: this.params.config.maxBatchSizeInBytes,\n\t\t\t\tchunkingEnabled: this.params.splitter.isBatchChunkingEnabled,\n\t\t\t\tcompressionOptions: JSON.stringify(this.params.config.compressionOptions),\n\t\t\t\tsocketSize: estimateSocketSize(batch),\n\t\t\t});\n\t\t}\n\n\t\treturn compressedBatch;\n\t}\n\n\t/**\n\t * Sends the batch object to the container context to be sent over the wire.\n\t *\n\t * @param batch - batch to be sent\n\t */\n\tprivate sendBatch(batch: IBatch) {\n\t\tconst length = batch.content.length;\n\n\t\t// Did we disconnect in the middle of turn-based batch?\n\t\t// If so, do nothing, as pending state manager will resubmit it correctly on reconnect.\n\t\tif (length === 0 || !this.params.shouldSend()) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst socketSize = estimateSocketSize(batch);\n\t\tif (socketSize >= this.params.config.maxBatchSizeInBytes) {\n\t\t\tthis.mc.logger.sendPerformanceEvent({\n\t\t\t\teventName: \"LargeBatch\",\n\t\t\t\tlength: batch.content.length,\n\t\t\t\tsizeInBytes: batch.contentSizeInBytes,\n\t\t\t\tsocketSize,\n\t\t\t});\n\t\t}\n\n\t\tif (this.params.containerContext.submitBatchFn === undefined) {\n\t\t\t// Legacy path - supporting old loader versions. Can be removed only when LTS moves above\n\t\t\t// version that has support for batches (submitBatchFn)\n\t\t\tassert(\n\t\t\t\tbatch.content[0].compression === undefined,\n\t\t\t\t0x5a6 /* Compression should not have happened if the loader does not support it */,\n\t\t\t);\n\n\t\t\tfor (const message of batch.content) {\n\t\t\t\tthis.params.containerContext.submitFn(\n\t\t\t\t\tMessageType.Operation,\n\t\t\t\t\t// For back-compat (submitFn only works on deserialized content)\n\t\t\t\t\tmessage.contents === undefined ? undefined : JSON.parse(message.contents),\n\t\t\t\t\ttrue, // batch\n\t\t\t\t\tmessage.metadata,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tthis.params.containerContext.deltaManager.flush();\n\t\t} else {\n\t\t\tassert(\n\t\t\t\tbatch.referenceSequenceNumber !== undefined,\n\t\t\t\t0x58e /* Batch must not be empty */,\n\t\t\t);\n\t\t\tthis.params.containerContext.submitBatchFn(\n\t\t\t\tbatch.content.map((message) => ({\n\t\t\t\t\tcontents: message.contents,\n\t\t\t\t\tmetadata: message.metadata,\n\t\t\t\t\tcompression: message.compression,\n\t\t\t\t\treferenceSequenceNumber: message.referenceSequenceNumber,\n\t\t\t\t})),\n\t\t\t\tbatch.referenceSequenceNumber,\n\t\t\t);\n\t\t}\n\t}\n\n\tprivate persistBatch(batch: BatchMessage[]) {\n\t\t// Let the PendingStateManager know that a message was submitted.\n\t\t// In future, need to shift toward keeping batch as a whole!\n\t\tfor (const message of batch) {\n\t\t\tthis.params.pendingStateManager.onSubmitMessage(\n\t\t\t\t// eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n\t\t\t\tmessage.contents!,\n\t\t\t\tmessage.referenceSequenceNumber,\n\t\t\t\tmessage.localOpMetadata,\n\t\t\t\tmessage.metadata,\n\t\t\t);\n\t\t}\n\t}\n\n\tpublic checkpoint() {\n\t\treturn {\n\t\t\tmainBatch: this.mainBatch.checkpoint(),\n\t\t\tattachFlowBatch: this.attachFlowBatch.checkpoint(),\n\t\t};\n\t}\n}\n"]}
1
+ {"version":3,"file":"outbox.js","sourceRoot":"","sources":["../../src/opLifecycle/outbox.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 {\n\tITelemetryLoggerExt,\n\tChildLogger,\n\tloggerToMonitoringContext,\n\tMonitoringContext,\n} from \"@fluidframework/telemetry-utils\";\nimport { assert } from \"@fluidframework/common-utils\";\nimport { IContainerContext, ICriticalContainerError } from \"@fluidframework/container-definitions\";\nimport { GenericError, UsageError } from \"@fluidframework/container-utils\";\nimport { MessageType } from \"@fluidframework/protocol-definitions\";\nimport { ICompressionRuntimeOptions } from \"../containerRuntime\";\nimport { IPendingBatchMessage, PendingStateManager } from \"../pendingStateManager\";\nimport {\n\tBatchManager,\n\tBatchSequenceNumbers,\n\testimateSocketSize,\n\tsequenceNumbersMatch,\n} from \"./batchManager\";\nimport { BatchMessage, IBatch } from \"./definitions\";\nimport { OpCompressor } from \"./opCompressor\";\nimport { OpGroupingManager } from \"./opGroupingManager\";\nimport { OpSplitter } from \"./opSplitter\";\n\nexport interface IOutboxConfig {\n\treadonly compressionOptions: ICompressionRuntimeOptions;\n\t// The maximum size of a batch that we can send over the wire.\n\treadonly maxBatchSizeInBytes: number;\n\treadonly disablePartialFlush: boolean;\n\treadonly enableGroupedBatching: boolean;\n}\n\nexport interface IOutboxParameters {\n\treadonly shouldSend: () => boolean;\n\treadonly pendingStateManager: PendingStateManager;\n\treadonly containerContext: IContainerContext;\n\treadonly config: IOutboxConfig;\n\treadonly compressor: OpCompressor;\n\treadonly splitter: OpSplitter;\n\treadonly logger: ITelemetryLoggerExt;\n\treadonly groupingManager: OpGroupingManager;\n\treadonly getCurrentSequenceNumbers: () => BatchSequenceNumbers;\n\treadonly reSubmit: (message: IPendingBatchMessage) => void;\n\treadonly opReentrancy: () => boolean;\n\treadonly closeContainer: (error?: ICriticalContainerError) => void;\n}\n\n/**\n * Temporarily increase the stack limit while executing the provided action.\n * If a negative value is provided for `length`, no stack frames will be collected.\n * If Infinity is provided, all frames will be collected.\n *\n * ADO:4663 - add this to the common packages.\n *\n * @param action - action which returns an error\n * @param length - number of stack frames to collect, 50 if unspecified.\n * @returns the result of the action provided\n */\nexport function getLongStack<T>(action: () => T, length: number = 50): T {\n\tconst errorObj = Error as any;\n\tif (\n\t\t(\n\t\t\tObject.getOwnPropertyDescriptor(errorObj, \"stackTraceLimit\") ||\n\t\t\tObject.getOwnPropertyDescriptor(Object.getPrototypeOf(errorObj), \"stackTraceLimit\") ||\n\t\t\t{}\n\t\t).writable !== true\n\t) {\n\t\treturn action();\n\t}\n\n\tconst originalStackTraceLimit = errorObj.stackTraceLimit;\n\ttry {\n\t\terrorObj.stackTraceLimit = length;\n\t\treturn action();\n\t} finally {\n\t\terrorObj.stackTraceLimit = originalStackTraceLimit;\n\t}\n}\n\nexport class Outbox {\n\tprivate readonly mc: MonitoringContext;\n\tprivate readonly attachFlowBatch: BatchManager;\n\tprivate readonly mainBatch: BatchManager;\n\tprivate readonly blobAttachBatch: BatchManager;\n\tprivate readonly defaultAttachFlowSoftLimitInBytes = 320 * 1024;\n\tprivate batchRebasesToReport = 5;\n\tprivate rebasing = false;\n\n\t/**\n\t * Track the number of ops which were detected to have a mismatched\n\t * reference sequence number, in order to self-throttle the telemetry events.\n\t *\n\t * This should be removed as part of ADO:2322\n\t */\n\tprivate readonly maxMismatchedOpsToReport = 3;\n\tprivate mismatchedOpsReported = 0;\n\n\tconstructor(private readonly params: IOutboxParameters) {\n\t\tthis.mc = loggerToMonitoringContext(ChildLogger.create(params.logger, \"Outbox\"));\n\t\tconst isCompressionEnabled =\n\t\t\tthis.params.config.compressionOptions.minimumBatchSizeInBytes !==\n\t\t\tNumber.POSITIVE_INFINITY;\n\t\t// We need to allow infinite size batches if we enable compression\n\t\tconst hardLimit = isCompressionEnabled ? Infinity : this.params.config.maxBatchSizeInBytes;\n\t\tconst softLimit = isCompressionEnabled ? Infinity : this.defaultAttachFlowSoftLimitInBytes;\n\n\t\tthis.attachFlowBatch = new BatchManager({ hardLimit, softLimit });\n\t\tthis.mainBatch = new BatchManager({ hardLimit });\n\t\tthis.blobAttachBatch = new BatchManager({ hardLimit });\n\t}\n\n\tpublic get isEmpty(): boolean {\n\t\treturn (\n\t\t\tthis.attachFlowBatch.length === 0 &&\n\t\t\tthis.mainBatch.length === 0 &&\n\t\t\tthis.blobAttachBatch.length === 0\n\t\t);\n\t}\n\n\t/**\n\t * If we detect that the reference sequence number of the incoming message does not match\n\t * what was already in the batch managers, this means that batching has been interrupted so\n\t * we will flush the accumulated messages to account for that and create a new batch with the new\n\t * message as the first message.\n\t */\n\tprivate maybeFlushPartialBatch() {\n\t\tconst mainBatchSeqNums = this.mainBatch.sequenceNumbers;\n\t\tconst attachFlowBatchSeqNums = this.attachFlowBatch.sequenceNumbers;\n\t\tconst blobAttachSeqNums = this.blobAttachBatch.sequenceNumbers;\n\t\tassert(\n\t\t\tthis.params.config.disablePartialFlush ||\n\t\t\t\t(sequenceNumbersMatch(mainBatchSeqNums, attachFlowBatchSeqNums) &&\n\t\t\t\t\tsequenceNumbersMatch(mainBatchSeqNums, blobAttachSeqNums)),\n\t\t\t0x58d /* Reference sequence numbers from both batches must be in sync */,\n\t\t);\n\n\t\tconst currentSequenceNumbers = this.params.getCurrentSequenceNumbers();\n\n\t\tif (\n\t\t\tsequenceNumbersMatch(mainBatchSeqNums, currentSequenceNumbers) &&\n\t\t\tsequenceNumbersMatch(attachFlowBatchSeqNums, currentSequenceNumbers) &&\n\t\t\tsequenceNumbersMatch(blobAttachSeqNums, currentSequenceNumbers)\n\t\t) {\n\t\t\t// The reference sequence numbers are stable, there is nothing to do\n\t\t\treturn;\n\t\t}\n\n\t\tif (++this.mismatchedOpsReported <= this.maxMismatchedOpsToReport) {\n\t\t\tthis.mc.logger.sendTelemetryEvent(\n\t\t\t\t{\n\t\t\t\t\tcategory: this.params.config.disablePartialFlush ? \"error\" : \"generic\",\n\t\t\t\t\teventName: \"ReferenceSequenceNumberMismatch\",\n\t\t\t\t\tmainReferenceSequenceNumber: mainBatchSeqNums.referenceSequenceNumber,\n\t\t\t\t\tmainClientSequenceNumber: mainBatchSeqNums.clientSequenceNumber,\n\t\t\t\t\tattachReferenceSequenceNumber: attachFlowBatchSeqNums.referenceSequenceNumber,\n\t\t\t\t\tattachClientSequenceNumber: attachFlowBatchSeqNums.clientSequenceNumber,\n\t\t\t\t\tblobAttachReferenceSequenceNumber: blobAttachSeqNums.referenceSequenceNumber,\n\t\t\t\t\tblobAttachClientSequenceNumber: blobAttachSeqNums.clientSequenceNumber,\n\t\t\t\t\tcurrentReferenceSequenceNumber: currentSequenceNumbers.referenceSequenceNumber,\n\t\t\t\t\tcurrentClientSequenceNumber: currentSequenceNumbers.clientSequenceNumber,\n\t\t\t\t},\n\t\t\t\tgetLongStack(() => new UsageError(\"Submission of an out of order message\")),\n\t\t\t);\n\t\t}\n\n\t\tif (!this.params.config.disablePartialFlush) {\n\t\t\tthis.flushAll();\n\t\t}\n\t}\n\n\tpublic submit(message: BatchMessage) {\n\t\tthis.maybeFlushPartialBatch();\n\n\t\tthis.addMessageToBatchManager(this.mainBatch, message);\n\t}\n\n\tpublic submitAttach(message: BatchMessage) {\n\t\tthis.maybeFlushPartialBatch();\n\n\t\tif (\n\t\t\t!this.attachFlowBatch.push(\n\t\t\t\tmessage,\n\t\t\t\tthis.isContextReentrant(),\n\t\t\t\tthis.params.getCurrentSequenceNumbers().clientSequenceNumber,\n\t\t\t)\n\t\t) {\n\t\t\t// BatchManager has two limits - soft limit & hard limit. Soft limit is only engaged\n\t\t\t// when queue is not empty.\n\t\t\t// Flush queue & retry. Failure on retry would mean - single message is bigger than hard limit\n\t\t\tthis.flushInternal(this.attachFlowBatch);\n\n\t\t\tthis.addMessageToBatchManager(this.attachFlowBatch, message);\n\t\t}\n\n\t\t// If compression is enabled, we will always successfully receive\n\t\t// attach ops and compress then send them at the next JS turn, regardless\n\t\t// of the overall size of the accumulated ops in the batch.\n\t\t// However, it is more efficient to flush these ops faster, preferably\n\t\t// after they reach a size which would benefit from compression.\n\t\tif (\n\t\t\tthis.attachFlowBatch.contentSizeInBytes >=\n\t\t\tthis.params.config.compressionOptions.minimumBatchSizeInBytes\n\t\t) {\n\t\t\tthis.flushInternal(this.attachFlowBatch);\n\t\t}\n\t}\n\n\tpublic submitBlobAttach(message: BatchMessage) {\n\t\tthis.maybeFlushPartialBatch();\n\n\t\tthis.addMessageToBatchManager(this.blobAttachBatch, message);\n\n\t\t// If compression is enabled, we will always successfully receive\n\t\t// blobAttach ops and compress then send them at the next JS turn, regardless\n\t\t// of the overall size of the accumulated ops in the batch.\n\t\t// However, it is more efficient to flush these ops faster, preferably\n\t\t// after they reach a size which would benefit from compression.\n\t\tif (\n\t\t\tthis.blobAttachBatch.contentSizeInBytes >=\n\t\t\tthis.params.config.compressionOptions.minimumBatchSizeInBytes\n\t\t) {\n\t\t\tthis.flushInternal(this.blobAttachBatch);\n\t\t}\n\t}\n\n\tprivate addMessageToBatchManager(batchManager: BatchManager, message: BatchMessage) {\n\t\tif (\n\t\t\t!batchManager.push(\n\t\t\t\tmessage,\n\t\t\t\tthis.isContextReentrant(),\n\t\t\t\tthis.params.getCurrentSequenceNumbers().clientSequenceNumber,\n\t\t\t)\n\t\t) {\n\t\t\tthrow new GenericError(\"BatchTooLarge\", /* error */ undefined, {\n\t\t\t\topSize: message.contents?.length ?? 0,\n\t\t\t\tbatchSize: batchManager.contentSizeInBytes,\n\t\t\t\tcount: batchManager.length,\n\t\t\t\tlimit: batchManager.options.hardLimit,\n\t\t\t});\n\t\t}\n\t}\n\n\tpublic flush() {\n\t\tif (this.isContextReentrant()) {\n\t\t\tconst error = new UsageError(\"Flushing is not supported inside DDS event handlers\");\n\t\t\tthis.params.closeContainer(error);\n\t\t\tthrow error;\n\t\t}\n\n\t\tthis.flushAll();\n\t}\n\n\tprivate flushAll() {\n\t\tthis.flushInternal(this.attachFlowBatch);\n\t\tthis.flushInternal(this.blobAttachBatch, true /* disableGroupedBatching */);\n\t\tthis.flushInternal(this.mainBatch);\n\t}\n\n\tprivate flushInternal(batchManager: BatchManager, disableGroupedBatching: boolean = false) {\n\t\tif (batchManager.empty) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst rawBatch = batchManager.popBatch();\n\t\tif (rawBatch.hasReentrantOps === true && this.params.config.enableGroupedBatching) {\n\t\t\tassert(!this.rebasing, 0x6fa /* A rebased batch should never have reentrant ops */);\n\t\t\t// If a batch contains reentrant ops (ops created as a result from processing another op)\n\t\t\t// it needs to be rebased so that we can ensure consistent reference sequence numbers\n\t\t\t// and eventual consistency at the DDS level.\n\t\t\tthis.rebase(rawBatch, batchManager);\n\t\t\treturn;\n\t\t}\n\n\t\tconst processedBatch = this.compressBatch(rawBatch, disableGroupedBatching);\n\t\tthis.sendBatch(processedBatch);\n\n\t\tthis.persistBatch(rawBatch.content);\n\t}\n\n\t/**\n\t * Rebases a batch. All the ops in the batch are resubmitted to the runtime and\n\t * they will end up back in the same batch manager they were flushed from and subsequently flushed.\n\t *\n\t * @param rawBatch - the batch to be rebased\n\t */\n\tprivate rebase(rawBatch: IBatch, batchManager: BatchManager) {\n\t\tassert(!this.rebasing, 0x6fb /* Reentrancy */);\n\n\t\tthis.rebasing = true;\n\t\tfor (const message of rawBatch.content) {\n\t\t\tthis.params.reSubmit({\n\t\t\t\t// eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n\t\t\t\tcontent: message.contents!,\n\t\t\t\tlocalOpMetadata: message.localOpMetadata,\n\t\t\t\topMetadata: message.metadata,\n\t\t\t});\n\t\t}\n\n\t\tif (this.batchRebasesToReport > 0) {\n\t\t\tthis.mc.logger.sendTelemetryEvent(\n\t\t\t\t{\n\t\t\t\t\teventName: \"BatchRebase\",\n\t\t\t\t\tlength: rawBatch.content.length,\n\t\t\t\t\treferenceSequenceNumber: rawBatch.referenceSequenceNumber,\n\t\t\t\t},\n\t\t\t\tnew UsageError(\"BatchRebase\"),\n\t\t\t);\n\t\t\tthis.batchRebasesToReport--;\n\t\t}\n\n\t\tthis.flushInternal(batchManager);\n\t\tthis.rebasing = false;\n\t}\n\n\tprivate isContextReentrant(): boolean {\n\t\treturn this.params.opReentrancy() && !this.rebasing;\n\t}\n\n\tprivate compressBatch(batch: IBatch, disableGroupedBatching: boolean): IBatch {\n\t\tif (\n\t\t\tbatch.content.length === 0 ||\n\t\t\tthis.params.config.compressionOptions === undefined ||\n\t\t\tthis.params.config.compressionOptions.minimumBatchSizeInBytes >\n\t\t\t\tbatch.contentSizeInBytes ||\n\t\t\tthis.params.containerContext.submitBatchFn === undefined\n\t\t) {\n\t\t\t// Nothing to do if the batch is empty or if compression is disabled or not supported, or if we don't need to compress\n\t\t\treturn disableGroupedBatching ? batch : this.params.groupingManager.groupBatch(batch);\n\t\t}\n\n\t\tconst compressedBatch = this.params.compressor.compressBatch(\n\t\t\tdisableGroupedBatching ? batch : this.params.groupingManager.groupBatch(batch),\n\t\t);\n\n\t\tif (this.params.splitter.isBatchChunkingEnabled) {\n\t\t\treturn compressedBatch.contentSizeInBytes <= this.params.splitter.chunkSizeInBytes\n\t\t\t\t? compressedBatch\n\t\t\t\t: this.params.splitter.splitFirstBatchMessage(compressedBatch);\n\t\t}\n\n\t\tif (compressedBatch.contentSizeInBytes >= this.params.config.maxBatchSizeInBytes) {\n\t\t\tthrow new GenericError(\"BatchTooLarge\", /* error */ undefined, {\n\t\t\t\tbatchSize: batch.contentSizeInBytes,\n\t\t\t\tcompressedBatchSize: compressedBatch.contentSizeInBytes,\n\t\t\t\tcount: compressedBatch.content.length,\n\t\t\t\tlimit: this.params.config.maxBatchSizeInBytes,\n\t\t\t\tchunkingEnabled: this.params.splitter.isBatchChunkingEnabled,\n\t\t\t\tcompressionOptions: JSON.stringify(this.params.config.compressionOptions),\n\t\t\t\tsocketSize: estimateSocketSize(batch),\n\t\t\t});\n\t\t}\n\n\t\treturn compressedBatch;\n\t}\n\n\t/**\n\t * Sends the batch object to the container context to be sent over the wire.\n\t *\n\t * @param batch - batch to be sent\n\t */\n\tprivate sendBatch(batch: IBatch) {\n\t\tconst length = batch.content.length;\n\n\t\t// Did we disconnect in the middle of turn-based batch?\n\t\t// If so, do nothing, as pending state manager will resubmit it correctly on reconnect.\n\t\tif (length === 0 || !this.params.shouldSend()) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst socketSize = estimateSocketSize(batch);\n\t\tif (socketSize >= this.params.config.maxBatchSizeInBytes) {\n\t\t\tthis.mc.logger.sendPerformanceEvent({\n\t\t\t\teventName: \"LargeBatch\",\n\t\t\t\tlength: batch.content.length,\n\t\t\t\tsizeInBytes: batch.contentSizeInBytes,\n\t\t\t\tsocketSize,\n\t\t\t});\n\t\t}\n\n\t\tif (this.params.containerContext.submitBatchFn === undefined) {\n\t\t\t// Legacy path - supporting old loader versions. Can be removed only when LTS moves above\n\t\t\t// version that has support for batches (submitBatchFn)\n\t\t\tassert(\n\t\t\t\tbatch.content[0].compression === undefined,\n\t\t\t\t0x5a6 /* Compression should not have happened if the loader does not support it */,\n\t\t\t);\n\n\t\t\tfor (const message of batch.content) {\n\t\t\t\tthis.params.containerContext.submitFn(\n\t\t\t\t\tMessageType.Operation,\n\t\t\t\t\t// For back-compat (submitFn only works on deserialized content)\n\t\t\t\t\tmessage.contents === undefined ? undefined : JSON.parse(message.contents),\n\t\t\t\t\ttrue, // batch\n\t\t\t\t\tmessage.metadata,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tthis.params.containerContext.deltaManager.flush();\n\t\t} else {\n\t\t\tassert(\n\t\t\t\tbatch.referenceSequenceNumber !== undefined,\n\t\t\t\t0x58e /* Batch must not be empty */,\n\t\t\t);\n\t\t\tthis.params.containerContext.submitBatchFn(\n\t\t\t\tbatch.content.map((message) => ({\n\t\t\t\t\tcontents: message.contents,\n\t\t\t\t\tmetadata: message.metadata,\n\t\t\t\t\tcompression: message.compression,\n\t\t\t\t\treferenceSequenceNumber: message.referenceSequenceNumber,\n\t\t\t\t})),\n\t\t\t\tbatch.referenceSequenceNumber,\n\t\t\t);\n\t\t}\n\t}\n\n\tprivate persistBatch(batch: BatchMessage[]) {\n\t\t// Let the PendingStateManager know that a message was submitted.\n\t\t// In future, need to shift toward keeping batch as a whole!\n\t\tfor (const message of batch) {\n\t\t\tthis.params.pendingStateManager.onSubmitMessage(\n\t\t\t\t// eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n\t\t\t\tmessage.contents!,\n\t\t\t\tmessage.referenceSequenceNumber,\n\t\t\t\tmessage.localOpMetadata,\n\t\t\t\tmessage.metadata,\n\t\t\t);\n\t\t}\n\t}\n\n\tpublic checkpoint() {\n\t\treturn {\n\t\t\tmainBatch: this.mainBatch.checkpoint(),\n\t\t\tattachFlowBatch: this.attachFlowBatch.checkpoint(),\n\t\t\tblobAttachBatch: this.blobAttachBatch.checkpoint(),\n\t\t};\n\t}\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"remoteMessageProcessor.d.ts","sourceRoot":"","sources":["../../src/opLifecycle/remoteMessageProcessor.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,yBAAyB,EAAe,MAAM,sCAAsC,CAAC;AAE9F,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAE1C,qBAAa,sBAAsB;IAEjC,OAAO,CAAC,QAAQ,CAAC,UAAU;IAC3B,OAAO,CAAC,QAAQ,CAAC,cAAc;IAC/B,OAAO,CAAC,QAAQ,CAAC,iBAAiB;gBAFjB,UAAU,EAAE,UAAU,EACtB,cAAc,EAAE,cAAc,EAC9B,iBAAiB,EAAE,iBAAiB;IAGtD,IAAW,eAAe,IAAI,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAE1D;IAEM,uBAAuB,CAAC,QAAQ,EAAE,MAAM;IAIxC,OAAO,CAAC,aAAa,EAAE,yBAAyB,GAAG,yBAAyB,EAAE;CAgDrF;AA6BD;;;;;;;;GAQG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,yBAAyB,GAAG,OAAO,CAkBhF"}
1
+ {"version":3,"file":"remoteMessageProcessor.d.ts","sourceRoot":"","sources":["../../src/opLifecycle/remoteMessageProcessor.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,yBAAyB,EAAe,MAAM,sCAAsC,CAAC;AAE9F,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAE1C,qBAAa,sBAAsB;IAEjC,OAAO,CAAC,QAAQ,CAAC,UAAU;IAC3B,OAAO,CAAC,QAAQ,CAAC,cAAc;IAC/B,OAAO,CAAC,QAAQ,CAAC,iBAAiB;gBAFjB,UAAU,EAAE,UAAU,EACtB,cAAc,EAAE,cAAc,EAC9B,iBAAiB,EAAE,iBAAiB;IAGtD,IAAW,eAAe,IAAI,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAE1D;IAEM,uBAAuB,CAAC,QAAQ,EAAE,MAAM;IAIxC,OAAO,CAAC,aAAa,EAAE,yBAAyB,GAAG,yBAAyB,EAAE;CAgDrF;AA6BD;;;;;;;;GAQG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,yBAAyB,GAAG,OAAO,CAsBhF"}
@@ -92,7 +92,9 @@ export function unpackRuntimeMessage(message) {
92
92
  return false;
93
93
  }
94
94
  // legacy op format?
95
- if (message.contents.address !== undefined && message.contents.type === undefined) {
95
+ // TODO: Unsure if this is a real format we should be concerned with. There doesn't appear to be anything prepared to handle the address member.
96
+ if (message.contents.address !== undefined &&
97
+ message.contents.type === undefined) {
96
98
  message.type = ContainerMessageType.FluidDataStoreOp;
97
99
  }
98
100
  else {
@@ -1 +1 @@
1
- {"version":3,"file":"remoteMessageProcessor.js","sourceRoot":"","sources":["../../src/opLifecycle/remoteMessageProcessor.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 { ISequencedDocumentMessage, MessageType } from \"@fluidframework/protocol-definitions\";\nimport { ContainerMessageType, ContainerRuntimeMessage } from \"../containerRuntime\";\nimport { OpDecompressor } from \"./opDecompressor\";\nimport { OpGroupingManager } from \"./opGroupingManager\";\nimport { OpSplitter } from \"./opSplitter\";\n\nexport class RemoteMessageProcessor {\n\tconstructor(\n\t\tprivate readonly opSplitter: OpSplitter,\n\t\tprivate readonly opDecompressor: OpDecompressor,\n\t\tprivate readonly opGroupingManager: OpGroupingManager,\n\t) {}\n\n\tpublic get partialMessages(): ReadonlyMap<string, string[]> {\n\t\treturn this.opSplitter.chunks;\n\t}\n\n\tpublic clearPartialMessagesFor(clientId: string) {\n\t\tthis.opSplitter.clearPartialChunks(clientId);\n\t}\n\n\tpublic process(remoteMessage: ISequencedDocumentMessage): ISequencedDocumentMessage[] {\n\t\tconst result: ISequencedDocumentMessage[] = [];\n\n\t\t// Ungroup before and after decompression for back-compat (cleanup tracked by AB#4371)\n\t\tfor (const ungroupedMessage of this.opGroupingManager.ungroupOp(copy(remoteMessage))) {\n\t\t\tconst message = this.opDecompressor.processMessage(ungroupedMessage).message;\n\n\t\t\tfor (let ungroupedMessage2 of this.opGroupingManager.ungroupOp(message)) {\n\t\t\t\tunpackRuntimeMessage(ungroupedMessage2);\n\n\t\t\t\tconst chunkProcessingResult =\n\t\t\t\t\tthis.opSplitter.processRemoteMessage(ungroupedMessage2);\n\t\t\t\tungroupedMessage2 = chunkProcessingResult.message;\n\t\t\t\tif (chunkProcessingResult.state !== \"Processed\") {\n\t\t\t\t\t// If the message is not chunked or if the splitter is still rebuilding the original message,\n\t\t\t\t\t// there is no need to continue processing\n\t\t\t\t\tresult.push(ungroupedMessage2);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\t// Ungroup before and after decompression for back-compat (cleanup tracked by AB#4371)\n\t\t\t\tfor (const ungroupedMessageAfterChunking of this.opGroupingManager.ungroupOp(\n\t\t\t\t\tungroupedMessage2,\n\t\t\t\t)) {\n\t\t\t\t\tconst decompressionAfterChunking = this.opDecompressor.processMessage(\n\t\t\t\t\t\tungroupedMessageAfterChunking,\n\t\t\t\t\t);\n\n\t\t\t\t\tfor (const ungroupedMessageAfterChunking2 of this.opGroupingManager.ungroupOp(\n\t\t\t\t\t\tdecompressionAfterChunking.message,\n\t\t\t\t\t)) {\n\t\t\t\t\t\tif (decompressionAfterChunking.state === \"Skipped\") {\n\t\t\t\t\t\t\t// After chunking, if the original message was not compressed,\n\t\t\t\t\t\t\t// there is no need to continue processing\n\t\t\t\t\t\t\tresult.push(ungroupedMessageAfterChunking2);\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// The message needs to be unpacked after chunking + decompression\n\t\t\t\t\t\tunpack(ungroupedMessageAfterChunking2);\n\t\t\t\t\t\tresult.push(ungroupedMessageAfterChunking2);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn result;\n\t}\n}\n\nconst copy = (remoteMessage: ISequencedDocumentMessage): ISequencedDocumentMessage => {\n\t// Do shallow copy of message, as the processing flow will modify it.\n\t// There might be multiple container instances receiving same message\n\t// We do not need to make deep copy, as each layer will just replace message.content itself,\n\t// but would not modify contents details\n\tconst message = { ...remoteMessage };\n\n\t// back-compat: ADO #1385: eventually should become unconditional, but only for runtime messages!\n\t// System message may have no contents, or in some cases (mostly for back-compat) they may have actual objects.\n\t// Old ops may contain empty string (I assume noops).\n\tif (typeof message.contents === \"string\" && message.contents !== \"\") {\n\t\tmessage.contents = JSON.parse(message.contents);\n\t}\n\n\treturn message;\n};\n\n/**\n * For a given message, it moves the nested contents and type on level up.\n *\n */\nconst unpack = (message: ISequencedDocumentMessage) => {\n\tconst innerContents = message.contents as ContainerRuntimeMessage;\n\tmessage.type = innerContents.type;\n\tmessage.contents = innerContents.contents;\n};\n\n/**\n * Unpacks runtime messages.\n *\n * @remarks This API makes no promises regarding backward-compatibility. This is internal API.\n * @param message - message (as it observed in storage / service)\n * @returns unpacked runtime message\n *\n * @internal\n */\nexport function unpackRuntimeMessage(message: ISequencedDocumentMessage): boolean {\n\tif (message.type !== MessageType.Operation) {\n\t\t// Legacy format, but it's already \"unpacked\",\n\t\t// i.e. message.type is actually ContainerMessageType.\n\t\t// Or it's non-runtime message.\n\t\t// Nothing to do in such case.\n\t\treturn false;\n\t}\n\n\t// legacy op format?\n\tif (message.contents.address !== undefined && message.contents.type === undefined) {\n\t\tmessage.type = ContainerMessageType.FluidDataStoreOp;\n\t} else {\n\t\t// new format\n\t\tunpack(message);\n\t}\n\n\treturn true;\n}\n"]}
1
+ {"version":3,"file":"remoteMessageProcessor.js","sourceRoot":"","sources":["../../src/opLifecycle/remoteMessageProcessor.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAA6B,WAAW,EAAE,MAAM,sCAAsC,CAAC;AAC9F,OAAO,EAAE,oBAAoB,EAA2B,MAAM,qBAAqB,CAAC;AAKpF,MAAM,OAAO,sBAAsB;IAClC,YACkB,UAAsB,EACtB,cAA8B,EAC9B,iBAAoC;QAFpC,eAAU,GAAV,UAAU,CAAY;QACtB,mBAAc,GAAd,cAAc,CAAgB;QAC9B,sBAAiB,GAAjB,iBAAiB,CAAmB;IACnD,CAAC;IAEJ,IAAW,eAAe;QACzB,OAAO,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC;IAC/B,CAAC;IAEM,uBAAuB,CAAC,QAAgB;QAC9C,IAAI,CAAC,UAAU,CAAC,kBAAkB,CAAC,QAAQ,CAAC,CAAC;IAC9C,CAAC;IAEM,OAAO,CAAC,aAAwC;QACtD,MAAM,MAAM,GAAgC,EAAE,CAAC;QAE/C,sFAAsF;QACtF,KAAK,MAAM,gBAAgB,IAAI,IAAI,CAAC,iBAAiB,CAAC,SAAS,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,EAAE;YACrF,MAAM,OAAO,GAAG,IAAI,CAAC,cAAc,CAAC,cAAc,CAAC,gBAAgB,CAAC,CAAC,OAAO,CAAC;YAE7E,KAAK,IAAI,iBAAiB,IAAI,IAAI,CAAC,iBAAiB,CAAC,SAAS,CAAC,OAAO,CAAC,EAAE;gBACxE,oBAAoB,CAAC,iBAAiB,CAAC,CAAC;gBAExC,MAAM,qBAAqB,GAC1B,IAAI,CAAC,UAAU,CAAC,oBAAoB,CAAC,iBAAiB,CAAC,CAAC;gBACzD,iBAAiB,GAAG,qBAAqB,CAAC,OAAO,CAAC;gBAClD,IAAI,qBAAqB,CAAC,KAAK,KAAK,WAAW,EAAE;oBAChD,6FAA6F;oBAC7F,0CAA0C;oBAC1C,MAAM,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC;oBAC/B,SAAS;iBACT;gBAED,sFAAsF;gBACtF,KAAK,MAAM,6BAA6B,IAAI,IAAI,CAAC,iBAAiB,CAAC,SAAS,CAC3E,iBAAiB,CACjB,EAAE;oBACF,MAAM,0BAA0B,GAAG,IAAI,CAAC,cAAc,CAAC,cAAc,CACpE,6BAA6B,CAC7B,CAAC;oBAEF,KAAK,MAAM,8BAA8B,IAAI,IAAI,CAAC,iBAAiB,CAAC,SAAS,CAC5E,0BAA0B,CAAC,OAAO,CAClC,EAAE;wBACF,IAAI,0BAA0B,CAAC,KAAK,KAAK,SAAS,EAAE;4BACnD,8DAA8D;4BAC9D,0CAA0C;4BAC1C,MAAM,CAAC,IAAI,CAAC,8BAA8B,CAAC,CAAC;4BAC5C,SAAS;yBACT;wBAED,kEAAkE;wBAClE,MAAM,CAAC,8BAA8B,CAAC,CAAC;wBACvC,MAAM,CAAC,IAAI,CAAC,8BAA8B,CAAC,CAAC;qBAC5C;iBACD;aACD;SACD;QAED,OAAO,MAAM,CAAC;IACf,CAAC;CACD;AAED,MAAM,IAAI,GAAG,CAAC,aAAwC,EAA6B,EAAE;IACpF,qEAAqE;IACrE,qEAAqE;IACrE,4FAA4F;IAC5F,wCAAwC;IACxC,MAAM,OAAO,qBAAQ,aAAa,CAAE,CAAC;IAErC,iGAAiG;IACjG,+GAA+G;IAC/G,qDAAqD;IACrD,IAAI,OAAO,OAAO,CAAC,QAAQ,KAAK,QAAQ,IAAI,OAAO,CAAC,QAAQ,KAAK,EAAE,EAAE;QACpE,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;KAChD;IAED,OAAO,OAAO,CAAC;AAChB,CAAC,CAAC;AAEF;;;GAGG;AACH,MAAM,MAAM,GAAG,CAAC,OAAkC,EAAE,EAAE;IACrD,MAAM,aAAa,GAAG,OAAO,CAAC,QAAmC,CAAC;IAClE,OAAO,CAAC,IAAI,GAAG,aAAa,CAAC,IAAI,CAAC;IAClC,OAAO,CAAC,QAAQ,GAAG,aAAa,CAAC,QAAQ,CAAC;AAC3C,CAAC,CAAC;AAEF;;;;;;;;GAQG;AACH,MAAM,UAAU,oBAAoB,CAAC,OAAkC;IACtE,IAAI,OAAO,CAAC,IAAI,KAAK,WAAW,CAAC,SAAS,EAAE;QAC3C,8CAA8C;QAC9C,sDAAsD;QACtD,+BAA+B;QAC/B,8BAA8B;QAC9B,OAAO,KAAK,CAAC;KACb;IAED,oBAAoB;IACpB,gJAAgJ;IAChJ,IACE,OAAO,CAAC,QAAkC,CAAC,OAAO,KAAK,SAAS;QAChE,OAAO,CAAC,QAA+B,CAAC,IAAI,KAAK,SAAS,EAC1D;QACD,OAAO,CAAC,IAAI,GAAG,oBAAoB,CAAC,gBAAgB,CAAC;KACrD;SAAM;QACN,aAAa;QACb,MAAM,CAAC,OAAO,CAAC,CAAC;KAChB;IAED,OAAO,IAAI,CAAC;AACb,CAAC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { ISequencedDocumentMessage, MessageType } from \"@fluidframework/protocol-definitions\";\nimport { ContainerMessageType, ContainerRuntimeMessage } from \"../containerRuntime\";\nimport { OpDecompressor } from \"./opDecompressor\";\nimport { OpGroupingManager } from \"./opGroupingManager\";\nimport { OpSplitter } from \"./opSplitter\";\n\nexport class RemoteMessageProcessor {\n\tconstructor(\n\t\tprivate readonly opSplitter: OpSplitter,\n\t\tprivate readonly opDecompressor: OpDecompressor,\n\t\tprivate readonly opGroupingManager: OpGroupingManager,\n\t) {}\n\n\tpublic get partialMessages(): ReadonlyMap<string, string[]> {\n\t\treturn this.opSplitter.chunks;\n\t}\n\n\tpublic clearPartialMessagesFor(clientId: string) {\n\t\tthis.opSplitter.clearPartialChunks(clientId);\n\t}\n\n\tpublic process(remoteMessage: ISequencedDocumentMessage): ISequencedDocumentMessage[] {\n\t\tconst result: ISequencedDocumentMessage[] = [];\n\n\t\t// Ungroup before and after decompression for back-compat (cleanup tracked by AB#4371)\n\t\tfor (const ungroupedMessage of this.opGroupingManager.ungroupOp(copy(remoteMessage))) {\n\t\t\tconst message = this.opDecompressor.processMessage(ungroupedMessage).message;\n\n\t\t\tfor (let ungroupedMessage2 of this.opGroupingManager.ungroupOp(message)) {\n\t\t\t\tunpackRuntimeMessage(ungroupedMessage2);\n\n\t\t\t\tconst chunkProcessingResult =\n\t\t\t\t\tthis.opSplitter.processRemoteMessage(ungroupedMessage2);\n\t\t\t\tungroupedMessage2 = chunkProcessingResult.message;\n\t\t\t\tif (chunkProcessingResult.state !== \"Processed\") {\n\t\t\t\t\t// If the message is not chunked or if the splitter is still rebuilding the original message,\n\t\t\t\t\t// there is no need to continue processing\n\t\t\t\t\tresult.push(ungroupedMessage2);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\t// Ungroup before and after decompression for back-compat (cleanup tracked by AB#4371)\n\t\t\t\tfor (const ungroupedMessageAfterChunking of this.opGroupingManager.ungroupOp(\n\t\t\t\t\tungroupedMessage2,\n\t\t\t\t)) {\n\t\t\t\t\tconst decompressionAfterChunking = this.opDecompressor.processMessage(\n\t\t\t\t\t\tungroupedMessageAfterChunking,\n\t\t\t\t\t);\n\n\t\t\t\t\tfor (const ungroupedMessageAfterChunking2 of this.opGroupingManager.ungroupOp(\n\t\t\t\t\t\tdecompressionAfterChunking.message,\n\t\t\t\t\t)) {\n\t\t\t\t\t\tif (decompressionAfterChunking.state === \"Skipped\") {\n\t\t\t\t\t\t\t// After chunking, if the original message was not compressed,\n\t\t\t\t\t\t\t// there is no need to continue processing\n\t\t\t\t\t\t\tresult.push(ungroupedMessageAfterChunking2);\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// The message needs to be unpacked after chunking + decompression\n\t\t\t\t\t\tunpack(ungroupedMessageAfterChunking2);\n\t\t\t\t\t\tresult.push(ungroupedMessageAfterChunking2);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn result;\n\t}\n}\n\nconst copy = (remoteMessage: ISequencedDocumentMessage): ISequencedDocumentMessage => {\n\t// Do shallow copy of message, as the processing flow will modify it.\n\t// There might be multiple container instances receiving same message\n\t// We do not need to make deep copy, as each layer will just replace message.content itself,\n\t// but would not modify contents details\n\tconst message = { ...remoteMessage };\n\n\t// back-compat: ADO #1385: eventually should become unconditional, but only for runtime messages!\n\t// System message may have no contents, or in some cases (mostly for back-compat) they may have actual objects.\n\t// Old ops may contain empty string (I assume noops).\n\tif (typeof message.contents === \"string\" && message.contents !== \"\") {\n\t\tmessage.contents = JSON.parse(message.contents);\n\t}\n\n\treturn message;\n};\n\n/**\n * For a given message, it moves the nested contents and type on level up.\n *\n */\nconst unpack = (message: ISequencedDocumentMessage) => {\n\tconst innerContents = message.contents as ContainerRuntimeMessage;\n\tmessage.type = innerContents.type;\n\tmessage.contents = innerContents.contents;\n};\n\n/**\n * Unpacks runtime messages.\n *\n * @remarks This API makes no promises regarding backward-compatibility. This is internal API.\n * @param message - message (as it observed in storage / service)\n * @returns unpacked runtime message\n *\n * @internal\n */\nexport function unpackRuntimeMessage(message: ISequencedDocumentMessage): boolean {\n\tif (message.type !== MessageType.Operation) {\n\t\t// Legacy format, but it's already \"unpacked\",\n\t\t// i.e. message.type is actually ContainerMessageType.\n\t\t// Or it's non-runtime message.\n\t\t// Nothing to do in such case.\n\t\treturn false;\n\t}\n\n\t// legacy op format?\n\t// TODO: Unsure if this is a real format we should be concerned with. There doesn't appear to be anything prepared to handle the address member.\n\tif (\n\t\t(message.contents as { address?: unknown }).address !== undefined &&\n\t\t(message.contents as { type?: unknown }).type === undefined\n\t) {\n\t\tmessage.type = ContainerMessageType.FluidDataStoreOp;\n\t} else {\n\t\t// new format\n\t\tunpack(message);\n\t}\n\n\treturn true;\n}\n"]}
@@ -5,5 +5,5 @@
5
5
  * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY
6
6
  */
7
7
  export declare const pkgName = "@fluidframework/container-runtime";
8
- export declare const pkgVersion = "2.0.0-internal.5.2.0";
8
+ export declare const pkgVersion = "2.0.0-internal.5.3.0";
9
9
  //# sourceMappingURL=packageVersion.d.ts.map
@@ -5,5 +5,5 @@
5
5
  * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY
6
6
  */
7
7
  export const pkgName = "@fluidframework/container-runtime";
8
- export const pkgVersion = "2.0.0-internal.5.2.0";
8
+ export const pkgVersion = "2.0.0-internal.5.3.0";
9
9
  //# sourceMappingURL=packageVersion.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"packageVersion.js","sourceRoot":"","sources":["../src/packageVersion.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,CAAC,MAAM,OAAO,GAAG,mCAAmC,CAAC;AAC3D,MAAM,CAAC,MAAM,UAAU,GAAG,sBAAsB,CAAC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n *\n * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY\n */\n\nexport const pkgName = \"@fluidframework/container-runtime\";\nexport const pkgVersion = \"2.0.0-internal.5.2.0\";\n"]}
1
+ {"version":3,"file":"packageVersion.js","sourceRoot":"","sources":["../src/packageVersion.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,CAAC,MAAM,OAAO,GAAG,mCAAmC,CAAC;AAC3D,MAAM,CAAC,MAAM,UAAU,GAAG,sBAAsB,CAAC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n *\n * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY\n */\n\nexport const pkgName = \"@fluidframework/container-runtime\";\nexport const pkgVersion = \"2.0.0-internal.5.3.0\";\n"]}
@@ -2,7 +2,7 @@
2
2
  * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
3
  * Licensed under the MIT License.
4
4
  */
5
- import { IDisposable } from "@fluidframework/core-interfaces";
5
+ import { IDisposable } from "@fluidframework/common-definitions";
6
6
  import { ICriticalContainerError } from "@fluidframework/container-definitions";
7
7
  import { ISequencedDocumentMessage } from "@fluidframework/protocol-definitions";
8
8
  import { ContainerMessageType } from "./containerRuntime";
@@ -1 +1 @@
1
- {"version":3,"file":"pendingStateManager.d.ts","sourceRoot":"","sources":["../src/pendingStateManager.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"pendingStateManager.d.ts","sourceRoot":"","sources":["../src/pendingStateManager.ts"],"names":[],"mappings":"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"}
@@ -2,8 +2,9 @@
2
2
  * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
3
  * Licensed under the MIT License.
4
4
  */
5
- import { assert, Lazy } from "@fluidframework/common-utils";
5
+ import { assert } from "@fluidframework/common-utils";
6
6
  import { DataProcessingError } from "@fluidframework/container-utils";
7
+ import { Lazy } from "@fluidframework/core-utils";
7
8
  import Deque from "double-ended-queue";
8
9
  import { ContainerMessageType } from "./containerRuntime";
9
10
  import { pkgVersion } from "./packageVersion";
@@ -116,9 +117,14 @@ export class PendingStateManager {
116
117
  throw new Error("loaded from snapshot too recent to apply stashed ops");
117
118
  }
118
119
  }
119
- // applyStashedOp will cause the DDS to behave as if it has sent the op but not actually send it
120
- const localOpMetadata = await this.stateHandler.applyStashedOp(nextMessage.content);
121
- nextMessage.localOpMetadata = localOpMetadata;
120
+ try {
121
+ // applyStashedOp will cause the DDS to behave as if it has sent the op but not actually send it
122
+ const localOpMetadata = await this.stateHandler.applyStashedOp(nextMessage.content);
123
+ nextMessage.localOpMetadata = localOpMetadata;
124
+ }
125
+ catch (error) {
126
+ throw DataProcessingError.wrapIfUnrecognized(error, "applyStashedOp", nextMessage);
127
+ }
122
128
  // then we push onto pendingMessages which will cause PendingStateManager to resubmit when we connect
123
129
  // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
124
130
  this.pendingMessages.push(this.initialMessages.shift());
@@ -188,7 +194,9 @@ export class PendingStateManager {
188
194
  this.stateHandler.close(DataProcessingError.create("Pending batch inconsistency", // Formerly known as asserts 0x16f and 0x170
189
195
  "processPendingLocalMessage", message, {
190
196
  runtimeVersion: pkgVersion,
191
- batchClientId: this.pendingBatchBeginMessage.clientId,
197
+ batchClientId: this.pendingBatchBeginMessage.clientId === null
198
+ ? "null"
199
+ : this.pendingBatchBeginMessage.clientId,
192
200
  clientId: this.stateHandler.clientId(),
193
201
  hasBatchStart: batchBeginMetadata === true,
194
202
  hasBatchEnd: batchEndMetadata === false,
@@ -1 +1 @@
1
- {"version":3,"file":"pendingStateManager.js","sourceRoot":"","sources":["../src/pendingStateManager.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 { IDisposable } from \"@fluidframework/core-interfaces\";\nimport { assert, Lazy } from \"@fluidframework/common-utils\";\nimport { ICriticalContainerError } from \"@fluidframework/container-definitions\";\nimport { DataProcessingError } from \"@fluidframework/container-utils\";\nimport { ISequencedDocumentMessage } from \"@fluidframework/protocol-definitions\";\nimport Deque from \"double-ended-queue\";\nimport { ContainerMessageType } from \"./containerRuntime\";\nimport { pkgVersion } from \"./packageVersion\";\n\n/**\n * ! TODO: Remove this interface in \"2.0.0-internal.7.0.0\" once we only read IPendingMessageNew\n */\nexport interface IPendingMessageOld {\n\ttype: \"message\";\n\tmessageType: ContainerMessageType;\n\tclientSequenceNumber: number;\n\treferenceSequenceNumber: number;\n\tcontent: any;\n\tlocalOpMetadata: unknown;\n\topMetadata: Record<string, unknown> | undefined;\n}\n\n/**\n * This represents a message that has been submitted and is added to the pending queue when `submit` is called on the\n * ContainerRuntime. This message has either not been ack'd by the server or has not been submitted to the server yet.\n */\nexport interface IPendingMessageNew {\n\ttype: \"message\";\n\tclientSequenceNumber: number;\n\treferenceSequenceNumber: number;\n\tcontent: string;\n\tlocalOpMetadata: unknown;\n\topMetadata: Record<string, unknown> | undefined;\n}\n\n/**\n * ! TODO: Remove this type in \"2.0.0-internal.7.0.0\"\n */\nexport type IPendingState = IPendingMessageOld | IPendingMessageNew;\n\nexport interface IPendingLocalState {\n\t/**\n\t * list of pending states, including ops and batch information\n\t */\n\tpendingStates: IPendingState[];\n}\n\nexport interface IPendingBatchMessage {\n\tcontent: string;\n\tlocalOpMetadata: unknown;\n\topMetadata: Record<string, unknown> | undefined;\n}\n\nexport interface IRuntimeStateHandler {\n\tconnected(): boolean;\n\tclientId(): string | undefined;\n\tclose(error?: ICriticalContainerError): void;\n\tapplyStashedOp(content: string): Promise<unknown>;\n\treSubmit(message: IPendingBatchMessage): void;\n\treSubmitBatch(batch: IPendingBatchMessage[]): void;\n}\n\n/**\n * PendingStateManager is responsible for maintaining the messages that have not been sent or have not yet been\n * acknowledged by the server. It also maintains the batch information for both automatically and manually flushed\n * batches along with the messages.\n * When the Container reconnects, it replays the pending states, which includes manual flushing\n * of messages and triggering resubmission of unacked ops.\n *\n * It verifies that all the ops are acked, are received in the right order and batch information is correct.\n */\nexport class PendingStateManager implements IDisposable {\n\tprivate readonly pendingMessages = new Deque<IPendingMessageNew>();\n\tprivate readonly initialMessages = new Deque<IPendingMessageNew>();\n\tprivate readonly disposeOnce = new Lazy<void>(() => {\n\t\tthis.initialMessages.clear();\n\t\tthis.pendingMessages.clear();\n\t});\n\n\tpublic get pendingMessagesCount(): number {\n\t\treturn this.pendingMessages.length;\n\t}\n\n\t// Indicates whether we are processing a batch.\n\tprivate isProcessingBatch: boolean = false;\n\n\t// This stores the first message in the batch that we are processing. This is used to verify that we get\n\t// the correct batch metadata.\n\tprivate pendingBatchBeginMessage: ISequencedDocumentMessage | undefined;\n\n\tprivate clientId: string | undefined;\n\n\t/**\n\t * Called to check if there are any pending messages in the pending message queue.\n\t * @returns A boolean indicating whether there are messages or not.\n\t */\n\tpublic hasPendingMessages(): boolean {\n\t\treturn !this.pendingMessages.isEmpty() || !this.initialMessages.isEmpty();\n\t}\n\n\tpublic getLocalState(): IPendingLocalState | undefined {\n\t\tassert(\n\t\t\tthis.initialMessages.isEmpty(),\n\t\t\t0x2e9 /* \"Must call getLocalState() after applying initial states\" */,\n\t\t);\n\t\tif (!this.pendingMessages.isEmpty()) {\n\t\t\treturn {\n\t\t\t\tpendingStates: this.pendingMessages.toArray().map((message) => {\n\t\t\t\t\t// ! TODO: Remove conversion to IPendingMessageOld in \"2.0.0-internal.6.0.0\" AB#3826\n\t\t\t\t\tconst content = JSON.parse(message.content);\n\t\t\t\t\t// IdAllocations need their localOpMetadata stashed in the contents\n\t\t\t\t\t// of the op to correctly resume the session when processing stashed ops\n\t\t\t\t\tif (content.type === ContainerMessageType.IdAllocation) {\n\t\t\t\t\t\tcontent.contents.stashedState = message.localOpMetadata;\n\t\t\t\t\t}\n\t\t\t\t\treturn {\n\t\t\t\t\t\t...message,\n\t\t\t\t\t\tmessageType: content.type,\n\t\t\t\t\t\tcontent: content.contents,\n\t\t\t\t\t\t// delete localOpMetadata since it may not be serializable\n\t\t\t\t\t\t// and will be regenerated by applyStashedOp()\n\t\t\t\t\t\tlocalOpMetadata: undefined,\n\t\t\t\t\t};\n\t\t\t\t}),\n\t\t\t};\n\t\t}\n\t}\n\n\tconstructor(\n\t\tprivate readonly stateHandler: IRuntimeStateHandler,\n\t\tinitialLocalState: IPendingLocalState | undefined,\n\t) {\n\t\t/**\n\t\t * Convert old local state format to the new format (IPendingMessageOld to IPendingMessageNew)\n\t\t * ! TODO: Remove this conversion in \"2.0.0-internal.7.0.0\"\n\t\t */\n\t\tif (initialLocalState?.pendingStates) {\n\t\t\tfor (const initialState of initialLocalState.pendingStates) {\n\t\t\t\tlet messageContent = initialState.content;\n\t\t\t\tif (\n\t\t\t\t\t(initialState as IPendingMessageOld).messageType !== undefined &&\n\t\t\t\t\ttypeof initialState.content !== \"string\"\n\t\t\t\t) {\n\t\t\t\t\t// Convert IPendingMessageOld to IPendingMessageNew\n\t\t\t\t\tmessageContent = JSON.stringify({\n\t\t\t\t\t\ttype: (initialState as IPendingMessageOld).messageType,\n\t\t\t\t\t\tcontents: initialState.content,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\t// Note: this object may contain \"messageType\" prop, but it should not be easily accesible due to interface being used\n\t\t\t\tthis.initialMessages.push({\n\t\t\t\t\t...initialState,\n\t\t\t\t\tcontent: messageContent,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic get disposed() {\n\t\treturn this.disposeOnce.evaluated;\n\t}\n\tpublic readonly dispose = () => this.disposeOnce.value;\n\n\t/**\n\t * Called when a message is submitted locally. Adds the message and the associated details to the pending state\n\t * queue.\n\t * @param type - The container message type.\n\t * @param content - The message content.\n\t * @param localOpMetadata - The local metadata associated with the message.\n\t */\n\tpublic onSubmitMessage(\n\t\tcontent: string,\n\t\treferenceSequenceNumber: number,\n\t\tlocalOpMetadata: unknown,\n\t\topMetadata: Record<string, unknown> | undefined,\n\t) {\n\t\tconst pendingMessage: IPendingMessageNew = {\n\t\t\ttype: \"message\",\n\t\t\tclientSequenceNumber: -1, // dummy value (not to be used anywhere)\n\t\t\treferenceSequenceNumber,\n\t\t\tcontent,\n\t\t\tlocalOpMetadata,\n\t\t\topMetadata,\n\t\t};\n\n\t\tthis.pendingMessages.push(pendingMessage);\n\t}\n\n\t/**\n\t * Applies stashed ops at their reference sequence number so they are ready to be ACKed or resubmitted\n\t * @param seqNum - Sequence number at which to apply ops. Will apply all ops if seqNum is undefined.\n\t */\n\tpublic async applyStashedOpsAt(seqNum?: number) {\n\t\t// apply stashed ops at sequence number\n\t\twhile (!this.initialMessages.isEmpty()) {\n\t\t\t// eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n\t\t\tconst nextMessage = this.initialMessages.peekFront()!;\n\t\t\tif (seqNum !== undefined) {\n\t\t\t\tif (nextMessage.referenceSequenceNumber > seqNum) {\n\t\t\t\t\tbreak; // nothing left to do at this sequence number\n\t\t\t\t}\n\t\t\t\tif (nextMessage.referenceSequenceNumber < seqNum) {\n\t\t\t\t\tthrow new Error(\"loaded from snapshot too recent to apply stashed ops\");\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// applyStashedOp will cause the DDS to behave as if it has sent the op but not actually send it\n\t\t\tconst localOpMetadata = await this.stateHandler.applyStashedOp(nextMessage.content);\n\t\t\tnextMessage.localOpMetadata = localOpMetadata;\n\n\t\t\t// then we push onto pendingMessages which will cause PendingStateManager to resubmit when we connect\n\t\t\t// eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n\t\t\tthis.pendingMessages.push(this.initialMessages.shift()!);\n\t\t}\n\t}\n\n\t/**\n\t * Processes a local message once its ack'd by the server. It verifies that there was no data corruption and that\n\t * the batch information was preserved for batch messages.\n\t * @param message - The message that got ack'd and needs to be processed.\n\t */\n\tpublic processPendingLocalMessage(message: ISequencedDocumentMessage): unknown {\n\t\t// Pre-processing part - This may be the start of a batch.\n\t\tthis.maybeProcessBatchBegin(message);\n\n\t\t// Get the next message from the pending queue. Verify a message exists.\n\t\tconst pendingMessage = this.pendingMessages.peekFront();\n\t\tassert(\n\t\t\tpendingMessage !== undefined,\n\t\t\t0x169 /* \"No pending message found for this remote message\" */,\n\t\t);\n\t\tthis.pendingMessages.shift();\n\n\t\tconst messageContent = JSON.stringify({ type: message.type, contents: message.contents });\n\t\t// Stringified content does not match\n\t\tif (pendingMessage.content !== messageContent) {\n\t\t\tthis.stateHandler.close(\n\t\t\t\tDataProcessingError.create(\n\t\t\t\t\t\"pending local message content mismatch\",\n\t\t\t\t\t\"unexpectedAckReceived\",\n\t\t\t\t\tmessage,\n\t\t\t\t\t{\n\t\t\t\t\t\texpectedMessageType: JSON.parse(pendingMessage.content).type,\n\t\t\t\t\t},\n\t\t\t\t),\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\n\t\t// Post-processing part - If we are processing a batch then this could be the last message in the batch.\n\t\tthis.maybeProcessBatchEnd(message);\n\n\t\treturn pendingMessage.localOpMetadata;\n\t}\n\n\t/**\n\t * This message could be the first message in batch. If so, set batch state marking the beginning of a batch.\n\t * @param message - The message that is being processed.\n\t */\n\tprivate maybeProcessBatchBegin(message: ISequencedDocumentMessage) {\n\t\t// This message is the first in a batch if the \"batch\" property on the metadata is set to true\n\t\tif (message.metadata?.batch) {\n\t\t\t// We should not already be processing a batch and there should be no pending batch begin message.\n\t\t\tassert(\n\t\t\t\t!this.isProcessingBatch && this.pendingBatchBeginMessage === undefined,\n\t\t\t\t0x16b /* \"The pending batch state indicates we are already processing a batch\" */,\n\t\t\t);\n\n\t\t\t// Set the pending batch state indicating we have started processing a batch.\n\t\t\tthis.pendingBatchBeginMessage = message;\n\t\t\tthis.isProcessingBatch = true;\n\t\t}\n\t}\n\n\t/**\n\t * This message could be the last message in batch. If so, clear batch state since the batch is complete.\n\t * @param message - The message that is being processed.\n\t */\n\tprivate maybeProcessBatchEnd(message: ISequencedDocumentMessage) {\n\t\tif (!this.isProcessingBatch) {\n\t\t\treturn;\n\t\t}\n\n\t\t// There should be a pending batch begin message.\n\t\tassert(\n\t\t\tthis.pendingBatchBeginMessage !== undefined,\n\t\t\t0x16d /* \"There is no pending batch begin message\" */,\n\t\t);\n\n\t\tconst batchEndMetadata = message.metadata?.batch;\n\t\tif (this.pendingMessages.isEmpty() || batchEndMetadata === false) {\n\t\t\t// Get the batch begin metadata from the first message in the batch.\n\t\t\tconst batchBeginMetadata = this.pendingBatchBeginMessage.metadata?.batch;\n\n\t\t\t// There could be just a single message in the batch. If so, it should not have any batch metadata. If there\n\t\t\t// are multiple messages in the batch, verify that we got the correct batch begin and end metadata.\n\t\t\tif (this.pendingBatchBeginMessage === message) {\n\t\t\t\tassert(\n\t\t\t\t\tbatchBeginMetadata === undefined,\n\t\t\t\t\t0x16e /* \"Batch with single message should not have batch metadata\" */,\n\t\t\t\t);\n\t\t\t} else {\n\t\t\t\tif (batchBeginMetadata !== true || batchEndMetadata !== false) {\n\t\t\t\t\tthis.stateHandler.close(\n\t\t\t\t\t\tDataProcessingError.create(\n\t\t\t\t\t\t\t\"Pending batch inconsistency\", // Formerly known as asserts 0x16f and 0x170\n\t\t\t\t\t\t\t\"processPendingLocalMessage\",\n\t\t\t\t\t\t\tmessage,\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\truntimeVersion: pkgVersion,\n\t\t\t\t\t\t\t\tbatchClientId: this.pendingBatchBeginMessage.clientId,\n\t\t\t\t\t\t\t\tclientId: this.stateHandler.clientId(),\n\t\t\t\t\t\t\t\thasBatchStart: batchBeginMetadata === true,\n\t\t\t\t\t\t\t\thasBatchEnd: batchEndMetadata === false,\n\t\t\t\t\t\t\t\tmessageType: message.type,\n\t\t\t\t\t\t\t\tpendingMessagesCount: this.pendingMessagesCount,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t),\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Clear the pending batch state now that we have processed the entire batch.\n\t\t\tthis.pendingBatchBeginMessage = undefined;\n\t\t\tthis.isProcessingBatch = false;\n\t\t}\n\t}\n\n\t/**\n\t * Called when the Container's connection state changes. If the Container gets connected, it replays all the pending\n\t * states in its queue. This includes triggering resubmission of unacked ops.\n\t */\n\tpublic replayPendingStates() {\n\t\tassert(\n\t\t\tthis.stateHandler.connected(),\n\t\t\t0x172 /* \"The connection state is not consistent with the runtime\" */,\n\t\t);\n\n\t\t// This assert suggests we are about to send same ops twice, which will result in data loss.\n\t\tassert(\n\t\t\tthis.clientId !== this.stateHandler.clientId(),\n\t\t\t0x173 /* \"replayPendingStates called twice for same clientId!\" */,\n\t\t);\n\t\tthis.clientId = this.stateHandler.clientId();\n\n\t\tassert(\n\t\t\tthis.initialMessages.isEmpty(),\n\t\t\t0x174 /* \"initial states should be empty before replaying pending\" */,\n\t\t);\n\n\t\tlet pendingMessagesCount = this.pendingMessages.length;\n\t\tif (pendingMessagesCount === 0) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Process exactly `pendingMessagesCount` items in the queue as it represents the number of messages that were\n\t\t// pending when we connected. This is important because the `reSubmitFn` might add more items in the queue\n\t\t// which must not be replayed.\n\t\twhile (pendingMessagesCount > 0) {\n\t\t\t// eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n\t\t\tlet pendingMessage = this.pendingMessages.shift()!;\n\t\t\tpendingMessagesCount--;\n\t\t\tassert(\n\t\t\t\tpendingMessage.opMetadata?.batch !== false,\n\t\t\t\t0x41b /* We cannot process batches in chunks */,\n\t\t\t);\n\n\t\t\t/**\n\t\t\t * We want to ensure grouped messages get processed in a batch.\n\t\t\t * Note: It is not possible for the PendingStateManager to receive a partially acked batch. It will\n\t\t\t * either receive the whole batch ack or nothing at all.\n\t\t\t */\n\t\t\tif (pendingMessage.opMetadata?.batch) {\n\t\t\t\tassert(\n\t\t\t\t\tpendingMessagesCount > 0,\n\t\t\t\t\t0x554 /* Last pending message cannot be a batch begin */,\n\t\t\t\t);\n\n\t\t\t\tconst batch: IPendingBatchMessage[] = [];\n\n\t\t\t\t// check is >= because batch end may be last pending message\n\t\t\t\twhile (pendingMessagesCount >= 0) {\n\t\t\t\t\tbatch.push({\n\t\t\t\t\t\tcontent: pendingMessage.content,\n\t\t\t\t\t\tlocalOpMetadata: pendingMessage.localOpMetadata,\n\t\t\t\t\t\topMetadata: pendingMessage.opMetadata,\n\t\t\t\t\t});\n\n\t\t\t\t\tif (pendingMessage.opMetadata?.batch === false) {\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tassert(pendingMessagesCount > 0, 0x555 /* No batch end found */);\n\n\t\t\t\t\t// eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n\t\t\t\t\tpendingMessage = this.pendingMessages.shift()!;\n\t\t\t\t\tpendingMessagesCount--;\n\t\t\t\t\tassert(\n\t\t\t\t\t\tpendingMessage.opMetadata?.batch !== true,\n\t\t\t\t\t\t0x556 /* Batch start needs a corresponding batch end */,\n\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\tthis.stateHandler.reSubmitBatch(batch);\n\t\t\t} else {\n\t\t\t\tthis.stateHandler.reSubmit({\n\t\t\t\t\tcontent: pendingMessage.content,\n\t\t\t\t\tlocalOpMetadata: pendingMessage.localOpMetadata,\n\t\t\t\t\topMetadata: pendingMessage.opMetadata,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t}\n}\n"]}
1
+ {"version":3,"file":"pendingStateManager.js","sourceRoot":"","sources":["../src/pendingStateManager.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 { IDisposable } from \"@fluidframework/common-definitions\";\nimport { assert } from \"@fluidframework/common-utils\";\nimport { ICriticalContainerError } from \"@fluidframework/container-definitions\";\nimport { DataProcessingError } from \"@fluidframework/container-utils\";\nimport { Lazy } from \"@fluidframework/core-utils\";\nimport { ISequencedDocumentMessage } from \"@fluidframework/protocol-definitions\";\nimport Deque from \"double-ended-queue\";\nimport { ContainerMessageType } from \"./containerRuntime\";\nimport { pkgVersion } from \"./packageVersion\";\nimport { IBatchMetadata } from \"./metadata\";\n\n/**\n * ! TODO: Remove this interface in \"2.0.0-internal.7.0.0\" once we only read IPendingMessageNew\n */\nexport interface IPendingMessageOld {\n\ttype: \"message\";\n\tmessageType: ContainerMessageType;\n\tclientSequenceNumber: number;\n\treferenceSequenceNumber: number;\n\tcontent: any;\n\tlocalOpMetadata: unknown;\n\topMetadata: Record<string, unknown> | undefined;\n}\n\n/**\n * This represents a message that has been submitted and is added to the pending queue when `submit` is called on the\n * ContainerRuntime. This message has either not been ack'd by the server or has not been submitted to the server yet.\n */\nexport interface IPendingMessageNew {\n\ttype: \"message\";\n\tclientSequenceNumber: number;\n\treferenceSequenceNumber: number;\n\tcontent: string;\n\tlocalOpMetadata: unknown;\n\topMetadata: Record<string, unknown> | undefined;\n}\n\n/**\n * ! TODO: Remove this type in \"2.0.0-internal.7.0.0\"\n */\nexport type IPendingState = IPendingMessageOld | IPendingMessageNew;\n\nexport interface IPendingLocalState {\n\t/**\n\t * list of pending states, including ops and batch information\n\t */\n\tpendingStates: IPendingState[];\n}\n\nexport interface IPendingBatchMessage {\n\tcontent: string;\n\tlocalOpMetadata: unknown;\n\topMetadata: Record<string, unknown> | undefined;\n}\n\nexport interface IRuntimeStateHandler {\n\tconnected(): boolean;\n\tclientId(): string | undefined;\n\tclose(error?: ICriticalContainerError): void;\n\tapplyStashedOp(content: string): Promise<unknown>;\n\treSubmit(message: IPendingBatchMessage): void;\n\treSubmitBatch(batch: IPendingBatchMessage[]): void;\n}\n\n/**\n * PendingStateManager is responsible for maintaining the messages that have not been sent or have not yet been\n * acknowledged by the server. It also maintains the batch information for both automatically and manually flushed\n * batches along with the messages.\n * When the Container reconnects, it replays the pending states, which includes manual flushing\n * of messages and triggering resubmission of unacked ops.\n *\n * It verifies that all the ops are acked, are received in the right order and batch information is correct.\n */\nexport class PendingStateManager implements IDisposable {\n\tprivate readonly pendingMessages = new Deque<IPendingMessageNew>();\n\tprivate readonly initialMessages = new Deque<IPendingMessageNew>();\n\tprivate readonly disposeOnce = new Lazy<void>(() => {\n\t\tthis.initialMessages.clear();\n\t\tthis.pendingMessages.clear();\n\t});\n\n\tpublic get pendingMessagesCount(): number {\n\t\treturn this.pendingMessages.length;\n\t}\n\n\t// Indicates whether we are processing a batch.\n\tprivate isProcessingBatch: boolean = false;\n\n\t// This stores the first message in the batch that we are processing. This is used to verify that we get\n\t// the correct batch metadata.\n\tprivate pendingBatchBeginMessage: ISequencedDocumentMessage | undefined;\n\n\tprivate clientId: string | undefined;\n\n\t/**\n\t * Called to check if there are any pending messages in the pending message queue.\n\t * @returns A boolean indicating whether there are messages or not.\n\t */\n\tpublic hasPendingMessages(): boolean {\n\t\treturn !this.pendingMessages.isEmpty() || !this.initialMessages.isEmpty();\n\t}\n\n\tpublic getLocalState(): IPendingLocalState | undefined {\n\t\tassert(\n\t\t\tthis.initialMessages.isEmpty(),\n\t\t\t0x2e9 /* \"Must call getLocalState() after applying initial states\" */,\n\t\t);\n\t\tif (!this.pendingMessages.isEmpty()) {\n\t\t\treturn {\n\t\t\t\tpendingStates: this.pendingMessages.toArray().map((message) => {\n\t\t\t\t\t// ! TODO: Remove conversion to IPendingMessageOld in \"2.0.0-internal.6.0.0\" AB#3826\n\t\t\t\t\tconst content = JSON.parse(message.content);\n\t\t\t\t\t// IdAllocations need their localOpMetadata stashed in the contents\n\t\t\t\t\t// of the op to correctly resume the session when processing stashed ops\n\t\t\t\t\tif (content.type === ContainerMessageType.IdAllocation) {\n\t\t\t\t\t\tcontent.contents.stashedState = message.localOpMetadata;\n\t\t\t\t\t}\n\t\t\t\t\treturn {\n\t\t\t\t\t\t...message,\n\t\t\t\t\t\tmessageType: content.type,\n\t\t\t\t\t\tcontent: content.contents,\n\t\t\t\t\t\t// delete localOpMetadata since it may not be serializable\n\t\t\t\t\t\t// and will be regenerated by applyStashedOp()\n\t\t\t\t\t\tlocalOpMetadata: undefined,\n\t\t\t\t\t};\n\t\t\t\t}),\n\t\t\t};\n\t\t}\n\t}\n\n\tconstructor(\n\t\tprivate readonly stateHandler: IRuntimeStateHandler,\n\t\tinitialLocalState: IPendingLocalState | undefined,\n\t) {\n\t\t/**\n\t\t * Convert old local state format to the new format (IPendingMessageOld to IPendingMessageNew)\n\t\t * ! TODO: Remove this conversion in \"2.0.0-internal.7.0.0\"\n\t\t */\n\t\tif (initialLocalState?.pendingStates) {\n\t\t\tfor (const initialState of initialLocalState.pendingStates) {\n\t\t\t\tlet messageContent = initialState.content;\n\t\t\t\tif (\n\t\t\t\t\t(initialState as IPendingMessageOld).messageType !== undefined &&\n\t\t\t\t\ttypeof initialState.content !== \"string\"\n\t\t\t\t) {\n\t\t\t\t\t// Convert IPendingMessageOld to IPendingMessageNew\n\t\t\t\t\tmessageContent = JSON.stringify({\n\t\t\t\t\t\ttype: (initialState as IPendingMessageOld).messageType,\n\t\t\t\t\t\tcontents: initialState.content,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\t// Note: this object may contain \"messageType\" prop, but it should not be easily accesible due to interface being used\n\t\t\t\tthis.initialMessages.push({\n\t\t\t\t\t...initialState,\n\t\t\t\t\tcontent: messageContent,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic get disposed() {\n\t\treturn this.disposeOnce.evaluated;\n\t}\n\tpublic readonly dispose = () => this.disposeOnce.value;\n\n\t/**\n\t * Called when a message is submitted locally. Adds the message and the associated details to the pending state\n\t * queue.\n\t * @param type - The container message type.\n\t * @param content - The message content.\n\t * @param localOpMetadata - The local metadata associated with the message.\n\t */\n\tpublic onSubmitMessage(\n\t\tcontent: string,\n\t\treferenceSequenceNumber: number,\n\t\tlocalOpMetadata: unknown,\n\t\topMetadata: Record<string, unknown> | undefined,\n\t) {\n\t\tconst pendingMessage: IPendingMessageNew = {\n\t\t\ttype: \"message\",\n\t\t\tclientSequenceNumber: -1, // dummy value (not to be used anywhere)\n\t\t\treferenceSequenceNumber,\n\t\t\tcontent,\n\t\t\tlocalOpMetadata,\n\t\t\topMetadata,\n\t\t};\n\n\t\tthis.pendingMessages.push(pendingMessage);\n\t}\n\n\t/**\n\t * Applies stashed ops at their reference sequence number so they are ready to be ACKed or resubmitted\n\t * @param seqNum - Sequence number at which to apply ops. Will apply all ops if seqNum is undefined.\n\t */\n\tpublic async applyStashedOpsAt(seqNum?: number) {\n\t\t// apply stashed ops at sequence number\n\t\twhile (!this.initialMessages.isEmpty()) {\n\t\t\t// eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n\t\t\tconst nextMessage = this.initialMessages.peekFront()!;\n\t\t\tif (seqNum !== undefined) {\n\t\t\t\tif (nextMessage.referenceSequenceNumber > seqNum) {\n\t\t\t\t\tbreak; // nothing left to do at this sequence number\n\t\t\t\t}\n\t\t\t\tif (nextMessage.referenceSequenceNumber < seqNum) {\n\t\t\t\t\tthrow new Error(\"loaded from snapshot too recent to apply stashed ops\");\n\t\t\t\t}\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\t// applyStashedOp will cause the DDS to behave as if it has sent the op but not actually send it\n\t\t\t\tconst localOpMetadata = await this.stateHandler.applyStashedOp(nextMessage.content);\n\t\t\t\tnextMessage.localOpMetadata = localOpMetadata;\n\t\t\t} catch (error) {\n\t\t\t\tthrow DataProcessingError.wrapIfUnrecognized(error, \"applyStashedOp\", nextMessage);\n\t\t\t}\n\n\t\t\t// then we push onto pendingMessages which will cause PendingStateManager to resubmit when we connect\n\t\t\t// eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n\t\t\tthis.pendingMessages.push(this.initialMessages.shift()!);\n\t\t}\n\t}\n\n\t/**\n\t * Processes a local message once its ack'd by the server. It verifies that there was no data corruption and that\n\t * the batch information was preserved for batch messages.\n\t * @param message - The message that got ack'd and needs to be processed.\n\t */\n\tpublic processPendingLocalMessage(message: ISequencedDocumentMessage): unknown {\n\t\t// Pre-processing part - This may be the start of a batch.\n\t\tthis.maybeProcessBatchBegin(message);\n\n\t\t// Get the next message from the pending queue. Verify a message exists.\n\t\tconst pendingMessage = this.pendingMessages.peekFront();\n\t\tassert(\n\t\t\tpendingMessage !== undefined,\n\t\t\t0x169 /* \"No pending message found for this remote message\" */,\n\t\t);\n\t\tthis.pendingMessages.shift();\n\n\t\tconst messageContent = JSON.stringify({ type: message.type, contents: message.contents });\n\t\t// Stringified content does not match\n\t\tif (pendingMessage.content !== messageContent) {\n\t\t\tthis.stateHandler.close(\n\t\t\t\tDataProcessingError.create(\n\t\t\t\t\t\"pending local message content mismatch\",\n\t\t\t\t\t\"unexpectedAckReceived\",\n\t\t\t\t\tmessage,\n\t\t\t\t\t{\n\t\t\t\t\t\texpectedMessageType: JSON.parse(pendingMessage.content).type,\n\t\t\t\t\t},\n\t\t\t\t),\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\n\t\t// Post-processing part - If we are processing a batch then this could be the last message in the batch.\n\t\tthis.maybeProcessBatchEnd(message);\n\n\t\treturn pendingMessage.localOpMetadata;\n\t}\n\n\t/**\n\t * This message could be the first message in batch. If so, set batch state marking the beginning of a batch.\n\t * @param message - The message that is being processed.\n\t */\n\tprivate maybeProcessBatchBegin(message: ISequencedDocumentMessage) {\n\t\t// This message is the first in a batch if the \"batch\" property on the metadata is set to true\n\t\tif ((message.metadata as IBatchMetadata | undefined)?.batch) {\n\t\t\t// We should not already be processing a batch and there should be no pending batch begin message.\n\t\t\tassert(\n\t\t\t\t!this.isProcessingBatch && this.pendingBatchBeginMessage === undefined,\n\t\t\t\t0x16b /* \"The pending batch state indicates we are already processing a batch\" */,\n\t\t\t);\n\n\t\t\t// Set the pending batch state indicating we have started processing a batch.\n\t\t\tthis.pendingBatchBeginMessage = message;\n\t\t\tthis.isProcessingBatch = true;\n\t\t}\n\t}\n\n\t/**\n\t * This message could be the last message in batch. If so, clear batch state since the batch is complete.\n\t * @param message - The message that is being processed.\n\t */\n\tprivate maybeProcessBatchEnd(message: ISequencedDocumentMessage) {\n\t\tif (!this.isProcessingBatch) {\n\t\t\treturn;\n\t\t}\n\n\t\t// There should be a pending batch begin message.\n\t\tassert(\n\t\t\tthis.pendingBatchBeginMessage !== undefined,\n\t\t\t0x16d /* \"There is no pending batch begin message\" */,\n\t\t);\n\n\t\tconst batchEndMetadata = (message.metadata as IBatchMetadata | undefined)?.batch;\n\t\tif (this.pendingMessages.isEmpty() || batchEndMetadata === false) {\n\t\t\t// Get the batch begin metadata from the first message in the batch.\n\t\t\tconst batchBeginMetadata = (\n\t\t\t\tthis.pendingBatchBeginMessage.metadata as IBatchMetadata | undefined\n\t\t\t)?.batch;\n\n\t\t\t// There could be just a single message in the batch. If so, it should not have any batch metadata. If there\n\t\t\t// are multiple messages in the batch, verify that we got the correct batch begin and end metadata.\n\t\t\tif (this.pendingBatchBeginMessage === message) {\n\t\t\t\tassert(\n\t\t\t\t\tbatchBeginMetadata === undefined,\n\t\t\t\t\t0x16e /* \"Batch with single message should not have batch metadata\" */,\n\t\t\t\t);\n\t\t\t} else {\n\t\t\t\tif (batchBeginMetadata !== true || batchEndMetadata !== false) {\n\t\t\t\t\tthis.stateHandler.close(\n\t\t\t\t\t\tDataProcessingError.create(\n\t\t\t\t\t\t\t\"Pending batch inconsistency\", // Formerly known as asserts 0x16f and 0x170\n\t\t\t\t\t\t\t\"processPendingLocalMessage\",\n\t\t\t\t\t\t\tmessage,\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\truntimeVersion: pkgVersion,\n\t\t\t\t\t\t\t\tbatchClientId:\n\t\t\t\t\t\t\t\t\tthis.pendingBatchBeginMessage.clientId === null\n\t\t\t\t\t\t\t\t\t\t? \"null\"\n\t\t\t\t\t\t\t\t\t\t: this.pendingBatchBeginMessage.clientId,\n\t\t\t\t\t\t\t\tclientId: this.stateHandler.clientId(),\n\t\t\t\t\t\t\t\thasBatchStart: batchBeginMetadata === true,\n\t\t\t\t\t\t\t\thasBatchEnd: batchEndMetadata === false,\n\t\t\t\t\t\t\t\tmessageType: message.type,\n\t\t\t\t\t\t\t\tpendingMessagesCount: this.pendingMessagesCount,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t),\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Clear the pending batch state now that we have processed the entire batch.\n\t\t\tthis.pendingBatchBeginMessage = undefined;\n\t\t\tthis.isProcessingBatch = false;\n\t\t}\n\t}\n\n\t/**\n\t * Called when the Container's connection state changes. If the Container gets connected, it replays all the pending\n\t * states in its queue. This includes triggering resubmission of unacked ops.\n\t */\n\tpublic replayPendingStates() {\n\t\tassert(\n\t\t\tthis.stateHandler.connected(),\n\t\t\t0x172 /* \"The connection state is not consistent with the runtime\" */,\n\t\t);\n\n\t\t// This assert suggests we are about to send same ops twice, which will result in data loss.\n\t\tassert(\n\t\t\tthis.clientId !== this.stateHandler.clientId(),\n\t\t\t0x173 /* \"replayPendingStates called twice for same clientId!\" */,\n\t\t);\n\t\tthis.clientId = this.stateHandler.clientId();\n\n\t\tassert(\n\t\t\tthis.initialMessages.isEmpty(),\n\t\t\t0x174 /* \"initial states should be empty before replaying pending\" */,\n\t\t);\n\n\t\tlet pendingMessagesCount = this.pendingMessages.length;\n\t\tif (pendingMessagesCount === 0) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Process exactly `pendingMessagesCount` items in the queue as it represents the number of messages that were\n\t\t// pending when we connected. This is important because the `reSubmitFn` might add more items in the queue\n\t\t// which must not be replayed.\n\t\twhile (pendingMessagesCount > 0) {\n\t\t\t// eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n\t\t\tlet pendingMessage = this.pendingMessages.shift()!;\n\t\t\tpendingMessagesCount--;\n\t\t\tassert(\n\t\t\t\tpendingMessage.opMetadata?.batch !== false,\n\t\t\t\t0x41b /* We cannot process batches in chunks */,\n\t\t\t);\n\n\t\t\t/**\n\t\t\t * We want to ensure grouped messages get processed in a batch.\n\t\t\t * Note: It is not possible for the PendingStateManager to receive a partially acked batch. It will\n\t\t\t * either receive the whole batch ack or nothing at all.\n\t\t\t */\n\t\t\tif (pendingMessage.opMetadata?.batch) {\n\t\t\t\tassert(\n\t\t\t\t\tpendingMessagesCount > 0,\n\t\t\t\t\t0x554 /* Last pending message cannot be a batch begin */,\n\t\t\t\t);\n\n\t\t\t\tconst batch: IPendingBatchMessage[] = [];\n\n\t\t\t\t// check is >= because batch end may be last pending message\n\t\t\t\twhile (pendingMessagesCount >= 0) {\n\t\t\t\t\tbatch.push({\n\t\t\t\t\t\tcontent: pendingMessage.content,\n\t\t\t\t\t\tlocalOpMetadata: pendingMessage.localOpMetadata,\n\t\t\t\t\t\topMetadata: pendingMessage.opMetadata,\n\t\t\t\t\t});\n\n\t\t\t\t\tif (pendingMessage.opMetadata?.batch === false) {\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tassert(pendingMessagesCount > 0, 0x555 /* No batch end found */);\n\n\t\t\t\t\t// eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n\t\t\t\t\tpendingMessage = this.pendingMessages.shift()!;\n\t\t\t\t\tpendingMessagesCount--;\n\t\t\t\t\tassert(\n\t\t\t\t\t\tpendingMessage.opMetadata?.batch !== true,\n\t\t\t\t\t\t0x556 /* Batch start needs a corresponding batch end */,\n\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\tthis.stateHandler.reSubmitBatch(batch);\n\t\t\t} else {\n\t\t\t\tthis.stateHandler.reSubmit({\n\t\t\t\t\tcontent: pendingMessage.content,\n\t\t\t\t\tlocalOpMetadata: pendingMessage.localOpMetadata,\n\t\t\t\t\topMetadata: pendingMessage.opMetadata,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t}\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"scheduleManager.d.ts","sourceRoot":"","sources":["../src/scheduleManager.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AACtC,OAAO,EAAE,aAAa,EAAE,MAAM,uCAAuC,CAAC;AACtE,OAAO,EAAE,gBAAgB,EAAE,yBAAyB,EAAE,MAAM,sCAAsC,CAAC;AACnG,OAAO,EAAE,mBAAmB,EAAe,MAAM,iCAAiC,CAAC;AAiBnF;;;;;;;;GAQG;AACH,qBAAa,eAAe;IAM1B,OAAO,CAAC,QAAQ,CAAC,YAAY;IAC7B,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,QAAQ,CAAC,WAAW,EAAE,MAAM,MAAM,GAAG,SAAS;IAC9C,OAAO,CAAC,QAAQ,CAAC,MAAM;IARxB,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAiB;IAChD,OAAO,CAAC,aAAa,CAAqB;IAC1C,OAAO,CAAC,QAAQ,CAAS;gBAGP,YAAY,EAAE,aAAa,CAAC,yBAAyB,EAAE,gBAAgB,CAAC,EACxE,OAAO,EAAE,YAAY,EAC7B,WAAW,EAAE,MAAM,MAAM,GAAG,SAAS,EAC7B,MAAM,EAAE,mBAAmB;IAStC,kBAAkB,CAAC,OAAO,EAAE,yBAAyB;IAgBrD,iBAAiB,CAAC,KAAK,EAAE,GAAG,GAAG,SAAS,EAAE,OAAO,EAAE,yBAAyB;CAwBnF"}
1
+ {"version":3,"file":"scheduleManager.d.ts","sourceRoot":"","sources":["../src/scheduleManager.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AACtC,OAAO,EAAE,aAAa,EAAE,MAAM,uCAAuC,CAAC;AACtE,OAAO,EAAE,gBAAgB,EAAE,yBAAyB,EAAE,MAAM,sCAAsC,CAAC;AACnG,OAAO,EAAE,mBAAmB,EAAe,MAAM,iCAAiC,CAAC;AAkBnF;;;;;;;;GAQG;AACH,qBAAa,eAAe;IAM1B,OAAO,CAAC,QAAQ,CAAC,YAAY;IAC7B,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,QAAQ,CAAC,WAAW,EAAE,MAAM,MAAM,GAAG,SAAS;IAC9C,OAAO,CAAC,QAAQ,CAAC,MAAM;IARxB,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAiB;IAChD,OAAO,CAAC,aAAa,CAAqB;IAC1C,OAAO,CAAC,QAAQ,CAAS;gBAGP,YAAY,EAAE,aAAa,CAAC,yBAAyB,EAAE,gBAAgB,CAAC,EACxE,OAAO,EAAE,YAAY,EAC7B,WAAW,EAAE,MAAM,MAAM,GAAG,SAAS,EAC7B,MAAM,EAAE,mBAAmB;IAStC,kBAAkB,CAAC,OAAO,EAAE,yBAAyB;IAkBrD,iBAAiB,CAAC,KAAK,EAAE,GAAG,GAAG,SAAS,EAAE,OAAO,EAAE,yBAAyB;CAwBnF"}
@@ -31,6 +31,8 @@ export class ScheduleManager {
31
31
  this.emitter.emit("batchBegin", message);
32
32
  this.deltaScheduler.batchBegin(message);
33
33
  const batch = (_a = message === null || message === void 0 ? void 0 : message.metadata) === null || _a === void 0 ? void 0 : _a.batch;
34
+ // TODO: Verify whether this should be able to handle server-generated ops (with null clientId)
35
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
34
36
  this.batchClientId = batch ? message.clientId : undefined;
35
37
  }
36
38
  }
@@ -87,6 +89,8 @@ class ScheduleManagerCore {
87
89
  }
88
90
  // Set the batch flag to false on the last message to indicate the end of the send batch
89
91
  const lastMessage = messages[messages.length - 1];
92
+ // TODO: It's not clear if this shallow clone is required, as opposed to just setting "batch" to false.
93
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
90
94
  lastMessage.metadata = Object.assign(Object.assign({}, lastMessage.metadata), { batch: false });
91
95
  });
92
96
  // Listen for updates and peek at the inbound
@@ -186,7 +190,7 @@ class ScheduleManagerCore {
186
190
  throw DataProcessingError.create("Received a system message during batch processing", // Formerly known as assert 0x29a
187
191
  "trackPending", message, {
188
192
  runtimeVersion: pkgVersion,
189
- batchClientId: this.currentBatchClientId,
193
+ batchClientId: this.currentBatchClientId === null ? "null" : this.currentBatchClientId,
190
194
  pauseSequenceNumber: this.pauseSequenceNumber,
191
195
  localBatch: this.currentBatchClientId === this.getClientId(),
192
196
  messageType: message.type,
@@ -205,7 +209,7 @@ class ScheduleManagerCore {
205
209
  // started the current batch (it should not be possible for ops from other clients to get interleaved with a batch).
206
210
  if (this.currentBatchClientId !== undefined &&
207
211
  this.currentBatchClientId !== message.clientId) {
208
- throw new DataCorruptionError("OpBatchIncomplete", Object.assign({ runtimeVersion: pkgVersion, batchClientId: this.currentBatchClientId, pauseSequenceNumber: this.pauseSequenceNumber, localBatch: this.currentBatchClientId === this.getClientId(), localMessage: message.clientId === this.getClientId() }, extractSafePropertiesFromMessage(message)));
212
+ throw new DataCorruptionError("OpBatchIncomplete", Object.assign({ runtimeVersion: pkgVersion, batchClientId: this.currentBatchClientId === null ? "null" : this.currentBatchClientId, pauseSequenceNumber: this.pauseSequenceNumber, localBatch: this.currentBatchClientId === this.getClientId(), localMessage: message.clientId === this.getClientId() }, extractSafePropertiesFromMessage(message)));
209
213
  }
210
214
  // The queue is
211
215
  // 1. paused only when the next message to be processed is the beginning of a batch. Done in two places:
@@ -216,6 +220,8 @@ class ScheduleManagerCore {
216
220
  assert(this.currentBatchClientId === undefined, 0x29e /* "there can't be active batch" */);
217
221
  assert(!this.localPaused, 0x29f /* "we should be processing ops when there is no active batch" */);
218
222
  this.pauseSequenceNumber = message.sequenceNumber;
223
+ // TODO: Verify whether this should be able to handle server-generated ops (with null clientId)
224
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
219
225
  this.currentBatchClientId = message.clientId;
220
226
  // Start of the batch
221
227
  // Only pause processing if queue has no other ops!