@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
@@ -4,13 +4,16 @@
4
4
  */
5
5
 
6
6
  import { IBatchMessage } from "@fluidframework/container-definitions/internal";
7
- import { ITelemetryBaseLogger } from "@fluidframework/core-interfaces";
7
+ import {
8
+ ITelemetryBaseLogger,
9
+ type ITelemetryBaseProperties,
10
+ } from "@fluidframework/core-interfaces";
8
11
  import { assert, Lazy } from "@fluidframework/core-utils/internal";
9
12
  import {
10
13
  DataProcessingError,
11
- GenericError,
12
14
  UsageError,
13
15
  createChildLogger,
16
+ type IFluidErrorBase,
14
17
  type ITelemetryLoggerExt,
15
18
  } from "@fluidframework/telemetry-utils/internal";
16
19
 
@@ -20,7 +23,6 @@ import { PendingMessageResubmitData, PendingStateManager } from "../pendingState
20
23
  import {
21
24
  BatchManager,
22
25
  BatchSequenceNumbers,
23
- estimateSocketSize,
24
26
  sequenceNumbersMatch,
25
27
  type BatchId,
26
28
  } from "./batchManager.js";
@@ -106,6 +108,55 @@ export function getLongStack<T>(action: () => T, length: number = 50): T {
106
108
  }
107
109
  }
108
110
 
111
+ /**
112
+ * Convert from local batch to outbound batch, including computing contentSizeInBytes.
113
+ */
114
+ export function localBatchToOutboundBatch(localBatch: LocalBatch): OutboundBatch {
115
+ // Shallow copy each message as we switch types
116
+ const outboundMessages = localBatch.messages.map<OutboundBatchMessage>(
117
+ ({ serializedOp, ...message }) => ({
118
+ contents: serializedOp,
119
+ ...message,
120
+ }),
121
+ );
122
+ const contentSizeInBytes = outboundMessages.reduce(
123
+ (acc, message) => acc + (message.contents?.length ?? 0),
124
+ 0,
125
+ );
126
+
127
+ // Shallow copy the local batch, updating the messages to be outbound messages and adding contentSizeInBytes
128
+ const outboundBatch: OutboundBatch = {
129
+ ...localBatch,
130
+ messages: outboundMessages,
131
+ contentSizeInBytes,
132
+ };
133
+
134
+ return outboundBatch;
135
+ }
136
+
137
+ /**
138
+ * Estimated size of the stringification overhead for an op accumulated
139
+ * from runtime to loader to the service.
140
+ */
141
+ const opOverhead = 200;
142
+
143
+ /**
144
+ * Estimates the real size in bytes on the socket for a given batch. It assumes that
145
+ * the envelope size (and the size of an empty op) is 200 bytes, taking into account
146
+ * extra overhead from stringification.
147
+ *
148
+ * @remarks
149
+ * Also content will be stringified, and that adds a lot of overhead due to a lot of escape characters.
150
+ * Not taking it into account, as compression work should help there - compressed payload will be
151
+ * initially stored as base64, and that requires only 2 extra escape characters.
152
+ *
153
+ * @param batch - the batch to inspect
154
+ * @returns An estimate of the payload size in bytes which will be produced when the batch is sent over the wire
155
+ */
156
+ export const estimateSocketSize = (batch: OutboundBatch): number => {
157
+ return batch.contentSizeInBytes + opOverhead * batch.messages.length;
158
+ };
159
+
109
160
  /**
110
161
  * The Outbox collects messages submitted by the ContainerRuntime into a batch,
111
162
  * and then flushes the batch when requested.
@@ -133,18 +184,9 @@ export class Outbox {
133
184
  constructor(private readonly params: IOutboxParameters) {
134
185
  this.logger = createChildLogger({ logger: params.logger, namespace: "Outbox" });
135
186
 
136
- const isCompressionEnabled =
137
- this.params.config.compressionOptions.minimumBatchSizeInBytes !==
138
- Number.POSITIVE_INFINITY;
139
- // We need to allow infinite size batches if we enable compression
140
- const hardLimit = isCompressionEnabled
141
- ? Number.POSITIVE_INFINITY
142
- : this.params.config.maxBatchSizeInBytes;
143
-
144
- this.mainBatch = new BatchManager({ hardLimit, canRebase: true });
145
- this.blobAttachBatch = new BatchManager({ hardLimit, canRebase: true });
187
+ this.mainBatch = new BatchManager({ canRebase: true });
188
+ this.blobAttachBatch = new BatchManager({ canRebase: true });
146
189
  this.idAllocationBatch = new BatchManager({
147
- hardLimit,
148
190
  canRebase: false,
149
191
  ignoreBatchId: true,
150
192
  });
@@ -274,20 +316,11 @@ export class Outbox {
274
316
  batchManager: BatchManager,
275
317
  message: LocalBatchMessage,
276
318
  ): void {
277
- if (
278
- !batchManager.push(
279
- message,
280
- this.isContextReentrant(),
281
- this.params.getCurrentSequenceNumbers().clientSequenceNumber,
282
- )
283
- ) {
284
- throw new GenericError("BatchTooLarge", /* error */ undefined, {
285
- opSize: message.serializedOp?.length ?? 0,
286
- batchSize: batchManager.contentSizeInBytes,
287
- count: batchManager.length,
288
- limit: batchManager.options.hardLimit,
289
- });
290
- }
319
+ batchManager.push(
320
+ message,
321
+ this.isContextReentrant(),
322
+ this.params.getCurrentSequenceNumbers().clientSequenceNumber,
323
+ );
291
324
  }
292
325
 
293
326
  /**
@@ -465,15 +498,7 @@ export class Outbox {
465
498
  */
466
499
  private virtualizeBatch(localBatch: LocalBatch, groupingEnabled: boolean): OutboundBatch {
467
500
  // Shallow copy the local batch, updating the messages to be outbound messages
468
- const originalBatch: OutboundBatch = {
469
- ...localBatch,
470
- messages: localBatch.messages.map<OutboundBatchMessage>(
471
- ({ serializedOp, ...message }) => ({
472
- contents: serializedOp,
473
- ...message,
474
- }),
475
- ),
476
- };
501
+ const originalBatch = localBatchToOutboundBatch(localBatch);
477
502
 
478
503
  const originalOrGroupedBatch = groupingEnabled
479
504
  ? this.params.groupingManager.groupBatch(originalBatch)
@@ -489,7 +514,6 @@ export class Outbox {
489
514
  const singletonBatch = originalOrGroupedBatch as OutboundSingletonBatch;
490
515
 
491
516
  if (
492
- this.params.config.compressionOptions === undefined ||
493
517
  this.params.config.compressionOptions.minimumBatchSizeInBytes >
494
518
  singletonBatch.contentSizeInBytes ||
495
519
  this.params.submitBatchFn === undefined
@@ -506,21 +530,11 @@ export class Outbox {
506
530
  : this.params.splitter.splitSingletonBatchMessage(compressedBatch);
507
531
  }
508
532
 
533
+ // We want to distinguish this "BatchTooLarge" case from the generic "BatchTooLarge" case in sendBatch
509
534
  if (compressedBatch.contentSizeInBytes >= this.params.config.maxBatchSizeInBytes) {
510
- throw DataProcessingError.create(
511
- "BatchTooLarge",
512
- "compressionInsufficient",
513
- /* sequencedMessage */ undefined,
514
- {
515
- batchSize: singletonBatch.contentSizeInBytes,
516
- compressedBatchSize: compressedBatch.contentSizeInBytes,
517
- count: compressedBatch.messages.length,
518
- limit: this.params.config.maxBatchSizeInBytes,
519
- chunkingEnabled: this.params.splitter.isBatchChunkingEnabled,
520
- compressionOptions: JSON.stringify(this.params.config.compressionOptions),
521
- socketSize: estimateSocketSize(singletonBatch),
522
- },
523
- );
535
+ throw this.makeBatchTooLargeError(compressedBatch, "CompressionInsufficient", {
536
+ uncompressedSizeInBytes: singletonBatch.contentSizeInBytes,
537
+ });
524
538
  }
525
539
 
526
540
  return compressedBatch;
@@ -540,12 +554,7 @@ export class Outbox {
540
554
 
541
555
  const socketSize = estimateSocketSize(batch);
542
556
  if (socketSize >= this.params.config.maxBatchSizeInBytes) {
543
- this.logger.sendPerformanceEvent({
544
- eventName: "LargeBatch",
545
- length: batch.messages.length,
546
- sizeInBytes: batch.contentSizeInBytes,
547
- socketSize,
548
- });
557
+ throw this.makeBatchTooLargeError(batch, "CannotSend");
549
558
  }
550
559
 
551
560
  let clientSequenceNumber: number;
@@ -577,6 +586,31 @@ export class Outbox {
577
586
  return clientSequenceNumber;
578
587
  }
579
588
 
589
+ private makeBatchTooLargeError(
590
+ batch: OutboundBatch,
591
+ codepath: string,
592
+ moreDetails?: ITelemetryBaseProperties,
593
+ ): IFluidErrorBase {
594
+ return DataProcessingError.create(
595
+ "BatchTooLarge",
596
+ codepath,
597
+ /* sequencedMessage */ undefined,
598
+ {
599
+ errorDetails: {
600
+ opCount: batch.messages.length,
601
+ contentSizeInBytes: batch.contentSizeInBytes,
602
+ socketSize: estimateSocketSize(batch),
603
+ maxBatchSizeInBytes: this.params.config.maxBatchSizeInBytes,
604
+ groupedBatchingEnabled: this.params.groupingManager.groupedBatchingEnabled(),
605
+ compressionOptions: JSON.stringify(this.params.config.compressionOptions),
606
+ chunkingEnabled: this.params.splitter.isBatchChunkingEnabled,
607
+ chunkSizeInBytes: this.params.splitter.chunkSizeInBytes,
608
+ ...moreDetails,
609
+ },
610
+ },
611
+ );
612
+ }
613
+
580
614
  /**
581
615
  * Gets a checkpoint object per batch that facilitates iterating over the batch messages when rolling back.
582
616
  */
@@ -6,4 +6,4 @@
6
6
  */
7
7
 
8
8
  export const pkgName = "@fluidframework/container-runtime";
9
- export const pkgVersion = "2.32.0";
9
+ export const pkgVersion = "2.33.0-333010";