@fluidframework/container-runtime 2.1.0-276985 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +4 -0
- package/README.md +71 -18
- package/api-extractor/api-extractor.current.json +5 -0
- package/api-extractor/api-extractor.legacy.json +1 -1
- package/api-extractor.json +1 -1
- package/api-report/container-runtime.legacy.public.api.md +9 -0
- package/container-runtime.test-files.tar +0 -0
- package/dist/blobManager/blobManager.d.ts +10 -0
- package/dist/blobManager/blobManager.d.ts.map +1 -1
- package/dist/blobManager/blobManager.js +19 -0
- package/dist/blobManager/blobManager.js.map +1 -1
- package/dist/channelCollection.d.ts +1 -1
- package/dist/channelCollection.d.ts.map +1 -1
- package/dist/channelCollection.js +40 -8
- package/dist/channelCollection.js.map +1 -1
- package/dist/containerRuntime.d.ts +14 -5
- package/dist/containerRuntime.d.ts.map +1 -1
- package/dist/containerRuntime.js +151 -99
- package/dist/containerRuntime.js.map +1 -1
- package/dist/dataStoreContext.d.ts +4 -0
- package/dist/dataStoreContext.d.ts.map +1 -1
- package/dist/dataStoreContext.js +9 -3
- package/dist/dataStoreContext.js.map +1 -1
- package/dist/gc/garbageCollection.d.ts +1 -1
- package/dist/gc/garbageCollection.d.ts.map +1 -1
- package/dist/gc/garbageCollection.js +14 -8
- package/dist/gc/garbageCollection.js.map +1 -1
- package/dist/gc/gcDefinitions.d.ts +4 -2
- package/dist/gc/gcDefinitions.d.ts.map +1 -1
- package/dist/gc/gcDefinitions.js.map +1 -1
- package/dist/gc/gcHelpers.d.ts.map +1 -1
- package/dist/gc/gcHelpers.js +12 -0
- package/dist/gc/gcHelpers.js.map +1 -1
- package/dist/gc/gcTelemetry.d.ts +3 -2
- package/dist/gc/gcTelemetry.d.ts.map +1 -1
- package/dist/gc/gcTelemetry.js +6 -6
- package/dist/gc/gcTelemetry.js.map +1 -1
- package/dist/legacy.d.ts +1 -1
- package/dist/metadata.d.ts +7 -1
- package/dist/metadata.d.ts.map +1 -1
- package/dist/metadata.js +6 -0
- package/dist/metadata.js.map +1 -1
- package/dist/opLifecycle/batchManager.d.ts +8 -1
- package/dist/opLifecycle/batchManager.d.ts.map +1 -1
- package/dist/opLifecycle/batchManager.js +37 -16
- package/dist/opLifecycle/batchManager.js.map +1 -1
- package/dist/opLifecycle/definitions.d.ts +1 -1
- package/dist/opLifecycle/definitions.d.ts.map +1 -1
- package/dist/opLifecycle/definitions.js.map +1 -1
- package/dist/opLifecycle/index.d.ts +2 -2
- package/dist/opLifecycle/index.d.ts.map +1 -1
- package/dist/opLifecycle/index.js +3 -1
- package/dist/opLifecycle/index.js.map +1 -1
- package/dist/opLifecycle/opCompressor.d.ts.map +1 -1
- package/dist/opLifecycle/opCompressor.js +12 -8
- package/dist/opLifecycle/opCompressor.js.map +1 -1
- package/dist/opLifecycle/opGroupingManager.d.ts.map +1 -1
- package/dist/opLifecycle/opGroupingManager.js +14 -11
- package/dist/opLifecycle/opGroupingManager.js.map +1 -1
- package/dist/opLifecycle/opSplitter.d.ts.map +1 -1
- package/dist/opLifecycle/opSplitter.js +11 -6
- package/dist/opLifecycle/opSplitter.js.map +1 -1
- package/dist/opLifecycle/outbox.d.ts +22 -6
- package/dist/opLifecycle/outbox.d.ts.map +1 -1
- package/dist/opLifecycle/outbox.js +43 -21
- package/dist/opLifecycle/outbox.js.map +1 -1
- package/dist/opLifecycle/remoteMessageProcessor.d.ts +10 -8
- package/dist/opLifecycle/remoteMessageProcessor.d.ts.map +1 -1
- package/dist/opLifecycle/remoteMessageProcessor.js +39 -15
- package/dist/opLifecycle/remoteMessageProcessor.js.map +1 -1
- package/dist/packageVersion.d.ts +1 -1
- package/dist/packageVersion.d.ts.map +1 -1
- package/dist/packageVersion.js +1 -1
- package/dist/packageVersion.js.map +1 -1
- package/dist/pendingStateManager.d.ts +37 -13
- package/dist/pendingStateManager.d.ts.map +1 -1
- package/dist/pendingStateManager.js +95 -45
- package/dist/pendingStateManager.js.map +1 -1
- package/dist/public.d.ts +1 -1
- package/dist/scheduleManager.js +4 -0
- package/dist/scheduleManager.js.map +1 -1
- package/dist/summary/summarizerNode/summarizerNodeUtils.d.ts.map +1 -1
- package/dist/summary/summarizerNode/summarizerNodeUtils.js +2 -0
- package/dist/summary/summarizerNode/summarizerNodeUtils.js.map +1 -1
- package/dist/summary/summaryFormat.d.ts.map +1 -1
- package/dist/summary/summaryFormat.js +4 -1
- package/dist/summary/summaryFormat.js.map +1 -1
- package/internal.d.ts +1 -1
- package/legacy.d.ts +1 -1
- package/lib/blobManager/blobManager.d.ts +10 -0
- package/lib/blobManager/blobManager.d.ts.map +1 -1
- package/lib/blobManager/blobManager.js +19 -0
- package/lib/blobManager/blobManager.js.map +1 -1
- package/lib/channelCollection.d.ts +1 -1
- package/lib/channelCollection.d.ts.map +1 -1
- package/lib/channelCollection.js +40 -8
- package/lib/channelCollection.js.map +1 -1
- package/lib/containerRuntime.d.ts +14 -5
- package/lib/containerRuntime.d.ts.map +1 -1
- package/lib/containerRuntime.js +152 -100
- package/lib/containerRuntime.js.map +1 -1
- package/lib/dataStoreContext.d.ts +4 -0
- package/lib/dataStoreContext.d.ts.map +1 -1
- package/lib/dataStoreContext.js +10 -4
- package/lib/dataStoreContext.js.map +1 -1
- package/lib/gc/garbageCollection.d.ts +1 -1
- package/lib/gc/garbageCollection.d.ts.map +1 -1
- package/lib/gc/garbageCollection.js +14 -8
- package/lib/gc/garbageCollection.js.map +1 -1
- package/lib/gc/gcDefinitions.d.ts +4 -2
- package/lib/gc/gcDefinitions.d.ts.map +1 -1
- package/lib/gc/gcDefinitions.js.map +1 -1
- package/lib/gc/gcHelpers.d.ts.map +1 -1
- package/lib/gc/gcHelpers.js +12 -0
- package/lib/gc/gcHelpers.js.map +1 -1
- package/lib/gc/gcTelemetry.d.ts +3 -2
- package/lib/gc/gcTelemetry.d.ts.map +1 -1
- package/lib/gc/gcTelemetry.js +6 -6
- package/lib/gc/gcTelemetry.js.map +1 -1
- package/lib/legacy.d.ts +1 -1
- package/lib/metadata.d.ts +7 -1
- package/lib/metadata.d.ts.map +1 -1
- package/lib/metadata.js +4 -1
- package/lib/metadata.js.map +1 -1
- package/lib/opLifecycle/batchManager.d.ts +8 -1
- package/lib/opLifecycle/batchManager.d.ts.map +1 -1
- package/lib/opLifecycle/batchManager.js +35 -15
- package/lib/opLifecycle/batchManager.js.map +1 -1
- package/lib/opLifecycle/definitions.d.ts +1 -1
- package/lib/opLifecycle/definitions.d.ts.map +1 -1
- package/lib/opLifecycle/definitions.js.map +1 -1
- package/lib/opLifecycle/index.d.ts +2 -2
- package/lib/opLifecycle/index.d.ts.map +1 -1
- package/lib/opLifecycle/index.js +2 -2
- package/lib/opLifecycle/index.js.map +1 -1
- package/lib/opLifecycle/opCompressor.d.ts.map +1 -1
- package/lib/opLifecycle/opCompressor.js +12 -8
- package/lib/opLifecycle/opCompressor.js.map +1 -1
- package/lib/opLifecycle/opGroupingManager.d.ts.map +1 -1
- package/lib/opLifecycle/opGroupingManager.js +14 -11
- package/lib/opLifecycle/opGroupingManager.js.map +1 -1
- package/lib/opLifecycle/opSplitter.d.ts.map +1 -1
- package/lib/opLifecycle/opSplitter.js +11 -6
- package/lib/opLifecycle/opSplitter.js.map +1 -1
- package/lib/opLifecycle/outbox.d.ts +22 -6
- package/lib/opLifecycle/outbox.d.ts.map +1 -1
- package/lib/opLifecycle/outbox.js +44 -22
- package/lib/opLifecycle/outbox.js.map +1 -1
- package/lib/opLifecycle/remoteMessageProcessor.d.ts +10 -8
- package/lib/opLifecycle/remoteMessageProcessor.d.ts.map +1 -1
- package/lib/opLifecycle/remoteMessageProcessor.js +37 -14
- package/lib/opLifecycle/remoteMessageProcessor.js.map +1 -1
- package/lib/packageVersion.d.ts +1 -1
- package/lib/packageVersion.d.ts.map +1 -1
- package/lib/packageVersion.js +1 -1
- package/lib/packageVersion.js.map +1 -1
- package/lib/pendingStateManager.d.ts +37 -13
- package/lib/pendingStateManager.d.ts.map +1 -1
- package/lib/pendingStateManager.js +95 -45
- package/lib/pendingStateManager.js.map +1 -1
- package/lib/public.d.ts +1 -1
- package/lib/scheduleManager.js +4 -0
- package/lib/scheduleManager.js.map +1 -1
- package/lib/summary/summarizerNode/summarizerNodeUtils.d.ts.map +1 -1
- package/lib/summary/summarizerNode/summarizerNodeUtils.js +2 -0
- package/lib/summary/summarizerNode/summarizerNodeUtils.js.map +1 -1
- package/lib/summary/summaryFormat.d.ts.map +1 -1
- package/lib/summary/summaryFormat.js +4 -1
- package/lib/summary/summaryFormat.js.map +1 -1
- package/package.json +46 -31
- package/src/blobManager/blobManager.ts +19 -0
- package/src/channelCollection.ts +48 -11
- package/src/containerRuntime.ts +203 -133
- package/src/dataStoreContext.ts +22 -4
- package/src/gc/garbageCollection.ts +15 -10
- package/src/gc/gcDefinitions.ts +7 -2
- package/src/gc/gcHelpers.ts +18 -6
- package/src/gc/gcTelemetry.ts +20 -8
- package/src/metadata.ts +11 -1
- package/src/opLifecycle/README.md +0 -8
- package/src/opLifecycle/batchManager.ts +49 -16
- package/src/opLifecycle/definitions.ts +1 -1
- package/src/opLifecycle/index.ts +13 -2
- package/src/opLifecycle/opCompressor.ts +12 -8
- package/src/opLifecycle/opGroupingManager.ts +14 -11
- package/src/opLifecycle/opSplitter.ts +10 -6
- package/src/opLifecycle/outbox.ts +64 -26
- package/src/opLifecycle/remoteMessageProcessor.ts +56 -17
- package/src/packageVersion.ts +1 -1
- package/src/pendingStateManager.ts +173 -74
- package/src/scheduleManager.ts +6 -2
- package/src/summary/README.md +81 -0
- package/src/summary/summarizerNode/summarizerNodeUtils.ts +3 -1
- package/src/summary/summaryFormat.ts +3 -1
- package/src/summary/summaryFormats.md +69 -8
- package/tsconfig.json +0 -1
- package/src/summary/images/appTree.png +0 -0
- package/src/summary/images/protocolAndAppTree.png +0 -0
- package/src/summary/images/summaryTree.png +0 -0
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Licensed under the MIT License.
|
|
4
4
|
*/
|
|
5
5
|
import { assert } from "@fluidframework/core-utils/internal";
|
|
6
|
-
import { GenericError, UsageError,
|
|
6
|
+
import { GenericError, UsageError, createChildLogger, } from "@fluidframework/telemetry-utils/internal";
|
|
7
7
|
import { BatchManager, estimateSocketSize, sequenceNumbersMatch, } from "./batchManager.js";
|
|
8
8
|
/**
|
|
9
9
|
* Temporarily increase the stack limit while executing the provided action.
|
|
@@ -36,6 +36,13 @@ export function getLongStack(action, length = 50) {
|
|
|
36
36
|
errorObj.stackTraceLimit = originalStackTraceLimit;
|
|
37
37
|
}
|
|
38
38
|
}
|
|
39
|
+
/**
|
|
40
|
+
* The Outbox collects messages submitted by the ContainerRuntime into a batch,
|
|
41
|
+
* and then flushes the batch when requested.
|
|
42
|
+
*
|
|
43
|
+
* @remarks There are actually multiple independent batches (some are for a specific message type),
|
|
44
|
+
* to support slight variation in semantics for each batch (e.g. support for rebasing or grouping).
|
|
45
|
+
*/
|
|
39
46
|
export class Outbox {
|
|
40
47
|
constructor(params) {
|
|
41
48
|
this.params = params;
|
|
@@ -49,7 +56,7 @@ export class Outbox {
|
|
|
49
56
|
*/
|
|
50
57
|
this.maxMismatchedOpsToReport = 3;
|
|
51
58
|
this.mismatchedOpsReported = 0;
|
|
52
|
-
this.
|
|
59
|
+
this.logger = createChildLogger({ logger: params.logger, namespace: "Outbox" });
|
|
53
60
|
const isCompressionEnabled = this.params.config.compressionOptions.minimumBatchSizeInBytes !==
|
|
54
61
|
Number.POSITIVE_INFINITY;
|
|
55
62
|
// We need to allow infinite size batches if we enable compression
|
|
@@ -89,7 +96,7 @@ export class Outbox {
|
|
|
89
96
|
return;
|
|
90
97
|
}
|
|
91
98
|
if (++this.mismatchedOpsReported <= this.maxMismatchedOpsToReport) {
|
|
92
|
-
this.
|
|
99
|
+
this.logger.sendTelemetryEvent({
|
|
93
100
|
category: this.params.config.disablePartialFlush ? "error" : "generic",
|
|
94
101
|
eventName: "ReferenceSequenceNumberMismatch",
|
|
95
102
|
mainReferenceSequenceNumber: mainBatchSeqNums.referenceSequenceNumber,
|
|
@@ -135,24 +142,32 @@ export class Outbox {
|
|
|
135
142
|
});
|
|
136
143
|
}
|
|
137
144
|
}
|
|
138
|
-
|
|
145
|
+
/**
|
|
146
|
+
* Flush all the batches to the ordering service.
|
|
147
|
+
* This method is expected to be called at the end of a batch.
|
|
148
|
+
* @param resubmittingBatchId - If defined, indicates this is a resubmission of a batch
|
|
149
|
+
* with the given Batch ID, which must be preserved
|
|
150
|
+
*/
|
|
151
|
+
flush(resubmittingBatchId) {
|
|
139
152
|
if (this.isContextReentrant()) {
|
|
140
153
|
const error = new UsageError("Flushing is not supported inside DDS event handlers");
|
|
141
154
|
this.params.closeContainer(error);
|
|
142
155
|
throw error;
|
|
143
156
|
}
|
|
144
|
-
this.flushAll();
|
|
157
|
+
this.flushAll(resubmittingBatchId);
|
|
145
158
|
}
|
|
146
|
-
flushAll() {
|
|
159
|
+
flushAll(resubmittingBatchId) {
|
|
160
|
+
// Don't use resubmittingBatchId for idAllocationBatch.
|
|
161
|
+
// ID Allocation messages are not directly resubmitted so we don't want to reuse the batch ID.
|
|
147
162
|
this.flushInternal(this.idAllocationBatch);
|
|
148
|
-
this.flushInternal(this.blobAttachBatch, true /* disableGroupedBatching
|
|
149
|
-
this.flushInternal(this.mainBatch);
|
|
163
|
+
this.flushInternal(this.blobAttachBatch, true /* disableGroupedBatching */, resubmittingBatchId);
|
|
164
|
+
this.flushInternal(this.mainBatch, false /* disableGroupedBatching */, resubmittingBatchId);
|
|
150
165
|
}
|
|
151
|
-
flushInternal(batchManager, disableGroupedBatching = false) {
|
|
166
|
+
flushInternal(batchManager, disableGroupedBatching = false, resubmittingBatchId) {
|
|
152
167
|
if (batchManager.empty) {
|
|
153
168
|
return;
|
|
154
169
|
}
|
|
155
|
-
const rawBatch = batchManager.popBatch();
|
|
170
|
+
const rawBatch = batchManager.popBatch(resubmittingBatchId);
|
|
156
171
|
const shouldGroup = !disableGroupedBatching && this.params.groupingManager.shouldGroup(rawBatch);
|
|
157
172
|
if (batchManager.options.canRebase && rawBatch.hasReentrantOps === true && shouldGroup) {
|
|
158
173
|
assert(!this.rebasing, 0x6fa /* A rebased batch should never have reentrant ops */);
|
|
@@ -169,8 +184,9 @@ export class Outbox {
|
|
|
169
184
|
if (this.params.shouldSend()) {
|
|
170
185
|
const processedBatch = this.compressBatch(shouldGroup ? this.params.groupingManager.groupBatch(rawBatch) : rawBatch);
|
|
171
186
|
clientSequenceNumber = this.sendBatch(processedBatch);
|
|
187
|
+
assert(clientSequenceNumber === undefined || clientSequenceNumber >= 0, 0x9d2 /* unexpected negative clientSequenceNumber (empty batch should yield undefined) */);
|
|
172
188
|
}
|
|
173
|
-
this.params.pendingStateManager.onFlushBatch(rawBatch.
|
|
189
|
+
this.params.pendingStateManager.onFlushBatch(rawBatch.messages, clientSequenceNumber);
|
|
174
190
|
}
|
|
175
191
|
/**
|
|
176
192
|
* Rebases a batch. All the ops in the batch are resubmitted to the runtime and
|
|
@@ -182,7 +198,7 @@ export class Outbox {
|
|
|
182
198
|
assert(!this.rebasing, 0x6fb /* Reentrancy */);
|
|
183
199
|
assert(batchManager.options.canRebase, 0x9a7 /* BatchManager does not support rebase */);
|
|
184
200
|
this.rebasing = true;
|
|
185
|
-
for (const message of rawBatch.
|
|
201
|
+
for (const message of rawBatch.messages) {
|
|
186
202
|
this.params.reSubmit({
|
|
187
203
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
188
204
|
content: message.contents,
|
|
@@ -191,9 +207,9 @@ export class Outbox {
|
|
|
191
207
|
});
|
|
192
208
|
}
|
|
193
209
|
if (this.batchRebasesToReport > 0) {
|
|
194
|
-
this.
|
|
210
|
+
this.logger.sendTelemetryEvent({
|
|
195
211
|
eventName: "BatchRebase",
|
|
196
|
-
length: rawBatch.
|
|
212
|
+
length: rawBatch.messages.length,
|
|
197
213
|
referenceSequenceNumber: rawBatch.referenceSequenceNumber,
|
|
198
214
|
}, new UsageError("BatchRebase"));
|
|
199
215
|
this.batchRebasesToReport--;
|
|
@@ -214,7 +230,7 @@ export class Outbox {
|
|
|
214
230
|
* or (C) a batch containing the last chunk (plus empty placeholders from compression if applicable).
|
|
215
231
|
*/
|
|
216
232
|
compressBatch(batch) {
|
|
217
|
-
if (batch.
|
|
233
|
+
if (batch.messages.length === 0 ||
|
|
218
234
|
this.params.config.compressionOptions === undefined ||
|
|
219
235
|
this.params.config.compressionOptions.minimumBatchSizeInBytes >
|
|
220
236
|
batch.contentSizeInBytes ||
|
|
@@ -232,7 +248,7 @@ export class Outbox {
|
|
|
232
248
|
throw new GenericError("BatchTooLarge", /* error */ undefined, {
|
|
233
249
|
batchSize: batch.contentSizeInBytes,
|
|
234
250
|
compressedBatchSize: compressedBatch.contentSizeInBytes,
|
|
235
|
-
count: compressedBatch.
|
|
251
|
+
count: compressedBatch.messages.length,
|
|
236
252
|
limit: this.params.config.maxBatchSizeInBytes,
|
|
237
253
|
chunkingEnabled: this.params.splitter.isBatchChunkingEnabled,
|
|
238
254
|
compressionOptions: JSON.stringify(this.params.config.compressionOptions),
|
|
@@ -248,15 +264,15 @@ export class Outbox {
|
|
|
248
264
|
* @returns the clientSequenceNumber of the start of the batch, or undefined if nothing was sent
|
|
249
265
|
*/
|
|
250
266
|
sendBatch(batch) {
|
|
251
|
-
const length = batch.
|
|
267
|
+
const length = batch.messages.length;
|
|
252
268
|
if (length === 0) {
|
|
253
269
|
return undefined; // Nothing submitted
|
|
254
270
|
}
|
|
255
271
|
const socketSize = estimateSocketSize(batch);
|
|
256
272
|
if (socketSize >= this.params.config.maxBatchSizeInBytes) {
|
|
257
|
-
this.
|
|
273
|
+
this.logger.sendPerformanceEvent({
|
|
258
274
|
eventName: "LargeBatch",
|
|
259
|
-
length: batch.
|
|
275
|
+
length: batch.messages.length,
|
|
260
276
|
sizeInBytes: batch.contentSizeInBytes,
|
|
261
277
|
socketSize,
|
|
262
278
|
});
|
|
@@ -265,12 +281,15 @@ export class Outbox {
|
|
|
265
281
|
if (this.params.submitBatchFn === undefined) {
|
|
266
282
|
// Legacy path - supporting old loader versions. Can be removed only when LTS moves above
|
|
267
283
|
// version that has support for batches (submitBatchFn)
|
|
268
|
-
assert(
|
|
284
|
+
assert(
|
|
285
|
+
// Non null asserting here because of the length check above
|
|
286
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
287
|
+
batch.messages[0].compression === undefined, 0x5a6 /* Compression should not have happened if the loader does not support it */);
|
|
269
288
|
clientSequenceNumber = this.params.legacySendBatchFn(batch);
|
|
270
289
|
}
|
|
271
290
|
else {
|
|
272
291
|
assert(batch.referenceSequenceNumber !== undefined, 0x58e /* Batch must not be empty */);
|
|
273
|
-
clientSequenceNumber = this.params.submitBatchFn(batch.
|
|
292
|
+
clientSequenceNumber = this.params.submitBatchFn(batch.messages.map((message) => ({
|
|
274
293
|
contents: message.contents,
|
|
275
294
|
metadata: message.metadata,
|
|
276
295
|
compression: message.compression,
|
|
@@ -282,7 +301,10 @@ export class Outbox {
|
|
|
282
301
|
assert(clientSequenceNumber >= 0, 0x3d0 /* clientSequenceNumber can't be negative */);
|
|
283
302
|
return clientSequenceNumber;
|
|
284
303
|
}
|
|
285
|
-
|
|
304
|
+
/**
|
|
305
|
+
* @returns A checkpoint object per batch that facilitates iterating over the batch messages when rolling back.
|
|
306
|
+
*/
|
|
307
|
+
getBatchCheckpoints() {
|
|
286
308
|
// This variable is declared with a specific type so that we have a standard import of the IBatchCheckpoint type.
|
|
287
309
|
// When the type is inferred, the generated .d.ts uses a dynamic import which doesn't resolve.
|
|
288
310
|
const mainBatch = this.mainBatch.checkpoint();
|
|
@@ -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 { ICriticalContainerError } from \"@fluidframework/container-definitions\";\nimport { IBatchMessage } from \"@fluidframework/container-definitions/internal\";\nimport { ITelemetryBaseLogger } from \"@fluidframework/core-interfaces\";\nimport { assert } from \"@fluidframework/core-utils/internal\";\nimport {\n\tGenericError,\n\tMonitoringContext,\n\tUsageError,\n\tcreateChildMonitoringContext,\n} from \"@fluidframework/telemetry-utils/internal\";\n\nimport { ICompressionRuntimeOptions } from \"../containerRuntime.js\";\nimport { IPendingBatchMessage, PendingStateManager } from \"../pendingStateManager.js\";\n\nimport {\n\tBatchManager,\n\tBatchSequenceNumbers,\n\testimateSocketSize,\n\tsequenceNumbersMatch,\n} from \"./batchManager.js\";\nimport { BatchMessage, IBatch, IBatchCheckpoint } from \"./definitions.js\";\nimport { OpCompressor } from \"./opCompressor.js\";\nimport { OpGroupingManager } from \"./opGroupingManager.js\";\nimport { OpSplitter } from \"./opSplitter.js\";\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}\n\nexport interface IOutboxParameters {\n\treadonly shouldSend: () => boolean;\n\treadonly pendingStateManager: PendingStateManager;\n\treadonly submitBatchFn:\n\t\t| ((batch: IBatchMessage[], referenceSequenceNumber?: number) => number)\n\t\t| undefined;\n\treadonly legacySendBatchFn: (batch: IBatch) => number;\n\treadonly config: IOutboxConfig;\n\treadonly compressor: OpCompressor;\n\treadonly splitter: OpSplitter;\n\treadonly logger: ITelemetryBaseLogger;\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/* eslint-disable @typescript-eslint/prefer-nullish-coalescing */\n\t\t// ?? is not logically equivalent when the first clause returns false.\n\t\t(\n\t\t\tObject.getOwnPropertyDescriptor(errorObj, \"stackTraceLimit\") ||\n\t\t\tObject.getOwnPropertyDescriptor(Object.getPrototypeOf(errorObj), \"stackTraceLimit\")\n\t\t)?.writable !== true\n\t\t/* eslint-enable @typescript-eslint/prefer-nullish-coalescing */\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 mainBatch: BatchManager;\n\tprivate readonly blobAttachBatch: BatchManager;\n\tprivate readonly idAllocationBatch: BatchManager;\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 = createChildMonitoringContext({ logger: params.logger, namespace: \"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\n\t\tthis.mainBatch = new BatchManager({ hardLimit, canRebase: true });\n\t\tthis.blobAttachBatch = new BatchManager({ hardLimit, canRebase: true });\n\t\tthis.idAllocationBatch = new BatchManager({ hardLimit, canRebase: false });\n\t}\n\n\tpublic get messageCount(): number {\n\t\treturn this.mainBatch.length + this.blobAttachBatch.length + this.idAllocationBatch.length;\n\t}\n\n\tpublic get isEmpty(): boolean {\n\t\treturn this.messageCount === 0;\n\t}\n\n\t/**\n\t * Detect whether batching has been interrupted by an incoming message being processed. In this case,\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\t * @remarks - To detect batch interruption, we compare both the reference sequence number\n\t * (i.e. last message processed by DeltaManager) and the client sequence number of the\n\t * last message processed by the ContainerRuntime. In the absence of op reentrancy, this\n\t * pair will remain stable during a single JS turn during which the batch is being built up.\n\t */\n\tprivate maybeFlushPartialBatch() {\n\t\tconst mainBatchSeqNums = this.mainBatch.sequenceNumbers;\n\t\tconst blobAttachSeqNums = this.blobAttachBatch.sequenceNumbers;\n\t\tconst idAllocSeqNums = this.idAllocationBatch.sequenceNumbers;\n\t\tassert(\n\t\t\tthis.params.config.disablePartialFlush ||\n\t\t\t\t(sequenceNumbersMatch(mainBatchSeqNums, blobAttachSeqNums) &&\n\t\t\t\t\tsequenceNumbersMatch(mainBatchSeqNums, idAllocSeqNums)),\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(blobAttachSeqNums, currentSequenceNumbers) &&\n\t\t\tsequenceNumbersMatch(idAllocSeqNums, 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\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 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\tpublic submitIdAllocation(message: BatchMessage) {\n\t\tthis.maybeFlushPartialBatch();\n\n\t\tthis.addMessageToBatchManager(this.idAllocationBatch, message);\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.idAllocationBatch);\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\tconst shouldGroup =\n\t\t\t!disableGroupedBatching && this.params.groupingManager.shouldGroup(rawBatch);\n\t\tif (batchManager.options.canRebase && rawBatch.hasReentrantOps === true && shouldGroup) {\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\tlet clientSequenceNumber: number | undefined;\n\t\t// Did we disconnect? (i.e. is shouldSend false?)\n\t\t// If so, do nothing, as pending state manager will resubmit it correctly on reconnect.\n\t\t// Because flush() is a task that executes async (on clean stack), we can get here in disconnected state.\n\t\tif (this.params.shouldSend()) {\n\t\t\tconst processedBatch = this.compressBatch(\n\t\t\t\tshouldGroup ? this.params.groupingManager.groupBatch(rawBatch) : rawBatch,\n\t\t\t);\n\t\t\tclientSequenceNumber = this.sendBatch(processedBatch);\n\t\t}\n\n\t\tthis.params.pendingStateManager.onFlushBatch(rawBatch.content, clientSequenceNumber);\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\t\tassert(batchManager.options.canRebase, 0x9a7 /* BatchManager does not support rebase */);\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\t/**\n\t * As necessary and enabled, compresses and chunks the given batch.\n\t *\n\t * @remarks - If chunking happens, a side effect here is that 1 or more chunks are queued immediately for sending in next JS turn.\n\t *\n\t * @param batch - Raw or Grouped batch to consider for compression/chunking\n\t * @returns Either (A) the original batch, (B) a compressed batch (same length as original),\n\t * or (C) a batch containing the last chunk (plus empty placeholders from compression if applicable).\n\t */\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.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 batch;\n\t\t}\n\n\t\tconst compressedBatch = this.params.compressor.compressBatch(batch);\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 * @returns the clientSequenceNumber of the start of the batch, or undefined if nothing was sent\n\t */\n\tprivate sendBatch(batch: IBatch) {\n\t\tconst length = batch.content.length;\n\t\tif (length === 0) {\n\t\t\treturn undefined; // Nothing submitted\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\tlet clientSequenceNumber: number;\n\t\tif (this.params.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\tclientSequenceNumber = this.params.legacySendBatchFn(batch);\n\t\t} else {\n\t\t\tassert(batch.referenceSequenceNumber !== undefined, 0x58e /* Batch must not be empty */);\n\t\t\tclientSequenceNumber = this.params.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\n\t\t// Convert from clientSequenceNumber of last message in the batch to clientSequenceNumber of first message.\n\t\tclientSequenceNumber -= length - 1;\n\t\tassert(clientSequenceNumber >= 0, 0x3d0 /* clientSequenceNumber can't be negative */);\n\t\treturn clientSequenceNumber;\n\t}\n\n\tpublic checkpoint() {\n\t\t// This variable is declared with a specific type so that we have a standard import of the IBatchCheckpoint type.\n\t\t// When the type is inferred, the generated .d.ts uses a dynamic import which doesn't resolve.\n\t\tconst mainBatch: IBatchCheckpoint = this.mainBatch.checkpoint();\n\t\treturn {\n\t\t\tmainBatch,\n\t\t\tidAllocationBatch: this.idAllocationBatch.checkpoint(),\n\t\t\tblobAttachBatch: this.blobAttachBatch.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 { ICriticalContainerError } from \"@fluidframework/container-definitions\";\nimport { IBatchMessage } from \"@fluidframework/container-definitions/internal\";\nimport { ITelemetryBaseLogger } from \"@fluidframework/core-interfaces\";\nimport { assert } from \"@fluidframework/core-utils/internal\";\nimport {\n\tGenericError,\n\tUsageError,\n\tcreateChildLogger,\n\ttype ITelemetryLoggerExt,\n} from \"@fluidframework/telemetry-utils/internal\";\n\nimport { ICompressionRuntimeOptions } from \"../containerRuntime.js\";\nimport { PendingMessageResubmitData, PendingStateManager } from \"../pendingStateManager.js\";\n\nimport {\n\tBatchManager,\n\tBatchSequenceNumbers,\n\testimateSocketSize,\n\tsequenceNumbersMatch,\n\ttype BatchId,\n} from \"./batchManager.js\";\nimport { BatchMessage, IBatch, IBatchCheckpoint } from \"./definitions.js\";\nimport { OpCompressor } from \"./opCompressor.js\";\nimport { OpGroupingManager } from \"./opGroupingManager.js\";\nimport { OpSplitter } from \"./opSplitter.js\";\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}\n\nexport interface IOutboxParameters {\n\treadonly shouldSend: () => boolean;\n\treadonly pendingStateManager: PendingStateManager;\n\treadonly submitBatchFn:\n\t\t| ((batch: IBatchMessage[], referenceSequenceNumber?: number) => number)\n\t\t| undefined;\n\treadonly legacySendBatchFn: (batch: IBatch) => number;\n\treadonly config: IOutboxConfig;\n\treadonly compressor: OpCompressor;\n\treadonly splitter: OpSplitter;\n\treadonly logger: ITelemetryBaseLogger;\n\treadonly groupingManager: OpGroupingManager;\n\treadonly getCurrentSequenceNumbers: () => BatchSequenceNumbers;\n\treadonly reSubmit: (message: PendingMessageResubmitData) => 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/* eslint-disable @typescript-eslint/prefer-nullish-coalescing */\n\t\t// ?? is not logically equivalent when the first clause returns false.\n\t\t(\n\t\t\tObject.getOwnPropertyDescriptor(errorObj, \"stackTraceLimit\") ||\n\t\t\tObject.getOwnPropertyDescriptor(Object.getPrototypeOf(errorObj), \"stackTraceLimit\")\n\t\t)?.writable !== true\n\t\t/* eslint-enable @typescript-eslint/prefer-nullish-coalescing */\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\n/**\n * The Outbox collects messages submitted by the ContainerRuntime into a batch,\n * and then flushes the batch when requested.\n *\n * @remarks There are actually multiple independent batches (some are for a specific message type),\n * to support slight variation in semantics for each batch (e.g. support for rebasing or grouping).\n */\nexport class Outbox {\n\tprivate readonly logger: ITelemetryLoggerExt;\n\tprivate readonly mainBatch: BatchManager;\n\tprivate readonly blobAttachBatch: BatchManager;\n\tprivate readonly idAllocationBatch: BatchManager;\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.logger = createChildLogger({ logger: params.logger, namespace: \"Outbox\" });\n\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\n\t\tthis.mainBatch = new BatchManager({ hardLimit, canRebase: true });\n\t\tthis.blobAttachBatch = new BatchManager({ hardLimit, canRebase: true });\n\t\tthis.idAllocationBatch = new BatchManager({ hardLimit, canRebase: false });\n\t}\n\n\tpublic get messageCount(): number {\n\t\treturn this.mainBatch.length + this.blobAttachBatch.length + this.idAllocationBatch.length;\n\t}\n\n\tpublic get isEmpty(): boolean {\n\t\treturn this.messageCount === 0;\n\t}\n\n\t/**\n\t * Detect whether batching has been interrupted by an incoming message being processed. In this case,\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\t * @remarks - To detect batch interruption, we compare both the reference sequence number\n\t * (i.e. last message processed by DeltaManager) and the client sequence number of the\n\t * last message processed by the ContainerRuntime. In the absence of op reentrancy, this\n\t * pair will remain stable during a single JS turn during which the batch is being built up.\n\t */\n\tprivate maybeFlushPartialBatch() {\n\t\tconst mainBatchSeqNums = this.mainBatch.sequenceNumbers;\n\t\tconst blobAttachSeqNums = this.blobAttachBatch.sequenceNumbers;\n\t\tconst idAllocSeqNums = this.idAllocationBatch.sequenceNumbers;\n\t\tassert(\n\t\t\tthis.params.config.disablePartialFlush ||\n\t\t\t\t(sequenceNumbersMatch(mainBatchSeqNums, blobAttachSeqNums) &&\n\t\t\t\t\tsequenceNumbersMatch(mainBatchSeqNums, idAllocSeqNums)),\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(blobAttachSeqNums, currentSequenceNumbers) &&\n\t\t\tsequenceNumbersMatch(idAllocSeqNums, 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.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\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 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\tpublic submitIdAllocation(message: BatchMessage) {\n\t\tthis.maybeFlushPartialBatch();\n\n\t\tthis.addMessageToBatchManager(this.idAllocationBatch, message);\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\t/**\n\t * Flush all the batches to the ordering service.\n\t * This method is expected to be called at the end of a batch.\n\t * @param resubmittingBatchId - If defined, indicates this is a resubmission of a batch\n\t * with the given Batch ID, which must be preserved\n\t */\n\tpublic flush(resubmittingBatchId?: BatchId) {\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(resubmittingBatchId);\n\t}\n\n\tprivate flushAll(resubmittingBatchId?: BatchId) {\n\t\t// Don't use resubmittingBatchId for idAllocationBatch.\n\t\t// ID Allocation messages are not directly resubmitted so we don't want to reuse the batch ID.\n\t\tthis.flushInternal(this.idAllocationBatch);\n\t\tthis.flushInternal(\n\t\t\tthis.blobAttachBatch,\n\t\t\ttrue /* disableGroupedBatching */,\n\t\t\tresubmittingBatchId,\n\t\t);\n\t\tthis.flushInternal(\n\t\t\tthis.mainBatch,\n\t\t\tfalse /* disableGroupedBatching */,\n\t\t\tresubmittingBatchId,\n\t\t);\n\t}\n\n\tprivate flushInternal(\n\t\tbatchManager: BatchManager,\n\t\tdisableGroupedBatching: boolean = false,\n\t\tresubmittingBatchId?: BatchId,\n\t) {\n\t\tif (batchManager.empty) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst rawBatch = batchManager.popBatch(resubmittingBatchId);\n\t\tconst shouldGroup =\n\t\t\t!disableGroupedBatching && this.params.groupingManager.shouldGroup(rawBatch);\n\t\tif (batchManager.options.canRebase && rawBatch.hasReentrantOps === true && shouldGroup) {\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\tlet clientSequenceNumber: number | undefined;\n\t\t// Did we disconnect? (i.e. is shouldSend false?)\n\t\t// If so, do nothing, as pending state manager will resubmit it correctly on reconnect.\n\t\t// Because flush() is a task that executes async (on clean stack), we can get here in disconnected state.\n\t\tif (this.params.shouldSend()) {\n\t\t\tconst processedBatch = this.compressBatch(\n\t\t\t\tshouldGroup ? this.params.groupingManager.groupBatch(rawBatch) : rawBatch,\n\t\t\t);\n\t\t\tclientSequenceNumber = this.sendBatch(processedBatch);\n\t\t\tassert(\n\t\t\t\tclientSequenceNumber === undefined || clientSequenceNumber >= 0,\n\t\t\t\t0x9d2 /* unexpected negative clientSequenceNumber (empty batch should yield undefined) */,\n\t\t\t);\n\t\t}\n\n\t\tthis.params.pendingStateManager.onFlushBatch(rawBatch.messages, clientSequenceNumber);\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\t\tassert(batchManager.options.canRebase, 0x9a7 /* BatchManager does not support rebase */);\n\n\t\tthis.rebasing = true;\n\t\tfor (const message of rawBatch.messages) {\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.logger.sendTelemetryEvent(\n\t\t\t\t{\n\t\t\t\t\teventName: \"BatchRebase\",\n\t\t\t\t\tlength: rawBatch.messages.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\t/**\n\t * As necessary and enabled, compresses and chunks the given batch.\n\t *\n\t * @remarks - If chunking happens, a side effect here is that 1 or more chunks are queued immediately for sending in next JS turn.\n\t *\n\t * @param batch - Raw or Grouped batch to consider for compression/chunking\n\t * @returns Either (A) the original batch, (B) a compressed batch (same length as original),\n\t * or (C) a batch containing the last chunk (plus empty placeholders from compression if applicable).\n\t */\n\tprivate compressBatch(batch: IBatch): IBatch {\n\t\tif (\n\t\t\tbatch.messages.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.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 batch;\n\t\t}\n\n\t\tconst compressedBatch = this.params.compressor.compressBatch(batch);\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.messages.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 * @returns the clientSequenceNumber of the start of the batch, or undefined if nothing was sent\n\t */\n\tprivate sendBatch(batch: IBatch) {\n\t\tconst length = batch.messages.length;\n\t\tif (length === 0) {\n\t\t\treturn undefined; // Nothing submitted\n\t\t}\n\n\t\tconst socketSize = estimateSocketSize(batch);\n\t\tif (socketSize >= this.params.config.maxBatchSizeInBytes) {\n\t\t\tthis.logger.sendPerformanceEvent({\n\t\t\t\teventName: \"LargeBatch\",\n\t\t\t\tlength: batch.messages.length,\n\t\t\t\tsizeInBytes: batch.contentSizeInBytes,\n\t\t\t\tsocketSize,\n\t\t\t});\n\t\t}\n\n\t\tlet clientSequenceNumber: number;\n\t\tif (this.params.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\t// Non null asserting here because of the length check above\n\t\t\t\t// eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n\t\t\t\tbatch.messages[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\tclientSequenceNumber = this.params.legacySendBatchFn(batch);\n\t\t} else {\n\t\t\tassert(batch.referenceSequenceNumber !== undefined, 0x58e /* Batch must not be empty */);\n\t\t\tclientSequenceNumber = this.params.submitBatchFn(\n\t\t\t\tbatch.messages.map<IBatchMessage>((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\n\t\t// Convert from clientSequenceNumber of last message in the batch to clientSequenceNumber of first message.\n\t\tclientSequenceNumber -= length - 1;\n\t\tassert(clientSequenceNumber >= 0, 0x3d0 /* clientSequenceNumber can't be negative */);\n\t\treturn clientSequenceNumber;\n\t}\n\n\t/**\n\t * @returns A checkpoint object per batch that facilitates iterating over the batch messages when rolling back.\n\t */\n\tpublic getBatchCheckpoints() {\n\t\t// This variable is declared with a specific type so that we have a standard import of the IBatchCheckpoint type.\n\t\t// When the type is inferred, the generated .d.ts uses a dynamic import which doesn't resolve.\n\t\tconst mainBatch: IBatchCheckpoint = this.mainBatch.checkpoint();\n\t\treturn {\n\t\t\tmainBatch,\n\t\t\tidAllocationBatch: this.idAllocationBatch.checkpoint(),\n\t\t\tblobAttachBatch: this.blobAttachBatch.checkpoint(),\n\t\t};\n\t}\n}\n"]}
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Licensed under the MIT License.
|
|
4
4
|
*/
|
|
5
5
|
import { ISequencedDocumentMessage } from "@fluidframework/driver-definitions/internal";
|
|
6
|
-
import { type
|
|
6
|
+
import { type InboundSequencedContainerRuntimeMessage } from "../messageTypes.js";
|
|
7
7
|
import { OpDecompressor } from "./opDecompressor.js";
|
|
8
8
|
import { OpGroupingManager } from "./opGroupingManager.js";
|
|
9
9
|
import { OpSplitter } from "./opSplitter.js";
|
|
@@ -24,11 +24,12 @@ export declare class RemoteMessageProcessor {
|
|
|
24
24
|
* @remarks For chunked batches, this is the CSN of the "representative" chunk (the final chunk)
|
|
25
25
|
*/
|
|
26
26
|
private batchStartCsn;
|
|
27
|
+
private readonly processorBatch;
|
|
27
28
|
constructor(opSplitter: OpSplitter, opDecompressor: OpDecompressor, opGroupingManager: OpGroupingManager);
|
|
28
29
|
get partialMessages(): ReadonlyMap<string, string[]>;
|
|
29
30
|
clearPartialMessagesFor(clientId: string): void;
|
|
30
31
|
/**
|
|
31
|
-
* Ungroups and Unchunks the runtime ops
|
|
32
|
+
* Ungroups and Unchunks the runtime ops of a batch received over the wire
|
|
32
33
|
* @param remoteMessageCopy - A shallow copy of a message from another client, possibly virtualized
|
|
33
34
|
* (grouped, compressed, and/or chunked).
|
|
34
35
|
* Being a shallow copy, it's considered mutable, meaning no other Container or other parallel procedure
|
|
@@ -43,12 +44,11 @@ export declare class RemoteMessageProcessor {
|
|
|
43
44
|
* 3. If grouped, ungroup the message
|
|
44
45
|
* For more details, see https://github.com/microsoft/FluidFramework/blob/main/packages/runtime/container-runtime/src/opLifecycle/README.md#inbound
|
|
45
46
|
*
|
|
46
|
-
* @returns the unchunked, decompressed, ungrouped, unpacked
|
|
47
|
-
*
|
|
48
|
-
* a singleton array [remoteMessageCopy] is returned
|
|
47
|
+
* @returns all the unchunked, decompressed, ungrouped, unpacked InboundSequencedContainerRuntimeMessage from a single batch
|
|
48
|
+
* or undefined if the batch is not yet complete.
|
|
49
49
|
*/
|
|
50
|
-
process(remoteMessageCopy: ISequencedDocumentMessage): {
|
|
51
|
-
messages:
|
|
50
|
+
process(remoteMessageCopy: ISequencedDocumentMessage, logLegacyCase: (codePath: string) => void): {
|
|
51
|
+
messages: InboundSequencedContainerRuntimeMessage[];
|
|
52
52
|
batchStartCsn: number;
|
|
53
53
|
} | undefined;
|
|
54
54
|
/**
|
|
@@ -58,6 +58,8 @@ export declare class RemoteMessageProcessor {
|
|
|
58
58
|
*/
|
|
59
59
|
private getAndUpdateBatchStartCsn;
|
|
60
60
|
}
|
|
61
|
+
/** Takes an incoming message and if the contents is a string, JSON.parse's it in place */
|
|
62
|
+
export declare function ensureContentsDeserialized(mutableMessage: ISequencedDocumentMessage, modernRuntimeMessage: boolean, logLegacyCase: (codePath: string) => void): void;
|
|
61
63
|
/**
|
|
62
64
|
* Unpacks runtime messages.
|
|
63
65
|
*
|
|
@@ -67,5 +69,5 @@ export declare class RemoteMessageProcessor {
|
|
|
67
69
|
*
|
|
68
70
|
* @internal
|
|
69
71
|
*/
|
|
70
|
-
export declare function unpackRuntimeMessage(message: ISequencedDocumentMessage): boolean;
|
|
72
|
+
export declare function unpackRuntimeMessage(message: ISequencedDocumentMessage, logLegacyCase?: (codePath: string) => void): boolean;
|
|
71
73
|
//# sourceMappingURL=remoteMessageProcessor.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"remoteMessageProcessor.d.ts","sourceRoot":"","sources":["../../src/opLifecycle/remoteMessageProcessor.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAEN,yBAAyB,EACzB,MAAM,6CAA6C,CAAC;AAErD,OAAO,
|
|
1
|
+
{"version":3,"file":"remoteMessageProcessor.d.ts","sourceRoot":"","sources":["../../src/opLifecycle/remoteMessageProcessor.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAEN,yBAAyB,EACzB,MAAM,6CAA6C,CAAC;AAErD,OAAO,EAGN,KAAK,uCAAuC,EAE5C,MAAM,oBAAoB,CAAC;AAG5B,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,EAAE,iBAAiB,EAAkB,MAAM,wBAAwB,CAAC;AAC3E,OAAO,EAAE,UAAU,EAAoB,MAAM,iBAAiB,CAAC;AAE/D;;;;;GAKG;AACH,qBAAa,sBAAsB;IAWjC,OAAO,CAAC,QAAQ,CAAC,UAAU;IAC3B,OAAO,CAAC,QAAQ,CAAC,cAAc;IAC/B,OAAO,CAAC,QAAQ,CAAC,iBAAiB;IAZnC;;;;;OAKG;IACH,OAAO,CAAC,aAAa,CAAqB;IAC1C,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAiD;gBAG9D,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;IAI/C;;;;;;;;;;;;;;;;;;OAkBG;IACI,OAAO,CACb,iBAAiB,EAAE,yBAAyB,EAC5C,aAAa,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,GAEvC;QACA,QAAQ,EAAE,uCAAuC,EAAE,CAAC;QACpD,aAAa,EAAE,MAAM,CAAC;KACrB,GACD,SAAS;IA+DZ;;;;OAIG;IACH,OAAO,CAAC,yBAAyB;CA4BjC;AAED,0FAA0F;AAC1F,wBAAgB,0BAA0B,CACzC,cAAc,EAAE,yBAAyB,EACzC,oBAAoB,EAAE,OAAO,EAC7B,aAAa,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,GACvC,IAAI,CAkBN;AAyBD;;;;;;;;GAQG;AACH,wBAAgB,oBAAoB,CACnC,OAAO,EAAE,yBAAyB,EAClC,aAAa,GAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAe,GAClD,OAAO,CAuBT"}
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
import { assert } from "@fluidframework/core-utils/internal";
|
|
6
6
|
import { MessageType, } from "@fluidframework/driver-definitions/internal";
|
|
7
7
|
import { ContainerMessageType, } from "../messageTypes.js";
|
|
8
|
+
import { asBatchMetadata } from "../metadata.js";
|
|
8
9
|
import { isGroupedBatch } from "./opGroupingManager.js";
|
|
9
10
|
import { isChunkedMessage } from "./opSplitter.js";
|
|
10
11
|
/**
|
|
@@ -18,6 +19,7 @@ export class RemoteMessageProcessor {
|
|
|
18
19
|
this.opSplitter = opSplitter;
|
|
19
20
|
this.opDecompressor = opDecompressor;
|
|
20
21
|
this.opGroupingManager = opGroupingManager;
|
|
22
|
+
this.processorBatch = [];
|
|
21
23
|
}
|
|
22
24
|
get partialMessages() {
|
|
23
25
|
return this.opSplitter.chunks;
|
|
@@ -26,7 +28,7 @@ export class RemoteMessageProcessor {
|
|
|
26
28
|
this.opSplitter.clearPartialChunks(clientId);
|
|
27
29
|
}
|
|
28
30
|
/**
|
|
29
|
-
* Ungroups and Unchunks the runtime ops
|
|
31
|
+
* Ungroups and Unchunks the runtime ops of a batch received over the wire
|
|
30
32
|
* @param remoteMessageCopy - A shallow copy of a message from another client, possibly virtualized
|
|
31
33
|
* (grouped, compressed, and/or chunked).
|
|
32
34
|
* Being a shallow copy, it's considered mutable, meaning no other Container or other parallel procedure
|
|
@@ -41,13 +43,11 @@ export class RemoteMessageProcessor {
|
|
|
41
43
|
* 3. If grouped, ungroup the message
|
|
42
44
|
* For more details, see https://github.com/microsoft/FluidFramework/blob/main/packages/runtime/container-runtime/src/opLifecycle/README.md#inbound
|
|
43
45
|
*
|
|
44
|
-
* @returns the unchunked, decompressed, ungrouped, unpacked
|
|
45
|
-
*
|
|
46
|
-
* a singleton array [remoteMessageCopy] is returned
|
|
46
|
+
* @returns all the unchunked, decompressed, ungrouped, unpacked InboundSequencedContainerRuntimeMessage from a single batch
|
|
47
|
+
* or undefined if the batch is not yet complete.
|
|
47
48
|
*/
|
|
48
|
-
process(remoteMessageCopy) {
|
|
49
|
+
process(remoteMessageCopy, logLegacyCase) {
|
|
49
50
|
let message = remoteMessageCopy;
|
|
50
|
-
ensureContentsDeserialized(message);
|
|
51
51
|
if (isChunkedMessage(message)) {
|
|
52
52
|
const chunkProcessingResult = this.opSplitter.processChunk(message);
|
|
53
53
|
// Only continue further if current chunk is the final chunk
|
|
@@ -69,7 +69,8 @@ export class RemoteMessageProcessor {
|
|
|
69
69
|
}
|
|
70
70
|
if (isGroupedBatch(message)) {
|
|
71
71
|
// We should be awaiting a new batch (batchStartCsn undefined)
|
|
72
|
-
assert(this.batchStartCsn === undefined,
|
|
72
|
+
assert(this.batchStartCsn === undefined, 0x9d3 /* Grouped batch interrupting another batch */);
|
|
73
|
+
assert(this.processorBatch.length === 0, 0x9d4 /* Processor batch should be empty on grouped batch */);
|
|
73
74
|
return {
|
|
74
75
|
messages: this.opGroupingManager.ungroupOp(message).map(unpack),
|
|
75
76
|
batchStartCsn: message.clientSequenceNumber,
|
|
@@ -77,9 +78,19 @@ export class RemoteMessageProcessor {
|
|
|
77
78
|
}
|
|
78
79
|
const batchStartCsn = this.getAndUpdateBatchStartCsn(message);
|
|
79
80
|
// Do a final unpack of runtime messages in case the message was not grouped, compressed, or chunked
|
|
80
|
-
unpackRuntimeMessage(message);
|
|
81
|
+
unpackRuntimeMessage(message, logLegacyCase);
|
|
82
|
+
this.processorBatch.push(message);
|
|
83
|
+
// this.batchStartCsn is undefined only if we have processed all messages in the batch.
|
|
84
|
+
// If it's still defined, we're still in the middle of a batch, so we return nothing, letting
|
|
85
|
+
// containerRuntime know that we're waiting for more messages to complete the batch.
|
|
86
|
+
if (this.batchStartCsn !== undefined) {
|
|
87
|
+
// batch not yet complete
|
|
88
|
+
return undefined;
|
|
89
|
+
}
|
|
90
|
+
const messages = [...this.processorBatch];
|
|
91
|
+
this.processorBatch.length = 0;
|
|
81
92
|
return {
|
|
82
|
-
messages
|
|
93
|
+
messages,
|
|
83
94
|
batchStartCsn,
|
|
84
95
|
};
|
|
85
96
|
}
|
|
@@ -89,10 +100,10 @@ export class RemoteMessageProcessor {
|
|
|
89
100
|
* the batch tracking info (this.batchStartCsn) based on whether we're still mid-batch.
|
|
90
101
|
*/
|
|
91
102
|
getAndUpdateBatchStartCsn(message) {
|
|
92
|
-
const batchMetadataFlag = message.metadata?.batch;
|
|
103
|
+
const batchMetadataFlag = asBatchMetadata(message.metadata)?.batch;
|
|
93
104
|
if (this.batchStartCsn === undefined) {
|
|
94
105
|
// We are waiting for a new batch
|
|
95
|
-
assert(batchMetadataFlag !== false,
|
|
106
|
+
assert(batchMetadataFlag !== false, 0x9d5 /* Unexpected batch end marker */);
|
|
96
107
|
// Start of a new multi-message batch
|
|
97
108
|
if (batchMetadataFlag === true) {
|
|
98
109
|
this.batchStartCsn = message.clientSequenceNumber;
|
|
@@ -104,7 +115,7 @@ export class RemoteMessageProcessor {
|
|
|
104
115
|
}
|
|
105
116
|
// We are in the middle or end of an existing multi-message batch. Return the current batchStartCsn
|
|
106
117
|
const batchStartCsn = this.batchStartCsn;
|
|
107
|
-
assert(batchMetadataFlag !== true,
|
|
118
|
+
assert(batchMetadataFlag !== true, 0x9d6 /* Unexpected batch start marker */);
|
|
108
119
|
if (batchMetadataFlag === false) {
|
|
109
120
|
// Batch end? Then get ready for the next batch to start
|
|
110
121
|
this.batchStartCsn = undefined;
|
|
@@ -113,12 +124,23 @@ export class RemoteMessageProcessor {
|
|
|
113
124
|
}
|
|
114
125
|
}
|
|
115
126
|
/** Takes an incoming message and if the contents is a string, JSON.parse's it in place */
|
|
116
|
-
function ensureContentsDeserialized(mutableMessage) {
|
|
127
|
+
export function ensureContentsDeserialized(mutableMessage, modernRuntimeMessage, logLegacyCase) {
|
|
117
128
|
// back-compat: ADO #1385: eventually should become unconditional, but only for runtime messages!
|
|
118
129
|
// System message may have no contents, or in some cases (mostly for back-compat) they may have actual objects.
|
|
119
130
|
// Old ops may contain empty string (I assume noops).
|
|
131
|
+
let parsedJsonContents;
|
|
120
132
|
if (typeof mutableMessage.contents === "string" && mutableMessage.contents !== "") {
|
|
121
133
|
mutableMessage.contents = JSON.parse(mutableMessage.contents);
|
|
134
|
+
parsedJsonContents = true;
|
|
135
|
+
}
|
|
136
|
+
else {
|
|
137
|
+
parsedJsonContents = false;
|
|
138
|
+
}
|
|
139
|
+
// We expect Modern Runtime Messages to have JSON serialized contents,
|
|
140
|
+
// and all other messages not to (system messages and legacy runtime messages without outer "op" type envelope)
|
|
141
|
+
// Let's observe if we are wrong about this to learn about these cases.
|
|
142
|
+
if (modernRuntimeMessage !== parsedJsonContents) {
|
|
143
|
+
logLegacyCase("ensureContentsDeserialized_unexpectedContentsType");
|
|
122
144
|
}
|
|
123
145
|
}
|
|
124
146
|
/**
|
|
@@ -150,7 +172,7 @@ function unpack(message) {
|
|
|
150
172
|
*
|
|
151
173
|
* @internal
|
|
152
174
|
*/
|
|
153
|
-
export function unpackRuntimeMessage(message) {
|
|
175
|
+
export function unpackRuntimeMessage(message, logLegacyCase = () => { }) {
|
|
154
176
|
if (message.type !== MessageType.Operation) {
|
|
155
177
|
// Legacy format, but it's already "unpacked",
|
|
156
178
|
// i.e. message.type is actually ContainerMessageType.
|
|
@@ -163,6 +185,7 @@ export function unpackRuntimeMessage(message) {
|
|
|
163
185
|
if (message.contents.address !== undefined &&
|
|
164
186
|
message.contents.type === undefined) {
|
|
165
187
|
message.type = ContainerMessageType.FluidDataStoreOp;
|
|
188
|
+
logLegacyCase("unpackRuntimeMessage_contentsWithAddress");
|
|
166
189
|
}
|
|
167
190
|
else {
|
|
168
191
|
// new format
|
|
@@ -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 { assert } from \"@fluidframework/core-utils/internal\";\nimport {\n\tMessageType,\n\tISequencedDocumentMessage,\n} from \"@fluidframework/driver-definitions/internal\";\n\nimport {\n\tContainerMessageType,\n\ttype InboundContainerRuntimeMessage,\n\ttype InboundSequencedContainerRuntimeMessage,\n\ttype InboundSequencedContainerRuntimeMessageOrSystemMessage,\n\ttype InboundSequencedRecentlyAddedContainerRuntimeMessage,\n} from \"../messageTypes.js\";\n\nimport { OpDecompressor } from \"./opDecompressor.js\";\nimport { OpGroupingManager, isGroupedBatch } from \"./opGroupingManager.js\";\nimport { OpSplitter, isChunkedMessage } from \"./opSplitter.js\";\n\n/**\n * Stateful class for processing incoming remote messages as the virtualization measures are unwrapped,\n * potentially across numerous inbound ops.\n *\n * @internal\n */\nexport class RemoteMessageProcessor {\n\t/**\n\t * Client Sequence Number of the first message in the current batch being processed.\n\t * If undefined, we are expecting the next message to start a new batch.\n\t *\n\t * @remarks For chunked batches, this is the CSN of the \"representative\" chunk (the final chunk)\n\t */\n\tprivate batchStartCsn: number | undefined;\n\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\t/**\n\t * Ungroups and Unchunks the runtime ops encapsulated by the single remoteMessage received over the wire\n\t * @param remoteMessageCopy - A shallow copy of a message from another client, possibly virtualized\n\t * (grouped, compressed, and/or chunked).\n\t * Being a shallow copy, it's considered mutable, meaning no other Container or other parallel procedure\n\t * depends on this object instance.\n\t * Note remoteMessageCopy.contents (and other object props) MUST not be modified,\n\t * but may be overwritten (as is the case with contents).\n\t *\n\t * Incoming messages will always have compression, chunking, and grouped batching happen in a defined order and that order cannot be changed.\n\t * When processing these messages, the order is:\n\t * 1. If chunked, process the chunk and only continue if this is a final chunk\n\t * 2. If compressed, decompress the message and store for further unrolling of the decompressed content\n\t * 3. If grouped, ungroup the message\n\t * For more details, see https://github.com/microsoft/FluidFramework/blob/main/packages/runtime/container-runtime/src/opLifecycle/README.md#inbound\n\t *\n\t * @returns the unchunked, decompressed, ungrouped, unpacked SequencedContainerRuntimeMessages encapsulated in the remote message.\n\t * For ops that weren't virtualized (e.g. System ops that the ContainerRuntime will ultimately ignore),\n\t * a singleton array [remoteMessageCopy] is returned\n\t */\n\tpublic process(remoteMessageCopy: ISequencedDocumentMessage):\n\t\t| {\n\t\t\t\tmessages: InboundSequencedContainerRuntimeMessageOrSystemMessage[];\n\t\t\t\tbatchStartCsn: number;\n\t\t }\n\t\t| undefined {\n\t\tlet message = remoteMessageCopy;\n\n\t\tensureContentsDeserialized(message);\n\n\t\tif (isChunkedMessage(message)) {\n\t\t\tconst chunkProcessingResult = this.opSplitter.processChunk(message);\n\t\t\t// Only continue further if current chunk is the final chunk\n\t\t\tif (!chunkProcessingResult.isFinalChunk) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t// This message will always be compressed\n\t\t\tmessage = chunkProcessingResult.message;\n\t\t}\n\n\t\tif (this.opDecompressor.isCompressedMessage(message)) {\n\t\t\tthis.opDecompressor.decompressAndStore(message);\n\t\t}\n\n\t\tif (this.opDecompressor.currentlyUnrolling) {\n\t\t\tmessage = this.opDecompressor.unroll(message);\n\t\t\t// Need to unpack after unrolling if not a groupedBatch\n\t\t\tif (!isGroupedBatch(message)) {\n\t\t\t\tunpack(message);\n\t\t\t}\n\t\t}\n\n\t\tif (isGroupedBatch(message)) {\n\t\t\t// We should be awaiting a new batch (batchStartCsn undefined)\n\t\t\tassert(this.batchStartCsn === undefined, \"Grouped batch interrupting another batch\");\n\t\t\treturn {\n\t\t\t\tmessages: this.opGroupingManager.ungroupOp(message).map(unpack),\n\t\t\t\tbatchStartCsn: message.clientSequenceNumber,\n\t\t\t};\n\t\t}\n\n\t\tconst batchStartCsn = this.getAndUpdateBatchStartCsn(message);\n\n\t\t// Do a final unpack of runtime messages in case the message was not grouped, compressed, or chunked\n\t\tunpackRuntimeMessage(message);\n\t\treturn {\n\t\t\tmessages: [message as InboundSequencedContainerRuntimeMessageOrSystemMessage],\n\t\t\tbatchStartCsn,\n\t\t};\n\t}\n\n\t/**\n\t * Based on pre-existing batch tracking info and the current message's batch metadata,\n\t * this will return the starting CSN for this message's batch, and will also update\n\t * the batch tracking info (this.batchStartCsn) based on whether we're still mid-batch.\n\t */\n\tprivate getAndUpdateBatchStartCsn(message: ISequencedDocumentMessage): number {\n\t\tconst batchMetadataFlag = (message.metadata as { batch: boolean | undefined })?.batch;\n\t\tif (this.batchStartCsn === undefined) {\n\t\t\t// We are waiting for a new batch\n\t\t\tassert(batchMetadataFlag !== false, \"Unexpected batch end marker\");\n\n\t\t\t// Start of a new multi-message batch\n\t\t\tif (batchMetadataFlag === true) {\n\t\t\t\tthis.batchStartCsn = message.clientSequenceNumber;\n\t\t\t\treturn this.batchStartCsn;\n\t\t\t}\n\n\t\t\t// Single-message batch (Since metadata flag is undefined)\n\t\t\t// IMPORTANT: Leave this.batchStartCsn undefined, we're ready for the next batch now.\n\t\t\treturn message.clientSequenceNumber;\n\t\t}\n\n\t\t// We are in the middle or end of an existing multi-message batch. Return the current batchStartCsn\n\t\tconst batchStartCsn = this.batchStartCsn;\n\n\t\tassert(batchMetadataFlag !== true, \"Unexpected batch start marker\");\n\t\tif (batchMetadataFlag === false) {\n\t\t\t// Batch end? Then get ready for the next batch to start\n\t\t\tthis.batchStartCsn = undefined;\n\t\t}\n\n\t\treturn batchStartCsn;\n\t}\n}\n\n/** Takes an incoming message and if the contents is a string, JSON.parse's it in place */\nfunction ensureContentsDeserialized(mutableMessage: ISequencedDocumentMessage): void {\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 mutableMessage.contents === \"string\" && mutableMessage.contents !== \"\") {\n\t\tmutableMessage.contents = JSON.parse(mutableMessage.contents);\n\t}\n}\n\n/**\n * For a given message, it moves the nested InboundContainerRuntimeMessage props one level up.\n *\n * The return type illustrates the assumption that the message param\n * becomes a InboundSequencedContainerRuntimeMessage by the time the function returns\n * (but there is no runtime validation of the 'type' or 'compatDetails' values).\n */\nfunction unpack(message: ISequencedDocumentMessage): InboundSequencedContainerRuntimeMessage {\n\t// We assume the contents is an InboundContainerRuntimeMessage (the message is \"packed\")\n\tconst contents = message.contents as InboundContainerRuntimeMessage;\n\n\t// We're going to unpack message in-place (promoting those properties of contents up to message itself)\n\tconst messageUnpacked = message as InboundSequencedContainerRuntimeMessage;\n\n\tmessageUnpacked.type = contents.type;\n\tmessageUnpacked.contents = contents.contents;\n\tif (\"compatDetails\" in contents) {\n\t\t(messageUnpacked as InboundSequencedRecentlyAddedContainerRuntimeMessage).compatDetails =\n\t\t\tcontents.compatDetails;\n\t}\n\treturn messageUnpacked;\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 whether the given message was unpacked\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"]}
|
|
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 { assert } from \"@fluidframework/core-utils/internal\";\nimport {\n\tMessageType,\n\tISequencedDocumentMessage,\n} from \"@fluidframework/driver-definitions/internal\";\n\nimport {\n\tContainerMessageType,\n\ttype InboundContainerRuntimeMessage,\n\ttype InboundSequencedContainerRuntimeMessage,\n\ttype InboundSequencedRecentlyAddedContainerRuntimeMessage,\n} from \"../messageTypes.js\";\nimport { asBatchMetadata } from \"../metadata.js\";\n\nimport { OpDecompressor } from \"./opDecompressor.js\";\nimport { OpGroupingManager, isGroupedBatch } from \"./opGroupingManager.js\";\nimport { OpSplitter, isChunkedMessage } from \"./opSplitter.js\";\n\n/**\n * Stateful class for processing incoming remote messages as the virtualization measures are unwrapped,\n * potentially across numerous inbound ops.\n *\n * @internal\n */\nexport class RemoteMessageProcessor {\n\t/**\n\t * Client Sequence Number of the first message in the current batch being processed.\n\t * If undefined, we are expecting the next message to start a new batch.\n\t *\n\t * @remarks For chunked batches, this is the CSN of the \"representative\" chunk (the final chunk)\n\t */\n\tprivate batchStartCsn: number | undefined;\n\tprivate readonly processorBatch: InboundSequencedContainerRuntimeMessage[] = [];\n\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\t/**\n\t * Ungroups and Unchunks the runtime ops of a batch received over the wire\n\t * @param remoteMessageCopy - A shallow copy of a message from another client, possibly virtualized\n\t * (grouped, compressed, and/or chunked).\n\t * Being a shallow copy, it's considered mutable, meaning no other Container or other parallel procedure\n\t * depends on this object instance.\n\t * Note remoteMessageCopy.contents (and other object props) MUST not be modified,\n\t * but may be overwritten (as is the case with contents).\n\t *\n\t * Incoming messages will always have compression, chunking, and grouped batching happen in a defined order and that order cannot be changed.\n\t * When processing these messages, the order is:\n\t * 1. If chunked, process the chunk and only continue if this is a final chunk\n\t * 2. If compressed, decompress the message and store for further unrolling of the decompressed content\n\t * 3. If grouped, ungroup the message\n\t * For more details, see https://github.com/microsoft/FluidFramework/blob/main/packages/runtime/container-runtime/src/opLifecycle/README.md#inbound\n\t *\n\t * @returns all the unchunked, decompressed, ungrouped, unpacked InboundSequencedContainerRuntimeMessage from a single batch\n\t * or undefined if the batch is not yet complete.\n\t */\n\tpublic process(\n\t\tremoteMessageCopy: ISequencedDocumentMessage,\n\t\tlogLegacyCase: (codePath: string) => void,\n\t):\n\t\t| {\n\t\t\t\tmessages: InboundSequencedContainerRuntimeMessage[];\n\t\t\t\tbatchStartCsn: number;\n\t\t }\n\t\t| undefined {\n\t\tlet message = remoteMessageCopy;\n\n\t\tif (isChunkedMessage(message)) {\n\t\t\tconst chunkProcessingResult = this.opSplitter.processChunk(message);\n\t\t\t// Only continue further if current chunk is the final chunk\n\t\t\tif (!chunkProcessingResult.isFinalChunk) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t// This message will always be compressed\n\t\t\tmessage = chunkProcessingResult.message;\n\t\t}\n\n\t\tif (this.opDecompressor.isCompressedMessage(message)) {\n\t\t\tthis.opDecompressor.decompressAndStore(message);\n\t\t}\n\n\t\tif (this.opDecompressor.currentlyUnrolling) {\n\t\t\tmessage = this.opDecompressor.unroll(message);\n\t\t\t// Need to unpack after unrolling if not a groupedBatch\n\t\t\tif (!isGroupedBatch(message)) {\n\t\t\t\tunpack(message);\n\t\t\t}\n\t\t}\n\n\t\tif (isGroupedBatch(message)) {\n\t\t\t// We should be awaiting a new batch (batchStartCsn undefined)\n\t\t\tassert(\n\t\t\t\tthis.batchStartCsn === undefined,\n\t\t\t\t0x9d3 /* Grouped batch interrupting another batch */,\n\t\t\t);\n\t\t\tassert(\n\t\t\t\tthis.processorBatch.length === 0,\n\t\t\t\t0x9d4 /* Processor batch should be empty on grouped batch */,\n\t\t\t);\n\t\t\treturn {\n\t\t\t\tmessages: this.opGroupingManager.ungroupOp(message).map(unpack),\n\t\t\t\tbatchStartCsn: message.clientSequenceNumber,\n\t\t\t};\n\t\t}\n\n\t\tconst batchStartCsn = this.getAndUpdateBatchStartCsn(message);\n\n\t\t// Do a final unpack of runtime messages in case the message was not grouped, compressed, or chunked\n\t\tunpackRuntimeMessage(message, logLegacyCase);\n\t\tthis.processorBatch.push(message as InboundSequencedContainerRuntimeMessage);\n\n\t\t// this.batchStartCsn is undefined only if we have processed all messages in the batch.\n\t\t// If it's still defined, we're still in the middle of a batch, so we return nothing, letting\n\t\t// containerRuntime know that we're waiting for more messages to complete the batch.\n\t\tif (this.batchStartCsn !== undefined) {\n\t\t\t// batch not yet complete\n\t\t\treturn undefined;\n\t\t}\n\n\t\tconst messages = [...this.processorBatch];\n\t\tthis.processorBatch.length = 0;\n\t\treturn {\n\t\t\tmessages,\n\t\t\tbatchStartCsn,\n\t\t};\n\t}\n\n\t/**\n\t * Based on pre-existing batch tracking info and the current message's batch metadata,\n\t * this will return the starting CSN for this message's batch, and will also update\n\t * the batch tracking info (this.batchStartCsn) based on whether we're still mid-batch.\n\t */\n\tprivate getAndUpdateBatchStartCsn(message: ISequencedDocumentMessage): number {\n\t\tconst batchMetadataFlag = asBatchMetadata(message.metadata)?.batch;\n\t\tif (this.batchStartCsn === undefined) {\n\t\t\t// We are waiting for a new batch\n\t\t\tassert(batchMetadataFlag !== false, 0x9d5 /* Unexpected batch end marker */);\n\n\t\t\t// Start of a new multi-message batch\n\t\t\tif (batchMetadataFlag === true) {\n\t\t\t\tthis.batchStartCsn = message.clientSequenceNumber;\n\t\t\t\treturn this.batchStartCsn;\n\t\t\t}\n\n\t\t\t// Single-message batch (Since metadata flag is undefined)\n\t\t\t// IMPORTANT: Leave this.batchStartCsn undefined, we're ready for the next batch now.\n\t\t\treturn message.clientSequenceNumber;\n\t\t}\n\n\t\t// We are in the middle or end of an existing multi-message batch. Return the current batchStartCsn\n\t\tconst batchStartCsn = this.batchStartCsn;\n\n\t\tassert(batchMetadataFlag !== true, 0x9d6 /* Unexpected batch start marker */);\n\t\tif (batchMetadataFlag === false) {\n\t\t\t// Batch end? Then get ready for the next batch to start\n\t\t\tthis.batchStartCsn = undefined;\n\t\t}\n\n\t\treturn batchStartCsn;\n\t}\n}\n\n/** Takes an incoming message and if the contents is a string, JSON.parse's it in place */\nexport function ensureContentsDeserialized(\n\tmutableMessage: ISequencedDocumentMessage,\n\tmodernRuntimeMessage: boolean,\n\tlogLegacyCase: (codePath: string) => void,\n): void {\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\tlet parsedJsonContents: boolean;\n\tif (typeof mutableMessage.contents === \"string\" && mutableMessage.contents !== \"\") {\n\t\tmutableMessage.contents = JSON.parse(mutableMessage.contents);\n\t\tparsedJsonContents = true;\n\t} else {\n\t\tparsedJsonContents = false;\n\t}\n\n\t// We expect Modern Runtime Messages to have JSON serialized contents,\n\t// and all other messages not to (system messages and legacy runtime messages without outer \"op\" type envelope)\n\t// Let's observe if we are wrong about this to learn about these cases.\n\tif (modernRuntimeMessage !== parsedJsonContents) {\n\t\tlogLegacyCase(\"ensureContentsDeserialized_unexpectedContentsType\");\n\t}\n}\n\n/**\n * For a given message, it moves the nested InboundContainerRuntimeMessage props one level up.\n *\n * The return type illustrates the assumption that the message param\n * becomes a InboundSequencedContainerRuntimeMessage by the time the function returns\n * (but there is no runtime validation of the 'type' or 'compatDetails' values).\n */\nfunction unpack(message: ISequencedDocumentMessage): InboundSequencedContainerRuntimeMessage {\n\t// We assume the contents is an InboundContainerRuntimeMessage (the message is \"packed\")\n\tconst contents = message.contents as InboundContainerRuntimeMessage;\n\n\t// We're going to unpack message in-place (promoting those properties of contents up to message itself)\n\tconst messageUnpacked = message as InboundSequencedContainerRuntimeMessage;\n\n\tmessageUnpacked.type = contents.type;\n\tmessageUnpacked.contents = contents.contents;\n\tif (\"compatDetails\" in contents) {\n\t\t(messageUnpacked as InboundSequencedRecentlyAddedContainerRuntimeMessage).compatDetails =\n\t\t\tcontents.compatDetails;\n\t}\n\treturn messageUnpacked;\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 whether the given message was unpacked\n *\n * @internal\n */\nexport function unpackRuntimeMessage(\n\tmessage: ISequencedDocumentMessage,\n\tlogLegacyCase: (codePath: string) => void = () => {},\n): 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\tlogLegacyCase(\"unpackRuntimeMessage_contentsWithAddress\");\n\t} else {\n\t\t// new format\n\t\tunpack(message);\n\t}\n\n\treturn true;\n}\n"]}
|
package/lib/packageVersion.d.ts
CHANGED
|
@@ -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.1.0
|
|
8
|
+
export declare const pkgVersion = "2.1.0";
|
|
9
9
|
//# sourceMappingURL=packageVersion.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"packageVersion.d.ts","sourceRoot":"","sources":["../src/packageVersion.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,eAAO,MAAM,OAAO,sCAAsC,CAAC;AAC3D,eAAO,MAAM,UAAU,
|
|
1
|
+
{"version":3,"file":"packageVersion.d.ts","sourceRoot":"","sources":["../src/packageVersion.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,eAAO,MAAM,OAAO,sCAAsC,CAAC;AAC3D,eAAO,MAAM,UAAU,UAAU,CAAC"}
|
package/lib/packageVersion.js
CHANGED
|
@@ -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,
|
|
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,OAAO,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.1.0\";\n"]}
|