@fluidframework/container-runtime 2.0.0-internal.7.0.0 → 2.0.0-internal.7.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (106) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/api-extractor.json +13 -1
  3. package/api-report/container-runtime.api.md +799 -0
  4. package/dist/blobManager.js +1 -1
  5. package/dist/blobManager.js.map +1 -1
  6. package/dist/connectionTelemetry.d.ts.map +1 -1
  7. package/dist/connectionTelemetry.js +75 -42
  8. package/dist/connectionTelemetry.js.map +1 -1
  9. package/dist/container-runtime-alpha.d.ts +1554 -0
  10. package/dist/container-runtime-beta.d.ts +1554 -0
  11. package/dist/container-runtime-public.d.ts +1554 -0
  12. package/dist/container-runtime.d.ts +1611 -0
  13. package/dist/containerRuntime.d.ts +20 -14
  14. package/dist/containerRuntime.d.ts.map +1 -1
  15. package/dist/containerRuntime.js +53 -27
  16. package/dist/containerRuntime.js.map +1 -1
  17. package/dist/error.d.ts.map +1 -1
  18. package/dist/error.js.map +1 -1
  19. package/dist/gc/gcDefinitions.d.ts +2 -2
  20. package/dist/gc/gcDefinitions.d.ts.map +1 -1
  21. package/dist/gc/gcDefinitions.js.map +1 -1
  22. package/dist/index.d.ts +1 -1
  23. package/dist/index.d.ts.map +1 -1
  24. package/dist/index.js +2 -1
  25. package/dist/index.js.map +1 -1
  26. package/dist/opLifecycle/outbox.d.ts.map +1 -1
  27. package/dist/opLifecycle/outbox.js +7 -2
  28. package/dist/opLifecycle/outbox.js.map +1 -1
  29. package/dist/packageVersion.d.ts +1 -1
  30. package/dist/packageVersion.js +1 -1
  31. package/dist/packageVersion.js.map +1 -1
  32. package/dist/pendingStateManager.d.ts.map +1 -1
  33. package/dist/pendingStateManager.js +3 -1
  34. package/dist/pendingStateManager.js.map +1 -1
  35. package/dist/scheduleManager.js +6 -2
  36. package/dist/scheduleManager.js.map +1 -1
  37. package/dist/summary/summarizer.d.ts +1 -0
  38. package/dist/summary/summarizer.d.ts.map +1 -1
  39. package/dist/summary/summarizer.js +14 -7
  40. package/dist/summary/summarizer.js.map +1 -1
  41. package/dist/summary/summaryCollection.d.ts.map +1 -1
  42. package/dist/summary/summaryCollection.js +1 -0
  43. package/dist/summary/summaryCollection.js.map +1 -1
  44. package/dist/summary/summaryGenerator.js.map +1 -1
  45. package/dist/summary/summaryManager.d.ts +2 -2
  46. package/dist/summary/summaryManager.d.ts.map +1 -1
  47. package/dist/summary/summaryManager.js +3 -3
  48. package/dist/summary/summaryManager.js.map +1 -1
  49. package/dist/tsdoc-metadata.json +1 -1
  50. package/lib/blobManager.js +1 -1
  51. package/lib/blobManager.js.map +1 -1
  52. package/lib/connectionTelemetry.d.ts.map +1 -1
  53. package/lib/connectionTelemetry.js +76 -43
  54. package/lib/connectionTelemetry.js.map +1 -1
  55. package/lib/containerRuntime.d.ts +20 -14
  56. package/lib/containerRuntime.d.ts.map +1 -1
  57. package/lib/containerRuntime.js +52 -29
  58. package/lib/containerRuntime.js.map +1 -1
  59. package/lib/error.d.ts.map +1 -1
  60. package/lib/error.js.map +1 -1
  61. package/lib/gc/gcDefinitions.d.ts +2 -2
  62. package/lib/gc/gcDefinitions.d.ts.map +1 -1
  63. package/lib/gc/gcDefinitions.js.map +1 -1
  64. package/lib/index.d.ts +1 -1
  65. package/lib/index.d.ts.map +1 -1
  66. package/lib/index.js +1 -1
  67. package/lib/index.js.map +1 -1
  68. package/lib/opLifecycle/outbox.d.ts.map +1 -1
  69. package/lib/opLifecycle/outbox.js +7 -2
  70. package/lib/opLifecycle/outbox.js.map +1 -1
  71. package/lib/packageVersion.d.ts +1 -1
  72. package/lib/packageVersion.js +1 -1
  73. package/lib/packageVersion.js.map +1 -1
  74. package/lib/pendingStateManager.d.ts.map +1 -1
  75. package/lib/pendingStateManager.js +3 -1
  76. package/lib/pendingStateManager.js.map +1 -1
  77. package/lib/scheduleManager.js +6 -2
  78. package/lib/scheduleManager.js.map +1 -1
  79. package/lib/summary/summarizer.d.ts +1 -0
  80. package/lib/summary/summarizer.d.ts.map +1 -1
  81. package/lib/summary/summarizer.js +15 -8
  82. package/lib/summary/summarizer.js.map +1 -1
  83. package/lib/summary/summaryCollection.d.ts.map +1 -1
  84. package/lib/summary/summaryCollection.js +1 -0
  85. package/lib/summary/summaryCollection.js.map +1 -1
  86. package/lib/summary/summaryGenerator.js.map +1 -1
  87. package/lib/summary/summaryManager.d.ts +2 -2
  88. package/lib/summary/summaryManager.d.ts.map +1 -1
  89. package/lib/summary/summaryManager.js +3 -3
  90. package/lib/summary/summaryManager.js.map +1 -1
  91. package/package.json +25 -60
  92. package/src/blobManager.ts +1 -1
  93. package/src/connectionTelemetry.ts +97 -52
  94. package/src/containerRuntime.ts +70 -43
  95. package/src/error.ts +4 -1
  96. package/src/gc/gcDefinitions.ts +2 -2
  97. package/src/index.ts +1 -0
  98. package/src/opLifecycle/README.md +53 -28
  99. package/src/opLifecycle/outbox.ts +3 -0
  100. package/src/packageVersion.ts +1 -1
  101. package/src/pendingStateManager.ts +1 -0
  102. package/src/scheduleManager.ts +2 -0
  103. package/src/summary/summarizer.ts +20 -9
  104. package/src/summary/summaryCollection.ts +1 -0
  105. package/src/summary/summaryGenerator.ts +3 -3
  106. package/src/summary/summaryManager.ts +2 -2
@@ -23,6 +23,7 @@ import {
23
23
  ICriticalContainerError,
24
24
  AttachState,
25
25
  ILoaderOptions,
26
+ ILoader,
26
27
  LoaderHeader,
27
28
  } from "@fluidframework/container-definitions";
28
29
  import {
@@ -99,12 +100,11 @@ import {
99
100
  create404Response,
100
101
  exceptionToResponse,
101
102
  GCDataBuilder,
102
- // eslint-disable-next-line import/no-deprecated
103
- requestFluidObject,
104
103
  seqFromTree,
105
104
  calculateStats,
106
105
  TelemetryContext,
107
106
  ReadAndParseBlob,
107
+ responseToException,
108
108
  } from "@fluidframework/runtime-utils";
109
109
  import { v4 as uuid } from "uuid";
110
110
  import { ContainerFluidHandleContext } from "./containerHandleContext";
@@ -144,7 +144,6 @@ import {
144
144
  IConnectableRuntime,
145
145
  IGeneratedSummaryStats,
146
146
  ISubmitSummaryOptions,
147
- ISummarizer,
148
147
  ISummarizerInternalsProvider,
149
148
  ISummarizerRuntime,
150
149
  IRefreshSummaryAckOptions,
@@ -157,6 +156,7 @@ import {
157
156
  EnqueueSummarizeResult,
158
157
  ISummarizerEvents,
159
158
  IBaseSummarizeResult,
159
+ ISummarizer,
160
160
  } from "./summary";
161
161
  import { formExponentialFn, Throttler } from "./throttler";
162
162
  import {
@@ -355,16 +355,17 @@ export interface ISummaryRuntimeOptions {
355
355
 
356
356
  /**
357
357
  * Options for op compression.
358
- * @experimental - Not ready for use
359
358
  */
360
359
  export interface ICompressionRuntimeOptions {
361
360
  /**
362
- * The minimum size the batch's payload must exceed before the batch's contents will be compressed.
361
+ * The value the batch's content size must exceed for the batch to be compressed.
362
+ * By default the value is 600 * 1024 = 614400 bytes. If the value is set to `Infinity`, compression will be disabled.
363
363
  */
364
364
  readonly minimumBatchSizeInBytes: number;
365
365
 
366
366
  /**
367
367
  * The compression algorithm that will be used to compress the op.
368
+ * By default the value is `lz4` which is the only compression algorithm currently supported.
368
369
  */
369
370
  readonly compressionAlgorithm: CompressionAlgorithms;
370
371
  }
@@ -392,8 +393,7 @@ export interface IContainerRuntimeOptions {
392
393
  */
393
394
  readonly flushMode?: FlushMode;
394
395
  /**
395
- * Enables the runtime to compress ops. Compression is disabled when undefined.
396
- * @experimental Not ready for use.
396
+ * Enables the runtime to compress ops. See {@link ICompressionRuntimeOptions}.
397
397
  */
398
398
  readonly compressionOptions?: ICompressionRuntimeOptions;
399
399
  /**
@@ -410,12 +410,15 @@ export interface IContainerRuntimeOptions {
410
410
  /**
411
411
  * If the op payload needs to be chunked in order to work around the maximum size of the batch, this value represents
412
412
  * how large the individual chunks will be. This is only supported when compression is enabled. If after compression, the
413
- * batch size exceeds this value, it will be chunked into smaller ops of this size.
413
+ * batch content size exceeds this value, it will be chunked into smaller ops of this exact size.
414
414
  *
415
- * If unspecified, if a batch exceeds `maxBatchSizeInBytes` after compression, the container will close with an instance
416
- * of `GenericError` with the `BatchTooLarge` message.
415
+ * This value is a trade-off between having many small chunks vs fewer larger chunks and by default, the runtime is configured to use
416
+ * 200 * 1024 = 204800 bytes. This default value ensures that no compressed payload's content is able to exceed {@link IContainerRuntimeOptions.maxBatchSizeInBytes}
417
+ * regardless of the overhead of an individual op.
417
418
  *
418
- * @experimental Not ready for use.
419
+ * Any value of `chunkSizeInBytes` exceeding {@link IContainerRuntimeOptions.maxBatchSizeInBytes} will disable this feature, therefore if a compressed batch's content
420
+ * size exceeds {@link IContainerRuntimeOptions.maxBatchSizeInBytes} after compression, the container will close with an instance of `GenericError` with
421
+ * the `BatchTooLarge` message.
419
422
  */
420
423
  readonly chunkSizeInBytes?: number;
421
424
 
@@ -629,6 +632,53 @@ type MessageWithContext =
629
632
  local: boolean;
630
633
  };
631
634
 
635
+ const summarizerRequestUrl = "_summarizer";
636
+
637
+ /**
638
+ * Create and retrieve the summmarizer
639
+ */
640
+ async function createSummarizer(loader: ILoader, url: string): Promise<ISummarizer> {
641
+ const request: IRequest = {
642
+ headers: {
643
+ [LoaderHeader.cache]: false,
644
+ [LoaderHeader.clientDetails]: {
645
+ capabilities: { interactive: false },
646
+ type: summarizerClientType,
647
+ },
648
+ [DriverHeader.summarizingClient]: true,
649
+ [LoaderHeader.reconnect]: false,
650
+ },
651
+ url,
652
+ };
653
+
654
+ const resolvedContainer = await loader.resolve(request);
655
+ let fluidObject: FluidObject<ISummarizer> | undefined;
656
+
657
+ // Older containers may not have the "getEntryPoint" API
658
+ // ! This check will need to stay until LTS of loader moves past 2.0.0-internal.7.0.0
659
+ if (resolvedContainer.getEntryPoint !== undefined) {
660
+ fluidObject = await resolvedContainer.getEntryPoint();
661
+ } else {
662
+ const response = await resolvedContainer.request({ url: `/${summarizerRequestUrl}` });
663
+ if (response.status !== 200 || response.mimeType !== "fluid/object") {
664
+ throw responseToException(response, request);
665
+ }
666
+ fluidObject = response.value;
667
+ }
668
+
669
+ if (fluidObject?.ISummarizer === undefined) {
670
+ throw new UsageError("Fluid object does not implement ISummarizer");
671
+ }
672
+ return fluidObject.ISummarizer;
673
+ }
674
+
675
+ /**
676
+ * This function is not supported publicly and exists for e2e testing
677
+ */
678
+ export async function TEST_requestSummarizer(loader: ILoader, url: string): Promise<ISummarizer> {
679
+ return createSummarizer(loader, url);
680
+ }
681
+
632
682
  /**
633
683
  * Represents the runtime of the container. Contains helper functions/state of the container.
634
684
  * It will define the store level mappings.
@@ -1577,7 +1627,7 @@ export class ContainerRuntime
1577
1627
  this, // IConnectedState
1578
1628
  this.summaryCollection,
1579
1629
  this.logger,
1580
- this.formRequestSummarizerFn(loader),
1630
+ this.formCreateSummarizerFn(loader),
1581
1631
  new Throttler(
1582
1632
  60 * 1000, // 60 sec delay window
1583
1633
  30 * 1000, // 30 sec max delay
@@ -1683,7 +1733,7 @@ export class ContainerRuntime
1683
1733
  const parser = RequestParser.create(request);
1684
1734
  const id = parser.pathParts[0];
1685
1735
 
1686
- if (id === "_summarizer" && parser.pathParts.length === 1) {
1736
+ if (id === summarizerRequestUrl && parser.pathParts.length === 1) {
1687
1737
  if (this._summarizer !== undefined) {
1688
1738
  return {
1689
1739
  status: 200,
@@ -1694,6 +1744,7 @@ export class ContainerRuntime
1694
1744
  return create404Response(request);
1695
1745
  }
1696
1746
  if (this.requestHandler !== undefined) {
1747
+ // eslint-disable-next-line @typescript-eslint/return-await -- Adding an await here causes test failures
1697
1748
  return this.requestHandler(parser, this);
1698
1749
  }
1699
1750
 
@@ -1713,6 +1764,7 @@ export class ContainerRuntime
1713
1764
  const id = requestParser.pathParts[0];
1714
1765
 
1715
1766
  if (id === "_channels") {
1767
+ // eslint-disable-next-line @typescript-eslint/return-await -- Adding an await here causes test failures
1716
1768
  return this.resolveHandle(requestParser.createSubRequest(1));
1717
1769
  }
1718
1770
 
@@ -1734,6 +1786,7 @@ export class ContainerRuntime
1734
1786
  subRequest.url.startsWith("/"),
1735
1787
  0x126 /* "Expected createSubRequest url to include a leading slash" */,
1736
1788
  );
1789
+ // eslint-disable-next-line @typescript-eslint/return-await -- Adding an await here causes test failures
1737
1790
  return dataStore.request(subRequest);
1738
1791
  }
1739
1792
 
@@ -3905,37 +3958,11 @@ export class ContainerRuntime
3905
3958
  }
3906
3959
 
3907
3960
  /**
3908
- * * Forms a function that will request a Summarizer.
3909
- * @param loaderRouter - the loader acting as an IFluidRouter
3910
- * */
3911
- // eslint-disable-next-line import/no-deprecated
3912
- private formRequestSummarizerFn(loaderRouter: IFluidRouter) {
3961
+ * Forms a function that will create and retrieve a Summarizer.
3962
+ */
3963
+ private formCreateSummarizerFn(loader: ILoader) {
3913
3964
  return async () => {
3914
- const request: IRequest = {
3915
- headers: {
3916
- [LoaderHeader.cache]: false,
3917
- [LoaderHeader.clientDetails]: {
3918
- capabilities: { interactive: false },
3919
- type: summarizerClientType,
3920
- },
3921
- [DriverHeader.summarizingClient]: true,
3922
- [LoaderHeader.reconnect]: false,
3923
- },
3924
- url: "/_summarizer",
3925
- };
3926
-
3927
- // eslint-disable-next-line import/no-deprecated
3928
- const fluidObject = await requestFluidObject<FluidObject<ISummarizer>>(
3929
- loaderRouter,
3930
- request,
3931
- );
3932
- const summarizer = fluidObject.ISummarizer;
3933
-
3934
- if (!summarizer) {
3935
- throw new UsageError("Fluid object does not implement ISummarizer");
3936
- }
3937
-
3938
- return summarizer;
3965
+ return createSummarizer(loader, `/${summarizerRequestUrl}`);
3939
3966
  };
3940
3967
  }
3941
3968
 
package/src/error.ts CHANGED
@@ -12,7 +12,10 @@ import { IFluidErrorBase, LoggingError } from "@fluidframework/telemetry-utils";
12
12
  export class ClientSessionExpiredError extends LoggingError implements IFluidErrorBase {
13
13
  readonly errorType = ContainerErrorTypes.clientSessionExpiredError;
14
14
 
15
- constructor(message: string, readonly expiryMs: number) {
15
+ constructor(
16
+ message: string,
17
+ readonly expiryMs: number,
18
+ ) {
16
19
  super(message, { timeoutMs: expiryMs });
17
20
  }
18
21
  }
@@ -165,7 +165,7 @@ export const GCNodeType = {
165
165
  // Nodes that are neither of the above. For example, root node.
166
166
  Other: "Other",
167
167
  };
168
- export type GCNodeType = typeof GCNodeType[keyof typeof GCNodeType];
168
+ export type GCNodeType = (typeof GCNodeType)[keyof typeof GCNodeType];
169
169
 
170
170
  /**
171
171
  * Defines the APIs for the runtime object to be passed to the garbage collector.
@@ -360,7 +360,7 @@ export const UnreferencedState = {
360
360
  /** The node is ready to be deleted by the sweep phase. */
361
361
  SweepReady: "SweepReady",
362
362
  } as const;
363
- export type UnreferencedState = typeof UnreferencedState[keyof typeof UnreferencedState];
363
+ export type UnreferencedState = (typeof UnreferencedState)[keyof typeof UnreferencedState];
364
364
 
365
365
  /**
366
366
  * Represents the result of a GC run.
package/src/index.ts CHANGED
@@ -23,6 +23,7 @@ export {
23
23
  DefaultSummaryConfiguration,
24
24
  ICompressionRuntimeOptions,
25
25
  CompressionAlgorithms,
26
+ TEST_requestSummarizer,
26
27
  } from "./containerRuntime";
27
28
  export {
28
29
  ContainerMessageType,
@@ -1,5 +1,19 @@
1
1
  # Configs and feature gates for solving the 1MB limit.
2
2
 
3
+ ## Table of contents
4
+
5
+ - [Introduction](#introduction)
6
+ - [How batching works](#how-batching-works)
7
+ - [Compression](#compression)
8
+ - [Grouped batching](#grouped-batching)
9
+ - [Risks](#risks)
10
+ - [Chunking for compression](#chunking-for-compression)
11
+ - [Disabling in case of emergency](#disabling-in-case-of-emergency)
12
+ - [Example configs](#example-configs)
13
+ - [Note about performance and latency](#note-about-performance-and-latency)
14
+ - [How it works](#how-it-works)
15
+ - [How grouped batching works](#how-grouped-batching-works)
16
+
3
17
  ## Introduction
4
18
 
5
19
  There is a current limitation regarding the size of the payload a Fluid client can send and receive. [The limit is 1MB per payload](https://github.com/microsoft/FluidFramework/issues/9023) and it is currently enforced explicitly with the `BatchTooLarge` error which closes the container.
@@ -8,17 +22,35 @@ There are two features which can be used to work around this size limit, batch c
8
22
 
9
23
  By default, the runtime is configured with a max batch size of `716800` bytes, which is lower than the 1MB limit. The reason for the lower value is to account for possible overhead from the op envelope and metadata.
10
24
 
11
- ## Table of contents
25
+ ### How batching works
12
26
 
13
- - [Introduction](#introduction)
14
- - [Compression](#compression)
15
- - [Grouped batching](#grouped-batching)
16
- - [Risks](#risks)
17
- - [Chunking for compression](#chunking-for-compression)
18
- - [Disabling in case of emergency](#disabling-in-case-of-emergency)
19
- - [Example configs](#example-configs)
20
- - [How it works](#how-it-works)
21
- - [How grouped batching works](#how-grouped-batching-works)
27
+ Batching in the context of Fluid ops is a way in which the framework accumulates and applies ops. A batch is a group of ops accumulated within a single JS turn, which will be broadcasted in the same order to all the other connected clients and applied synchronously. Additional logic and validation ensure that batches are never interleaved, nested or interrupted and they are processed in isolation without interleaving of ops from other clients.
28
+
29
+ The way batches are formed is governed by the `FlushMode` setting of the `ContainerRuntimeOptions` and it is immutable for the entire lifetime of the runtime and subsequently the container.
30
+
31
+ ```
32
+ export enum FlushMode {
33
+ /**
34
+ * In Immediate flush mode the runtime will immediately send all operations to the driver layer.
35
+ */
36
+ Immediate,
37
+
38
+ /**
39
+ * When in TurnBased flush mode the runtime will buffer operations in the current turn and send them as a single
40
+ * batch at the end of the turn. The flush call on the runtime can be used to force send the current batch.
41
+ */
42
+ TurnBased,
43
+ }
44
+ ```
45
+
46
+ What this means is that `FlushMode.Immediate` will send each op in its own payload to the server, while `FlushMode.TurnBased` will accumulate all ops in a single JS turn and send them together in the same payload. Technically, `FlushMode.Immediate` can be simulated with `FlushMode.TurnBased` by interrupting the JS turn after producing only one op (for example by pausing the execution to wait on a promise). Therefore, for all intents and purposes, `FlushMode.Immediate` enables all batches to have only one op.
47
+
48
+ **By default, Fluid uses `FlushMode.TurnBased`** as:
49
+
50
+ - it is more efficient from an I/O perspective (batching ops overall decrease the number of payloads sent to the server)
51
+ - reduces concurrency related bugs, as it ensures that all ops generated within the same JS turn are also applied by all other clients within a single JS turn. Clients using the same pattern can safely assume ops will be applied exactly as they are observed locally. The alternative would be for ops to be both produced and applied with interruptions (which may involve processing input or rendering), invalidating the state based off which the changes were produced.
52
+
53
+ As `FlushMode.TurnBased` accumulates ops, it is the most vulnerable to run into the 1MB socket limit.
22
54
 
23
55
  ## Compression
24
56
 
@@ -29,6 +61,8 @@ By default, the runtime is configured with a max batch size of `716800` bytes, w
29
61
  - `minimumBatchSizeInBytes` – the minimum size of the batch for which compression should kick in. If the payload is too small, compression may not yield too many benefits. To target the original 1MB issue, a good value here would be to match the default maxBatchSizeInBytes (972800), however, experimentally, a good lower value could be at around 614400 bytes. Setting this value to `Number.POSITIVE_INFINITY` will disable compression.
30
62
  - `compressionAlgorithm` – currently, only `lz4` is supported.
31
63
 
64
+ Compression is relevant for both `FlushMode.TurnBased` and `FlushMode.Immediate` as it only targets the contents of the ops and not the number of ops in a batch. Compression is opaque to the server and implementations of the Fluid protocol do not need to alter their behavior to support this client feature.
65
+
32
66
  ## Grouped batching
33
67
 
34
68
  **Note: This feature is currently considered experimental and is not ready for production usage.**
@@ -71,6 +105,8 @@ If all prerequisites in the previous section are met, enabling the feature can b
71
105
 
72
106
  In case of emergency grouped batching can be disabled at runtime, using feature gates. If `"Fluid.ContainerRuntime.DisableGroupedBatching"` is set to `true`, it will disable grouped batching if enabled from `IContainerRuntimeOptions` in the code.
73
107
 
108
+ Grouped batching is only relevant for `FlushMode.TurnBased` as it only targets the number of ops in a batch. Grouped batching is opaque to the server and implementations of the Fluid protocol do not need to alter their behavior to support this client feature.
109
+
74
110
  ## Chunking for compression
75
111
 
76
112
  **Op chunking for compression targets payloads which exceed the max batch size after compression.** So, only payloads which are already compressed. By default, the feature is enabled.
@@ -79,6 +115,8 @@ The `IContainerRuntimeOptions.chunkSizeInBytes` property is the only configurati
79
115
 
80
116
  This config would govern chunking compressed batches only. We will not be enabling chunking across all types of ops/batches but **only when compression is enabled and when the batch is compressed**, and its payload size is more than `IContainerRuntimeOptions.chunkSizeInBytes`.
81
117
 
118
+ Chunking is relevant for both `FlushMode.TurnBased` and `FlushMode.Immediate` as it only targets the contents of the ops and not the number of ops in a batch. Chunking is opaque to the server and implementations of the Fluid protocol do not need to alter their behavior to support this client feature.
119
+
82
120
  ## Disabling in case of emergency
83
121
 
84
122
  If the features are enabled using the configs, they can be disabled at runtime via feature gates as following:
@@ -102,32 +140,19 @@ By default, the runtime is configured with the following values related to compr
102
140
      }
103
141
  ```
104
142
 
105
- To use compression but disable chunking:
143
+ To enable grouped batching, use the following property:
106
144
 
107
145
  ```
108
146
  const runtimeOptions: IContainerRuntimeOptions = {
109
- chunkSizeInBytes: Number.POSITIVE_INFINITY,
147
+ enableGroupedBatching: true,
110
148
      }
111
149
  ```
112
150
 
113
- To disable compression (will also disable chunking, as chunking works only for compressed batches):
114
-
115
- ```
116
- const runtimeOptions: IContainerRuntimeOptions = {
117
- compressionOptions: {
118
- minimumBatchSizeInBytes: Number.POSITIVE_INFINITY,
119
- compressionAlgorithm: CompressionAlgorithms.lz4,
120
- },
121
-     }
122
- ```
151
+ ## Note about performance and latency
123
152
 
124
- To enable grouped batching:
153
+ In terms of performance and impact on latency, the results greatly depend on payload size, payload structure, network speed and CPU speed. Therefore, customers must perform the required measurements and adjust the settings according to their scenarios.
125
154
 
126
- ```
127
- const runtimeOptions: IContainerRuntimeOptions = {
128
- enableGroupedBatching: true,
129
-     }
130
- ```
155
+ In general, compression offers a trade-off between higher compute costs, lower bandwidth consumption and lower storage requirements, while chunking slightly increases latency due to the overhead of splitting an op, sending the chunks and reconstructing them on each client. Grouped batching heavily decreases the number of ops observed by the server and slightly decreases the bandwidth requirements as it merges all the ops in a batch into a single op and also eliminates the op envelope overhead.
131
156
 
132
157
  ## How it works
133
158
 
@@ -65,10 +65,13 @@ export interface IOutboxParameters {
65
65
  export function getLongStack<T>(action: () => T, length: number = 50): T {
66
66
  const errorObj = Error as any;
67
67
  if (
68
+ /* eslint-disable @typescript-eslint/prefer-nullish-coalescing */
69
+ // ?? is not logically equivalent when the first clause returns false.
68
70
  (
69
71
  Object.getOwnPropertyDescriptor(errorObj, "stackTraceLimit") ||
70
72
  Object.getOwnPropertyDescriptor(Object.getPrototypeOf(errorObj), "stackTraceLimit")
71
73
  )?.writable !== true
74
+ /* eslint-enable @typescript-eslint/prefer-nullish-coalescing */
72
75
  ) {
73
76
  return action();
74
77
  }
@@ -6,4 +6,4 @@
6
6
  */
7
7
 
8
8
  export const pkgName = "@fluidframework/container-runtime";
9
- export const pkgVersion = "2.0.0-internal.7.0.0";
9
+ export const pkgVersion = "2.0.0-internal.7.1.0";
@@ -318,6 +318,7 @@ export class PendingStateManager implements IDisposable {
318
318
  {
319
319
  runtimeVersion: pkgVersion,
320
320
  batchClientId:
321
+ // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
321
322
  this.pendingBatchBeginMessage.clientId === null
322
323
  ? "null"
323
324
  : this.pendingBatchBeginMessage.clientId,
@@ -271,6 +271,7 @@ class ScheduleManagerCore {
271
271
  {
272
272
  runtimeVersion: pkgVersion,
273
273
  batchClientId:
274
+ // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
274
275
  this.currentBatchClientId === null ? "null" : this.currentBatchClientId,
275
276
  pauseSequenceNumber: this.pauseSequenceNumber,
276
277
  localBatch: this.currentBatchClientId === this.getClientId(),
@@ -306,6 +307,7 @@ class ScheduleManagerCore {
306
307
  throw new DataCorruptionError("OpBatchIncomplete", {
307
308
  runtimeVersion: pkgVersion,
308
309
  batchClientId:
310
+ // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
309
311
  this.currentBatchClientId === null ? "null" : this.currentBatchClientId,
310
312
  pauseSequenceNumber: this.pauseSequenceNumber,
311
313
  localBatch: this.currentBatchClientId === this.getClientId(),
@@ -15,9 +15,8 @@ import {
15
15
  } from "@fluidframework/telemetry-utils";
16
16
  import { ILoader, LoaderHeader } from "@fluidframework/container-definitions";
17
17
  import { DriverHeader } from "@fluidframework/driver-definitions";
18
- // eslint-disable-next-line import/no-deprecated
19
- import { requestFluidObject } from "@fluidframework/runtime-utils";
20
18
  import { FluidObject, IFluidHandleContext, IRequest } from "@fluidframework/core-interfaces";
19
+ import { responseToException } from "@fluidframework/runtime-utils";
21
20
  import { ISummaryConfiguration } from "../containerRuntime";
22
21
  import { ICancellableSummarizerController } from "./runWhileConnectedCoordinator";
23
22
  import { summarizerClientType } from "./summarizerClientElection";
@@ -50,7 +49,10 @@ export class SummarizingWarning
50
49
  readonly errorType = summarizingError;
51
50
  readonly canRetry = true;
52
51
 
53
- constructor(errorMessage: string, readonly logged: boolean = false) {
52
+ constructor(
53
+ errorMessage: string,
54
+ readonly logged: boolean = false,
55
+ ) {
54
56
  super(errorMessage);
55
57
  }
56
58
 
@@ -108,6 +110,7 @@ export class Summarizer extends TypedEventEmitter<ISummarizerEvents> implements
108
110
  * interface will expect an absolute URL and will not handle "/".
109
111
  * @param loader - the loader that resolves the request
110
112
  * @param url - the URL used to resolve the container
113
+ * @deprecated Creating a summarizer is not a publicly supported API. Please remove all usage of this static method.
111
114
  */
112
115
  public static async create(loader: ILoader, url: string): Promise<ISummarizer> {
113
116
  const request: IRequest = {
@@ -124,12 +127,20 @@ export class Summarizer extends TypedEventEmitter<ISummarizerEvents> implements
124
127
  };
125
128
 
126
129
  const resolvedContainer = await loader.resolve(request);
127
- const fluidObject: FluidObject<ISummarizer> | undefined = resolvedContainer.getEntryPoint
128
- ? await resolvedContainer.getEntryPoint?.()
129
- : // eslint-disable-next-line import/no-deprecated
130
- await requestFluidObject<FluidObject<ISummarizer>>(resolvedContainer, {
131
- url: "_summarizer",
132
- });
130
+ let fluidObject: FluidObject<ISummarizer> | undefined;
131
+
132
+ // Older containers may not have the "getEntryPoint" API
133
+ // ! This check will need to stay until LTS of loader moves past 2.0.0-internal.7.0.0
134
+ if (resolvedContainer.getEntryPoint !== undefined) {
135
+ fluidObject = await resolvedContainer.getEntryPoint();
136
+ } else {
137
+ const response = await resolvedContainer.request({ url: "_summarizer" });
138
+ if (response.status !== 200 || response.mimeType !== "fluid/object") {
139
+ throw responseToException(response, request);
140
+ }
141
+ fluidObject = response.value;
142
+ }
143
+
133
144
  if (fluidObject?.ISummarizer === undefined) {
134
145
  throw new UsageError("Fluid object does not implement ISummarizer");
135
146
  }
@@ -405,6 +405,7 @@ export class SummaryCollection extends TypedEventEmitter<ISummaryCollectionOpEve
405
405
  private handleSummaryAck(op: ISummaryAckMessage) {
406
406
  const seq = op.contents.summaryProposal.summarySequenceNumber;
407
407
  const summary = this.pendingSummaries.get(seq);
408
+ // eslint-disable-next-line @typescript-eslint/prefer-optional-chain -- optional chain is not logically equivalent
408
409
  if (!summary || summary.summaryOp === undefined) {
409
410
  // Summary ack without an op should be rare. We could fetch the
410
411
  // reference sequence number from the snapshot, but instead we
@@ -47,12 +47,12 @@ export async function raceTimer<T>(
47
47
  cancellationToken?: ISummaryCancellationToken,
48
48
  ): Promise<raceTimerResult<T>> {
49
49
  const promises: Promise<raceTimerResult<T>>[] = [
50
- promise.then((value) => ({ result: "done", value } as const)),
51
- timer.then(({ timerResult: result }) => ({ result } as const)),
50
+ promise.then((value) => ({ result: "done", value }) as const),
51
+ timer.then(({ timerResult: result }) => ({ result }) as const),
52
52
  ];
53
53
  if (cancellationToken !== undefined) {
54
54
  promises.push(
55
- cancellationToken.waitCancelled.then(() => ({ result: "cancelled" } as const)),
55
+ cancellationToken.waitCancelled.then(() => ({ result: "cancelled" }) as const),
56
56
  );
57
57
  }
58
58
  return Promise.race(promises);
@@ -114,7 +114,7 @@ export class SummaryManager extends TypedEventEmitter<ISummarizerEvents> impleme
114
114
  parentLogger: ITelemetryBaseLogger,
115
115
  /** Creates summarizer by asking interactive container to spawn summarizing container and
116
116
  * get back its Summarizer instance. */
117
- private readonly requestSummarizerFn: () => Promise<ISummarizer>,
117
+ private readonly createSummarizerFn: () => Promise<ISummarizer>,
118
118
  private readonly startThrottler: IThrottler,
119
119
  {
120
120
  initialDelayMs = defaultInitialDelayMs,
@@ -264,7 +264,7 @@ export class SummaryManager extends TypedEventEmitter<ISummarizerEvents> impleme
264
264
  );
265
265
  this.state = SummaryManagerState.Running;
266
266
 
267
- const summarizer = await this.requestSummarizerFn();
267
+ const summarizer = await this.createSummarizerFn();
268
268
  this.summarizer = summarizer;
269
269
  this.summarizer.on("summarize", this.handleSummarizeEvent);
270
270