@fluidframework/container-runtime 2.0.0-dev.5.2.0.169897 → 2.0.0-dev.5.3.2.178189
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 +24 -0
- package/dist/blobManager.d.ts +5 -2
- package/dist/blobManager.d.ts.map +1 -1
- package/dist/blobManager.js +51 -22
- package/dist/blobManager.js.map +1 -1
- package/dist/connectionTelemetry.d.ts.map +1 -1
- package/dist/connectionTelemetry.js +8 -1
- package/dist/connectionTelemetry.js.map +1 -1
- package/dist/containerRuntime.d.ts +11 -0
- package/dist/containerRuntime.d.ts.map +1 -1
- package/dist/containerRuntime.js +59 -10
- package/dist/containerRuntime.js.map +1 -1
- package/dist/dataStoreContext.d.ts +1 -2
- package/dist/dataStoreContext.d.ts.map +1 -1
- package/dist/dataStoreContext.js +4 -3
- package/dist/dataStoreContext.js.map +1 -1
- package/dist/dataStoreContexts.d.ts +2 -1
- package/dist/dataStoreContexts.d.ts.map +1 -1
- package/dist/dataStoreContexts.js +2 -1
- package/dist/dataStoreContexts.js.map +1 -1
- package/dist/dataStores.d.ts +1 -1
- package/dist/dataStores.d.ts.map +1 -1
- package/dist/dataStores.js +2 -1
- package/dist/dataStores.js.map +1 -1
- package/dist/gc/garbageCollection.d.ts.map +1 -1
- package/dist/gc/garbageCollection.js +4 -3
- package/dist/gc/garbageCollection.js.map +1 -1
- package/dist/gc/gcDefinitions.d.ts +0 -1
- package/dist/gc/gcDefinitions.d.ts.map +1 -1
- package/dist/gc/gcDefinitions.js.map +1 -1
- package/dist/gc/gcTelemetry.d.ts +1 -1
- package/dist/gc/gcTelemetry.d.ts.map +1 -1
- package/dist/gc/gcTelemetry.js.map +1 -1
- package/dist/id-compressor/uuidUtilities.d.ts +0 -2
- package/dist/id-compressor/uuidUtilities.d.ts.map +1 -1
- package/dist/id-compressor/uuidUtilities.js +1 -3
- package/dist/id-compressor/uuidUtilities.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/metadata.d.ts +18 -0
- package/dist/metadata.d.ts.map +1 -0
- package/dist/metadata.js +7 -0
- package/dist/metadata.js.map +1 -0
- package/dist/opLifecycle/batchManager.d.ts +2 -1
- package/dist/opLifecycle/batchManager.d.ts.map +1 -1
- package/dist/opLifecycle/batchManager.js +5 -1
- package/dist/opLifecycle/batchManager.js.map +1 -1
- package/dist/opLifecycle/definitions.d.ts +11 -0
- package/dist/opLifecycle/definitions.d.ts.map +1 -1
- package/dist/opLifecycle/definitions.js.map +1 -1
- package/dist/opLifecycle/index.d.ts +1 -1
- package/dist/opLifecycle/index.d.ts.map +1 -1
- package/dist/opLifecycle/index.js +2 -1
- package/dist/opLifecycle/index.js.map +1 -1
- package/dist/opLifecycle/opDecompressor.d.ts.map +1 -1
- package/dist/opLifecycle/opDecompressor.js +14 -8
- 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 +2 -6
- package/dist/opLifecycle/opGroupingManager.js.map +1 -1
- package/dist/opLifecycle/opSplitter.d.ts.map +1 -1
- package/dist/opLifecycle/opSplitter.js +2 -0
- package/dist/opLifecycle/opSplitter.js.map +1 -1
- package/dist/opLifecycle/outbox.d.ts +33 -2
- package/dist/opLifecycle/outbox.d.ts.map +1 -1
- package/dist/opLifecycle/outbox.js +128 -42
- 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 +7 -2
- package/dist/pendingStateManager.d.ts.map +1 -1
- package/dist/pendingStateManager.js +36 -21
- package/dist/pendingStateManager.js.map +1 -1
- package/dist/scheduleManager.d.ts.map +1 -1
- package/dist/scheduleManager.js +8 -2
- package/dist/scheduleManager.js.map +1 -1
- package/dist/summary/index.d.ts +2 -2
- package/dist/summary/index.d.ts.map +1 -1
- package/dist/summary/index.js +2 -1
- package/dist/summary/index.js.map +1 -1
- package/dist/summary/runningSummarizer.d.ts +1 -1
- package/dist/summary/runningSummarizer.d.ts.map +1 -1
- package/dist/summary/runningSummarizer.js.map +1 -1
- package/dist/summary/summarizerNode/index.d.ts +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 +32 -6
- package/dist/summary/summarizerNode/summarizerNode.d.ts.map +1 -1
- package/dist/summary/summarizerNode/summarizerNode.js +90 -22
- package/dist/summary/summarizerNode/summarizerNode.js.map +1 -1
- package/dist/summary/summarizerNode/summarizerNodeUtils.d.ts +22 -2
- package/dist/summary/summarizerNode/summarizerNodeUtils.d.ts.map +1 -1
- package/dist/summary/summarizerNode/summarizerNodeUtils.js.map +1 -1
- package/dist/summary/summarizerNode/summarizerNodeWithGc.d.ts +17 -2
- package/dist/summary/summarizerNode/summarizerNodeWithGc.d.ts.map +1 -1
- package/dist/summary/summarizerNode/summarizerNodeWithGc.js +59 -22
- package/dist/summary/summarizerNode/summarizerNodeWithGc.js.map +1 -1
- package/dist/summary/summarizerTypes.d.ts +9 -2
- package/dist/summary/summarizerTypes.d.ts.map +1 -1
- package/dist/summary/summarizerTypes.js.map +1 -1
- package/dist/summary/summaryCollection.d.ts +2 -1
- package/dist/summary/summaryCollection.d.ts.map +1 -1
- package/dist/summary/summaryCollection.js +4 -0
- package/dist/summary/summaryCollection.js.map +1 -1
- package/dist/summary/summaryFormat.d.ts +1 -0
- package/dist/summary/summaryFormat.d.ts.map +1 -1
- package/dist/summary/summaryFormat.js +2 -1
- package/dist/summary/summaryFormat.js.map +1 -1
- package/dist/summary/summaryGenerator.d.ts +13 -4
- package/dist/summary/summaryGenerator.d.ts.map +1 -1
- package/dist/summary/summaryGenerator.js +22 -8
- package/dist/summary/summaryGenerator.js.map +1 -1
- package/dist/summary/summaryManager.d.ts +2 -1
- package/dist/summary/summaryManager.d.ts.map +1 -1
- package/dist/summary/summaryManager.js.map +1 -1
- package/lib/blobManager.d.ts +5 -2
- package/lib/blobManager.d.ts.map +1 -1
- package/lib/blobManager.js +51 -22
- package/lib/blobManager.js.map +1 -1
- package/lib/connectionTelemetry.d.ts.map +1 -1
- package/lib/connectionTelemetry.js +8 -1
- package/lib/connectionTelemetry.js.map +1 -1
- package/lib/containerRuntime.d.ts +11 -0
- package/lib/containerRuntime.d.ts.map +1 -1
- package/lib/containerRuntime.js +62 -13
- package/lib/containerRuntime.js.map +1 -1
- package/lib/dataStoreContext.d.ts +1 -2
- package/lib/dataStoreContext.d.ts.map +1 -1
- package/lib/dataStoreContext.js +4 -3
- package/lib/dataStoreContext.js.map +1 -1
- package/lib/dataStoreContexts.d.ts +2 -1
- package/lib/dataStoreContexts.d.ts.map +1 -1
- package/lib/dataStoreContexts.js +2 -1
- package/lib/dataStoreContexts.js.map +1 -1
- package/lib/dataStores.d.ts +1 -1
- package/lib/dataStores.d.ts.map +1 -1
- package/lib/dataStores.js +2 -1
- package/lib/dataStores.js.map +1 -1
- package/lib/gc/garbageCollection.d.ts.map +1 -1
- package/lib/gc/garbageCollection.js +2 -1
- package/lib/gc/garbageCollection.js.map +1 -1
- package/lib/gc/gcDefinitions.d.ts +0 -1
- package/lib/gc/gcDefinitions.d.ts.map +1 -1
- package/lib/gc/gcDefinitions.js.map +1 -1
- package/lib/gc/gcTelemetry.d.ts +1 -1
- package/lib/gc/gcTelemetry.d.ts.map +1 -1
- package/lib/gc/gcTelemetry.js.map +1 -1
- package/lib/id-compressor/uuidUtilities.d.ts +0 -2
- package/lib/id-compressor/uuidUtilities.d.ts.map +1 -1
- package/lib/id-compressor/uuidUtilities.js +1 -3
- package/lib/id-compressor/uuidUtilities.js.map +1 -1
- package/lib/index.d.ts +1 -1
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js.map +1 -1
- package/lib/metadata.d.ts +18 -0
- package/lib/metadata.d.ts.map +1 -0
- package/lib/metadata.js +6 -0
- package/lib/metadata.js.map +1 -0
- package/lib/opLifecycle/batchManager.d.ts +2 -1
- package/lib/opLifecycle/batchManager.d.ts.map +1 -1
- package/lib/opLifecycle/batchManager.js +5 -1
- package/lib/opLifecycle/batchManager.js.map +1 -1
- package/lib/opLifecycle/definitions.d.ts +11 -0
- package/lib/opLifecycle/definitions.d.ts.map +1 -1
- package/lib/opLifecycle/definitions.js.map +1 -1
- package/lib/opLifecycle/index.d.ts +1 -1
- package/lib/opLifecycle/index.d.ts.map +1 -1
- package/lib/opLifecycle/index.js +1 -1
- package/lib/opLifecycle/index.js.map +1 -1
- package/lib/opLifecycle/opDecompressor.d.ts.map +1 -1
- package/lib/opLifecycle/opDecompressor.js +14 -8
- 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 +2 -6
- package/lib/opLifecycle/opGroupingManager.js.map +1 -1
- package/lib/opLifecycle/opSplitter.d.ts.map +1 -1
- package/lib/opLifecycle/opSplitter.js +2 -0
- package/lib/opLifecycle/opSplitter.js.map +1 -1
- package/lib/opLifecycle/outbox.d.ts +33 -2
- package/lib/opLifecycle/outbox.d.ts.map +1 -1
- package/lib/opLifecycle/outbox.js +126 -41
- 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 +7 -2
- package/lib/pendingStateManager.d.ts.map +1 -1
- package/lib/pendingStateManager.js +36 -21
- package/lib/pendingStateManager.js.map +1 -1
- package/lib/scheduleManager.d.ts.map +1 -1
- package/lib/scheduleManager.js +8 -2
- package/lib/scheduleManager.js.map +1 -1
- package/lib/summary/index.d.ts +2 -2
- package/lib/summary/index.d.ts.map +1 -1
- package/lib/summary/index.js +1 -1
- package/lib/summary/index.js.map +1 -1
- package/lib/summary/runningSummarizer.d.ts +1 -1
- package/lib/summary/runningSummarizer.d.ts.map +1 -1
- package/lib/summary/runningSummarizer.js.map +1 -1
- package/lib/summary/summarizerNode/index.d.ts +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 +32 -6
- package/lib/summary/summarizerNode/summarizerNode.d.ts.map +1 -1
- package/lib/summary/summarizerNode/summarizerNode.js +90 -22
- package/lib/summary/summarizerNode/summarizerNode.js.map +1 -1
- package/lib/summary/summarizerNode/summarizerNodeUtils.d.ts +22 -2
- package/lib/summary/summarizerNode/summarizerNodeUtils.d.ts.map +1 -1
- package/lib/summary/summarizerNode/summarizerNodeUtils.js.map +1 -1
- package/lib/summary/summarizerNode/summarizerNodeWithGc.d.ts +17 -2
- package/lib/summary/summarizerNode/summarizerNodeWithGc.d.ts.map +1 -1
- package/lib/summary/summarizerNode/summarizerNodeWithGc.js +58 -21
- package/lib/summary/summarizerNode/summarizerNodeWithGc.js.map +1 -1
- package/lib/summary/summarizerTypes.d.ts +9 -2
- package/lib/summary/summarizerTypes.d.ts.map +1 -1
- package/lib/summary/summarizerTypes.js.map +1 -1
- package/lib/summary/summaryCollection.d.ts +2 -1
- package/lib/summary/summaryCollection.d.ts.map +1 -1
- package/lib/summary/summaryCollection.js +4 -0
- package/lib/summary/summaryCollection.js.map +1 -1
- package/lib/summary/summaryFormat.d.ts +1 -0
- package/lib/summary/summaryFormat.d.ts.map +1 -1
- package/lib/summary/summaryFormat.js +2 -1
- package/lib/summary/summaryFormat.js.map +1 -1
- package/lib/summary/summaryGenerator.d.ts +13 -4
- package/lib/summary/summaryGenerator.d.ts.map +1 -1
- package/lib/summary/summaryGenerator.js +20 -7
- package/lib/summary/summaryGenerator.js.map +1 -1
- package/lib/summary/summaryManager.d.ts +2 -1
- package/lib/summary/summaryManager.d.ts.map +1 -1
- package/lib/summary/summaryManager.js.map +1 -1
- package/package.json +20 -19
- package/src/blobManager.ts +68 -27
- package/src/connectionTelemetry.ts +10 -1
- package/src/containerRuntime.ts +74 -17
- package/src/dataStoreContext.ts +12 -5
- package/src/dataStoreContexts.ts +4 -2
- package/src/dataStores.ts +8 -3
- package/src/gc/garbageCollection.ts +2 -1
- package/src/gc/gcDefinitions.ts +0 -1
- package/src/gc/gcTelemetry.ts +1 -1
- package/src/id-compressor/uuidUtilities.ts +1 -4
- package/src/index.ts +2 -0
- package/src/metadata.ts +19 -0
- package/src/opLifecycle/README.md +20 -0
- package/src/opLifecycle/batchManager.ts +9 -1
- package/src/opLifecycle/definitions.ts +11 -0
- package/src/opLifecycle/index.ts +1 -1
- package/src/opLifecycle/opDecompressor.ts +41 -13
- package/src/opLifecycle/opGroupingManager.ts +14 -7
- package/src/opLifecycle/opSplitter.ts +3 -1
- package/src/opLifecycle/outbox.ts +163 -49
- package/src/opLifecycle/remoteMessageProcessor.ts +5 -1
- package/src/packageVersion.ts +1 -1
- package/src/pendingStateManager.ts +56 -41
- package/src/scheduleManager.ts +15 -6
- package/src/summary/index.ts +3 -1
- package/src/summary/runningSummarizer.ts +1 -1
- package/src/summary/summarizerNode/index.ts +1 -0
- package/src/summary/summarizerNode/summarizerNode.ts +107 -25
- package/src/summary/summarizerNode/summarizerNodeUtils.ts +25 -2
- package/src/summary/summarizerNode/summarizerNodeWithGc.ts +63 -20
- package/src/summary/summarizerTypes.ts +12 -2
- package/src/summary/summaryCollection.ts +8 -3
- package/src/summary/summaryFormat.ts +5 -1
- package/src/summary/summaryGenerator.ts +31 -8
- package/src/summary/summaryManager.ts +2 -1
package/src/containerRuntime.ts
CHANGED
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
* Copyright (c) Microsoft Corporation and contributors. All rights reserved.
|
|
3
3
|
* Licensed under the MIT License.
|
|
4
4
|
*/
|
|
5
|
-
import { ITelemetryBaseLogger, ITelemetryGenericEvent } from "@fluidframework/common-definitions";
|
|
6
5
|
import {
|
|
6
|
+
ITelemetryBaseLogger,
|
|
7
|
+
ITelemetryGenericEvent,
|
|
7
8
|
FluidObject,
|
|
8
9
|
IFluidHandle,
|
|
9
10
|
IFluidHandleContext,
|
|
@@ -28,11 +29,11 @@ import {
|
|
|
28
29
|
import {
|
|
29
30
|
assert,
|
|
30
31
|
delay,
|
|
31
|
-
LazyPromise,
|
|
32
32
|
Trace,
|
|
33
33
|
TypedEventEmitter,
|
|
34
34
|
unreachableCase,
|
|
35
35
|
} from "@fluidframework/common-utils";
|
|
36
|
+
import { LazyPromise } from "@fluidframework/core-utils";
|
|
36
37
|
import {
|
|
37
38
|
ChildLogger,
|
|
38
39
|
raiseConnectedEvent,
|
|
@@ -113,7 +114,11 @@ import { v4 as uuid } from "uuid";
|
|
|
113
114
|
import { ContainerFluidHandleContext } from "./containerHandleContext";
|
|
114
115
|
import { FluidDataStoreRegistry } from "./dataStoreRegistry";
|
|
115
116
|
import { ReportOpPerfTelemetry, IPerfSignalReport } from "./connectionTelemetry";
|
|
116
|
-
import {
|
|
117
|
+
import {
|
|
118
|
+
IPendingBatchMessage,
|
|
119
|
+
IPendingLocalState,
|
|
120
|
+
PendingStateManager,
|
|
121
|
+
} from "./pendingStateManager";
|
|
117
122
|
import { pkgVersion } from "./packageVersion";
|
|
118
123
|
import { BlobManager, IBlobManagerLoadInfo, IPendingBlobs } from "./blobManager";
|
|
119
124
|
import { DataStores, getSummaryForDatastores } from "./dataStores";
|
|
@@ -149,6 +154,8 @@ import {
|
|
|
149
154
|
ISummarizerRuntime,
|
|
150
155
|
IRefreshSummaryAckOptions,
|
|
151
156
|
RunWhileConnectedCoordinator,
|
|
157
|
+
IGenerateSummaryTreeResult,
|
|
158
|
+
RetriableSummaryError,
|
|
152
159
|
} from "./summary";
|
|
153
160
|
import { formExponentialFn, Throttler } from "./throttler";
|
|
154
161
|
import {
|
|
@@ -173,8 +180,10 @@ import {
|
|
|
173
180
|
OpSplitter,
|
|
174
181
|
RemoteMessageProcessor,
|
|
175
182
|
OpGroupingManager,
|
|
183
|
+
getLongStack,
|
|
176
184
|
} from "./opLifecycle";
|
|
177
185
|
import { DeltaManagerSummarizerProxy } from "./deltaManagerSummarizerProxy";
|
|
186
|
+
import { IBatchMetadata } from "./metadata";
|
|
178
187
|
|
|
179
188
|
export enum ContainerMessageType {
|
|
180
189
|
// An op to be delivered to store
|
|
@@ -831,6 +840,11 @@ export class ContainerRuntime
|
|
|
831
840
|
}
|
|
832
841
|
|
|
833
842
|
public get closeFn(): (error?: ICriticalContainerError) => void {
|
|
843
|
+
if (this._summarizer !== undefined) {
|
|
844
|
+
// In cases of summarizer, we want to dispose instead since consumer doesn't interact with this container
|
|
845
|
+
return this.disposeFn;
|
|
846
|
+
}
|
|
847
|
+
|
|
834
848
|
// Also call disposeFn to retain functionality of runtime being disposed on close
|
|
835
849
|
return (error?: ICriticalContainerError) => {
|
|
836
850
|
this.context.closeFn(error);
|
|
@@ -952,6 +966,12 @@ export class ContainerRuntime
|
|
|
952
966
|
private readonly disableAttachReorder: boolean | undefined;
|
|
953
967
|
private readonly summaryStateUpdateMethod: string | undefined;
|
|
954
968
|
private readonly closeSummarizerDelayMs: number;
|
|
969
|
+
/**
|
|
970
|
+
* If true, summary generated is validate before uploading it to the server. With single commit summaries,
|
|
971
|
+
* summaries will be accepted once uploaded, so they should be validated before upload. However, this can
|
|
972
|
+
* currently be controlled via a feature flag as its a new functionality.
|
|
973
|
+
*/
|
|
974
|
+
private readonly validateSummaryBeforeUpload: boolean;
|
|
955
975
|
|
|
956
976
|
private readonly defaultTelemetrySignalSampleCount = 100;
|
|
957
977
|
private _perfSignalData: IPerfSignalReport = {
|
|
@@ -1206,7 +1226,6 @@ export class ContainerRuntime
|
|
|
1206
1226
|
getNodePackagePath: async (nodePath: string) => this.getGCNodePackagePath(nodePath),
|
|
1207
1227
|
getLastSummaryTimestampMs: () => this.messageAtLastSummary?.timestamp,
|
|
1208
1228
|
readAndParseBlob: async <T>(id: string) => readAndParse<T>(this.storage, id),
|
|
1209
|
-
getContainerDiagnosticId: () => this.context.id,
|
|
1210
1229
|
// GC runs in summarizer client and needs access to the real (non-proxy) active information. The proxy
|
|
1211
1230
|
// delta manager would always return false for summarizer client.
|
|
1212
1231
|
activeConnection: () => this.innerDeltaManager.active,
|
|
@@ -1299,7 +1318,7 @@ export class ContainerRuntime
|
|
|
1299
1318
|
close: this.closeFn,
|
|
1300
1319
|
connected: () => this.connected,
|
|
1301
1320
|
reSubmit: this.reSubmit.bind(this),
|
|
1302
|
-
|
|
1321
|
+
reSubmitBatch: this.reSubmitBatch.bind(this),
|
|
1303
1322
|
},
|
|
1304
1323
|
pendingRuntimeState?.pending,
|
|
1305
1324
|
);
|
|
@@ -1328,6 +1347,7 @@ export class ContainerRuntime
|
|
|
1328
1347
|
compressionOptions,
|
|
1329
1348
|
maxBatchSizeInBytes: runtimeOptions.maxBatchSizeInBytes,
|
|
1330
1349
|
disablePartialFlush: disablePartialFlush === true,
|
|
1350
|
+
enableGroupedBatching: this.groupedBatchingEnabled,
|
|
1331
1351
|
},
|
|
1332
1352
|
logger: this.mc.logger,
|
|
1333
1353
|
groupingManager: opGroupingManager,
|
|
@@ -1335,6 +1355,9 @@ export class ContainerRuntime
|
|
|
1335
1355
|
referenceSequenceNumber: this.deltaManager.lastSequenceNumber,
|
|
1336
1356
|
clientSequenceNumber: this._processedClientSequenceNumber,
|
|
1337
1357
|
}),
|
|
1358
|
+
reSubmit: this.reSubmit.bind(this),
|
|
1359
|
+
opReentrancy: () => this.ensureNoDataModelChangesCalls > 0,
|
|
1360
|
+
closeContainer: this.closeFn,
|
|
1338
1361
|
});
|
|
1339
1362
|
|
|
1340
1363
|
this.context.quorum.on("removeMember", (clientId: string) => {
|
|
@@ -1342,12 +1365,15 @@ export class ContainerRuntime
|
|
|
1342
1365
|
});
|
|
1343
1366
|
|
|
1344
1367
|
this.summaryStateUpdateMethod = this.mc.config.getString(
|
|
1345
|
-
"Fluid.ContainerRuntime.Test.
|
|
1368
|
+
"Fluid.ContainerRuntime.Test.SummaryStateUpdateMethodV2",
|
|
1346
1369
|
);
|
|
1347
1370
|
const closeSummarizerDelayOverride = this.mc.config.getNumber(
|
|
1348
1371
|
"Fluid.ContainerRuntime.Test.CloseSummarizerDelayOverrideMs",
|
|
1349
1372
|
);
|
|
1350
1373
|
this.closeSummarizerDelayMs = closeSummarizerDelayOverride ?? defaultCloseSummarizerDelayMs;
|
|
1374
|
+
this.validateSummaryBeforeUpload =
|
|
1375
|
+
this.mc.config.getBoolean("Fluid.ContainerRuntime.Test.ValidateSummaryBeforeUpload") ??
|
|
1376
|
+
false;
|
|
1351
1377
|
|
|
1352
1378
|
this.summaryCollection = new SummaryCollection(this.deltaManager, this.logger);
|
|
1353
1379
|
|
|
@@ -2024,7 +2050,7 @@ export class ContainerRuntime
|
|
|
2024
2050
|
local,
|
|
2025
2051
|
type: message.type,
|
|
2026
2052
|
contentType: typeof message.contents,
|
|
2027
|
-
batch: message.metadata?.batch,
|
|
2053
|
+
batch: (message.metadata as IBatchMetadata | undefined)?.batch,
|
|
2028
2054
|
compression: message.compression,
|
|
2029
2055
|
},
|
|
2030
2056
|
);
|
|
@@ -2808,7 +2834,7 @@ export class ContainerRuntime
|
|
|
2808
2834
|
summaryNumber,
|
|
2809
2835
|
...partialStats,
|
|
2810
2836
|
};
|
|
2811
|
-
const generateSummaryData = {
|
|
2837
|
+
const generateSummaryData: Omit<IGenerateSummaryTreeResult, "stage" | "error"> = {
|
|
2812
2838
|
referenceSequenceNumber: summaryRefSeqNum,
|
|
2813
2839
|
minimumSequenceNumber,
|
|
2814
2840
|
summaryTree,
|
|
@@ -2817,6 +2843,21 @@ export class ContainerRuntime
|
|
|
2817
2843
|
forcedFullTree,
|
|
2818
2844
|
} as const;
|
|
2819
2845
|
|
|
2846
|
+
// If validateSummaryBeforeUpload is true, validate that the summary generated by the summarizer nodes is
|
|
2847
|
+
// correct before this summary is uploaded.
|
|
2848
|
+
if (this.validateSummaryBeforeUpload) {
|
|
2849
|
+
const validateResult = this.summarizerNode.validateSummary();
|
|
2850
|
+
if (!validateResult.success) {
|
|
2851
|
+
const { success, ...loggingProps } = validateResult;
|
|
2852
|
+
const error = new RetriableSummaryError(
|
|
2853
|
+
validateResult.reason,
|
|
2854
|
+
validateResult.retryAfterSeconds,
|
|
2855
|
+
{ ...loggingProps },
|
|
2856
|
+
);
|
|
2857
|
+
return { stage: "base", ...generateSummaryData, error };
|
|
2858
|
+
}
|
|
2859
|
+
}
|
|
2860
|
+
|
|
2820
2861
|
continueResult = checkContinue();
|
|
2821
2862
|
if (!continueResult.continue) {
|
|
2822
2863
|
return { stage: "generate", ...generateSummaryData, error: continueResult.error };
|
|
@@ -2894,7 +2935,15 @@ export class ContainerRuntime
|
|
|
2894
2935
|
submitOpDuration: trace.trace().duration,
|
|
2895
2936
|
} as const;
|
|
2896
2937
|
|
|
2897
|
-
|
|
2938
|
+
try {
|
|
2939
|
+
// If validateSummaryBeforeUpload is false, the summary should be validated in this step.
|
|
2940
|
+
this.summarizerNode.completeSummary(
|
|
2941
|
+
handle,
|
|
2942
|
+
!this.validateSummaryBeforeUpload /* validate */,
|
|
2943
|
+
);
|
|
2944
|
+
} catch (error) {
|
|
2945
|
+
return { stage: "upload", ...uploadData, error };
|
|
2946
|
+
}
|
|
2898
2947
|
return submitData;
|
|
2899
2948
|
} finally {
|
|
2900
2949
|
// Cleanup wip summary in case of failure
|
|
@@ -3064,6 +3113,9 @@ export class ContainerRuntime
|
|
|
3064
3113
|
this.disableAttachReorder !== true
|
|
3065
3114
|
) {
|
|
3066
3115
|
this.outbox.submitAttach(message);
|
|
3116
|
+
} else if (type === ContainerMessageType.BlobAttach) {
|
|
3117
|
+
// BlobAttach ops must have their metadata visible and cannot be grouped (see opGroupingManager.ts)
|
|
3118
|
+
this.outbox.submitBlobAttach(message);
|
|
3067
3119
|
} else {
|
|
3068
3120
|
this.outbox.submit(message);
|
|
3069
3121
|
}
|
|
@@ -3157,7 +3209,7 @@ export class ContainerRuntime
|
|
|
3157
3209
|
this.mc.logger.sendTelemetryEvent(
|
|
3158
3210
|
{ eventName: "OpReentry" },
|
|
3159
3211
|
// We need to capture the call stack in order to inspect the source of this usage pattern
|
|
3160
|
-
new UsageError(errorMessage),
|
|
3212
|
+
getLongStack(() => new UsageError(errorMessage)),
|
|
3161
3213
|
);
|
|
3162
3214
|
this.opReentryCallsToReport--;
|
|
3163
3215
|
}
|
|
@@ -3180,14 +3232,19 @@ export class ContainerRuntime
|
|
|
3180
3232
|
}
|
|
3181
3233
|
}
|
|
3182
3234
|
|
|
3183
|
-
private
|
|
3184
|
-
|
|
3185
|
-
|
|
3186
|
-
|
|
3187
|
-
|
|
3235
|
+
private reSubmitBatch(batch: IPendingBatchMessage[]) {
|
|
3236
|
+
this.orderSequentially(() => {
|
|
3237
|
+
for (const message of batch) {
|
|
3238
|
+
this.reSubmit(message);
|
|
3239
|
+
}
|
|
3240
|
+
});
|
|
3241
|
+
this.flush();
|
|
3242
|
+
}
|
|
3243
|
+
|
|
3244
|
+
private reSubmit(message: IPendingBatchMessage) {
|
|
3188
3245
|
// Need to parse from string for back-compat
|
|
3189
|
-
const { contents, type } = this.parseOpContent(content);
|
|
3190
|
-
this.reSubmitCore(type, contents, localOpMetadata, opMetadata);
|
|
3246
|
+
const { contents, type } = this.parseOpContent(message.content);
|
|
3247
|
+
this.reSubmitCore(type, contents, message.localOpMetadata, message.opMetadata);
|
|
3191
3248
|
}
|
|
3192
3249
|
|
|
3193
3250
|
/**
|
package/src/dataStoreContext.ts
CHANGED
|
@@ -3,15 +3,22 @@
|
|
|
3
3
|
* Licensed under the MIT License.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import {
|
|
7
|
-
|
|
6
|
+
import {
|
|
7
|
+
IDisposable,
|
|
8
|
+
FluidObject,
|
|
9
|
+
IRequest,
|
|
10
|
+
IResponse,
|
|
11
|
+
IFluidHandle,
|
|
12
|
+
ITelemetryProperties,
|
|
13
|
+
} from "@fluidframework/core-interfaces";
|
|
8
14
|
import {
|
|
9
15
|
IAudience,
|
|
10
16
|
IDeltaManager,
|
|
11
17
|
AttachState,
|
|
12
18
|
ILoaderOptions,
|
|
13
19
|
} from "@fluidframework/container-definitions";
|
|
14
|
-
import { assert, Deferred,
|
|
20
|
+
import { assert, Deferred, TypedEventEmitter } from "@fluidframework/common-utils";
|
|
21
|
+
import { LazyPromise } from "@fluidframework/core-utils";
|
|
15
22
|
import { IDocumentStorageService } from "@fluidframework/driver-definitions";
|
|
16
23
|
import { BlobTreeEntry, readAndParse } from "@fluidframework/driver-utils";
|
|
17
24
|
import {
|
|
@@ -390,7 +397,7 @@ export abstract class FluidDataStoreContext
|
|
|
390
397
|
packageName: packagePathToTelemetryProperty(this.pkg),
|
|
391
398
|
});
|
|
392
399
|
this.channelDeferred?.reject(errorWrapped);
|
|
393
|
-
this.logger.sendErrorEvent({ eventName: "RealizeError" }, errorWrapped);
|
|
400
|
+
this.mc.logger.sendErrorEvent({ eventName: "RealizeError" }, errorWrapped);
|
|
394
401
|
});
|
|
395
402
|
}
|
|
396
403
|
return this.channelDeferred.promise;
|
|
@@ -780,7 +787,7 @@ export abstract class FluidDataStoreContext
|
|
|
780
787
|
this.channelDeferred.resolve(this.channel);
|
|
781
788
|
} catch (error) {
|
|
782
789
|
this.channelDeferred?.reject(error);
|
|
783
|
-
this.logger.sendErrorEvent(
|
|
790
|
+
this.mc.logger.sendErrorEvent(
|
|
784
791
|
{
|
|
785
792
|
eventName: "BindRuntimeError",
|
|
786
793
|
fluidDataStoreId: {
|
package/src/dataStoreContexts.ts
CHANGED
|
@@ -3,8 +3,10 @@
|
|
|
3
3
|
* Licensed under the MIT License.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import {
|
|
7
|
-
import { assert, Deferred
|
|
6
|
+
import { ITelemetryBaseLogger } from "@fluidframework/common-definitions";
|
|
7
|
+
import { assert, Deferred } from "@fluidframework/common-utils";
|
|
8
|
+
import { Lazy } from "@fluidframework/core-utils";
|
|
9
|
+
import { IDisposable } from "@fluidframework/core-interfaces";
|
|
8
10
|
import { ChildLogger, ITelemetryLoggerExt } from "@fluidframework/telemetry-utils";
|
|
9
11
|
import { FluidDataStoreContext, LocalFluidDataStoreContext } from "./dataStoreContext";
|
|
10
12
|
|
package/src/dataStores.ts
CHANGED
|
@@ -3,12 +3,16 @@
|
|
|
3
3
|
* Licensed under the MIT License.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import { ITelemetryBaseLogger, IDisposable } from "@fluidframework/common-definitions";
|
|
7
6
|
import {
|
|
8
7
|
DataCorruptionError,
|
|
9
8
|
extractSafePropertiesFromMessage,
|
|
10
9
|
} from "@fluidframework/container-utils";
|
|
11
|
-
import {
|
|
10
|
+
import {
|
|
11
|
+
ITelemetryBaseLogger,
|
|
12
|
+
IDisposable,
|
|
13
|
+
IFluidHandle,
|
|
14
|
+
IRequest,
|
|
15
|
+
} from "@fluidframework/core-interfaces";
|
|
12
16
|
import { FluidObjectHandle } from "@fluidframework/datastore";
|
|
13
17
|
import { ISequencedDocumentMessage, ISnapshotTree } from "@fluidframework/protocol-definitions";
|
|
14
18
|
import {
|
|
@@ -46,7 +50,8 @@ import {
|
|
|
46
50
|
} from "@fluidframework/telemetry-utils";
|
|
47
51
|
import { AttachState } from "@fluidframework/container-definitions";
|
|
48
52
|
import { buildSnapshotTree } from "@fluidframework/driver-utils";
|
|
49
|
-
import { assert
|
|
53
|
+
import { assert } from "@fluidframework/common-utils";
|
|
54
|
+
import { Lazy } from "@fluidframework/core-utils";
|
|
50
55
|
import { v4 as uuid } from "uuid";
|
|
51
56
|
import { DataStoreContexts } from "./dataStoreContexts";
|
|
52
57
|
import {
|
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
* Licensed under the MIT License.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import {
|
|
6
|
+
import { Timer } from "@fluidframework/common-utils";
|
|
7
|
+
import { LazyPromise } from "@fluidframework/core-utils";
|
|
7
8
|
import { ClientSessionExpiredError, DataProcessingError } from "@fluidframework/container-utils";
|
|
8
9
|
import { IRequestHeader } from "@fluidframework/core-interfaces";
|
|
9
10
|
import {
|
package/src/gc/gcDefinitions.ts
CHANGED
|
@@ -263,7 +263,6 @@ export interface IGarbageCollectorCreateParams {
|
|
|
263
263
|
readonly getLastSummaryTimestampMs: () => number | undefined;
|
|
264
264
|
readonly readAndParseBlob: ReadAndParseBlob;
|
|
265
265
|
readonly activeConnection: () => boolean;
|
|
266
|
-
readonly getContainerDiagnosticId: () => string;
|
|
267
266
|
}
|
|
268
267
|
|
|
269
268
|
export interface IGCRuntimeOptions {
|
package/src/gc/gcTelemetry.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Licensed under the MIT License.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import { ITelemetryGenericEvent } from "@fluidframework/
|
|
6
|
+
import { ITelemetryGenericEvent } from "@fluidframework/core-interfaces";
|
|
7
7
|
import { IGarbageCollectionData } from "@fluidframework/runtime-definitions";
|
|
8
8
|
import { packagePathToTelemetryProperty } from "@fluidframework/runtime-utils";
|
|
9
9
|
import {
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
import { assert } from "@fluidframework/common-utils";
|
|
7
7
|
import { StableId, UuidString } from "@fluidframework/runtime-definitions";
|
|
8
|
-
import { v4
|
|
8
|
+
import { v4 } from "uuid";
|
|
9
9
|
|
|
10
10
|
const hexadecimalCharCodes = Array.from("09afAF").map((c) => c.charCodeAt(0)) as [
|
|
11
11
|
zero: number,
|
|
@@ -24,9 +24,6 @@ function isHexadecimalCharacter(charCode: number): boolean {
|
|
|
24
24
|
);
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
/** The null (lowest/all-zeros) UUID */
|
|
28
|
-
export const nilUuid = assertIsUuidString(NIL);
|
|
29
|
-
|
|
30
27
|
/**
|
|
31
28
|
* Asserts that the given string is a UUID
|
|
32
29
|
*/
|
package/src/index.ts
CHANGED
|
@@ -67,6 +67,8 @@ export {
|
|
|
67
67
|
OpActionEventListener,
|
|
68
68
|
OpActionEventName,
|
|
69
69
|
ICancellableSummarizerController,
|
|
70
|
+
SubmitSummaryFailureData,
|
|
71
|
+
SummaryStage,
|
|
70
72
|
} from "./summary";
|
|
71
73
|
export { IChunkedOp, unpackRuntimeMessage } from "./opLifecycle";
|
|
72
74
|
export { generateStableId, isStableId, assertIsStableId } from "./id-compressor";
|
package/src/metadata.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* Copyright (c) Microsoft Corporation and contributors. All rights reserved.
|
|
3
|
+
* Licensed under the MIT License.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Batching makes assumptions about what might be on the metadata. This interface codifies those assumptions, but does not validate them.
|
|
8
|
+
*/
|
|
9
|
+
export interface IBatchMetadata {
|
|
10
|
+
batch?: boolean;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Blob handling makes assumptions about what might be on the metadata. This interface codifies those assumptions, but does not validate them.
|
|
15
|
+
*/
|
|
16
|
+
export interface IBlobMetadata {
|
|
17
|
+
blobId?: string;
|
|
18
|
+
localId?: string;
|
|
19
|
+
}
|
|
@@ -51,6 +51,26 @@ and verifying that the following expectation changes won't have any effects:
|
|
|
51
51
|
- client sequence numbers on batch messages can only be used to order messages with the same sequenceNumber
|
|
52
52
|
- requires all ops to be processed by runtime layer (version "2.0.0-internal.1.2.0" or later https://github.com/microsoft/FluidFramework/pull/11832)
|
|
53
53
|
|
|
54
|
+
Grouped batching may become problematic for batches which contain reentrant ops. This is the case when changes are made to a DDS inside a DDS 'onChanged' event handler. This means that the reentrant op will have a different reference sequence number than the rest of the ops in the batch, resulting in a different view of the state of the data model.
|
|
55
|
+
|
|
56
|
+
Therefore, when grouped batching is enabled, all batches with reentrant ops are rebased to the current reference sequence number and resubmitted to the data stores so that all ops are in agreement about the state of the data model and ensure eventual consistency.
|
|
57
|
+
|
|
58
|
+
### How to enable
|
|
59
|
+
|
|
60
|
+
**This feature is disabled by default, currently considered experimental and not ready for production usage.**
|
|
61
|
+
|
|
62
|
+
If all prerequisites in the previous section are met, enabling the feature can be done via the `IContainerRuntimeOptions` as following:
|
|
63
|
+
|
|
64
|
+
```
|
|
65
|
+
const runtimeOptions: IContainerRuntimeOptions = {
|
|
66
|
+
(...)
|
|
67
|
+
enableGroupedBatching: true,
|
|
68
|
+
(...)
|
|
69
|
+
}
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
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
|
+
|
|
54
74
|
## Chunking for compression
|
|
55
75
|
|
|
56
76
|
**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.
|
|
@@ -29,6 +29,7 @@ const opOverhead = 200;
|
|
|
29
29
|
export class BatchManager {
|
|
30
30
|
private pendingBatch: BatchMessage[] = [];
|
|
31
31
|
private batchContentSize = 0;
|
|
32
|
+
private hasReentrantOps = false;
|
|
32
33
|
|
|
33
34
|
public get length() {
|
|
34
35
|
return this.pendingBatch.length;
|
|
@@ -54,9 +55,14 @@ export class BatchManager {
|
|
|
54
55
|
|
|
55
56
|
constructor(public readonly options: IBatchManagerOptions) {}
|
|
56
57
|
|
|
57
|
-
public push(
|
|
58
|
+
public push(
|
|
59
|
+
message: BatchMessage,
|
|
60
|
+
reentrant: boolean,
|
|
61
|
+
currentClientSequenceNumber?: number,
|
|
62
|
+
): boolean {
|
|
58
63
|
const contentSize = this.batchContentSize + (message.contents?.length ?? 0);
|
|
59
64
|
const opCount = this.pendingBatch.length;
|
|
65
|
+
this.hasReentrantOps = this.hasReentrantOps || reentrant;
|
|
60
66
|
|
|
61
67
|
// Attempt to estimate batch size, aka socket message size.
|
|
62
68
|
// Each op has pretty large envelope, estimating to be 200 bytes.
|
|
@@ -100,11 +106,13 @@ export class BatchManager {
|
|
|
100
106
|
content: this.pendingBatch,
|
|
101
107
|
contentSizeInBytes: this.batchContentSize,
|
|
102
108
|
referenceSequenceNumber: this.referenceSequenceNumber,
|
|
109
|
+
hasReentrantOps: this.hasReentrantOps,
|
|
103
110
|
};
|
|
104
111
|
|
|
105
112
|
this.pendingBatch = [];
|
|
106
113
|
this.batchContentSize = 0;
|
|
107
114
|
this.clientSequenceNumber = undefined;
|
|
115
|
+
this.hasReentrantOps = false;
|
|
108
116
|
|
|
109
117
|
return addBatchMetadata(batch);
|
|
110
118
|
}
|
|
@@ -34,6 +34,17 @@ export interface IBatch {
|
|
|
34
34
|
* The reference sequence number for the batch
|
|
35
35
|
*/
|
|
36
36
|
readonly referenceSequenceNumber: number | undefined;
|
|
37
|
+
/**
|
|
38
|
+
* Wether or not the batch contains at least one op which was produced as the result
|
|
39
|
+
* of processing another op. This means that the batch must be rebased before
|
|
40
|
+
* submitted, to ensure that all ops have the same reference sequence numbers and a
|
|
41
|
+
* consistent view of the data model. This happens when the op is created within a
|
|
42
|
+
* 'changed' event handler of a DDS and will have a different reference sequence number
|
|
43
|
+
* than the rest of the ops in the batch, meaning that it has a different view of the
|
|
44
|
+
* state of the data model, therefore all ops must be resubmitted and rebased to the current
|
|
45
|
+
* reference sequence number to be in agreement about the data model state.
|
|
46
|
+
*/
|
|
47
|
+
readonly hasReentrantOps?: boolean;
|
|
37
48
|
}
|
|
38
49
|
|
|
39
50
|
export interface IBatchCheckpoint {
|
package/src/opLifecycle/index.ts
CHANGED
|
@@ -11,7 +11,7 @@ export {
|
|
|
11
11
|
IChunkedOp,
|
|
12
12
|
IMessageProcessingResult,
|
|
13
13
|
} from "./definitions";
|
|
14
|
-
export { Outbox } from "./outbox";
|
|
14
|
+
export { Outbox, getLongStack } from "./outbox";
|
|
15
15
|
export { OpCompressor } from "./opCompressor";
|
|
16
16
|
export { OpDecompressor } from "./opDecompressor";
|
|
17
17
|
export { OpSplitter, splitOp } from "./opSplitter";
|
|
@@ -8,8 +8,16 @@ import { ISequencedDocumentMessage } from "@fluidframework/protocol-definitions"
|
|
|
8
8
|
import { assert, IsoBuffer, Uint8ArrayToString } from "@fluidframework/common-utils";
|
|
9
9
|
import { ChildLogger, ITelemetryLoggerExt } from "@fluidframework/telemetry-utils";
|
|
10
10
|
import { CompressionAlgorithms } from "../containerRuntime";
|
|
11
|
+
import { IBatchMetadata } from "../metadata";
|
|
11
12
|
import { IMessageProcessingResult } from "./definitions";
|
|
12
13
|
|
|
14
|
+
/**
|
|
15
|
+
* Compression makes assumptions about the shape of message contents. This interface codifies those assumptions, but does not validate them.
|
|
16
|
+
*/
|
|
17
|
+
interface IPackedContentsContents {
|
|
18
|
+
packedContents: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
13
21
|
/**
|
|
14
22
|
* State machine that "unrolls" contents of compressed batches of ops after decompressing them.
|
|
15
23
|
* This class relies on some implicit contracts defined below:
|
|
@@ -34,7 +42,10 @@ export class OpDecompressor {
|
|
|
34
42
|
0x511 /* Only lz4 compression is supported */,
|
|
35
43
|
);
|
|
36
44
|
|
|
37
|
-
if (
|
|
45
|
+
if (
|
|
46
|
+
(message.metadata as IBatchMetadata | undefined)?.batch === true &&
|
|
47
|
+
this.isCompressed(message)
|
|
48
|
+
) {
|
|
38
49
|
// Beginning of a compressed batch
|
|
39
50
|
assert(this.activeBatch === false, 0x4b8 /* shouldn't have multiple active batches */);
|
|
40
51
|
if (message.compression) {
|
|
@@ -47,7 +58,10 @@ export class OpDecompressor {
|
|
|
47
58
|
|
|
48
59
|
this.activeBatch = true;
|
|
49
60
|
|
|
50
|
-
const contents = IsoBuffer.from(
|
|
61
|
+
const contents = IsoBuffer.from(
|
|
62
|
+
(message.contents as IPackedContentsContents).packedContents,
|
|
63
|
+
"base64",
|
|
64
|
+
);
|
|
51
65
|
const decompressedMessage = decompress(contents);
|
|
52
66
|
const intoString = Uint8ArrayToString(decompressedMessage);
|
|
53
67
|
const asObj = JSON.parse(intoString);
|
|
@@ -61,7 +75,7 @@ export class OpDecompressor {
|
|
|
61
75
|
|
|
62
76
|
if (
|
|
63
77
|
this.rootMessageContents !== undefined &&
|
|
64
|
-
message.metadata?.batch === undefined &&
|
|
78
|
+
(message.metadata as IBatchMetadata | undefined)?.batch === undefined &&
|
|
65
79
|
this.activeBatch
|
|
66
80
|
) {
|
|
67
81
|
assert(message.contents === undefined, 0x512 /* Expecting empty message */);
|
|
@@ -73,7 +87,10 @@ export class OpDecompressor {
|
|
|
73
87
|
};
|
|
74
88
|
}
|
|
75
89
|
|
|
76
|
-
if (
|
|
90
|
+
if (
|
|
91
|
+
this.rootMessageContents !== undefined &&
|
|
92
|
+
(message.metadata as IBatchMetadata | undefined)?.batch === false
|
|
93
|
+
) {
|
|
77
94
|
// End of compressed batch
|
|
78
95
|
const returnMessage = newMessage(
|
|
79
96
|
message,
|
|
@@ -90,14 +107,20 @@ export class OpDecompressor {
|
|
|
90
107
|
};
|
|
91
108
|
}
|
|
92
109
|
|
|
93
|
-
if (
|
|
110
|
+
if (
|
|
111
|
+
(message.metadata as IBatchMetadata | undefined)?.batch === undefined &&
|
|
112
|
+
this.isCompressed(message)
|
|
113
|
+
) {
|
|
94
114
|
// Single compressed message
|
|
95
115
|
assert(
|
|
96
116
|
this.activeBatch === false,
|
|
97
117
|
0x4ba /* shouldn't receive compressed message in middle of a batch */,
|
|
98
118
|
);
|
|
99
119
|
|
|
100
|
-
const contents = IsoBuffer.from(
|
|
120
|
+
const contents = IsoBuffer.from(
|
|
121
|
+
(message.contents as IPackedContentsContents).packedContents,
|
|
122
|
+
"base64",
|
|
123
|
+
);
|
|
101
124
|
const decompressedMessage = decompress(contents);
|
|
102
125
|
const intoString = new TextDecoder().decode(decompressedMessage);
|
|
103
126
|
const asObj = JSON.parse(intoString);
|
|
@@ -135,16 +158,19 @@ export class OpDecompressor {
|
|
|
135
158
|
message.contents !== null &&
|
|
136
159
|
typeof message.contents === "object" &&
|
|
137
160
|
Object.keys(message.contents).length === 1 &&
|
|
138
|
-
message.contents
|
|
139
|
-
|
|
140
|
-
message.contents.packedContents.length > 0 &&
|
|
141
|
-
IsoBuffer.from(
|
|
142
|
-
message.contents.packedContents
|
|
161
|
+
typeof (message.contents as { packedContents?: unknown }).packedContents ===
|
|
162
|
+
"string" &&
|
|
163
|
+
(message.contents as IPackedContentsContents).packedContents.length > 0 &&
|
|
164
|
+
IsoBuffer.from(
|
|
165
|
+
(message.contents as IPackedContentsContents).packedContents,
|
|
166
|
+
"base64",
|
|
167
|
+
).toString("base64") ===
|
|
168
|
+
(message.contents as IPackedContentsContents).packedContents
|
|
143
169
|
) {
|
|
144
170
|
this.logger.sendTelemetryEvent({
|
|
145
171
|
eventName: "LegacyCompression",
|
|
146
172
|
type: message.type,
|
|
147
|
-
batch: message.metadata?.batch,
|
|
173
|
+
batch: (message.metadata as IBatchMetadata | undefined)?.batch,
|
|
148
174
|
});
|
|
149
175
|
return true;
|
|
150
176
|
}
|
|
@@ -164,5 +190,7 @@ const newMessage = (
|
|
|
164
190
|
...originalMessage,
|
|
165
191
|
contents,
|
|
166
192
|
compression: undefined,
|
|
167
|
-
|
|
193
|
+
// TODO: It should already be the case that we're not modifying any metadata, not clear if/why this shallow clone should be required.
|
|
194
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
|
|
195
|
+
metadata: { ...(originalMessage.metadata as any) },
|
|
168
196
|
});
|
|
@@ -8,6 +8,14 @@ import { ISequencedDocumentMessage } from "@fluidframework/protocol-definitions"
|
|
|
8
8
|
import { ContainerMessageType } from "..";
|
|
9
9
|
import { IBatch } from "./definitions";
|
|
10
10
|
|
|
11
|
+
/**
|
|
12
|
+
* Grouping makes assumptions about the shape of message contents. This interface codifies those assumptions, but does not validate them.
|
|
13
|
+
*/
|
|
14
|
+
interface IGroupedBatchMessageContents {
|
|
15
|
+
type: typeof OpGroupingManager.groupedBatchOp;
|
|
16
|
+
contents: IGroupedMessage[];
|
|
17
|
+
}
|
|
18
|
+
|
|
11
19
|
interface IGroupedMessage {
|
|
12
20
|
contents?: unknown;
|
|
13
21
|
metadata?: Record<string, unknown>;
|
|
@@ -15,7 +23,7 @@ interface IGroupedMessage {
|
|
|
15
23
|
}
|
|
16
24
|
|
|
17
25
|
export class OpGroupingManager {
|
|
18
|
-
static groupedBatchOp = "groupedBatch";
|
|
26
|
+
static readonly groupedBatchOp = "groupedBatch";
|
|
19
27
|
|
|
20
28
|
constructor(private readonly groupedBatchingEnabled: boolean) {}
|
|
21
29
|
|
|
@@ -25,10 +33,6 @@ export class OpGroupingManager {
|
|
|
25
33
|
}
|
|
26
34
|
|
|
27
35
|
for (const message of batch.content) {
|
|
28
|
-
// Blob attaches cannot be grouped (grouped batching would hide metadata)
|
|
29
|
-
if (message.type === ContainerMessageType.BlobAttach) {
|
|
30
|
-
return batch;
|
|
31
|
-
}
|
|
32
36
|
if (message.metadata) {
|
|
33
37
|
const keys = Object.keys(message.metadata);
|
|
34
38
|
assert(keys.length < 2, 0x5dd /* cannot group ops with metadata */);
|
|
@@ -64,11 +68,14 @@ export class OpGroupingManager {
|
|
|
64
68
|
}
|
|
65
69
|
|
|
66
70
|
public ungroupOp(op: ISequencedDocumentMessage): ISequencedDocumentMessage[] {
|
|
67
|
-
if (
|
|
71
|
+
if (
|
|
72
|
+
(op.contents as { type?: unknown } | undefined)?.type !==
|
|
73
|
+
OpGroupingManager.groupedBatchOp
|
|
74
|
+
) {
|
|
68
75
|
return [op];
|
|
69
76
|
}
|
|
70
77
|
|
|
71
|
-
const messages = op.contents
|
|
78
|
+
const messages = (op.contents as IGroupedBatchMessageContents).contents;
|
|
72
79
|
let fakeCsn = 1;
|
|
73
80
|
return messages.map((subMessage) => ({
|
|
74
81
|
...op,
|
|
@@ -52,7 +52,9 @@ export class OpSplitter {
|
|
|
52
52
|
};
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
-
|
|
55
|
+
// TODO: Verify whether this should be able to handle server-generated ops (with null clientId)
|
|
56
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
|
|
57
|
+
const clientId = message.clientId as string;
|
|
56
58
|
const chunkedContent = message.contents as IChunkedOp;
|
|
57
59
|
this.addChunk(clientId, chunkedContent, message);
|
|
58
60
|
|