@fluidframework/container-runtime 2.20.0 → 2.21.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/.eslintrc.cjs +36 -6
- package/CHANGELOG.md +38 -0
- package/api-report/container-runtime.legacy.alpha.api.md +31 -31
- package/dist/batchTracker.d.ts +1 -2
- package/dist/batchTracker.d.ts.map +1 -1
- package/dist/batchTracker.js +1 -1
- package/dist/batchTracker.js.map +1 -1
- package/dist/blobManager/blobManager.d.ts.map +1 -1
- package/dist/blobManager/blobManager.js +14 -11
- package/dist/blobManager/blobManager.js.map +1 -1
- package/dist/blobManager/blobManagerSnapSum.d.ts +1 -0
- package/dist/blobManager/blobManagerSnapSum.d.ts.map +1 -1
- package/dist/blobManager/blobManagerSnapSum.js +7 -5
- package/dist/blobManager/blobManagerSnapSum.js.map +1 -1
- package/dist/channelCollection.d.ts.map +1 -1
- package/dist/channelCollection.js +63 -41
- package/dist/channelCollection.js.map +1 -1
- package/dist/connectionTelemetry.d.ts +2 -2
- package/dist/connectionTelemetry.d.ts.map +1 -1
- package/dist/connectionTelemetry.js +4 -4
- package/dist/connectionTelemetry.js.map +1 -1
- package/dist/containerRuntime.d.ts +14 -30
- package/dist/containerRuntime.d.ts.map +1 -1
- package/dist/containerRuntime.js +264 -194
- package/dist/containerRuntime.js.map +1 -1
- package/dist/dataStore.js +6 -3
- package/dist/dataStore.js.map +1 -1
- package/dist/dataStoreContext.d.ts.map +1 -1
- package/dist/dataStoreContext.js +16 -11
- package/dist/dataStoreContext.js.map +1 -1
- package/dist/dataStoreContexts.d.ts.map +1 -1
- package/dist/dataStoreContexts.js +1 -0
- package/dist/dataStoreContexts.js.map +1 -1
- package/dist/deltaScheduler.d.ts.map +1 -1
- package/dist/deltaScheduler.js +5 -5
- package/dist/deltaScheduler.js.map +1 -1
- package/dist/gc/garbageCollection.d.ts.map +1 -1
- package/dist/gc/garbageCollection.js +36 -14
- package/dist/gc/garbageCollection.js.map +1 -1
- package/dist/gc/gcConfigs.d.ts.map +1 -1
- package/dist/gc/gcConfigs.js +2 -0
- package/dist/gc/gcConfigs.js.map +1 -1
- package/dist/gc/gcDefinitions.d.ts +8 -0
- package/dist/gc/gcDefinitions.d.ts.map +1 -1
- package/dist/gc/gcDefinitions.js +1 -0
- package/dist/gc/gcDefinitions.js.map +1 -1
- package/dist/gc/gcHelpers.d.ts.map +1 -1
- package/dist/gc/gcHelpers.js +8 -5
- package/dist/gc/gcHelpers.js.map +1 -1
- package/dist/gc/gcSummaryStateTracker.d.ts.map +1 -1
- package/dist/gc/gcSummaryStateTracker.js +2 -1
- package/dist/gc/gcSummaryStateTracker.js.map +1 -1
- package/dist/gc/gcTelemetry.d.ts.map +1 -1
- package/dist/gc/gcTelemetry.js +29 -15
- package/dist/gc/gcTelemetry.js.map +1 -1
- package/dist/inboundBatchAggregator.js +3 -3
- package/dist/inboundBatchAggregator.js.map +1 -1
- package/dist/layerCompatState.d.ts +19 -0
- package/dist/layerCompatState.d.ts.map +1 -0
- package/dist/layerCompatState.js +64 -0
- package/dist/layerCompatState.js.map +1 -0
- package/dist/messageTypes.d.ts.map +1 -1
- package/dist/messageTypes.js.map +1 -1
- package/dist/opLifecycle/duplicateBatchDetector.js +2 -2
- package/dist/opLifecycle/duplicateBatchDetector.js.map +1 -1
- package/dist/opLifecycle/opCompressor.d.ts +3 -2
- package/dist/opLifecycle/opCompressor.d.ts.map +1 -1
- package/dist/opLifecycle/opCompressor.js +13 -19
- package/dist/opLifecycle/opCompressor.js.map +1 -1
- package/dist/opLifecycle/opDecompressor.d.ts +3 -0
- package/dist/opLifecycle/opDecompressor.d.ts.map +1 -1
- package/dist/opLifecycle/opDecompressor.js +4 -1
- package/dist/opLifecycle/opDecompressor.js.map +1 -1
- package/dist/opLifecycle/opGroupingManager.d.ts +1 -1
- package/dist/opLifecycle/opGroupingManager.d.ts.map +1 -1
- package/dist/opLifecycle/opGroupingManager.js +5 -3
- package/dist/opLifecycle/opGroupingManager.js.map +1 -1
- package/dist/opLifecycle/opSplitter.d.ts +13 -10
- package/dist/opLifecycle/opSplitter.d.ts.map +1 -1
- package/dist/opLifecycle/opSplitter.js +14 -11
- package/dist/opLifecycle/opSplitter.js.map +1 -1
- package/dist/opLifecycle/outbox.d.ts +3 -3
- package/dist/opLifecycle/outbox.d.ts.map +1 -1
- package/dist/opLifecycle/outbox.js +11 -15
- package/dist/opLifecycle/outbox.js.map +1 -1
- package/dist/opLifecycle/remoteMessageProcessor.d.ts.map +1 -1
- package/dist/opLifecycle/remoteMessageProcessor.js +3 -1
- 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 +3 -4
- package/dist/pendingStateManager.d.ts.map +1 -1
- package/dist/pendingStateManager.js +11 -10
- package/dist/pendingStateManager.js.map +1 -1
- package/dist/summary/documentSchema.d.ts +7 -0
- package/dist/summary/documentSchema.d.ts.map +1 -1
- package/dist/summary/documentSchema.js +6 -4
- package/dist/summary/documentSchema.js.map +1 -1
- package/dist/summary/orderedClientElection.d.ts +1 -0
- package/dist/summary/orderedClientElection.d.ts.map +1 -1
- package/dist/summary/orderedClientElection.js +13 -11
- package/dist/summary/orderedClientElection.js.map +1 -1
- package/dist/summary/runWhileConnectedCoordinator.d.ts +1 -0
- package/dist/summary/runWhileConnectedCoordinator.d.ts.map +1 -1
- package/dist/summary/runWhileConnectedCoordinator.js +7 -2
- package/dist/summary/runWhileConnectedCoordinator.js.map +1 -1
- package/dist/summary/runningSummarizer.d.ts +2 -2
- package/dist/summary/runningSummarizer.d.ts.map +1 -1
- package/dist/summary/runningSummarizer.js +38 -17
- 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 +18 -9
- package/dist/summary/summarizer.js.map +1 -1
- package/dist/summary/summarizerClientElection.d.ts.map +1 -1
- package/dist/summary/summarizerClientElection.js +1 -0
- package/dist/summary/summarizerClientElection.js.map +1 -1
- package/dist/summary/summarizerHeuristics.js +1 -1
- package/dist/summary/summarizerHeuristics.js.map +1 -1
- package/dist/summary/summarizerNode/index.d.ts.map +1 -1
- package/dist/summary/summarizerNode/index.js.map +1 -1
- package/dist/summary/summarizerNode/summarizerNode.d.ts.map +1 -1
- package/dist/summary/summarizerNode/summarizerNode.js +30 -31
- package/dist/summary/summarizerNode/summarizerNode.js.map +1 -1
- package/dist/summary/summarizerNode/summarizerNodeWithGc.d.ts +1 -1
- package/dist/summary/summarizerNode/summarizerNodeWithGc.js +3 -3
- package/dist/summary/summarizerNode/summarizerNodeWithGc.js.map +1 -1
- package/dist/summary/summarizerTypes.d.ts +7 -0
- package/dist/summary/summarizerTypes.d.ts.map +1 -1
- package/dist/summary/summarizerTypes.js.map +1 -1
- package/dist/summary/summaryCollection.d.ts +3 -4
- package/dist/summary/summaryCollection.d.ts.map +1 -1
- package/dist/summary/summaryCollection.js +9 -6
- package/dist/summary/summaryCollection.js.map +1 -1
- package/dist/summary/summaryFormat.d.ts +4 -1
- package/dist/summary/summaryFormat.d.ts.map +1 -1
- package/dist/summary/summaryFormat.js +3 -2
- package/dist/summary/summaryFormat.js.map +1 -1
- package/dist/summary/summaryGenerator.d.ts.map +1 -1
- package/dist/summary/summaryGenerator.js +19 -8
- package/dist/summary/summaryGenerator.js.map +1 -1
- package/dist/summary/summaryManager.d.ts.map +1 -1
- package/dist/summary/summaryManager.js +12 -9
- package/dist/summary/summaryManager.js.map +1 -1
- package/lib/batchTracker.d.ts +1 -2
- package/lib/batchTracker.d.ts.map +1 -1
- package/lib/batchTracker.js +2 -2
- package/lib/batchTracker.js.map +1 -1
- package/lib/blobManager/blobManager.d.ts.map +1 -1
- package/lib/blobManager/blobManager.js +14 -11
- package/lib/blobManager/blobManager.js.map +1 -1
- package/lib/blobManager/blobManagerSnapSum.d.ts +1 -0
- package/lib/blobManager/blobManagerSnapSum.d.ts.map +1 -1
- package/lib/blobManager/blobManagerSnapSum.js +7 -5
- package/lib/blobManager/blobManagerSnapSum.js.map +1 -1
- package/lib/channelCollection.d.ts.map +1 -1
- package/lib/channelCollection.js +66 -42
- package/lib/channelCollection.js.map +1 -1
- package/lib/connectionTelemetry.d.ts +2 -2
- package/lib/connectionTelemetry.d.ts.map +1 -1
- package/lib/connectionTelemetry.js +5 -5
- package/lib/connectionTelemetry.js.map +1 -1
- package/lib/containerRuntime.d.ts +14 -30
- package/lib/containerRuntime.d.ts.map +1 -1
- package/lib/containerRuntime.js +271 -196
- package/lib/containerRuntime.js.map +1 -1
- package/lib/dataStore.js +6 -3
- package/lib/dataStore.js.map +1 -1
- package/lib/dataStoreContext.d.ts.map +1 -1
- package/lib/dataStoreContext.js +16 -11
- package/lib/dataStoreContext.js.map +1 -1
- package/lib/dataStoreContexts.d.ts.map +1 -1
- package/lib/dataStoreContexts.js +1 -0
- package/lib/dataStoreContexts.js.map +1 -1
- package/lib/deltaScheduler.d.ts.map +1 -1
- package/lib/deltaScheduler.js +6 -6
- package/lib/deltaScheduler.js.map +1 -1
- package/lib/gc/garbageCollection.d.ts.map +1 -1
- package/lib/gc/garbageCollection.js +39 -15
- package/lib/gc/garbageCollection.js.map +1 -1
- package/lib/gc/gcConfigs.d.ts.map +1 -1
- package/lib/gc/gcConfigs.js +2 -0
- package/lib/gc/gcConfigs.js.map +1 -1
- package/lib/gc/gcDefinitions.d.ts +8 -0
- package/lib/gc/gcDefinitions.d.ts.map +1 -1
- package/lib/gc/gcDefinitions.js +1 -0
- package/lib/gc/gcDefinitions.js.map +1 -1
- package/lib/gc/gcHelpers.d.ts.map +1 -1
- package/lib/gc/gcHelpers.js +8 -5
- package/lib/gc/gcHelpers.js.map +1 -1
- package/lib/gc/gcSummaryStateTracker.d.ts.map +1 -1
- package/lib/gc/gcSummaryStateTracker.js +2 -1
- package/lib/gc/gcSummaryStateTracker.js.map +1 -1
- package/lib/gc/gcTelemetry.d.ts.map +1 -1
- package/lib/gc/gcTelemetry.js +32 -16
- package/lib/gc/gcTelemetry.js.map +1 -1
- package/lib/inboundBatchAggregator.js +4 -4
- package/lib/inboundBatchAggregator.js.map +1 -1
- package/lib/layerCompatState.d.ts +19 -0
- package/lib/layerCompatState.d.ts.map +1 -0
- package/lib/layerCompatState.js +60 -0
- package/lib/layerCompatState.js.map +1 -0
- package/lib/messageTypes.d.ts.map +1 -1
- package/lib/messageTypes.js.map +1 -1
- package/lib/opLifecycle/duplicateBatchDetector.js +2 -2
- package/lib/opLifecycle/duplicateBatchDetector.js.map +1 -1
- package/lib/opLifecycle/opCompressor.d.ts +3 -2
- package/lib/opLifecycle/opCompressor.d.ts.map +1 -1
- package/lib/opLifecycle/opCompressor.js +13 -19
- package/lib/opLifecycle/opCompressor.js.map +1 -1
- package/lib/opLifecycle/opDecompressor.d.ts +3 -0
- package/lib/opLifecycle/opDecompressor.d.ts.map +1 -1
- package/lib/opLifecycle/opDecompressor.js +4 -1
- package/lib/opLifecycle/opDecompressor.js.map +1 -1
- package/lib/opLifecycle/opGroupingManager.d.ts +1 -1
- package/lib/opLifecycle/opGroupingManager.d.ts.map +1 -1
- package/lib/opLifecycle/opGroupingManager.js +5 -3
- package/lib/opLifecycle/opGroupingManager.js.map +1 -1
- package/lib/opLifecycle/opSplitter.d.ts +13 -10
- package/lib/opLifecycle/opSplitter.d.ts.map +1 -1
- package/lib/opLifecycle/opSplitter.js +14 -11
- package/lib/opLifecycle/opSplitter.js.map +1 -1
- package/lib/opLifecycle/outbox.d.ts +3 -3
- package/lib/opLifecycle/outbox.d.ts.map +1 -1
- package/lib/opLifecycle/outbox.js +11 -15
- package/lib/opLifecycle/outbox.js.map +1 -1
- package/lib/opLifecycle/remoteMessageProcessor.d.ts.map +1 -1
- package/lib/opLifecycle/remoteMessageProcessor.js +3 -1
- 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 +3 -4
- package/lib/pendingStateManager.d.ts.map +1 -1
- package/lib/pendingStateManager.js +12 -11
- package/lib/pendingStateManager.js.map +1 -1
- package/lib/summary/documentSchema.d.ts +7 -0
- package/lib/summary/documentSchema.d.ts.map +1 -1
- package/lib/summary/documentSchema.js +6 -4
- package/lib/summary/documentSchema.js.map +1 -1
- package/lib/summary/orderedClientElection.d.ts +1 -0
- package/lib/summary/orderedClientElection.d.ts.map +1 -1
- package/lib/summary/orderedClientElection.js +13 -11
- package/lib/summary/orderedClientElection.js.map +1 -1
- package/lib/summary/runWhileConnectedCoordinator.d.ts +1 -0
- package/lib/summary/runWhileConnectedCoordinator.d.ts.map +1 -1
- package/lib/summary/runWhileConnectedCoordinator.js +7 -2
- package/lib/summary/runWhileConnectedCoordinator.js.map +1 -1
- package/lib/summary/runningSummarizer.d.ts +2 -2
- package/lib/summary/runningSummarizer.d.ts.map +1 -1
- package/lib/summary/runningSummarizer.js +38 -17
- 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.d.ts.map +1 -1
- package/lib/summary/summarizerClientElection.js +1 -0
- package/lib/summary/summarizerClientElection.js.map +1 -1
- package/lib/summary/summarizerHeuristics.js +1 -1
- package/lib/summary/summarizerHeuristics.js.map +1 -1
- package/lib/summary/summarizerNode/index.d.ts.map +1 -1
- package/lib/summary/summarizerNode/index.js.map +1 -1
- package/lib/summary/summarizerNode/summarizerNode.d.ts.map +1 -1
- package/lib/summary/summarizerNode/summarizerNode.js +30 -31
- package/lib/summary/summarizerNode/summarizerNode.js.map +1 -1
- package/lib/summary/summarizerNode/summarizerNodeWithGc.d.ts +1 -1
- package/lib/summary/summarizerNode/summarizerNodeWithGc.js +3 -3
- package/lib/summary/summarizerNode/summarizerNodeWithGc.js.map +1 -1
- package/lib/summary/summarizerTypes.d.ts +7 -0
- package/lib/summary/summarizerTypes.d.ts.map +1 -1
- package/lib/summary/summarizerTypes.js.map +1 -1
- package/lib/summary/summaryCollection.d.ts +3 -4
- package/lib/summary/summaryCollection.d.ts.map +1 -1
- package/lib/summary/summaryCollection.js +9 -6
- package/lib/summary/summaryCollection.js.map +1 -1
- package/lib/summary/summaryFormat.d.ts +4 -1
- package/lib/summary/summaryFormat.d.ts.map +1 -1
- package/lib/summary/summaryFormat.js +2 -2
- package/lib/summary/summaryFormat.js.map +1 -1
- package/lib/summary/summaryGenerator.d.ts.map +1 -1
- package/lib/summary/summaryGenerator.js +19 -8
- package/lib/summary/summaryGenerator.js.map +1 -1
- package/lib/summary/summaryManager.d.ts.map +1 -1
- package/lib/summary/summaryManager.js +12 -9
- package/lib/summary/summaryManager.js.map +1 -1
- package/package.json +21 -43
- package/src/batchTracker.ts +3 -3
- package/src/blobManager/blobManager.ts +16 -14
- package/src/blobManager/blobManagerSnapSum.ts +8 -8
- package/src/channelCollection.ts +63 -44
- package/src/connectionTelemetry.ts +12 -6
- package/src/containerRuntime.ts +306 -235
- package/src/dataStore.ts +6 -3
- package/src/dataStoreContext.ts +16 -16
- package/src/dataStoreContexts.ts +1 -0
- package/src/deltaScheduler.ts +6 -6
- package/src/gc/garbageCollection.ts +47 -20
- package/src/gc/gcConfigs.ts +9 -1
- package/src/gc/gcDefinitions.ts +12 -0
- package/src/gc/gcHelpers.ts +9 -4
- package/src/gc/gcSummaryStateTracker.ts +3 -1
- package/src/gc/gcTelemetry.ts +26 -11
- package/src/inboundBatchAggregator.ts +4 -4
- package/src/layerCompatState.ts +75 -0
- package/src/messageTypes.ts +2 -0
- package/src/opLifecycle/README.md +43 -34
- package/src/opLifecycle/duplicateBatchDetector.ts +2 -2
- package/src/opLifecycle/opCompressor.ts +16 -23
- package/src/opLifecycle/opDecompressor.ts +4 -1
- package/src/opLifecycle/opGroupingManager.ts +5 -4
- package/src/opLifecycle/opSplitter.ts +14 -11
- package/src/opLifecycle/outbox.ts +13 -20
- package/src/opLifecycle/remoteMessageProcessor.ts +3 -1
- package/src/packageVersion.ts +1 -1
- package/src/pendingStateManager.ts +15 -10
- package/src/summary/documentSchema.ts +11 -4
- package/src/summary/orderedClientElection.ts +14 -11
- package/src/summary/runWhileConnectedCoordinator.ts +6 -0
- package/src/summary/runningSummarizer.ts +43 -19
- package/src/summary/summarizer.ts +24 -11
- package/src/summary/summarizerClientElection.ts +2 -0
- package/src/summary/summarizerHeuristics.ts +1 -1
- package/src/summary/summarizerNode/index.ts +1 -0
- package/src/summary/summarizerNode/summarizerNode.ts +32 -31
- package/src/summary/summarizerNode/summarizerNodeWithGc.ts +4 -4
- package/src/summary/summarizerTypes.ts +7 -0
- package/src/summary/summaryCollection.ts +19 -8
- package/src/summary/summaryFormat.ts +10 -5
- package/src/summary/summaryGenerator.ts +25 -10
- package/src/summary/summaryManager.ts +14 -12
- package/container-runtime.test-files.tar +0 -0
package/src/gc/gcTelemetry.ts
CHANGED
|
@@ -14,10 +14,13 @@ import {
|
|
|
14
14
|
} from "@fluidframework/telemetry-utils/internal";
|
|
15
15
|
|
|
16
16
|
import { RuntimeHeaderData } from "../containerRuntime.js";
|
|
17
|
+
// eslint-disable-next-line import/no-deprecated
|
|
17
18
|
import { ICreateContainerMetadata } from "../summary/index.js";
|
|
18
19
|
|
|
19
20
|
import {
|
|
21
|
+
// eslint-disable-next-line import/no-deprecated
|
|
20
22
|
GCFeatureMatrix,
|
|
23
|
+
// eslint-disable-next-line import/no-deprecated
|
|
21
24
|
GCNodeType,
|
|
22
25
|
IGarbageCollectorConfigs,
|
|
23
26
|
UnreferencedState,
|
|
@@ -41,6 +44,7 @@ interface ICommonProps {
|
|
|
41
44
|
/**
|
|
42
45
|
* The event that is logged when unreferenced node is used after a certain time.
|
|
43
46
|
*/
|
|
47
|
+
// eslint-disable-next-line import/no-deprecated
|
|
44
48
|
interface IUnreferencedEventProps extends ICreateContainerMetadata, ICommonProps {
|
|
45
49
|
/**
|
|
46
50
|
* The id that GC uses to track the node. May or may not match id
|
|
@@ -52,11 +56,13 @@ interface IUnreferencedEventProps extends ICreateContainerMetadata, ICommonProps
|
|
|
52
56
|
*/
|
|
53
57
|
id: Tagged<string>;
|
|
54
58
|
fromId?: Tagged<string>;
|
|
59
|
+
// eslint-disable-next-line import/no-deprecated
|
|
55
60
|
type: GCNodeType;
|
|
56
61
|
unrefTime: number;
|
|
57
62
|
age: number;
|
|
58
63
|
// Expanding GC feature matrix. Without doing this, the configs cannot be logged in telemetry directly.
|
|
59
64
|
gcConfigs: Omit<IGarbageCollectorConfigs, "persistedGcFeatureMatrix"> & {
|
|
65
|
+
// eslint-disable-next-line import/no-deprecated
|
|
60
66
|
[K in keyof GCFeatureMatrix]: GCFeatureMatrix[K];
|
|
61
67
|
};
|
|
62
68
|
timeout?: number;
|
|
@@ -123,7 +129,9 @@ export class GCTelemetryTracker {
|
|
|
123
129
|
private readonly mc: MonitoringContext,
|
|
124
130
|
private readonly configs: IGarbageCollectorConfigs,
|
|
125
131
|
private readonly isSummarizerClient: boolean,
|
|
132
|
+
// eslint-disable-next-line import/no-deprecated
|
|
126
133
|
private readonly createContainerMetadata: ICreateContainerMetadata,
|
|
134
|
+
// eslint-disable-next-line import/no-deprecated
|
|
127
135
|
private readonly getNodeType: (nodeId: string) => GCNodeType,
|
|
128
136
|
private readonly getNodeStateTracker: (
|
|
129
137
|
nodeId: string,
|
|
@@ -144,6 +152,7 @@ export class GCTelemetryTracker {
|
|
|
144
152
|
* 2. An event is logged only once per container instance per event per node.
|
|
145
153
|
*/
|
|
146
154
|
private shouldLogNonActiveEvent(
|
|
155
|
+
// eslint-disable-next-line import/no-deprecated
|
|
147
156
|
nodeType: GCNodeType,
|
|
148
157
|
usageType: NodeUsageType,
|
|
149
158
|
nodeStateTracker: UnreferencedStateTracker,
|
|
@@ -153,12 +162,14 @@ export class GCTelemetryTracker {
|
|
|
153
162
|
return false;
|
|
154
163
|
}
|
|
155
164
|
|
|
165
|
+
// eslint-disable-next-line import/no-deprecated
|
|
156
166
|
if (nodeType === GCNodeType.Other) {
|
|
157
167
|
return false;
|
|
158
168
|
}
|
|
159
169
|
|
|
160
170
|
// For sub data store (DDS) nodes, if they are changed, its data store will also be changed,
|
|
161
171
|
// so skip logging to make the telemetry less noisy.
|
|
172
|
+
// eslint-disable-next-line import/no-deprecated
|
|
162
173
|
if (nodeType === GCNodeType.SubDataStore && usageType === "Changed") {
|
|
163
174
|
return false;
|
|
164
175
|
}
|
|
@@ -196,17 +207,21 @@ export class GCTelemetryTracker {
|
|
|
196
207
|
|
|
197
208
|
const timeout = (() => {
|
|
198
209
|
switch (nodeStateTracker?.state) {
|
|
199
|
-
case UnreferencedState.Inactive:
|
|
210
|
+
case UnreferencedState.Inactive: {
|
|
200
211
|
return this.configs.inactiveTimeoutMs;
|
|
201
|
-
|
|
212
|
+
}
|
|
213
|
+
case UnreferencedState.TombstoneReady: {
|
|
202
214
|
return this.configs.tombstoneTimeoutMs;
|
|
203
|
-
|
|
215
|
+
}
|
|
216
|
+
case UnreferencedState.SweepReady: {
|
|
204
217
|
return (
|
|
205
218
|
this.configs.tombstoneTimeoutMs &&
|
|
206
219
|
this.configs.tombstoneTimeoutMs + this.configs.sweepGracePeriodMs
|
|
207
220
|
);
|
|
208
|
-
|
|
221
|
+
}
|
|
222
|
+
default: {
|
|
209
223
|
return undefined;
|
|
224
|
+
}
|
|
210
225
|
}
|
|
211
226
|
})();
|
|
212
227
|
const { persistedGcFeatureMatrix, ...configs } = this.configs;
|
|
@@ -215,9 +230,9 @@ export class GCTelemetryTracker {
|
|
|
215
230
|
type: nodeType,
|
|
216
231
|
unrefTime: nodeStateTracker?.unreferencedTimestampMs ?? -1,
|
|
217
232
|
age:
|
|
218
|
-
nodeStateTracker
|
|
219
|
-
?
|
|
220
|
-
: -
|
|
233
|
+
nodeStateTracker === undefined
|
|
234
|
+
? -1
|
|
235
|
+
: currentReferenceTimestampMs - nodeStateTracker.unreferencedTimestampMs,
|
|
221
236
|
timeout,
|
|
222
237
|
isTombstoned,
|
|
223
238
|
...tagCodeArtifacts({ id: untaggedId, fromId: untaggedFromId }),
|
|
@@ -290,6 +305,7 @@ export class GCTelemetryTracker {
|
|
|
290
305
|
*/
|
|
291
306
|
private logTombstoneUsageTelemetry(
|
|
292
307
|
unrefEventProps: Omit<IUnreferencedEventProps, "state" | "usageType">,
|
|
308
|
+
// eslint-disable-next-line import/no-deprecated
|
|
293
309
|
nodeType: GCNodeType,
|
|
294
310
|
usageType: NodeUsageType,
|
|
295
311
|
packagePath?: readonly string[],
|
|
@@ -357,6 +373,7 @@ export class GCTelemetryTracker {
|
|
|
357
373
|
for (const route of currentOutboundRoutes) {
|
|
358
374
|
const nodeType = this.getNodeType(route);
|
|
359
375
|
if (
|
|
376
|
+
// eslint-disable-next-line import/no-deprecated
|
|
360
377
|
(nodeType === GCNodeType.DataStore || nodeType === GCNodeType.Blob) &&
|
|
361
378
|
!nodeId.startsWith(route) &&
|
|
362
379
|
!previousRoutes.includes(route) &&
|
|
@@ -411,10 +428,8 @@ export class GCTelemetryTracker {
|
|
|
411
428
|
const active =
|
|
412
429
|
nodeStateTracker === undefined || nodeStateTracker.state === UnreferencedState.Active;
|
|
413
430
|
if ((usageType === "Revived") === active) {
|
|
414
|
-
const pkg = await this.getNodePackagePath(
|
|
415
|
-
const fromPkg =
|
|
416
|
-
? await this.getNodePackagePath(eventProps.fromId.value)
|
|
417
|
-
: undefined;
|
|
431
|
+
const pkg = await this.getNodePackagePath(id.value);
|
|
432
|
+
const fromPkg = fromId ? await this.getNodePackagePath(fromId.value) : undefined;
|
|
418
433
|
const event = {
|
|
419
434
|
eventName: `${state}Object_${usageType}`,
|
|
420
435
|
id,
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Licensed under the MIT License.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import {
|
|
6
|
+
import { performanceNow } from "@fluid-internal/client-utils";
|
|
7
7
|
import { IDeltaManagerFull } from "@fluidframework/container-definitions/internal";
|
|
8
8
|
import { assert } from "@fluidframework/core-utils/internal";
|
|
9
9
|
import { ISequencedDocumentMessage } from "@fluidframework/driver-definitions/internal";
|
|
@@ -114,7 +114,7 @@ export class InboundBatchAggregator {
|
|
|
114
114
|
*/
|
|
115
115
|
private readonly trackPending = (message: ISequencedDocumentMessage): void => {
|
|
116
116
|
assert(
|
|
117
|
-
this.deltaManager.inbound.length
|
|
117
|
+
this.deltaManager.inbound.length > 0,
|
|
118
118
|
0x298 /* "we have something in the queue that generates this event" */,
|
|
119
119
|
);
|
|
120
120
|
|
|
@@ -227,14 +227,14 @@ export class InboundBatchAggregator {
|
|
|
227
227
|
private pauseQueue(): void {
|
|
228
228
|
assert(!this.localPaused, 0x297 /* "always called from resumed state" */);
|
|
229
229
|
this.localPaused = true;
|
|
230
|
-
this.timePaused =
|
|
230
|
+
this.timePaused = performanceNow();
|
|
231
231
|
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
232
232
|
this.deltaManager.inbound.pause();
|
|
233
233
|
}
|
|
234
234
|
|
|
235
235
|
private resumeQueue(startBatch: number, messageEndBatch: ISequencedDocumentMessage): void {
|
|
236
236
|
const endBatch = messageEndBatch.sequenceNumber;
|
|
237
|
-
const duration = this.localPaused ?
|
|
237
|
+
const duration = this.localPaused ? performanceNow() - this.timePaused : undefined;
|
|
238
238
|
|
|
239
239
|
this.batchCount++;
|
|
240
240
|
if (this.batchCount % 1000 === 1) {
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* Copyright (c) Microsoft Corporation and contributors. All rights reserved.
|
|
3
|
+
* Licensed under the MIT License.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import {
|
|
7
|
+
checkLayerCompatibility,
|
|
8
|
+
type ILayerCompatDetails,
|
|
9
|
+
type ILayerCompatSupportRequirements,
|
|
10
|
+
} from "@fluid-internal/client-utils";
|
|
11
|
+
import type { ICriticalContainerError } from "@fluidframework/container-definitions";
|
|
12
|
+
import { UsageError } from "@fluidframework/telemetry-utils/internal";
|
|
13
|
+
|
|
14
|
+
import { pkgVersion } from "./packageVersion.js";
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Runtime's compatibility details that is exposed to the Loader layer.
|
|
18
|
+
*/
|
|
19
|
+
export const RuntimeCompatDetails: ILayerCompatDetails = {
|
|
20
|
+
/**
|
|
21
|
+
* The package version of the Runtime layer.
|
|
22
|
+
*/
|
|
23
|
+
pkgVersion,
|
|
24
|
+
/**
|
|
25
|
+
* The current generation of the Runtime layer.
|
|
26
|
+
*/
|
|
27
|
+
generation: 1,
|
|
28
|
+
/**
|
|
29
|
+
* The features supported by the Runtime layer across the Runtime / Loader boundary.
|
|
30
|
+
*/
|
|
31
|
+
supportedFeatures: new Set<string>(),
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* The requirements that the Loader layer must meet to be compatible with this Runtime.
|
|
36
|
+
*/
|
|
37
|
+
export const LoaderSupportRequirements: ILayerCompatSupportRequirements = {
|
|
38
|
+
/**
|
|
39
|
+
* Minimum generation that Loader must be at to be compatible with Runtime.
|
|
40
|
+
*/
|
|
41
|
+
minSupportedGeneration: 0,
|
|
42
|
+
/**
|
|
43
|
+
* The features that the Loader must support to be compatible with Runtime. Note that 0 is used here for
|
|
44
|
+
* Loader layers before the introduction of the layer compatibility enforcement.
|
|
45
|
+
*/
|
|
46
|
+
requiredFeatures: [],
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Validates that the Loader layer is compatible with this Runtime.
|
|
51
|
+
*/
|
|
52
|
+
export function validateLoaderCompatibility(
|
|
53
|
+
maybeLoaderCompatDetails: ILayerCompatDetails | undefined,
|
|
54
|
+
disposeFn: (error?: ICriticalContainerError) => void,
|
|
55
|
+
): void {
|
|
56
|
+
const layerCheckResult = checkLayerCompatibility(
|
|
57
|
+
LoaderSupportRequirements,
|
|
58
|
+
maybeLoaderCompatDetails,
|
|
59
|
+
);
|
|
60
|
+
if (!layerCheckResult.isCompatible) {
|
|
61
|
+
const error = new UsageError("Runtime is not compatible with Loader", {
|
|
62
|
+
errorDetails: JSON.stringify({
|
|
63
|
+
runtimeVersion: RuntimeCompatDetails.pkgVersion,
|
|
64
|
+
loaderVersion: maybeLoaderCompatDetails?.pkgVersion,
|
|
65
|
+
runtimeGeneration: RuntimeCompatDetails.generation,
|
|
66
|
+
loaderGeneration: maybeLoaderCompatDetails?.generation,
|
|
67
|
+
minSupportedGeneration: LoaderSupportRequirements.minSupportedGeneration,
|
|
68
|
+
isGenerationCompatible: layerCheckResult.isGenerationCompatible,
|
|
69
|
+
unsupportedFeatures: layerCheckResult.unsupportedFeatures,
|
|
70
|
+
}),
|
|
71
|
+
});
|
|
72
|
+
disposeFn(error);
|
|
73
|
+
throw error;
|
|
74
|
+
}
|
|
75
|
+
}
|
package/src/messageTypes.ts
CHANGED
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
import { IDataStoreAliasMessage } from "./dataStore.js";
|
|
15
15
|
import { GarbageCollectionMessage } from "./gc/index.js";
|
|
16
16
|
import { IChunkedOp } from "./opLifecycle/index.js";
|
|
17
|
+
// eslint-disable-next-line import/no-deprecated
|
|
17
18
|
import { IDocumentSchemaChangeMessage } from "./summary/index.js";
|
|
18
19
|
|
|
19
20
|
/**
|
|
@@ -114,6 +115,7 @@ export type ContainerRuntimeGCMessage = TypedContainerRuntimeMessage<
|
|
|
114
115
|
>;
|
|
115
116
|
export type ContainerRuntimeDocumentSchemaMessage = TypedContainerRuntimeMessage<
|
|
116
117
|
ContainerMessageType.DocumentSchemaChange,
|
|
118
|
+
// eslint-disable-next-line import/no-deprecated
|
|
117
119
|
IDocumentSchemaChangeMessage
|
|
118
120
|
>;
|
|
119
121
|
|
|
@@ -2,21 +2,23 @@
|
|
|
2
2
|
|
|
3
3
|
## Table of contents
|
|
4
4
|
|
|
5
|
-
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
-
|
|
11
|
-
|
|
12
|
-
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
5
|
+
- [Configs and feature gates for solving the 1MB limit.](#configs-and-feature-gates-for-solving-the-1mb-limit)
|
|
6
|
+
- [Table of contents](#table-of-contents)
|
|
7
|
+
- [Introduction](#introduction)
|
|
8
|
+
- [How batching works](#how-batching-works)
|
|
9
|
+
- [Grouped batching](#grouped-batching)
|
|
10
|
+
- [Changes in op semantics](#changes-in-op-semantics)
|
|
11
|
+
- [Compression](#compression)
|
|
12
|
+
- [Only single-message batches are compressed](#only-single-message-batches-are-compressed)
|
|
13
|
+
- [Chunking for compression](#chunking-for-compression)
|
|
14
|
+
- [Configuration](#configuration)
|
|
15
|
+
- [Note about performance and latency](#note-about-performance-and-latency)
|
|
16
|
+
- [How it works](#how-it-works)
|
|
17
|
+
- [Legacy behavior - How it used to work (Compression+Chunking without Grouped Batching)](#legacy-behavior---how-it-used-to-work-compressionchunking-without-grouped-batching)
|
|
18
|
+
- [IMPORTANT - As of 2.20.0, we no longer compress ungrouped batches, but we do need to read such ops - read on to learn what these legacy ops look like](#important---as-of-2200-we-no-longer-compress-ungrouped-batches-but-we-do-need-to-read-such-ops---read-on-to-learn-what-these-legacy-ops-look-like)
|
|
19
|
+
- [How the overall op flow works](#how-the-overall-op-flow-works)
|
|
20
|
+
- [Outbound](#outbound)
|
|
21
|
+
- [Inbound](#inbound)
|
|
20
22
|
|
|
21
23
|
## Introduction
|
|
22
24
|
|
|
@@ -56,20 +58,6 @@ What this means is that `FlushMode.Immediate` will send each op in its own paylo
|
|
|
56
58
|
|
|
57
59
|
As `FlushMode.TurnBased` accumulates ops, it is the most vulnerable to run into the 1MB socket limit.
|
|
58
60
|
|
|
59
|
-
## Compression
|
|
60
|
-
|
|
61
|
-
**Compression targets payloads which exceed the max batch size and it is enabled by default.**. The `IContainerRuntimeOptions.compressionOptions` property, of type `ICompressionRuntimeOptions` is the configuration governing how compression works.
|
|
62
|
-
|
|
63
|
-
`ICompressionRuntimeOptions` has two properties:
|
|
64
|
-
|
|
65
|
-
- `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.
|
|
66
|
-
- `compressionAlgorithm` – currently, only `lz4` is supported.
|
|
67
|
-
|
|
68
|
-
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.
|
|
69
|
-
|
|
70
|
-
Compressing a batch yields a batch with the same number of messages. It compresses all the content, shifting the compressed payload into the first op,
|
|
71
|
-
leaving the rest of the batch's messages as empty placeholders to reserve sequence numbers for the compressed messages.
|
|
72
|
-
|
|
73
61
|
## Grouped batching
|
|
74
62
|
|
|
75
63
|
With Grouped Batching enabled (it's on by default), all batch messages are combined under a single "grouped" message _before compression_. Upon receiving this new "grouped" message, the batch messages will be extracted, and they each will be given the same sequence number - that of the parent "grouped" message.
|
|
@@ -78,9 +66,7 @@ The purpose for enabling grouped batching before compression is to eliminate the
|
|
|
78
66
|
|
|
79
67
|
Grouped batching is only relevant for `FlushMode.TurnBased`, since `OpGroupingManagerConfig.opCountThreshold` defaults to 2. 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.
|
|
80
68
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
See [below](#how-grouped-batching-works) for an example.
|
|
69
|
+
See [below](#how-it-works) for an example.
|
|
84
70
|
|
|
85
71
|
### Changes in op semantics
|
|
86
72
|
|
|
@@ -92,6 +78,27 @@ Grouped Batching changed a couple of expectations around message structure and r
|
|
|
92
78
|
- All ops in a batch must also have the same reference sequence number to ensure eventualy consistency of the model. The runtime will "rebase" ops in a batch with different ref sequence number to satisfy that requirement.
|
|
93
79
|
- What causes ops in a single JS turn (and thus in a batch) to have different reference sequence number? "Op reentrancy", where changes are made to a DDS inside a DDS 'onChanged' event handler.
|
|
94
80
|
|
|
81
|
+
## Compression
|
|
82
|
+
|
|
83
|
+
**Compression targets payloads which exceed the max batch size and it is enabled by default.**. The `IContainerRuntimeOptions.compressionOptions` property, of type `ICompressionRuntimeOptions` is the configuration governing how compression works.
|
|
84
|
+
|
|
85
|
+
`ICompressionRuntimeOptions` has two properties:
|
|
86
|
+
|
|
87
|
+
- `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.
|
|
88
|
+
- `compressionAlgorithm` – currently, only `lz4` is supported.
|
|
89
|
+
|
|
90
|
+
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.
|
|
91
|
+
|
|
92
|
+
### Only single-message batches are compressed
|
|
93
|
+
|
|
94
|
+
The batch to compress has to have only one message and it yields a batch with a single message. It compresses all the content, replacing the op's original contents with the compressed payload and the appropriate metadata to indicate it's compressed.
|
|
95
|
+
|
|
96
|
+
Compression is only enabled if Grouped Batching is enabled.
|
|
97
|
+
|
|
98
|
+
Legacy compressed batches could contain multiple messages, compressing all the content and shifting the compressed payload into the first op, leaving the rest of the batch's messages as empty placeholders to reserve sequence numbers for the compressed messages.
|
|
99
|
+
|
|
100
|
+
While it remains possible to read compressed batches with empty ops, the system will no longer create said type of batches.
|
|
101
|
+
|
|
95
102
|
## Chunking for compression
|
|
96
103
|
|
|
97
104
|
**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.
|
|
@@ -200,7 +207,9 @@ Ungrouped batch:
|
|
|
200
207
|
+-----------------+-----------------+-----------------+-----------------+-----------------+
|
|
201
208
|
```
|
|
202
209
|
|
|
203
|
-
## How it
|
|
210
|
+
## Legacy behavior - How it used to work (Compression+Chunking without Grouped Batching)
|
|
211
|
+
|
|
212
|
+
### IMPORTANT - As of 2.20.0, we no longer compress ungrouped batches, but we do need to read such ops - read on to learn what these legacy ops look like
|
|
204
213
|
|
|
205
214
|
If we have a batch with a size larger than the configured minimum required for compression (in the example let’s say it’s 850 bytes), as following:
|
|
206
215
|
|
|
@@ -311,7 +320,7 @@ stateDiagram-v2
|
|
|
311
320
|
groupBatch --> if_compression
|
|
312
321
|
flushInternal --> if_compression
|
|
313
322
|
if_compression --> post
|
|
314
|
-
if_compression --> compress: if compression
|
|
323
|
+
if_compression --> compress: if compression and grouped batching are enabled
|
|
315
324
|
compress --> post
|
|
316
325
|
compress --> opSplitter.split: if the compressed payload is larger than the chunk size
|
|
317
326
|
opSplitter.split --> post
|
|
@@ -85,12 +85,12 @@ export class DuplicateBatchDetector {
|
|
|
85
85
|
* since the batch start has been processed by all clients, and local batches are deduped and the forked client would close.
|
|
86
86
|
*/
|
|
87
87
|
private clearOldBatchIds(msn: number): void {
|
|
88
|
-
this.batchIdsBySeqNum
|
|
88
|
+
for (const [sequenceNumber, batchId] of this.batchIdsBySeqNum) {
|
|
89
89
|
if (sequenceNumber < msn) {
|
|
90
90
|
this.batchIdsBySeqNum.delete(sequenceNumber);
|
|
91
91
|
this.batchIdsAll.delete(batchId);
|
|
92
92
|
}
|
|
93
|
-
}
|
|
93
|
+
}
|
|
94
94
|
}
|
|
95
95
|
|
|
96
96
|
/**
|
|
@@ -34,13 +34,14 @@ export class OpCompressor {
|
|
|
34
34
|
* Combines the contents of the batch into a single JSON string and compresses it, putting
|
|
35
35
|
* the resulting string as the first message of the batch. The rest of the messages are
|
|
36
36
|
* empty placeholders to reserve sequence numbers.
|
|
37
|
+
* This should only take a single message batch and compress it.
|
|
37
38
|
* @param batch - The batch to compress
|
|
38
39
|
* @returns A batch of the same length as the input batch, containing a single compressed message followed by empty placeholders
|
|
39
40
|
*/
|
|
40
|
-
public compressBatch(batch: IBatch): IBatch {
|
|
41
|
+
public compressBatch(batch: IBatch): IBatch<[BatchMessage]> {
|
|
41
42
|
assert(
|
|
42
|
-
batch.contentSizeInBytes > 0 && batch.messages.length
|
|
43
|
-
0x5a4 /* Batch should not be empty */,
|
|
43
|
+
batch.contentSizeInBytes > 0 && batch.messages.length === 1,
|
|
44
|
+
0x5a4 /* Batch should not be empty and should contain a single message */,
|
|
44
45
|
);
|
|
45
46
|
|
|
46
47
|
const compressionStart = Date.now();
|
|
@@ -49,24 +50,16 @@ export class OpCompressor {
|
|
|
49
50
|
const compressedContent = IsoBuffer.from(compressedContents).toString("base64");
|
|
50
51
|
const duration = Date.now() - compressionStart;
|
|
51
52
|
|
|
52
|
-
const messages: BatchMessage
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
53
|
+
const messages: [BatchMessage] = [
|
|
54
|
+
{
|
|
55
|
+
...batch.messages[0],
|
|
56
|
+
contents: JSON.stringify({ packedContents: compressedContent }),
|
|
57
|
+
metadata: batch.messages[0].metadata,
|
|
58
|
+
compression: CompressionAlgorithms.lz4,
|
|
59
|
+
},
|
|
60
|
+
];
|
|
59
61
|
|
|
60
|
-
|
|
61
|
-
for (const message of batch.messages.slice(1)) {
|
|
62
|
-
messages.push({
|
|
63
|
-
localOpMetadata: message.localOpMetadata,
|
|
64
|
-
metadata: message.metadata,
|
|
65
|
-
referenceSequenceNumber: message.referenceSequenceNumber,
|
|
66
|
-
});
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
const compressedBatch: IBatch = {
|
|
62
|
+
const compressedBatch = {
|
|
70
63
|
contentSizeInBytes: compressedContent.length,
|
|
71
64
|
messages,
|
|
72
65
|
referenceSequenceNumber: batch.referenceSequenceNumber,
|
|
@@ -93,8 +86,8 @@ export class OpCompressor {
|
|
|
93
86
|
try {
|
|
94
87
|
// Yields a valid JSON array, since each message.contents is already serialized to JSON
|
|
95
88
|
return `[${batch.messages.map(({ contents }) => contents).join(",")}]`;
|
|
96
|
-
} catch (
|
|
97
|
-
if ((
|
|
89
|
+
} catch (newError: unknown) {
|
|
90
|
+
if ((newError as Partial<Error>).message === "Invalid string length") {
|
|
98
91
|
// This is how JSON.stringify signals that
|
|
99
92
|
// the content size exceeds its capacity
|
|
100
93
|
const error = new UsageError("Payload too large");
|
|
@@ -109,7 +102,7 @@ export class OpCompressor {
|
|
|
109
102
|
throw error;
|
|
110
103
|
}
|
|
111
104
|
|
|
112
|
-
throw
|
|
105
|
+
throw newError;
|
|
113
106
|
}
|
|
114
107
|
}
|
|
115
108
|
}
|
|
@@ -30,6 +30,9 @@ interface IPackedContentsContents {
|
|
|
30
30
|
* 2. Messages in the middle of a compressed batch will have neither batch metadata nor the compression property set
|
|
31
31
|
* 3. The final message of a batch will have batch metadata set to false
|
|
32
32
|
* 4. An individually compressed op will have undefined batch metadata and compression set to true
|
|
33
|
+
*
|
|
34
|
+
* Compressed batches from current code are always a single message but this class needs to handle a legacy compressed batch with multiple messages
|
|
35
|
+
* because we need that functionality for back compat.
|
|
33
36
|
*/
|
|
34
37
|
export class OpDecompressor {
|
|
35
38
|
private activeBatch = false;
|
|
@@ -79,7 +82,7 @@ export class OpDecompressor {
|
|
|
79
82
|
});
|
|
80
83
|
return true;
|
|
81
84
|
}
|
|
82
|
-
} catch
|
|
85
|
+
} catch {
|
|
83
86
|
return false;
|
|
84
87
|
}
|
|
85
88
|
|
|
@@ -40,7 +40,6 @@ export function isGroupedBatch(op: ISequencedDocumentMessage): boolean {
|
|
|
40
40
|
|
|
41
41
|
export interface OpGroupingManagerConfig {
|
|
42
42
|
readonly groupedBatchingEnabled: boolean;
|
|
43
|
-
readonly opCountThreshold: number;
|
|
44
43
|
}
|
|
45
44
|
|
|
46
45
|
export class OpGroupingManager {
|
|
@@ -102,7 +101,6 @@ export class OpGroupingManager {
|
|
|
102
101
|
this.logger.sendTelemetryEvent({
|
|
103
102
|
eventName: "GroupLargeBatch",
|
|
104
103
|
length: batch.messages.length,
|
|
105
|
-
threshold: this.config.opCountThreshold,
|
|
106
104
|
reentrant: batch.hasReentrantOps,
|
|
107
105
|
referenceSequenceNumber: batch.messages[0].referenceSequenceNumber,
|
|
108
106
|
});
|
|
@@ -159,10 +157,13 @@ export class OpGroupingManager {
|
|
|
159
157
|
return (
|
|
160
158
|
// Grouped batching must be enabled
|
|
161
159
|
this.config.groupedBatchingEnabled &&
|
|
162
|
-
// The number of ops in the batch must
|
|
160
|
+
// The number of ops in the batch must be 2 or more
|
|
163
161
|
// or be empty (to allow for empty batches to be grouped)
|
|
164
|
-
|
|
162
|
+
batch.messages.length !== 1
|
|
165
163
|
// Support for reentrant batches will be on by default
|
|
166
164
|
);
|
|
167
165
|
}
|
|
166
|
+
public groupedBatchingEnabled(): boolean {
|
|
167
|
+
return this.config.groupedBatchingEnabled;
|
|
168
|
+
}
|
|
168
169
|
}
|
|
@@ -99,25 +99,28 @@ export class OpSplitter {
|
|
|
99
99
|
* Splits the first op of a compressed batch in chunks, sends the chunks separately and
|
|
100
100
|
* returns a new batch composed of the last chunk and the rest of the ops in the original batch.
|
|
101
101
|
*
|
|
102
|
-
* A compressed batch is formed by one large op at the first position
|
|
103
|
-
* which are used in order to reserve the sequence numbers for when the first op gets unrolled into the original
|
|
104
|
-
* uncompressed ops at ingestion in the runtime.
|
|
102
|
+
* A compressed batch is formed by one large op at the first position.
|
|
105
103
|
*
|
|
106
|
-
* If the
|
|
104
|
+
* If the op is too large, it can be chunked (split into smaller op) which can be sent individually over the wire
|
|
107
105
|
* and accumulate at ingestion, until the last op in the chunk is processed, when the original op is unrolled.
|
|
108
106
|
*
|
|
109
|
-
* This method will send the first N - 1 chunks separately and use the last chunk as the first message in the result batch
|
|
110
|
-
*
|
|
111
|
-
*
|
|
112
|
-
* (as it is the last chunk).
|
|
107
|
+
* This method will send the first N - 1 chunks separately and use the last chunk as the first message in the result batch.
|
|
108
|
+
* This will ensure that the batch semantics of the original (non-compressed) batch are preserved, as the original chunked op
|
|
109
|
+
* will be unrolled by the runtime when the first message in the batch is processed (as it is the last chunk).
|
|
113
110
|
*
|
|
114
|
-
* To
|
|
111
|
+
* To handle legacy compressed batches with empty placeholders this method can attach the empty placeholder ops at the end
|
|
112
|
+
* of the result batch, ensuring that the batch semantics are preserved.
|
|
113
|
+
*
|
|
114
|
+
* To illustrate the current functionality, if the input is `[largeOp]`, `largeOp` will be split into `[chunk1, chunk2, chunk3, chunk4]`.
|
|
115
|
+
* `chunk1`, `chunk2` and `chunk3` will be sent individually and `[chunk4]` will be returned.
|
|
116
|
+
*
|
|
117
|
+
* With the legacy code, if the input is `[largeOp, emptyOp, emptyOp]`, `largeOp` will be split into `[chunk1, chunk2, chunk3, chunk4]`.
|
|
115
118
|
* `chunk1`, `chunk2` and `chunk3` will be sent individually and `[chunk4, emptyOp, emptyOp]` will be returned.
|
|
116
119
|
*
|
|
117
120
|
* @remarks - A side effect here is that 1 or more chunks are queued immediately for sending in next JS turn.
|
|
118
121
|
*
|
|
119
122
|
* @param batch - the compressed batch which needs to be processed
|
|
120
|
-
* @returns A
|
|
123
|
+
* @returns A batch with the last chunk of the original message
|
|
121
124
|
*/
|
|
122
125
|
public splitFirstBatchMessage(batch: IBatch): IBatch {
|
|
123
126
|
assert(this.isBatchChunkingEnabled, 0x513 /* Chunking needs to be enabled */);
|
|
@@ -282,7 +285,7 @@ export const splitOp = (
|
|
|
282
285
|
for (let chunkId = 1; chunkId <= chunkCount; chunkId++) {
|
|
283
286
|
const chunk: IChunkedOp = {
|
|
284
287
|
chunkId,
|
|
285
|
-
contents: op.contents.
|
|
288
|
+
contents: op.contents.slice(offset, offset + chunkSizeInBytes),
|
|
286
289
|
totalChunks: chunkCount,
|
|
287
290
|
};
|
|
288
291
|
|
|
@@ -135,7 +135,9 @@ export class Outbox {
|
|
|
135
135
|
this.params.config.compressionOptions.minimumBatchSizeInBytes !==
|
|
136
136
|
Number.POSITIVE_INFINITY;
|
|
137
137
|
// We need to allow infinite size batches if we enable compression
|
|
138
|
-
const hardLimit = isCompressionEnabled
|
|
138
|
+
const hardLimit = isCompressionEnabled
|
|
139
|
+
? Number.POSITIVE_INFINITY
|
|
140
|
+
: this.params.config.maxBatchSizeInBytes;
|
|
139
141
|
|
|
140
142
|
this.mainBatch = new BatchManager({ hardLimit, canRebase: true });
|
|
141
143
|
this.blobAttachBatch = new BatchManager({ hardLimit, canRebase: true });
|
|
@@ -229,18 +231,6 @@ export class Outbox {
|
|
|
229
231
|
this.maybeFlushPartialBatch();
|
|
230
232
|
|
|
231
233
|
this.addMessageToBatchManager(this.blobAttachBatch, message);
|
|
232
|
-
|
|
233
|
-
// If compression is enabled, we will always successfully receive
|
|
234
|
-
// blobAttach ops and compress then send them at the next JS turn, regardless
|
|
235
|
-
// of the overall size of the accumulated ops in the batch.
|
|
236
|
-
// However, it is more efficient to flush these ops faster, preferably
|
|
237
|
-
// after they reach a size which would benefit from compression.
|
|
238
|
-
if (
|
|
239
|
-
this.blobAttachBatch.contentSizeInBytes >=
|
|
240
|
-
this.params.config.compressionOptions.minimumBatchSizeInBytes
|
|
241
|
-
) {
|
|
242
|
-
this.flushInternal(this.blobAttachBatch);
|
|
243
|
-
}
|
|
244
234
|
}
|
|
245
235
|
|
|
246
236
|
public submitIdAllocation(message: BatchMessage): void {
|
|
@@ -357,9 +347,11 @@ export class Outbox {
|
|
|
357
347
|
// If so, do nothing, as pending state manager will resubmit it correctly on reconnect.
|
|
358
348
|
// Because flush() is a task that executes async (on clean stack), we can get here in disconnected state.
|
|
359
349
|
if (this.params.shouldSend()) {
|
|
360
|
-
const processedBatch =
|
|
361
|
-
|
|
362
|
-
|
|
350
|
+
const processedBatch = disableGroupedBatching
|
|
351
|
+
? rawBatch
|
|
352
|
+
: this.compressAndChunkBatch(
|
|
353
|
+
shouldGroup ? this.params.groupingManager.groupBatch(rawBatch) : rawBatch,
|
|
354
|
+
);
|
|
363
355
|
clientSequenceNumber = this.sendBatch(processedBatch);
|
|
364
356
|
assert(
|
|
365
357
|
clientSequenceNumber === undefined || clientSequenceNumber >= 0,
|
|
@@ -420,16 +412,17 @@ export class Outbox {
|
|
|
420
412
|
* @remarks - If chunking happens, a side effect here is that 1 or more chunks are queued immediately for sending in next JS turn.
|
|
421
413
|
*
|
|
422
414
|
* @param batch - Raw or Grouped batch to consider for compression/chunking
|
|
423
|
-
* @returns Either (A) the original batch, (B) a compressed batch (same length as original)
|
|
424
|
-
* or (C) a batch containing the last chunk
|
|
415
|
+
* @returns Either (A) the original batch, (B) a compressed batch (same length as original)
|
|
416
|
+
* or (C) a batch containing the last chunk.
|
|
425
417
|
*/
|
|
426
|
-
private
|
|
418
|
+
private compressAndChunkBatch(batch: IBatch): IBatch {
|
|
427
419
|
if (
|
|
428
420
|
batch.messages.length === 0 ||
|
|
429
421
|
this.params.config.compressionOptions === undefined ||
|
|
430
422
|
this.params.config.compressionOptions.minimumBatchSizeInBytes >
|
|
431
423
|
batch.contentSizeInBytes ||
|
|
432
|
-
this.params.submitBatchFn === undefined
|
|
424
|
+
this.params.submitBatchFn === undefined ||
|
|
425
|
+
!this.params.groupingManager.groupedBatchingEnabled()
|
|
433
426
|
) {
|
|
434
427
|
// Nothing to do if the batch is empty or if compression is disabled or not supported, or if we don't need to compress
|
|
435
428
|
return batch;
|
|
@@ -165,7 +165,9 @@ export class RemoteMessageProcessor {
|
|
|
165
165
|
// We should be awaiting a new batch (batchInProgress false)
|
|
166
166
|
assert(!this.batchInProgress, 0x9d3 /* Grouped batch interrupting another batch */);
|
|
167
167
|
const batchId = asBatchMetadata(message.metadata)?.batchId;
|
|
168
|
-
const groupedMessages = this.opGroupingManager
|
|
168
|
+
const groupedMessages = this.opGroupingManager
|
|
169
|
+
.ungroupOp(message)
|
|
170
|
+
.map((innerMessage) => unpack(innerMessage));
|
|
169
171
|
|
|
170
172
|
return {
|
|
171
173
|
type: "fullBatch",
|
package/src/packageVersion.ts
CHANGED