@fluidframework/container-runtime 2.0.0-internal.6.4.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.
- package/CHANGELOG.md +111 -0
- package/api-extractor.json +13 -1
- package/api-report/container-runtime.api.md +799 -0
- package/dist/blobManager.d.ts +1 -1
- package/dist/blobManager.d.ts.map +1 -1
- package/dist/blobManager.js +7 -7
- package/dist/blobManager.js.map +1 -1
- package/dist/connectionTelemetry.d.ts.map +1 -1
- package/dist/connectionTelemetry.js +75 -42
- package/dist/connectionTelemetry.js.map +1 -1
- package/dist/container-runtime-alpha.d.ts +1554 -0
- package/dist/container-runtime-beta.d.ts +1554 -0
- package/dist/container-runtime-public.d.ts +1554 -0
- package/dist/container-runtime.d.ts +1611 -0
- package/dist/containerHandleContext.js +3 -3
- package/dist/containerHandleContext.js.map +1 -1
- package/dist/containerRuntime.d.ts +28 -24
- package/dist/containerRuntime.d.ts.map +1 -1
- package/dist/containerRuntime.js +277 -256
- package/dist/containerRuntime.js.map +1 -1
- package/dist/dataStore.js +9 -9
- package/dist/dataStore.js.map +1 -1
- package/dist/dataStoreContext.d.ts +1 -3
- package/dist/dataStoreContext.d.ts.map +1 -1
- package/dist/dataStoreContext.js +54 -58
- package/dist/dataStoreContext.js.map +1 -1
- package/dist/dataStoreRegistry.js +3 -3
- package/dist/dataStoreRegistry.js.map +1 -1
- package/dist/deltaManagerProxyBase.js +4 -4
- package/dist/deltaManagerProxyBase.js.map +1 -1
- package/dist/deltaManagerSummarizerProxy.js +6 -6
- package/dist/deltaManagerSummarizerProxy.js.map +1 -1
- package/dist/deltaScheduler.js.map +1 -1
- package/dist/error.d.ts.map +1 -1
- package/dist/error.js.map +1 -1
- package/dist/gc/garbageCollection.js +13 -13
- package/dist/gc/garbageCollection.js.map +1 -1
- package/dist/gc/gcDefinitions.d.ts +4 -15
- package/dist/gc/gcDefinitions.d.ts.map +1 -1
- package/dist/gc/gcDefinitions.js.map +1 -1
- package/dist/gc/gcTelemetry.d.ts +1 -1
- package/dist/gc/gcTelemetry.d.ts.map +1 -1
- package/dist/gc/gcUnreferencedStateTracker.js +3 -3
- package/dist/gc/gcUnreferencedStateTracker.js.map +1 -1
- package/dist/id-compressor/appendOnlySortedMap.js.map +1 -1
- package/dist/id-compressor/idCompressor.js.map +1 -1
- package/dist/id-compressor/identifiers.d.ts +3 -3
- package/dist/id-compressor/identifiers.d.ts.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/messageTypes.d.ts +17 -17
- package/dist/messageTypes.d.ts.map +1 -1
- package/dist/messageTypes.js +1 -1
- package/dist/messageTypes.js.map +1 -1
- package/dist/opLifecycle/batchManager.js +6 -6
- package/dist/opLifecycle/batchManager.js.map +1 -1
- package/dist/opLifecycle/definitions.d.ts +2 -2
- package/dist/opLifecycle/definitions.d.ts.map +1 -1
- package/dist/opLifecycle/outbox.d.ts.map +1 -1
- package/dist/opLifecycle/outbox.js +7 -2
- package/dist/opLifecycle/outbox.js.map +1 -1
- package/dist/packageVersion.d.ts +1 -1
- package/dist/packageVersion.js +1 -1
- package/dist/packageVersion.js.map +1 -1
- package/dist/pendingStateManager.d.ts +3 -19
- package/dist/pendingStateManager.d.ts.map +1 -1
- package/dist/pendingStateManager.js +23 -40
- package/dist/pendingStateManager.js.map +1 -1
- package/dist/scheduleManager.js +6 -2
- package/dist/scheduleManager.js.map +1 -1
- package/dist/summary/orderedClientElection.d.ts +3 -3
- package/dist/summary/orderedClientElection.d.ts.map +1 -1
- package/dist/summary/orderedClientElection.js +54 -54
- package/dist/summary/orderedClientElection.js.map +1 -1
- package/dist/summary/runWhileConnectedCoordinator.js +6 -6
- package/dist/summary/runWhileConnectedCoordinator.js.map +1 -1
- package/dist/summary/runningSummarizer.js +37 -37
- package/dist/summary/runningSummarizer.js.map +1 -1
- package/dist/summary/summarizer.d.ts +1 -0
- package/dist/summary/summarizer.d.ts.map +1 -1
- package/dist/summary/summarizer.js +17 -8
- package/dist/summary/summarizer.js.map +1 -1
- package/dist/summary/summarizerClientElection.js +6 -6
- package/dist/summary/summarizerClientElection.js.map +1 -1
- package/dist/summary/summarizerHeuristics.js +9 -9
- package/dist/summary/summarizerHeuristics.js.map +1 -1
- package/dist/summary/summarizerNode/summarizerNode.js +7 -7
- package/dist/summary/summarizerNode/summarizerNode.js.map +1 -1
- package/dist/summary/summarizerNode/summarizerNodeUtils.d.ts +1 -1
- package/dist/summary/summarizerNode/summarizerNodeUtils.d.ts.map +1 -1
- package/dist/summary/summarizerNode/summarizerNodeUtils.js +3 -3
- package/dist/summary/summarizerNode/summarizerNodeUtils.js.map +1 -1
- package/dist/summary/summarizerNode/summarizerNodeWithGc.d.ts +2 -2
- package/dist/summary/summarizerNode/summarizerNodeWithGc.d.ts.map +1 -1
- package/dist/summary/summarizerTypes.d.ts +12 -12
- package/dist/summary/summarizerTypes.d.ts.map +1 -1
- package/dist/summary/summaryCollection.d.ts +2 -2
- package/dist/summary/summaryCollection.d.ts.map +1 -1
- package/dist/summary/summaryCollection.js +22 -21
- package/dist/summary/summaryCollection.js.map +1 -1
- package/dist/summary/summaryFormat.d.ts +5 -5
- package/dist/summary/summaryFormat.d.ts.map +1 -1
- package/dist/summary/summaryGenerator.d.ts +3 -3
- package/dist/summary/summaryGenerator.d.ts.map +1 -1
- package/dist/summary/summaryGenerator.js.map +1 -1
- package/dist/summary/summaryManager.d.ts +2 -2
- package/dist/summary/summaryManager.d.ts.map +1 -1
- package/dist/summary/summaryManager.js +10 -10
- package/dist/summary/summaryManager.js.map +1 -1
- package/dist/throttler.js +16 -16
- package/dist/throttler.js.map +1 -1
- package/dist/tsdoc-metadata.json +1 -1
- package/lib/blobManager.d.ts +1 -1
- package/lib/blobManager.d.ts.map +1 -1
- package/lib/blobManager.js +7 -7
- package/lib/blobManager.js.map +1 -1
- package/lib/connectionTelemetry.d.ts.map +1 -1
- package/lib/connectionTelemetry.js +76 -43
- package/lib/connectionTelemetry.js.map +1 -1
- package/lib/containerHandleContext.js +3 -3
- package/lib/containerHandleContext.js.map +1 -1
- package/lib/containerRuntime.d.ts +28 -24
- package/lib/containerRuntime.d.ts.map +1 -1
- package/lib/containerRuntime.js +268 -252
- package/lib/containerRuntime.js.map +1 -1
- package/lib/dataStore.js +9 -9
- package/lib/dataStore.js.map +1 -1
- package/lib/dataStoreContext.d.ts +1 -3
- package/lib/dataStoreContext.d.ts.map +1 -1
- package/lib/dataStoreContext.js +54 -58
- package/lib/dataStoreContext.js.map +1 -1
- package/lib/dataStoreRegistry.js +3 -3
- package/lib/dataStoreRegistry.js.map +1 -1
- package/lib/deltaManagerProxyBase.js +4 -4
- package/lib/deltaManagerProxyBase.js.map +1 -1
- package/lib/deltaManagerSummarizerProxy.js +6 -6
- package/lib/deltaManagerSummarizerProxy.js.map +1 -1
- package/lib/deltaScheduler.js.map +1 -1
- package/lib/error.d.ts.map +1 -1
- package/lib/error.js.map +1 -1
- package/lib/gc/garbageCollection.js +13 -13
- package/lib/gc/garbageCollection.js.map +1 -1
- package/lib/gc/gcDefinitions.d.ts +4 -15
- package/lib/gc/gcDefinitions.d.ts.map +1 -1
- package/lib/gc/gcDefinitions.js.map +1 -1
- package/lib/gc/gcTelemetry.d.ts +1 -1
- package/lib/gc/gcTelemetry.d.ts.map +1 -1
- package/lib/gc/gcUnreferencedStateTracker.js +3 -3
- package/lib/gc/gcUnreferencedStateTracker.js.map +1 -1
- package/lib/id-compressor/appendOnlySortedMap.js.map +1 -1
- package/lib/id-compressor/idCompressor.js.map +1 -1
- package/lib/id-compressor/identifiers.d.ts +3 -3
- package/lib/id-compressor/identifiers.d.ts.map +1 -1
- package/lib/index.d.ts +1 -1
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +1 -1
- package/lib/index.js.map +1 -1
- package/lib/messageTypes.d.ts +17 -17
- package/lib/messageTypes.d.ts.map +1 -1
- package/lib/opLifecycle/batchManager.js +6 -6
- package/lib/opLifecycle/batchManager.js.map +1 -1
- package/lib/opLifecycle/definitions.d.ts +2 -2
- package/lib/opLifecycle/definitions.d.ts.map +1 -1
- package/lib/opLifecycle/outbox.d.ts.map +1 -1
- package/lib/opLifecycle/outbox.js +7 -2
- package/lib/opLifecycle/outbox.js.map +1 -1
- package/lib/packageVersion.d.ts +1 -1
- package/lib/packageVersion.js +1 -1
- package/lib/packageVersion.js.map +1 -1
- package/lib/pendingStateManager.d.ts +3 -19
- package/lib/pendingStateManager.d.ts.map +1 -1
- package/lib/pendingStateManager.js +23 -40
- package/lib/pendingStateManager.js.map +1 -1
- package/lib/scheduleManager.js +6 -2
- package/lib/scheduleManager.js.map +1 -1
- package/lib/summary/orderedClientElection.d.ts +3 -3
- package/lib/summary/orderedClientElection.d.ts.map +1 -1
- package/lib/summary/orderedClientElection.js +54 -54
- package/lib/summary/orderedClientElection.js.map +1 -1
- package/lib/summary/runWhileConnectedCoordinator.js +6 -6
- package/lib/summary/runWhileConnectedCoordinator.js.map +1 -1
- package/lib/summary/runningSummarizer.js +37 -37
- package/lib/summary/runningSummarizer.js.map +1 -1
- package/lib/summary/summarizer.d.ts +1 -0
- package/lib/summary/summarizer.d.ts.map +1 -1
- package/lib/summary/summarizer.js +18 -9
- package/lib/summary/summarizer.js.map +1 -1
- package/lib/summary/summarizerClientElection.js +6 -6
- package/lib/summary/summarizerClientElection.js.map +1 -1
- package/lib/summary/summarizerHeuristics.js +9 -9
- package/lib/summary/summarizerHeuristics.js.map +1 -1
- package/lib/summary/summarizerNode/summarizerNode.js +7 -7
- package/lib/summary/summarizerNode/summarizerNode.js.map +1 -1
- package/lib/summary/summarizerNode/summarizerNodeUtils.d.ts +1 -1
- package/lib/summary/summarizerNode/summarizerNodeUtils.d.ts.map +1 -1
- package/lib/summary/summarizerNode/summarizerNodeUtils.js +3 -3
- package/lib/summary/summarizerNode/summarizerNodeUtils.js.map +1 -1
- package/lib/summary/summarizerNode/summarizerNodeWithGc.d.ts +2 -2
- package/lib/summary/summarizerNode/summarizerNodeWithGc.d.ts.map +1 -1
- package/lib/summary/summarizerTypes.d.ts +12 -12
- package/lib/summary/summarizerTypes.d.ts.map +1 -1
- package/lib/summary/summaryCollection.d.ts +2 -2
- package/lib/summary/summaryCollection.d.ts.map +1 -1
- package/lib/summary/summaryCollection.js +22 -21
- package/lib/summary/summaryCollection.js.map +1 -1
- package/lib/summary/summaryFormat.d.ts +5 -5
- package/lib/summary/summaryFormat.d.ts.map +1 -1
- package/lib/summary/summaryGenerator.d.ts +3 -3
- package/lib/summary/summaryGenerator.d.ts.map +1 -1
- package/lib/summary/summaryGenerator.js.map +1 -1
- package/lib/summary/summaryManager.d.ts +2 -2
- package/lib/summary/summaryManager.d.ts.map +1 -1
- package/lib/summary/summaryManager.js +9 -9
- package/lib/summary/summaryManager.js.map +1 -1
- package/lib/throttler.js +16 -16
- package/lib/throttler.js.map +1 -1
- package/package.json +27 -27
- package/src/blobManager.ts +1 -1
- package/src/connectionTelemetry.ts +97 -52
- package/src/containerRuntime.ts +96 -73
- package/src/dataStore.ts +1 -1
- package/src/dataStoreContext.ts +1 -6
- package/src/error.ts +4 -1
- package/src/gc/gcDefinitions.ts +3 -15
- package/src/gc/gcEarlyAdoption.md +1 -1
- package/src/index.ts +1 -0
- package/src/opLifecycle/README.md +53 -28
- package/src/opLifecycle/outbox.ts +3 -0
- package/src/packageVersion.ts +1 -1
- package/src/pendingStateManager.ts +8 -46
- package/src/scheduleManager.ts +2 -0
- package/src/summary/summarizer.ts +20 -7
- package/src/summary/summaryCollection.ts +1 -0
- package/src/summary/summaryGenerator.ts +3 -3
- package/src/summary/summaryManager.ts +2 -2
package/src/dataStoreContext.ts
CHANGED
|
@@ -740,11 +740,6 @@ export abstract class FluidDataStoreContext
|
|
|
740
740
|
this.makeLocallyVisibleFn();
|
|
741
741
|
}
|
|
742
742
|
|
|
743
|
-
/** @deprecated - To be replaced by calling makeLocallyVisible directly */
|
|
744
|
-
public bindToContext() {
|
|
745
|
-
this.makeLocallyVisibleFn();
|
|
746
|
-
}
|
|
747
|
-
|
|
748
743
|
protected bindRuntime(channel: IFluidDataStoreChannel) {
|
|
749
744
|
if (this.channel) {
|
|
750
745
|
throw new Error("Runtime already bound");
|
|
@@ -1206,7 +1201,7 @@ export class LocalDetachedFluidDataStoreContext
|
|
|
1206
1201
|
// of data store factories tends to construct the data object (at least kick off an async method that returns
|
|
1207
1202
|
// it); that code moved to the entryPoint initialization function, so we want to ensure it still executes
|
|
1208
1203
|
// before the data store is attached.
|
|
1209
|
-
await dataStoreChannel.entryPoint
|
|
1204
|
+
await dataStoreChannel.entryPoint.get();
|
|
1210
1205
|
|
|
1211
1206
|
if (await this.isRoot()) {
|
|
1212
1207
|
dataStoreChannel.makeVisibleAndAttachGraph();
|
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(
|
|
15
|
+
constructor(
|
|
16
|
+
message: string,
|
|
17
|
+
readonly expiryMs: number,
|
|
18
|
+
) {
|
|
16
19
|
super(message, { timeoutMs: expiryMs });
|
|
17
20
|
}
|
|
18
21
|
}
|
package/src/gc/gcDefinitions.ts
CHANGED
|
@@ -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.
|
|
@@ -267,24 +267,12 @@ export interface IGCRuntimeOptions {
|
|
|
267
267
|
* GC has mark phase and sweep phase. In mark phase, unreferenced objects are identified
|
|
268
268
|
* and marked as such in the summary. This option enables the mark phase.
|
|
269
269
|
* In sweep phase, unreferenced objects are eventually deleted from the container if they meet certain conditions.
|
|
270
|
-
* Sweep phase can be enabled
|
|
270
|
+
* Sweep phase can be enabled using the "gcSweepGeneration" option.
|
|
271
271
|
*
|
|
272
272
|
* Note: This setting is persisted in the container's summary and cannot be changed.
|
|
273
273
|
*/
|
|
274
274
|
gcAllowed?: boolean;
|
|
275
275
|
|
|
276
|
-
/**
|
|
277
|
-
* @deprecated - @see gcSweepGenerationOptionName and @see GCFeatureMatrix.sweepGeneration
|
|
278
|
-
*
|
|
279
|
-
* Flag that if true, enables GC's sweep phase for a new container.
|
|
280
|
-
*
|
|
281
|
-
* This will allow GC to eventually delete unreferenced objects from the container.
|
|
282
|
-
* This flag should only be set to true if "gcAllowed" is true.
|
|
283
|
-
*
|
|
284
|
-
* Note: This setting is persisted in the container's summary and cannot be changed.
|
|
285
|
-
*/
|
|
286
|
-
sweepAllowed?: boolean;
|
|
287
|
-
|
|
288
276
|
/**
|
|
289
277
|
* Flag that if true, will disable garbage collection for the session.
|
|
290
278
|
* Can be used to disable running GC on containers where it is allowed via the gcAllowed option.
|
|
@@ -372,7 +360,7 @@ export const UnreferencedState = {
|
|
|
372
360
|
/** The node is ready to be deleted by the sweep phase. */
|
|
373
361
|
SweepReady: "SweepReady",
|
|
374
362
|
} as const;
|
|
375
|
-
export type UnreferencedState = typeof UnreferencedState[keyof typeof UnreferencedState];
|
|
363
|
+
export type UnreferencedState = (typeof UnreferencedState)[keyof typeof UnreferencedState];
|
|
376
364
|
|
|
377
365
|
/**
|
|
378
366
|
* Represents the result of a GC run.
|
|
@@ -78,7 +78,7 @@ For this to work, you must disable the `Fluid.GarbageCollection.ThrowOnTombstone
|
|
|
78
78
|
|
|
79
79
|
When a Tombstoned object (via `handle.get()`) fails to load, the 404 response error object has an `underlyingResponseHeaders` with the
|
|
80
80
|
`isTombstoned` flag set to true: i.e. `error.underlyingResponseHeaders?.isTombstoned === true`. In this case,
|
|
81
|
-
you may turn around and use `
|
|
81
|
+
you may turn around and use `IContainerRuntimeWithResolveHandle_Deprecated.resolveHandle` with `allowTombstone: true` in `IRequest.headers` to request
|
|
82
82
|
the object again - this time it will succeed.
|
|
83
83
|
|
|
84
84
|
To be very clear once again - This path uses deprecated APIs (`resolveHandle`) and comes with no guarantees of support.
|
package/src/index.ts
CHANGED
|
@@ -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
|
-
|
|
25
|
+
### How batching works
|
|
12
26
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
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
|
|
143
|
+
To enable grouped batching, use the following property:
|
|
106
144
|
|
|
107
145
|
```
|
|
108
146
|
const runtimeOptions: IContainerRuntimeOptions = {
|
|
109
|
-
|
|
147
|
+
enableGroupedBatching: true,
|
|
110
148
|
}
|
|
111
149
|
```
|
|
112
150
|
|
|
113
|
-
|
|
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
|
-
|
|
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
|
}
|
package/src/packageVersion.ts
CHANGED
|
@@ -15,24 +15,11 @@ import { ContainerMessageType, InboundSequencedContainerRuntimeMessage } from ".
|
|
|
15
15
|
import { pkgVersion } from "./packageVersion";
|
|
16
16
|
import { IBatchMetadata } from "./metadata";
|
|
17
17
|
|
|
18
|
-
/**
|
|
19
|
-
* ! TODO: Remove this interface in "2.0.0-internal.7.0.0" once we only read IPendingMessageNew (AB#4763)
|
|
20
|
-
*/
|
|
21
|
-
export interface IPendingMessageOld {
|
|
22
|
-
type: "message";
|
|
23
|
-
messageType: ContainerMessageType;
|
|
24
|
-
clientSequenceNumber: number;
|
|
25
|
-
referenceSequenceNumber: number;
|
|
26
|
-
content: any;
|
|
27
|
-
localOpMetadata: unknown;
|
|
28
|
-
opMetadata: Record<string, unknown> | undefined;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
18
|
/**
|
|
32
19
|
* This represents a message that has been submitted and is added to the pending queue when `submit` is called on the
|
|
33
20
|
* ContainerRuntime. This message has either not been ack'd by the server or has not been submitted to the server yet.
|
|
34
21
|
*/
|
|
35
|
-
export interface
|
|
22
|
+
export interface IPendingMessage {
|
|
36
23
|
type: "message";
|
|
37
24
|
clientSequenceNumber: number;
|
|
38
25
|
referenceSequenceNumber: number;
|
|
@@ -41,16 +28,11 @@ export interface IPendingMessageNew {
|
|
|
41
28
|
opMetadata: Record<string, unknown> | undefined;
|
|
42
29
|
}
|
|
43
30
|
|
|
44
|
-
/**
|
|
45
|
-
* ! TODO: Remove this type in "2.0.0-internal.7.0.0" (AB#4763)
|
|
46
|
-
*/
|
|
47
|
-
export type IPendingState = IPendingMessageOld | IPendingMessageNew;
|
|
48
|
-
|
|
49
31
|
export interface IPendingLocalState {
|
|
50
32
|
/**
|
|
51
33
|
* list of pending states, including ops and batch information
|
|
52
34
|
*/
|
|
53
|
-
pendingStates:
|
|
35
|
+
pendingStates: IPendingMessage[];
|
|
54
36
|
}
|
|
55
37
|
|
|
56
38
|
export interface IPendingBatchMessage {
|
|
@@ -99,13 +81,13 @@ function buildPendingMessageContent(
|
|
|
99
81
|
* It verifies that all the ops are acked, are received in the right order and batch information is correct.
|
|
100
82
|
*/
|
|
101
83
|
export class PendingStateManager implements IDisposable {
|
|
102
|
-
private readonly pendingMessages = new Deque<
|
|
103
|
-
private readonly initialMessages = new Deque<
|
|
84
|
+
private readonly pendingMessages = new Deque<IPendingMessage>();
|
|
85
|
+
private readonly initialMessages = new Deque<IPendingMessage>();
|
|
104
86
|
|
|
105
87
|
/**
|
|
106
88
|
* Sequenced local ops that are saved when stashing since pending ops may depend on them
|
|
107
89
|
*/
|
|
108
|
-
private savedOps:
|
|
90
|
+
private savedOps: IPendingMessage[] = [];
|
|
109
91
|
|
|
110
92
|
private readonly disposeOnce = new Lazy<void>(() => {
|
|
111
93
|
this.initialMessages.clear();
|
|
@@ -169,29 +151,8 @@ export class PendingStateManager implements IDisposable {
|
|
|
169
151
|
initialLocalState: IPendingLocalState | undefined,
|
|
170
152
|
private readonly logger: ITelemetryLoggerExt | undefined,
|
|
171
153
|
) {
|
|
172
|
-
/**
|
|
173
|
-
* Convert old local state format to the new format (IPendingMessageOld to IPendingMessageNew)
|
|
174
|
-
* ! TODO: Remove this conversion in "2.0.0-internal.7.0.0" (AB#4763)
|
|
175
|
-
*/
|
|
176
154
|
if (initialLocalState?.pendingStates) {
|
|
177
|
-
|
|
178
|
-
let messageContent = initialState.content;
|
|
179
|
-
if (
|
|
180
|
-
(initialState as IPendingMessageOld).messageType !== undefined &&
|
|
181
|
-
typeof initialState.content !== "string"
|
|
182
|
-
) {
|
|
183
|
-
// Convert IPendingMessageOld to IPendingMessageNew
|
|
184
|
-
messageContent = JSON.stringify({
|
|
185
|
-
type: (initialState as IPendingMessageOld).messageType,
|
|
186
|
-
contents: initialState.content,
|
|
187
|
-
});
|
|
188
|
-
}
|
|
189
|
-
// Note: this object may contain "messageType" prop, but it should not be easily accesible due to interface being used
|
|
190
|
-
this.initialMessages.push({
|
|
191
|
-
...initialState,
|
|
192
|
-
content: messageContent,
|
|
193
|
-
});
|
|
194
|
-
}
|
|
155
|
+
this.initialMessages.push(...initialLocalState.pendingStates);
|
|
195
156
|
}
|
|
196
157
|
}
|
|
197
158
|
|
|
@@ -213,7 +174,7 @@ export class PendingStateManager implements IDisposable {
|
|
|
213
174
|
localOpMetadata: unknown,
|
|
214
175
|
opMetadata: Record<string, unknown> | undefined,
|
|
215
176
|
) {
|
|
216
|
-
const pendingMessage:
|
|
177
|
+
const pendingMessage: IPendingMessage = {
|
|
217
178
|
type: "message",
|
|
218
179
|
clientSequenceNumber: -1, // dummy value (not to be used anywhere)
|
|
219
180
|
referenceSequenceNumber,
|
|
@@ -357,6 +318,7 @@ export class PendingStateManager implements IDisposable {
|
|
|
357
318
|
{
|
|
358
319
|
runtimeVersion: pkgVersion,
|
|
359
320
|
batchClientId:
|
|
321
|
+
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
|
|
360
322
|
this.pendingBatchBeginMessage.clientId === null
|
|
361
323
|
? "null"
|
|
362
324
|
: this.pendingBatchBeginMessage.clientId,
|
package/src/scheduleManager.ts
CHANGED
|
@@ -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,8 +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
|
-
import { requestFluidObject } from "@fluidframework/runtime-utils";
|
|
19
18
|
import { FluidObject, IFluidHandleContext, IRequest } from "@fluidframework/core-interfaces";
|
|
19
|
+
import { responseToException } from "@fluidframework/runtime-utils";
|
|
20
20
|
import { ISummaryConfiguration } from "../containerRuntime";
|
|
21
21
|
import { ICancellableSummarizerController } from "./runWhileConnectedCoordinator";
|
|
22
22
|
import { summarizerClientType } from "./summarizerClientElection";
|
|
@@ -49,7 +49,10 @@ export class SummarizingWarning
|
|
|
49
49
|
readonly errorType = summarizingError;
|
|
50
50
|
readonly canRetry = true;
|
|
51
51
|
|
|
52
|
-
constructor(
|
|
52
|
+
constructor(
|
|
53
|
+
errorMessage: string,
|
|
54
|
+
readonly logged: boolean = false,
|
|
55
|
+
) {
|
|
53
56
|
super(errorMessage);
|
|
54
57
|
}
|
|
55
58
|
|
|
@@ -107,6 +110,7 @@ export class Summarizer extends TypedEventEmitter<ISummarizerEvents> implements
|
|
|
107
110
|
* interface will expect an absolute URL and will not handle "/".
|
|
108
111
|
* @param loader - the loader that resolves the request
|
|
109
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.
|
|
110
114
|
*/
|
|
111
115
|
public static async create(loader: ILoader, url: string): Promise<ISummarizer> {
|
|
112
116
|
const request: IRequest = {
|
|
@@ -123,11 +127,20 @@ export class Summarizer extends TypedEventEmitter<ISummarizerEvents> implements
|
|
|
123
127
|
};
|
|
124
128
|
|
|
125
129
|
const resolvedContainer = await loader.resolve(request);
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
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
|
+
|
|
131
144
|
if (fluidObject?.ISummarizer === undefined) {
|
|
132
145
|
throw new UsageError("Fluid object does not implement ISummarizer");
|
|
133
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
|
|
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.
|
|
267
|
+
const summarizer = await this.createSummarizerFn();
|
|
268
268
|
this.summarizer = summarizer;
|
|
269
269
|
this.summarizer.on("summarize", this.handleSummarizeEvent);
|
|
270
270
|
|