@fluidframework/container-runtime 2.32.0 → 2.33.0-333010

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 (61) hide show
  1. package/container-runtime.test-files.tar +0 -0
  2. package/dist/containerRuntime.d.ts.map +1 -1
  3. package/dist/opLifecycle/batchManager.d.ts +2 -14
  4. package/dist/opLifecycle/batchManager.d.ts.map +1 -1
  5. package/dist/opLifecycle/batchManager.js +1 -38
  6. package/dist/opLifecycle/batchManager.js.map +1 -1
  7. package/dist/opLifecycle/definitions.d.ts +9 -7
  8. package/dist/opLifecycle/definitions.d.ts.map +1 -1
  9. package/dist/opLifecycle/definitions.js.map +1 -1
  10. package/dist/opLifecycle/index.d.ts +2 -2
  11. package/dist/opLifecycle/index.d.ts.map +1 -1
  12. package/dist/opLifecycle/index.js +3 -2
  13. package/dist/opLifecycle/index.js.map +1 -1
  14. package/dist/opLifecycle/opCompressor.d.ts.map +1 -1
  15. package/dist/opLifecycle/opCompressor.js +2 -2
  16. package/dist/opLifecycle/opCompressor.js.map +1 -1
  17. package/dist/opLifecycle/opSplitter.d.ts.map +1 -1
  18. package/dist/opLifecycle/opSplitter.js +2 -2
  19. package/dist/opLifecycle/opSplitter.js.map +1 -1
  20. package/dist/opLifecycle/outbox.d.ts +20 -1
  21. package/dist/opLifecycle/outbox.d.ts.map +1 -1
  22. package/dist/opLifecycle/outbox.js +69 -44
  23. package/dist/opLifecycle/outbox.js.map +1 -1
  24. package/dist/packageVersion.d.ts +1 -1
  25. package/dist/packageVersion.d.ts.map +1 -1
  26. package/dist/packageVersion.js +1 -1
  27. package/dist/packageVersion.js.map +1 -1
  28. package/lib/containerRuntime.d.ts.map +1 -1
  29. package/lib/opLifecycle/batchManager.d.ts +2 -14
  30. package/lib/opLifecycle/batchManager.d.ts.map +1 -1
  31. package/lib/opLifecycle/batchManager.js +0 -36
  32. package/lib/opLifecycle/batchManager.js.map +1 -1
  33. package/lib/opLifecycle/definitions.d.ts +9 -7
  34. package/lib/opLifecycle/definitions.d.ts.map +1 -1
  35. package/lib/opLifecycle/definitions.js.map +1 -1
  36. package/lib/opLifecycle/index.d.ts +2 -2
  37. package/lib/opLifecycle/index.d.ts.map +1 -1
  38. package/lib/opLifecycle/index.js +2 -2
  39. package/lib/opLifecycle/index.js.map +1 -1
  40. package/lib/opLifecycle/opCompressor.d.ts.map +1 -1
  41. package/lib/opLifecycle/opCompressor.js +1 -1
  42. package/lib/opLifecycle/opCompressor.js.map +1 -1
  43. package/lib/opLifecycle/opSplitter.d.ts.map +1 -1
  44. package/lib/opLifecycle/opSplitter.js +1 -1
  45. package/lib/opLifecycle/opSplitter.js.map +1 -1
  46. package/lib/opLifecycle/outbox.d.ts +20 -1
  47. package/lib/opLifecycle/outbox.d.ts.map +1 -1
  48. package/lib/opLifecycle/outbox.js +67 -44
  49. package/lib/opLifecycle/outbox.js.map +1 -1
  50. package/lib/packageVersion.d.ts +1 -1
  51. package/lib/packageVersion.d.ts.map +1 -1
  52. package/lib/packageVersion.js +1 -1
  53. package/lib/packageVersion.js.map +1 -1
  54. package/package.json +17 -17
  55. package/src/opLifecycle/batchManager.ts +2 -48
  56. package/src/opLifecycle/definitions.ts +11 -7
  57. package/src/opLifecycle/index.ts +6 -2
  58. package/src/opLifecycle/opCompressor.ts +1 -1
  59. package/src/opLifecycle/opSplitter.ts +1 -1
  60. package/src/opLifecycle/outbox.ts +92 -58
  61. package/src/packageVersion.ts +1 -1
@@ -3,8 +3,8 @@
3
3
  * Licensed under the MIT License.
4
4
  */
5
5
  import { assert, Lazy } from "@fluidframework/core-utils/internal";
6
- import { DataProcessingError, GenericError, UsageError, createChildLogger, } from "@fluidframework/telemetry-utils/internal";
7
- import { BatchManager, estimateSocketSize, sequenceNumbersMatch, } from "./batchManager.js";
6
+ import { DataProcessingError, UsageError, createChildLogger, } from "@fluidframework/telemetry-utils/internal";
7
+ import { BatchManager, sequenceNumbersMatch, } from "./batchManager.js";
8
8
  /**
9
9
  * Temporarily increase the stack limit while executing the provided action.
10
10
  * If a negative value is provided for `length`, no stack frames will be collected.
@@ -41,6 +41,45 @@ export function getLongStack(action, length = 50) {
41
41
  errorObj.stackTraceLimit = originalStackTraceLimit;
42
42
  }
43
43
  }
44
+ /**
45
+ * Convert from local batch to outbound batch, including computing contentSizeInBytes.
46
+ */
47
+ export function localBatchToOutboundBatch(localBatch) {
48
+ // Shallow copy each message as we switch types
49
+ const outboundMessages = localBatch.messages.map(({ serializedOp, ...message }) => ({
50
+ contents: serializedOp,
51
+ ...message,
52
+ }));
53
+ const contentSizeInBytes = outboundMessages.reduce((acc, message) => acc + (message.contents?.length ?? 0), 0);
54
+ // Shallow copy the local batch, updating the messages to be outbound messages and adding contentSizeInBytes
55
+ const outboundBatch = {
56
+ ...localBatch,
57
+ messages: outboundMessages,
58
+ contentSizeInBytes,
59
+ };
60
+ return outboundBatch;
61
+ }
62
+ /**
63
+ * Estimated size of the stringification overhead for an op accumulated
64
+ * from runtime to loader to the service.
65
+ */
66
+ const opOverhead = 200;
67
+ /**
68
+ * Estimates the real size in bytes on the socket for a given batch. It assumes that
69
+ * the envelope size (and the size of an empty op) is 200 bytes, taking into account
70
+ * extra overhead from stringification.
71
+ *
72
+ * @remarks
73
+ * Also content will be stringified, and that adds a lot of overhead due to a lot of escape characters.
74
+ * Not taking it into account, as compression work should help there - compressed payload will be
75
+ * initially stored as base64, and that requires only 2 extra escape characters.
76
+ *
77
+ * @param batch - the batch to inspect
78
+ * @returns An estimate of the payload size in bytes which will be produced when the batch is sent over the wire
79
+ */
80
+ export const estimateSocketSize = (batch) => {
81
+ return batch.contentSizeInBytes + opOverhead * batch.messages.length;
82
+ };
44
83
  /**
45
84
  * The Outbox collects messages submitted by the ContainerRuntime into a batch,
46
85
  * and then flushes the batch when requested.
@@ -62,16 +101,9 @@ export class Outbox {
62
101
  this.maxMismatchedOpsToReport = 3;
63
102
  this.mismatchedOpsReported = 0;
64
103
  this.logger = createChildLogger({ logger: params.logger, namespace: "Outbox" });
65
- const isCompressionEnabled = this.params.config.compressionOptions.minimumBatchSizeInBytes !==
66
- Number.POSITIVE_INFINITY;
67
- // We need to allow infinite size batches if we enable compression
68
- const hardLimit = isCompressionEnabled
69
- ? Number.POSITIVE_INFINITY
70
- : this.params.config.maxBatchSizeInBytes;
71
- this.mainBatch = new BatchManager({ hardLimit, canRebase: true });
72
- this.blobAttachBatch = new BatchManager({ hardLimit, canRebase: true });
104
+ this.mainBatch = new BatchManager({ canRebase: true });
105
+ this.blobAttachBatch = new BatchManager({ canRebase: true });
73
106
  this.idAllocationBatch = new BatchManager({
74
- hardLimit,
75
107
  canRebase: false,
76
108
  ignoreBatchId: true,
77
109
  });
@@ -162,14 +194,7 @@ export class Outbox {
162
194
  this.addMessageToBatchManager(this.idAllocationBatch, message);
163
195
  }
164
196
  addMessageToBatchManager(batchManager, message) {
165
- if (!batchManager.push(message, this.isContextReentrant(), this.params.getCurrentSequenceNumbers().clientSequenceNumber)) {
166
- throw new GenericError("BatchTooLarge", /* error */ undefined, {
167
- opSize: message.serializedOp?.length ?? 0,
168
- batchSize: batchManager.contentSizeInBytes,
169
- count: batchManager.length,
170
- limit: batchManager.options.hardLimit,
171
- });
172
- }
197
+ batchManager.push(message, this.isContextReentrant(), this.params.getCurrentSequenceNumbers().clientSequenceNumber);
173
198
  }
174
199
  /**
175
200
  * Flush all the batches to the ordering service.
@@ -291,13 +316,7 @@ export class Outbox {
291
316
  */
292
317
  virtualizeBatch(localBatch, groupingEnabled) {
293
318
  // Shallow copy the local batch, updating the messages to be outbound messages
294
- const originalBatch = {
295
- ...localBatch,
296
- messages: localBatch.messages.map(({ serializedOp, ...message }) => ({
297
- contents: serializedOp,
298
- ...message,
299
- })),
300
- };
319
+ const originalBatch = localBatchToOutboundBatch(localBatch);
301
320
  const originalOrGroupedBatch = groupingEnabled
302
321
  ? this.params.groupingManager.groupBatch(originalBatch)
303
322
  : originalBatch;
@@ -308,9 +327,8 @@ export class Outbox {
308
327
  // Regardless of whether we grouped or not, we now have a batch with a single message.
309
328
  // Now proceed to compress/chunk it if necessary.
310
329
  const singletonBatch = originalOrGroupedBatch;
311
- if (this.params.config.compressionOptions === undefined ||
312
- this.params.config.compressionOptions.minimumBatchSizeInBytes >
313
- singletonBatch.contentSizeInBytes ||
330
+ if (this.params.config.compressionOptions.minimumBatchSizeInBytes >
331
+ singletonBatch.contentSizeInBytes ||
314
332
  this.params.submitBatchFn === undefined) {
315
333
  // Nothing to do if compression is disabled, unnecessary or unsupported.
316
334
  return singletonBatch;
@@ -321,16 +339,10 @@ export class Outbox {
321
339
  ? compressedBatch
322
340
  : this.params.splitter.splitSingletonBatchMessage(compressedBatch);
323
341
  }
342
+ // We want to distinguish this "BatchTooLarge" case from the generic "BatchTooLarge" case in sendBatch
324
343
  if (compressedBatch.contentSizeInBytes >= this.params.config.maxBatchSizeInBytes) {
325
- throw DataProcessingError.create("BatchTooLarge", "compressionInsufficient",
326
- /* sequencedMessage */ undefined, {
327
- batchSize: singletonBatch.contentSizeInBytes,
328
- compressedBatchSize: compressedBatch.contentSizeInBytes,
329
- count: compressedBatch.messages.length,
330
- limit: this.params.config.maxBatchSizeInBytes,
331
- chunkingEnabled: this.params.splitter.isBatchChunkingEnabled,
332
- compressionOptions: JSON.stringify(this.params.config.compressionOptions),
333
- socketSize: estimateSocketSize(singletonBatch),
344
+ throw this.makeBatchTooLargeError(compressedBatch, "CompressionInsufficient", {
345
+ uncompressedSizeInBytes: singletonBatch.contentSizeInBytes,
334
346
  });
335
347
  }
336
348
  return compressedBatch;
@@ -348,12 +360,7 @@ export class Outbox {
348
360
  }
349
361
  const socketSize = estimateSocketSize(batch);
350
362
  if (socketSize >= this.params.config.maxBatchSizeInBytes) {
351
- this.logger.sendPerformanceEvent({
352
- eventName: "LargeBatch",
353
- length: batch.messages.length,
354
- sizeInBytes: batch.contentSizeInBytes,
355
- socketSize,
356
- });
363
+ throw this.makeBatchTooLargeError(batch, "CannotSend");
357
364
  }
358
365
  let clientSequenceNumber;
359
366
  if (this.params.submitBatchFn === undefined) {
@@ -376,6 +383,22 @@ export class Outbox {
376
383
  assert(clientSequenceNumber >= 0, 0x3d0 /* clientSequenceNumber can't be negative */);
377
384
  return clientSequenceNumber;
378
385
  }
386
+ makeBatchTooLargeError(batch, codepath, moreDetails) {
387
+ return DataProcessingError.create("BatchTooLarge", codepath,
388
+ /* sequencedMessage */ undefined, {
389
+ errorDetails: {
390
+ opCount: batch.messages.length,
391
+ contentSizeInBytes: batch.contentSizeInBytes,
392
+ socketSize: estimateSocketSize(batch),
393
+ maxBatchSizeInBytes: this.params.config.maxBatchSizeInBytes,
394
+ groupedBatchingEnabled: this.params.groupingManager.groupedBatchingEnabled(),
395
+ compressionOptions: JSON.stringify(this.params.config.compressionOptions),
396
+ chunkingEnabled: this.params.splitter.isBatchChunkingEnabled,
397
+ chunkSizeInBytes: this.params.splitter.chunkSizeInBytes,
398
+ ...moreDetails,
399
+ },
400
+ });
401
+ }
379
402
  /**
380
403
  * Gets a checkpoint object per batch that facilitates iterating over the batch messages when rolling back.
381
404
  */
@@ -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 { IBatchMessage } from \"@fluidframework/container-definitions/internal\";\nimport { ITelemetryBaseLogger } from \"@fluidframework/core-interfaces\";\nimport { assert, Lazy } from \"@fluidframework/core-utils/internal\";\nimport {\n\tDataProcessingError,\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 {\n\tLocalBatchMessage,\n\tIBatchCheckpoint,\n\ttype OutboundBatchMessage,\n\ttype OutboundSingletonBatch,\n\ttype LocalBatch,\n\ttype OutboundBatch,\n} 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/**\n\t * The maximum size of a batch that we can send over the wire.\n\t */\n\treadonly maxBatchSizeInBytes: number;\n\t/**\n\t * If true, maybeFlushPartialBatch will flush the batch if the reference sequence number changed\n\t * since the batch started. Otherwise, it will throw in this case (apart from reentrancy which is handled elsewhere).\n\t * Once the new throw-based flow is proved in a production environment, this option will be removed.\n\t */\n\treadonly flushPartialBatches: 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: OutboundBatch) => 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}\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\t// TODO: better typing here\n\t// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-assignment\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\t// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-assignment\n\tconst originalStackTraceLimit = errorObj.stackTraceLimit;\n\ttry {\n\t\t// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n\t\terrorObj.stackTraceLimit = length;\n\t\treturn action();\n\t} finally {\n\t\t// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-assignment\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\n\t\t\t? Number.POSITIVE_INFINITY\n\t\t\t: 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({\n\t\t\thardLimit,\n\t\t\tcanRebase: false,\n\t\t\tignoreBatchId: true,\n\t\t});\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 mainBatchMessageCount(): number {\n\t\treturn this.mainBatch.length;\n\t}\n\n\tpublic get blobAttachBatchMessageCount(): number {\n\t\treturn this.blobAttachBatch.length;\n\t}\n\n\tpublic get idAllocationBatchMessageCount(): number {\n\t\treturn 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 (if allowed) and create a new batch with the new\n\t * message as the first message. If flushing partial batch is not enabled, we will throw (except for reentrant ops).\n\t * This would indicate we expected this case to be precluded by logic elsewhere.\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(): void {\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\tsequenceNumbersMatch(mainBatchSeqNums, blobAttachSeqNums) &&\n\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\t// Reference and/or Client sequence number will be advancing while processing this batch,\n\t\t// so we can't use this check to detect wrongdoing. But we will still log via telemetry.\n\t\t// This is rare, and the reentrancy will be handled during Flush.\n\t\tconst expectedDueToReentrancy = this.isContextReentrant();\n\n\t\tconst errorWrapper = new Lazy(() =>\n\t\t\tgetLongStack(() =>\n\t\t\t\tDataProcessingError.create(\n\t\t\t\t\t\"Sequence numbers advanced as if ops were processed while a batch is accumulating\",\n\t\t\t\t\t\"outboxSequenceNumberCoherencyCheck\",\n\t\t\t\t),\n\t\t\t),\n\t\t);\n\t\tif (++this.mismatchedOpsReported <= this.maxMismatchedOpsToReport) {\n\t\t\tthis.logger.sendTelemetryEvent(\n\t\t\t\t{\n\t\t\t\t\t// Only log error if this is truly unexpected\n\t\t\t\t\tcategory:\n\t\t\t\t\t\texpectedDueToReentrancy || this.params.config.flushPartialBatches\n\t\t\t\t\t\t\t? \"generic\"\n\t\t\t\t\t\t\t: \"error\",\n\t\t\t\t\teventName: \"ReferenceSequenceNumberMismatch\",\n\t\t\t\t\tData_details: {\n\t\t\t\t\t\texpectedDueToReentrancy,\n\t\t\t\t\t\tmainReferenceSequenceNumber: mainBatchSeqNums.referenceSequenceNumber,\n\t\t\t\t\t\tmainClientSequenceNumber: mainBatchSeqNums.clientSequenceNumber,\n\t\t\t\t\t\tblobAttachReferenceSequenceNumber: blobAttachSeqNums.referenceSequenceNumber,\n\t\t\t\t\t\tblobAttachClientSequenceNumber: blobAttachSeqNums.clientSequenceNumber,\n\t\t\t\t\t\tcurrentReferenceSequenceNumber: currentSequenceNumbers.referenceSequenceNumber,\n\t\t\t\t\t\tcurrentClientSequenceNumber: currentSequenceNumbers.clientSequenceNumber,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\terrorWrapper.value,\n\t\t\t);\n\t\t}\n\n\t\t// If we're configured to flush partial batches, do that now and return (don't throw)\n\t\tif (this.params.config.flushPartialBatches) {\n\t\t\tthis.flushAll();\n\t\t\treturn;\n\t\t}\n\n\t\t// If we are in a reentrant context, we know this can happen without causing any harm.\n\t\tif (expectedDueToReentrancy) {\n\t\t\treturn;\n\t\t}\n\n\t\tthrow errorWrapper.value;\n\t}\n\n\tpublic submit(message: LocalBatchMessage): void {\n\t\tthis.maybeFlushPartialBatch();\n\n\t\tthis.addMessageToBatchManager(this.mainBatch, message);\n\t}\n\n\tpublic submitBlobAttach(message: LocalBatchMessage): void {\n\t\tthis.maybeFlushPartialBatch();\n\n\t\tthis.addMessageToBatchManager(this.blobAttachBatch, message);\n\t}\n\n\tpublic submitIdAllocation(message: LocalBatchMessage): void {\n\t\tthis.maybeFlushPartialBatch();\n\n\t\tthis.addMessageToBatchManager(this.idAllocationBatch, message);\n\t}\n\n\tprivate addMessageToBatchManager(\n\t\tbatchManager: BatchManager,\n\t\tmessage: LocalBatchMessage,\n\t): void {\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.serializedOp?.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 *\n\t * @throws If called from a reentrant context, or if the batch being flushed is too large.\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): void {\n\t\tassert(\n\t\t\t!this.isContextReentrant(),\n\t\t\t0xb7b /* Flushing must not happen while incoming changes are being processed */,\n\t\t);\n\n\t\tthis.flushAll(resubmittingBatchId);\n\t}\n\n\tprivate flushAll(resubmittingBatchId?: BatchId): void {\n\t\tconst allBatchesEmpty =\n\t\t\tthis.idAllocationBatch.empty && this.blobAttachBatch.empty && this.mainBatch.empty;\n\t\tif (allBatchesEmpty) {\n\t\t\t// If we're resubmitting and all batches are empty, we need to flush an empty batch.\n\t\t\t// Note that we currently resubmit one batch at a time, so on resubmit, 2 of the 3 batches will *always* be empty.\n\t\t\t// It's theoretically possible that we don't *need* to resubmit this empty batch, and in those cases, it'll safely be ignored\n\t\t\t// by the rest of the system, including remote clients.\n\t\t\t// In some cases we *must* resubmit the empty batch (to match up with a non-empty version tracked locally by a container fork), so we do it always.\n\t\t\tif (resubmittingBatchId) {\n\t\t\t\tthis.flushEmptyBatch(resubmittingBatchId);\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\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 flushEmptyBatch(resubmittingBatchId: BatchId): void {\n\t\tconst referenceSequenceNumber =\n\t\t\tthis.params.getCurrentSequenceNumbers().referenceSequenceNumber;\n\t\tassert(\n\t\t\treferenceSequenceNumber !== undefined,\n\t\t\t0xa01 /* reference sequence number should be defined */,\n\t\t);\n\t\tconst { outboundBatch, placeholderMessage } =\n\t\t\tthis.params.groupingManager.createEmptyGroupedBatch(\n\t\t\t\tresubmittingBatchId,\n\t\t\t\treferenceSequenceNumber,\n\t\t\t);\n\t\tlet clientSequenceNumber: number | undefined;\n\t\tif (this.params.shouldSend()) {\n\t\t\tclientSequenceNumber = this.sendBatch(outboundBatch);\n\t\t}\n\n\t\t// Push the empty batch placeholder to the PendingStateManager\n\t\tthis.params.pendingStateManager.onFlushBatch(\n\t\t\t[{ ...placeholderMessage, serializedOp: \"\", contents: undefined }], // placeholder message - serializedOp will never be used\n\t\t\tclientSequenceNumber,\n\t\t);\n\t\treturn;\n\t}\n\n\tprivate flushInternal(\n\t\tbatchManager: BatchManager,\n\t\tdisableGroupedBatching: boolean = false,\n\t\tresubmittingBatchId?: BatchId,\n\t): void {\n\t\tif (batchManager.empty) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst rawBatch = batchManager.popBatch(resubmittingBatchId);\n\t\tconst groupingEnabled =\n\t\t\t!disableGroupedBatching && this.params.groupingManager.groupedBatchingEnabled();\n\t\tif (\n\t\t\tbatchManager.options.canRebase &&\n\t\t\trawBatch.hasReentrantOps === true &&\n\t\t\t// NOTE: This is too restrictive. We should rebase for any reentrant op, not just if it's going to be a grouped batch\n\t\t\t// However there is some test that is depending on this behavior so we haven't removed these conditions yet. See AB#33427\n\t\t\tgroupingEnabled &&\n\t\t\trawBatch.messages.length > 1\n\t\t) {\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 virtualizedBatch = this.virtualizeBatch(rawBatch, groupingEnabled);\n\n\t\t\tclientSequenceNumber = this.sendBatch(virtualizedBatch);\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(\n\t\t\trawBatch.messages,\n\t\t\tclientSequenceNumber,\n\t\t\tbatchManager.options.ignoreBatchId,\n\t\t);\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: LocalBatch, batchManager: BatchManager): void {\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\tcontent: message.serializedOp,\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, groups / compresses / 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 localBatch - Local Batch to be virtualized - i.e. transformed into an Outbound Batch\n\t * @param groupingEnabled - If true, Grouped batching is enabled.\n\t * @returns One of the following:\n\t * - (A) The original batch (Based on what's enabled)\n\t * - (B) A grouped batch (it's a singleton batch)\n\t * - (C) A compressed singleton batch\n\t * - (D) A singleton batch containing the last chunk.\n\t */\n\tprivate virtualizeBatch(localBatch: LocalBatch, groupingEnabled: boolean): OutboundBatch {\n\t\t// Shallow copy the local batch, updating the messages to be outbound messages\n\t\tconst originalBatch: OutboundBatch = {\n\t\t\t...localBatch,\n\t\t\tmessages: localBatch.messages.map<OutboundBatchMessage>(\n\t\t\t\t({ serializedOp, ...message }) => ({\n\t\t\t\t\tcontents: serializedOp,\n\t\t\t\t\t...message,\n\t\t\t\t}),\n\t\t\t),\n\t\t};\n\n\t\tconst originalOrGroupedBatch = groupingEnabled\n\t\t\t? this.params.groupingManager.groupBatch(originalBatch)\n\t\t\t: originalBatch;\n\n\t\tif (originalOrGroupedBatch.messages.length !== 1) {\n\t\t\t// Compression requires a single message, so return early otherwise.\n\t\t\treturn originalOrGroupedBatch;\n\t\t}\n\n\t\t// Regardless of whether we grouped or not, we now have a batch with a single message.\n\t\t// Now proceed to compress/chunk it if necessary.\n\t\tconst singletonBatch = originalOrGroupedBatch as OutboundSingletonBatch;\n\n\t\tif (\n\t\t\tthis.params.config.compressionOptions === undefined ||\n\t\t\tthis.params.config.compressionOptions.minimumBatchSizeInBytes >\n\t\t\t\tsingletonBatch.contentSizeInBytes ||\n\t\t\tthis.params.submitBatchFn === undefined\n\t\t) {\n\t\t\t// Nothing to do if compression is disabled, unnecessary or unsupported.\n\t\t\treturn singletonBatch;\n\t\t}\n\n\t\tconst compressedBatch = this.params.compressor.compressBatch(singletonBatch);\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.splitSingletonBatchMessage(compressedBatch);\n\t\t}\n\n\t\tif (compressedBatch.contentSizeInBytes >= this.params.config.maxBatchSizeInBytes) {\n\t\t\tthrow DataProcessingError.create(\n\t\t\t\t\"BatchTooLarge\",\n\t\t\t\t\"compressionInsufficient\",\n\t\t\t\t/* sequencedMessage */ undefined,\n\t\t\t\t{\n\t\t\t\t\tbatchSize: singletonBatch.contentSizeInBytes,\n\t\t\t\t\tcompressedBatchSize: compressedBatch.contentSizeInBytes,\n\t\t\t\t\tcount: compressedBatch.messages.length,\n\t\t\t\t\tlimit: this.params.config.maxBatchSizeInBytes,\n\t\t\t\t\tchunkingEnabled: this.params.splitter.isBatchChunkingEnabled,\n\t\t\t\t\tcompressionOptions: JSON.stringify(this.params.config.compressionOptions),\n\t\t\t\t\tsocketSize: estimateSocketSize(singletonBatch),\n\t\t\t\t},\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: OutboundBatch): number | undefined {\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\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 * Gets a checkpoint object per batch that facilitates iterating over the batch messages when rolling back.\n\t */\n\tpublic getBatchCheckpoints(): {\n\t\tmainBatch: IBatchCheckpoint;\n\t\tidAllocationBatch: IBatchCheckpoint;\n\t\tblobAttachBatch: IBatchCheckpoint;\n\t} {\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 { IBatchMessage } from \"@fluidframework/container-definitions/internal\";\nimport {\n\tITelemetryBaseLogger,\n\ttype ITelemetryBaseProperties,\n} from \"@fluidframework/core-interfaces\";\nimport { assert, Lazy } from \"@fluidframework/core-utils/internal\";\nimport {\n\tDataProcessingError,\n\tUsageError,\n\tcreateChildLogger,\n\ttype IFluidErrorBase,\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\tsequenceNumbersMatch,\n\ttype BatchId,\n} from \"./batchManager.js\";\nimport {\n\tLocalBatchMessage,\n\tIBatchCheckpoint,\n\ttype OutboundBatchMessage,\n\ttype OutboundSingletonBatch,\n\ttype LocalBatch,\n\ttype OutboundBatch,\n} 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/**\n\t * The maximum size of a batch that we can send over the wire.\n\t */\n\treadonly maxBatchSizeInBytes: number;\n\t/**\n\t * If true, maybeFlushPartialBatch will flush the batch if the reference sequence number changed\n\t * since the batch started. Otherwise, it will throw in this case (apart from reentrancy which is handled elsewhere).\n\t * Once the new throw-based flow is proved in a production environment, this option will be removed.\n\t */\n\treadonly flushPartialBatches: 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: OutboundBatch) => 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}\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\t// TODO: better typing here\n\t// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-assignment\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\t// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-assignment\n\tconst originalStackTraceLimit = errorObj.stackTraceLimit;\n\ttry {\n\t\t// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n\t\terrorObj.stackTraceLimit = length;\n\t\treturn action();\n\t} finally {\n\t\t// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-assignment\n\t\terrorObj.stackTraceLimit = originalStackTraceLimit;\n\t}\n}\n\n/**\n * Convert from local batch to outbound batch, including computing contentSizeInBytes.\n */\nexport function localBatchToOutboundBatch(localBatch: LocalBatch): OutboundBatch {\n\t// Shallow copy each message as we switch types\n\tconst outboundMessages = localBatch.messages.map<OutboundBatchMessage>(\n\t\t({ serializedOp, ...message }) => ({\n\t\t\tcontents: serializedOp,\n\t\t\t...message,\n\t\t}),\n\t);\n\tconst contentSizeInBytes = outboundMessages.reduce(\n\t\t(acc, message) => acc + (message.contents?.length ?? 0),\n\t\t0,\n\t);\n\n\t// Shallow copy the local batch, updating the messages to be outbound messages and adding contentSizeInBytes\n\tconst outboundBatch: OutboundBatch = {\n\t\t...localBatch,\n\t\tmessages: outboundMessages,\n\t\tcontentSizeInBytes,\n\t};\n\n\treturn outboundBatch;\n}\n\n/**\n * Estimated size of the stringification overhead for an op accumulated\n * from runtime to loader to the service.\n */\nconst opOverhead = 200;\n\n/**\n * Estimates the real size in bytes on the socket for a given batch. It assumes that\n * the envelope size (and the size of an empty op) is 200 bytes, taking into account\n * extra overhead from stringification.\n *\n * @remarks\n * Also content will be stringified, and that adds a lot of overhead due to a lot of escape characters.\n * Not taking it into account, as compression work should help there - compressed payload will be\n * initially stored as base64, and that requires only 2 extra escape characters.\n *\n * @param batch - the batch to inspect\n * @returns An estimate of the payload size in bytes which will be produced when the batch is sent over the wire\n */\nexport const estimateSocketSize = (batch: OutboundBatch): number => {\n\treturn batch.contentSizeInBytes + opOverhead * batch.messages.length;\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\tthis.mainBatch = new BatchManager({ canRebase: true });\n\t\tthis.blobAttachBatch = new BatchManager({ canRebase: true });\n\t\tthis.idAllocationBatch = new BatchManager({\n\t\t\tcanRebase: false,\n\t\t\tignoreBatchId: true,\n\t\t});\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 mainBatchMessageCount(): number {\n\t\treturn this.mainBatch.length;\n\t}\n\n\tpublic get blobAttachBatchMessageCount(): number {\n\t\treturn this.blobAttachBatch.length;\n\t}\n\n\tpublic get idAllocationBatchMessageCount(): number {\n\t\treturn 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 (if allowed) and create a new batch with the new\n\t * message as the first message. If flushing partial batch is not enabled, we will throw (except for reentrant ops).\n\t * This would indicate we expected this case to be precluded by logic elsewhere.\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(): void {\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\tsequenceNumbersMatch(mainBatchSeqNums, blobAttachSeqNums) &&\n\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\t// Reference and/or Client sequence number will be advancing while processing this batch,\n\t\t// so we can't use this check to detect wrongdoing. But we will still log via telemetry.\n\t\t// This is rare, and the reentrancy will be handled during Flush.\n\t\tconst expectedDueToReentrancy = this.isContextReentrant();\n\n\t\tconst errorWrapper = new Lazy(() =>\n\t\t\tgetLongStack(() =>\n\t\t\t\tDataProcessingError.create(\n\t\t\t\t\t\"Sequence numbers advanced as if ops were processed while a batch is accumulating\",\n\t\t\t\t\t\"outboxSequenceNumberCoherencyCheck\",\n\t\t\t\t),\n\t\t\t),\n\t\t);\n\t\tif (++this.mismatchedOpsReported <= this.maxMismatchedOpsToReport) {\n\t\t\tthis.logger.sendTelemetryEvent(\n\t\t\t\t{\n\t\t\t\t\t// Only log error if this is truly unexpected\n\t\t\t\t\tcategory:\n\t\t\t\t\t\texpectedDueToReentrancy || this.params.config.flushPartialBatches\n\t\t\t\t\t\t\t? \"generic\"\n\t\t\t\t\t\t\t: \"error\",\n\t\t\t\t\teventName: \"ReferenceSequenceNumberMismatch\",\n\t\t\t\t\tData_details: {\n\t\t\t\t\t\texpectedDueToReentrancy,\n\t\t\t\t\t\tmainReferenceSequenceNumber: mainBatchSeqNums.referenceSequenceNumber,\n\t\t\t\t\t\tmainClientSequenceNumber: mainBatchSeqNums.clientSequenceNumber,\n\t\t\t\t\t\tblobAttachReferenceSequenceNumber: blobAttachSeqNums.referenceSequenceNumber,\n\t\t\t\t\t\tblobAttachClientSequenceNumber: blobAttachSeqNums.clientSequenceNumber,\n\t\t\t\t\t\tcurrentReferenceSequenceNumber: currentSequenceNumbers.referenceSequenceNumber,\n\t\t\t\t\t\tcurrentClientSequenceNumber: currentSequenceNumbers.clientSequenceNumber,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\terrorWrapper.value,\n\t\t\t);\n\t\t}\n\n\t\t// If we're configured to flush partial batches, do that now and return (don't throw)\n\t\tif (this.params.config.flushPartialBatches) {\n\t\t\tthis.flushAll();\n\t\t\treturn;\n\t\t}\n\n\t\t// If we are in a reentrant context, we know this can happen without causing any harm.\n\t\tif (expectedDueToReentrancy) {\n\t\t\treturn;\n\t\t}\n\n\t\tthrow errorWrapper.value;\n\t}\n\n\tpublic submit(message: LocalBatchMessage): void {\n\t\tthis.maybeFlushPartialBatch();\n\n\t\tthis.addMessageToBatchManager(this.mainBatch, message);\n\t}\n\n\tpublic submitBlobAttach(message: LocalBatchMessage): void {\n\t\tthis.maybeFlushPartialBatch();\n\n\t\tthis.addMessageToBatchManager(this.blobAttachBatch, message);\n\t}\n\n\tpublic submitIdAllocation(message: LocalBatchMessage): void {\n\t\tthis.maybeFlushPartialBatch();\n\n\t\tthis.addMessageToBatchManager(this.idAllocationBatch, message);\n\t}\n\n\tprivate addMessageToBatchManager(\n\t\tbatchManager: BatchManager,\n\t\tmessage: LocalBatchMessage,\n\t): void {\n\t\tbatchManager.push(\n\t\t\tmessage,\n\t\t\tthis.isContextReentrant(),\n\t\t\tthis.params.getCurrentSequenceNumbers().clientSequenceNumber,\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 *\n\t * @throws If called from a reentrant context, or if the batch being flushed is too large.\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): void {\n\t\tassert(\n\t\t\t!this.isContextReentrant(),\n\t\t\t0xb7b /* Flushing must not happen while incoming changes are being processed */,\n\t\t);\n\n\t\tthis.flushAll(resubmittingBatchId);\n\t}\n\n\tprivate flushAll(resubmittingBatchId?: BatchId): void {\n\t\tconst allBatchesEmpty =\n\t\t\tthis.idAllocationBatch.empty && this.blobAttachBatch.empty && this.mainBatch.empty;\n\t\tif (allBatchesEmpty) {\n\t\t\t// If we're resubmitting and all batches are empty, we need to flush an empty batch.\n\t\t\t// Note that we currently resubmit one batch at a time, so on resubmit, 2 of the 3 batches will *always* be empty.\n\t\t\t// It's theoretically possible that we don't *need* to resubmit this empty batch, and in those cases, it'll safely be ignored\n\t\t\t// by the rest of the system, including remote clients.\n\t\t\t// In some cases we *must* resubmit the empty batch (to match up with a non-empty version tracked locally by a container fork), so we do it always.\n\t\t\tif (resubmittingBatchId) {\n\t\t\t\tthis.flushEmptyBatch(resubmittingBatchId);\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\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 flushEmptyBatch(resubmittingBatchId: BatchId): void {\n\t\tconst referenceSequenceNumber =\n\t\t\tthis.params.getCurrentSequenceNumbers().referenceSequenceNumber;\n\t\tassert(\n\t\t\treferenceSequenceNumber !== undefined,\n\t\t\t0xa01 /* reference sequence number should be defined */,\n\t\t);\n\t\tconst { outboundBatch, placeholderMessage } =\n\t\t\tthis.params.groupingManager.createEmptyGroupedBatch(\n\t\t\t\tresubmittingBatchId,\n\t\t\t\treferenceSequenceNumber,\n\t\t\t);\n\t\tlet clientSequenceNumber: number | undefined;\n\t\tif (this.params.shouldSend()) {\n\t\t\tclientSequenceNumber = this.sendBatch(outboundBatch);\n\t\t}\n\n\t\t// Push the empty batch placeholder to the PendingStateManager\n\t\tthis.params.pendingStateManager.onFlushBatch(\n\t\t\t[{ ...placeholderMessage, serializedOp: \"\", contents: undefined }], // placeholder message - serializedOp will never be used\n\t\t\tclientSequenceNumber,\n\t\t);\n\t\treturn;\n\t}\n\n\tprivate flushInternal(\n\t\tbatchManager: BatchManager,\n\t\tdisableGroupedBatching: boolean = false,\n\t\tresubmittingBatchId?: BatchId,\n\t): void {\n\t\tif (batchManager.empty) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst rawBatch = batchManager.popBatch(resubmittingBatchId);\n\t\tconst groupingEnabled =\n\t\t\t!disableGroupedBatching && this.params.groupingManager.groupedBatchingEnabled();\n\t\tif (\n\t\t\tbatchManager.options.canRebase &&\n\t\t\trawBatch.hasReentrantOps === true &&\n\t\t\t// NOTE: This is too restrictive. We should rebase for any reentrant op, not just if it's going to be a grouped batch\n\t\t\t// However there is some test that is depending on this behavior so we haven't removed these conditions yet. See AB#33427\n\t\t\tgroupingEnabled &&\n\t\t\trawBatch.messages.length > 1\n\t\t) {\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 virtualizedBatch = this.virtualizeBatch(rawBatch, groupingEnabled);\n\n\t\t\tclientSequenceNumber = this.sendBatch(virtualizedBatch);\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(\n\t\t\trawBatch.messages,\n\t\t\tclientSequenceNumber,\n\t\t\tbatchManager.options.ignoreBatchId,\n\t\t);\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: LocalBatch, batchManager: BatchManager): void {\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\tcontent: message.serializedOp,\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, groups / compresses / 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 localBatch - Local Batch to be virtualized - i.e. transformed into an Outbound Batch\n\t * @param groupingEnabled - If true, Grouped batching is enabled.\n\t * @returns One of the following:\n\t * - (A) The original batch (Based on what's enabled)\n\t * - (B) A grouped batch (it's a singleton batch)\n\t * - (C) A compressed singleton batch\n\t * - (D) A singleton batch containing the last chunk.\n\t */\n\tprivate virtualizeBatch(localBatch: LocalBatch, groupingEnabled: boolean): OutboundBatch {\n\t\t// Shallow copy the local batch, updating the messages to be outbound messages\n\t\tconst originalBatch = localBatchToOutboundBatch(localBatch);\n\n\t\tconst originalOrGroupedBatch = groupingEnabled\n\t\t\t? this.params.groupingManager.groupBatch(originalBatch)\n\t\t\t: originalBatch;\n\n\t\tif (originalOrGroupedBatch.messages.length !== 1) {\n\t\t\t// Compression requires a single message, so return early otherwise.\n\t\t\treturn originalOrGroupedBatch;\n\t\t}\n\n\t\t// Regardless of whether we grouped or not, we now have a batch with a single message.\n\t\t// Now proceed to compress/chunk it if necessary.\n\t\tconst singletonBatch = originalOrGroupedBatch as OutboundSingletonBatch;\n\n\t\tif (\n\t\t\tthis.params.config.compressionOptions.minimumBatchSizeInBytes >\n\t\t\t\tsingletonBatch.contentSizeInBytes ||\n\t\t\tthis.params.submitBatchFn === undefined\n\t\t) {\n\t\t\t// Nothing to do if compression is disabled, unnecessary or unsupported.\n\t\t\treturn singletonBatch;\n\t\t}\n\n\t\tconst compressedBatch = this.params.compressor.compressBatch(singletonBatch);\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.splitSingletonBatchMessage(compressedBatch);\n\t\t}\n\n\t\t// We want to distinguish this \"BatchTooLarge\" case from the generic \"BatchTooLarge\" case in sendBatch\n\t\tif (compressedBatch.contentSizeInBytes >= this.params.config.maxBatchSizeInBytes) {\n\t\t\tthrow this.makeBatchTooLargeError(compressedBatch, \"CompressionInsufficient\", {\n\t\t\t\tuncompressedSizeInBytes: singletonBatch.contentSizeInBytes,\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: OutboundBatch): number | undefined {\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\tthrow this.makeBatchTooLargeError(batch, \"CannotSend\");\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.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\tprivate makeBatchTooLargeError(\n\t\tbatch: OutboundBatch,\n\t\tcodepath: string,\n\t\tmoreDetails?: ITelemetryBaseProperties,\n\t): IFluidErrorBase {\n\t\treturn DataProcessingError.create(\n\t\t\t\"BatchTooLarge\",\n\t\t\tcodepath,\n\t\t\t/* sequencedMessage */ undefined,\n\t\t\t{\n\t\t\t\terrorDetails: {\n\t\t\t\t\topCount: batch.messages.length,\n\t\t\t\t\tcontentSizeInBytes: batch.contentSizeInBytes,\n\t\t\t\t\tsocketSize: estimateSocketSize(batch),\n\t\t\t\t\tmaxBatchSizeInBytes: this.params.config.maxBatchSizeInBytes,\n\t\t\t\t\tgroupedBatchingEnabled: this.params.groupingManager.groupedBatchingEnabled(),\n\t\t\t\t\tcompressionOptions: JSON.stringify(this.params.config.compressionOptions),\n\t\t\t\t\tchunkingEnabled: this.params.splitter.isBatchChunkingEnabled,\n\t\t\t\t\tchunkSizeInBytes: this.params.splitter.chunkSizeInBytes,\n\t\t\t\t\t...moreDetails,\n\t\t\t\t},\n\t\t\t},\n\t\t);\n\t}\n\n\t/**\n\t * Gets a checkpoint object per batch that facilitates iterating over the batch messages when rolling back.\n\t */\n\tpublic getBatchCheckpoints(): {\n\t\tmainBatch: IBatchCheckpoint;\n\t\tidAllocationBatch: IBatchCheckpoint;\n\t\tblobAttachBatch: IBatchCheckpoint;\n\t} {\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"]}
@@ -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.32.0";
8
+ export declare const pkgVersion = "2.33.0-333010";
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,WAAW,CAAC"}
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,kBAAkB,CAAC"}
@@ -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.32.0";
8
+ export const pkgVersion = "2.33.0-333010";
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,QAAQ,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.32.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,eAAe,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.33.0-333010\";\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fluidframework/container-runtime",
3
- "version": "2.32.0",
3
+ "version": "2.33.0-333010",
4
4
  "description": "Fluid container runtime",
5
5
  "homepage": "https://fluidframework.com",
6
6
  "repository": {
@@ -119,18 +119,18 @@
119
119
  "temp-directory": "nyc/.nyc_output"
120
120
  },
121
121
  "dependencies": {
122
- "@fluid-internal/client-utils": "~2.32.0",
123
- "@fluidframework/container-definitions": "~2.32.0",
124
- "@fluidframework/container-runtime-definitions": "~2.32.0",
125
- "@fluidframework/core-interfaces": "~2.32.0",
126
- "@fluidframework/core-utils": "~2.32.0",
127
- "@fluidframework/datastore": "~2.32.0",
128
- "@fluidframework/driver-definitions": "~2.32.0",
129
- "@fluidframework/driver-utils": "~2.32.0",
130
- "@fluidframework/id-compressor": "~2.32.0",
131
- "@fluidframework/runtime-definitions": "~2.32.0",
132
- "@fluidframework/runtime-utils": "~2.32.0",
133
- "@fluidframework/telemetry-utils": "~2.32.0",
122
+ "@fluid-internal/client-utils": "2.33.0-333010",
123
+ "@fluidframework/container-definitions": "2.33.0-333010",
124
+ "@fluidframework/container-runtime-definitions": "2.33.0-333010",
125
+ "@fluidframework/core-interfaces": "2.33.0-333010",
126
+ "@fluidframework/core-utils": "2.33.0-333010",
127
+ "@fluidframework/datastore": "2.33.0-333010",
128
+ "@fluidframework/driver-definitions": "2.33.0-333010",
129
+ "@fluidframework/driver-utils": "2.33.0-333010",
130
+ "@fluidframework/id-compressor": "2.33.0-333010",
131
+ "@fluidframework/runtime-definitions": "2.33.0-333010",
132
+ "@fluidframework/runtime-utils": "2.33.0-333010",
133
+ "@fluidframework/telemetry-utils": "2.33.0-333010",
134
134
  "@tylerbu/sorted-btree-es6": "^1.8.0",
135
135
  "double-ended-queue": "^2.1.0-0",
136
136
  "lz4js": "^0.2.0",
@@ -139,16 +139,16 @@
139
139
  "devDependencies": {
140
140
  "@arethetypeswrong/cli": "^0.17.1",
141
141
  "@biomejs/biome": "~1.9.3",
142
- "@fluid-internal/mocha-test-setup": "~2.32.0",
143
- "@fluid-private/stochastic-test-utils": "~2.32.0",
144
- "@fluid-private/test-pairwise-generator": "~2.32.0",
142
+ "@fluid-internal/mocha-test-setup": "2.33.0-333010",
143
+ "@fluid-private/stochastic-test-utils": "2.33.0-333010",
144
+ "@fluid-private/test-pairwise-generator": "2.33.0-333010",
145
145
  "@fluid-tools/benchmark": "^0.50.0",
146
146
  "@fluid-tools/build-cli": "^0.55.0",
147
147
  "@fluidframework/build-common": "^2.0.3",
148
148
  "@fluidframework/build-tools": "^0.55.0",
149
149
  "@fluidframework/container-runtime-previous": "npm:@fluidframework/container-runtime@2.31.0",
150
150
  "@fluidframework/eslint-config-fluid": "^5.7.3",
151
- "@fluidframework/test-runtime-utils": "~2.32.0",
151
+ "@fluidframework/test-runtime-utils": "2.33.0-333010",
152
152
  "@microsoft/api-extractor": "7.50.1",
153
153
  "@types/double-ended-queue": "^2.1.0",
154
154
  "@types/lz4js": "^0.2.0",
@@ -14,16 +14,10 @@ import { ICompressionRuntimeOptions } from "../containerRuntime.js";
14
14
  import { asBatchMetadata, type IBatchMetadata } from "../metadata.js";
15
15
  import type { IPendingMessage } from "../pendingStateManager.js";
16
16
 
17
- import {
18
- LocalBatchMessage,
19
- IBatchCheckpoint,
20
- type LocalBatch,
21
- type OutboundBatch,
22
- } from "./definitions.js";
17
+ import { LocalBatchMessage, IBatchCheckpoint, type LocalBatch } from "./definitions.js";
23
18
  import type { BatchStartInfo } from "./remoteMessageProcessor.js";
24
19
 
25
20
  export interface IBatchManagerOptions {
26
- readonly hardLimit: number;
27
21
  readonly compressionOptions?: ICompressionRuntimeOptions;
28
22
 
29
23
  /**
@@ -78,26 +72,16 @@ export function getEffectiveBatchId(
78
72
  return batchStart.batchId ?? generateBatchId(batchStart.clientId, batchStart.batchStartCsn);
79
73
  }
80
74
 
81
- /**
82
- * Estimated size of the stringification overhead for an op accumulated
83
- * from runtime to loader to the service.
84
- */
85
- const opOverhead = 200;
86
-
87
75
  /**
88
76
  * Helper class that manages partial batch & rollback.
89
77
  */
90
78
  export class BatchManager {
91
79
  private pendingBatch: LocalBatchMessage[] = [];
92
- private batchContentSize = 0;
93
80
  private hasReentrantOps = false;
94
81
 
95
82
  public get length(): number {
96
83
  return this.pendingBatch.length;
97
84
  }
98
- public get contentSizeInBytes(): number {
99
- return this.batchContentSize;
100
- }
101
85
 
102
86
  public get sequenceNumbers(): BatchSequenceNumbers {
103
87
  return {
@@ -125,29 +109,14 @@ export class BatchManager {
125
109
  message: LocalBatchMessage,
126
110
  reentrant: boolean,
127
111
  currentClientSequenceNumber?: number,
128
- ): boolean {
129
- const contentSize = this.batchContentSize + (message.serializedOp?.length ?? 0);
130
- const opCount = this.pendingBatch.length;
112
+ ): void {
131
113
  this.hasReentrantOps = this.hasReentrantOps || reentrant;
132
114
 
133
- // Attempt to estimate batch size, aka socket message size.
134
- // Each op has pretty large envelope, estimating to be 200 bytes.
135
- // Also content will be strigified, and that adds a lot of overhead due to a lot of escape characters.
136
- // Not taking it into account, as compression work should help there - compressed payload will be
137
- // initially stored as base64, and that requires only 2 extra escape characters.
138
- const socketMessageSize = contentSize + opOverhead * opCount;
139
-
140
- if (socketMessageSize >= this.options.hardLimit) {
141
- return false;
142
- }
143
-
144
115
  if (this.pendingBatch.length === 0) {
145
116
  this.clientSequenceNumber = currentClientSequenceNumber;
146
117
  }
147
118
 
148
- this.batchContentSize = contentSize;
149
119
  this.pendingBatch.push(message);
150
- return true;
151
120
  }
152
121
 
153
122
  public get empty(): boolean {
@@ -160,13 +129,11 @@ export class BatchManager {
160
129
  public popBatch(batchId?: BatchId): LocalBatch {
161
130
  const batch: LocalBatch = {
162
131
  messages: this.pendingBatch,
163
- contentSizeInBytes: this.batchContentSize,
164
132
  referenceSequenceNumber: this.referenceSequenceNumber,
165
133
  hasReentrantOps: this.hasReentrantOps,
166
134
  };
167
135
 
168
136
  this.pendingBatch = [];
169
- this.batchContentSize = 0;
170
137
  this.clientSequenceNumber = undefined;
171
138
  this.hasReentrantOps = false;
172
139
 
@@ -184,7 +151,6 @@ export class BatchManager {
184
151
  this.clientSequenceNumber = startSequenceNumber;
185
152
  const rollbackOpsLifo = this.pendingBatch.splice(startPoint).reverse();
186
153
  for (const message of rollbackOpsLifo) {
187
- this.batchContentSize -= message.serializedOp?.length ?? 0;
188
154
  process(message);
189
155
  }
190
156
  const count = this.pendingBatch.length - startPoint;
@@ -233,18 +199,6 @@ const addBatchMetadata = (batch: LocalBatch, batchId?: BatchId): LocalBatch => {
233
199
  return batch;
234
200
  };
235
201
 
236
- /**
237
- * Estimates the real size in bytes on the socket for a given batch. It assumes that
238
- * the envelope size (and the size of an empty op) is 200 bytes, taking into account
239
- * extra overhead from stringification.
240
- *
241
- * @param batch - the batch to inspect
242
- * @returns An estimate of the payload size in bytes which will be produced when the batch is sent over the wire
243
- */
244
- export const estimateSocketSize = (batch: OutboundBatch): number => {
245
- return batch.contentSizeInBytes + opOverhead * batch.messages.length;
246
- };
247
-
248
202
  export const sequenceNumbersMatch = (
249
203
  seqNums: BatchSequenceNumbers,
250
204
  otherSeqNums: BatchSequenceNumbers,
@@ -45,25 +45,29 @@ export type LocalBatch = IBatch<LocalBatchMessage[]>;
45
45
  /**
46
46
  * A batch of messages that has been virtualized as needed (grouped, compressed, chunked)
47
47
  * and is ready to be sent to the ordering service.
48
+ * At the very least, the op contents have been serialized to string.
48
49
  */
49
- export type OutboundBatch = IBatch<OutboundBatchMessage[]>;
50
+ export interface OutboundBatch<
51
+ TMessages extends OutboundBatchMessage[] = OutboundBatchMessage[],
52
+ > extends IBatch<TMessages> {
53
+ /**
54
+ * Sum of the in-memory content sizes of all messages in the batch.
55
+ * If the batch is compressed, this number reflects the post-compression size.
56
+ */
57
+ readonly contentSizeInBytes: number;
58
+ }
50
59
 
51
60
  /**
52
61
  * An {@link OutboundBatch} with exactly one message
53
62
  * This type is helpful as Grouping yields this kind of batch, and Compression only operates on this type of batch.
54
63
  */
55
- export type OutboundSingletonBatch = IBatch<[OutboundBatchMessage]>;
64
+ export type OutboundSingletonBatch = OutboundBatch<[OutboundBatchMessage]>;
56
65
 
57
66
  /**
58
67
  * Base batch interface used internally by the runtime.
59
68
  * See {@link LocalBatch} and {@link OutboundBatch} for the concrete types.
60
69
  */
61
70
  interface IBatch<TMessages extends LocalBatchMessage[] | OutboundBatchMessage[]> {
62
- /**
63
- * Sum of the in-memory content sizes of all messages in the batch.
64
- * If the batch is compressed, this number reflects the post-compression size.
65
- */
66
- readonly contentSizeInBytes: number;
67
71
  /**
68
72
  * All the messages in the batch
69
73
  */
@@ -7,7 +7,6 @@ export {
7
7
  BatchId,
8
8
  BatchManager,
9
9
  BatchSequenceNumbers,
10
- estimateSocketSize,
11
10
  getEffectiveBatchId,
12
11
  generateBatchId,
13
12
  IBatchManagerOptions,
@@ -26,7 +25,12 @@ export {
26
25
  serializeOp,
27
26
  ensureContentsDeserialized,
28
27
  } from "./opSerialization.js";
29
- export { Outbox, getLongStack } from "./outbox.js";
28
+ export {
29
+ estimateSocketSize,
30
+ localBatchToOutboundBatch,
31
+ Outbox,
32
+ getLongStack,
33
+ } from "./outbox.js";
30
34
  export { OpCompressor } from "./opCompressor.js";
31
35
  export { OpDecompressor } from "./opDecompressor.js";
32
36
  export { OpSplitter, splitOp, isChunkedMessage } from "./opSplitter.js";
@@ -15,8 +15,8 @@ import { compress } from "lz4js";
15
15
 
16
16
  import { CompressionAlgorithms } from "../containerRuntime.js";
17
17
 
18
- import { estimateSocketSize } from "./batchManager.js";
19
18
  import { type OutboundBatchMessage, type OutboundSingletonBatch } from "./definitions.js";
19
+ import { estimateSocketSize } from "./outbox.js";
20
20
 
21
21
  /**
22
22
  * Compresses batches of ops.
@@ -16,12 +16,12 @@ import {
16
16
 
17
17
  import { ContainerMessageType, ContainerRuntimeChunkedOpMessage } from "../messageTypes.js";
18
18
 
19
- import { estimateSocketSize } from "./batchManager.js";
20
19
  import {
21
20
  IChunkedOp,
22
21
  type OutboundBatchMessage,
23
22
  type OutboundSingletonBatch,
24
23
  } from "./definitions.js";
24
+ import { estimateSocketSize } from "./outbox.js";
25
25
 
26
26
  export function isChunkedMessage(message: ISequencedDocumentMessage): boolean {
27
27
  return isChunkedContents(message.contents);