@fluidframework/container-runtime 2.0.0-rc.3.0.3 → 2.0.0-rc.4.0.1
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 +46 -0
- package/api-report/container-runtime.api.md +72 -34
- package/dist/batchTracker.d.ts +1 -1
- package/dist/batchTracker.d.ts.map +1 -1
- package/dist/batchTracker.js.map +1 -1
- package/dist/blobManager.d.ts +7 -7
- package/dist/blobManager.d.ts.map +1 -1
- package/dist/blobManager.js +2 -4
- package/dist/blobManager.js.map +1 -1
- package/dist/channelCollection.d.ts +6 -4
- package/dist/channelCollection.d.ts.map +1 -1
- package/dist/channelCollection.js +20 -7
- 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 +54 -5
- package/dist/connectionTelemetry.js.map +1 -1
- package/dist/containerRuntime.d.ts +17 -35
- package/dist/containerRuntime.d.ts.map +1 -1
- package/dist/containerRuntime.js +194 -163
- package/dist/containerRuntime.js.map +1 -1
- package/dist/dataStore.d.ts +1 -1
- package/dist/dataStore.d.ts.map +1 -1
- package/dist/dataStore.js.map +1 -1
- package/dist/dataStoreContext.d.ts +9 -6
- package/dist/dataStoreContext.d.ts.map +1 -1
- package/dist/dataStoreContext.js +19 -5
- package/dist/dataStoreContext.js.map +1 -1
- package/dist/dataStoreContexts.d.ts.map +1 -1
- package/dist/dataStoreContexts.js.map +1 -1
- package/dist/deltaManagerProxies.d.ts +81 -0
- package/dist/deltaManagerProxies.d.ts.map +1 -0
- package/dist/{deltaManagerSummarizerProxy.js → deltaManagerProxies.js} +75 -20
- package/dist/deltaManagerProxies.js.map +1 -0
- package/dist/deltaScheduler.d.ts +2 -2
- package/dist/deltaScheduler.d.ts.map +1 -1
- package/dist/deltaScheduler.js.map +1 -1
- package/dist/gc/garbageCollection.d.ts +1 -1
- package/dist/gc/garbageCollection.d.ts.map +1 -1
- package/dist/gc/garbageCollection.js.map +1 -1
- package/dist/gc/gcDefinitions.d.ts +1 -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 -2
- package/dist/gc/gcTelemetry.d.ts.map +1 -1
- package/dist/gc/gcTelemetry.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/legacy.d.ts +6 -1
- package/dist/messageTypes.d.ts +5 -2
- package/dist/messageTypes.d.ts.map +1 -1
- package/dist/messageTypes.js.map +1 -1
- package/dist/metadata.d.ts +2 -2
- package/dist/metadata.d.ts.map +1 -1
- package/dist/metadata.js.map +1 -1
- package/dist/opLifecycle/batchManager.d.ts +4 -1
- package/dist/opLifecycle/batchManager.d.ts.map +1 -1
- package/dist/opLifecycle/batchManager.js +0 -10
- package/dist/opLifecycle/batchManager.js.map +1 -1
- package/dist/opLifecycle/outbox.d.ts +0 -4
- package/dist/opLifecycle/outbox.d.ts.map +1 -1
- package/dist/opLifecycle/outbox.js +7 -38
- package/dist/opLifecycle/outbox.js.map +1 -1
- package/dist/packageVersion.d.ts +1 -1
- package/dist/packageVersion.js +1 -1
- package/dist/packageVersion.js.map +1 -1
- package/dist/pendingStateManager.d.ts +9 -2
- package/dist/pendingStateManager.d.ts.map +1 -1
- package/dist/pendingStateManager.js +26 -10
- package/dist/pendingStateManager.js.map +1 -1
- package/dist/scheduleManager.d.ts +2 -2
- package/dist/scheduleManager.d.ts.map +1 -1
- package/dist/scheduleManager.js.map +1 -1
- package/dist/summary/documentSchema.d.ts +3 -1
- package/dist/summary/documentSchema.d.ts.map +1 -1
- package/dist/summary/documentSchema.js +25 -7
- package/dist/summary/documentSchema.js.map +1 -1
- package/dist/summary/index.d.ts +1 -1
- package/dist/summary/index.d.ts.map +1 -1
- package/dist/summary/index.js.map +1 -1
- package/dist/summary/orderedClientElection.d.ts +2 -2
- package/dist/summary/orderedClientElection.d.ts.map +1 -1
- package/dist/summary/orderedClientElection.js.map +1 -1
- package/dist/summary/runningSummarizer.js +10 -10
- package/dist/summary/runningSummarizer.js.map +1 -1
- package/dist/summary/summarizer.d.ts +1 -2
- package/dist/summary/summarizer.d.ts.map +1 -1
- package/dist/summary/summarizer.js.map +1 -1
- package/dist/summary/summarizerClientElection.d.ts +1 -1
- package/dist/summary/summarizerClientElection.d.ts.map +1 -1
- package/dist/summary/summarizerClientElection.js.map +1 -1
- package/dist/summary/summarizerHeuristics.d.ts +1 -1
- package/dist/summary/summarizerHeuristics.d.ts.map +1 -1
- package/dist/summary/summarizerHeuristics.js.map +1 -1
- package/dist/summary/summarizerNode/summarizerNode.d.ts +4 -3
- package/dist/summary/summarizerNode/summarizerNode.d.ts.map +1 -1
- package/dist/summary/summarizerNode/summarizerNode.js +4 -10
- package/dist/summary/summarizerNode/summarizerNode.js.map +1 -1
- package/dist/summary/summarizerNode/summarizerNodeUtils.d.ts +2 -3
- 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 +1 -2
- package/dist/summary/summarizerNode/summarizerNodeWithGc.d.ts.map +1 -1
- package/dist/summary/summarizerNode/summarizerNodeWithGc.js +2 -9
- package/dist/summary/summarizerNode/summarizerNodeWithGc.js.map +1 -1
- package/dist/summary/summarizerTypes.d.ts +3 -5
- package/dist/summary/summarizerTypes.d.ts.map +1 -1
- package/dist/summary/summarizerTypes.js.map +1 -1
- package/dist/summary/summaryCollection.d.ts +2 -2
- package/dist/summary/summaryCollection.d.ts.map +1 -1
- package/dist/summary/summaryCollection.js.map +1 -1
- package/dist/summary/summaryFormat.d.ts +25 -5
- package/dist/summary/summaryFormat.d.ts.map +1 -1
- package/dist/summary/summaryFormat.js.map +1 -1
- package/dist/summary/summaryGenerator.d.ts +1 -2
- package/dist/summary/summaryGenerator.d.ts.map +1 -1
- package/dist/summary/summaryGenerator.js +12 -11
- package/dist/summary/summaryGenerator.js.map +1 -1
- package/dist/summary/summaryManager.d.ts.map +1 -1
- package/dist/summary/summaryManager.js +5 -5
- package/dist/summary/summaryManager.js.map +1 -1
- package/lib/batchTracker.d.ts +1 -1
- package/lib/batchTracker.d.ts.map +1 -1
- package/lib/batchTracker.js.map +1 -1
- package/lib/blobManager.d.ts +7 -7
- package/lib/blobManager.d.ts.map +1 -1
- package/lib/blobManager.js +3 -5
- package/lib/blobManager.js.map +1 -1
- package/lib/channelCollection.d.ts +6 -4
- package/lib/channelCollection.d.ts.map +1 -1
- package/lib/channelCollection.js +21 -8
- 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 +49 -0
- package/lib/connectionTelemetry.js.map +1 -1
- package/lib/containerRuntime.d.ts +17 -35
- package/lib/containerRuntime.d.ts.map +1 -1
- package/lib/containerRuntime.js +195 -164
- package/lib/containerRuntime.js.map +1 -1
- package/lib/dataStore.d.ts +1 -1
- package/lib/dataStore.d.ts.map +1 -1
- package/lib/dataStore.js +1 -1
- package/lib/dataStore.js.map +1 -1
- package/lib/dataStoreContext.d.ts +9 -6
- package/lib/dataStoreContext.d.ts.map +1 -1
- package/lib/dataStoreContext.js +21 -7
- package/lib/dataStoreContext.js.map +1 -1
- package/lib/dataStoreContexts.d.ts.map +1 -1
- package/lib/dataStoreContexts.js.map +1 -1
- package/lib/deltaManagerProxies.d.ts +81 -0
- package/lib/deltaManagerProxies.d.ts.map +1 -0
- package/lib/{deltaManagerSummarizerProxy.js → deltaManagerProxies.js} +72 -19
- package/lib/deltaManagerProxies.js.map +1 -0
- package/lib/deltaScheduler.d.ts +2 -2
- package/lib/deltaScheduler.d.ts.map +1 -1
- package/lib/deltaScheduler.js.map +1 -1
- package/lib/gc/garbageCollection.d.ts +1 -1
- package/lib/gc/garbageCollection.d.ts.map +1 -1
- package/lib/gc/garbageCollection.js.map +1 -1
- package/lib/gc/gcDefinitions.d.ts +1 -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 -2
- package/lib/gc/gcTelemetry.d.ts.map +1 -1
- package/lib/gc/gcTelemetry.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/legacy.d.ts +6 -1
- package/lib/messageTypes.d.ts +5 -2
- package/lib/messageTypes.d.ts.map +1 -1
- package/lib/messageTypes.js.map +1 -1
- package/lib/metadata.d.ts +2 -2
- package/lib/metadata.d.ts.map +1 -1
- package/lib/metadata.js.map +1 -1
- package/lib/opLifecycle/batchManager.d.ts +4 -1
- package/lib/opLifecycle/batchManager.d.ts.map +1 -1
- package/lib/opLifecycle/batchManager.js +0 -10
- package/lib/opLifecycle/batchManager.js.map +1 -1
- package/lib/opLifecycle/outbox.d.ts +0 -4
- package/lib/opLifecycle/outbox.d.ts.map +1 -1
- package/lib/opLifecycle/outbox.js +7 -38
- package/lib/opLifecycle/outbox.js.map +1 -1
- package/lib/packageVersion.d.ts +1 -1
- package/lib/packageVersion.js +1 -1
- package/lib/packageVersion.js.map +1 -1
- package/lib/pendingStateManager.d.ts +9 -2
- package/lib/pendingStateManager.d.ts.map +1 -1
- package/lib/pendingStateManager.js +27 -11
- package/lib/pendingStateManager.js.map +1 -1
- package/lib/scheduleManager.d.ts +2 -2
- package/lib/scheduleManager.d.ts.map +1 -1
- package/lib/scheduleManager.js.map +1 -1
- package/lib/summary/documentSchema.d.ts +3 -1
- package/lib/summary/documentSchema.d.ts.map +1 -1
- package/lib/summary/documentSchema.js +25 -7
- package/lib/summary/documentSchema.js.map +1 -1
- package/lib/summary/index.d.ts +1 -1
- package/lib/summary/index.d.ts.map +1 -1
- package/lib/summary/index.js.map +1 -1
- package/lib/summary/orderedClientElection.d.ts +2 -2
- package/lib/summary/orderedClientElection.d.ts.map +1 -1
- package/lib/summary/orderedClientElection.js +1 -1
- package/lib/summary/orderedClientElection.js.map +1 -1
- package/lib/summary/runningSummarizer.js +1 -1
- package/lib/summary/runningSummarizer.js.map +1 -1
- package/lib/summary/summarizer.d.ts +1 -2
- package/lib/summary/summarizer.d.ts.map +1 -1
- package/lib/summary/summarizer.js.map +1 -1
- package/lib/summary/summarizerClientElection.d.ts +1 -1
- package/lib/summary/summarizerClientElection.d.ts.map +1 -1
- package/lib/summary/summarizerClientElection.js.map +1 -1
- package/lib/summary/summarizerHeuristics.d.ts +1 -1
- package/lib/summary/summarizerHeuristics.d.ts.map +1 -1
- package/lib/summary/summarizerHeuristics.js.map +1 -1
- package/lib/summary/summarizerNode/summarizerNode.d.ts +4 -3
- package/lib/summary/summarizerNode/summarizerNode.d.ts.map +1 -1
- package/lib/summary/summarizerNode/summarizerNode.js +4 -10
- package/lib/summary/summarizerNode/summarizerNode.js.map +1 -1
- package/lib/summary/summarizerNode/summarizerNodeUtils.d.ts +2 -3
- 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 +1 -2
- package/lib/summary/summarizerNode/summarizerNodeWithGc.d.ts.map +1 -1
- package/lib/summary/summarizerNode/summarizerNodeWithGc.js +2 -9
- package/lib/summary/summarizerNode/summarizerNodeWithGc.js.map +1 -1
- package/lib/summary/summarizerTypes.d.ts +3 -5
- package/lib/summary/summarizerTypes.d.ts.map +1 -1
- package/lib/summary/summarizerTypes.js.map +1 -1
- package/lib/summary/summaryCollection.d.ts +2 -2
- package/lib/summary/summaryCollection.d.ts.map +1 -1
- package/lib/summary/summaryCollection.js.map +1 -1
- package/lib/summary/summaryFormat.d.ts +25 -5
- package/lib/summary/summaryFormat.d.ts.map +1 -1
- package/lib/summary/summaryFormat.js.map +1 -1
- package/lib/summary/summaryGenerator.d.ts +1 -2
- package/lib/summary/summaryGenerator.d.ts.map +1 -1
- package/lib/summary/summaryGenerator.js +5 -4
- package/lib/summary/summaryGenerator.js.map +1 -1
- package/lib/summary/summaryManager.d.ts.map +1 -1
- package/lib/summary/summaryManager.js +2 -2
- package/lib/summary/summaryManager.js.map +1 -1
- package/lib/tsdoc-metadata.json +1 -1
- package/package.json +28 -50
- package/src/batchTracker.ts +1 -2
- package/src/blobManager.ts +11 -10
- package/src/channelCollection.ts +30 -12
- package/src/connectionTelemetry.ts +59 -4
- package/src/containerRuntime.ts +262 -239
- package/src/dataStore.ts +7 -4
- package/src/dataStoreContext.ts +57 -16
- package/src/dataStoreContexts.ts +1 -2
- package/src/{deltaManagerSummarizerProxy.ts → deltaManagerProxies.ts} +98 -24
- package/src/deltaScheduler.ts +2 -3
- package/src/gc/garbageCollection.ts +1 -1
- package/src/gc/gcDefinitions.ts +1 -1
- package/src/gc/gcTelemetry.ts +1 -3
- package/src/index.ts +5 -0
- package/src/messageTypes.ts +4 -2
- package/src/metadata.ts +2 -2
- package/src/opLifecycle/README.md +4 -4
- package/src/opLifecycle/batchManager.ts +5 -14
- package/src/opLifecycle/outbox.ts +7 -53
- package/src/packageVersion.ts +1 -1
- package/src/pendingStateManager.ts +38 -15
- package/src/scheduleManager.ts +2 -2
- package/src/summary/documentSchema.ts +37 -12
- package/src/summary/index.ts +4 -0
- package/src/summary/orderedClientElection.ts +6 -3
- package/src/summary/runningSummarizer.ts +1 -1
- package/src/summary/summarizer.ts +1 -1
- package/src/summary/summarizerClientElection.ts +1 -1
- package/src/summary/summarizerHeuristics.ts +1 -1
- package/src/summary/summarizerNode/summarizerNode.ts +3 -12
- package/src/summary/summarizerNode/summarizerNodeUtils.ts +2 -3
- package/src/summary/summarizerNode/summarizerNodeWithGc.ts +1 -10
- package/src/summary/summarizerTypes.ts +6 -5
- package/src/summary/summaryCollection.ts +2 -2
- package/src/summary/summaryFormat.ts +30 -4
- package/src/summary/summaryGenerator.ts +20 -9
- package/src/summary/summaryManager.ts +6 -3
- package/dist/deltaManagerSummarizerProxy.d.ts +0 -44
- package/dist/deltaManagerSummarizerProxy.d.ts.map +0 -1
- package/dist/deltaManagerSummarizerProxy.js.map +0 -1
- package/lib/deltaManagerSummarizerProxy.d.ts +0 -44
- package/lib/deltaManagerSummarizerProxy.d.ts.map +0 -1
- package/lib/deltaManagerSummarizerProxy.js.map +0 -1
package/src/containerRuntime.ts
CHANGED
|
@@ -7,8 +7,8 @@ import { Trace, TypedEventEmitter } from "@fluid-internal/client-utils";
|
|
|
7
7
|
import {
|
|
8
8
|
AttachState,
|
|
9
9
|
IAudience,
|
|
10
|
+
ISelf,
|
|
10
11
|
ICriticalContainerError,
|
|
11
|
-
IDeltaManager,
|
|
12
12
|
} from "@fluidframework/container-definitions";
|
|
13
13
|
import {
|
|
14
14
|
IBatchMessage,
|
|
@@ -17,6 +17,8 @@ import {
|
|
|
17
17
|
ILoader,
|
|
18
18
|
IRuntime,
|
|
19
19
|
LoaderHeader,
|
|
20
|
+
type IAudienceEvents,
|
|
21
|
+
IDeltaManager,
|
|
20
22
|
} from "@fluidframework/container-definitions/internal";
|
|
21
23
|
import {
|
|
22
24
|
IContainerRuntime,
|
|
@@ -26,11 +28,12 @@ import {
|
|
|
26
28
|
FluidObject,
|
|
27
29
|
IFluidHandle,
|
|
28
30
|
IFluidHandleContext,
|
|
31
|
+
type IFluidHandleInternal,
|
|
29
32
|
IProvideFluidHandleContext,
|
|
30
33
|
IRequest,
|
|
31
34
|
IResponse,
|
|
32
35
|
ITelemetryBaseLogger,
|
|
33
|
-
} from "@fluidframework/core-interfaces";
|
|
36
|
+
} from "@fluidframework/core-interfaces/internal";
|
|
34
37
|
import { ISignalEnvelope } from "@fluidframework/core-interfaces/internal";
|
|
35
38
|
import {
|
|
36
39
|
assert,
|
|
@@ -99,11 +102,9 @@ import {
|
|
|
99
102
|
responseToException,
|
|
100
103
|
seqFromTree,
|
|
101
104
|
} from "@fluidframework/runtime-utils/internal";
|
|
105
|
+
import type { ITelemetryGenericEventExt } from "@fluidframework/telemetry-utils/internal";
|
|
102
106
|
import {
|
|
103
|
-
type ITelemetryGenericEventExt,
|
|
104
107
|
ITelemetryLoggerExt,
|
|
105
|
-
} from "@fluidframework/telemetry-utils";
|
|
106
|
-
import {
|
|
107
108
|
DataCorruptionError,
|
|
108
109
|
DataProcessingError,
|
|
109
110
|
GenericError,
|
|
@@ -130,7 +131,7 @@ import { IPerfSignalReport, ReportOpPerfTelemetry } from "./connectionTelemetry.
|
|
|
130
131
|
import { ContainerFluidHandleContext } from "./containerHandleContext.js";
|
|
131
132
|
import { channelToDataStore } from "./dataStore.js";
|
|
132
133
|
import { FluidDataStoreRegistry } from "./dataStoreRegistry.js";
|
|
133
|
-
import { DeltaManagerSummarizerProxy } from "./
|
|
134
|
+
import { DeltaManagerPendingOpsProxy, DeltaManagerSummarizerProxy } from "./deltaManagerProxies.js";
|
|
134
135
|
import {
|
|
135
136
|
GCNodeType,
|
|
136
137
|
GarbageCollector,
|
|
@@ -150,7 +151,7 @@ import {
|
|
|
150
151
|
type OutboundContainerRuntimeMessage,
|
|
151
152
|
type UnknownContainerRuntimeMessage,
|
|
152
153
|
} from "./messageTypes.js";
|
|
153
|
-
import { IBatchMetadata,
|
|
154
|
+
import { IBatchMetadata, ISavedOpMetadata } from "./metadata.js";
|
|
154
155
|
import {
|
|
155
156
|
BatchMessage,
|
|
156
157
|
IBatch,
|
|
@@ -161,7 +162,6 @@ import {
|
|
|
161
162
|
OpSplitter,
|
|
162
163
|
Outbox,
|
|
163
164
|
RemoteMessageProcessor,
|
|
164
|
-
getLongStack,
|
|
165
165
|
} from "./opLifecycle/index.js";
|
|
166
166
|
import { pkgVersion } from "./packageVersion.js";
|
|
167
167
|
import {
|
|
@@ -461,25 +461,12 @@ export interface IContainerRuntimeOptions {
|
|
|
461
461
|
*/
|
|
462
462
|
readonly enableRuntimeIdCompressor?: IdCompressorMode;
|
|
463
463
|
|
|
464
|
-
/**
|
|
465
|
-
* If enabled, the runtime will block all attempts to send an op inside the
|
|
466
|
-
* {@link ContainerRuntime#ensureNoDataModelChanges} callback. The callback is used by
|
|
467
|
-
* {@link @fluidframework/shared-object-base#SharedObjectCore} for event handlers so enabling this
|
|
468
|
-
* will disallow modifying DDSes while handling DDS events.
|
|
469
|
-
*
|
|
470
|
-
* By default, the feature is disabled. If enabled from options, the `Fluid.ContainerRuntime.DisableOpReentryCheck`
|
|
471
|
-
* can be used to disable it at runtime.
|
|
472
|
-
*/
|
|
473
|
-
readonly enableOpReentryCheck?: boolean;
|
|
474
464
|
/**
|
|
475
465
|
* If enabled, the runtime will group messages within a batch into a single
|
|
476
466
|
* message to be sent to the service.
|
|
477
467
|
* The grouping an ungrouping of such messages is handled by the "OpGroupingManager".
|
|
478
468
|
*
|
|
479
|
-
* By default, the feature is
|
|
480
|
-
* flag can be used to disable it at runtime.
|
|
481
|
-
*
|
|
482
|
-
* @experimental Not ready for use.
|
|
469
|
+
* By default, the feature is enabled.
|
|
483
470
|
*/
|
|
484
471
|
readonly enableGroupedBatching?: boolean;
|
|
485
472
|
|
|
@@ -671,11 +658,13 @@ type MessageWithContext =
|
|
|
671
658
|
message: InboundSequencedContainerRuntimeMessage;
|
|
672
659
|
modernRuntimeMessage: true;
|
|
673
660
|
local: boolean;
|
|
661
|
+
savedOp?: boolean;
|
|
674
662
|
}
|
|
675
663
|
| {
|
|
676
664
|
message: InboundSequencedContainerRuntimeMessageOrSystemMessage;
|
|
677
665
|
modernRuntimeMessage: false;
|
|
678
666
|
local: boolean;
|
|
667
|
+
savedOp?: boolean;
|
|
679
668
|
};
|
|
680
669
|
|
|
681
670
|
const summarizerRequestUrl = "_summarizer";
|
|
@@ -810,8 +799,7 @@ export class ContainerRuntime
|
|
|
810
799
|
maxBatchSizeInBytes = defaultMaxBatchSizeInBytes,
|
|
811
800
|
enableRuntimeIdCompressor,
|
|
812
801
|
chunkSizeInBytes = defaultChunkSizeInBytes,
|
|
813
|
-
|
|
814
|
-
enableGroupedBatching = false,
|
|
802
|
+
enableGroupedBatching = true,
|
|
815
803
|
explicitSchemaControl = false,
|
|
816
804
|
} = runtimeOptions;
|
|
817
805
|
|
|
@@ -857,9 +845,9 @@ export class ContainerRuntime
|
|
|
857
845
|
|
|
858
846
|
// Verify summary runtime sequence number matches protocol sequence number.
|
|
859
847
|
const runtimeSequenceNumber = messageAtLastSummary?.sequenceNumber;
|
|
848
|
+
const protocolSequenceNumber = context.deltaManager.initialSequenceNumber;
|
|
860
849
|
// When we load with pending state, we reuse an old snapshot so we don't expect these numbers to match
|
|
861
850
|
if (!context.pendingLocalState && runtimeSequenceNumber !== undefined) {
|
|
862
|
-
const protocolSequenceNumber = context.deltaManager.initialSequenceNumber;
|
|
863
851
|
// Unless bypass is explicitly set, then take action when sequence numbers mismatch.
|
|
864
852
|
if (
|
|
865
853
|
loadSequenceNumberVerification !== "bypass" &&
|
|
@@ -972,9 +960,6 @@ export class ContainerRuntime
|
|
|
972
960
|
}
|
|
973
961
|
};
|
|
974
962
|
|
|
975
|
-
const disableGroupedBatching = mc.config.getBoolean(
|
|
976
|
-
"Fluid.ContainerRuntime.DisableGroupedBatching",
|
|
977
|
-
);
|
|
978
963
|
const disableCompression = mc.config.getBoolean(
|
|
979
964
|
"Fluid.ContainerRuntime.CompressionDisabled",
|
|
980
965
|
);
|
|
@@ -983,16 +968,15 @@ export class ContainerRuntime
|
|
|
983
968
|
compressionOptions.minimumBatchSizeInBytes !== Infinity &&
|
|
984
969
|
compressionOptions.compressionAlgorithm === "lz4";
|
|
985
970
|
|
|
986
|
-
const opGroupingEnabled = disableGroupedBatching !== true && enableGroupedBatching;
|
|
987
|
-
|
|
988
971
|
const documentSchemaController = new DocumentsSchemaController(
|
|
989
972
|
existing,
|
|
973
|
+
protocolSequenceNumber,
|
|
990
974
|
metadata?.documentSchema,
|
|
991
975
|
{
|
|
992
976
|
explicitSchemaControl,
|
|
993
977
|
compressionLz4,
|
|
994
978
|
idCompressorMode,
|
|
995
|
-
opGroupingEnabled,
|
|
979
|
+
opGroupingEnabled: enableGroupedBatching,
|
|
996
980
|
disallowedVersions: [],
|
|
997
981
|
},
|
|
998
982
|
(schema) => {
|
|
@@ -1001,7 +985,6 @@ export class ContainerRuntime
|
|
|
1001
985
|
);
|
|
1002
986
|
|
|
1003
987
|
const featureGatesForTelemetry: Record<string, boolean | number | undefined> = {
|
|
1004
|
-
disableGroupedBatching,
|
|
1005
988
|
disableCompression,
|
|
1006
989
|
};
|
|
1007
990
|
|
|
@@ -1022,7 +1005,6 @@ export class ContainerRuntime
|
|
|
1022
1005
|
chunkSizeInBytes,
|
|
1023
1006
|
// Requires<> drops undefined from IdCompressorType
|
|
1024
1007
|
enableRuntimeIdCompressor: enableRuntimeIdCompressor as "on" | "delayed",
|
|
1025
|
-
enableOpReentryCheck,
|
|
1026
1008
|
enableGroupedBatching,
|
|
1027
1009
|
explicitSchemaControl,
|
|
1028
1010
|
},
|
|
@@ -1115,7 +1097,17 @@ export class ContainerRuntime
|
|
|
1115
1097
|
return this._getAttachState();
|
|
1116
1098
|
}
|
|
1117
1099
|
|
|
1118
|
-
|
|
1100
|
+
/**
|
|
1101
|
+
* Current session schema - defines what options are on & off.
|
|
1102
|
+
* It's overlap of document schema (controlled by summary & ops) and options controlling this session.
|
|
1103
|
+
* For example, document schema might have compression ON, but feature gates / runtime options turn it Off.
|
|
1104
|
+
* In such case it will be off in session schema (i.e. this session should not use compression), but this client
|
|
1105
|
+
* has to deal with compressed ops as other clients might send them.
|
|
1106
|
+
* And in reverse, session schema can have compression Off, but feature gates / runtime options want it On.
|
|
1107
|
+
* In such case it will be off in session schema, however this client will propose change to schema, and once / if
|
|
1108
|
+
* this op rountrips, compression will be On. Client can't send compressed ops until it's change in schema.
|
|
1109
|
+
*/
|
|
1110
|
+
public get sessionSchema() {
|
|
1119
1111
|
return this.documentsSchemaController.sessionSchema.runtime;
|
|
1120
1112
|
}
|
|
1121
1113
|
|
|
@@ -1128,11 +1120,11 @@ export class ContainerRuntime
|
|
|
1128
1120
|
// Id Compressor serializes final state (see getPendingLocalState()). As result, it needs to skip all ops that preceeded that state
|
|
1129
1121
|
// (such ops will be marked by Loader layer as savedOp === true)
|
|
1130
1122
|
// That said, in "delayed" mode it's possible that Id Compressor was never initialized before getPendingLocalState() is called.
|
|
1131
|
-
// In such case we have to process all ops, including those marked with
|
|
1123
|
+
// In such case we have to process all ops, including those marked with savedOp === true.
|
|
1132
1124
|
private readonly skipSavedCompressorOps: boolean;
|
|
1133
1125
|
|
|
1134
1126
|
public get idCompressorMode() {
|
|
1135
|
-
return this.
|
|
1127
|
+
return this.sessionSchema.idCompressorMode;
|
|
1136
1128
|
}
|
|
1137
1129
|
/**
|
|
1138
1130
|
* See IContainerRuntimeBase.idCompressor() for details.
|
|
@@ -1215,14 +1207,6 @@ export class ContainerRuntime
|
|
|
1215
1207
|
|
|
1216
1208
|
private ensureNoDataModelChangesCalls = 0;
|
|
1217
1209
|
|
|
1218
|
-
/**
|
|
1219
|
-
* Tracks the number of detected reentrant ops to report,
|
|
1220
|
-
* in order to self-throttle the telemetry events.
|
|
1221
|
-
*
|
|
1222
|
-
* This should be removed as part of ADO:2322
|
|
1223
|
-
*/
|
|
1224
|
-
private opReentryCallsToReport = 5;
|
|
1225
|
-
|
|
1226
1210
|
/**
|
|
1227
1211
|
* Invokes the given callback and expects that no ops are submitted
|
|
1228
1212
|
* until execution finishes. If an op is submitted, an error will be raised.
|
|
@@ -1256,16 +1240,8 @@ export class ContainerRuntime
|
|
|
1256
1240
|
|
|
1257
1241
|
private dirtyContainer: boolean;
|
|
1258
1242
|
private emitDirtyDocumentEvent = true;
|
|
1259
|
-
private readonly enableOpReentryCheck: boolean;
|
|
1260
1243
|
private readonly disableAttachReorder: boolean | undefined;
|
|
1261
1244
|
private readonly closeSummarizerDelayMs: number;
|
|
1262
|
-
/**
|
|
1263
|
-
* If true, summary generated is validate before uploading it to the server. With single commit summaries,
|
|
1264
|
-
* summaries will be accepted once uploaded, so they should be validated before upload. However, this can
|
|
1265
|
-
* currently be controlled via a feature flag as its a new functionality.
|
|
1266
|
-
*/
|
|
1267
|
-
private readonly validateSummaryBeforeUpload: boolean;
|
|
1268
|
-
|
|
1269
1245
|
private readonly defaultTelemetrySignalSampleCount = 100;
|
|
1270
1246
|
private readonly _perfSignalData: IPerfSignalReport = {
|
|
1271
1247
|
signalsLost: 0,
|
|
@@ -1410,6 +1386,7 @@ export class ContainerRuntime
|
|
|
1410
1386
|
loader,
|
|
1411
1387
|
pendingLocalState,
|
|
1412
1388
|
supportedFeatures,
|
|
1389
|
+
snapshotWithContents,
|
|
1413
1390
|
} = context;
|
|
1414
1391
|
|
|
1415
1392
|
this.mc = createChildMonitoringContext({
|
|
@@ -1422,14 +1399,13 @@ export class ContainerRuntime
|
|
|
1422
1399
|
// If it's not in the list, then we will need to either use no compression, or fallback to some other (supported by format)
|
|
1423
1400
|
// compression.
|
|
1424
1401
|
const compressionOptions: ICompressionRuntimeOptions = {
|
|
1425
|
-
minimumBatchSizeInBytes: this.
|
|
1402
|
+
minimumBatchSizeInBytes: this.sessionSchema.compressionLz4
|
|
1426
1403
|
? runtimeOptions.compressionOptions.minimumBatchSizeInBytes
|
|
1427
1404
|
: Number.POSITIVE_INFINITY,
|
|
1428
1405
|
compressionAlgorithm: CompressionAlgorithms.lz4,
|
|
1429
1406
|
};
|
|
1430
1407
|
|
|
1431
1408
|
this.innerDeltaManager = deltaManager;
|
|
1432
|
-
this.deltaManager = new DeltaManagerSummarizerProxy(this.innerDeltaManager);
|
|
1433
1409
|
|
|
1434
1410
|
// Here we could wrap/intercept on these functions to block/modify outgoing messages if needed.
|
|
1435
1411
|
// This makes ContainerRuntime the final gatekeeper for outgoing messages.
|
|
@@ -1534,20 +1510,50 @@ export class ContainerRuntime
|
|
|
1534
1510
|
opGroupingManager,
|
|
1535
1511
|
);
|
|
1536
1512
|
|
|
1513
|
+
const pendingRuntimeState = pendingLocalState as IPendingRuntimeState | undefined;
|
|
1514
|
+
this.pendingStateManager = new PendingStateManager(
|
|
1515
|
+
{
|
|
1516
|
+
applyStashedOp: this.applyStashedOp.bind(this),
|
|
1517
|
+
clientId: () => this.clientId,
|
|
1518
|
+
close: this.closeFn,
|
|
1519
|
+
connected: () => this.connected,
|
|
1520
|
+
reSubmit: (message: IPendingBatchMessage) => {
|
|
1521
|
+
this.reSubmit(message);
|
|
1522
|
+
this.flush();
|
|
1523
|
+
},
|
|
1524
|
+
reSubmitBatch: this.reSubmitBatch.bind(this),
|
|
1525
|
+
isActiveConnection: () => this.innerDeltaManager.active,
|
|
1526
|
+
isAttached: () => this.attachState !== AttachState.Detached,
|
|
1527
|
+
},
|
|
1528
|
+
pendingRuntimeState?.pending,
|
|
1529
|
+
this.logger,
|
|
1530
|
+
);
|
|
1531
|
+
|
|
1532
|
+
let outerDeltaManager: IDeltaManager<ISequencedDocumentMessage, IDocumentMessage>;
|
|
1533
|
+
const useDeltaManagerOpsProxy =
|
|
1534
|
+
this.mc.config.getBoolean("Fluid.ContainerRuntime.DeltaManagerOpsProxy") !== false;
|
|
1535
|
+
// The summarizerDeltaManager Proxy is used to lie to the summarizer to convince it is in the right state as a summarizer client.
|
|
1536
|
+
const summarizerDeltaManagerProxy = new DeltaManagerSummarizerProxy(this.innerDeltaManager);
|
|
1537
|
+
outerDeltaManager = summarizerDeltaManagerProxy;
|
|
1538
|
+
|
|
1539
|
+
// The DeltaManagerPendingOpsProxy is used to control the minimum sequence number
|
|
1540
|
+
// It allows us to lie to the layers below so that they can maintain enough local state for rebasing ops.
|
|
1541
|
+
if (useDeltaManagerOpsProxy) {
|
|
1542
|
+
const pendingOpsDeltaManagerProxy = new DeltaManagerPendingOpsProxy(
|
|
1543
|
+
summarizerDeltaManagerProxy,
|
|
1544
|
+
this.pendingStateManager,
|
|
1545
|
+
);
|
|
1546
|
+
outerDeltaManager = pendingOpsDeltaManagerProxy;
|
|
1547
|
+
}
|
|
1548
|
+
|
|
1549
|
+
this.deltaManager = outerDeltaManager;
|
|
1550
|
+
|
|
1537
1551
|
this.handleContext = new ContainerFluidHandleContext("", this);
|
|
1538
1552
|
|
|
1539
1553
|
if (this.summaryConfiguration.state === "enabled") {
|
|
1540
1554
|
this.validateSummaryHeuristicConfiguration(this.summaryConfiguration);
|
|
1541
1555
|
}
|
|
1542
1556
|
|
|
1543
|
-
const disableOpReentryCheck = this.mc.config.getBoolean(
|
|
1544
|
-
"Fluid.ContainerRuntime.DisableOpReentryCheck",
|
|
1545
|
-
);
|
|
1546
|
-
this.enableOpReentryCheck =
|
|
1547
|
-
runtimeOptions.enableOpReentryCheck === true &&
|
|
1548
|
-
// Allow for a break-glass config to override the options
|
|
1549
|
-
disableOpReentryCheck !== true;
|
|
1550
|
-
|
|
1551
1557
|
this.summariesDisabled = this.isSummariesDisabled();
|
|
1552
1558
|
this.maxOpsSinceLastSummary = this.getMaxOpsSinceLastSummary();
|
|
1553
1559
|
this.initialSummarizerDelayMs = this.getInitialSummarizerDelayMs();
|
|
@@ -1567,8 +1573,6 @@ export class ContainerRuntime
|
|
|
1567
1573
|
this._flushMode = runtimeOptions.flushMode;
|
|
1568
1574
|
}
|
|
1569
1575
|
|
|
1570
|
-
const pendingRuntimeState = pendingLocalState as IPendingRuntimeState | undefined;
|
|
1571
|
-
|
|
1572
1576
|
if (context.attachState === AttachState.Attached) {
|
|
1573
1577
|
const maxSnapshotCacheDurationMs = this._storage?.policies?.maximumCacheDurationMs;
|
|
1574
1578
|
if (
|
|
@@ -1640,8 +1644,19 @@ export class ContainerRuntime
|
|
|
1640
1644
|
return this.submitSignalFn(envelope2, targetClientId);
|
|
1641
1645
|
};
|
|
1642
1646
|
|
|
1647
|
+
let snapshot: ISnapshot | ISnapshotTree | undefined = getSummaryForDatastores(
|
|
1648
|
+
baseSnapshot,
|
|
1649
|
+
metadata,
|
|
1650
|
+
);
|
|
1651
|
+
if (snapshot !== undefined && snapshotWithContents !== undefined) {
|
|
1652
|
+
snapshot = {
|
|
1653
|
+
...snapshotWithContents,
|
|
1654
|
+
snapshotTree: snapshot,
|
|
1655
|
+
};
|
|
1656
|
+
}
|
|
1657
|
+
|
|
1643
1658
|
this.channelCollection = new ChannelCollection(
|
|
1644
|
-
|
|
1659
|
+
snapshot,
|
|
1645
1660
|
parentContext,
|
|
1646
1661
|
this.mc.logger,
|
|
1647
1662
|
(props) => this.garbageCollector.nodeUpdated(props),
|
|
@@ -1684,24 +1699,6 @@ export class ContainerRuntime
|
|
|
1684
1699
|
createChildLogger({ logger: this.logger, namespace: "ScheduleManager" }),
|
|
1685
1700
|
);
|
|
1686
1701
|
|
|
1687
|
-
this.pendingStateManager = new PendingStateManager(
|
|
1688
|
-
{
|
|
1689
|
-
applyStashedOp: this.applyStashedOp.bind(this),
|
|
1690
|
-
clientId: () => this.clientId,
|
|
1691
|
-
close: this.closeFn,
|
|
1692
|
-
connected: () => this.connected,
|
|
1693
|
-
reSubmit: (message: IPendingBatchMessage) => {
|
|
1694
|
-
this.reSubmit(message);
|
|
1695
|
-
this.flush();
|
|
1696
|
-
},
|
|
1697
|
-
reSubmitBatch: this.reSubmitBatch.bind(this),
|
|
1698
|
-
isActiveConnection: () => this.innerDeltaManager.active,
|
|
1699
|
-
isAttached: () => this.attachState !== AttachState.Detached,
|
|
1700
|
-
},
|
|
1701
|
-
pendingRuntimeState?.pending,
|
|
1702
|
-
this.logger,
|
|
1703
|
-
);
|
|
1704
|
-
|
|
1705
1702
|
const disablePartialFlush = this.mc.config.getBoolean(
|
|
1706
1703
|
"Fluid.ContainerRuntime.DisablePartialFlush",
|
|
1707
1704
|
);
|
|
@@ -1736,16 +1733,37 @@ export class ContainerRuntime
|
|
|
1736
1733
|
this.remoteMessageProcessor.clearPartialMessagesFor(clientId);
|
|
1737
1734
|
});
|
|
1738
1735
|
|
|
1739
|
-
|
|
1740
|
-
|
|
1736
|
+
this._audience = audience;
|
|
1737
|
+
if (audience.getSelf === undefined) {
|
|
1738
|
+
// back-compat, added in 2.0 RC3.
|
|
1739
|
+
// Purpose: deal with cases when we run against old loader that does not have newly added capabilities
|
|
1740
|
+
audience.getSelf = () => {
|
|
1741
|
+
const clientId = this._getClientId();
|
|
1742
|
+
return clientId === undefined
|
|
1743
|
+
? undefined
|
|
1744
|
+
: ({
|
|
1745
|
+
clientId,
|
|
1746
|
+
client: audience.getMember(clientId),
|
|
1747
|
+
} satisfies ISelf);
|
|
1748
|
+
};
|
|
1749
|
+
|
|
1750
|
+
let oldClientId = this.clientId;
|
|
1751
|
+
this.on("connected", () => {
|
|
1752
|
+
const clientId = this.clientId;
|
|
1753
|
+
assert(clientId !== undefined, 0x975 /* can't be undefined */);
|
|
1754
|
+
(audience as unknown as TypedEventEmitter<IAudienceEvents>).emit(
|
|
1755
|
+
"selfChanged",
|
|
1756
|
+
{ clientId: oldClientId },
|
|
1757
|
+
{ clientId, client: audience.getMember(clientId) },
|
|
1758
|
+
);
|
|
1759
|
+
oldClientId = clientId;
|
|
1760
|
+
});
|
|
1761
|
+
}
|
|
1741
1762
|
|
|
1742
1763
|
const closeSummarizerDelayOverride = this.mc.config.getNumber(
|
|
1743
1764
|
"Fluid.ContainerRuntime.Test.CloseSummarizerDelayOverrideMs",
|
|
1744
1765
|
);
|
|
1745
1766
|
this.closeSummarizerDelayMs = closeSummarizerDelayOverride ?? defaultCloseSummarizerDelayMs;
|
|
1746
|
-
this.validateSummaryBeforeUpload =
|
|
1747
|
-
this.mc.config.getBoolean("Fluid.Summarizer.ValidateSummaryBeforeUpload") ?? false;
|
|
1748
|
-
|
|
1749
1767
|
this.summaryCollection = new SummaryCollection(this.deltaManager, this.logger);
|
|
1750
1768
|
|
|
1751
1769
|
this.dirtyContainer =
|
|
@@ -1857,9 +1875,9 @@ export class ContainerRuntime
|
|
|
1857
1875
|
options: JSON.stringify(runtimeOptions),
|
|
1858
1876
|
idCompressorModeMetadata: metadata?.documentSchema?.runtime?.idCompressorMode,
|
|
1859
1877
|
idCompressorMode: this.idCompressorMode,
|
|
1878
|
+
sessionRuntimeSchema: JSON.stringify(this.sessionSchema),
|
|
1860
1879
|
featureGates: JSON.stringify({
|
|
1861
1880
|
...featureGatesForTelemetry,
|
|
1862
|
-
disableOpReentryCheck,
|
|
1863
1881
|
disableChunking,
|
|
1864
1882
|
disableAttachReorder: this.disableAttachReorder,
|
|
1865
1883
|
disablePartialFlush,
|
|
@@ -1867,6 +1885,7 @@ export class ContainerRuntime
|
|
|
1867
1885
|
}),
|
|
1868
1886
|
telemetryDocumentId: this.telemetryDocumentId,
|
|
1869
1887
|
groupedBatchingEnabled: this.groupedBatchingEnabled,
|
|
1888
|
+
initialSequenceNumber: this.deltaManager.initialSequenceNumber,
|
|
1870
1889
|
});
|
|
1871
1890
|
|
|
1872
1891
|
ReportOpPerfTelemetry(this.clientId, this.deltaManager, this, this.logger);
|
|
@@ -1889,6 +1908,11 @@ export class ContainerRuntime
|
|
|
1889
1908
|
}
|
|
1890
1909
|
|
|
1891
1910
|
public onSchemaChange(schema: IDocumentSchemaCurrent) {
|
|
1911
|
+
this.logger.sendTelemetryEvent({
|
|
1912
|
+
eventName: "SchemaChangeAccept",
|
|
1913
|
+
sessionRuntimeSchema: JSON.stringify(schema),
|
|
1914
|
+
});
|
|
1915
|
+
|
|
1892
1916
|
// Most of the settings will be picked up only by new sessions (i.e. after reload).
|
|
1893
1917
|
// We can make it better in the future (i.e. start to use op compression right away), but for simplicity
|
|
1894
1918
|
// this is not done.
|
|
@@ -1938,9 +1962,9 @@ export class ContainerRuntime
|
|
|
1938
1962
|
this.idCompressorMode === "on" ||
|
|
1939
1963
|
(this.idCompressorMode === "delayed" && this.connected)
|
|
1940
1964
|
) {
|
|
1965
|
+
this._idCompressor = await this.createIdCompressor();
|
|
1941
1966
|
// This is called from loadRuntime(), long before we process any ops, so there should be no ops accumulated yet.
|
|
1942
1967
|
assert(this.pendingIdCompressorOps.length === 0, 0x8ec /* no pending ops */);
|
|
1943
|
-
this._idCompressor = await this.createIdCompressor();
|
|
1944
1968
|
}
|
|
1945
1969
|
|
|
1946
1970
|
await this.garbageCollector.initializeBaseState();
|
|
@@ -2318,6 +2342,7 @@ export class ContainerRuntime
|
|
|
2318
2342
|
let newState: boolean;
|
|
2319
2343
|
|
|
2320
2344
|
try {
|
|
2345
|
+
this.submitIdAllocationOpIfNeeded(true);
|
|
2321
2346
|
// replay the ops
|
|
2322
2347
|
this.pendingStateManager.replayPendingStates();
|
|
2323
2348
|
} finally {
|
|
@@ -2370,8 +2395,6 @@ export class ContainerRuntime
|
|
|
2370
2395
|
return;
|
|
2371
2396
|
case ContainerMessageType.BlobAttach:
|
|
2372
2397
|
return;
|
|
2373
|
-
case ContainerMessageType.ChunkedOp:
|
|
2374
|
-
throw new Error("chunkedOp not expected here");
|
|
2375
2398
|
case ContainerMessageType.Rejoin:
|
|
2376
2399
|
throw new Error("rejoin not expected here");
|
|
2377
2400
|
case ContainerMessageType.GC:
|
|
@@ -2384,7 +2407,7 @@ export class ContainerRuntime
|
|
|
2384
2407
|
const compatBehavior = opContents.compatDetails?.behavior;
|
|
2385
2408
|
if (!compatBehaviorAllowsMessageType(opContents.type, compatBehavior)) {
|
|
2386
2409
|
const error = DataProcessingError.create(
|
|
2387
|
-
"Stashed runtime message of
|
|
2410
|
+
"Stashed runtime message of unexpected type",
|
|
2388
2411
|
"applyStashedOp",
|
|
2389
2412
|
undefined /* sequencedMessage */,
|
|
2390
2413
|
{
|
|
@@ -2411,12 +2434,14 @@ export class ContainerRuntime
|
|
|
2411
2434
|
) {
|
|
2412
2435
|
this._loadIdCompressor = this.createIdCompressor()
|
|
2413
2436
|
.then((compressor) => {
|
|
2414
|
-
this._idCompressor = compressor;
|
|
2415
2437
|
// Finalize any ranges we received while the compressor was turned off.
|
|
2416
|
-
|
|
2417
|
-
this._idCompressor.finalizeCreationRange(range);
|
|
2418
|
-
}
|
|
2438
|
+
const ops = this.pendingIdCompressorOps;
|
|
2419
2439
|
this.pendingIdCompressorOps = [];
|
|
2440
|
+
for (const range of ops) {
|
|
2441
|
+
compressor.finalizeCreationRange(range);
|
|
2442
|
+
}
|
|
2443
|
+
assert(this.pendingIdCompressorOps.length === 0, 0x976 /* No new ops added */);
|
|
2444
|
+
this._idCompressor = compressor;
|
|
2420
2445
|
})
|
|
2421
2446
|
.catch((error) => {
|
|
2422
2447
|
this.logger.sendErrorEvent({ eventName: "IdCompressorDelayedLoad" }, error);
|
|
@@ -2427,6 +2452,14 @@ export class ContainerRuntime
|
|
|
2427
2452
|
}
|
|
2428
2453
|
|
|
2429
2454
|
public setConnectionState(connected: boolean, clientId?: string) {
|
|
2455
|
+
// Validate we have consistent state
|
|
2456
|
+
const currentClientId = this._audience.getSelf()?.clientId;
|
|
2457
|
+
assert(clientId === currentClientId, 0x977 /* input clientId does not match Audience */);
|
|
2458
|
+
assert(
|
|
2459
|
+
this.clientId === currentClientId,
|
|
2460
|
+
0x978 /* this.clientId does not match Audience */,
|
|
2461
|
+
);
|
|
2462
|
+
|
|
2430
2463
|
if (connected && this.idCompressorMode === "delayed") {
|
|
2431
2464
|
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
2432
2465
|
this.loadIdCompressor();
|
|
@@ -2540,21 +2573,28 @@ export class ContainerRuntime
|
|
|
2540
2573
|
// We do not need to make a deep copy. Each layer will just replace message.contents itself,
|
|
2541
2574
|
// but will not modify the contents object (likely it will replace it on the message).
|
|
2542
2575
|
const messageCopy = { ...messageArg };
|
|
2576
|
+
const savedOp = (messageCopy.metadata as ISavedOpMetadata)?.savedOp;
|
|
2543
2577
|
for (const message of this.remoteMessageProcessor.process(messageCopy)) {
|
|
2544
|
-
|
|
2545
|
-
|
|
2546
|
-
|
|
2547
|
-
|
|
2548
|
-
|
|
2549
|
-
|
|
2550
|
-
|
|
2551
|
-
|
|
2552
|
-
|
|
2553
|
-
|
|
2554
|
-
|
|
2555
|
-
|
|
2556
|
-
|
|
2557
|
-
|
|
2578
|
+
const msg: MessageWithContext = modernRuntimeMessage
|
|
2579
|
+
? {
|
|
2580
|
+
// Cast it since we expect it to be this based on modernRuntimeMessage computation above.
|
|
2581
|
+
// There is nothing really ensuring that anytime original message.type is Operation that
|
|
2582
|
+
// the result messages will be so. In the end modern bool being true only directs to
|
|
2583
|
+
// throw error if ultimately unrecognized without compat details saying otherwise.
|
|
2584
|
+
message: message as InboundSequencedContainerRuntimeMessage,
|
|
2585
|
+
local,
|
|
2586
|
+
modernRuntimeMessage,
|
|
2587
|
+
}
|
|
2588
|
+
: // Unrecognized message will be ignored.
|
|
2589
|
+
{
|
|
2590
|
+
message,
|
|
2591
|
+
local,
|
|
2592
|
+
modernRuntimeMessage,
|
|
2593
|
+
};
|
|
2594
|
+
msg.savedOp = savedOp;
|
|
2595
|
+
|
|
2596
|
+
// ensure that we observe any re-entrancy, and if needed, rebase ops
|
|
2597
|
+
this.ensureNoDataModelChanges(() => this.processCore(msg));
|
|
2558
2598
|
}
|
|
2559
2599
|
}
|
|
2560
2600
|
|
|
@@ -2565,6 +2605,17 @@ export class ContainerRuntime
|
|
|
2565
2605
|
*/
|
|
2566
2606
|
private processCore(messageWithContext: MessageWithContext) {
|
|
2567
2607
|
const { message, local } = messageWithContext;
|
|
2608
|
+
|
|
2609
|
+
// Intercept to reduce minimum sequence number to the delta manager's minimum sequence number.
|
|
2610
|
+
// Sequence numbers are not guaranteed to follow any sort of order. Re-entrancy is one of those situations
|
|
2611
|
+
if (
|
|
2612
|
+
this.deltaManager.minimumSequenceNumber <
|
|
2613
|
+
messageWithContext.message.minimumSequenceNumber
|
|
2614
|
+
) {
|
|
2615
|
+
messageWithContext.message.minimumSequenceNumber =
|
|
2616
|
+
this.deltaManager.minimumSequenceNumber;
|
|
2617
|
+
}
|
|
2618
|
+
|
|
2568
2619
|
// Surround the actual processing of the operation with messages to the schedule manager indicating
|
|
2569
2620
|
// the beginning and end. This allows it to emit appropriate events and/or pause the processing of new
|
|
2570
2621
|
// messages once a batch has been fully processed.
|
|
@@ -2642,13 +2693,7 @@ export class ContainerRuntime
|
|
|
2642
2693
|
// stashed ops flow. The compressor is stashed with these ops already processed.
|
|
2643
2694
|
// That said, in idCompressorMode === "delayed", we might not serialize ID compressor, and
|
|
2644
2695
|
// thus we need to process all the ops.
|
|
2645
|
-
if (
|
|
2646
|
-
!(
|
|
2647
|
-
this.skipSavedCompressorOps &&
|
|
2648
|
-
(messageWithContext.message.metadata as IIdAllocationMetadata)?.savedOp ===
|
|
2649
|
-
true
|
|
2650
|
-
)
|
|
2651
|
-
) {
|
|
2696
|
+
if (!(this.skipSavedCompressorOps && messageWithContext.savedOp === true)) {
|
|
2652
2697
|
const range = messageWithContext.message.contents;
|
|
2653
2698
|
// Some other client turned on the id compressor. If we have not turned it on,
|
|
2654
2699
|
// put it in a pending queue and delay finalization.
|
|
@@ -2659,6 +2704,10 @@ export class ContainerRuntime
|
|
|
2659
2704
|
);
|
|
2660
2705
|
this.pendingIdCompressorOps.push(range);
|
|
2661
2706
|
} else {
|
|
2707
|
+
assert(
|
|
2708
|
+
this.pendingIdCompressorOps.length === 0,
|
|
2709
|
+
0x979 /* there should be no pending ops! */,
|
|
2710
|
+
);
|
|
2662
2711
|
this._idCompressor.finalizeCreationRange(range);
|
|
2663
2712
|
}
|
|
2664
2713
|
}
|
|
@@ -2809,9 +2858,9 @@ export class ContainerRuntime
|
|
|
2809
2858
|
let checkpoint: IBatchCheckpoint | undefined;
|
|
2810
2859
|
let result: T;
|
|
2811
2860
|
if (this.mc.config.getBoolean("Fluid.ContainerRuntime.EnableRollback")) {
|
|
2812
|
-
// Note: we are not touching
|
|
2813
|
-
// 1. It would not help, as
|
|
2814
|
-
// 2. There is no way to undo process of data store creation.
|
|
2861
|
+
// Note: we are not touching any batches other than mainBatch here, for two reasons:
|
|
2862
|
+
// 1. It would not help, as other batches are flushed independently from main batch.
|
|
2863
|
+
// 2. There is no way to undo process of data store creation, blob creation, ID compressor ops, or other things tracked by other batches.
|
|
2815
2864
|
checkpoint = this.outbox.checkpoint().mainBatch;
|
|
2816
2865
|
}
|
|
2817
2866
|
try {
|
|
@@ -3393,10 +3442,14 @@ export class ContainerRuntime
|
|
|
3393
3442
|
// The summary number for this summary. This will be updated during the summary process, so get it now and
|
|
3394
3443
|
// use it for all events logged during this summary.
|
|
3395
3444
|
const summaryNumber = this.nextSummaryNumber;
|
|
3445
|
+
let summaryRefSeqNum: number | undefined;
|
|
3396
3446
|
const summaryNumberLogger = createChildLogger({
|
|
3397
3447
|
logger: summaryLogger,
|
|
3398
3448
|
properties: {
|
|
3399
|
-
all: {
|
|
3449
|
+
all: {
|
|
3450
|
+
summaryNumber,
|
|
3451
|
+
referenceSequenceNumber: () => summaryRefSeqNum,
|
|
3452
|
+
},
|
|
3400
3453
|
},
|
|
3401
3454
|
});
|
|
3402
3455
|
|
|
@@ -3415,7 +3468,7 @@ export class ContainerRuntime
|
|
|
3415
3468
|
// If the container is dirty, i.e., there are pending unacked ops, the summary will not be eventual consistent
|
|
3416
3469
|
// and it may even be incorrect. So, wait for the container to be saved with a timeout. If the container is not
|
|
3417
3470
|
// saved within the timeout, check if it should be failed or can continue.
|
|
3418
|
-
if (this.
|
|
3471
|
+
if (this.isDirty) {
|
|
3419
3472
|
const countBefore = this.pendingMessagesCount;
|
|
3420
3473
|
// The timeout for waiting for pending ops can be overridden via configurations.
|
|
3421
3474
|
const pendingOpsTimeout =
|
|
@@ -3465,8 +3518,6 @@ export class ContainerRuntime
|
|
|
3465
3518
|
"Fluid.ContainerRuntime.SubmitSummary.shouldValidatePreSummaryState",
|
|
3466
3519
|
) === true;
|
|
3467
3520
|
|
|
3468
|
-
let summaryRefSeqNum: number | undefined;
|
|
3469
|
-
|
|
3470
3521
|
try {
|
|
3471
3522
|
await this.deltaManager.inbound.pause();
|
|
3472
3523
|
if (shouldPauseInboundSignal) {
|
|
@@ -3509,7 +3560,9 @@ export class ContainerRuntime
|
|
|
3509
3560
|
stage: "base",
|
|
3510
3561
|
referenceSequenceNumber: summaryRefSeqNum,
|
|
3511
3562
|
minimumSequenceNumber,
|
|
3512
|
-
error:
|
|
3563
|
+
error: new LoggingError(
|
|
3564
|
+
`Summarizer node state inconsistent with summarizer state.`,
|
|
3565
|
+
),
|
|
3513
3566
|
};
|
|
3514
3567
|
}
|
|
3515
3568
|
}
|
|
@@ -3559,7 +3612,7 @@ export class ContainerRuntime
|
|
|
3559
3612
|
stage: "base",
|
|
3560
3613
|
referenceSequenceNumber: summaryRefSeqNum,
|
|
3561
3614
|
minimumSequenceNumber,
|
|
3562
|
-
error: continueResult.error,
|
|
3615
|
+
error: new LoggingError(continueResult.error),
|
|
3563
3616
|
};
|
|
3564
3617
|
}
|
|
3565
3618
|
|
|
@@ -3580,39 +3633,38 @@ export class ContainerRuntime
|
|
|
3580
3633
|
stage: "base",
|
|
3581
3634
|
referenceSequenceNumber: summaryRefSeqNum,
|
|
3582
3635
|
minimumSequenceNumber,
|
|
3583
|
-
error,
|
|
3636
|
+
error: wrapError(error, (msg) => new LoggingError(msg)),
|
|
3584
3637
|
};
|
|
3585
3638
|
}
|
|
3586
3639
|
|
|
3587
|
-
//
|
|
3588
|
-
|
|
3589
|
-
|
|
3590
|
-
const
|
|
3591
|
-
|
|
3592
|
-
|
|
3593
|
-
|
|
3594
|
-
|
|
3595
|
-
validateResult.retryAfterSeconds,
|
|
3596
|
-
{ ...loggingProps },
|
|
3597
|
-
);
|
|
3598
|
-
return {
|
|
3599
|
-
stage: "base",
|
|
3600
|
-
referenceSequenceNumber: summaryRefSeqNum,
|
|
3601
|
-
minimumSequenceNumber,
|
|
3602
|
-
error,
|
|
3603
|
-
};
|
|
3604
|
-
}
|
|
3605
|
-
|
|
3606
|
-
const pendingMessagesFailResult = await this.shouldFailSummaryOnPendingOps(
|
|
3607
|
-
summaryNumberLogger,
|
|
3608
|
-
summaryRefSeqNum,
|
|
3609
|
-
minimumSequenceNumber,
|
|
3610
|
-
finalAttempt,
|
|
3611
|
-
false /* beforeSummaryGeneration */,
|
|
3640
|
+
// Validate that the summary generated by summarizer nodes is correct before uploading.
|
|
3641
|
+
const validateResult = this.summarizerNode.validateSummary();
|
|
3642
|
+
if (!validateResult.success) {
|
|
3643
|
+
const { success, ...loggingProps } = validateResult;
|
|
3644
|
+
const error = new RetriableSummaryError(
|
|
3645
|
+
validateResult.reason,
|
|
3646
|
+
validateResult.retryAfterSeconds,
|
|
3647
|
+
{ ...loggingProps },
|
|
3612
3648
|
);
|
|
3613
|
-
|
|
3614
|
-
|
|
3615
|
-
|
|
3649
|
+
return {
|
|
3650
|
+
stage: "base",
|
|
3651
|
+
referenceSequenceNumber: summaryRefSeqNum,
|
|
3652
|
+
minimumSequenceNumber,
|
|
3653
|
+
error,
|
|
3654
|
+
};
|
|
3655
|
+
}
|
|
3656
|
+
|
|
3657
|
+
// If there are pending unacked ops, this summary attempt may fail as the uploaded
|
|
3658
|
+
// summary would be eventually inconsistent.
|
|
3659
|
+
const pendingMessagesFailResult = await this.shouldFailSummaryOnPendingOps(
|
|
3660
|
+
summaryNumberLogger,
|
|
3661
|
+
summaryRefSeqNum,
|
|
3662
|
+
minimumSequenceNumber,
|
|
3663
|
+
finalAttempt,
|
|
3664
|
+
false /* beforeSummaryGeneration */,
|
|
3665
|
+
);
|
|
3666
|
+
if (pendingMessagesFailResult !== undefined) {
|
|
3667
|
+
return pendingMessagesFailResult;
|
|
3616
3668
|
}
|
|
3617
3669
|
|
|
3618
3670
|
const { summary: summaryTree, stats: partialStats } = summarizeResult;
|
|
@@ -3653,7 +3705,11 @@ export class ContainerRuntime
|
|
|
3653
3705
|
|
|
3654
3706
|
continueResult = checkContinue();
|
|
3655
3707
|
if (!continueResult.continue) {
|
|
3656
|
-
return {
|
|
3708
|
+
return {
|
|
3709
|
+
stage: "generate",
|
|
3710
|
+
...generateSummaryData,
|
|
3711
|
+
error: new LoggingError(continueResult.error),
|
|
3712
|
+
};
|
|
3657
3713
|
}
|
|
3658
3714
|
|
|
3659
3715
|
const summaryContext =
|
|
@@ -3676,7 +3732,11 @@ export class ContainerRuntime
|
|
|
3676
3732
|
summaryContext,
|
|
3677
3733
|
);
|
|
3678
3734
|
} catch (error) {
|
|
3679
|
-
return {
|
|
3735
|
+
return {
|
|
3736
|
+
stage: "generate",
|
|
3737
|
+
...generateSummaryData,
|
|
3738
|
+
error: wrapError(error, (msg) => new LoggingError(msg)),
|
|
3739
|
+
};
|
|
3680
3740
|
}
|
|
3681
3741
|
|
|
3682
3742
|
const parent = summaryContext.ackHandle;
|
|
@@ -3695,14 +3755,22 @@ export class ContainerRuntime
|
|
|
3695
3755
|
|
|
3696
3756
|
continueResult = checkContinue();
|
|
3697
3757
|
if (!continueResult.continue) {
|
|
3698
|
-
return {
|
|
3758
|
+
return {
|
|
3759
|
+
stage: "upload",
|
|
3760
|
+
...uploadData,
|
|
3761
|
+
error: new LoggingError(continueResult.error),
|
|
3762
|
+
};
|
|
3699
3763
|
}
|
|
3700
3764
|
|
|
3701
3765
|
let clientSequenceNumber: number;
|
|
3702
3766
|
try {
|
|
3703
3767
|
clientSequenceNumber = this.submitSummaryMessage(summaryMessage, summaryRefSeqNum);
|
|
3704
3768
|
} catch (error) {
|
|
3705
|
-
return {
|
|
3769
|
+
return {
|
|
3770
|
+
stage: "upload",
|
|
3771
|
+
...uploadData,
|
|
3772
|
+
error: wrapError(error, (msg) => new LoggingError(msg)),
|
|
3773
|
+
};
|
|
3706
3774
|
}
|
|
3707
3775
|
|
|
3708
3776
|
const submitData = {
|
|
@@ -3713,13 +3781,13 @@ export class ContainerRuntime
|
|
|
3713
3781
|
} as const;
|
|
3714
3782
|
|
|
3715
3783
|
try {
|
|
3716
|
-
|
|
3717
|
-
this.summarizerNode.completeSummary(
|
|
3718
|
-
handle,
|
|
3719
|
-
!this.validateSummaryBeforeUpload /* validate */,
|
|
3720
|
-
);
|
|
3784
|
+
this.summarizerNode.completeSummary(handle);
|
|
3721
3785
|
} catch (error) {
|
|
3722
|
-
return {
|
|
3786
|
+
return {
|
|
3787
|
+
stage: "upload",
|
|
3788
|
+
...uploadData,
|
|
3789
|
+
error: wrapError(error, (msg) => new LoggingError(msg)),
|
|
3790
|
+
};
|
|
3723
3791
|
}
|
|
3724
3792
|
return submitData;
|
|
3725
3793
|
} finally {
|
|
@@ -3846,14 +3914,16 @@ export class ContainerRuntime
|
|
|
3846
3914
|
public async uploadBlob(
|
|
3847
3915
|
blob: ArrayBufferLike,
|
|
3848
3916
|
signal?: AbortSignal,
|
|
3849
|
-
): Promise<
|
|
3917
|
+
): Promise<IFluidHandleInternal<ArrayBufferLike>> {
|
|
3850
3918
|
this.verifyNotClosed();
|
|
3851
3919
|
return this.blobManager.createBlob(blob, signal);
|
|
3852
3920
|
}
|
|
3853
3921
|
|
|
3854
|
-
private submitIdAllocationOpIfNeeded(): void {
|
|
3922
|
+
private submitIdAllocationOpIfNeeded(resubmitOutstandingRanges = false): void {
|
|
3855
3923
|
if (this._idCompressor) {
|
|
3856
|
-
const idRange =
|
|
3924
|
+
const idRange = resubmitOutstandingRanges
|
|
3925
|
+
? this.idCompressor?.takeUnfinalizedCreationRange()
|
|
3926
|
+
: this._idCompressor.takeNextCreationRange();
|
|
3857
3927
|
// Don't include the idRange if there weren't any Ids allocated
|
|
3858
3928
|
if (idRange?.ids !== undefined) {
|
|
3859
3929
|
const idAllocationMessage: ContainerRuntimeIdAllocationMessage = {
|
|
@@ -3875,7 +3945,6 @@ export class ContainerRuntime
|
|
|
3875
3945
|
metadata?: { localId: string; blobId?: string },
|
|
3876
3946
|
): void {
|
|
3877
3947
|
this.verifyNotClosed();
|
|
3878
|
-
this.verifyCanSubmitOps();
|
|
3879
3948
|
|
|
3880
3949
|
// There should be no ops in detached container state!
|
|
3881
3950
|
assert(
|
|
@@ -3923,6 +3992,14 @@ export class ContainerRuntime
|
|
|
3923
3992
|
// on this callback to do actual sending.
|
|
3924
3993
|
const contents = this.documentsSchemaController.maybeSendSchemaMessage();
|
|
3925
3994
|
if (contents) {
|
|
3995
|
+
this.logger.sendTelemetryEvent({
|
|
3996
|
+
eventName: "SchemaChangeProposal",
|
|
3997
|
+
refSeq: contents.refSeq,
|
|
3998
|
+
version: contents.version,
|
|
3999
|
+
newRuntimeSchema: JSON.stringify(contents.runtime),
|
|
4000
|
+
sessionRuntimeSchema: JSON.stringify(this.sessionSchema),
|
|
4001
|
+
oldRuntimeSchema: JSON.stringify(this.metadata?.documentSchema?.runtime),
|
|
4002
|
+
});
|
|
3926
4003
|
const msg: ContainerRuntimeDocumentSchemaMessage = {
|
|
3927
4004
|
type: ContainerMessageType.DocumentSchemaChange,
|
|
3928
4005
|
contents,
|
|
@@ -3933,33 +4010,7 @@ export class ContainerRuntime
|
|
|
3933
4010
|
});
|
|
3934
4011
|
}
|
|
3935
4012
|
|
|
3936
|
-
|
|
3937
|
-
// Is it safe:
|
|
3938
|
-
// Yes, this should be safe reordering. Newly created data stores are not visible through API surface.
|
|
3939
|
-
// They become visible only when aliased, or handle to some sub-element of newly created datastore
|
|
3940
|
-
// is stored in some DDS, i.e. only after some other op.
|
|
3941
|
-
// Why:
|
|
3942
|
-
// Attach ops are large, and expensive to process. Plus there are scenarios where a lot of new data
|
|
3943
|
-
// stores are created, causing issues like relay service throttling (too many ops) and catastrophic
|
|
3944
|
-
// failure (batch is too large). Pushing them earlier and outside of main batch should alleviate
|
|
3945
|
-
// these issues.
|
|
3946
|
-
// Cons:
|
|
3947
|
-
// 1. With large batches, relay service may throttle clients. Clients may disconnect while throttled.
|
|
3948
|
-
// This change creates new possibility of a lot of newly created data stores never being referenced
|
|
3949
|
-
// because client died before it had a change to submit the rest of the ops. This will create more
|
|
3950
|
-
// garbage that needs to be collected leveraging GC (Garbage Collection) feature.
|
|
3951
|
-
// 2. Sending ops out of order means they are excluded from rollback functionality. This is not an issue
|
|
3952
|
-
// today as rollback can't undo creation of data store. To some extent not sending them is a bigger
|
|
3953
|
-
// issue than sending.
|
|
3954
|
-
// Please note that this does not change file format, so it can be disabled in the future if this
|
|
3955
|
-
// optimization no longer makes sense (for example, batch compression may make it less appealing).
|
|
3956
|
-
if (
|
|
3957
|
-
this.currentlyBatching() &&
|
|
3958
|
-
type === ContainerMessageType.Attach &&
|
|
3959
|
-
this.disableAttachReorder !== true
|
|
3960
|
-
) {
|
|
3961
|
-
this.outbox.submitAttach(message);
|
|
3962
|
-
} else if (type === ContainerMessageType.BlobAttach) {
|
|
4013
|
+
if (type === ContainerMessageType.BlobAttach) {
|
|
3963
4014
|
// BlobAttach ops must have their metadata visible and cannot be grouped (see opGroupingManager.ts)
|
|
3964
4015
|
this.outbox.submitBlobAttach(message);
|
|
3965
4016
|
} else {
|
|
@@ -4048,37 +4099,6 @@ export class ContainerRuntime
|
|
|
4048
4099
|
}
|
|
4049
4100
|
}
|
|
4050
4101
|
|
|
4051
|
-
private verifyCanSubmitOps() {
|
|
4052
|
-
if (this.ensureNoDataModelChangesCalls > 0) {
|
|
4053
|
-
const errorMessage =
|
|
4054
|
-
"Op was submitted from within a `ensureNoDataModelChanges` callback";
|
|
4055
|
-
if (this.opReentryCallsToReport > 0) {
|
|
4056
|
-
this.mc.logger.sendTelemetryEvent(
|
|
4057
|
-
{ eventName: "OpReentry" },
|
|
4058
|
-
// We need to capture the call stack in order to inspect the source of this usage pattern
|
|
4059
|
-
getLongStack(() => new UsageError(errorMessage)),
|
|
4060
|
-
);
|
|
4061
|
-
this.opReentryCallsToReport--;
|
|
4062
|
-
}
|
|
4063
|
-
|
|
4064
|
-
// Creating ops while processing ops can lead
|
|
4065
|
-
// to undefined behavior and events observed in the wrong order.
|
|
4066
|
-
// For example, we have two callbacks registered for a DDS, A and B.
|
|
4067
|
-
// Then if on change #1 callback A creates change #2, the invocation flow will be:
|
|
4068
|
-
//
|
|
4069
|
-
// A because of #1
|
|
4070
|
-
// A because of #2
|
|
4071
|
-
// B because of #2
|
|
4072
|
-
// B because of #1
|
|
4073
|
-
//
|
|
4074
|
-
// The runtime must enforce op coherence by not allowing ops to be submitted
|
|
4075
|
-
// while ops are being processed.
|
|
4076
|
-
if (this.enableOpReentryCheck) {
|
|
4077
|
-
throw new UsageError(errorMessage);
|
|
4078
|
-
}
|
|
4079
|
-
}
|
|
4080
|
-
}
|
|
4081
|
-
|
|
4082
4102
|
private reSubmitBatch(batch: IPendingBatchMessage[]) {
|
|
4083
4103
|
this.orderSequentially(() => {
|
|
4084
4104
|
for (const message of batch) {
|
|
@@ -4119,11 +4139,15 @@ export class ContainerRuntime
|
|
|
4119
4139
|
this.channelCollection.reSubmit(message.type, message.contents, localOpMetadata);
|
|
4120
4140
|
break;
|
|
4121
4141
|
case ContainerMessageType.IdAllocation: {
|
|
4122
|
-
|
|
4142
|
+
// Allocation ops are never resubmitted/rebased. This is because they require special handling to
|
|
4143
|
+
// avoid being submitted out of order. For example, if the pending state manager contained
|
|
4144
|
+
// [idOp1, dataOp1, idOp2, dataOp2] and the resubmission of dataOp1 generated idOp3, that would be
|
|
4145
|
+
// placed into the outbox in the same batch as idOp1, but before idOp2 is resubmitted.
|
|
4146
|
+
// To avoid this, allocation ops are simply never resubmitted. Prior to invoking the pending state
|
|
4147
|
+
// manager to replay pending ops, the runtime will always submit a new allocation range that includes
|
|
4148
|
+
// all pending IDs. The resubmitted allocation ops are then ignored here.
|
|
4123
4149
|
break;
|
|
4124
4150
|
}
|
|
4125
|
-
case ContainerMessageType.ChunkedOp:
|
|
4126
|
-
throw new Error(`chunkedOp not expected here`);
|
|
4127
4151
|
case ContainerMessageType.BlobAttach:
|
|
4128
4152
|
this.blobManager.reSubmit(opMetadata);
|
|
4129
4153
|
break;
|
|
@@ -4150,7 +4174,7 @@ export class ContainerRuntime
|
|
|
4150
4174
|
});
|
|
4151
4175
|
} else {
|
|
4152
4176
|
const error = DataProcessingError.create(
|
|
4153
|
-
"Resubmitting runtime message of
|
|
4177
|
+
"Resubmitting runtime message of unexpected type",
|
|
4154
4178
|
"reSubmitCore",
|
|
4155
4179
|
undefined /* sequencedMessage */,
|
|
4156
4180
|
{
|
|
@@ -4243,7 +4267,7 @@ export class ContainerRuntime
|
|
|
4243
4267
|
|
|
4244
4268
|
return {
|
|
4245
4269
|
stage: "base",
|
|
4246
|
-
error: "summary state stale - Unsupported option 'refreshLatestAck'",
|
|
4270
|
+
error: new LoggingError("summary state stale - Unsupported option 'refreshLatestAck'"),
|
|
4247
4271
|
referenceSequenceNumber: this.deltaManager.lastSequenceNumber,
|
|
4248
4272
|
minimumSequenceNumber: this.deltaManager.minimumSequenceNumber,
|
|
4249
4273
|
};
|
|
@@ -4325,10 +4349,9 @@ export class ContainerRuntime
|
|
|
4325
4349
|
const getSyncState = (
|
|
4326
4350
|
pendingAttachmentBlobs?: IPendingBlobs,
|
|
4327
4351
|
): IPendingRuntimeState | undefined => {
|
|
4328
|
-
const pending = this.pendingStateManager.getLocalState();
|
|
4329
|
-
|
|
4330
|
-
|
|
4331
|
-
}
|
|
4352
|
+
const pending = this.pendingStateManager.getLocalState(props?.snapshotSequenceNumber);
|
|
4353
|
+
const sessionExpiryTimerStarted =
|
|
4354
|
+
props?.sessionExpiryTimerStarted ?? this.garbageCollector.sessionExpiryTimerStarted;
|
|
4332
4355
|
|
|
4333
4356
|
const pendingIdCompressorState = this._idCompressor?.serialize(true);
|
|
4334
4357
|
|
|
@@ -4336,7 +4359,7 @@ export class ContainerRuntime
|
|
|
4336
4359
|
pending,
|
|
4337
4360
|
pendingIdCompressorState,
|
|
4338
4361
|
pendingAttachmentBlobs,
|
|
4339
|
-
sessionExpiryTimerStarted
|
|
4362
|
+
sessionExpiryTimerStarted,
|
|
4340
4363
|
};
|
|
4341
4364
|
};
|
|
4342
4365
|
const perfEvent = {
|
|
@@ -4427,6 +4450,6 @@ export class ContainerRuntime
|
|
|
4427
4450
|
}
|
|
4428
4451
|
|
|
4429
4452
|
private get groupedBatchingEnabled(): boolean {
|
|
4430
|
-
return this.
|
|
4453
|
+
return this.sessionSchema.opGroupingEnabled === true;
|
|
4431
4454
|
}
|
|
4432
4455
|
}
|