@fluidframework/container-runtime 2.0.0-internal.6.3.3 → 2.0.0-internal.7.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +111 -0
- package/dist/blobManager.d.ts +4 -3
- package/dist/blobManager.d.ts.map +1 -1
- package/dist/blobManager.js +35 -31
- package/dist/blobManager.js.map +1 -1
- package/dist/containerHandleContext.js +3 -3
- package/dist/containerHandleContext.js.map +1 -1
- package/dist/containerRuntime.d.ts +21 -78
- package/dist/containerRuntime.d.ts.map +1 -1
- package/dist/containerRuntime.js +378 -412
- package/dist/containerRuntime.js.map +1 -1
- package/dist/dataStore.js +9 -9
- package/dist/dataStore.js.map +1 -1
- package/dist/dataStoreContext.d.ts +1 -3
- package/dist/dataStoreContext.d.ts.map +1 -1
- package/dist/dataStoreContext.js +54 -56
- package/dist/dataStoreContext.js.map +1 -1
- package/dist/dataStoreRegistry.js +3 -3
- package/dist/dataStoreRegistry.js.map +1 -1
- package/dist/dataStores.d.ts +1 -1
- package/dist/dataStores.js +3 -3
- package/dist/dataStores.js.map +1 -1
- package/dist/deltaManagerProxyBase.js +4 -4
- package/dist/deltaManagerProxyBase.js.map +1 -1
- package/dist/deltaManagerSummarizerProxy.js +6 -6
- package/dist/deltaManagerSummarizerProxy.js.map +1 -1
- package/dist/deltaScheduler.js.map +1 -1
- package/dist/gc/garbageCollection.d.ts +6 -3
- package/dist/gc/garbageCollection.d.ts.map +1 -1
- package/dist/gc/garbageCollection.js +19 -16
- package/dist/gc/garbageCollection.js.map +1 -1
- package/dist/gc/gcDefinitions.d.ts +17 -17
- package/dist/gc/gcDefinitions.d.ts.map +1 -1
- package/dist/gc/gcDefinitions.js +14 -15
- package/dist/gc/gcDefinitions.js.map +1 -1
- package/dist/gc/gcHelpers.d.ts +0 -9
- package/dist/gc/gcHelpers.d.ts.map +1 -1
- package/dist/gc/gcHelpers.js +1 -13
- package/dist/gc/gcHelpers.js.map +1 -1
- package/dist/gc/gcTelemetry.d.ts +1 -1
- package/dist/gc/gcTelemetry.d.ts.map +1 -1
- package/dist/gc/gcTelemetry.js +1 -4
- package/dist/gc/gcTelemetry.js.map +1 -1
- package/dist/gc/gcUnreferencedStateTracker.js +3 -3
- package/dist/gc/gcUnreferencedStateTracker.js.map +1 -1
- package/dist/gc/index.d.ts +2 -2
- package/dist/gc/index.d.ts.map +1 -1
- package/dist/gc/index.js +3 -4
- package/dist/gc/index.js.map +1 -1
- package/dist/id-compressor/appendOnlySortedMap.js.map +1 -1
- package/dist/id-compressor/idCompressor.js.map +1 -1
- package/dist/id-compressor/identifiers.d.ts +3 -3
- package/dist/id-compressor/identifiers.d.ts.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -2
- package/dist/index.js.map +1 -1
- package/dist/messageTypes.d.ts +134 -0
- package/dist/messageTypes.d.ts.map +1 -0
- package/dist/messageTypes.js +29 -0
- package/dist/messageTypes.js.map +1 -0
- package/dist/opLifecycle/batchManager.js +6 -6
- package/dist/opLifecycle/batchManager.js.map +1 -1
- package/dist/opLifecycle/definitions.d.ts +4 -3
- package/dist/opLifecycle/definitions.d.ts.map +1 -1
- package/dist/opLifecycle/definitions.js.map +1 -1
- package/dist/opLifecycle/opDecompressor.d.ts.map +1 -1
- package/dist/opLifecycle/opDecompressor.js +0 -4
- package/dist/opLifecycle/opDecompressor.js.map +1 -1
- package/dist/opLifecycle/opGroupingManager.d.ts.map +1 -1
- package/dist/opLifecycle/opGroupingManager.js +4 -2
- package/dist/opLifecycle/opGroupingManager.js.map +1 -1
- package/dist/opLifecycle/opSplitter.js +3 -3
- package/dist/opLifecycle/opSplitter.js.map +1 -1
- package/dist/opLifecycle/remoteMessageProcessor.d.ts +17 -3
- package/dist/opLifecycle/remoteMessageProcessor.d.ts.map +1 -1
- package/dist/opLifecycle/remoteMessageProcessor.js +38 -25
- package/dist/opLifecycle/remoteMessageProcessor.js.map +1 -1
- package/dist/packageVersion.d.ts +1 -1
- package/dist/packageVersion.js +1 -1
- package/dist/packageVersion.js.map +1 -1
- package/dist/pendingStateManager.d.ts +4 -20
- package/dist/pendingStateManager.d.ts.map +1 -1
- package/dist/pendingStateManager.js +33 -45
- package/dist/pendingStateManager.js.map +1 -1
- package/dist/summary/orderedClientElection.d.ts +3 -3
- package/dist/summary/orderedClientElection.d.ts.map +1 -1
- package/dist/summary/orderedClientElection.js +54 -54
- package/dist/summary/orderedClientElection.js.map +1 -1
- package/dist/summary/runWhileConnectedCoordinator.js +6 -6
- package/dist/summary/runWhileConnectedCoordinator.js.map +1 -1
- package/dist/summary/runningSummarizer.d.ts +1 -1
- package/dist/summary/runningSummarizer.d.ts.map +1 -1
- package/dist/summary/runningSummarizer.js +41 -39
- package/dist/summary/runningSummarizer.js.map +1 -1
- package/dist/summary/summarizer.d.ts +2 -2
- package/dist/summary/summarizer.d.ts.map +1 -1
- package/dist/summary/summarizer.js +10 -8
- package/dist/summary/summarizer.js.map +1 -1
- package/dist/summary/summarizerClientElection.js +6 -6
- package/dist/summary/summarizerClientElection.js.map +1 -1
- package/dist/summary/summarizerHeuristics.js +9 -9
- package/dist/summary/summarizerHeuristics.js.map +1 -1
- package/dist/summary/summarizerNode/summarizerNode.d.ts +1 -1
- package/dist/summary/summarizerNode/summarizerNode.js +8 -8
- package/dist/summary/summarizerNode/summarizerNode.js.map +1 -1
- package/dist/summary/summarizerNode/summarizerNodeUtils.d.ts +1 -1
- package/dist/summary/summarizerNode/summarizerNodeUtils.d.ts.map +1 -1
- package/dist/summary/summarizerNode/summarizerNodeUtils.js +3 -3
- package/dist/summary/summarizerNode/summarizerNodeUtils.js.map +1 -1
- package/dist/summary/summarizerNode/summarizerNodeWithGc.d.ts +2 -2
- package/dist/summary/summarizerNode/summarizerNodeWithGc.d.ts.map +1 -1
- package/dist/summary/summarizerTypes.d.ts +14 -13
- 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 +21 -21
- package/dist/summary/summaryCollection.js.map +1 -1
- package/dist/summary/summaryFormat.d.ts +8 -5
- package/dist/summary/summaryFormat.d.ts.map +1 -1
- package/dist/summary/summaryFormat.js +3 -0
- package/dist/summary/summaryFormat.js.map +1 -1
- package/dist/summary/summaryGenerator.d.ts +3 -3
- package/dist/summary/summaryGenerator.d.ts.map +1 -1
- package/dist/summary/summaryManager.js +7 -7
- package/dist/summary/summaryManager.js.map +1 -1
- package/dist/throttler.js +16 -16
- package/dist/throttler.js.map +1 -1
- package/dist/tsdoc-metadata.json +1 -1
- package/lib/blobManager.d.ts +4 -3
- package/lib/blobManager.d.ts.map +1 -1
- package/lib/blobManager.js +36 -32
- package/lib/blobManager.js.map +1 -1
- package/lib/containerHandleContext.js +3 -3
- package/lib/containerHandleContext.js.map +1 -1
- package/lib/containerRuntime.d.ts +21 -78
- package/lib/containerRuntime.d.ts.map +1 -1
- package/lib/containerRuntime.js +339 -375
- package/lib/containerRuntime.js.map +1 -1
- package/lib/dataStore.js +9 -9
- package/lib/dataStore.js.map +1 -1
- package/lib/dataStoreContext.d.ts +1 -3
- package/lib/dataStoreContext.d.ts.map +1 -1
- package/lib/dataStoreContext.js +54 -56
- package/lib/dataStoreContext.js.map +1 -1
- package/lib/dataStoreRegistry.js +3 -3
- package/lib/dataStoreRegistry.js.map +1 -1
- package/lib/dataStores.d.ts +1 -1
- package/lib/dataStores.js +4 -4
- package/lib/dataStores.js.map +1 -1
- package/lib/deltaManagerProxyBase.js +4 -4
- package/lib/deltaManagerProxyBase.js.map +1 -1
- package/lib/deltaManagerSummarizerProxy.js +6 -6
- package/lib/deltaManagerSummarizerProxy.js.map +1 -1
- package/lib/deltaScheduler.js.map +1 -1
- package/lib/gc/garbageCollection.d.ts +6 -3
- package/lib/gc/garbageCollection.d.ts.map +1 -1
- package/lib/gc/garbageCollection.js +19 -16
- package/lib/gc/garbageCollection.js.map +1 -1
- package/lib/gc/gcDefinitions.d.ts +17 -17
- package/lib/gc/gcDefinitions.d.ts.map +1 -1
- package/lib/gc/gcDefinitions.js +13 -14
- package/lib/gc/gcDefinitions.js.map +1 -1
- package/lib/gc/gcHelpers.d.ts +0 -9
- package/lib/gc/gcHelpers.d.ts.map +1 -1
- package/lib/gc/gcHelpers.js +0 -11
- package/lib/gc/gcHelpers.js.map +1 -1
- package/lib/gc/gcTelemetry.d.ts +1 -1
- package/lib/gc/gcTelemetry.d.ts.map +1 -1
- package/lib/gc/gcTelemetry.js +1 -4
- package/lib/gc/gcTelemetry.js.map +1 -1
- package/lib/gc/gcUnreferencedStateTracker.js +3 -3
- package/lib/gc/gcUnreferencedStateTracker.js.map +1 -1
- package/lib/gc/index.d.ts +2 -2
- package/lib/gc/index.d.ts.map +1 -1
- package/lib/gc/index.js +2 -2
- package/lib/gc/index.js.map +1 -1
- package/lib/id-compressor/appendOnlySortedMap.js.map +1 -1
- package/lib/id-compressor/idCompressor.js.map +1 -1
- package/lib/id-compressor/identifiers.d.ts +3 -3
- package/lib/id-compressor/identifiers.d.ts.map +1 -1
- package/lib/index.d.ts +2 -1
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +2 -1
- package/lib/index.js.map +1 -1
- package/lib/messageTypes.d.ts +134 -0
- package/lib/messageTypes.d.ts.map +1 -0
- package/lib/messageTypes.js +26 -0
- package/lib/messageTypes.js.map +1 -0
- package/lib/opLifecycle/batchManager.js +6 -6
- package/lib/opLifecycle/batchManager.js.map +1 -1
- package/lib/opLifecycle/definitions.d.ts +4 -3
- package/lib/opLifecycle/definitions.d.ts.map +1 -1
- package/lib/opLifecycle/definitions.js.map +1 -1
- package/lib/opLifecycle/opDecompressor.d.ts.map +1 -1
- package/lib/opLifecycle/opDecompressor.js +0 -4
- package/lib/opLifecycle/opDecompressor.js.map +1 -1
- package/lib/opLifecycle/opGroupingManager.d.ts.map +1 -1
- package/lib/opLifecycle/opGroupingManager.js +4 -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/remoteMessageProcessor.d.ts +17 -3
- package/lib/opLifecycle/remoteMessageProcessor.d.ts.map +1 -1
- package/lib/opLifecycle/remoteMessageProcessor.js +37 -24
- package/lib/opLifecycle/remoteMessageProcessor.js.map +1 -1
- package/lib/packageVersion.d.ts +1 -1
- package/lib/packageVersion.js +1 -1
- package/lib/packageVersion.js.map +1 -1
- package/lib/pendingStateManager.d.ts +4 -20
- package/lib/pendingStateManager.d.ts.map +1 -1
- package/lib/pendingStateManager.js +32 -44
- package/lib/pendingStateManager.js.map +1 -1
- package/lib/summary/orderedClientElection.d.ts +3 -3
- package/lib/summary/orderedClientElection.d.ts.map +1 -1
- package/lib/summary/orderedClientElection.js +54 -54
- package/lib/summary/orderedClientElection.js.map +1 -1
- package/lib/summary/runWhileConnectedCoordinator.js +6 -6
- package/lib/summary/runWhileConnectedCoordinator.js.map +1 -1
- package/lib/summary/runningSummarizer.d.ts +1 -1
- package/lib/summary/runningSummarizer.d.ts.map +1 -1
- package/lib/summary/runningSummarizer.js +41 -39
- package/lib/summary/runningSummarizer.js.map +1 -1
- package/lib/summary/summarizer.d.ts +2 -2
- package/lib/summary/summarizer.d.ts.map +1 -1
- package/lib/summary/summarizer.js +10 -8
- package/lib/summary/summarizer.js.map +1 -1
- package/lib/summary/summarizerClientElection.js +6 -6
- package/lib/summary/summarizerClientElection.js.map +1 -1
- package/lib/summary/summarizerHeuristics.js +9 -9
- package/lib/summary/summarizerHeuristics.js.map +1 -1
- package/lib/summary/summarizerNode/summarizerNode.d.ts +1 -1
- package/lib/summary/summarizerNode/summarizerNode.js +8 -8
- package/lib/summary/summarizerNode/summarizerNode.js.map +1 -1
- package/lib/summary/summarizerNode/summarizerNodeUtils.d.ts +1 -1
- package/lib/summary/summarizerNode/summarizerNodeUtils.d.ts.map +1 -1
- package/lib/summary/summarizerNode/summarizerNodeUtils.js +3 -3
- package/lib/summary/summarizerNode/summarizerNodeUtils.js.map +1 -1
- package/lib/summary/summarizerNode/summarizerNodeWithGc.d.ts +2 -2
- package/lib/summary/summarizerNode/summarizerNodeWithGc.d.ts.map +1 -1
- package/lib/summary/summarizerTypes.d.ts +14 -13
- 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 +21 -21
- package/lib/summary/summaryCollection.js.map +1 -1
- package/lib/summary/summaryFormat.d.ts +8 -5
- package/lib/summary/summaryFormat.d.ts.map +1 -1
- package/lib/summary/summaryFormat.js +3 -0
- package/lib/summary/summaryFormat.js.map +1 -1
- package/lib/summary/summaryGenerator.d.ts +3 -3
- package/lib/summary/summaryGenerator.d.ts.map +1 -1
- package/lib/summary/summaryManager.js +6 -6
- package/lib/summary/summaryManager.js.map +1 -1
- package/lib/throttler.js +16 -16
- package/lib/throttler.js.map +1 -1
- package/package.json +57 -22
- package/src/blobManager.ts +38 -28
- package/src/containerRuntime.ts +210 -278
- package/src/dataStore.ts +1 -1
- package/src/dataStoreContext.ts +4 -7
- package/src/dataStores.ts +4 -4
- package/src/gc/garbageCollection.md +53 -5
- package/src/gc/garbageCollection.ts +6 -3
- package/src/gc/gcDefinitions.ts +14 -27
- package/src/gc/gcEarlyAdoption.md +145 -0
- package/src/gc/gcHelpers.ts +0 -12
- package/src/gc/gcTelemetry.ts +1 -4
- package/src/gc/index.ts +2 -3
- package/src/index.ts +7 -4
- package/src/messageTypes.ts +225 -0
- package/src/opLifecycle/README.md +40 -40
- package/src/opLifecycle/definitions.ts +2 -1
- package/src/opLifecycle/opDecompressor.ts +0 -8
- package/src/opLifecycle/opGroupingManager.ts +7 -6
- package/src/opLifecycle/opSplitter.ts +2 -2
- package/src/opLifecycle/remoteMessageProcessor.ts +54 -33
- package/src/packageVersion.ts +1 -1
- package/src/pendingStateManager.ts +30 -52
- package/src/summary/runningSummarizer.ts +4 -2
- package/src/summary/summarizer.ts +5 -3
- package/src/summary/summarizerNode/summarizerNode.ts +1 -1
- package/src/summary/summarizerTypes.ts +2 -1
- package/src/summary/summaryFormat.ts +3 -0
package/lib/containerRuntime.js
CHANGED
|
@@ -8,7 +8,9 @@ import { DriverHeader, FetchSource, } from "@fluidframework/driver-definitions";
|
|
|
8
8
|
import { readAndParse } from "@fluidframework/driver-utils";
|
|
9
9
|
import { MessageType, SummaryType, } from "@fluidframework/protocol-definitions";
|
|
10
10
|
import { FlushMode, FlushModeExperimental, gcTreeKey, channelsTreeName, } from "@fluidframework/runtime-definitions";
|
|
11
|
-
import { addBlobToSummary, addSummarizeResultToSummary, addTreeToSummary, RequestParser, create404Response, exceptionToResponse, GCDataBuilder,
|
|
11
|
+
import { addBlobToSummary, addSummarizeResultToSummary, addTreeToSummary, RequestParser, create404Response, exceptionToResponse, GCDataBuilder,
|
|
12
|
+
// eslint-disable-next-line import/no-deprecated
|
|
13
|
+
requestFluidObject, seqFromTree, calculateStats, TelemetryContext, } from "@fluidframework/runtime-utils";
|
|
12
14
|
import { v4 as uuid } from "uuid";
|
|
13
15
|
import { ContainerFluidHandleContext } from "./containerHandleContext";
|
|
14
16
|
import { FluidDataStoreRegistry } from "./dataStoreRegistry";
|
|
@@ -25,32 +27,12 @@ import { BindBatchTracker } from "./batchTracker";
|
|
|
25
27
|
import { ScheduleManager } from "./scheduleManager";
|
|
26
28
|
import { OpCompressor, OpDecompressor, Outbox, OpSplitter, RemoteMessageProcessor, OpGroupingManager, getLongStack, } from "./opLifecycle";
|
|
27
29
|
import { DeltaManagerSummarizerProxy } from "./deltaManagerSummarizerProxy";
|
|
28
|
-
|
|
29
|
-
(function (ContainerMessageType) {
|
|
30
|
-
// An op to be delivered to store
|
|
31
|
-
ContainerMessageType["FluidDataStoreOp"] = "component";
|
|
32
|
-
// Creates a new store
|
|
33
|
-
ContainerMessageType["Attach"] = "attach";
|
|
34
|
-
// Chunked operation.
|
|
35
|
-
ContainerMessageType["ChunkedOp"] = "chunkedOp";
|
|
36
|
-
// Signifies that a blob has been attached and should not be garbage collected by storage
|
|
37
|
-
ContainerMessageType["BlobAttach"] = "blobAttach";
|
|
38
|
-
// Ties our new clientId to our old one on reconnect
|
|
39
|
-
ContainerMessageType["Rejoin"] = "rejoin";
|
|
40
|
-
// Sets the alias of a root data store
|
|
41
|
-
ContainerMessageType["Alias"] = "alias";
|
|
42
|
-
/**
|
|
43
|
-
* An op containing an IdRange of Ids allocated using the runtime's IdCompressor since
|
|
44
|
-
* the last allocation op was sent.
|
|
45
|
-
* See the [IdCompressor README](./id-compressor/README.md) for more details.
|
|
46
|
-
*/
|
|
47
|
-
ContainerMessageType["IdAllocation"] = "idAllocation";
|
|
48
|
-
})(ContainerMessageType || (ContainerMessageType = {}));
|
|
30
|
+
import { ContainerMessageType, } from "./messageTypes";
|
|
49
31
|
/**
|
|
50
32
|
* Utility to implement compat behaviors given an unknown message type
|
|
51
33
|
* The parameters are typed to support compile-time enforcement of handling all known types/behaviors
|
|
52
34
|
*
|
|
53
|
-
* @param _unknownContainerRuntimeMessageType - Typed as
|
|
35
|
+
* @param _unknownContainerRuntimeMessageType - Typed as something unexpected, to ensure all known types have been
|
|
54
36
|
* handled before calling this function (e.g. in a switch statement).
|
|
55
37
|
* @param compatBehavior - Typed redundantly with CompatModeBehavior to ensure handling is added when updating that type
|
|
56
38
|
*/
|
|
@@ -58,6 +40,12 @@ function compatBehaviorAllowsMessageType(_unknownContainerRuntimeMessageType, co
|
|
|
58
40
|
// undefined defaults to same behavior as "FailToProcess"
|
|
59
41
|
return compatBehavior === "Ignore";
|
|
60
42
|
}
|
|
43
|
+
function prepareLocalContainerRuntimeIdAllocationMessageForTransit(message) {
|
|
44
|
+
// Remove the stashedState from the op if it's a stashed op
|
|
45
|
+
if ("stashedState" in message.contents) {
|
|
46
|
+
delete message.contents.stashedState;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
61
49
|
export const DefaultSummaryConfiguration = {
|
|
62
50
|
state: "enabled",
|
|
63
51
|
minIdleTime: 0,
|
|
@@ -127,7 +115,7 @@ export const defaultPendingOpsRetryDelayMs = 1000;
|
|
|
127
115
|
*/
|
|
128
116
|
const defaultCloseSummarizerDelayMs = 5000; // 5 seconds
|
|
129
117
|
/**
|
|
130
|
-
* @deprecated - use
|
|
118
|
+
* @deprecated - use ContainerRuntimeMessageType instead
|
|
131
119
|
*/
|
|
132
120
|
export var RuntimeMessage;
|
|
133
121
|
(function (RuntimeMessage) {
|
|
@@ -183,15 +171,227 @@ export const makeLegacySendBatchFn = (submitFn, deltaManager) => (batch) => {
|
|
|
183
171
|
* It will define the store level mappings.
|
|
184
172
|
*/
|
|
185
173
|
export class ContainerRuntime extends TypedEventEmitter {
|
|
174
|
+
/**
|
|
175
|
+
* @deprecated - Will be removed in future major release. Migrate all usage of IFluidRouter to the "entryPoint" pattern. Refer to Removing-IFluidRouter.md
|
|
176
|
+
*/
|
|
177
|
+
get IFluidRouter() {
|
|
178
|
+
return this;
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* @deprecated - use loadRuntime instead.
|
|
182
|
+
* Load the stores from a snapshot and returns the runtime.
|
|
183
|
+
* @param context - Context of the container.
|
|
184
|
+
* @param registryEntries - Mapping to the stores.
|
|
185
|
+
* @param requestHandler - Request handlers for the container runtime
|
|
186
|
+
* @param runtimeOptions - Additional options to be passed to the runtime
|
|
187
|
+
* @param existing - (optional) When loading from an existing snapshot. Precedes context.existing if provided
|
|
188
|
+
* @param containerRuntimeCtor - (optional) Constructor to use to create the ContainerRuntime instance. This
|
|
189
|
+
* allows mixin classes to leverage this method to define their own async initializer.
|
|
190
|
+
*/
|
|
191
|
+
static async load(context, registryEntries, requestHandler, runtimeOptions = {}, containerScope = context.scope, existing, containerRuntimeCtor = ContainerRuntime) {
|
|
192
|
+
let existingFlag = true;
|
|
193
|
+
if (!existing) {
|
|
194
|
+
existingFlag = false;
|
|
195
|
+
}
|
|
196
|
+
return this.loadRuntime({
|
|
197
|
+
context,
|
|
198
|
+
registryEntries,
|
|
199
|
+
existing: existingFlag,
|
|
200
|
+
runtimeOptions,
|
|
201
|
+
containerScope,
|
|
202
|
+
containerRuntimeCtor,
|
|
203
|
+
requestHandler,
|
|
204
|
+
provideEntryPoint: () => {
|
|
205
|
+
throw new UsageError("ContainerRuntime.load is deprecated and should no longer be used");
|
|
206
|
+
},
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Load the stores from a snapshot and returns the runtime.
|
|
211
|
+
* @param params - An object housing the runtime properties:
|
|
212
|
+
* - context - Context of the container.
|
|
213
|
+
* - registryEntries - Mapping from data store types to their corresponding factories.
|
|
214
|
+
* - existing - Pass 'true' if loading from an existing snapshot.
|
|
215
|
+
* - requestHandler - (optional) Request handler for the request() method of the container runtime.
|
|
216
|
+
* Only relevant for back-compat while we remove the request() method and move fully to entryPoint as the main pattern.
|
|
217
|
+
* - runtimeOptions - Additional options to be passed to the runtime
|
|
218
|
+
* - containerScope - runtime services provided with context
|
|
219
|
+
* - containerRuntimeCtor - Constructor to use to create the ContainerRuntime instance.
|
|
220
|
+
* This allows mixin classes to leverage this method to define their own async initializer.
|
|
221
|
+
* - provideEntryPoint - Promise that resolves to an object which will act as entryPoint for the Container.
|
|
222
|
+
* This object should provide all the functionality that the Container is expected to provide to the loader layer.
|
|
223
|
+
*/
|
|
224
|
+
static async loadRuntime(params) {
|
|
225
|
+
const { context, registryEntries, existing, requestHandler, provideEntryPoint, runtimeOptions = {}, containerScope = {}, containerRuntimeCtor = ContainerRuntime, } = params;
|
|
226
|
+
// If taggedLogger exists, use it. Otherwise, wrap the vanilla logger:
|
|
227
|
+
// back-compat: Remove the TaggedLoggerAdapter fallback once all the host are using loader > 0.45
|
|
228
|
+
const backCompatContext = context;
|
|
229
|
+
const passLogger = backCompatContext.taggedLogger ??
|
|
230
|
+
// eslint-disable-next-line import/no-deprecated
|
|
231
|
+
new TaggedLoggerAdapter(backCompatContext.logger);
|
|
232
|
+
const logger = createChildLogger({
|
|
233
|
+
logger: passLogger,
|
|
234
|
+
properties: {
|
|
235
|
+
all: {
|
|
236
|
+
runtimeVersion: pkgVersion,
|
|
237
|
+
},
|
|
238
|
+
},
|
|
239
|
+
});
|
|
240
|
+
const { summaryOptions = {}, gcOptions = {}, loadSequenceNumberVerification = "close", flushMode = defaultFlushMode, compressionOptions = defaultCompressionConfig, maxBatchSizeInBytes = defaultMaxBatchSizeInBytes, enableRuntimeIdCompressor = false, chunkSizeInBytes = defaultChunkSizeInBytes, enableOpReentryCheck = false, enableGroupedBatching = false, } = runtimeOptions;
|
|
241
|
+
const registry = new FluidDataStoreRegistry(registryEntries);
|
|
242
|
+
const tryFetchBlob = async (blobName) => {
|
|
243
|
+
const blobId = context.baseSnapshot?.blobs[blobName];
|
|
244
|
+
if (context.baseSnapshot && blobId) {
|
|
245
|
+
// IContainerContext storage api return type still has undefined in 0.39 package version.
|
|
246
|
+
// So once we release 0.40 container-defn package we can remove this check.
|
|
247
|
+
assert(context.storage !== undefined, 0x1f5 /* "Attached state should have storage" */);
|
|
248
|
+
return readAndParse(context.storage, blobId);
|
|
249
|
+
}
|
|
250
|
+
};
|
|
251
|
+
const [chunks, metadata, electedSummarizerData, aliases, serializedIdCompressor] = await Promise.all([
|
|
252
|
+
tryFetchBlob(chunksBlobName),
|
|
253
|
+
tryFetchBlob(metadataBlobName),
|
|
254
|
+
tryFetchBlob(electedSummarizerBlobName),
|
|
255
|
+
tryFetchBlob(aliasBlobName),
|
|
256
|
+
tryFetchBlob(idCompressorBlobName),
|
|
257
|
+
]);
|
|
258
|
+
// read snapshot blobs needed for BlobManager to load
|
|
259
|
+
const blobManagerSnapshot = await BlobManager.load(context.baseSnapshot?.trees[blobsTreeName], async (id) => {
|
|
260
|
+
// IContainerContext storage api return type still has undefined in 0.39 package version.
|
|
261
|
+
// So once we release 0.40 container-defn package we can remove this check.
|
|
262
|
+
assert(context.storage !== undefined, 0x256 /* "storage undefined in attached container" */);
|
|
263
|
+
return readAndParse(context.storage, id);
|
|
264
|
+
});
|
|
265
|
+
// Verify summary runtime sequence number matches protocol sequence number.
|
|
266
|
+
const runtimeSequenceNumber = metadata?.message?.sequenceNumber;
|
|
267
|
+
// When we load with pending state, we reuse an old snapshot so we don't expect these numbers to match
|
|
268
|
+
if (!context.pendingLocalState && runtimeSequenceNumber !== undefined) {
|
|
269
|
+
const protocolSequenceNumber = context.deltaManager.initialSequenceNumber;
|
|
270
|
+
// Unless bypass is explicitly set, then take action when sequence numbers mismatch.
|
|
271
|
+
if (loadSequenceNumberVerification !== "bypass" &&
|
|
272
|
+
runtimeSequenceNumber !== protocolSequenceNumber) {
|
|
273
|
+
// "Load from summary, runtime metadata sequenceNumber !== initialSequenceNumber"
|
|
274
|
+
const error = new DataCorruptionError(
|
|
275
|
+
// pre-0.58 error message: SummaryMetadataMismatch
|
|
276
|
+
"Summary metadata mismatch", { runtimeVersion: pkgVersion, runtimeSequenceNumber, protocolSequenceNumber });
|
|
277
|
+
if (loadSequenceNumberVerification === "log") {
|
|
278
|
+
logger.sendErrorEvent({ eventName: "SequenceNumberMismatch" }, error);
|
|
279
|
+
}
|
|
280
|
+
else {
|
|
281
|
+
context.closeFn(error);
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
const idCompressorEnabled = metadata?.idCompressorEnabled ?? runtimeOptions.enableRuntimeIdCompressor ?? false;
|
|
286
|
+
let idCompressor;
|
|
287
|
+
if (idCompressorEnabled) {
|
|
288
|
+
const { IdCompressor, createSessionId } = await import("./id-compressor");
|
|
289
|
+
idCompressor =
|
|
290
|
+
serializedIdCompressor !== undefined
|
|
291
|
+
? IdCompressor.deserialize(serializedIdCompressor, createSessionId())
|
|
292
|
+
: IdCompressor.create(logger);
|
|
293
|
+
}
|
|
294
|
+
const runtime = new containerRuntimeCtor(context, registry, metadata, electedSummarizerData, chunks ?? [], aliases ?? [], {
|
|
295
|
+
summaryOptions,
|
|
296
|
+
gcOptions,
|
|
297
|
+
loadSequenceNumberVerification,
|
|
298
|
+
flushMode,
|
|
299
|
+
compressionOptions,
|
|
300
|
+
maxBatchSizeInBytes,
|
|
301
|
+
chunkSizeInBytes,
|
|
302
|
+
enableRuntimeIdCompressor,
|
|
303
|
+
enableOpReentryCheck,
|
|
304
|
+
enableGroupedBatching,
|
|
305
|
+
}, containerScope, logger, existing, blobManagerSnapshot, context.storage, idCompressor, provideEntryPoint, requestHandler, undefined);
|
|
306
|
+
// Apply stashed ops with a reference sequence number equal to the sequence number of the snapshot,
|
|
307
|
+
// or zero. This must be done before Container replays saved ops.
|
|
308
|
+
await runtime.pendingStateManager.applyStashedOpsAt(runtimeSequenceNumber ?? 0);
|
|
309
|
+
// Initialize the base state of the runtime before it's returned.
|
|
310
|
+
await runtime.initializeBaseState();
|
|
311
|
+
return runtime;
|
|
312
|
+
}
|
|
313
|
+
get clientId() {
|
|
314
|
+
return this._getClientId();
|
|
315
|
+
}
|
|
316
|
+
get storage() {
|
|
317
|
+
return this._storage;
|
|
318
|
+
}
|
|
319
|
+
get flushMode() {
|
|
320
|
+
return this._flushMode;
|
|
321
|
+
}
|
|
322
|
+
get scope() {
|
|
323
|
+
return this.containerScope;
|
|
324
|
+
}
|
|
325
|
+
get IFluidDataStoreRegistry() {
|
|
326
|
+
return this.registry;
|
|
327
|
+
}
|
|
328
|
+
get attachState() {
|
|
329
|
+
return this._getAttachState();
|
|
330
|
+
}
|
|
331
|
+
get IFluidHandleContext() {
|
|
332
|
+
return this.handleContext;
|
|
333
|
+
}
|
|
334
|
+
/**
|
|
335
|
+
* Invokes the given callback and expects that no ops are submitted
|
|
336
|
+
* until execution finishes. If an op is submitted, an error will be raised.
|
|
337
|
+
*
|
|
338
|
+
* Can be disabled by feature gate `Fluid.ContainerRuntime.DisableOpReentryCheck`
|
|
339
|
+
*
|
|
340
|
+
* @param callback - the callback to be invoked
|
|
341
|
+
*/
|
|
342
|
+
ensureNoDataModelChanges(callback) {
|
|
343
|
+
this.ensureNoDataModelChangesCalls++;
|
|
344
|
+
try {
|
|
345
|
+
return callback();
|
|
346
|
+
}
|
|
347
|
+
finally {
|
|
348
|
+
this.ensureNoDataModelChangesCalls--;
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
get connected() {
|
|
352
|
+
return this._connected;
|
|
353
|
+
}
|
|
354
|
+
/** clientId of parent (non-summarizing) container that owns summarizer container */
|
|
355
|
+
get summarizerClientId() {
|
|
356
|
+
return this.summarizerClientElection?.electedClientId;
|
|
357
|
+
}
|
|
358
|
+
get disposed() {
|
|
359
|
+
return this._disposed;
|
|
360
|
+
}
|
|
361
|
+
get summarizer() {
|
|
362
|
+
assert(this._summarizer !== undefined, 0x257 /* "This is not summarizing container" */);
|
|
363
|
+
return this._summarizer;
|
|
364
|
+
}
|
|
365
|
+
isSummariesDisabled() {
|
|
366
|
+
return this.summaryConfiguration.state === "disabled";
|
|
367
|
+
}
|
|
368
|
+
isHeuristicsDisabled() {
|
|
369
|
+
return this.summaryConfiguration.state === "disableHeuristics";
|
|
370
|
+
}
|
|
371
|
+
getMaxOpsSinceLastSummary() {
|
|
372
|
+
return this.summaryConfiguration.state !== "disabled"
|
|
373
|
+
? this.summaryConfiguration.maxOpsSinceLastSummary
|
|
374
|
+
: 0;
|
|
375
|
+
}
|
|
376
|
+
getInitialSummarizerDelayMs() {
|
|
377
|
+
// back-compat: initialSummarizerDelayMs was moved from ISummaryRuntimeOptions
|
|
378
|
+
// to ISummaryConfiguration in 0.60.
|
|
379
|
+
if (this.runtimeOptions.summaryOptions.initialSummarizerDelayMs !== undefined) {
|
|
380
|
+
return this.runtimeOptions.summaryOptions.initialSummarizerDelayMs;
|
|
381
|
+
}
|
|
382
|
+
return this.summaryConfiguration.state !== "disabled"
|
|
383
|
+
? this.summaryConfiguration.initialSummarizerDelayMs
|
|
384
|
+
: 0;
|
|
385
|
+
}
|
|
186
386
|
/**
|
|
187
387
|
* @internal
|
|
188
388
|
*/
|
|
189
|
-
constructor(context, registry, metadata, electedSummarizerData, chunks, dataStoreAliasMap, runtimeOptions, containerScope, logger, existing, blobManagerSnapshot, _storage, idCompressor, requestHandler, summaryConfiguration = {
|
|
389
|
+
constructor(context, registry, metadata, electedSummarizerData, chunks, dataStoreAliasMap, runtimeOptions, containerScope, logger, existing, blobManagerSnapshot, _storage, idCompressor, provideEntryPoint, requestHandler, summaryConfiguration = {
|
|
190
390
|
// the defaults
|
|
191
391
|
...DefaultSummaryConfiguration,
|
|
192
392
|
// the runtime configuration overrides
|
|
193
393
|
...runtimeOptions.summaryOptions?.summaryConfigOverrides,
|
|
194
|
-
}
|
|
394
|
+
}) {
|
|
195
395
|
super();
|
|
196
396
|
this.registry = registry;
|
|
197
397
|
this.runtimeOptions = runtimeOptions;
|
|
@@ -495,26 +695,6 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
495
695
|
this.summaryManager.start();
|
|
496
696
|
}
|
|
497
697
|
}
|
|
498
|
-
this.deltaManager.on("readonly", (readonly) => {
|
|
499
|
-
// we accumulate ops while being in read-only state.
|
|
500
|
-
// once user gets write permissions and we have active connection, flush all pending ops.
|
|
501
|
-
// Note that the inner (non-proxy) delta manager is needed here to get the readonly information.
|
|
502
|
-
assert(readonly === this.innerDeltaManager.readOnlyInfo.readonly, 0x124 /* "inconsistent readonly property/event state" */);
|
|
503
|
-
// We need to be very careful with when we (re)send pending ops, to ensure that we only send ops
|
|
504
|
-
// when we either never send an op, or attempted to send it but we know for sure it was not
|
|
505
|
-
// sequenced by server and will never be sequenced (i.e. was lost)
|
|
506
|
-
// For loss of connection, we wait for our own "join" op and use it a a barrier to know all the
|
|
507
|
-
// ops that made it from previous connection, before switching clientId and raising "connected" event
|
|
508
|
-
// But with read-only permissions, if we transition between read-only and r/w states while on same
|
|
509
|
-
// connection, then we have no good signal to tell us when it's safe to send ops we accumulated while
|
|
510
|
-
// being in read-only state.
|
|
511
|
-
// For that reason, we support getting to read-only state only when disconnected. This ensures that we
|
|
512
|
-
// can rely on same safety mechanism and resend ops only when we establish new connection.
|
|
513
|
-
// This is applicable for read-only permissions (event is raised before connection is properly registered),
|
|
514
|
-
// but it's an extra requirement for Container.forceReadonly() API
|
|
515
|
-
assert(!readonly || !this.connected, 0x125 /* "Unsafe to transition to read-only state!" */);
|
|
516
|
-
this.replayPendingStates();
|
|
517
|
-
});
|
|
518
698
|
// logging hardware telemetry
|
|
519
699
|
logger.sendTelemetryEvent({
|
|
520
700
|
eventName: "DeviceSpec",
|
|
@@ -548,233 +728,8 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
548
728
|
assert(this._summarizer !== undefined, 0x5bf /* Summarizer object is undefined in a summarizer client */);
|
|
549
729
|
return this._summarizer;
|
|
550
730
|
}
|
|
551
|
-
return
|
|
552
|
-
});
|
|
553
|
-
}
|
|
554
|
-
/**
|
|
555
|
-
* @deprecated - Will be removed in future major release. Migrate all usage of IFluidRouter to the "entryPoint" pattern. Refer to Removing-IFluidRouter.md
|
|
556
|
-
*/
|
|
557
|
-
get IFluidRouter() {
|
|
558
|
-
return this;
|
|
559
|
-
}
|
|
560
|
-
/**
|
|
561
|
-
* @deprecated - use loadRuntime instead.
|
|
562
|
-
* Load the stores from a snapshot and returns the runtime.
|
|
563
|
-
* @param context - Context of the container.
|
|
564
|
-
* @param registryEntries - Mapping to the stores.
|
|
565
|
-
* @param requestHandler - Request handlers for the container runtime
|
|
566
|
-
* @param runtimeOptions - Additional options to be passed to the runtime
|
|
567
|
-
* @param existing - (optional) When loading from an existing snapshot. Precedes context.existing if provided
|
|
568
|
-
* @param containerRuntimeCtor - (optional) Constructor to use to create the ContainerRuntime instance. This
|
|
569
|
-
* allows mixin classes to leverage this method to define their own async initializer.
|
|
570
|
-
*/
|
|
571
|
-
static async load(context, registryEntries, requestHandler, runtimeOptions = {}, containerScope = context.scope, existing, containerRuntimeCtor = ContainerRuntime) {
|
|
572
|
-
let existingFlag = true;
|
|
573
|
-
if (!existing) {
|
|
574
|
-
existingFlag = false;
|
|
575
|
-
}
|
|
576
|
-
return this.loadRuntime({
|
|
577
|
-
context,
|
|
578
|
-
registryEntries,
|
|
579
|
-
existing: existingFlag,
|
|
580
|
-
requestHandler,
|
|
581
|
-
runtimeOptions,
|
|
582
|
-
containerScope,
|
|
583
|
-
containerRuntimeCtor,
|
|
584
|
-
});
|
|
585
|
-
}
|
|
586
|
-
/**
|
|
587
|
-
* Load the stores from a snapshot and returns the runtime.
|
|
588
|
-
* @param params - An object housing the runtime properties:
|
|
589
|
-
* - context - Context of the container.
|
|
590
|
-
* - registryEntries - Mapping from data store types to their corresponding factories.
|
|
591
|
-
* - existing - Pass 'true' if loading from an existing snapshot.
|
|
592
|
-
* - requestHandler - (optional) Request handler for the request() method of the container runtime.
|
|
593
|
-
* Only relevant for back-compat while we remove the request() method and move fully to entryPoint as the main pattern.
|
|
594
|
-
* - runtimeOptions - Additional options to be passed to the runtime
|
|
595
|
-
* - containerScope - runtime services provided with context
|
|
596
|
-
* - containerRuntimeCtor - Constructor to use to create the ContainerRuntime instance.
|
|
597
|
-
* This allows mixin classes to leverage this method to define their own async initializer.
|
|
598
|
-
* - initializeEntryPoint - Promise that resolves to an object which will act as entryPoint for the Container.
|
|
599
|
-
* This object should provide all the functionality that the Container is expected to provide to the loader layer.
|
|
600
|
-
*/
|
|
601
|
-
static async loadRuntime(params) {
|
|
602
|
-
const { context, registryEntries, existing, requestHandler, runtimeOptions = {}, containerScope = {}, containerRuntimeCtor = ContainerRuntime, } = params;
|
|
603
|
-
const initializeEntryPoint = params.initializeEntryPoint ??
|
|
604
|
-
(async (containerRuntime) => ({
|
|
605
|
-
get IFluidRouter() {
|
|
606
|
-
return this;
|
|
607
|
-
},
|
|
608
|
-
async request(req) {
|
|
609
|
-
return containerRuntime.request(req);
|
|
610
|
-
},
|
|
611
|
-
}));
|
|
612
|
-
// If taggedLogger exists, use it. Otherwise, wrap the vanilla logger:
|
|
613
|
-
// back-compat: Remove the TaggedLoggerAdapter fallback once all the host are using loader > 0.45
|
|
614
|
-
const backCompatContext = context;
|
|
615
|
-
const passLogger = backCompatContext.taggedLogger ??
|
|
616
|
-
// eslint-disable-next-line import/no-deprecated
|
|
617
|
-
new TaggedLoggerAdapter(backCompatContext.logger);
|
|
618
|
-
const logger = createChildLogger({
|
|
619
|
-
logger: passLogger,
|
|
620
|
-
properties: {
|
|
621
|
-
all: {
|
|
622
|
-
runtimeVersion: pkgVersion,
|
|
623
|
-
},
|
|
624
|
-
},
|
|
625
|
-
});
|
|
626
|
-
const { summaryOptions = {}, gcOptions = {}, loadSequenceNumberVerification = "close", flushMode = defaultFlushMode, compressionOptions = defaultCompressionConfig, maxBatchSizeInBytes = defaultMaxBatchSizeInBytes, enableRuntimeIdCompressor = false, chunkSizeInBytes = defaultChunkSizeInBytes, enableOpReentryCheck = false, enableGroupedBatching = false, } = runtimeOptions;
|
|
627
|
-
const registry = new FluidDataStoreRegistry(registryEntries);
|
|
628
|
-
const tryFetchBlob = async (blobName) => {
|
|
629
|
-
const blobId = context.baseSnapshot?.blobs[blobName];
|
|
630
|
-
if (context.baseSnapshot && blobId) {
|
|
631
|
-
// IContainerContext storage api return type still has undefined in 0.39 package version.
|
|
632
|
-
// So once we release 0.40 container-defn package we can remove this check.
|
|
633
|
-
assert(context.storage !== undefined, 0x1f5 /* "Attached state should have storage" */);
|
|
634
|
-
return readAndParse(context.storage, blobId);
|
|
635
|
-
}
|
|
636
|
-
};
|
|
637
|
-
const [chunks, metadata, electedSummarizerData, aliases, serializedIdCompressor] = await Promise.all([
|
|
638
|
-
tryFetchBlob(chunksBlobName),
|
|
639
|
-
tryFetchBlob(metadataBlobName),
|
|
640
|
-
tryFetchBlob(electedSummarizerBlobName),
|
|
641
|
-
tryFetchBlob(aliasBlobName),
|
|
642
|
-
tryFetchBlob(idCompressorBlobName),
|
|
643
|
-
]);
|
|
644
|
-
// read snapshot blobs needed for BlobManager to load
|
|
645
|
-
const blobManagerSnapshot = await BlobManager.load(context.baseSnapshot?.trees[blobsTreeName], async (id) => {
|
|
646
|
-
// IContainerContext storage api return type still has undefined in 0.39 package version.
|
|
647
|
-
// So once we release 0.40 container-defn package we can remove this check.
|
|
648
|
-
assert(context.storage !== undefined, 0x256 /* "storage undefined in attached container" */);
|
|
649
|
-
return readAndParse(context.storage, id);
|
|
731
|
+
return provideEntryPoint(this);
|
|
650
732
|
});
|
|
651
|
-
// Verify summary runtime sequence number matches protocol sequence number.
|
|
652
|
-
const runtimeSequenceNumber = metadata?.message?.sequenceNumber;
|
|
653
|
-
// When we load with pending state, we reuse an old snapshot so we don't expect these numbers to match
|
|
654
|
-
if (!context.pendingLocalState && runtimeSequenceNumber !== undefined) {
|
|
655
|
-
const protocolSequenceNumber = context.deltaManager.initialSequenceNumber;
|
|
656
|
-
// Unless bypass is explicitly set, then take action when sequence numbers mismatch.
|
|
657
|
-
if (loadSequenceNumberVerification !== "bypass" &&
|
|
658
|
-
runtimeSequenceNumber !== protocolSequenceNumber) {
|
|
659
|
-
// "Load from summary, runtime metadata sequenceNumber !== initialSequenceNumber"
|
|
660
|
-
const error = new DataCorruptionError(
|
|
661
|
-
// pre-0.58 error message: SummaryMetadataMismatch
|
|
662
|
-
"Summary metadata mismatch", { runtimeVersion: pkgVersion, runtimeSequenceNumber, protocolSequenceNumber });
|
|
663
|
-
if (loadSequenceNumberVerification === "log") {
|
|
664
|
-
logger.sendErrorEvent({ eventName: "SequenceNumberMismatch" }, error);
|
|
665
|
-
}
|
|
666
|
-
else {
|
|
667
|
-
context.closeFn(error);
|
|
668
|
-
}
|
|
669
|
-
}
|
|
670
|
-
}
|
|
671
|
-
const idCompressorEnabled = metadata?.idCompressorEnabled ?? runtimeOptions.enableRuntimeIdCompressor ?? false;
|
|
672
|
-
let idCompressor;
|
|
673
|
-
if (idCompressorEnabled) {
|
|
674
|
-
const { IdCompressor, createSessionId } = await import("./id-compressor");
|
|
675
|
-
idCompressor =
|
|
676
|
-
serializedIdCompressor !== undefined
|
|
677
|
-
? IdCompressor.deserialize(serializedIdCompressor, createSessionId())
|
|
678
|
-
: IdCompressor.create(logger);
|
|
679
|
-
}
|
|
680
|
-
const runtime = new containerRuntimeCtor(context, registry, metadata, electedSummarizerData, chunks ?? [], aliases ?? [], {
|
|
681
|
-
summaryOptions,
|
|
682
|
-
gcOptions,
|
|
683
|
-
loadSequenceNumberVerification,
|
|
684
|
-
flushMode,
|
|
685
|
-
compressionOptions,
|
|
686
|
-
maxBatchSizeInBytes,
|
|
687
|
-
chunkSizeInBytes,
|
|
688
|
-
enableRuntimeIdCompressor,
|
|
689
|
-
enableOpReentryCheck,
|
|
690
|
-
enableGroupedBatching,
|
|
691
|
-
}, containerScope, logger, existing, blobManagerSnapshot, context.storage, idCompressor, requestHandler, undefined, // summaryConfiguration
|
|
692
|
-
initializeEntryPoint);
|
|
693
|
-
await runtime.blobManager.processStashedChanges();
|
|
694
|
-
// It's possible to have ops with a reference sequence number of 0. Op sequence numbers start
|
|
695
|
-
// at 1, so we won't see a replayed saved op with a sequence number of 0.
|
|
696
|
-
await runtime.pendingStateManager.applyStashedOpsAt(0);
|
|
697
|
-
// Initialize the base state of the runtime before it's returned.
|
|
698
|
-
await runtime.initializeBaseState();
|
|
699
|
-
return runtime;
|
|
700
|
-
}
|
|
701
|
-
get clientId() {
|
|
702
|
-
return this._getClientId();
|
|
703
|
-
}
|
|
704
|
-
get storage() {
|
|
705
|
-
return this._storage;
|
|
706
|
-
}
|
|
707
|
-
/** @deprecated - The functionality is no longer exposed publicly */
|
|
708
|
-
get reSubmitFn() {
|
|
709
|
-
return (type, contents, localOpMetadata, opMetadata) => this.reSubmitCore({ type, contents }, localOpMetadata, opMetadata);
|
|
710
|
-
// Note: compatDetails is not included in this deprecated API
|
|
711
|
-
}
|
|
712
|
-
get flushMode() {
|
|
713
|
-
return this._flushMode;
|
|
714
|
-
}
|
|
715
|
-
get scope() {
|
|
716
|
-
return this.containerScope;
|
|
717
|
-
}
|
|
718
|
-
get IFluidDataStoreRegistry() {
|
|
719
|
-
return this.registry;
|
|
720
|
-
}
|
|
721
|
-
get attachState() {
|
|
722
|
-
return this._getAttachState();
|
|
723
|
-
}
|
|
724
|
-
get IFluidHandleContext() {
|
|
725
|
-
return this.handleContext;
|
|
726
|
-
}
|
|
727
|
-
/**
|
|
728
|
-
* Invokes the given callback and expects that no ops are submitted
|
|
729
|
-
* until execution finishes. If an op is submitted, an error will be raised.
|
|
730
|
-
*
|
|
731
|
-
* Can be disabled by feature gate `Fluid.ContainerRuntime.DisableOpReentryCheck`
|
|
732
|
-
*
|
|
733
|
-
* @param callback - the callback to be invoked
|
|
734
|
-
*/
|
|
735
|
-
ensureNoDataModelChanges(callback) {
|
|
736
|
-
this.ensureNoDataModelChangesCalls++;
|
|
737
|
-
try {
|
|
738
|
-
return callback();
|
|
739
|
-
}
|
|
740
|
-
finally {
|
|
741
|
-
this.ensureNoDataModelChangesCalls--;
|
|
742
|
-
}
|
|
743
|
-
}
|
|
744
|
-
get connected() {
|
|
745
|
-
return this._connected;
|
|
746
|
-
}
|
|
747
|
-
/** clientId of parent (non-summarizing) container that owns summarizer container */
|
|
748
|
-
get summarizerClientId() {
|
|
749
|
-
return this.summarizerClientElection?.electedClientId;
|
|
750
|
-
}
|
|
751
|
-
get disposed() {
|
|
752
|
-
return this._disposed;
|
|
753
|
-
}
|
|
754
|
-
get summarizer() {
|
|
755
|
-
assert(this._summarizer !== undefined, 0x257 /* "This is not summarizing container" */);
|
|
756
|
-
return this._summarizer;
|
|
757
|
-
}
|
|
758
|
-
isSummariesDisabled() {
|
|
759
|
-
return this.summaryConfiguration.state === "disabled";
|
|
760
|
-
}
|
|
761
|
-
isHeuristicsDisabled() {
|
|
762
|
-
return this.summaryConfiguration.state === "disableHeuristics";
|
|
763
|
-
}
|
|
764
|
-
getMaxOpsSinceLastSummary() {
|
|
765
|
-
return this.summaryConfiguration.state !== "disabled"
|
|
766
|
-
? this.summaryConfiguration.maxOpsSinceLastSummary
|
|
767
|
-
: 0;
|
|
768
|
-
}
|
|
769
|
-
getInitialSummarizerDelayMs() {
|
|
770
|
-
// back-compat: initialSummarizerDelayMs was moved from ISummaryRuntimeOptions
|
|
771
|
-
// to ISummaryConfiguration in 0.60.
|
|
772
|
-
if (this.runtimeOptions.summaryOptions.initialSummarizerDelayMs !== undefined) {
|
|
773
|
-
return this.runtimeOptions.summaryOptions.initialSummarizerDelayMs;
|
|
774
|
-
}
|
|
775
|
-
return this.summaryConfiguration.state !== "disabled"
|
|
776
|
-
? this.summaryConfiguration.initialSummarizerDelayMs
|
|
777
|
-
: 0;
|
|
778
733
|
}
|
|
779
734
|
/**
|
|
780
735
|
* Initializes the state from the base snapshot this container runtime loaded from.
|
|
@@ -1024,23 +979,24 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1024
979
|
* Parse an op's type and actual content from given serialized content
|
|
1025
980
|
* ! Note: this format needs to be in-line with what is set in the "ContainerRuntime.submit(...)" method
|
|
1026
981
|
*/
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
982
|
+
// TODO: markfields: confirm Local- versus Outbound- ContainerRuntimeMessage typing
|
|
983
|
+
parseLocalOpContent(serializedContents) {
|
|
984
|
+
assert(serializedContents !== undefined, 0x6d5 /* content must be defined */);
|
|
985
|
+
const message = JSON.parse(serializedContents);
|
|
986
|
+
assert(message.type !== undefined, 0x6d6 /* incorrect op content format */);
|
|
987
|
+
return message;
|
|
988
|
+
}
|
|
989
|
+
async applyStashedOp(serializedOpContent) {
|
|
1034
990
|
// Need to parse from string for back-compat
|
|
1035
|
-
const
|
|
1036
|
-
switch (type) {
|
|
991
|
+
const opContents = this.parseLocalOpContent(serializedOpContent);
|
|
992
|
+
switch (opContents.type) {
|
|
1037
993
|
case ContainerMessageType.FluidDataStoreOp:
|
|
1038
|
-
return this.dataStores.applyStashedOp(contents);
|
|
994
|
+
return this.dataStores.applyStashedOp(opContents.contents);
|
|
1039
995
|
case ContainerMessageType.Attach:
|
|
1040
|
-
return this.dataStores.applyStashedAttachOp(contents);
|
|
996
|
+
return this.dataStores.applyStashedAttachOp(opContents.contents);
|
|
1041
997
|
case ContainerMessageType.IdAllocation:
|
|
1042
998
|
assert(this.idCompressor !== undefined, 0x67b /* IdCompressor should be defined if enabled */);
|
|
1043
|
-
return this.applyStashedIdAllocationOp(contents);
|
|
999
|
+
return this.applyStashedIdAllocationOp(opContents.contents);
|
|
1044
1000
|
case ContainerMessageType.Alias:
|
|
1045
1001
|
case ContainerMessageType.BlobAttach:
|
|
1046
1002
|
return;
|
|
@@ -1052,11 +1008,11 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1052
1008
|
// This should be extremely rare for stashed ops.
|
|
1053
1009
|
// It would require a newer runtime stashing ops and then an older one applying them,
|
|
1054
1010
|
// e.g. if an app rolled back its container version
|
|
1055
|
-
const compatBehavior = compatDetails?.behavior;
|
|
1056
|
-
if (!compatBehaviorAllowsMessageType(type, compatBehavior)) {
|
|
1011
|
+
const compatBehavior = opContents.compatDetails?.behavior;
|
|
1012
|
+
if (!compatBehaviorAllowsMessageType(opContents.type, compatBehavior)) {
|
|
1057
1013
|
const error = DataProcessingError.create("Stashed runtime message of unknown type", "applyStashedOp", undefined /* sequencedMessage */, {
|
|
1058
1014
|
messageDetails: JSON.stringify({
|
|
1059
|
-
type,
|
|
1015
|
+
type: opContents.type,
|
|
1060
1016
|
compatBehavior,
|
|
1061
1017
|
}),
|
|
1062
1018
|
});
|
|
@@ -1075,6 +1031,23 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1075
1031
|
// Don't propagate "disconnected" event because we didn't propagate the previous "connected" event
|
|
1076
1032
|
return;
|
|
1077
1033
|
}
|
|
1034
|
+
// If there are stashed blobs in the pending state, we need to delay
|
|
1035
|
+
// propagation of the "connected" event until we have uploaded them to
|
|
1036
|
+
// ensure we don't submit ops referencing a blob that has not been uploaded
|
|
1037
|
+
const connecting = connected && !this._connected;
|
|
1038
|
+
if (connecting && this.blobManager.hasPendingStashedBlobs()) {
|
|
1039
|
+
assert(!this.delayConnectClientId, 0x791 /* Connect event delay must be canceled before subsequent connect event */);
|
|
1040
|
+
assert(!!clientId, 0x792 /* Must have clientId when connecting */);
|
|
1041
|
+
this.delayConnectClientId = clientId;
|
|
1042
|
+
this.blobManager.processStashedChanges().then(() => {
|
|
1043
|
+
// make sure we didn't reconnect before the promise resolved
|
|
1044
|
+
if (this.delayConnectClientId === clientId && !this.disposed) {
|
|
1045
|
+
this.delayConnectClientId = undefined;
|
|
1046
|
+
this.setConnectionStateCore(connected, clientId);
|
|
1047
|
+
}
|
|
1048
|
+
}, (error) => this.closeFn(error));
|
|
1049
|
+
return;
|
|
1050
|
+
}
|
|
1078
1051
|
this.setConnectionStateCore(connected, clientId);
|
|
1079
1052
|
}
|
|
1080
1053
|
setConnectionStateCore(connected, clientId) {
|
|
@@ -1127,18 +1100,33 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1127
1100
|
// or something different, like a system message.
|
|
1128
1101
|
const modernRuntimeMessage = messageArg.type === MessageType.Operation;
|
|
1129
1102
|
// Do shallow copy of message, as the processing flow will modify it.
|
|
1103
|
+
// There might be multiple container instances receiving the same message.
|
|
1104
|
+
// We do not need to make a deep copy. Each layer will just replace message.contents itself,
|
|
1105
|
+
// but will not modify the contents object (likely it will replace it on the message).
|
|
1130
1106
|
const messageCopy = { ...messageArg };
|
|
1131
1107
|
for (const message of this.remoteMessageProcessor.process(messageCopy)) {
|
|
1132
|
-
|
|
1108
|
+
if (modernRuntimeMessage) {
|
|
1109
|
+
this.processCore({
|
|
1110
|
+
// Cast it since we expect it to be this based on modernRuntimeMessage computation above.
|
|
1111
|
+
// There is nothing really ensuring that anytime original message.type is Operation that
|
|
1112
|
+
// the result messages will be so. In the end modern bool being true only directs to
|
|
1113
|
+
// throw error if ultimately unrecognized without compat details saying otherwise.
|
|
1114
|
+
message: message,
|
|
1115
|
+
local,
|
|
1116
|
+
modernRuntimeMessage,
|
|
1117
|
+
});
|
|
1118
|
+
}
|
|
1119
|
+
else {
|
|
1120
|
+
// Unrecognized message will be ignored.
|
|
1121
|
+
this.processCore({ message, local, modernRuntimeMessage });
|
|
1122
|
+
}
|
|
1133
1123
|
}
|
|
1134
1124
|
}
|
|
1135
1125
|
/**
|
|
1136
1126
|
* Direct the message to the correct subsystem for processing, and implement other side effects
|
|
1137
|
-
* @param message - The unpacked message. Likely a ContainerRuntimeMessage, but could also be a system op
|
|
1138
|
-
* @param local - Did this client send the op?
|
|
1139
|
-
* @param modernRuntimeMessage - Does this appear like a current ContainerRuntimeMessage?
|
|
1140
1127
|
*/
|
|
1141
|
-
processCore(
|
|
1128
|
+
processCore(messageWithContext) {
|
|
1129
|
+
const { message, local } = messageWithContext;
|
|
1142
1130
|
// Surround the actual processing of the operation with messages to the schedule manager indicating
|
|
1143
1131
|
// the beginning and end. This allows it to emit appropriate events and/or pause the processing of new
|
|
1144
1132
|
// messages once a batch has been fully processed.
|
|
@@ -1146,16 +1134,18 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1146
1134
|
this._processedClientSequenceNumber = message.clientSequenceNumber;
|
|
1147
1135
|
try {
|
|
1148
1136
|
let localOpMetadata;
|
|
1149
|
-
if (local &&
|
|
1150
|
-
|
|
1137
|
+
if (local &&
|
|
1138
|
+
messageWithContext.modernRuntimeMessage &&
|
|
1139
|
+
message.type !== ContainerMessageType.ChunkedOp) {
|
|
1140
|
+
localOpMetadata = this.pendingStateManager.processPendingLocalMessage(messageWithContext.message);
|
|
1151
1141
|
}
|
|
1152
1142
|
// If there are no more pending messages after processing a local message,
|
|
1153
1143
|
// the document is no longer dirty.
|
|
1154
1144
|
if (!this.hasPendingMessages()) {
|
|
1155
1145
|
this.updateDocumentDirtyState(false);
|
|
1156
1146
|
}
|
|
1157
|
-
this.validateAndProcessRuntimeMessage(
|
|
1158
|
-
this.emit("op", message, modernRuntimeMessage);
|
|
1147
|
+
this.validateAndProcessRuntimeMessage(messageWithContext, localOpMetadata);
|
|
1148
|
+
this.emit("op", message, messageWithContext.modernRuntimeMessage);
|
|
1159
1149
|
this.scheduleManager.afterOpProcessing(undefined, message);
|
|
1160
1150
|
if (local) {
|
|
1161
1151
|
// If we have processed a local op, this means that the container is
|
|
@@ -1170,29 +1160,29 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1170
1160
|
}
|
|
1171
1161
|
}
|
|
1172
1162
|
/**
|
|
1173
|
-
* Assuming the given message is also a
|
|
1163
|
+
* Assuming the given message is also a TypedContainerRuntimeMessage,
|
|
1174
1164
|
* checks its type and dispatches the message to the appropriate handler in the runtime.
|
|
1175
|
-
* Throws a DataProcessingError if the message doesn't conform to
|
|
1165
|
+
* Throws a DataProcessingError if the message looks like but doesn't conform to a known TypedContainerRuntimeMessage type.
|
|
1176
1166
|
*/
|
|
1177
|
-
validateAndProcessRuntimeMessage(
|
|
1178
|
-
//
|
|
1179
|
-
const {
|
|
1180
|
-
switch (
|
|
1167
|
+
validateAndProcessRuntimeMessage(messageWithContext, localOpMetadata) {
|
|
1168
|
+
// TODO: destructure message and modernRuntimeMessage once using typescript 5.2.2+
|
|
1169
|
+
const { local } = messageWithContext;
|
|
1170
|
+
switch (messageWithContext.message.type) {
|
|
1181
1171
|
case ContainerMessageType.Attach:
|
|
1182
|
-
this.dataStores.processAttachMessage(message, local);
|
|
1172
|
+
this.dataStores.processAttachMessage(messageWithContext.message, local);
|
|
1183
1173
|
break;
|
|
1184
1174
|
case ContainerMessageType.Alias:
|
|
1185
|
-
this.dataStores.processAliasMessage(message, localOpMetadata, local);
|
|
1175
|
+
this.dataStores.processAliasMessage(messageWithContext.message, localOpMetadata, local);
|
|
1186
1176
|
break;
|
|
1187
1177
|
case ContainerMessageType.FluidDataStoreOp:
|
|
1188
|
-
this.dataStores.processFluidDataStoreOp(message, local, localOpMetadata);
|
|
1178
|
+
this.dataStores.processFluidDataStoreOp(messageWithContext.message, local, localOpMetadata);
|
|
1189
1179
|
break;
|
|
1190
1180
|
case ContainerMessageType.BlobAttach:
|
|
1191
|
-
this.blobManager.processBlobAttachOp(message, local);
|
|
1181
|
+
this.blobManager.processBlobAttachOp(messageWithContext.message, local);
|
|
1192
1182
|
break;
|
|
1193
1183
|
case ContainerMessageType.IdAllocation:
|
|
1194
1184
|
assert(this.idCompressor !== undefined, 0x67c /* IdCompressor should be defined if enabled */);
|
|
1195
|
-
this.idCompressor.finalizeCreationRange(message.contents);
|
|
1185
|
+
this.idCompressor.finalizeCreationRange(messageWithContext.message.contents);
|
|
1196
1186
|
break;
|
|
1197
1187
|
case ContainerMessageType.ChunkedOp:
|
|
1198
1188
|
case ContainerMessageType.Rejoin:
|
|
@@ -1200,11 +1190,12 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1200
1190
|
default: {
|
|
1201
1191
|
// If we didn't necessarily expect a runtime message type, then no worries - just return
|
|
1202
1192
|
// e.g. this case applies to system ops, or legacy ops that would have fallen into the above cases anyway.
|
|
1203
|
-
if (!
|
|
1193
|
+
if (!messageWithContext.modernRuntimeMessage) {
|
|
1204
1194
|
return;
|
|
1205
1195
|
}
|
|
1206
|
-
const compatBehavior = compatDetails?.behavior;
|
|
1207
|
-
if (!compatBehaviorAllowsMessageType(
|
|
1196
|
+
const compatBehavior = messageWithContext.message.compatDetails?.behavior;
|
|
1197
|
+
if (!compatBehaviorAllowsMessageType(messageWithContext.message.type, compatBehavior)) {
|
|
1198
|
+
const { message } = messageWithContext;
|
|
1208
1199
|
const error = DataProcessingError.create(
|
|
1209
1200
|
// Former assert 0x3ce
|
|
1210
1201
|
"Runtime message of unknown type", "OpProcessing", message, {
|
|
@@ -1282,6 +1273,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1282
1273
|
* @param wait - True if you want to wait for it.
|
|
1283
1274
|
* @deprecated - Use getAliasedDataStoreEntryPoint instead to get an aliased data store's entry point.
|
|
1284
1275
|
*/
|
|
1276
|
+
// eslint-disable-next-line import/no-deprecated
|
|
1285
1277
|
async getRootDataStore(id, wait = true) {
|
|
1286
1278
|
return this.getRootDataStoreChannel(id, wait);
|
|
1287
1279
|
}
|
|
@@ -1346,8 +1338,8 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1346
1338
|
/**
|
|
1347
1339
|
* Returns the aliased data store's entryPoint, given the alias.
|
|
1348
1340
|
* @param alias - The alias for the data store.
|
|
1349
|
-
* @returns
|
|
1350
|
-
* data store has been assigned the given alias.
|
|
1341
|
+
* @returns The data store's entry point ({@link @fluidframework/core-interfaces#IFluidHandle}) if it exists and is aliased.
|
|
1342
|
+
* Returns undefined if no data store has been assigned the given alias.
|
|
1351
1343
|
*/
|
|
1352
1344
|
async getAliasedDataStoreEntryPoint(alias) {
|
|
1353
1345
|
await this.dataStores.waitIfPendingAlias(alias);
|
|
@@ -1586,7 +1578,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1586
1578
|
/**
|
|
1587
1579
|
* After GC has run and identified nodes that are sweep ready, this is called to delete the sweep ready nodes.
|
|
1588
1580
|
* @param sweepReadyRoutes - The routes of nodes that are sweep ready and should be deleted.
|
|
1589
|
-
* @returns
|
|
1581
|
+
* @returns The routes of nodes that were deleted.
|
|
1590
1582
|
*/
|
|
1591
1583
|
deleteSweepReadyNodes(sweepReadyRoutes) {
|
|
1592
1584
|
const { dataStoreRoutes, blobManagerRoutes } = this.getDataStoreAndBlobManagerRoutes(sweepReadyRoutes);
|
|
@@ -1648,7 +1640,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1648
1640
|
/**
|
|
1649
1641
|
* From a given list of routes, separate and return routes that belong to blob manager and data stores.
|
|
1650
1642
|
* @param routes - A list of routes that can belong to data stores or blob manager.
|
|
1651
|
-
* @returns
|
|
1643
|
+
* @returns Two route lists - One that contains routes for blob manager and another one that contains routes
|
|
1652
1644
|
* for data stores.
|
|
1653
1645
|
*/
|
|
1654
1646
|
getDataStoreAndBlobManagerRoutes(routes) {
|
|
@@ -1700,16 +1692,13 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1700
1692
|
},
|
|
1701
1693
|
});
|
|
1702
1694
|
assert(this.outbox.isEmpty, 0x3d1 /* Can't trigger summary in the middle of a batch */);
|
|
1695
|
+
// We close the summarizer and download a new snapshot and reload the container
|
|
1703
1696
|
let latestSnapshotVersionId;
|
|
1704
|
-
if (refreshLatestAck) {
|
|
1705
|
-
|
|
1697
|
+
if (refreshLatestAck === true) {
|
|
1698
|
+
return this.prefetchLatestSummaryThenClose(createChildLogger({
|
|
1706
1699
|
logger: summaryNumberLogger,
|
|
1707
1700
|
properties: { all: { safeSummary: true } },
|
|
1708
1701
|
}));
|
|
1709
|
-
const latestSnapshotRefSeq = latestSnapshotInfo.latestSnapshotRefSeq;
|
|
1710
|
-
latestSnapshotVersionId = latestSnapshotInfo.latestSnapshotVersionId;
|
|
1711
|
-
// We might need to catch up to the latest summary's reference sequence number before pausing.
|
|
1712
|
-
await this.waitForDeltaManagerToCatchup(latestSnapshotRefSeq, summaryNumberLogger);
|
|
1713
1702
|
}
|
|
1714
1703
|
// If there are pending (unacked ops), the summary will not be eventual consistent and it may even be
|
|
1715
1704
|
// incorrect. So, wait for the container to be saved with a timeout. If the container is not saved
|
|
@@ -2233,34 +2222,31 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
2233
2222
|
}
|
|
2234
2223
|
reSubmit(message) {
|
|
2235
2224
|
// Need to parse from string for back-compat
|
|
2236
|
-
const containerRuntimeMessage = this.
|
|
2225
|
+
const containerRuntimeMessage = this.parseLocalOpContent(message.content);
|
|
2237
2226
|
this.reSubmitCore(containerRuntimeMessage, message.localOpMetadata, message.opMetadata);
|
|
2238
2227
|
}
|
|
2239
2228
|
/**
|
|
2240
2229
|
* Finds the right store and asks it to resubmit the message. This typically happens when we
|
|
2241
2230
|
* reconnect and there are pending messages.
|
|
2242
|
-
* @param message - The original
|
|
2231
|
+
* @param message - The original LocalContainerRuntimeMessage.
|
|
2243
2232
|
* @param localOpMetadata - The local metadata associated with the original message.
|
|
2244
2233
|
*/
|
|
2245
2234
|
reSubmitCore(message, localOpMetadata, opMetadata) {
|
|
2246
|
-
const contents = message.contents;
|
|
2247
2235
|
switch (message.type) {
|
|
2248
2236
|
case ContainerMessageType.FluidDataStoreOp:
|
|
2249
2237
|
// For Operations, call resubmitDataStoreOp which will find the right store
|
|
2250
2238
|
// and trigger resubmission on it.
|
|
2251
|
-
this.dataStores.resubmitDataStoreOp(contents, localOpMetadata);
|
|
2239
|
+
this.dataStores.resubmitDataStoreOp(message.contents, localOpMetadata);
|
|
2252
2240
|
break;
|
|
2253
2241
|
case ContainerMessageType.Attach:
|
|
2254
2242
|
case ContainerMessageType.Alias:
|
|
2255
2243
|
this.submit(message, localOpMetadata);
|
|
2256
2244
|
break;
|
|
2257
|
-
case ContainerMessageType.IdAllocation:
|
|
2258
|
-
|
|
2259
|
-
if (contents.stashedState !== undefined) {
|
|
2260
|
-
delete contents.stashedState;
|
|
2261
|
-
}
|
|
2245
|
+
case ContainerMessageType.IdAllocation: {
|
|
2246
|
+
prepareLocalContainerRuntimeIdAllocationMessageForTransit(message);
|
|
2262
2247
|
this.submit(message, localOpMetadata);
|
|
2263
2248
|
break;
|
|
2249
|
+
}
|
|
2264
2250
|
case ContainerMessageType.ChunkedOp:
|
|
2265
2251
|
throw new Error(`chunkedOp not expected here`);
|
|
2266
2252
|
case ContainerMessageType.BlobAttach:
|
|
@@ -2294,7 +2280,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
2294
2280
|
}
|
|
2295
2281
|
rollback(content, localOpMetadata) {
|
|
2296
2282
|
// Need to parse from string for back-compat
|
|
2297
|
-
const { type, contents } = this.
|
|
2283
|
+
const { type, contents } = this.parseLocalOpContent(content);
|
|
2298
2284
|
switch (type) {
|
|
2299
2285
|
case ContainerMessageType.FluidDataStoreOp:
|
|
2300
2286
|
// For operations, call rollbackDataStoreOp which will find the right store
|
|
@@ -2306,17 +2292,6 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
2306
2292
|
throw new Error(`Can't rollback ${type}`);
|
|
2307
2293
|
}
|
|
2308
2294
|
}
|
|
2309
|
-
async waitForDeltaManagerToCatchup(latestSnapshotRefSeq, summaryLogger) {
|
|
2310
|
-
if (latestSnapshotRefSeq > this.deltaManager.lastSequenceNumber) {
|
|
2311
|
-
// We need to catch up to the latest summary's reference sequence number before proceeding.
|
|
2312
|
-
await PerformanceEvent.timedExecAsync(summaryLogger, {
|
|
2313
|
-
eventName: "WaitingForSeq",
|
|
2314
|
-
lastSequenceNumber: this.deltaManager.lastSequenceNumber,
|
|
2315
|
-
targetSequenceNumber: latestSnapshotRefSeq,
|
|
2316
|
-
lastKnownSeqNumber: this.deltaManager.lastKnownSeqNumber,
|
|
2317
|
-
}, async () => waitForSeq(this.deltaManager, latestSnapshotRefSeq), { start: true, end: true, cancel: "error" });
|
|
2318
|
-
}
|
|
2319
|
-
}
|
|
2320
2295
|
/** Implementation of ISummarizerInternalsProvider.refreshLatestSummaryAck */
|
|
2321
2296
|
async refreshLatestSummaryAck(options) {
|
|
2322
2297
|
const { proposalHandle, ackHandle, summaryRefSeq, summaryLogger } = options;
|
|
@@ -2362,18 +2337,25 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
2362
2337
|
await this.garbageCollector.refreshLatestSummary(result);
|
|
2363
2338
|
}
|
|
2364
2339
|
/**
|
|
2365
|
-
* Fetches the latest snapshot from storage
|
|
2366
|
-
*
|
|
2340
|
+
* Fetches the latest snapshot from storage to refresh the cache as a performance optimization and closes the
|
|
2341
|
+
* summarizer to reload from new state.
|
|
2367
2342
|
* @param summaryLogger - logger to use when fetching snapshot from storage
|
|
2368
|
-
* @returns
|
|
2343
|
+
* @returns a generic summarization error
|
|
2369
2344
|
*/
|
|
2370
|
-
async
|
|
2345
|
+
async prefetchLatestSummaryThenClose(summaryLogger) {
|
|
2371
2346
|
const readAndParseBlob = async (id) => readAndParse(this.storage, id);
|
|
2372
|
-
|
|
2347
|
+
// This is a performance optimization as the same parent is likely to be elected again, and would use its
|
|
2348
|
+
// cache to fetch the snapshot instead of the network.
|
|
2349
|
+
await this.fetchSnapshotFromStorage(summaryLogger, {
|
|
2373
2350
|
eventName: "RefreshLatestSummaryFromServerFetch",
|
|
2374
2351
|
}, readAndParseBlob, null);
|
|
2375
2352
|
await this.closeStaleSummarizer("RefreshLatestSummaryFromServerFetch");
|
|
2376
|
-
return {
|
|
2353
|
+
return {
|
|
2354
|
+
stage: "base",
|
|
2355
|
+
error: "summary state stale - Unsupported option 'refreshLatestAck'",
|
|
2356
|
+
referenceSequenceNumber: this.deltaManager.lastSequenceNumber,
|
|
2357
|
+
minimumSequenceNumber: this.deltaManager.minimumSequenceNumber,
|
|
2358
|
+
};
|
|
2377
2359
|
}
|
|
2378
2360
|
async closeStaleSummarizer(codePath) {
|
|
2379
2361
|
this.mc.logger.sendTelemetryEvent({
|
|
@@ -2396,7 +2378,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
2396
2378
|
return PerformanceEvent.timedExecAsync(logger, event, async (perfEvent) => {
|
|
2397
2379
|
const stats = {};
|
|
2398
2380
|
const trace = Trace.start();
|
|
2399
|
-
const versions = await this.storage.getVersions(versionId, 1, "
|
|
2381
|
+
const versions = await this.storage.getVersions(versionId, 1, "prefetchLatestSummaryBeforeClose", versionId === null ? FetchSource.noCache : undefined);
|
|
2400
2382
|
assert(!!versions && !!versions[0], 0x137 /* "Failed to get version from storage" */);
|
|
2401
2383
|
stats.getVersionDuration = trace.trace().duration;
|
|
2402
2384
|
const maybeSnapshot = await this.storage.getSnapshotTree(versions[0]);
|
|
@@ -2424,15 +2406,17 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
2424
2406
|
if (this._orderSequentiallyCalls !== 0) {
|
|
2425
2407
|
throw new UsageError("can't get state during orderSequentially");
|
|
2426
2408
|
}
|
|
2427
|
-
const pendingAttachmentBlobs = await this.blobManager.getPendingBlobs(waitBlobsToAttach);
|
|
2428
|
-
const pending = this.pendingStateManager.getLocalState();
|
|
2429
|
-
if (!pendingAttachmentBlobs && !this.hasPendingMessages()) {
|
|
2430
|
-
return; // no pending state to save
|
|
2431
|
-
}
|
|
2432
2409
|
// Flush pending batch.
|
|
2433
2410
|
// getPendingLocalState() is only exposed through Container.closeAndGetPendingLocalState(), so it's safe
|
|
2434
2411
|
// to close current batch.
|
|
2435
2412
|
this.flush();
|
|
2413
|
+
const pendingAttachmentBlobs = waitBlobsToAttach
|
|
2414
|
+
? await this.blobManager.attachAndGetPendingBlobs()
|
|
2415
|
+
: undefined;
|
|
2416
|
+
const pending = this.pendingStateManager.getLocalState();
|
|
2417
|
+
if (!pendingAttachmentBlobs && !this.hasPendingMessages()) {
|
|
2418
|
+
return; // no pending state to save
|
|
2419
|
+
}
|
|
2436
2420
|
const pendingState = {
|
|
2437
2421
|
pending,
|
|
2438
2422
|
pendingAttachmentBlobs,
|
|
@@ -2476,6 +2460,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
2476
2460
|
* * Forms a function that will request a Summarizer.
|
|
2477
2461
|
* @param loaderRouter - the loader acting as an IFluidRouter
|
|
2478
2462
|
* */
|
|
2463
|
+
// eslint-disable-next-line import/no-deprecated
|
|
2479
2464
|
formRequestSummarizerFn(loaderRouter) {
|
|
2480
2465
|
return async () => {
|
|
2481
2466
|
const request = {
|
|
@@ -2490,6 +2475,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
2490
2475
|
},
|
|
2491
2476
|
url: "/_summarizer",
|
|
2492
2477
|
};
|
|
2478
|
+
// eslint-disable-next-line import/no-deprecated
|
|
2493
2479
|
const fluidObject = await requestFluidObject(loaderRouter, request);
|
|
2494
2480
|
const summarizer = fluidObject.ISummarizer;
|
|
2495
2481
|
if (!summarizer) {
|
|
@@ -2514,26 +2500,4 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
2514
2500
|
return killSwitch !== true && this.runtimeOptions.enableGroupedBatching;
|
|
2515
2501
|
}
|
|
2516
2502
|
}
|
|
2517
|
-
/**
|
|
2518
|
-
* Wait for a specific sequence number. Promise should resolve when we reach that number,
|
|
2519
|
-
* or reject if closed.
|
|
2520
|
-
*/
|
|
2521
|
-
const waitForSeq = async (deltaManager, targetSeq) => new Promise((resolve, reject) => {
|
|
2522
|
-
// TODO: remove cast to any when actual event is determined
|
|
2523
|
-
deltaManager.on("closed", reject);
|
|
2524
|
-
deltaManager.on("disposed", reject);
|
|
2525
|
-
// If we already reached target sequence number, simply resolve the promise.
|
|
2526
|
-
if (deltaManager.lastSequenceNumber >= targetSeq) {
|
|
2527
|
-
resolve();
|
|
2528
|
-
}
|
|
2529
|
-
else {
|
|
2530
|
-
const handleOp = (message) => {
|
|
2531
|
-
if (message.sequenceNumber >= targetSeq) {
|
|
2532
|
-
resolve();
|
|
2533
|
-
deltaManager.off("op", handleOp);
|
|
2534
|
-
}
|
|
2535
|
-
};
|
|
2536
|
-
deltaManager.on("op", handleOp);
|
|
2537
|
-
}
|
|
2538
|
-
});
|
|
2539
2503
|
//# sourceMappingURL=containerRuntime.js.map
|