@fluidframework/container-runtime 2.0.0-dev.6.4.0.191515 → 2.0.0-dev.7.2.0.203917
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 +119 -0
- package/api-extractor.json +1 -1
- package/api-report/container-runtime.api.md +864 -0
- package/dist/blobManager.d.ts +4 -4
- package/dist/blobManager.d.ts.map +1 -1
- package/dist/blobManager.js +55 -71
- 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 +1742 -0
- package/dist/container-runtime-beta.d.ts +1742 -0
- package/dist/container-runtime-public.d.ts +1742 -0
- package/dist/container-runtime-untrimmed.d.ts +1803 -0
- package/dist/containerHandleContext.js +3 -3
- package/dist/containerHandleContext.js.map +1 -1
- package/dist/containerRuntime.d.ts +88 -98
- package/dist/containerRuntime.d.ts.map +1 -1
- package/dist/containerRuntime.js +466 -453
- package/dist/containerRuntime.js.map +1 -1
- package/dist/dataStore.js +11 -11
- package/dist/dataStore.js.map +1 -1
- package/dist/dataStoreContext.d.ts +2 -4
- package/dist/dataStoreContext.d.ts.map +1 -1
- package/dist/dataStoreContext.js +60 -59
- package/dist/dataStoreContext.js.map +1 -1
- package/dist/dataStoreRegistry.d.ts +3 -0
- package/dist/dataStoreRegistry.d.ts.map +1 -1
- package/dist/dataStoreRegistry.js +6 -3
- package/dist/dataStoreRegistry.js.map +1 -1
- package/dist/dataStores.js +1 -1
- package/dist/dataStores.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.d.ts.map +1 -1
- package/dist/gc/garbageCollection.js +23 -24
- package/dist/gc/garbageCollection.js.map +1 -1
- package/dist/gc/gcConfigs.js +1 -1
- package/dist/gc/gcConfigs.js.map +1 -1
- package/dist/gc/gcDefinitions.d.ts +34 -24
- package/dist/gc/gcDefinitions.d.ts.map +1 -1
- package/dist/gc/gcDefinitions.js +10 -7
- package/dist/gc/gcDefinitions.js.map +1 -1
- package/dist/gc/gcSummaryDefinitions.d.ts +1 -1
- package/dist/gc/gcSummaryDefinitions.js.map +1 -1
- package/dist/gc/gcTelemetry.d.ts +2 -2
- package/dist/gc/gcTelemetry.d.ts.map +1 -1
- package/dist/gc/gcTelemetry.js +5 -5
- package/dist/gc/gcTelemetry.js.map +1 -1
- package/dist/gc/gcUnreferencedStateTracker.js +3 -3
- package/dist/gc/gcUnreferencedStateTracker.js.map +1 -1
- package/dist/gc/index.d.ts +1 -1
- package/dist/gc/index.d.ts.map +1 -1
- package/dist/gc/index.js +3 -3
- package/dist/gc/index.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/id-compressor/utilities.d.ts +3 -0
- package/dist/id-compressor/utilities.d.ts.map +1 -1
- package/dist/id-compressor/utilities.js +3 -0
- package/dist/id-compressor/utilities.js.map +1 -1
- package/dist/index.d.ts +5 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -2
- package/dist/index.js.map +1 -1
- package/dist/messageTypes.d.ts +137 -0
- package/dist/messageTypes.d.ts.map +1 -0
- package/dist/messageTypes.js +32 -0
- package/dist/messageTypes.js.map +1 -0
- package/dist/opLifecycle/batchManager.js +6 -6
- package/dist/opLifecycle/batchManager.js.map +1 -1
- package/dist/opLifecycle/definitions.d.ts +7 -3
- package/dist/opLifecycle/definitions.d.ts.map +1 -1
- package/dist/opLifecycle/definitions.js.map +1 -1
- package/dist/opLifecycle/opDecompressor.d.ts.map +1 -1
- package/dist/opLifecycle/opDecompressor.js +0 -4
- package/dist/opLifecycle/opDecompressor.js.map +1 -1
- package/dist/opLifecycle/opGroupingManager.d.ts.map +1 -1
- package/dist/opLifecycle/opGroupingManager.js.map +1 -1
- package/dist/opLifecycle/opSplitter.js +3 -3
- package/dist/opLifecycle/opSplitter.js.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/opLifecycle/remoteMessageProcessor.d.ts +17 -3
- package/dist/opLifecycle/remoteMessageProcessor.d.ts.map +1 -1
- package/dist/opLifecycle/remoteMessageProcessor.js +38 -25
- package/dist/opLifecycle/remoteMessageProcessor.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 +4 -20
- package/dist/pendingStateManager.d.ts.map +1 -1
- package/dist/pendingStateManager.js +36 -46
- 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 +7 -4
- 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.d.ts +5 -0
- package/dist/summary/runWhileConnectedCoordinator.d.ts.map +1 -1
- package/dist/summary/runWhileConnectedCoordinator.js +7 -6
- package/dist/summary/runWhileConnectedCoordinator.js.map +1 -1
- package/dist/summary/runningSummarizer.d.ts.map +1 -1
- package/dist/summary/runningSummarizer.js +40 -38
- package/dist/summary/runningSummarizer.js.map +1 -1
- package/dist/summary/summarizer.d.ts +2 -0
- package/dist/summary/summarizer.d.ts.map +1 -1
- package/dist/summary/summarizer.js +18 -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.d.ts +1 -1
- package/dist/summary/summarizerNode/summarizerNode.js +8 -8
- 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 +107 -22
- package/dist/summary/summarizerTypes.d.ts.map +1 -1
- package/dist/summary/summarizerTypes.js.map +1 -1
- package/dist/summary/summaryCollection.d.ts +18 -2
- package/dist/summary/summaryCollection.d.ts.map +1 -1
- package/dist/summary/summaryCollection.js +23 -21
- package/dist/summary/summaryCollection.js.map +1 -1
- package/dist/summary/summaryFormat.d.ts +15 -6
- package/dist/summary/summaryFormat.d.ts.map +1 -1
- package/dist/summary/summaryFormat.js.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 +4 -4
- package/lib/blobManager.d.ts.map +1 -1
- package/lib/blobManager.js +56 -72
- 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 +88 -98
- package/lib/containerRuntime.d.ts.map +1 -1
- package/lib/containerRuntime.js +424 -416
- package/lib/containerRuntime.js.map +1 -1
- package/lib/dataStore.js +11 -11
- package/lib/dataStore.js.map +1 -1
- package/lib/dataStoreContext.d.ts +2 -4
- package/lib/dataStoreContext.d.ts.map +1 -1
- package/lib/dataStoreContext.js +60 -59
- package/lib/dataStoreContext.js.map +1 -1
- package/lib/dataStoreRegistry.d.ts +3 -0
- package/lib/dataStoreRegistry.d.ts.map +1 -1
- package/lib/dataStoreRegistry.js +6 -3
- package/lib/dataStoreRegistry.js.map +1 -1
- package/lib/dataStores.js +1 -1
- package/lib/dataStores.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.d.ts.map +1 -1
- package/lib/gc/garbageCollection.js +23 -24
- package/lib/gc/garbageCollection.js.map +1 -1
- package/lib/gc/gcConfigs.js +2 -2
- package/lib/gc/gcConfigs.js.map +1 -1
- package/lib/gc/gcDefinitions.d.ts +34 -24
- package/lib/gc/gcDefinitions.d.ts.map +1 -1
- package/lib/gc/gcDefinitions.js +9 -6
- package/lib/gc/gcDefinitions.js.map +1 -1
- package/lib/gc/gcSummaryDefinitions.d.ts +1 -1
- package/lib/gc/gcSummaryDefinitions.js.map +1 -1
- package/lib/gc/gcTelemetry.d.ts +2 -2
- package/lib/gc/gcTelemetry.d.ts.map +1 -1
- package/lib/gc/gcTelemetry.js +5 -5
- package/lib/gc/gcTelemetry.js.map +1 -1
- package/lib/gc/gcUnreferencedStateTracker.js +3 -3
- package/lib/gc/gcUnreferencedStateTracker.js.map +1 -1
- package/lib/gc/index.d.ts +1 -1
- package/lib/gc/index.d.ts.map +1 -1
- package/lib/gc/index.js +1 -1
- package/lib/gc/index.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/id-compressor/utilities.d.ts +3 -0
- package/lib/id-compressor/utilities.d.ts.map +1 -1
- package/lib/id-compressor/utilities.js +3 -0
- package/lib/id-compressor/utilities.js.map +1 -1
- package/lib/index.d.ts +5 -3
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +3 -1
- package/lib/index.js.map +1 -1
- package/lib/messageTypes.d.ts +137 -0
- package/lib/messageTypes.d.ts.map +1 -0
- package/lib/messageTypes.js +29 -0
- package/lib/messageTypes.js.map +1 -0
- package/lib/opLifecycle/batchManager.js +6 -6
- package/lib/opLifecycle/batchManager.js.map +1 -1
- package/lib/opLifecycle/definitions.d.ts +7 -3
- package/lib/opLifecycle/definitions.d.ts.map +1 -1
- package/lib/opLifecycle/definitions.js.map +1 -1
- package/lib/opLifecycle/opDecompressor.d.ts.map +1 -1
- package/lib/opLifecycle/opDecompressor.js +0 -4
- package/lib/opLifecycle/opDecompressor.js.map +1 -1
- package/lib/opLifecycle/opGroupingManager.d.ts.map +1 -1
- package/lib/opLifecycle/opGroupingManager.js.map +1 -1
- package/lib/opLifecycle/opSplitter.js +1 -1
- package/lib/opLifecycle/opSplitter.js.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/opLifecycle/remoteMessageProcessor.d.ts +17 -3
- package/lib/opLifecycle/remoteMessageProcessor.d.ts.map +1 -1
- package/lib/opLifecycle/remoteMessageProcessor.js +37 -24
- package/lib/opLifecycle/remoteMessageProcessor.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 +4 -20
- package/lib/pendingStateManager.d.ts.map +1 -1
- package/lib/pendingStateManager.js +35 -45
- 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 +7 -4
- 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.d.ts +5 -0
- package/lib/summary/runWhileConnectedCoordinator.d.ts.map +1 -1
- package/lib/summary/runWhileConnectedCoordinator.js +7 -6
- package/lib/summary/runWhileConnectedCoordinator.js.map +1 -1
- package/lib/summary/runningSummarizer.d.ts.map +1 -1
- package/lib/summary/runningSummarizer.js +40 -38
- package/lib/summary/runningSummarizer.js.map +1 -1
- package/lib/summary/summarizer.d.ts +2 -0
- package/lib/summary/summarizer.d.ts.map +1 -1
- package/lib/summary/summarizer.js +19 -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.d.ts +1 -1
- package/lib/summary/summarizerNode/summarizerNode.js +8 -8
- 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 +107 -22
- package/lib/summary/summarizerTypes.d.ts.map +1 -1
- package/lib/summary/summarizerTypes.js.map +1 -1
- package/lib/summary/summaryCollection.d.ts +18 -2
- package/lib/summary/summaryCollection.d.ts.map +1 -1
- package/lib/summary/summaryCollection.js +23 -21
- package/lib/summary/summaryCollection.js.map +1 -1
- package/lib/summary/summaryFormat.d.ts +15 -6
- package/lib/summary/summaryFormat.d.ts.map +1 -1
- package/lib/summary/summaryFormat.js.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 -28
- package/src/blobManager.ts +64 -77
- package/src/connectionTelemetry.ts +97 -52
- package/src/containerRuntime.ts +337 -341
- package/src/dataStore.ts +3 -3
- package/src/dataStoreContext.ts +7 -7
- package/src/dataStoreRegistry.ts +3 -0
- package/src/dataStores.ts +1 -1
- package/src/error.ts +4 -1
- package/src/gc/garbageCollection.ts +12 -11
- package/src/gc/gcConfigs.ts +3 -3
- package/src/gc/gcDefinitions.ts +35 -25
- package/src/gc/gcSummaryDefinitions.ts +1 -1
- package/src/gc/gcTelemetry.ts +6 -5
- package/src/gc/index.ts +2 -2
- package/src/id-compressor/utilities.ts +3 -0
- package/src/index.ts +21 -5
- package/src/messageTypes.ts +228 -0
- package/src/opLifecycle/README.md +93 -68
- package/src/opLifecycle/definitions.ts +5 -1
- package/src/opLifecycle/opDecompressor.ts +0 -8
- package/src/opLifecycle/opGroupingManager.ts +2 -4
- package/src/opLifecycle/opSplitter.ts +2 -2
- package/src/opLifecycle/outbox.ts +3 -0
- package/src/opLifecycle/remoteMessageProcessor.ts +54 -33
- package/src/packageVersion.ts +1 -1
- package/src/pendingStateManager.ts +31 -52
- package/src/scheduleManager.ts +2 -0
- package/src/summary/orderedClientElection.ts +4 -1
- package/src/summary/runWhileConnectedCoordinator.ts +5 -1
- package/src/summary/runningSummarizer.ts +3 -1
- package/src/summary/summarizer.ts +21 -7
- package/src/summary/summarizerNode/summarizerNode.ts +1 -1
- package/src/summary/summarizerTypes.ts +96 -11
- package/src/summary/summaryCollection.ts +19 -1
- package/src/summary/summaryFormat.ts +11 -1
- package/src/summary/summaryGenerator.ts +3 -3
- package/src/summary/summaryManager.ts +2 -2
- package/src/gc/gcEarlyAdoption.md +0 -145
|
@@ -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
|
-
|
|
151
|
+
## Note about performance and latency
|
|
114
152
|
|
|
115
|
-
|
|
116
|
-
const runtimeOptions: IContainerRuntimeOptions = {
|
|
117
|
-
compressionOptions: {
|
|
118
|
-
minimumBatchSizeInBytes: Number.POSITIVE_INFINITY,
|
|
119
|
-
compressionAlgorithm: CompressionAlgorithms.lz4,
|
|
120
|
-
},
|
|
121
|
-
}
|
|
122
|
-
```
|
|
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.
|
|
123
154
|
|
|
124
|
-
|
|
125
|
-
|
|
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
|
|
|
@@ -212,8 +237,6 @@ On the receiving end, the client will accumulate chunks 1 and 2 and keep them in
|
|
|
212
237
|
|
|
213
238
|
## How grouped batching works
|
|
214
239
|
|
|
215
|
-
**Note: There are plans to replace empty ops with something more efficient when doing grouped batching AB#4092**
|
|
216
|
-
|
|
217
240
|
Given the following baseline batch:
|
|
218
241
|
|
|
219
242
|
```
|
|
@@ -223,68 +246,70 @@ Given the following baseline batch:
|
|
|
223
246
|
+---------------+---------------+---------------+---------------+---------------+
|
|
224
247
|
```
|
|
225
248
|
|
|
226
|
-
|
|
249
|
+
Grouped batch:
|
|
227
250
|
|
|
228
251
|
```
|
|
229
|
-
|
|
230
|
-
| Op 1
|
|
231
|
-
|
|
|
232
|
-
|
|
|
233
|
-
|
|
252
|
+
+---------------------------------------------------------------------------------------------------------------------+
|
|
253
|
+
| Op 1 Contents: +----------------+---------------+---------------+---------------+---------------+ |
|
|
254
|
+
| Type: "groupedBatch" | Op 1 | Op 2 | Op 3 | Op 4 | Op 5 | |
|
|
255
|
+
| | Contents: "a" | Contents: "b" | Contents: "c" | Contents: "d" | Contents: "e" | |
|
|
256
|
+
| +----------------+---------------+---------------+---------------+---------------+ |
|
|
257
|
+
+---------------------------------------------------------------------------------------------------------------------+
|
|
234
258
|
```
|
|
235
259
|
|
|
236
|
-
|
|
260
|
+
Compressed batch:
|
|
237
261
|
|
|
238
262
|
```
|
|
239
|
-
|
|
240
|
-
| Op 1 Contents:
|
|
241
|
-
|
|
|
242
|
-
|
|
|
243
|
-
| |
|
|
244
|
-
|
|
|
245
|
-
|
|
263
|
+
+-------------------------------------------------------------------------------------------------------------------------+
|
|
264
|
+
| Op 1 Contents: +------------------------------------------------------------------------------------+ |
|
|
265
|
+
| Compression: 'lz4' | Type: "groupedBatch" | |
|
|
266
|
+
| | +----------------+---------------+---------------+---------------+---------------+ | |
|
|
267
|
+
| | | Op 1 | Op 2 | Op 3 | Op 4 | Op 5 | | |
|
|
268
|
+
| | | Contents: "a" | Contents: "b" | Contents: "c" | Contents: "d" | Contents: "e" | | |
|
|
269
|
+
| | +----------------+---------------+---------------+---------------+---------------+ | |
|
|
270
|
+
| +------------------------------------------------------------------------------------+ |
|
|
271
|
+
+-------------------------------------------------------------------------------------------------------------------------+
|
|
246
272
|
```
|
|
247
273
|
|
|
248
274
|
Can produce the following chunks:
|
|
249
275
|
|
|
250
276
|
```
|
|
251
|
-
|
|
252
|
-
| Chunk 1/2 Contents:
|
|
253
|
-
|
|
|
254
|
-
| |
|
|
255
|
-
| |
|
|
256
|
-
|
|
|
257
|
-
|
|
277
|
+
+------------------------------------------------+
|
|
278
|
+
| Chunk 1/2 Contents: +---------------------+ |
|
|
279
|
+
| | +-----------------+ | |
|
|
280
|
+
| | | Contents: "abc" | | |
|
|
281
|
+
| | +-----------------+ | |
|
|
282
|
+
| +---------------------+ |
|
|
283
|
+
+------------------------------------------------+
|
|
258
284
|
```
|
|
259
285
|
|
|
260
286
|
```
|
|
261
|
-
|
|
262
|
-
| Chunk 2/2 Contents:
|
|
263
|
-
|
|
|
264
|
-
| |
|
|
265
|
-
| |
|
|
266
|
-
|
|
|
267
|
-
|
|
287
|
+
+-----------------------------------------------+
|
|
288
|
+
| Chunk 2/2 Contents: +--------------------+ |
|
|
289
|
+
| | +----------------+ | |
|
|
290
|
+
| | | Contents: "de" | | |
|
|
291
|
+
| | +----------------+ | |
|
|
292
|
+
| +--------------------+ |
|
|
293
|
+
+-----------------------------------------------+
|
|
268
294
|
```
|
|
269
295
|
|
|
270
296
|
- Send to service
|
|
271
297
|
- Service acks ops sent
|
|
272
298
|
- Receive chunks from service
|
|
273
|
-
- Recompile to the
|
|
299
|
+
- Recompile to the compression step
|
|
274
300
|
|
|
275
|
-
|
|
301
|
+
Decompressed batch:
|
|
276
302
|
|
|
277
303
|
```
|
|
278
|
-
|
|
279
|
-
| Op 1
|
|
280
|
-
|
|
|
281
|
-
|
|
|
282
|
-
|
|
|
283
|
-
|
|
284
|
-
+--------------------+-----------------+-----------------+-----------------+-----------------+
|
|
304
|
+
+---------------------------------------------------------------------------------------------------------------------+
|
|
305
|
+
| Op 1 Contents: +----------------+---------------+---------------+---------------+---------------+ |
|
|
306
|
+
| SeqNum: 2 | Op 1 | Op 2 | Op 3 | Op 4 | Op 5 | |
|
|
307
|
+
| Type: "groupedBatch" | Contents: "a" | Contents: "b" | Contents: "c" | Contents: "d" | Contents: "e" | |
|
|
308
|
+
| +----------------+---------------+---------------+---------------+---------------+ |
|
|
309
|
+
+---------------------------------------------------------------------------------------------------------------------+
|
|
285
310
|
```
|
|
286
311
|
|
|
287
|
-
|
|
312
|
+
Ungrouped batch:
|
|
288
313
|
|
|
289
314
|
```
|
|
290
315
|
+-----------------+-----------------+-----------------+-----------------+-----------------+
|
|
@@ -5,7 +5,8 @@
|
|
|
5
5
|
|
|
6
6
|
import { IBatchMessage } from "@fluidframework/container-definitions";
|
|
7
7
|
import { ISequencedDocumentMessage, MessageType } from "@fluidframework/protocol-definitions";
|
|
8
|
-
import { CompressionAlgorithms
|
|
8
|
+
import { CompressionAlgorithms } from "..";
|
|
9
|
+
import { ContainerMessageType } from "../messageTypes";
|
|
9
10
|
|
|
10
11
|
/**
|
|
11
12
|
* Batch message type used internally by the runtime
|
|
@@ -51,6 +52,9 @@ export interface IBatchCheckpoint {
|
|
|
51
52
|
rollback: (action: (message: BatchMessage) => void) => void;
|
|
52
53
|
}
|
|
53
54
|
|
|
55
|
+
/**
|
|
56
|
+
* @public
|
|
57
|
+
*/
|
|
54
58
|
export interface IChunkedOp {
|
|
55
59
|
chunkId: number;
|
|
56
60
|
totalChunks: number;
|
|
@@ -50,14 +50,6 @@ export class OpDecompressor {
|
|
|
50
50
|
) {
|
|
51
51
|
// Beginning of a compressed batch
|
|
52
52
|
assert(this.activeBatch === false, 0x4b8 /* shouldn't have multiple active batches */);
|
|
53
|
-
if (message.compression) {
|
|
54
|
-
// lz4 is the only supported compression algorithm for now
|
|
55
|
-
assert(
|
|
56
|
-
message.compression === CompressionAlgorithms.lz4,
|
|
57
|
-
0x4b9 /* lz4 is currently the only supported compression algorithm */,
|
|
58
|
-
);
|
|
59
|
-
}
|
|
60
|
-
|
|
61
53
|
this.activeBatch = true;
|
|
62
54
|
|
|
63
55
|
const contents = IsoBuffer.from(
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
import { assert } from "@fluidframework/core-utils";
|
|
7
7
|
import { ISequencedDocumentMessage } from "@fluidframework/protocol-definitions";
|
|
8
|
-
import { ContainerMessageType } from "
|
|
8
|
+
import { ContainerMessageType } from "../messageTypes";
|
|
9
9
|
import { IBatch } from "./definitions";
|
|
10
10
|
|
|
11
11
|
/**
|
|
@@ -22,9 +22,7 @@ interface IGroupedMessage {
|
|
|
22
22
|
compression?: string;
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
function isGroupContents(
|
|
26
|
-
opContents: IGroupedBatchMessageContents | { type?: unknown } | undefined,
|
|
27
|
-
): opContents is IGroupedBatchMessageContents {
|
|
25
|
+
function isGroupContents(opContents: any): opContents is IGroupedBatchMessageContents {
|
|
28
26
|
return opContents?.type === OpGroupingManager.groupedBatchOp;
|
|
29
27
|
}
|
|
30
28
|
|
|
@@ -12,7 +12,7 @@ import { assert } from "@fluidframework/core-utils";
|
|
|
12
12
|
import { IBatchMessage } from "@fluidframework/container-definitions";
|
|
13
13
|
import { ISequencedDocumentMessage } from "@fluidframework/protocol-definitions";
|
|
14
14
|
import { ITelemetryBaseLogger } from "@fluidframework/core-interfaces";
|
|
15
|
-
import { ContainerMessageType,
|
|
15
|
+
import { ContainerMessageType, ContainerRuntimeChunkedOpMessage } from "../messageTypes";
|
|
16
16
|
import { estimateSocketSize } from "./batchManager";
|
|
17
17
|
import { BatchMessage, IBatch, IChunkedOp, IMessageProcessingResult } from "./definitions";
|
|
18
18
|
|
|
@@ -210,7 +210,7 @@ const chunkToBatchMessage = (
|
|
|
210
210
|
referenceSequenceNumber: number,
|
|
211
211
|
metadata: Record<string, unknown> | undefined = undefined,
|
|
212
212
|
): BatchMessage => {
|
|
213
|
-
const payload:
|
|
213
|
+
const payload: ContainerRuntimeChunkedOpMessage = {
|
|
214
214
|
type: ContainerMessageType.ChunkedOp,
|
|
215
215
|
contents: chunk,
|
|
216
216
|
};
|
|
@@ -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,13 +6,21 @@
|
|
|
6
6
|
import { ISequencedDocumentMessage, MessageType } from "@fluidframework/protocol-definitions";
|
|
7
7
|
import {
|
|
8
8
|
ContainerMessageType,
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
type InboundContainerRuntimeMessage,
|
|
10
|
+
type InboundSequencedContainerRuntimeMessage,
|
|
11
|
+
type InboundSequencedContainerRuntimeMessageOrSystemMessage,
|
|
12
|
+
type InboundSequencedRecentlyAddedContainerRuntimeMessage,
|
|
13
|
+
} from "../messageTypes";
|
|
12
14
|
import { OpDecompressor } from "./opDecompressor";
|
|
13
15
|
import { OpGroupingManager } from "./opGroupingManager";
|
|
14
16
|
import { OpSplitter } from "./opSplitter";
|
|
15
17
|
|
|
18
|
+
/**
|
|
19
|
+
* Stateful class for processing incoming remote messages as the virtualization measures are unwrapped,
|
|
20
|
+
* potentially across numerous inbound ops.
|
|
21
|
+
*
|
|
22
|
+
* @internal
|
|
23
|
+
*/
|
|
16
24
|
export class RemoteMessageProcessor {
|
|
17
25
|
constructor(
|
|
18
26
|
private readonly opSplitter: OpSplitter,
|
|
@@ -30,14 +38,25 @@ export class RemoteMessageProcessor {
|
|
|
30
38
|
|
|
31
39
|
/**
|
|
32
40
|
* Ungroups and Unchunks the runtime ops encapsulated by the single remoteMessage received over the wire
|
|
33
|
-
* @param
|
|
34
|
-
*
|
|
41
|
+
* @param remoteMessageCopy - A shallow copy of a message from another client, possibly virtualized
|
|
42
|
+
* (grouped, compressed, and/or chunked).
|
|
43
|
+
* Being a shallow copy, it's considered mutable, meaning no other Container or other parallel procedure
|
|
44
|
+
* depends on this object instance.
|
|
45
|
+
* Note remoteMessageCopy.contents (and other object props) MUST not be modified,
|
|
46
|
+
* but may be overwritten (as is the case with contents).
|
|
47
|
+
* @returns the unchunked, decompressed, ungrouped, unpacked SequencedContainerRuntimeMessages encapsulated in the remote message.
|
|
48
|
+
* For ops that weren't virtualized (e.g. System ops that the ContainerRuntime will ultimately ignore),
|
|
49
|
+
* a singleton array [remoteMessageCopy] is returned
|
|
35
50
|
*/
|
|
36
|
-
public process(
|
|
37
|
-
|
|
51
|
+
public process(
|
|
52
|
+
remoteMessageCopy: ISequencedDocumentMessage,
|
|
53
|
+
): InboundSequencedContainerRuntimeMessageOrSystemMessage[] {
|
|
54
|
+
const result: InboundSequencedContainerRuntimeMessageOrSystemMessage[] = [];
|
|
55
|
+
|
|
56
|
+
ensureContentsDeserialized(remoteMessageCopy);
|
|
38
57
|
|
|
39
58
|
// Ungroup before and after decompression for back-compat (cleanup tracked by AB#4371)
|
|
40
|
-
for (const ungroupedMessage of this.opGroupingManager.ungroupOp(
|
|
59
|
+
for (const ungroupedMessage of this.opGroupingManager.ungroupOp(remoteMessageCopy)) {
|
|
41
60
|
const message = this.opDecompressor.processMessage(ungroupedMessage).message;
|
|
42
61
|
|
|
43
62
|
for (let ungroupedMessage2 of this.opGroupingManager.ungroupOp(message)) {
|
|
@@ -50,7 +69,9 @@ export class RemoteMessageProcessor {
|
|
|
50
69
|
if (chunkProcessingResult.state !== "Processed") {
|
|
51
70
|
// If the message is not chunked or if the splitter is still rebuilding the original message,
|
|
52
71
|
// there is no need to continue processing
|
|
53
|
-
result.push(
|
|
72
|
+
result.push(
|
|
73
|
+
ungroupedMessage2 as InboundSequencedContainerRuntimeMessageOrSystemMessage,
|
|
74
|
+
);
|
|
54
75
|
continue;
|
|
55
76
|
}
|
|
56
77
|
|
|
@@ -68,7 +89,9 @@ export class RemoteMessageProcessor {
|
|
|
68
89
|
if (decompressionAfterChunking.state === "Skipped") {
|
|
69
90
|
// After chunking, if the original message was not compressed,
|
|
70
91
|
// there is no need to continue processing
|
|
71
|
-
result.push(
|
|
92
|
+
result.push(
|
|
93
|
+
ungroupedMessageAfterChunking2 as InboundSequencedContainerRuntimeMessageOrSystemMessage,
|
|
94
|
+
);
|
|
72
95
|
continue;
|
|
73
96
|
}
|
|
74
97
|
|
|
@@ -84,40 +107,38 @@ export class RemoteMessageProcessor {
|
|
|
84
107
|
}
|
|
85
108
|
}
|
|
86
109
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
// There might be multiple container instances receiving same message
|
|
90
|
-
// We do not need to make deep copy, as each layer will just replace message.content itself,
|
|
91
|
-
// but would not modify contents details
|
|
92
|
-
const message = { ...remoteMessage };
|
|
93
|
-
|
|
110
|
+
/** Takes an incoming message and if the contents is a string, JSON.parse's it in place */
|
|
111
|
+
function ensureContentsDeserialized(mutableMessage: ISequencedDocumentMessage): void {
|
|
94
112
|
// back-compat: ADO #1385: eventually should become unconditional, but only for runtime messages!
|
|
95
113
|
// System message may have no contents, or in some cases (mostly for back-compat) they may have actual objects.
|
|
96
114
|
// Old ops may contain empty string (I assume noops).
|
|
97
|
-
if (typeof
|
|
98
|
-
|
|
115
|
+
if (typeof mutableMessage.contents === "string" && mutableMessage.contents !== "") {
|
|
116
|
+
mutableMessage.contents = JSON.parse(mutableMessage.contents);
|
|
99
117
|
}
|
|
100
|
-
|
|
101
|
-
return message;
|
|
102
|
-
};
|
|
118
|
+
}
|
|
103
119
|
|
|
104
120
|
/**
|
|
105
|
-
* For a given message, it moves the nested
|
|
121
|
+
* For a given message, it moves the nested InboundContainerRuntimeMessage props one level up.
|
|
106
122
|
*
|
|
107
123
|
* The return type illustrates the assumption that the message param
|
|
108
|
-
* becomes a
|
|
109
|
-
* (but there is no runtime validation of the 'type' or 'compatDetails' values)
|
|
124
|
+
* becomes a InboundSequencedContainerRuntimeMessage by the time the function returns
|
|
125
|
+
* (but there is no runtime validation of the 'type' or 'compatDetails' values).
|
|
110
126
|
*/
|
|
111
127
|
function unpack(
|
|
112
128
|
message: ISequencedDocumentMessage,
|
|
113
|
-
): asserts message is
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
129
|
+
): asserts message is InboundSequencedContainerRuntimeMessage {
|
|
130
|
+
// We assume the contents is an InboundContainerRuntimeMessage (the message is "packed")
|
|
131
|
+
const contents = message.contents as InboundContainerRuntimeMessage;
|
|
132
|
+
|
|
133
|
+
// We're going to unpack message in-place (promoting those properties of contents up to message itself)
|
|
134
|
+
const messageUnpacked = message as InboundSequencedContainerRuntimeMessage;
|
|
135
|
+
|
|
136
|
+
messageUnpacked.type = contents.type;
|
|
137
|
+
messageUnpacked.contents = contents.contents;
|
|
138
|
+
if ("compatDetails" in contents) {
|
|
139
|
+
(messageUnpacked as InboundSequencedRecentlyAddedContainerRuntimeMessage).compatDetails =
|
|
140
|
+
contents.compatDetails;
|
|
141
|
+
}
|
|
121
142
|
}
|
|
122
143
|
|
|
123
144
|
/**
|
package/src/packageVersion.ts
CHANGED
|
@@ -11,28 +11,15 @@ import { ICriticalContainerError } from "@fluidframework/container-definitions";
|
|
|
11
11
|
import { ISequencedDocumentMessage } from "@fluidframework/protocol-definitions";
|
|
12
12
|
import { DataProcessingError, ITelemetryLoggerExt } from "@fluidframework/telemetry-utils";
|
|
13
13
|
|
|
14
|
-
import { ContainerMessageType,
|
|
14
|
+
import { ContainerMessageType, InboundSequencedContainerRuntimeMessage } from "./messageTypes";
|
|
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 {
|
|
@@ -69,6 +51,26 @@ export interface IRuntimeStateHandler {
|
|
|
69
51
|
isActiveConnection: () => boolean;
|
|
70
52
|
}
|
|
71
53
|
|
|
54
|
+
/** Union of keys of T */
|
|
55
|
+
type KeysOfUnion<T extends object> = T extends T ? keyof T : never;
|
|
56
|
+
/** *Partial* type all possible combinations of properties and values of union T.
|
|
57
|
+
* This loosens typing allowing access to all possible properties without
|
|
58
|
+
* narrowing.
|
|
59
|
+
*/
|
|
60
|
+
type AnyComboFromUnion<T extends object> = { [P in KeysOfUnion<T>]?: T[P] };
|
|
61
|
+
|
|
62
|
+
function buildPendingMessageContent(
|
|
63
|
+
// AnyComboFromUnion is needed need to gain access to compatDetails that
|
|
64
|
+
// is only defined for some cases.
|
|
65
|
+
message: AnyComboFromUnion<InboundSequencedContainerRuntimeMessage>,
|
|
66
|
+
): string {
|
|
67
|
+
// IMPORTANT: Order matters here, this must match the order of the properties used
|
|
68
|
+
// when submitting the message.
|
|
69
|
+
const { type, contents, compatDetails } = message;
|
|
70
|
+
// Any properties that are not defined, won't be emitted by stringify.
|
|
71
|
+
return JSON.stringify({ type, contents, compatDetails });
|
|
72
|
+
}
|
|
73
|
+
|
|
72
74
|
/**
|
|
73
75
|
* PendingStateManager is responsible for maintaining the messages that have not been sent or have not yet been
|
|
74
76
|
* acknowledged by the server. It also maintains the batch information for both automatically and manually flushed
|
|
@@ -79,13 +81,13 @@ export interface IRuntimeStateHandler {
|
|
|
79
81
|
* It verifies that all the ops are acked, are received in the right order and batch information is correct.
|
|
80
82
|
*/
|
|
81
83
|
export class PendingStateManager implements IDisposable {
|
|
82
|
-
private readonly pendingMessages = new Deque<
|
|
83
|
-
private readonly initialMessages = new Deque<
|
|
84
|
+
private readonly pendingMessages = new Deque<IPendingMessage>();
|
|
85
|
+
private readonly initialMessages = new Deque<IPendingMessage>();
|
|
84
86
|
|
|
85
87
|
/**
|
|
86
88
|
* Sequenced local ops that are saved when stashing since pending ops may depend on them
|
|
87
89
|
*/
|
|
88
|
-
private savedOps:
|
|
90
|
+
private savedOps: IPendingMessage[] = [];
|
|
89
91
|
|
|
90
92
|
private readonly disposeOnce = new Lazy<void>(() => {
|
|
91
93
|
this.initialMessages.clear();
|
|
@@ -149,29 +151,8 @@ export class PendingStateManager implements IDisposable {
|
|
|
149
151
|
initialLocalState: IPendingLocalState | undefined,
|
|
150
152
|
private readonly logger: ITelemetryLoggerExt | undefined,
|
|
151
153
|
) {
|
|
152
|
-
/**
|
|
153
|
-
* Convert old local state format to the new format (IPendingMessageOld to IPendingMessageNew)
|
|
154
|
-
* ! TODO: Remove this conversion in "2.0.0-internal.7.0.0" (AB#4763)
|
|
155
|
-
*/
|
|
156
154
|
if (initialLocalState?.pendingStates) {
|
|
157
|
-
|
|
158
|
-
let messageContent = initialState.content;
|
|
159
|
-
if (
|
|
160
|
-
(initialState as IPendingMessageOld).messageType !== undefined &&
|
|
161
|
-
typeof initialState.content !== "string"
|
|
162
|
-
) {
|
|
163
|
-
// Convert IPendingMessageOld to IPendingMessageNew
|
|
164
|
-
messageContent = JSON.stringify({
|
|
165
|
-
type: (initialState as IPendingMessageOld).messageType,
|
|
166
|
-
contents: initialState.content,
|
|
167
|
-
});
|
|
168
|
-
}
|
|
169
|
-
// Note: this object may contain "messageType" prop, but it should not be easily accesible due to interface being used
|
|
170
|
-
this.initialMessages.push({
|
|
171
|
-
...initialState,
|
|
172
|
-
content: messageContent,
|
|
173
|
-
});
|
|
174
|
-
}
|
|
155
|
+
this.initialMessages.push(...initialLocalState.pendingStates);
|
|
175
156
|
}
|
|
176
157
|
}
|
|
177
158
|
|
|
@@ -193,7 +174,7 @@ export class PendingStateManager implements IDisposable {
|
|
|
193
174
|
localOpMetadata: unknown,
|
|
194
175
|
opMetadata: Record<string, unknown> | undefined,
|
|
195
176
|
) {
|
|
196
|
-
const pendingMessage:
|
|
177
|
+
const pendingMessage: IPendingMessage = {
|
|
197
178
|
type: "message",
|
|
198
179
|
clientSequenceNumber: -1, // dummy value (not to be used anywhere)
|
|
199
180
|
referenceSequenceNumber,
|
|
@@ -242,7 +223,7 @@ export class PendingStateManager implements IDisposable {
|
|
|
242
223
|
* the batch information was preserved for batch messages.
|
|
243
224
|
* @param message - The message that got ack'd and needs to be processed.
|
|
244
225
|
*/
|
|
245
|
-
public processPendingLocalMessage(message:
|
|
226
|
+
public processPendingLocalMessage(message: InboundSequencedContainerRuntimeMessage): unknown {
|
|
246
227
|
// Pre-processing part - This may be the start of a batch.
|
|
247
228
|
this.maybeProcessBatchBegin(message);
|
|
248
229
|
|
|
@@ -256,10 +237,7 @@ export class PendingStateManager implements IDisposable {
|
|
|
256
237
|
|
|
257
238
|
this.pendingMessages.shift();
|
|
258
239
|
|
|
259
|
-
|
|
260
|
-
// when submitting the message.
|
|
261
|
-
const { type, contents, compatDetails } = message;
|
|
262
|
-
const messageContent = JSON.stringify({ type, contents, compatDetails });
|
|
240
|
+
const messageContent = buildPendingMessageContent(message);
|
|
263
241
|
|
|
264
242
|
// Stringified content should match
|
|
265
243
|
if (pendingMessage.content !== messageContent) {
|
|
@@ -340,6 +318,7 @@ export class PendingStateManager implements IDisposable {
|
|
|
340
318
|
{
|
|
341
319
|
runtimeVersion: pkgVersion,
|
|
342
320
|
batchClientId:
|
|
321
|
+
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
|
|
343
322
|
this.pendingBatchBeginMessage.clientId === null
|
|
344
323
|
? "null"
|
|
345
324
|
: this.pendingBatchBeginMessage.clientId,
|