@fluidframework/container-runtime 2.0.0-dev-rc.3.0.0.254866 → 2.0.0-dev-rc.5.0.0.263932
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 +69 -0
- package/api-report/container-runtime.api.md +93 -39
- 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 +10 -6
- package/dist/channelCollection.d.ts.map +1 -1
- package/dist/channelCollection.js +85 -22
- 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 +22 -35
- package/dist/containerRuntime.d.ts.map +1 -1
- package/dist/containerRuntime.js +232 -174
- 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 +2 -0
- package/dist/dataStoreContexts.d.ts.map +1 -1
- package/dist/dataStoreContexts.js +7 -0
- 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 +5 -12
- package/dist/gc/garbageCollection.d.ts.map +1 -1
- package/dist/gc/garbageCollection.js +45 -29
- package/dist/gc/garbageCollection.js.map +1 -1
- package/dist/gc/gcDefinitions.d.ts +27 -6
- package/dist/gc/gcDefinitions.d.ts.map +1 -1
- package/dist/gc/gcDefinitions.js.map +1 -1
- package/dist/gc/gcHelpers.d.ts +5 -4
- package/dist/gc/gcHelpers.d.ts.map +1 -1
- package/dist/gc/gcHelpers.js +14 -2
- package/dist/gc/gcHelpers.js.map +1 -1
- package/dist/gc/gcTelemetry.d.ts +14 -4
- package/dist/gc/gcTelemetry.d.ts.map +1 -1
- package/dist/gc/gcTelemetry.js +24 -21
- package/dist/gc/gcTelemetry.js.map +1 -1
- package/dist/gc/index.d.ts +2 -2
- package/dist/gc/index.d.ts.map +1 -1
- package/dist/gc/index.js +2 -2
- package/dist/gc/index.js.map +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/{alpha.d.ts → legacy.d.ts} +8 -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/opDecompressor.d.ts.map +1 -1
- package/dist/opLifecycle/opDecompressor.js +6 -6
- package/dist/opLifecycle/opDecompressor.js.map +1 -1
- package/dist/opLifecycle/opGroupingManager.js +2 -2
- package/dist/opLifecycle/opGroupingManager.js.map +1 -1
- package/dist/opLifecycle/opSplitter.js +1 -1
- package/dist/opLifecycle/opSplitter.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/public.d.ts +3 -0
- 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 +34 -16
- 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/beta.d.ts → internal.d.ts} +2 -0
- package/{dist/beta.d.ts → legacy.d.ts} +2 -0
- 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 +10 -6
- package/lib/channelCollection.d.ts.map +1 -1
- package/lib/channelCollection.js +88 -25
- 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 +22 -35
- package/lib/containerRuntime.d.ts.map +1 -1
- package/lib/containerRuntime.js +232 -174
- 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 +2 -0
- package/lib/dataStoreContexts.d.ts.map +1 -1
- package/lib/dataStoreContexts.js +7 -0
- 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 +5 -12
- package/lib/gc/garbageCollection.d.ts.map +1 -1
- package/lib/gc/garbageCollection.js +47 -31
- package/lib/gc/garbageCollection.js.map +1 -1
- package/lib/gc/gcDefinitions.d.ts +27 -6
- package/lib/gc/gcDefinitions.d.ts.map +1 -1
- package/lib/gc/gcDefinitions.js.map +1 -1
- package/lib/gc/gcHelpers.d.ts +5 -4
- package/lib/gc/gcHelpers.d.ts.map +1 -1
- package/lib/gc/gcHelpers.js +12 -1
- package/lib/gc/gcHelpers.js.map +1 -1
- package/lib/gc/gcTelemetry.d.ts +14 -4
- package/lib/gc/gcTelemetry.d.ts.map +1 -1
- package/lib/gc/gcTelemetry.js +24 -21
- package/lib/gc/gcTelemetry.js.map +1 -1
- package/lib/gc/index.d.ts +2 -2
- package/lib/gc/index.d.ts.map +1 -1
- package/lib/gc/index.js +1 -1
- package/lib/gc/index.js.map +1 -1
- package/lib/index.d.ts +3 -3
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +1 -1
- package/lib/index.js.map +1 -1
- package/lib/{alpha.d.ts → legacy.d.ts} +8 -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/opDecompressor.d.ts.map +1 -1
- package/lib/opLifecycle/opDecompressor.js +6 -6
- package/lib/opLifecycle/opDecompressor.js.map +1 -1
- package/lib/opLifecycle/opGroupingManager.js +2 -2
- package/lib/opLifecycle/opGroupingManager.js.map +1 -1
- package/lib/opLifecycle/opSplitter.js +1 -1
- package/lib/opLifecycle/opSplitter.js.map +1 -1
- package/lib/opLifecycle/outbox.d.ts +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/public.d.ts +3 -0
- 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 +34 -16
- 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 +37 -59
- package/src/batchTracker.ts +1 -2
- package/src/blobManager.ts +11 -10
- package/src/channelCollection.ts +115 -47
- package/src/connectionTelemetry.ts +59 -4
- package/src/containerRuntime.ts +302 -270
- package/src/dataStore.ts +7 -4
- package/src/dataStoreContext.ts +57 -16
- package/src/dataStoreContexts.ts +13 -2
- package/src/{deltaManagerSummarizerProxy.ts → deltaManagerProxies.ts} +98 -24
- package/src/deltaScheduler.ts +2 -3
- package/src/gc/garbageCollection.ts +64 -42
- package/src/gc/gcDefinitions.ts +22 -10
- package/src/gc/gcHelpers.ts +14 -1
- package/src/gc/gcTelemetry.ts +57 -50
- package/src/gc/index.ts +2 -1
- package/src/index.ts +7 -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/opDecompressor.ts +12 -6
- package/src/opLifecycle/opGroupingManager.ts +2 -2
- package/src/opLifecycle/opSplitter.ts +1 -1
- 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 +52 -18
- 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
|
|
|
@@ -493,6 +480,11 @@ export interface IContainerRuntimeOptions {
|
|
|
493
480
|
readonly explicitSchemaControl?: boolean;
|
|
494
481
|
}
|
|
495
482
|
|
|
483
|
+
/**
|
|
484
|
+
* Error responses when requesting a deleted object will have this header set to true
|
|
485
|
+
* @alpha
|
|
486
|
+
*/
|
|
487
|
+
export const DeletedResponseHeaderKey = "wasDeleted";
|
|
496
488
|
/**
|
|
497
489
|
* Tombstone error responses will have this header set to true
|
|
498
490
|
* @alpha
|
|
@@ -666,11 +658,13 @@ type MessageWithContext =
|
|
|
666
658
|
message: InboundSequencedContainerRuntimeMessage;
|
|
667
659
|
modernRuntimeMessage: true;
|
|
668
660
|
local: boolean;
|
|
661
|
+
savedOp?: boolean;
|
|
669
662
|
}
|
|
670
663
|
| {
|
|
671
664
|
message: InboundSequencedContainerRuntimeMessageOrSystemMessage;
|
|
672
665
|
modernRuntimeMessage: false;
|
|
673
666
|
local: boolean;
|
|
667
|
+
savedOp?: boolean;
|
|
674
668
|
};
|
|
675
669
|
|
|
676
670
|
const summarizerRequestUrl = "_summarizer";
|
|
@@ -805,8 +799,7 @@ export class ContainerRuntime
|
|
|
805
799
|
maxBatchSizeInBytes = defaultMaxBatchSizeInBytes,
|
|
806
800
|
enableRuntimeIdCompressor,
|
|
807
801
|
chunkSizeInBytes = defaultChunkSizeInBytes,
|
|
808
|
-
|
|
809
|
-
enableGroupedBatching = false,
|
|
802
|
+
enableGroupedBatching = true,
|
|
810
803
|
explicitSchemaControl = false,
|
|
811
804
|
} = runtimeOptions;
|
|
812
805
|
|
|
@@ -852,9 +845,9 @@ export class ContainerRuntime
|
|
|
852
845
|
|
|
853
846
|
// Verify summary runtime sequence number matches protocol sequence number.
|
|
854
847
|
const runtimeSequenceNumber = messageAtLastSummary?.sequenceNumber;
|
|
848
|
+
const protocolSequenceNumber = context.deltaManager.initialSequenceNumber;
|
|
855
849
|
// When we load with pending state, we reuse an old snapshot so we don't expect these numbers to match
|
|
856
850
|
if (!context.pendingLocalState && runtimeSequenceNumber !== undefined) {
|
|
857
|
-
const protocolSequenceNumber = context.deltaManager.initialSequenceNumber;
|
|
858
851
|
// Unless bypass is explicitly set, then take action when sequence numbers mismatch.
|
|
859
852
|
if (
|
|
860
853
|
loadSequenceNumberVerification !== "bypass" &&
|
|
@@ -914,7 +907,16 @@ export class ContainerRuntime
|
|
|
914
907
|
|
|
915
908
|
// This is the only exception to the rule above - we have proper plumbing to load ID compressor on schema change
|
|
916
909
|
// event. It is loaded async (relative to op processing), so this conversion is only safe for off -> delayed conversion!
|
|
917
|
-
|
|
910
|
+
// Clients do not expect ID compressor ops unless ID compressor is On for them, and that could be achieved only through
|
|
911
|
+
// explicit schema change, i.e. only if explicitSchemaControl is on.
|
|
912
|
+
// Note: it would be better if we throw on combination of options (explicitSchemaControl = off, desiredIdCompressorMode === "delayed")
|
|
913
|
+
// that is not supported. But our service tests are oblivious to these problems and throwing here will cause a ton of failures
|
|
914
|
+
// We ignored incompatible ID compressor changes from the start (they were sticky), so that's not a new problem being introduced...
|
|
915
|
+
if (
|
|
916
|
+
idCompressorMode === undefined &&
|
|
917
|
+
desiredIdCompressorMode === "delayed" &&
|
|
918
|
+
explicitSchemaControl
|
|
919
|
+
) {
|
|
918
920
|
idCompressorMode = desiredIdCompressorMode;
|
|
919
921
|
}
|
|
920
922
|
} else {
|
|
@@ -958,9 +960,6 @@ export class ContainerRuntime
|
|
|
958
960
|
}
|
|
959
961
|
};
|
|
960
962
|
|
|
961
|
-
const disableGroupedBatching = mc.config.getBoolean(
|
|
962
|
-
"Fluid.ContainerRuntime.DisableGroupedBatching",
|
|
963
|
-
);
|
|
964
963
|
const disableCompression = mc.config.getBoolean(
|
|
965
964
|
"Fluid.ContainerRuntime.CompressionDisabled",
|
|
966
965
|
);
|
|
@@ -969,16 +968,15 @@ export class ContainerRuntime
|
|
|
969
968
|
compressionOptions.minimumBatchSizeInBytes !== Infinity &&
|
|
970
969
|
compressionOptions.compressionAlgorithm === "lz4";
|
|
971
970
|
|
|
972
|
-
const opGroupingEnabled = disableGroupedBatching !== true && enableGroupedBatching;
|
|
973
|
-
|
|
974
971
|
const documentSchemaController = new DocumentsSchemaController(
|
|
975
972
|
existing,
|
|
973
|
+
protocolSequenceNumber,
|
|
976
974
|
metadata?.documentSchema,
|
|
977
975
|
{
|
|
978
976
|
explicitSchemaControl,
|
|
979
977
|
compressionLz4,
|
|
980
978
|
idCompressorMode,
|
|
981
|
-
opGroupingEnabled,
|
|
979
|
+
opGroupingEnabled: enableGroupedBatching,
|
|
982
980
|
disallowedVersions: [],
|
|
983
981
|
},
|
|
984
982
|
(schema) => {
|
|
@@ -987,7 +985,6 @@ export class ContainerRuntime
|
|
|
987
985
|
);
|
|
988
986
|
|
|
989
987
|
const featureGatesForTelemetry: Record<string, boolean | number | undefined> = {
|
|
990
|
-
disableGroupedBatching,
|
|
991
988
|
disableCompression,
|
|
992
989
|
};
|
|
993
990
|
|
|
@@ -1008,7 +1005,6 @@ export class ContainerRuntime
|
|
|
1008
1005
|
chunkSizeInBytes,
|
|
1009
1006
|
// Requires<> drops undefined from IdCompressorType
|
|
1010
1007
|
enableRuntimeIdCompressor: enableRuntimeIdCompressor as "on" | "delayed",
|
|
1011
|
-
enableOpReentryCheck,
|
|
1012
1008
|
enableGroupedBatching,
|
|
1013
1009
|
explicitSchemaControl,
|
|
1014
1010
|
},
|
|
@@ -1101,7 +1097,17 @@ export class ContainerRuntime
|
|
|
1101
1097
|
return this._getAttachState();
|
|
1102
1098
|
}
|
|
1103
1099
|
|
|
1104
|
-
|
|
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() {
|
|
1105
1111
|
return this.documentsSchemaController.sessionSchema.runtime;
|
|
1106
1112
|
}
|
|
1107
1113
|
|
|
@@ -1114,11 +1120,11 @@ export class ContainerRuntime
|
|
|
1114
1120
|
// Id Compressor serializes final state (see getPendingLocalState()). As result, it needs to skip all ops that preceeded that state
|
|
1115
1121
|
// (such ops will be marked by Loader layer as savedOp === true)
|
|
1116
1122
|
// That said, in "delayed" mode it's possible that Id Compressor was never initialized before getPendingLocalState() is called.
|
|
1117
|
-
// 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.
|
|
1118
1124
|
private readonly skipSavedCompressorOps: boolean;
|
|
1119
1125
|
|
|
1120
1126
|
public get idCompressorMode() {
|
|
1121
|
-
return this.
|
|
1127
|
+
return this.sessionSchema.idCompressorMode;
|
|
1122
1128
|
}
|
|
1123
1129
|
/**
|
|
1124
1130
|
* See IContainerRuntimeBase.idCompressor() for details.
|
|
@@ -1201,14 +1207,6 @@ export class ContainerRuntime
|
|
|
1201
1207
|
|
|
1202
1208
|
private ensureNoDataModelChangesCalls = 0;
|
|
1203
1209
|
|
|
1204
|
-
/**
|
|
1205
|
-
* Tracks the number of detected reentrant ops to report,
|
|
1206
|
-
* in order to self-throttle the telemetry events.
|
|
1207
|
-
*
|
|
1208
|
-
* This should be removed as part of ADO:2322
|
|
1209
|
-
*/
|
|
1210
|
-
private opReentryCallsToReport = 5;
|
|
1211
|
-
|
|
1212
1210
|
/**
|
|
1213
1211
|
* Invokes the given callback and expects that no ops are submitted
|
|
1214
1212
|
* until execution finishes. If an op is submitted, an error will be raised.
|
|
@@ -1242,16 +1240,8 @@ export class ContainerRuntime
|
|
|
1242
1240
|
|
|
1243
1241
|
private dirtyContainer: boolean;
|
|
1244
1242
|
private emitDirtyDocumentEvent = true;
|
|
1245
|
-
private readonly enableOpReentryCheck: boolean;
|
|
1246
1243
|
private readonly disableAttachReorder: boolean | undefined;
|
|
1247
1244
|
private readonly closeSummarizerDelayMs: number;
|
|
1248
|
-
/**
|
|
1249
|
-
* If true, summary generated is validate before uploading it to the server. With single commit summaries,
|
|
1250
|
-
* summaries will be accepted once uploaded, so they should be validated before upload. However, this can
|
|
1251
|
-
* currently be controlled via a feature flag as its a new functionality.
|
|
1252
|
-
*/
|
|
1253
|
-
private readonly validateSummaryBeforeUpload: boolean;
|
|
1254
|
-
|
|
1255
1245
|
private readonly defaultTelemetrySignalSampleCount = 100;
|
|
1256
1246
|
private readonly _perfSignalData: IPerfSignalReport = {
|
|
1257
1247
|
signalsLost: 0,
|
|
@@ -1396,6 +1386,7 @@ export class ContainerRuntime
|
|
|
1396
1386
|
loader,
|
|
1397
1387
|
pendingLocalState,
|
|
1398
1388
|
supportedFeatures,
|
|
1389
|
+
snapshotWithContents,
|
|
1399
1390
|
} = context;
|
|
1400
1391
|
|
|
1401
1392
|
this.mc = createChildMonitoringContext({
|
|
@@ -1408,14 +1399,13 @@ export class ContainerRuntime
|
|
|
1408
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)
|
|
1409
1400
|
// compression.
|
|
1410
1401
|
const compressionOptions: ICompressionRuntimeOptions = {
|
|
1411
|
-
minimumBatchSizeInBytes: this.
|
|
1402
|
+
minimumBatchSizeInBytes: this.sessionSchema.compressionLz4
|
|
1412
1403
|
? runtimeOptions.compressionOptions.minimumBatchSizeInBytes
|
|
1413
1404
|
: Number.POSITIVE_INFINITY,
|
|
1414
1405
|
compressionAlgorithm: CompressionAlgorithms.lz4,
|
|
1415
1406
|
};
|
|
1416
1407
|
|
|
1417
1408
|
this.innerDeltaManager = deltaManager;
|
|
1418
|
-
this.deltaManager = new DeltaManagerSummarizerProxy(this.innerDeltaManager);
|
|
1419
1409
|
|
|
1420
1410
|
// Here we could wrap/intercept on these functions to block/modify outgoing messages if needed.
|
|
1421
1411
|
// This makes ContainerRuntime the final gatekeeper for outgoing messages.
|
|
@@ -1520,20 +1510,50 @@ export class ContainerRuntime
|
|
|
1520
1510
|
opGroupingManager,
|
|
1521
1511
|
);
|
|
1522
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
|
+
|
|
1523
1551
|
this.handleContext = new ContainerFluidHandleContext("", this);
|
|
1524
1552
|
|
|
1525
1553
|
if (this.summaryConfiguration.state === "enabled") {
|
|
1526
1554
|
this.validateSummaryHeuristicConfiguration(this.summaryConfiguration);
|
|
1527
1555
|
}
|
|
1528
1556
|
|
|
1529
|
-
const disableOpReentryCheck = this.mc.config.getBoolean(
|
|
1530
|
-
"Fluid.ContainerRuntime.DisableOpReentryCheck",
|
|
1531
|
-
);
|
|
1532
|
-
this.enableOpReentryCheck =
|
|
1533
|
-
runtimeOptions.enableOpReentryCheck === true &&
|
|
1534
|
-
// Allow for a break-glass config to override the options
|
|
1535
|
-
disableOpReentryCheck !== true;
|
|
1536
|
-
|
|
1537
1557
|
this.summariesDisabled = this.isSummariesDisabled();
|
|
1538
1558
|
this.maxOpsSinceLastSummary = this.getMaxOpsSinceLastSummary();
|
|
1539
1559
|
this.initialSummarizerDelayMs = this.getInitialSummarizerDelayMs();
|
|
@@ -1553,8 +1573,6 @@ export class ContainerRuntime
|
|
|
1553
1573
|
this._flushMode = runtimeOptions.flushMode;
|
|
1554
1574
|
}
|
|
1555
1575
|
|
|
1556
|
-
const pendingRuntimeState = pendingLocalState as IPendingRuntimeState | undefined;
|
|
1557
|
-
|
|
1558
1576
|
if (context.attachState === AttachState.Attached) {
|
|
1559
1577
|
const maxSnapshotCacheDurationMs = this._storage?.policies?.maximumCacheDurationMs;
|
|
1560
1578
|
if (
|
|
@@ -1626,26 +1644,22 @@ export class ContainerRuntime
|
|
|
1626
1644
|
return this.submitSignalFn(envelope2, targetClientId);
|
|
1627
1645
|
};
|
|
1628
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
|
+
|
|
1629
1658
|
this.channelCollection = new ChannelCollection(
|
|
1630
|
-
|
|
1659
|
+
snapshot,
|
|
1631
1660
|
parentContext,
|
|
1632
1661
|
this.mc.logger,
|
|
1633
|
-
(
|
|
1634
|
-
path: string,
|
|
1635
|
-
reason: "Loaded" | "Changed",
|
|
1636
|
-
timestampMs?: number,
|
|
1637
|
-
packagePath?: readonly string[],
|
|
1638
|
-
request?: IRequest,
|
|
1639
|
-
headerData?: RuntimeHeaderData,
|
|
1640
|
-
) =>
|
|
1641
|
-
this.garbageCollector.nodeUpdated(
|
|
1642
|
-
path,
|
|
1643
|
-
reason,
|
|
1644
|
-
timestampMs,
|
|
1645
|
-
packagePath,
|
|
1646
|
-
request,
|
|
1647
|
-
headerData,
|
|
1648
|
-
),
|
|
1662
|
+
(props) => this.garbageCollector.nodeUpdated(props),
|
|
1649
1663
|
(path: string) => this.garbageCollector.isNodeDeleted(path),
|
|
1650
1664
|
new Map<string, string>(dataStoreAliasMap),
|
|
1651
1665
|
async (runtime: ChannelCollection) => provideEntryPoint,
|
|
@@ -1668,7 +1682,10 @@ export class ContainerRuntime
|
|
|
1668
1682
|
}
|
|
1669
1683
|
},
|
|
1670
1684
|
blobRequested: (blobPath: string) =>
|
|
1671
|
-
this.garbageCollector.nodeUpdated(
|
|
1685
|
+
this.garbageCollector.nodeUpdated({
|
|
1686
|
+
node: { type: "Blob", path: blobPath },
|
|
1687
|
+
reason: "Loaded",
|
|
1688
|
+
}),
|
|
1672
1689
|
isBlobDeleted: (blobPath: string) => this.garbageCollector.isNodeDeleted(blobPath),
|
|
1673
1690
|
runtime: this,
|
|
1674
1691
|
stashedBlobs: pendingRuntimeState?.pendingAttachmentBlobs,
|
|
@@ -1682,24 +1699,6 @@ export class ContainerRuntime
|
|
|
1682
1699
|
createChildLogger({ logger: this.logger, namespace: "ScheduleManager" }),
|
|
1683
1700
|
);
|
|
1684
1701
|
|
|
1685
|
-
this.pendingStateManager = new PendingStateManager(
|
|
1686
|
-
{
|
|
1687
|
-
applyStashedOp: this.applyStashedOp.bind(this),
|
|
1688
|
-
clientId: () => this.clientId,
|
|
1689
|
-
close: this.closeFn,
|
|
1690
|
-
connected: () => this.connected,
|
|
1691
|
-
reSubmit: (message: IPendingBatchMessage) => {
|
|
1692
|
-
this.reSubmit(message);
|
|
1693
|
-
this.flush();
|
|
1694
|
-
},
|
|
1695
|
-
reSubmitBatch: this.reSubmitBatch.bind(this),
|
|
1696
|
-
isActiveConnection: () => this.innerDeltaManager.active,
|
|
1697
|
-
isAttached: () => this.attachState !== AttachState.Detached,
|
|
1698
|
-
},
|
|
1699
|
-
pendingRuntimeState?.pending,
|
|
1700
|
-
this.logger,
|
|
1701
|
-
);
|
|
1702
|
-
|
|
1703
1702
|
const disablePartialFlush = this.mc.config.getBoolean(
|
|
1704
1703
|
"Fluid.ContainerRuntime.DisablePartialFlush",
|
|
1705
1704
|
);
|
|
@@ -1734,16 +1733,37 @@ export class ContainerRuntime
|
|
|
1734
1733
|
this.remoteMessageProcessor.clearPartialMessagesFor(clientId);
|
|
1735
1734
|
});
|
|
1736
1735
|
|
|
1737
|
-
|
|
1738
|
-
|
|
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
|
+
}
|
|
1739
1762
|
|
|
1740
1763
|
const closeSummarizerDelayOverride = this.mc.config.getNumber(
|
|
1741
1764
|
"Fluid.ContainerRuntime.Test.CloseSummarizerDelayOverrideMs",
|
|
1742
1765
|
);
|
|
1743
1766
|
this.closeSummarizerDelayMs = closeSummarizerDelayOverride ?? defaultCloseSummarizerDelayMs;
|
|
1744
|
-
this.validateSummaryBeforeUpload =
|
|
1745
|
-
this.mc.config.getBoolean("Fluid.Summarizer.ValidateSummaryBeforeUpload") ?? false;
|
|
1746
|
-
|
|
1747
1767
|
this.summaryCollection = new SummaryCollection(this.deltaManager, this.logger);
|
|
1748
1768
|
|
|
1749
1769
|
this.dirtyContainer =
|
|
@@ -1855,9 +1875,9 @@ export class ContainerRuntime
|
|
|
1855
1875
|
options: JSON.stringify(runtimeOptions),
|
|
1856
1876
|
idCompressorModeMetadata: metadata?.documentSchema?.runtime?.idCompressorMode,
|
|
1857
1877
|
idCompressorMode: this.idCompressorMode,
|
|
1878
|
+
sessionRuntimeSchema: JSON.stringify(this.sessionSchema),
|
|
1858
1879
|
featureGates: JSON.stringify({
|
|
1859
1880
|
...featureGatesForTelemetry,
|
|
1860
|
-
disableOpReentryCheck,
|
|
1861
1881
|
disableChunking,
|
|
1862
1882
|
disableAttachReorder: this.disableAttachReorder,
|
|
1863
1883
|
disablePartialFlush,
|
|
@@ -1865,6 +1885,7 @@ export class ContainerRuntime
|
|
|
1865
1885
|
}),
|
|
1866
1886
|
telemetryDocumentId: this.telemetryDocumentId,
|
|
1867
1887
|
groupedBatchingEnabled: this.groupedBatchingEnabled,
|
|
1888
|
+
initialSequenceNumber: this.deltaManager.initialSequenceNumber,
|
|
1868
1889
|
});
|
|
1869
1890
|
|
|
1870
1891
|
ReportOpPerfTelemetry(this.clientId, this.deltaManager, this, this.logger);
|
|
@@ -1887,6 +1908,11 @@ export class ContainerRuntime
|
|
|
1887
1908
|
}
|
|
1888
1909
|
|
|
1889
1910
|
public onSchemaChange(schema: IDocumentSchemaCurrent) {
|
|
1911
|
+
this.logger.sendTelemetryEvent({
|
|
1912
|
+
eventName: "SchemaChangeAccept",
|
|
1913
|
+
sessionRuntimeSchema: JSON.stringify(schema),
|
|
1914
|
+
});
|
|
1915
|
+
|
|
1890
1916
|
// Most of the settings will be picked up only by new sessions (i.e. after reload).
|
|
1891
1917
|
// We can make it better in the future (i.e. start to use op compression right away), but for simplicity
|
|
1892
1918
|
// this is not done.
|
|
@@ -1936,9 +1962,9 @@ export class ContainerRuntime
|
|
|
1936
1962
|
this.idCompressorMode === "on" ||
|
|
1937
1963
|
(this.idCompressorMode === "delayed" && this.connected)
|
|
1938
1964
|
) {
|
|
1965
|
+
this._idCompressor = await this.createIdCompressor();
|
|
1939
1966
|
// This is called from loadRuntime(), long before we process any ops, so there should be no ops accumulated yet.
|
|
1940
1967
|
assert(this.pendingIdCompressorOps.length === 0, 0x8ec /* no pending ops */);
|
|
1941
|
-
this._idCompressor = await this.createIdCompressor();
|
|
1942
1968
|
}
|
|
1943
1969
|
|
|
1944
1970
|
await this.garbageCollector.initializeBaseState();
|
|
@@ -2316,6 +2342,7 @@ export class ContainerRuntime
|
|
|
2316
2342
|
let newState: boolean;
|
|
2317
2343
|
|
|
2318
2344
|
try {
|
|
2345
|
+
this.submitIdAllocationOpIfNeeded(true);
|
|
2319
2346
|
// replay the ops
|
|
2320
2347
|
this.pendingStateManager.replayPendingStates();
|
|
2321
2348
|
} finally {
|
|
@@ -2368,8 +2395,6 @@ export class ContainerRuntime
|
|
|
2368
2395
|
return;
|
|
2369
2396
|
case ContainerMessageType.BlobAttach:
|
|
2370
2397
|
return;
|
|
2371
|
-
case ContainerMessageType.ChunkedOp:
|
|
2372
|
-
throw new Error("chunkedOp not expected here");
|
|
2373
2398
|
case ContainerMessageType.Rejoin:
|
|
2374
2399
|
throw new Error("rejoin not expected here");
|
|
2375
2400
|
case ContainerMessageType.GC:
|
|
@@ -2382,7 +2407,7 @@ export class ContainerRuntime
|
|
|
2382
2407
|
const compatBehavior = opContents.compatDetails?.behavior;
|
|
2383
2408
|
if (!compatBehaviorAllowsMessageType(opContents.type, compatBehavior)) {
|
|
2384
2409
|
const error = DataProcessingError.create(
|
|
2385
|
-
"Stashed runtime message of
|
|
2410
|
+
"Stashed runtime message of unexpected type",
|
|
2386
2411
|
"applyStashedOp",
|
|
2387
2412
|
undefined /* sequencedMessage */,
|
|
2388
2413
|
{
|
|
@@ -2409,12 +2434,14 @@ export class ContainerRuntime
|
|
|
2409
2434
|
) {
|
|
2410
2435
|
this._loadIdCompressor = this.createIdCompressor()
|
|
2411
2436
|
.then((compressor) => {
|
|
2412
|
-
this._idCompressor = compressor;
|
|
2413
2437
|
// Finalize any ranges we received while the compressor was turned off.
|
|
2414
|
-
|
|
2415
|
-
this._idCompressor.finalizeCreationRange(range);
|
|
2416
|
-
}
|
|
2438
|
+
const ops = this.pendingIdCompressorOps;
|
|
2417
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;
|
|
2418
2445
|
})
|
|
2419
2446
|
.catch((error) => {
|
|
2420
2447
|
this.logger.sendErrorEvent({ eventName: "IdCompressorDelayedLoad" }, error);
|
|
@@ -2425,6 +2452,14 @@ export class ContainerRuntime
|
|
|
2425
2452
|
}
|
|
2426
2453
|
|
|
2427
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
|
+
|
|
2428
2463
|
if (connected && this.idCompressorMode === "delayed") {
|
|
2429
2464
|
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
2430
2465
|
this.loadIdCompressor();
|
|
@@ -2538,21 +2573,28 @@ export class ContainerRuntime
|
|
|
2538
2573
|
// We do not need to make a deep copy. Each layer will just replace message.contents itself,
|
|
2539
2574
|
// but will not modify the contents object (likely it will replace it on the message).
|
|
2540
2575
|
const messageCopy = { ...messageArg };
|
|
2576
|
+
const savedOp = (messageCopy.metadata as ISavedOpMetadata)?.savedOp;
|
|
2541
2577
|
for (const message of this.remoteMessageProcessor.process(messageCopy)) {
|
|
2542
|
-
|
|
2543
|
-
|
|
2544
|
-
|
|
2545
|
-
|
|
2546
|
-
|
|
2547
|
-
|
|
2548
|
-
|
|
2549
|
-
|
|
2550
|
-
|
|
2551
|
-
|
|
2552
|
-
|
|
2553
|
-
|
|
2554
|
-
|
|
2555
|
-
|
|
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));
|
|
2556
2598
|
}
|
|
2557
2599
|
}
|
|
2558
2600
|
|
|
@@ -2563,6 +2605,17 @@ export class ContainerRuntime
|
|
|
2563
2605
|
*/
|
|
2564
2606
|
private processCore(messageWithContext: MessageWithContext) {
|
|
2565
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
|
+
|
|
2566
2619
|
// Surround the actual processing of the operation with messages to the schedule manager indicating
|
|
2567
2620
|
// the beginning and end. This allows it to emit appropriate events and/or pause the processing of new
|
|
2568
2621
|
// messages once a batch has been fully processed.
|
|
@@ -2577,7 +2630,7 @@ export class ContainerRuntime
|
|
|
2577
2630
|
// 2) this.resetReconnectCount() below
|
|
2578
2631
|
assert(
|
|
2579
2632
|
message.type !== ContainerMessageType.ChunkedOp,
|
|
2580
|
-
|
|
2633
|
+
0x93b /* we should never get here with chunked ops */,
|
|
2581
2634
|
);
|
|
2582
2635
|
|
|
2583
2636
|
let localOpMetadata: unknown;
|
|
@@ -2640,23 +2693,21 @@ export class ContainerRuntime
|
|
|
2640
2693
|
// stashed ops flow. The compressor is stashed with these ops already processed.
|
|
2641
2694
|
// That said, in idCompressorMode === "delayed", we might not serialize ID compressor, and
|
|
2642
2695
|
// thus we need to process all the ops.
|
|
2643
|
-
if (
|
|
2644
|
-
!(
|
|
2645
|
-
this.skipSavedCompressorOps &&
|
|
2646
|
-
(messageWithContext.message.metadata as IIdAllocationMetadata)?.savedOp ===
|
|
2647
|
-
true
|
|
2648
|
-
)
|
|
2649
|
-
) {
|
|
2696
|
+
if (!(this.skipSavedCompressorOps && messageWithContext.savedOp === true)) {
|
|
2650
2697
|
const range = messageWithContext.message.contents;
|
|
2651
2698
|
// Some other client turned on the id compressor. If we have not turned it on,
|
|
2652
2699
|
// put it in a pending queue and delay finalization.
|
|
2653
2700
|
if (this._idCompressor === undefined) {
|
|
2654
2701
|
assert(
|
|
2655
2702
|
this.idCompressorMode !== undefined,
|
|
2656
|
-
|
|
2703
|
+
0x93c /* id compressor should be enabled */,
|
|
2657
2704
|
);
|
|
2658
2705
|
this.pendingIdCompressorOps.push(range);
|
|
2659
2706
|
} else {
|
|
2707
|
+
assert(
|
|
2708
|
+
this.pendingIdCompressorOps.length === 0,
|
|
2709
|
+
0x979 /* there should be no pending ops! */,
|
|
2710
|
+
);
|
|
2660
2711
|
this._idCompressor.finalizeCreationRange(range);
|
|
2661
2712
|
}
|
|
2662
2713
|
}
|
|
@@ -2667,7 +2718,7 @@ export class ContainerRuntime
|
|
|
2667
2718
|
case ContainerMessageType.ChunkedOp:
|
|
2668
2719
|
// From observability POV, we should not exppse the rest of the system (including "op" events on object) to these messages.
|
|
2669
2720
|
// Also resetReconnectCount() would be wrong - see comment that was there before this change was made.
|
|
2670
|
-
assert(false,
|
|
2721
|
+
assert(false, 0x93d /* should not even get here */);
|
|
2671
2722
|
case ContainerMessageType.Rejoin:
|
|
2672
2723
|
break;
|
|
2673
2724
|
case ContainerMessageType.DocumentSchemaChange:
|
|
@@ -2807,9 +2858,9 @@ export class ContainerRuntime
|
|
|
2807
2858
|
let checkpoint: IBatchCheckpoint | undefined;
|
|
2808
2859
|
let result: T;
|
|
2809
2860
|
if (this.mc.config.getBoolean("Fluid.ContainerRuntime.EnableRollback")) {
|
|
2810
|
-
// Note: we are not touching
|
|
2811
|
-
// 1. It would not help, as
|
|
2812
|
-
// 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.
|
|
2813
2864
|
checkpoint = this.outbox.checkpoint().mainBatch;
|
|
2814
2865
|
}
|
|
2815
2866
|
try {
|
|
@@ -2892,12 +2943,11 @@ export class ContainerRuntime
|
|
|
2892
2943
|
"entryPoint must be defined on data store runtime for using getAliasedDataStoreEntryPoint",
|
|
2893
2944
|
);
|
|
2894
2945
|
}
|
|
2895
|
-
this.garbageCollector.nodeUpdated(
|
|
2896
|
-
`/${internalId}
|
|
2897
|
-
"Loaded",
|
|
2898
|
-
|
|
2899
|
-
|
|
2900
|
-
);
|
|
2946
|
+
this.garbageCollector.nodeUpdated({
|
|
2947
|
+
node: { type: "DataStore", path: `/${internalId}` },
|
|
2948
|
+
reason: "Loaded",
|
|
2949
|
+
packagePath: context.packagePath,
|
|
2950
|
+
});
|
|
2901
2951
|
return channel.entryPoint;
|
|
2902
2952
|
}
|
|
2903
2953
|
|
|
@@ -3083,7 +3133,7 @@ export class ContainerRuntime
|
|
|
3083
3133
|
if (idRange !== undefined) {
|
|
3084
3134
|
assert(
|
|
3085
3135
|
idRange.ids === undefined || idRange.ids.firstGenCount === 1,
|
|
3086
|
-
|
|
3136
|
+
0x93e /* No other ranges should be taken while container is detached. */,
|
|
3087
3137
|
);
|
|
3088
3138
|
this._idCompressor?.finalizeCreationRange(idRange);
|
|
3089
3139
|
}
|
|
@@ -3188,7 +3238,7 @@ export class ContainerRuntime
|
|
|
3188
3238
|
|
|
3189
3239
|
return { stats, summary };
|
|
3190
3240
|
} finally {
|
|
3191
|
-
|
|
3241
|
+
summaryLogger.sendTelemetryEvent({
|
|
3192
3242
|
eventName: "SummarizeTelemetry",
|
|
3193
3243
|
details: telemetryContext.serialize(),
|
|
3194
3244
|
});
|
|
@@ -3392,10 +3442,14 @@ export class ContainerRuntime
|
|
|
3392
3442
|
// The summary number for this summary. This will be updated during the summary process, so get it now and
|
|
3393
3443
|
// use it for all events logged during this summary.
|
|
3394
3444
|
const summaryNumber = this.nextSummaryNumber;
|
|
3445
|
+
let summaryRefSeqNum: number | undefined;
|
|
3395
3446
|
const summaryNumberLogger = createChildLogger({
|
|
3396
3447
|
logger: summaryLogger,
|
|
3397
3448
|
properties: {
|
|
3398
|
-
all: {
|
|
3449
|
+
all: {
|
|
3450
|
+
summaryNumber,
|
|
3451
|
+
referenceSequenceNumber: () => summaryRefSeqNum,
|
|
3452
|
+
},
|
|
3399
3453
|
},
|
|
3400
3454
|
});
|
|
3401
3455
|
|
|
@@ -3414,7 +3468,7 @@ export class ContainerRuntime
|
|
|
3414
3468
|
// If the container is dirty, i.e., there are pending unacked ops, the summary will not be eventual consistent
|
|
3415
3469
|
// and it may even be incorrect. So, wait for the container to be saved with a timeout. If the container is not
|
|
3416
3470
|
// saved within the timeout, check if it should be failed or can continue.
|
|
3417
|
-
if (this.
|
|
3471
|
+
if (this.isDirty) {
|
|
3418
3472
|
const countBefore = this.pendingMessagesCount;
|
|
3419
3473
|
// The timeout for waiting for pending ops can be overridden via configurations.
|
|
3420
3474
|
const pendingOpsTimeout =
|
|
@@ -3464,8 +3518,6 @@ export class ContainerRuntime
|
|
|
3464
3518
|
"Fluid.ContainerRuntime.SubmitSummary.shouldValidatePreSummaryState",
|
|
3465
3519
|
) === true;
|
|
3466
3520
|
|
|
3467
|
-
let summaryRefSeqNum: number | undefined;
|
|
3468
|
-
|
|
3469
3521
|
try {
|
|
3470
3522
|
await this.deltaManager.inbound.pause();
|
|
3471
3523
|
if (shouldPauseInboundSignal) {
|
|
@@ -3483,11 +3535,19 @@ export class ContainerRuntime
|
|
|
3483
3535
|
latestSummaryRefSeqNum,
|
|
3484
3536
|
);
|
|
3485
3537
|
|
|
3538
|
+
/**
|
|
3539
|
+
* This was added to validate that the summarizer node tree has the same reference sequence number from the
|
|
3540
|
+
* top running summarizer down to the lowest summarizer node.
|
|
3541
|
+
*
|
|
3542
|
+
* The order of mismatch numbers goes (validate sequence number)-(node sequence number).
|
|
3543
|
+
* Generally the validate sequence number comes from the running summarizer and the node sequence number comes from the
|
|
3544
|
+
* summarizer nodes.
|
|
3545
|
+
*/
|
|
3486
3546
|
if (
|
|
3487
3547
|
startSummaryResult.invalidNodes > 0 ||
|
|
3488
3548
|
startSummaryResult.mismatchNumbers.size > 0
|
|
3489
3549
|
) {
|
|
3490
|
-
summaryLogger.
|
|
3550
|
+
summaryLogger.sendTelemetryEvent({
|
|
3491
3551
|
eventName: "LatestSummaryRefSeqNumMismatch",
|
|
3492
3552
|
details: {
|
|
3493
3553
|
...startSummaryResult,
|
|
@@ -3500,7 +3560,9 @@ export class ContainerRuntime
|
|
|
3500
3560
|
stage: "base",
|
|
3501
3561
|
referenceSequenceNumber: summaryRefSeqNum,
|
|
3502
3562
|
minimumSequenceNumber,
|
|
3503
|
-
error:
|
|
3563
|
+
error: new LoggingError(
|
|
3564
|
+
`Summarizer node state inconsistent with summarizer state.`,
|
|
3565
|
+
),
|
|
3504
3566
|
};
|
|
3505
3567
|
}
|
|
3506
3568
|
}
|
|
@@ -3550,7 +3612,7 @@ export class ContainerRuntime
|
|
|
3550
3612
|
stage: "base",
|
|
3551
3613
|
referenceSequenceNumber: summaryRefSeqNum,
|
|
3552
3614
|
minimumSequenceNumber,
|
|
3553
|
-
error: continueResult.error,
|
|
3615
|
+
error: new LoggingError(continueResult.error),
|
|
3554
3616
|
};
|
|
3555
3617
|
}
|
|
3556
3618
|
|
|
@@ -3571,39 +3633,38 @@ export class ContainerRuntime
|
|
|
3571
3633
|
stage: "base",
|
|
3572
3634
|
referenceSequenceNumber: summaryRefSeqNum,
|
|
3573
3635
|
minimumSequenceNumber,
|
|
3574
|
-
error,
|
|
3636
|
+
error: wrapError(error, (msg) => new LoggingError(msg)),
|
|
3575
3637
|
};
|
|
3576
3638
|
}
|
|
3577
3639
|
|
|
3578
|
-
//
|
|
3579
|
-
|
|
3580
|
-
|
|
3581
|
-
const
|
|
3582
|
-
|
|
3583
|
-
|
|
3584
|
-
|
|
3585
|
-
|
|
3586
|
-
validateResult.retryAfterSeconds,
|
|
3587
|
-
{ ...loggingProps },
|
|
3588
|
-
);
|
|
3589
|
-
return {
|
|
3590
|
-
stage: "base",
|
|
3591
|
-
referenceSequenceNumber: summaryRefSeqNum,
|
|
3592
|
-
minimumSequenceNumber,
|
|
3593
|
-
error,
|
|
3594
|
-
};
|
|
3595
|
-
}
|
|
3596
|
-
|
|
3597
|
-
const pendingMessagesFailResult = await this.shouldFailSummaryOnPendingOps(
|
|
3598
|
-
summaryNumberLogger,
|
|
3599
|
-
summaryRefSeqNum,
|
|
3600
|
-
minimumSequenceNumber,
|
|
3601
|
-
finalAttempt,
|
|
3602
|
-
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 },
|
|
3603
3648
|
);
|
|
3604
|
-
|
|
3605
|
-
|
|
3606
|
-
|
|
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;
|
|
3607
3668
|
}
|
|
3608
3669
|
|
|
3609
3670
|
const { summary: summaryTree, stats: partialStats } = summarizeResult;
|
|
@@ -3644,7 +3705,11 @@ export class ContainerRuntime
|
|
|
3644
3705
|
|
|
3645
3706
|
continueResult = checkContinue();
|
|
3646
3707
|
if (!continueResult.continue) {
|
|
3647
|
-
return {
|
|
3708
|
+
return {
|
|
3709
|
+
stage: "generate",
|
|
3710
|
+
...generateSummaryData,
|
|
3711
|
+
error: new LoggingError(continueResult.error),
|
|
3712
|
+
};
|
|
3648
3713
|
}
|
|
3649
3714
|
|
|
3650
3715
|
const summaryContext =
|
|
@@ -3667,7 +3732,11 @@ export class ContainerRuntime
|
|
|
3667
3732
|
summaryContext,
|
|
3668
3733
|
);
|
|
3669
3734
|
} catch (error) {
|
|
3670
|
-
return {
|
|
3735
|
+
return {
|
|
3736
|
+
stage: "generate",
|
|
3737
|
+
...generateSummaryData,
|
|
3738
|
+
error: wrapError(error, (msg) => new LoggingError(msg)),
|
|
3739
|
+
};
|
|
3671
3740
|
}
|
|
3672
3741
|
|
|
3673
3742
|
const parent = summaryContext.ackHandle;
|
|
@@ -3686,14 +3755,22 @@ export class ContainerRuntime
|
|
|
3686
3755
|
|
|
3687
3756
|
continueResult = checkContinue();
|
|
3688
3757
|
if (!continueResult.continue) {
|
|
3689
|
-
return {
|
|
3758
|
+
return {
|
|
3759
|
+
stage: "upload",
|
|
3760
|
+
...uploadData,
|
|
3761
|
+
error: new LoggingError(continueResult.error),
|
|
3762
|
+
};
|
|
3690
3763
|
}
|
|
3691
3764
|
|
|
3692
3765
|
let clientSequenceNumber: number;
|
|
3693
3766
|
try {
|
|
3694
3767
|
clientSequenceNumber = this.submitSummaryMessage(summaryMessage, summaryRefSeqNum);
|
|
3695
3768
|
} catch (error) {
|
|
3696
|
-
return {
|
|
3769
|
+
return {
|
|
3770
|
+
stage: "upload",
|
|
3771
|
+
...uploadData,
|
|
3772
|
+
error: wrapError(error, (msg) => new LoggingError(msg)),
|
|
3773
|
+
};
|
|
3697
3774
|
}
|
|
3698
3775
|
|
|
3699
3776
|
const submitData = {
|
|
@@ -3704,13 +3781,13 @@ export class ContainerRuntime
|
|
|
3704
3781
|
} as const;
|
|
3705
3782
|
|
|
3706
3783
|
try {
|
|
3707
|
-
|
|
3708
|
-
this.summarizerNode.completeSummary(
|
|
3709
|
-
handle,
|
|
3710
|
-
!this.validateSummaryBeforeUpload /* validate */,
|
|
3711
|
-
);
|
|
3784
|
+
this.summarizerNode.completeSummary(handle);
|
|
3712
3785
|
} catch (error) {
|
|
3713
|
-
return {
|
|
3786
|
+
return {
|
|
3787
|
+
stage: "upload",
|
|
3788
|
+
...uploadData,
|
|
3789
|
+
error: wrapError(error, (msg) => new LoggingError(msg)),
|
|
3790
|
+
};
|
|
3714
3791
|
}
|
|
3715
3792
|
return submitData;
|
|
3716
3793
|
} finally {
|
|
@@ -3837,14 +3914,16 @@ export class ContainerRuntime
|
|
|
3837
3914
|
public async uploadBlob(
|
|
3838
3915
|
blob: ArrayBufferLike,
|
|
3839
3916
|
signal?: AbortSignal,
|
|
3840
|
-
): Promise<
|
|
3917
|
+
): Promise<IFluidHandleInternal<ArrayBufferLike>> {
|
|
3841
3918
|
this.verifyNotClosed();
|
|
3842
3919
|
return this.blobManager.createBlob(blob, signal);
|
|
3843
3920
|
}
|
|
3844
3921
|
|
|
3845
|
-
private submitIdAllocationOpIfNeeded(): void {
|
|
3922
|
+
private submitIdAllocationOpIfNeeded(resubmitOutstandingRanges = false): void {
|
|
3846
3923
|
if (this._idCompressor) {
|
|
3847
|
-
const idRange =
|
|
3924
|
+
const idRange = resubmitOutstandingRanges
|
|
3925
|
+
? this.idCompressor?.takeUnfinalizedCreationRange()
|
|
3926
|
+
: this._idCompressor.takeNextCreationRange();
|
|
3848
3927
|
// Don't include the idRange if there weren't any Ids allocated
|
|
3849
3928
|
if (idRange?.ids !== undefined) {
|
|
3850
3929
|
const idAllocationMessage: ContainerRuntimeIdAllocationMessage = {
|
|
@@ -3866,7 +3945,6 @@ export class ContainerRuntime
|
|
|
3866
3945
|
metadata?: { localId: string; blobId?: string },
|
|
3867
3946
|
): void {
|
|
3868
3947
|
this.verifyNotClosed();
|
|
3869
|
-
this.verifyCanSubmitOps();
|
|
3870
3948
|
|
|
3871
3949
|
// There should be no ops in detached container state!
|
|
3872
3950
|
assert(
|
|
@@ -3877,7 +3955,7 @@ export class ContainerRuntime
|
|
|
3877
3955
|
assert(
|
|
3878
3956
|
metadata === undefined ||
|
|
3879
3957
|
containerRuntimeMessage.type === ContainerMessageType.BlobAttach,
|
|
3880
|
-
|
|
3958
|
+
0x93f /* metadata */,
|
|
3881
3959
|
);
|
|
3882
3960
|
|
|
3883
3961
|
const serializedContent = JSON.stringify(containerRuntimeMessage);
|
|
@@ -3914,6 +3992,14 @@ export class ContainerRuntime
|
|
|
3914
3992
|
// on this callback to do actual sending.
|
|
3915
3993
|
const contents = this.documentsSchemaController.maybeSendSchemaMessage();
|
|
3916
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
|
+
});
|
|
3917
4003
|
const msg: ContainerRuntimeDocumentSchemaMessage = {
|
|
3918
4004
|
type: ContainerMessageType.DocumentSchemaChange,
|
|
3919
4005
|
contents,
|
|
@@ -3924,33 +4010,7 @@ export class ContainerRuntime
|
|
|
3924
4010
|
});
|
|
3925
4011
|
}
|
|
3926
4012
|
|
|
3927
|
-
|
|
3928
|
-
// Is it safe:
|
|
3929
|
-
// Yes, this should be safe reordering. Newly created data stores are not visible through API surface.
|
|
3930
|
-
// They become visible only when aliased, or handle to some sub-element of newly created datastore
|
|
3931
|
-
// is stored in some DDS, i.e. only after some other op.
|
|
3932
|
-
// Why:
|
|
3933
|
-
// Attach ops are large, and expensive to process. Plus there are scenarios where a lot of new data
|
|
3934
|
-
// stores are created, causing issues like relay service throttling (too many ops) and catastrophic
|
|
3935
|
-
// failure (batch is too large). Pushing them earlier and outside of main batch should alleviate
|
|
3936
|
-
// these issues.
|
|
3937
|
-
// Cons:
|
|
3938
|
-
// 1. With large batches, relay service may throttle clients. Clients may disconnect while throttled.
|
|
3939
|
-
// This change creates new possibility of a lot of newly created data stores never being referenced
|
|
3940
|
-
// because client died before it had a change to submit the rest of the ops. This will create more
|
|
3941
|
-
// garbage that needs to be collected leveraging GC (Garbage Collection) feature.
|
|
3942
|
-
// 2. Sending ops out of order means they are excluded from rollback functionality. This is not an issue
|
|
3943
|
-
// today as rollback can't undo creation of data store. To some extent not sending them is a bigger
|
|
3944
|
-
// issue than sending.
|
|
3945
|
-
// Please note that this does not change file format, so it can be disabled in the future if this
|
|
3946
|
-
// optimization no longer makes sense (for example, batch compression may make it less appealing).
|
|
3947
|
-
if (
|
|
3948
|
-
this.currentlyBatching() &&
|
|
3949
|
-
type === ContainerMessageType.Attach &&
|
|
3950
|
-
this.disableAttachReorder !== true
|
|
3951
|
-
) {
|
|
3952
|
-
this.outbox.submitAttach(message);
|
|
3953
|
-
} else if (type === ContainerMessageType.BlobAttach) {
|
|
4013
|
+
if (type === ContainerMessageType.BlobAttach) {
|
|
3954
4014
|
// BlobAttach ops must have their metadata visible and cannot be grouped (see opGroupingManager.ts)
|
|
3955
4015
|
this.outbox.submitBlobAttach(message);
|
|
3956
4016
|
} else {
|
|
@@ -4039,37 +4099,6 @@ export class ContainerRuntime
|
|
|
4039
4099
|
}
|
|
4040
4100
|
}
|
|
4041
4101
|
|
|
4042
|
-
private verifyCanSubmitOps() {
|
|
4043
|
-
if (this.ensureNoDataModelChangesCalls > 0) {
|
|
4044
|
-
const errorMessage =
|
|
4045
|
-
"Op was submitted from within a `ensureNoDataModelChanges` callback";
|
|
4046
|
-
if (this.opReentryCallsToReport > 0) {
|
|
4047
|
-
this.mc.logger.sendTelemetryEvent(
|
|
4048
|
-
{ eventName: "OpReentry" },
|
|
4049
|
-
// We need to capture the call stack in order to inspect the source of this usage pattern
|
|
4050
|
-
getLongStack(() => new UsageError(errorMessage)),
|
|
4051
|
-
);
|
|
4052
|
-
this.opReentryCallsToReport--;
|
|
4053
|
-
}
|
|
4054
|
-
|
|
4055
|
-
// Creating ops while processing ops can lead
|
|
4056
|
-
// to undefined behavior and events observed in the wrong order.
|
|
4057
|
-
// For example, we have two callbacks registered for a DDS, A and B.
|
|
4058
|
-
// Then if on change #1 callback A creates change #2, the invocation flow will be:
|
|
4059
|
-
//
|
|
4060
|
-
// A because of #1
|
|
4061
|
-
// A because of #2
|
|
4062
|
-
// B because of #2
|
|
4063
|
-
// B because of #1
|
|
4064
|
-
//
|
|
4065
|
-
// The runtime must enforce op coherence by not allowing ops to be submitted
|
|
4066
|
-
// while ops are being processed.
|
|
4067
|
-
if (this.enableOpReentryCheck) {
|
|
4068
|
-
throw new UsageError(errorMessage);
|
|
4069
|
-
}
|
|
4070
|
-
}
|
|
4071
|
-
}
|
|
4072
|
-
|
|
4073
4102
|
private reSubmitBatch(batch: IPendingBatchMessage[]) {
|
|
4074
4103
|
this.orderSequentially(() => {
|
|
4075
4104
|
for (const message of batch) {
|
|
@@ -4110,11 +4139,15 @@ export class ContainerRuntime
|
|
|
4110
4139
|
this.channelCollection.reSubmit(message.type, message.contents, localOpMetadata);
|
|
4111
4140
|
break;
|
|
4112
4141
|
case ContainerMessageType.IdAllocation: {
|
|
4113
|
-
|
|
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.
|
|
4114
4149
|
break;
|
|
4115
4150
|
}
|
|
4116
|
-
case ContainerMessageType.ChunkedOp:
|
|
4117
|
-
throw new Error(`chunkedOp not expected here`);
|
|
4118
4151
|
case ContainerMessageType.BlobAttach:
|
|
4119
4152
|
this.blobManager.reSubmit(opMetadata);
|
|
4120
4153
|
break;
|
|
@@ -4141,7 +4174,7 @@ export class ContainerRuntime
|
|
|
4141
4174
|
});
|
|
4142
4175
|
} else {
|
|
4143
4176
|
const error = DataProcessingError.create(
|
|
4144
|
-
"Resubmitting runtime message of
|
|
4177
|
+
"Resubmitting runtime message of unexpected type",
|
|
4145
4178
|
"reSubmitCore",
|
|
4146
4179
|
undefined /* sequencedMessage */,
|
|
4147
4180
|
{
|
|
@@ -4234,7 +4267,7 @@ export class ContainerRuntime
|
|
|
4234
4267
|
|
|
4235
4268
|
return {
|
|
4236
4269
|
stage: "base",
|
|
4237
|
-
error: "summary state stale - Unsupported option 'refreshLatestAck'",
|
|
4270
|
+
error: new LoggingError("summary state stale - Unsupported option 'refreshLatestAck'"),
|
|
4238
4271
|
referenceSequenceNumber: this.deltaManager.lastSequenceNumber,
|
|
4239
4272
|
minimumSequenceNumber: this.deltaManager.minimumSequenceNumber,
|
|
4240
4273
|
};
|
|
@@ -4316,10 +4349,9 @@ export class ContainerRuntime
|
|
|
4316
4349
|
const getSyncState = (
|
|
4317
4350
|
pendingAttachmentBlobs?: IPendingBlobs,
|
|
4318
4351
|
): IPendingRuntimeState | undefined => {
|
|
4319
|
-
const pending = this.pendingStateManager.getLocalState();
|
|
4320
|
-
|
|
4321
|
-
|
|
4322
|
-
}
|
|
4352
|
+
const pending = this.pendingStateManager.getLocalState(props?.snapshotSequenceNumber);
|
|
4353
|
+
const sessionExpiryTimerStarted =
|
|
4354
|
+
props?.sessionExpiryTimerStarted ?? this.garbageCollector.sessionExpiryTimerStarted;
|
|
4323
4355
|
|
|
4324
4356
|
const pendingIdCompressorState = this._idCompressor?.serialize(true);
|
|
4325
4357
|
|
|
@@ -4327,7 +4359,7 @@ export class ContainerRuntime
|
|
|
4327
4359
|
pending,
|
|
4328
4360
|
pendingIdCompressorState,
|
|
4329
4361
|
pendingAttachmentBlobs,
|
|
4330
|
-
sessionExpiryTimerStarted
|
|
4362
|
+
sessionExpiryTimerStarted,
|
|
4331
4363
|
};
|
|
4332
4364
|
};
|
|
4333
4365
|
const perfEvent = {
|
|
@@ -4418,6 +4450,6 @@ export class ContainerRuntime
|
|
|
4418
4450
|
}
|
|
4419
4451
|
|
|
4420
4452
|
private get groupedBatchingEnabled(): boolean {
|
|
4421
|
-
return this.
|
|
4453
|
+
return this.sessionSchema.opGroupingEnabled === true;
|
|
4422
4454
|
}
|
|
4423
4455
|
}
|