@fluidframework/container-runtime 2.0.0-dev.6.4.0.191515 → 2.0.0-dev.7.2.0.203917
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 +119 -0
- package/api-extractor.json +1 -1
- package/api-report/container-runtime.api.md +864 -0
- package/dist/blobManager.d.ts +4 -4
- package/dist/blobManager.d.ts.map +1 -1
- package/dist/blobManager.js +55 -71
- package/dist/blobManager.js.map +1 -1
- package/dist/connectionTelemetry.d.ts.map +1 -1
- package/dist/connectionTelemetry.js +75 -42
- package/dist/connectionTelemetry.js.map +1 -1
- package/dist/container-runtime-alpha.d.ts +1742 -0
- package/dist/container-runtime-beta.d.ts +1742 -0
- package/dist/container-runtime-public.d.ts +1742 -0
- package/dist/container-runtime-untrimmed.d.ts +1803 -0
- package/dist/containerHandleContext.js +3 -3
- package/dist/containerHandleContext.js.map +1 -1
- package/dist/containerRuntime.d.ts +88 -98
- package/dist/containerRuntime.d.ts.map +1 -1
- package/dist/containerRuntime.js +466 -453
- package/dist/containerRuntime.js.map +1 -1
- package/dist/dataStore.js +11 -11
- package/dist/dataStore.js.map +1 -1
- package/dist/dataStoreContext.d.ts +2 -4
- package/dist/dataStoreContext.d.ts.map +1 -1
- package/dist/dataStoreContext.js +60 -59
- package/dist/dataStoreContext.js.map +1 -1
- package/dist/dataStoreRegistry.d.ts +3 -0
- package/dist/dataStoreRegistry.d.ts.map +1 -1
- package/dist/dataStoreRegistry.js +6 -3
- package/dist/dataStoreRegistry.js.map +1 -1
- package/dist/dataStores.js +1 -1
- 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/error.d.ts.map +1 -1
- package/dist/error.js.map +1 -1
- package/dist/gc/garbageCollection.d.ts.map +1 -1
- package/dist/gc/garbageCollection.js +23 -24
- package/dist/gc/garbageCollection.js.map +1 -1
- package/dist/gc/gcConfigs.js +1 -1
- package/dist/gc/gcConfigs.js.map +1 -1
- package/dist/gc/gcDefinitions.d.ts +34 -24
- package/dist/gc/gcDefinitions.d.ts.map +1 -1
- package/dist/gc/gcDefinitions.js +10 -7
- package/dist/gc/gcDefinitions.js.map +1 -1
- package/dist/gc/gcSummaryDefinitions.d.ts +1 -1
- package/dist/gc/gcSummaryDefinitions.js.map +1 -1
- package/dist/gc/gcTelemetry.d.ts +2 -2
- package/dist/gc/gcTelemetry.d.ts.map +1 -1
- package/dist/gc/gcTelemetry.js +5 -5
- 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 +1 -1
- package/dist/gc/index.d.ts.map +1 -1
- package/dist/gc/index.js +3 -3
- 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/id-compressor/utilities.d.ts +3 -0
- package/dist/id-compressor/utilities.d.ts.map +1 -1
- package/dist/id-compressor/utilities.js +3 -0
- package/dist/id-compressor/utilities.js.map +1 -1
- package/dist/index.d.ts +5 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -2
- package/dist/index.js.map +1 -1
- package/dist/messageTypes.d.ts +137 -0
- package/dist/messageTypes.d.ts.map +1 -0
- package/dist/messageTypes.js +32 -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 +7 -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.map +1 -1
- package/dist/opLifecycle/opSplitter.js +3 -3
- package/dist/opLifecycle/opSplitter.js.map +1 -1
- package/dist/opLifecycle/outbox.d.ts.map +1 -1
- package/dist/opLifecycle/outbox.js +7 -2
- package/dist/opLifecycle/outbox.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 +36 -46
- package/dist/pendingStateManager.js.map +1 -1
- package/dist/scheduleManager.js +6 -2
- package/dist/scheduleManager.js.map +1 -1
- package/dist/summary/orderedClientElection.d.ts +7 -4
- 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.d.ts +5 -0
- package/dist/summary/runWhileConnectedCoordinator.d.ts.map +1 -1
- package/dist/summary/runWhileConnectedCoordinator.js +7 -6
- package/dist/summary/runWhileConnectedCoordinator.js.map +1 -1
- package/dist/summary/runningSummarizer.d.ts.map +1 -1
- package/dist/summary/runningSummarizer.js +40 -38
- package/dist/summary/runningSummarizer.js.map +1 -1
- package/dist/summary/summarizer.d.ts +2 -0
- package/dist/summary/summarizer.d.ts.map +1 -1
- package/dist/summary/summarizer.js +18 -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 +107 -22
- package/dist/summary/summarizerTypes.d.ts.map +1 -1
- package/dist/summary/summarizerTypes.js.map +1 -1
- package/dist/summary/summaryCollection.d.ts +18 -2
- package/dist/summary/summaryCollection.d.ts.map +1 -1
- package/dist/summary/summaryCollection.js +23 -21
- package/dist/summary/summaryCollection.js.map +1 -1
- package/dist/summary/summaryFormat.d.ts +15 -6
- package/dist/summary/summaryFormat.d.ts.map +1 -1
- 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/summaryGenerator.js.map +1 -1
- package/dist/summary/summaryManager.d.ts +2 -2
- package/dist/summary/summaryManager.d.ts.map +1 -1
- package/dist/summary/summaryManager.js +10 -10
- 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 -4
- package/lib/blobManager.d.ts.map +1 -1
- package/lib/blobManager.js +56 -72
- package/lib/blobManager.js.map +1 -1
- package/lib/connectionTelemetry.d.ts.map +1 -1
- package/lib/connectionTelemetry.js +76 -43
- package/lib/connectionTelemetry.js.map +1 -1
- package/lib/containerHandleContext.js +3 -3
- package/lib/containerHandleContext.js.map +1 -1
- package/lib/containerRuntime.d.ts +88 -98
- package/lib/containerRuntime.d.ts.map +1 -1
- package/lib/containerRuntime.js +424 -416
- package/lib/containerRuntime.js.map +1 -1
- package/lib/dataStore.js +11 -11
- package/lib/dataStore.js.map +1 -1
- package/lib/dataStoreContext.d.ts +2 -4
- package/lib/dataStoreContext.d.ts.map +1 -1
- package/lib/dataStoreContext.js +60 -59
- package/lib/dataStoreContext.js.map +1 -1
- package/lib/dataStoreRegistry.d.ts +3 -0
- package/lib/dataStoreRegistry.d.ts.map +1 -1
- package/lib/dataStoreRegistry.js +6 -3
- package/lib/dataStoreRegistry.js.map +1 -1
- package/lib/dataStores.js +1 -1
- 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/error.d.ts.map +1 -1
- package/lib/error.js.map +1 -1
- package/lib/gc/garbageCollection.d.ts.map +1 -1
- package/lib/gc/garbageCollection.js +23 -24
- package/lib/gc/garbageCollection.js.map +1 -1
- package/lib/gc/gcConfigs.js +2 -2
- package/lib/gc/gcConfigs.js.map +1 -1
- package/lib/gc/gcDefinitions.d.ts +34 -24
- package/lib/gc/gcDefinitions.d.ts.map +1 -1
- package/lib/gc/gcDefinitions.js +9 -6
- package/lib/gc/gcDefinitions.js.map +1 -1
- package/lib/gc/gcSummaryDefinitions.d.ts +1 -1
- package/lib/gc/gcSummaryDefinitions.js.map +1 -1
- package/lib/gc/gcTelemetry.d.ts +2 -2
- package/lib/gc/gcTelemetry.d.ts.map +1 -1
- package/lib/gc/gcTelemetry.js +5 -5
- 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 +1 -1
- 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/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/id-compressor/utilities.d.ts +3 -0
- package/lib/id-compressor/utilities.d.ts.map +1 -1
- package/lib/id-compressor/utilities.js +3 -0
- package/lib/id-compressor/utilities.js.map +1 -1
- package/lib/index.d.ts +5 -3
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +3 -1
- package/lib/index.js.map +1 -1
- package/lib/messageTypes.d.ts +137 -0
- package/lib/messageTypes.d.ts.map +1 -0
- package/lib/messageTypes.js +29 -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 +7 -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.map +1 -1
- package/lib/opLifecycle/opSplitter.js +1 -1
- package/lib/opLifecycle/opSplitter.js.map +1 -1
- package/lib/opLifecycle/outbox.d.ts.map +1 -1
- package/lib/opLifecycle/outbox.js +7 -2
- package/lib/opLifecycle/outbox.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 +35 -45
- package/lib/pendingStateManager.js.map +1 -1
- package/lib/scheduleManager.js +6 -2
- package/lib/scheduleManager.js.map +1 -1
- package/lib/summary/orderedClientElection.d.ts +7 -4
- 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.d.ts +5 -0
- package/lib/summary/runWhileConnectedCoordinator.d.ts.map +1 -1
- package/lib/summary/runWhileConnectedCoordinator.js +7 -6
- package/lib/summary/runWhileConnectedCoordinator.js.map +1 -1
- package/lib/summary/runningSummarizer.d.ts.map +1 -1
- package/lib/summary/runningSummarizer.js +40 -38
- package/lib/summary/runningSummarizer.js.map +1 -1
- package/lib/summary/summarizer.d.ts +2 -0
- package/lib/summary/summarizer.d.ts.map +1 -1
- package/lib/summary/summarizer.js +19 -9
- 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 +107 -22
- package/lib/summary/summarizerTypes.d.ts.map +1 -1
- package/lib/summary/summarizerTypes.js.map +1 -1
- package/lib/summary/summaryCollection.d.ts +18 -2
- package/lib/summary/summaryCollection.d.ts.map +1 -1
- package/lib/summary/summaryCollection.js +23 -21
- package/lib/summary/summaryCollection.js.map +1 -1
- package/lib/summary/summaryFormat.d.ts +15 -6
- package/lib/summary/summaryFormat.d.ts.map +1 -1
- 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/summaryGenerator.js.map +1 -1
- package/lib/summary/summaryManager.d.ts +2 -2
- package/lib/summary/summaryManager.d.ts.map +1 -1
- package/lib/summary/summaryManager.js +9 -9
- package/lib/summary/summaryManager.js.map +1 -1
- package/lib/throttler.js +16 -16
- package/lib/throttler.js.map +1 -1
- package/package.json +27 -28
- package/src/blobManager.ts +64 -77
- package/src/connectionTelemetry.ts +97 -52
- package/src/containerRuntime.ts +337 -341
- package/src/dataStore.ts +3 -3
- package/src/dataStoreContext.ts +7 -7
- package/src/dataStoreRegistry.ts +3 -0
- package/src/dataStores.ts +1 -1
- package/src/error.ts +4 -1
- package/src/gc/garbageCollection.ts +12 -11
- package/src/gc/gcConfigs.ts +3 -3
- package/src/gc/gcDefinitions.ts +35 -25
- package/src/gc/gcSummaryDefinitions.ts +1 -1
- package/src/gc/gcTelemetry.ts +6 -5
- package/src/gc/index.ts +2 -2
- package/src/id-compressor/utilities.ts +3 -0
- package/src/index.ts +21 -5
- package/src/messageTypes.ts +228 -0
- package/src/opLifecycle/README.md +93 -68
- package/src/opLifecycle/definitions.ts +5 -1
- package/src/opLifecycle/opDecompressor.ts +0 -8
- package/src/opLifecycle/opGroupingManager.ts +2 -4
- package/src/opLifecycle/opSplitter.ts +2 -2
- package/src/opLifecycle/outbox.ts +3 -0
- package/src/opLifecycle/remoteMessageProcessor.ts +54 -33
- package/src/packageVersion.ts +1 -1
- package/src/pendingStateManager.ts +31 -52
- package/src/scheduleManager.ts +2 -0
- package/src/summary/orderedClientElection.ts +4 -1
- package/src/summary/runWhileConnectedCoordinator.ts +5 -1
- package/src/summary/runningSummarizer.ts +3 -1
- package/src/summary/summarizer.ts +21 -7
- package/src/summary/summarizerNode/summarizerNode.ts +1 -1
- package/src/summary/summarizerTypes.ts +96 -11
- package/src/summary/summaryCollection.ts +19 -1
- package/src/summary/summaryFormat.ts +11 -1
- package/src/summary/summaryGenerator.ts +3 -3
- package/src/summary/summaryManager.ts +2 -2
- package/src/gc/gcEarlyAdoption.md +0 -145
package/lib/containerRuntime.js
CHANGED
|
@@ -8,7 +8,7 @@ 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, seqFromTree, calculateStats, TelemetryContext, responseToException, } from "@fluidframework/runtime-utils";
|
|
12
12
|
import { v4 as uuid } from "uuid";
|
|
13
13
|
import { ContainerFluidHandleContext } from "./containerHandleContext";
|
|
14
14
|
import { FluidDataStoreRegistry } from "./dataStoreRegistry";
|
|
@@ -25,32 +25,12 @@ import { BindBatchTracker } from "./batchTracker";
|
|
|
25
25
|
import { ScheduleManager } from "./scheduleManager";
|
|
26
26
|
import { OpCompressor, OpDecompressor, Outbox, OpSplitter, RemoteMessageProcessor, OpGroupingManager, getLongStack, } from "./opLifecycle";
|
|
27
27
|
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 = {}));
|
|
28
|
+
import { ContainerMessageType, } from "./messageTypes";
|
|
49
29
|
/**
|
|
50
30
|
* Utility to implement compat behaviors given an unknown message type
|
|
51
31
|
* The parameters are typed to support compile-time enforcement of handling all known types/behaviors
|
|
52
32
|
*
|
|
53
|
-
* @param _unknownContainerRuntimeMessageType - Typed as
|
|
33
|
+
* @param _unknownContainerRuntimeMessageType - Typed as something unexpected, to ensure all known types have been
|
|
54
34
|
* handled before calling this function (e.g. in a switch statement).
|
|
55
35
|
* @param compatBehavior - Typed redundantly with CompatModeBehavior to ensure handling is added when updating that type
|
|
56
36
|
*/
|
|
@@ -58,6 +38,15 @@ function compatBehaviorAllowsMessageType(_unknownContainerRuntimeMessageType, co
|
|
|
58
38
|
// undefined defaults to same behavior as "FailToProcess"
|
|
59
39
|
return compatBehavior === "Ignore";
|
|
60
40
|
}
|
|
41
|
+
function prepareLocalContainerRuntimeIdAllocationMessageForTransit(message) {
|
|
42
|
+
// Remove the stashedState from the op if it's a stashed op
|
|
43
|
+
if ("stashedState" in message.contents) {
|
|
44
|
+
delete message.contents.stashedState;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* @public
|
|
49
|
+
*/
|
|
61
50
|
export const DefaultSummaryConfiguration = {
|
|
62
51
|
state: "enabled",
|
|
63
52
|
minIdleTime: 0,
|
|
@@ -74,6 +63,7 @@ export const DefaultSummaryConfiguration = {
|
|
|
74
63
|
};
|
|
75
64
|
/**
|
|
76
65
|
* Accepted header keys for requests coming to the runtime.
|
|
66
|
+
* @public
|
|
77
67
|
*/
|
|
78
68
|
export var RuntimeHeaders;
|
|
79
69
|
(function (RuntimeHeaders) {
|
|
@@ -82,13 +72,24 @@ export var RuntimeHeaders;
|
|
|
82
72
|
/** True if the request is coming from an IFluidHandle. */
|
|
83
73
|
RuntimeHeaders["viaHandle"] = "viaHandle";
|
|
84
74
|
})(RuntimeHeaders || (RuntimeHeaders = {}));
|
|
85
|
-
/** True if a tombstoned object should be returned without erroring
|
|
75
|
+
/** True if a tombstoned object should be returned without erroring
|
|
76
|
+
* @public
|
|
77
|
+
*/
|
|
86
78
|
export const AllowTombstoneRequestHeaderKey = "allowTombstone"; // Belongs in the enum above, but avoiding the breaking change
|
|
87
|
-
/**
|
|
79
|
+
/**
|
|
80
|
+
* [IRRELEVANT IF throwOnInactiveLoad OPTION NOT SET] True if an inactive object should be returned without erroring
|
|
81
|
+
* @public
|
|
82
|
+
*/
|
|
88
83
|
export const AllowInactiveRequestHeaderKey = "allowInactive"; // Belongs in the enum above, but avoiding the breaking change
|
|
89
|
-
/**
|
|
84
|
+
/**
|
|
85
|
+
* Tombstone error responses will have this header set to true
|
|
86
|
+
* @public
|
|
87
|
+
*/
|
|
90
88
|
export const TombstoneResponseHeaderKey = "isTombstoned";
|
|
91
|
-
/**
|
|
89
|
+
/**
|
|
90
|
+
* Inactive error responses will have this header set to true
|
|
91
|
+
* @public
|
|
92
|
+
*/
|
|
92
93
|
export const InactiveResponseHeaderKey = "isInactive";
|
|
93
94
|
/** Default values for Runtime Headers */
|
|
94
95
|
export const defaultRuntimeHeaderData = {
|
|
@@ -98,6 +99,7 @@ export const defaultRuntimeHeaderData = {
|
|
|
98
99
|
};
|
|
99
100
|
/**
|
|
100
101
|
* Available compression algorithms for op compression.
|
|
102
|
+
* @public
|
|
101
103
|
*/
|
|
102
104
|
export var CompressionAlgorithms;
|
|
103
105
|
(function (CompressionAlgorithms) {
|
|
@@ -127,7 +129,8 @@ export const defaultPendingOpsRetryDelayMs = 1000;
|
|
|
127
129
|
*/
|
|
128
130
|
const defaultCloseSummarizerDelayMs = 5000; // 5 seconds
|
|
129
131
|
/**
|
|
130
|
-
* @deprecated
|
|
132
|
+
* @deprecated use ContainerRuntimeMessageType instead
|
|
133
|
+
* @public
|
|
131
134
|
*/
|
|
132
135
|
export var RuntimeMessage;
|
|
133
136
|
(function (RuntimeMessage) {
|
|
@@ -140,7 +143,8 @@ export var RuntimeMessage;
|
|
|
140
143
|
RuntimeMessage["Operation"] = "op";
|
|
141
144
|
})(RuntimeMessage || (RuntimeMessage = {}));
|
|
142
145
|
/**
|
|
143
|
-
* @deprecated
|
|
146
|
+
* @deprecated please use version in driver-utils
|
|
147
|
+
* @public
|
|
144
148
|
*/
|
|
145
149
|
export function isRuntimeMessage(message) {
|
|
146
150
|
return Object.values(RuntimeMessage).includes(message.type);
|
|
@@ -149,6 +153,7 @@ export function isRuntimeMessage(message) {
|
|
|
149
153
|
* Legacy ID for the built-in AgentScheduler. To minimize disruption while removing it, retaining this as a
|
|
150
154
|
* special-case for document dirty state. Ultimately we should have no special-cases from the
|
|
151
155
|
* ContainerRuntime's perspective.
|
|
156
|
+
* @public
|
|
152
157
|
*/
|
|
153
158
|
export const agentSchedulerId = "_scheduler";
|
|
154
159
|
// safely check navigator and get the hardware spec value
|
|
@@ -178,20 +183,276 @@ export const makeLegacySendBatchFn = (submitFn, deltaManager) => (batch) => {
|
|
|
178
183
|
}
|
|
179
184
|
deltaManager.flush();
|
|
180
185
|
};
|
|
186
|
+
const summarizerRequestUrl = "_summarizer";
|
|
187
|
+
/**
|
|
188
|
+
* Create and retrieve the summmarizer
|
|
189
|
+
*/
|
|
190
|
+
async function createSummarizer(loader, url) {
|
|
191
|
+
const request = {
|
|
192
|
+
headers: {
|
|
193
|
+
[LoaderHeader.cache]: false,
|
|
194
|
+
[LoaderHeader.clientDetails]: {
|
|
195
|
+
capabilities: { interactive: false },
|
|
196
|
+
type: summarizerClientType,
|
|
197
|
+
},
|
|
198
|
+
[DriverHeader.summarizingClient]: true,
|
|
199
|
+
[LoaderHeader.reconnect]: false,
|
|
200
|
+
},
|
|
201
|
+
url,
|
|
202
|
+
};
|
|
203
|
+
const resolvedContainer = await loader.resolve(request);
|
|
204
|
+
let fluidObject;
|
|
205
|
+
// Older containers may not have the "getEntryPoint" API
|
|
206
|
+
// ! This check will need to stay until LTS of loader moves past 2.0.0-internal.7.0.0
|
|
207
|
+
if (resolvedContainer.getEntryPoint !== undefined) {
|
|
208
|
+
fluidObject = await resolvedContainer.getEntryPoint();
|
|
209
|
+
}
|
|
210
|
+
else {
|
|
211
|
+
const response = await resolvedContainer.request({ url: `/${summarizerRequestUrl}` });
|
|
212
|
+
if (response.status !== 200 || response.mimeType !== "fluid/object") {
|
|
213
|
+
throw responseToException(response, request);
|
|
214
|
+
}
|
|
215
|
+
fluidObject = response.value;
|
|
216
|
+
}
|
|
217
|
+
if (fluidObject?.ISummarizer === undefined) {
|
|
218
|
+
throw new UsageError("Fluid object does not implement ISummarizer");
|
|
219
|
+
}
|
|
220
|
+
return fluidObject.ISummarizer;
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* This function is not supported publicly and exists for e2e testing
|
|
224
|
+
* @internal
|
|
225
|
+
*/
|
|
226
|
+
export async function TEST_requestSummarizer(loader, url) {
|
|
227
|
+
return createSummarizer(loader, url);
|
|
228
|
+
}
|
|
181
229
|
/**
|
|
182
230
|
* Represents the runtime of the container. Contains helper functions/state of the container.
|
|
183
231
|
* It will define the store level mappings.
|
|
232
|
+
* @public
|
|
184
233
|
*/
|
|
185
234
|
export class ContainerRuntime extends TypedEventEmitter {
|
|
235
|
+
/**
|
|
236
|
+
* @deprecated Will be removed in future major release. Migrate all usage of IFluidRouter to the "entryPoint" pattern. Refer to Removing-IFluidRouter.md
|
|
237
|
+
*/
|
|
238
|
+
get IFluidRouter() {
|
|
239
|
+
return this;
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* @deprecated use loadRuntime instead.
|
|
243
|
+
* Load the stores from a snapshot and returns the runtime.
|
|
244
|
+
* @param context - Context of the container.
|
|
245
|
+
* @param registryEntries - Mapping to the stores.
|
|
246
|
+
* @param requestHandler - Request handlers for the container runtime
|
|
247
|
+
* @param runtimeOptions - Additional options to be passed to the runtime
|
|
248
|
+
* @param existing - (optional) When loading from an existing snapshot. Precedes context.existing if provided
|
|
249
|
+
* @param containerRuntimeCtor - (optional) Constructor to use to create the ContainerRuntime instance. This
|
|
250
|
+
* allows mixin classes to leverage this method to define their own async initializer.
|
|
251
|
+
*/
|
|
252
|
+
static async load(context, registryEntries, requestHandler, runtimeOptions = {}, containerScope = context.scope, existing, containerRuntimeCtor = ContainerRuntime) {
|
|
253
|
+
let existingFlag = true;
|
|
254
|
+
if (!existing) {
|
|
255
|
+
existingFlag = false;
|
|
256
|
+
}
|
|
257
|
+
return this.loadRuntime({
|
|
258
|
+
context,
|
|
259
|
+
registryEntries,
|
|
260
|
+
existing: existingFlag,
|
|
261
|
+
runtimeOptions,
|
|
262
|
+
containerScope,
|
|
263
|
+
containerRuntimeCtor,
|
|
264
|
+
requestHandler,
|
|
265
|
+
provideEntryPoint: () => {
|
|
266
|
+
throw new UsageError("ContainerRuntime.load is deprecated and should no longer be used");
|
|
267
|
+
},
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
/**
|
|
271
|
+
* Load the stores from a snapshot and returns the runtime.
|
|
272
|
+
* @param params - An object housing the runtime properties:
|
|
273
|
+
* - context - Context of the container.
|
|
274
|
+
* - registryEntries - Mapping from data store types to their corresponding factories.
|
|
275
|
+
* - existing - Pass 'true' if loading from an existing snapshot.
|
|
276
|
+
* - requestHandler - (optional) Request handler for the request() method of the container runtime.
|
|
277
|
+
* Only relevant for back-compat while we remove the request() method and move fully to entryPoint as the main pattern.
|
|
278
|
+
* - runtimeOptions - Additional options to be passed to the runtime
|
|
279
|
+
* - containerScope - runtime services provided with context
|
|
280
|
+
* - containerRuntimeCtor - Constructor to use to create the ContainerRuntime instance.
|
|
281
|
+
* This allows mixin classes to leverage this method to define their own async initializer.
|
|
282
|
+
* - provideEntryPoint - Promise that resolves to an object which will act as entryPoint for the Container.
|
|
283
|
+
* This object should provide all the functionality that the Container is expected to provide to the loader layer.
|
|
284
|
+
*/
|
|
285
|
+
static async loadRuntime(params) {
|
|
286
|
+
const { context, registryEntries, existing, requestHandler, provideEntryPoint, runtimeOptions = {}, containerScope = {}, containerRuntimeCtor = ContainerRuntime, } = params;
|
|
287
|
+
// If taggedLogger exists, use it. Otherwise, wrap the vanilla logger:
|
|
288
|
+
// back-compat: Remove the TaggedLoggerAdapter fallback once all the host are using loader > 0.45
|
|
289
|
+
const backCompatContext = context;
|
|
290
|
+
const passLogger = backCompatContext.taggedLogger ??
|
|
291
|
+
// eslint-disable-next-line import/no-deprecated
|
|
292
|
+
new TaggedLoggerAdapter(backCompatContext.logger);
|
|
293
|
+
const logger = createChildLogger({
|
|
294
|
+
logger: passLogger,
|
|
295
|
+
properties: {
|
|
296
|
+
all: {
|
|
297
|
+
runtimeVersion: pkgVersion,
|
|
298
|
+
},
|
|
299
|
+
},
|
|
300
|
+
});
|
|
301
|
+
const { summaryOptions = {}, gcOptions = {}, loadSequenceNumberVerification = "close", flushMode = defaultFlushMode, compressionOptions = defaultCompressionConfig, maxBatchSizeInBytes = defaultMaxBatchSizeInBytes, enableRuntimeIdCompressor = false, chunkSizeInBytes = defaultChunkSizeInBytes, enableOpReentryCheck = false, enableGroupedBatching = false, } = runtimeOptions;
|
|
302
|
+
const registry = new FluidDataStoreRegistry(registryEntries);
|
|
303
|
+
const tryFetchBlob = async (blobName) => {
|
|
304
|
+
const blobId = context.baseSnapshot?.blobs[blobName];
|
|
305
|
+
if (context.baseSnapshot && blobId) {
|
|
306
|
+
// IContainerContext storage api return type still has undefined in 0.39 package version.
|
|
307
|
+
// So once we release 0.40 container-defn package we can remove this check.
|
|
308
|
+
assert(context.storage !== undefined, 0x1f5 /* "Attached state should have storage" */);
|
|
309
|
+
return readAndParse(context.storage, blobId);
|
|
310
|
+
}
|
|
311
|
+
};
|
|
312
|
+
const [chunks, metadata, electedSummarizerData, aliases, serializedIdCompressor] = await Promise.all([
|
|
313
|
+
tryFetchBlob(chunksBlobName),
|
|
314
|
+
tryFetchBlob(metadataBlobName),
|
|
315
|
+
tryFetchBlob(electedSummarizerBlobName),
|
|
316
|
+
tryFetchBlob(aliasBlobName),
|
|
317
|
+
tryFetchBlob(idCompressorBlobName),
|
|
318
|
+
]);
|
|
319
|
+
// read snapshot blobs needed for BlobManager to load
|
|
320
|
+
const blobManagerSnapshot = await BlobManager.load(context.baseSnapshot?.trees[blobsTreeName], async (id) => {
|
|
321
|
+
// IContainerContext storage api return type still has undefined in 0.39 package version.
|
|
322
|
+
// So once we release 0.40 container-defn package we can remove this check.
|
|
323
|
+
assert(context.storage !== undefined, 0x256 /* "storage undefined in attached container" */);
|
|
324
|
+
return readAndParse(context.storage, id);
|
|
325
|
+
});
|
|
326
|
+
// Verify summary runtime sequence number matches protocol sequence number.
|
|
327
|
+
const runtimeSequenceNumber = metadata?.message?.sequenceNumber;
|
|
328
|
+
// When we load with pending state, we reuse an old snapshot so we don't expect these numbers to match
|
|
329
|
+
if (!context.pendingLocalState && runtimeSequenceNumber !== undefined) {
|
|
330
|
+
const protocolSequenceNumber = context.deltaManager.initialSequenceNumber;
|
|
331
|
+
// Unless bypass is explicitly set, then take action when sequence numbers mismatch.
|
|
332
|
+
if (loadSequenceNumberVerification !== "bypass" &&
|
|
333
|
+
runtimeSequenceNumber !== protocolSequenceNumber) {
|
|
334
|
+
// "Load from summary, runtime metadata sequenceNumber !== initialSequenceNumber"
|
|
335
|
+
const error = new DataCorruptionError(
|
|
336
|
+
// pre-0.58 error message: SummaryMetadataMismatch
|
|
337
|
+
"Summary metadata mismatch", { runtimeVersion: pkgVersion, runtimeSequenceNumber, protocolSequenceNumber });
|
|
338
|
+
if (loadSequenceNumberVerification === "log") {
|
|
339
|
+
logger.sendErrorEvent({ eventName: "SequenceNumberMismatch" }, error);
|
|
340
|
+
}
|
|
341
|
+
else {
|
|
342
|
+
context.closeFn(error);
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
const idCompressorEnabled = metadata?.idCompressorEnabled ?? runtimeOptions.enableRuntimeIdCompressor ?? false;
|
|
347
|
+
let idCompressor;
|
|
348
|
+
if (idCompressorEnabled) {
|
|
349
|
+
const { IdCompressor, createSessionId } = await import("./id-compressor");
|
|
350
|
+
idCompressor =
|
|
351
|
+
serializedIdCompressor !== undefined
|
|
352
|
+
? IdCompressor.deserialize(serializedIdCompressor, createSessionId())
|
|
353
|
+
: IdCompressor.create(logger);
|
|
354
|
+
}
|
|
355
|
+
const runtime = new containerRuntimeCtor(context, registry, metadata, electedSummarizerData, chunks ?? [], aliases ?? [], {
|
|
356
|
+
summaryOptions,
|
|
357
|
+
gcOptions,
|
|
358
|
+
loadSequenceNumberVerification,
|
|
359
|
+
flushMode,
|
|
360
|
+
compressionOptions,
|
|
361
|
+
maxBatchSizeInBytes,
|
|
362
|
+
chunkSizeInBytes,
|
|
363
|
+
enableRuntimeIdCompressor,
|
|
364
|
+
enableOpReentryCheck,
|
|
365
|
+
enableGroupedBatching,
|
|
366
|
+
}, containerScope, logger, existing, blobManagerSnapshot, context.storage, idCompressor, provideEntryPoint, requestHandler, undefined);
|
|
367
|
+
// Apply stashed ops with a reference sequence number equal to the sequence number of the snapshot,
|
|
368
|
+
// or zero. This must be done before Container replays saved ops.
|
|
369
|
+
await runtime.pendingStateManager.applyStashedOpsAt(runtimeSequenceNumber ?? 0);
|
|
370
|
+
// Initialize the base state of the runtime before it's returned.
|
|
371
|
+
await runtime.initializeBaseState();
|
|
372
|
+
return runtime;
|
|
373
|
+
}
|
|
374
|
+
get clientId() {
|
|
375
|
+
return this._getClientId();
|
|
376
|
+
}
|
|
377
|
+
get storage() {
|
|
378
|
+
return this._storage;
|
|
379
|
+
}
|
|
380
|
+
get flushMode() {
|
|
381
|
+
return this._flushMode;
|
|
382
|
+
}
|
|
383
|
+
get scope() {
|
|
384
|
+
return this.containerScope;
|
|
385
|
+
}
|
|
386
|
+
get IFluidDataStoreRegistry() {
|
|
387
|
+
return this.registry;
|
|
388
|
+
}
|
|
389
|
+
get attachState() {
|
|
390
|
+
return this._getAttachState();
|
|
391
|
+
}
|
|
392
|
+
get IFluidHandleContext() {
|
|
393
|
+
return this.handleContext;
|
|
394
|
+
}
|
|
395
|
+
/**
|
|
396
|
+
* Invokes the given callback and expects that no ops are submitted
|
|
397
|
+
* until execution finishes. If an op is submitted, an error will be raised.
|
|
398
|
+
*
|
|
399
|
+
* Can be disabled by feature gate `Fluid.ContainerRuntime.DisableOpReentryCheck`
|
|
400
|
+
*
|
|
401
|
+
* @param callback - the callback to be invoked
|
|
402
|
+
*/
|
|
403
|
+
ensureNoDataModelChanges(callback) {
|
|
404
|
+
this.ensureNoDataModelChangesCalls++;
|
|
405
|
+
try {
|
|
406
|
+
return callback();
|
|
407
|
+
}
|
|
408
|
+
finally {
|
|
409
|
+
this.ensureNoDataModelChangesCalls--;
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
get connected() {
|
|
413
|
+
return this._connected;
|
|
414
|
+
}
|
|
415
|
+
/** clientId of parent (non-summarizing) container that owns summarizer container */
|
|
416
|
+
get summarizerClientId() {
|
|
417
|
+
return this.summarizerClientElection?.electedClientId;
|
|
418
|
+
}
|
|
419
|
+
get disposed() {
|
|
420
|
+
return this._disposed;
|
|
421
|
+
}
|
|
422
|
+
get summarizer() {
|
|
423
|
+
assert(this._summarizer !== undefined, 0x257 /* "This is not summarizing container" */);
|
|
424
|
+
return this._summarizer;
|
|
425
|
+
}
|
|
426
|
+
isSummariesDisabled() {
|
|
427
|
+
return this.summaryConfiguration.state === "disabled";
|
|
428
|
+
}
|
|
429
|
+
isHeuristicsDisabled() {
|
|
430
|
+
return this.summaryConfiguration.state === "disableHeuristics";
|
|
431
|
+
}
|
|
432
|
+
getMaxOpsSinceLastSummary() {
|
|
433
|
+
return this.summaryConfiguration.state !== "disabled"
|
|
434
|
+
? this.summaryConfiguration.maxOpsSinceLastSummary
|
|
435
|
+
: 0;
|
|
436
|
+
}
|
|
437
|
+
getInitialSummarizerDelayMs() {
|
|
438
|
+
// back-compat: initialSummarizerDelayMs was moved from ISummaryRuntimeOptions
|
|
439
|
+
// to ISummaryConfiguration in 0.60.
|
|
440
|
+
if (this.runtimeOptions.summaryOptions.initialSummarizerDelayMs !== undefined) {
|
|
441
|
+
return this.runtimeOptions.summaryOptions.initialSummarizerDelayMs;
|
|
442
|
+
}
|
|
443
|
+
return this.summaryConfiguration.state !== "disabled"
|
|
444
|
+
? this.summaryConfiguration.initialSummarizerDelayMs
|
|
445
|
+
: 0;
|
|
446
|
+
}
|
|
186
447
|
/**
|
|
187
448
|
* @internal
|
|
188
449
|
*/
|
|
189
|
-
constructor(context, registry, metadata, electedSummarizerData, chunks, dataStoreAliasMap, runtimeOptions, containerScope, logger, existing, blobManagerSnapshot, _storage, idCompressor, requestHandler, summaryConfiguration = {
|
|
450
|
+
constructor(context, registry, metadata, electedSummarizerData, chunks, dataStoreAliasMap, runtimeOptions, containerScope, logger, existing, blobManagerSnapshot, _storage, idCompressor, provideEntryPoint, requestHandler, summaryConfiguration = {
|
|
190
451
|
// the defaults
|
|
191
452
|
...DefaultSummaryConfiguration,
|
|
192
453
|
// the runtime configuration overrides
|
|
193
454
|
...runtimeOptions.summaryOptions?.summaryConfigOverrides,
|
|
194
|
-
}
|
|
455
|
+
}) {
|
|
195
456
|
super();
|
|
196
457
|
this.registry = registry;
|
|
197
458
|
this.runtimeOptions = runtimeOptions;
|
|
@@ -483,7 +744,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
483
744
|
this.summaryCollection.on("default", defaultAction);
|
|
484
745
|
// Create the SummaryManager and mark the initial state
|
|
485
746
|
this.summaryManager = new SummaryManager(this.summarizerClientElection, this, // IConnectedState
|
|
486
|
-
this.summaryCollection, this.logger, this.
|
|
747
|
+
this.summaryCollection, this.logger, this.formCreateSummarizerFn(loader), new Throttler(60 * 1000, // 60 sec delay window
|
|
487
748
|
30 * 1000, // 30 sec max delay
|
|
488
749
|
// throttling function increases exponentially (0ms, 40ms, 80ms, 160ms, etc)
|
|
489
750
|
formExponentialFn({ coefficient: 20, initialDelay: 0 })), {
|
|
@@ -495,26 +756,6 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
495
756
|
this.summaryManager.start();
|
|
496
757
|
}
|
|
497
758
|
}
|
|
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
759
|
// logging hardware telemetry
|
|
519
760
|
logger.sendTelemetryEvent({
|
|
520
761
|
eventName: "DeviceSpec",
|
|
@@ -548,234 +789,9 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
548
789
|
assert(this._summarizer !== undefined, 0x5bf /* Summarizer object is undefined in a summarizer client */);
|
|
549
790
|
return this._summarizer;
|
|
550
791
|
}
|
|
551
|
-
return
|
|
792
|
+
return provideEntryPoint(this);
|
|
552
793
|
});
|
|
553
794
|
}
|
|
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);
|
|
650
|
-
});
|
|
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
|
-
}
|
|
779
795
|
/**
|
|
780
796
|
* Initializes the state from the base snapshot this container runtime loaded from.
|
|
781
797
|
*/
|
|
@@ -806,13 +822,13 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
806
822
|
/**
|
|
807
823
|
* Notifies this object about the request made to the container.
|
|
808
824
|
* @param request - Request made to the handler.
|
|
809
|
-
* @deprecated
|
|
825
|
+
* @deprecated Will be removed in future major release. Migrate all usage of IFluidRouter to the "entryPoint" pattern. Refer to Removing-IFluidRouter.md
|
|
810
826
|
*/
|
|
811
827
|
async request(request) {
|
|
812
828
|
try {
|
|
813
829
|
const parser = RequestParser.create(request);
|
|
814
830
|
const id = parser.pathParts[0];
|
|
815
|
-
if (id ===
|
|
831
|
+
if (id === summarizerRequestUrl && parser.pathParts.length === 1) {
|
|
816
832
|
if (this._summarizer !== undefined) {
|
|
817
833
|
return {
|
|
818
834
|
status: 200,
|
|
@@ -823,6 +839,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
823
839
|
return create404Response(request);
|
|
824
840
|
}
|
|
825
841
|
if (this.requestHandler !== undefined) {
|
|
842
|
+
// eslint-disable-next-line @typescript-eslint/return-await -- Adding an await here causes test failures
|
|
826
843
|
return this.requestHandler(parser, this);
|
|
827
844
|
}
|
|
828
845
|
return create404Response(request);
|
|
@@ -840,6 +857,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
840
857
|
const requestParser = RequestParser.create(request);
|
|
841
858
|
const id = requestParser.pathParts[0];
|
|
842
859
|
if (id === "_channels") {
|
|
860
|
+
// eslint-disable-next-line @typescript-eslint/return-await -- Adding an await here causes test failures
|
|
843
861
|
return this.resolveHandle(requestParser.createSubRequest(1));
|
|
844
862
|
}
|
|
845
863
|
if (id === BlobManager.basePath && requestParser.isLeaf(2)) {
|
|
@@ -853,11 +871,14 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
853
871
|
: create404Response(request);
|
|
854
872
|
}
|
|
855
873
|
else if (requestParser.pathParts.length > 0) {
|
|
856
|
-
|
|
874
|
+
// Differentiate between requesting the dataStore directly, or one of its children
|
|
875
|
+
const requestForChild = !requestParser.isLeaf(1);
|
|
876
|
+
const dataStore = await this.getDataStoreFromRequest(id, request, requestForChild);
|
|
857
877
|
const subRequest = requestParser.createSubRequest(1);
|
|
858
878
|
// We always expect createSubRequest to include a leading slash, but asserting here to protect against
|
|
859
879
|
// unintentionally modifying the url if that changes.
|
|
860
880
|
assert(subRequest.url.startsWith("/"), 0x126 /* "Expected createSubRequest url to include a leading slash" */);
|
|
881
|
+
// eslint-disable-next-line @typescript-eslint/return-await -- Adding an await here causes test failures
|
|
861
882
|
return dataStore.request(subRequest);
|
|
862
883
|
}
|
|
863
884
|
return create404Response(request);
|
|
@@ -875,7 +896,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
875
896
|
internalId(maybeAlias) {
|
|
876
897
|
return this.dataStores.aliases.get(maybeAlias) ?? maybeAlias;
|
|
877
898
|
}
|
|
878
|
-
async getDataStoreFromRequest(id, request) {
|
|
899
|
+
async getDataStoreFromRequest(id, request, requestForChild) {
|
|
879
900
|
const headerData = {};
|
|
880
901
|
if (typeof request.headers?.[RuntimeHeaders.wait] === "boolean") {
|
|
881
902
|
headerData.wait = request.headers[RuntimeHeaders.wait];
|
|
@@ -886,6 +907,10 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
886
907
|
if (typeof request.headers?.[AllowTombstoneRequestHeaderKey] === "boolean") {
|
|
887
908
|
headerData.allowTombstone = request.headers[AllowTombstoneRequestHeaderKey];
|
|
888
909
|
}
|
|
910
|
+
// We allow Tombstone requests for sub-DataStore objects
|
|
911
|
+
if (requestForChild) {
|
|
912
|
+
headerData.allowTombstone = true;
|
|
913
|
+
}
|
|
889
914
|
await this.dataStores.waitIfPendingAlias(id);
|
|
890
915
|
const internalId = this.internalId(id);
|
|
891
916
|
const dataStoreContext = await this.dataStores.getDataStore(internalId, headerData);
|
|
@@ -1024,23 +1049,24 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1024
1049
|
* Parse an op's type and actual content from given serialized content
|
|
1025
1050
|
* ! Note: this format needs to be in-line with what is set in the "ContainerRuntime.submit(...)" method
|
|
1026
1051
|
*/
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1052
|
+
// TODO: markfields: confirm Local- versus Outbound- ContainerRuntimeMessage typing
|
|
1053
|
+
parseLocalOpContent(serializedContents) {
|
|
1054
|
+
assert(serializedContents !== undefined, 0x6d5 /* content must be defined */);
|
|
1055
|
+
const message = JSON.parse(serializedContents);
|
|
1056
|
+
assert(message.type !== undefined, 0x6d6 /* incorrect op content format */);
|
|
1057
|
+
return message;
|
|
1058
|
+
}
|
|
1059
|
+
async applyStashedOp(serializedOpContent) {
|
|
1034
1060
|
// Need to parse from string for back-compat
|
|
1035
|
-
const
|
|
1036
|
-
switch (type) {
|
|
1061
|
+
const opContents = this.parseLocalOpContent(serializedOpContent);
|
|
1062
|
+
switch (opContents.type) {
|
|
1037
1063
|
case ContainerMessageType.FluidDataStoreOp:
|
|
1038
|
-
return this.dataStores.applyStashedOp(contents);
|
|
1064
|
+
return this.dataStores.applyStashedOp(opContents.contents);
|
|
1039
1065
|
case ContainerMessageType.Attach:
|
|
1040
|
-
return this.dataStores.applyStashedAttachOp(contents);
|
|
1066
|
+
return this.dataStores.applyStashedAttachOp(opContents.contents);
|
|
1041
1067
|
case ContainerMessageType.IdAllocation:
|
|
1042
1068
|
assert(this.idCompressor !== undefined, 0x67b /* IdCompressor should be defined if enabled */);
|
|
1043
|
-
return this.applyStashedIdAllocationOp(contents);
|
|
1069
|
+
return this.applyStashedIdAllocationOp(opContents.contents);
|
|
1044
1070
|
case ContainerMessageType.Alias:
|
|
1045
1071
|
case ContainerMessageType.BlobAttach:
|
|
1046
1072
|
return;
|
|
@@ -1052,11 +1078,11 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1052
1078
|
// This should be extremely rare for stashed ops.
|
|
1053
1079
|
// It would require a newer runtime stashing ops and then an older one applying them,
|
|
1054
1080
|
// e.g. if an app rolled back its container version
|
|
1055
|
-
const compatBehavior = compatDetails?.behavior;
|
|
1056
|
-
if (!compatBehaviorAllowsMessageType(type, compatBehavior)) {
|
|
1081
|
+
const compatBehavior = opContents.compatDetails?.behavior;
|
|
1082
|
+
if (!compatBehaviorAllowsMessageType(opContents.type, compatBehavior)) {
|
|
1057
1083
|
const error = DataProcessingError.create("Stashed runtime message of unknown type", "applyStashedOp", undefined /* sequencedMessage */, {
|
|
1058
1084
|
messageDetails: JSON.stringify({
|
|
1059
|
-
type,
|
|
1085
|
+
type: opContents.type,
|
|
1060
1086
|
compatBehavior,
|
|
1061
1087
|
}),
|
|
1062
1088
|
});
|
|
@@ -1075,6 +1101,23 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1075
1101
|
// Don't propagate "disconnected" event because we didn't propagate the previous "connected" event
|
|
1076
1102
|
return;
|
|
1077
1103
|
}
|
|
1104
|
+
// If there are stashed blobs in the pending state, we need to delay
|
|
1105
|
+
// propagation of the "connected" event until we have uploaded them to
|
|
1106
|
+
// ensure we don't submit ops referencing a blob that has not been uploaded
|
|
1107
|
+
const connecting = connected && !this._connected;
|
|
1108
|
+
if (connecting && this.blobManager.hasPendingStashedBlobs()) {
|
|
1109
|
+
assert(!this.delayConnectClientId, 0x791 /* Connect event delay must be canceled before subsequent connect event */);
|
|
1110
|
+
assert(!!clientId, 0x792 /* Must have clientId when connecting */);
|
|
1111
|
+
this.delayConnectClientId = clientId;
|
|
1112
|
+
this.blobManager.processStashedChanges().then(() => {
|
|
1113
|
+
// make sure we didn't reconnect before the promise resolved
|
|
1114
|
+
if (this.delayConnectClientId === clientId && !this.disposed) {
|
|
1115
|
+
this.delayConnectClientId = undefined;
|
|
1116
|
+
this.setConnectionStateCore(connected, clientId);
|
|
1117
|
+
}
|
|
1118
|
+
}, (error) => this.closeFn(error));
|
|
1119
|
+
return;
|
|
1120
|
+
}
|
|
1078
1121
|
this.setConnectionStateCore(connected, clientId);
|
|
1079
1122
|
}
|
|
1080
1123
|
setConnectionStateCore(connected, clientId) {
|
|
@@ -1127,18 +1170,33 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1127
1170
|
// or something different, like a system message.
|
|
1128
1171
|
const modernRuntimeMessage = messageArg.type === MessageType.Operation;
|
|
1129
1172
|
// Do shallow copy of message, as the processing flow will modify it.
|
|
1173
|
+
// There might be multiple container instances receiving the same message.
|
|
1174
|
+
// We do not need to make a deep copy. Each layer will just replace message.contents itself,
|
|
1175
|
+
// but will not modify the contents object (likely it will replace it on the message).
|
|
1130
1176
|
const messageCopy = { ...messageArg };
|
|
1131
1177
|
for (const message of this.remoteMessageProcessor.process(messageCopy)) {
|
|
1132
|
-
|
|
1178
|
+
if (modernRuntimeMessage) {
|
|
1179
|
+
this.processCore({
|
|
1180
|
+
// Cast it since we expect it to be this based on modernRuntimeMessage computation above.
|
|
1181
|
+
// There is nothing really ensuring that anytime original message.type is Operation that
|
|
1182
|
+
// the result messages will be so. In the end modern bool being true only directs to
|
|
1183
|
+
// throw error if ultimately unrecognized without compat details saying otherwise.
|
|
1184
|
+
message: message,
|
|
1185
|
+
local,
|
|
1186
|
+
modernRuntimeMessage,
|
|
1187
|
+
});
|
|
1188
|
+
}
|
|
1189
|
+
else {
|
|
1190
|
+
// Unrecognized message will be ignored.
|
|
1191
|
+
this.processCore({ message, local, modernRuntimeMessage });
|
|
1192
|
+
}
|
|
1133
1193
|
}
|
|
1134
1194
|
}
|
|
1135
1195
|
/**
|
|
1136
1196
|
* 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
1197
|
*/
|
|
1141
|
-
processCore(
|
|
1198
|
+
processCore(messageWithContext) {
|
|
1199
|
+
const { message, local } = messageWithContext;
|
|
1142
1200
|
// Surround the actual processing of the operation with messages to the schedule manager indicating
|
|
1143
1201
|
// the beginning and end. This allows it to emit appropriate events and/or pause the processing of new
|
|
1144
1202
|
// messages once a batch has been fully processed.
|
|
@@ -1146,16 +1204,18 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1146
1204
|
this._processedClientSequenceNumber = message.clientSequenceNumber;
|
|
1147
1205
|
try {
|
|
1148
1206
|
let localOpMetadata;
|
|
1149
|
-
if (local &&
|
|
1150
|
-
|
|
1207
|
+
if (local &&
|
|
1208
|
+
messageWithContext.modernRuntimeMessage &&
|
|
1209
|
+
message.type !== ContainerMessageType.ChunkedOp) {
|
|
1210
|
+
localOpMetadata = this.pendingStateManager.processPendingLocalMessage(messageWithContext.message);
|
|
1151
1211
|
}
|
|
1152
1212
|
// If there are no more pending messages after processing a local message,
|
|
1153
1213
|
// the document is no longer dirty.
|
|
1154
1214
|
if (!this.hasPendingMessages()) {
|
|
1155
1215
|
this.updateDocumentDirtyState(false);
|
|
1156
1216
|
}
|
|
1157
|
-
this.validateAndProcessRuntimeMessage(
|
|
1158
|
-
this.emit("op", message, modernRuntimeMessage);
|
|
1217
|
+
this.validateAndProcessRuntimeMessage(messageWithContext, localOpMetadata);
|
|
1218
|
+
this.emit("op", message, messageWithContext.modernRuntimeMessage);
|
|
1159
1219
|
this.scheduleManager.afterOpProcessing(undefined, message);
|
|
1160
1220
|
if (local) {
|
|
1161
1221
|
// If we have processed a local op, this means that the container is
|
|
@@ -1170,29 +1230,29 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1170
1230
|
}
|
|
1171
1231
|
}
|
|
1172
1232
|
/**
|
|
1173
|
-
* Assuming the given message is also a
|
|
1233
|
+
* Assuming the given message is also a TypedContainerRuntimeMessage,
|
|
1174
1234
|
* 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
|
|
1235
|
+
* Throws a DataProcessingError if the message looks like but doesn't conform to a known TypedContainerRuntimeMessage type.
|
|
1176
1236
|
*/
|
|
1177
|
-
validateAndProcessRuntimeMessage(
|
|
1178
|
-
//
|
|
1179
|
-
const {
|
|
1180
|
-
switch (
|
|
1237
|
+
validateAndProcessRuntimeMessage(messageWithContext, localOpMetadata) {
|
|
1238
|
+
// TODO: destructure message and modernRuntimeMessage once using typescript 5.2.2+
|
|
1239
|
+
const { local } = messageWithContext;
|
|
1240
|
+
switch (messageWithContext.message.type) {
|
|
1181
1241
|
case ContainerMessageType.Attach:
|
|
1182
|
-
this.dataStores.processAttachMessage(message, local);
|
|
1242
|
+
this.dataStores.processAttachMessage(messageWithContext.message, local);
|
|
1183
1243
|
break;
|
|
1184
1244
|
case ContainerMessageType.Alias:
|
|
1185
|
-
this.dataStores.processAliasMessage(message, localOpMetadata, local);
|
|
1245
|
+
this.dataStores.processAliasMessage(messageWithContext.message, localOpMetadata, local);
|
|
1186
1246
|
break;
|
|
1187
1247
|
case ContainerMessageType.FluidDataStoreOp:
|
|
1188
|
-
this.dataStores.processFluidDataStoreOp(message, local, localOpMetadata);
|
|
1248
|
+
this.dataStores.processFluidDataStoreOp(messageWithContext.message, local, localOpMetadata);
|
|
1189
1249
|
break;
|
|
1190
1250
|
case ContainerMessageType.BlobAttach:
|
|
1191
|
-
this.blobManager.processBlobAttachOp(message, local);
|
|
1251
|
+
this.blobManager.processBlobAttachOp(messageWithContext.message, local);
|
|
1192
1252
|
break;
|
|
1193
1253
|
case ContainerMessageType.IdAllocation:
|
|
1194
1254
|
assert(this.idCompressor !== undefined, 0x67c /* IdCompressor should be defined if enabled */);
|
|
1195
|
-
this.idCompressor.finalizeCreationRange(message.contents);
|
|
1255
|
+
this.idCompressor.finalizeCreationRange(messageWithContext.message.contents);
|
|
1196
1256
|
break;
|
|
1197
1257
|
case ContainerMessageType.ChunkedOp:
|
|
1198
1258
|
case ContainerMessageType.Rejoin:
|
|
@@ -1200,11 +1260,12 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1200
1260
|
default: {
|
|
1201
1261
|
// If we didn't necessarily expect a runtime message type, then no worries - just return
|
|
1202
1262
|
// e.g. this case applies to system ops, or legacy ops that would have fallen into the above cases anyway.
|
|
1203
|
-
if (!
|
|
1263
|
+
if (!messageWithContext.modernRuntimeMessage) {
|
|
1204
1264
|
return;
|
|
1205
1265
|
}
|
|
1206
|
-
const compatBehavior = compatDetails?.behavior;
|
|
1207
|
-
if (!compatBehaviorAllowsMessageType(
|
|
1266
|
+
const compatBehavior = messageWithContext.message.compatDetails?.behavior;
|
|
1267
|
+
if (!compatBehaviorAllowsMessageType(messageWithContext.message.type, compatBehavior)) {
|
|
1268
|
+
const { message } = messageWithContext;
|
|
1208
1269
|
const error = DataProcessingError.create(
|
|
1209
1270
|
// Former assert 0x3ce
|
|
1210
1271
|
"Runtime message of unknown type", "OpProcessing", message, {
|
|
@@ -1280,8 +1341,9 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1280
1341
|
* Returns the runtime of the data store.
|
|
1281
1342
|
* @param id - Id supplied during creating the data store.
|
|
1282
1343
|
* @param wait - True if you want to wait for it.
|
|
1283
|
-
* @deprecated
|
|
1344
|
+
* @deprecated Use getAliasedDataStoreEntryPoint instead to get an aliased data store's entry point.
|
|
1284
1345
|
*/
|
|
1346
|
+
// eslint-disable-next-line import/no-deprecated
|
|
1285
1347
|
async getRootDataStore(id, wait = true) {
|
|
1286
1348
|
return this.getRootDataStoreChannel(id, wait);
|
|
1287
1349
|
}
|
|
@@ -1578,7 +1640,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1578
1640
|
this.dataStores.updateUnusedRoutes(dataStoreRoutes);
|
|
1579
1641
|
}
|
|
1580
1642
|
/**
|
|
1581
|
-
* @deprecated
|
|
1643
|
+
* @deprecated Replaced by deleteSweepReadyNodes.
|
|
1582
1644
|
*/
|
|
1583
1645
|
deleteUnusedNodes(unusedRoutes) {
|
|
1584
1646
|
throw new Error("deleteUnusedRoutes should not be called");
|
|
@@ -1700,16 +1762,13 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1700
1762
|
},
|
|
1701
1763
|
});
|
|
1702
1764
|
assert(this.outbox.isEmpty, 0x3d1 /* Can't trigger summary in the middle of a batch */);
|
|
1765
|
+
// We close the summarizer and download a new snapshot and reload the container
|
|
1703
1766
|
let latestSnapshotVersionId;
|
|
1704
|
-
if (refreshLatestAck) {
|
|
1705
|
-
|
|
1767
|
+
if (refreshLatestAck === true) {
|
|
1768
|
+
return this.prefetchLatestSummaryThenClose(createChildLogger({
|
|
1706
1769
|
logger: summaryNumberLogger,
|
|
1707
1770
|
properties: { all: { safeSummary: true } },
|
|
1708
1771
|
}));
|
|
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
1772
|
}
|
|
1714
1773
|
// If there are pending (unacked ops), the summary will not be eventual consistent and it may even be
|
|
1715
1774
|
// incorrect. So, wait for the container to be saved with a timeout. If the container is not saved
|
|
@@ -2233,34 +2292,31 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
2233
2292
|
}
|
|
2234
2293
|
reSubmit(message) {
|
|
2235
2294
|
// Need to parse from string for back-compat
|
|
2236
|
-
const containerRuntimeMessage = this.
|
|
2295
|
+
const containerRuntimeMessage = this.parseLocalOpContent(message.content);
|
|
2237
2296
|
this.reSubmitCore(containerRuntimeMessage, message.localOpMetadata, message.opMetadata);
|
|
2238
2297
|
}
|
|
2239
2298
|
/**
|
|
2240
2299
|
* Finds the right store and asks it to resubmit the message. This typically happens when we
|
|
2241
2300
|
* reconnect and there are pending messages.
|
|
2242
|
-
* @param message - The original
|
|
2301
|
+
* @param message - The original LocalContainerRuntimeMessage.
|
|
2243
2302
|
* @param localOpMetadata - The local metadata associated with the original message.
|
|
2244
2303
|
*/
|
|
2245
2304
|
reSubmitCore(message, localOpMetadata, opMetadata) {
|
|
2246
|
-
const contents = message.contents;
|
|
2247
2305
|
switch (message.type) {
|
|
2248
2306
|
case ContainerMessageType.FluidDataStoreOp:
|
|
2249
2307
|
// For Operations, call resubmitDataStoreOp which will find the right store
|
|
2250
2308
|
// and trigger resubmission on it.
|
|
2251
|
-
this.dataStores.resubmitDataStoreOp(contents, localOpMetadata);
|
|
2309
|
+
this.dataStores.resubmitDataStoreOp(message.contents, localOpMetadata);
|
|
2252
2310
|
break;
|
|
2253
2311
|
case ContainerMessageType.Attach:
|
|
2254
2312
|
case ContainerMessageType.Alias:
|
|
2255
2313
|
this.submit(message, localOpMetadata);
|
|
2256
2314
|
break;
|
|
2257
|
-
case ContainerMessageType.IdAllocation:
|
|
2258
|
-
|
|
2259
|
-
if (contents.stashedState !== undefined) {
|
|
2260
|
-
delete contents.stashedState;
|
|
2261
|
-
}
|
|
2315
|
+
case ContainerMessageType.IdAllocation: {
|
|
2316
|
+
prepareLocalContainerRuntimeIdAllocationMessageForTransit(message);
|
|
2262
2317
|
this.submit(message, localOpMetadata);
|
|
2263
2318
|
break;
|
|
2319
|
+
}
|
|
2264
2320
|
case ContainerMessageType.ChunkedOp:
|
|
2265
2321
|
throw new Error(`chunkedOp not expected here`);
|
|
2266
2322
|
case ContainerMessageType.BlobAttach:
|
|
@@ -2294,7 +2350,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
2294
2350
|
}
|
|
2295
2351
|
rollback(content, localOpMetadata) {
|
|
2296
2352
|
// Need to parse from string for back-compat
|
|
2297
|
-
const { type, contents } = this.
|
|
2353
|
+
const { type, contents } = this.parseLocalOpContent(content);
|
|
2298
2354
|
switch (type) {
|
|
2299
2355
|
case ContainerMessageType.FluidDataStoreOp:
|
|
2300
2356
|
// For operations, call rollbackDataStoreOp which will find the right store
|
|
@@ -2306,17 +2362,6 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
2306
2362
|
throw new Error(`Can't rollback ${type}`);
|
|
2307
2363
|
}
|
|
2308
2364
|
}
|
|
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
2365
|
/** Implementation of ISummarizerInternalsProvider.refreshLatestSummaryAck */
|
|
2321
2366
|
async refreshLatestSummaryAck(options) {
|
|
2322
2367
|
const { proposalHandle, ackHandle, summaryRefSeq, summaryLogger } = options;
|
|
@@ -2331,11 +2376,11 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
2331
2376
|
* and then close as the current main client is likely to be re-elected as the parent summarizer again.
|
|
2332
2377
|
*/
|
|
2333
2378
|
if (!result.isSummaryTracked && result.isSummaryNewer) {
|
|
2334
|
-
const fetchResult = await this.
|
|
2379
|
+
const fetchResult = await this.fetchLatestSnapshotFromStorage(summaryLogger, {
|
|
2335
2380
|
eventName: "RefreshLatestSummaryAckFetch",
|
|
2336
2381
|
ackHandle,
|
|
2337
2382
|
targetSequenceNumber: summaryRefSeq,
|
|
2338
|
-
}, readAndParseBlob
|
|
2383
|
+
}, readAndParseBlob);
|
|
2339
2384
|
/**
|
|
2340
2385
|
* If the fetched snapshot is older than the one for which the ack was received, close the container.
|
|
2341
2386
|
* This should never happen because an ack should be sent after the latest summary is updated in the server.
|
|
@@ -2362,41 +2407,42 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
2362
2407
|
await this.garbageCollector.refreshLatestSummary(result);
|
|
2363
2408
|
}
|
|
2364
2409
|
/**
|
|
2365
|
-
* Fetches the latest snapshot from storage
|
|
2366
|
-
*
|
|
2410
|
+
* Fetches the latest snapshot from storage to refresh the cache as a performance optimization and closes the
|
|
2411
|
+
* summarizer to reload from new state.
|
|
2367
2412
|
* @param summaryLogger - logger to use when fetching snapshot from storage
|
|
2368
|
-
* @returns
|
|
2413
|
+
* @returns a generic summarization error
|
|
2369
2414
|
*/
|
|
2370
|
-
async
|
|
2415
|
+
async prefetchLatestSummaryThenClose(summaryLogger) {
|
|
2371
2416
|
const readAndParseBlob = async (id) => readAndParse(this.storage, id);
|
|
2372
|
-
|
|
2417
|
+
// This is a performance optimization as the same parent is likely to be elected again, and would use its
|
|
2418
|
+
// cache to fetch the snapshot instead of the network.
|
|
2419
|
+
await this.fetchLatestSnapshotFromStorage(summaryLogger, {
|
|
2373
2420
|
eventName: "RefreshLatestSummaryFromServerFetch",
|
|
2374
|
-
}, readAndParseBlob
|
|
2421
|
+
}, readAndParseBlob);
|
|
2375
2422
|
await this.closeStaleSummarizer("RefreshLatestSummaryFromServerFetch");
|
|
2376
|
-
return {
|
|
2423
|
+
return {
|
|
2424
|
+
stage: "base",
|
|
2425
|
+
error: "summary state stale - Unsupported option 'refreshLatestAck'",
|
|
2426
|
+
referenceSequenceNumber: this.deltaManager.lastSequenceNumber,
|
|
2427
|
+
minimumSequenceNumber: this.deltaManager.minimumSequenceNumber,
|
|
2428
|
+
};
|
|
2377
2429
|
}
|
|
2378
2430
|
async closeStaleSummarizer(codePath) {
|
|
2379
|
-
this.mc.logger.sendTelemetryEvent({
|
|
2380
|
-
eventName: "ClosingSummarizerOnSummaryStale",
|
|
2381
|
-
codePath,
|
|
2382
|
-
message: "Stopping fetch from storage",
|
|
2383
|
-
closeSummarizerDelayMs: this.closeSummarizerDelayMs,
|
|
2384
|
-
}, new GenericError("Restarting summarizer instead of refreshing"));
|
|
2385
2431
|
// Delay before restarting summarizer to prevent the summarizer from restarting too frequently.
|
|
2386
2432
|
await delay(this.closeSummarizerDelayMs);
|
|
2387
2433
|
this._summarizer?.stop("latestSummaryStateStale");
|
|
2388
2434
|
this.disposeFn();
|
|
2389
2435
|
}
|
|
2390
2436
|
/**
|
|
2391
|
-
* Downloads
|
|
2437
|
+
* Downloads the latest snapshot from storage.
|
|
2392
2438
|
* By default, it also closes the container after downloading the snapshot. However, this may be
|
|
2393
2439
|
* overridden via options.
|
|
2394
2440
|
*/
|
|
2395
|
-
async
|
|
2441
|
+
async fetchLatestSnapshotFromStorage(logger, event, readAndParseBlob) {
|
|
2396
2442
|
return PerformanceEvent.timedExecAsync(logger, event, async (perfEvent) => {
|
|
2397
2443
|
const stats = {};
|
|
2398
2444
|
const trace = Trace.start();
|
|
2399
|
-
const versions = await this.storage.getVersions(
|
|
2445
|
+
const versions = await this.storage.getVersions(null, 1, "prefetchLatestSummaryBeforeClose", FetchSource.noCache);
|
|
2400
2446
|
assert(!!versions && !!versions[0], 0x137 /* "Failed to get version from storage" */);
|
|
2401
2447
|
stats.getVersionDuration = trace.trace().duration;
|
|
2402
2448
|
const maybeSnapshot = await this.storage.getSnapshotTree(versions[0]);
|
|
@@ -2424,15 +2470,17 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
2424
2470
|
if (this._orderSequentiallyCalls !== 0) {
|
|
2425
2471
|
throw new UsageError("can't get state during orderSequentially");
|
|
2426
2472
|
}
|
|
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
2473
|
// Flush pending batch.
|
|
2433
2474
|
// getPendingLocalState() is only exposed through Container.closeAndGetPendingLocalState(), so it's safe
|
|
2434
2475
|
// to close current batch.
|
|
2435
2476
|
this.flush();
|
|
2477
|
+
const pendingAttachmentBlobs = waitBlobsToAttach
|
|
2478
|
+
? await this.blobManager.attachAndGetPendingBlobs()
|
|
2479
|
+
: undefined;
|
|
2480
|
+
const pending = this.pendingStateManager.getLocalState();
|
|
2481
|
+
if (!pendingAttachmentBlobs && !this.hasPendingMessages()) {
|
|
2482
|
+
return; // no pending state to save
|
|
2483
|
+
}
|
|
2436
2484
|
const pendingState = {
|
|
2437
2485
|
pending,
|
|
2438
2486
|
pendingAttachmentBlobs,
|
|
@@ -2473,29 +2521,11 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
2473
2521
|
}
|
|
2474
2522
|
}
|
|
2475
2523
|
/**
|
|
2476
|
-
*
|
|
2477
|
-
|
|
2478
|
-
|
|
2479
|
-
formRequestSummarizerFn(loaderRouter) {
|
|
2524
|
+
* Forms a function that will create and retrieve a Summarizer.
|
|
2525
|
+
*/
|
|
2526
|
+
formCreateSummarizerFn(loader) {
|
|
2480
2527
|
return async () => {
|
|
2481
|
-
|
|
2482
|
-
headers: {
|
|
2483
|
-
[LoaderHeader.cache]: false,
|
|
2484
|
-
[LoaderHeader.clientDetails]: {
|
|
2485
|
-
capabilities: { interactive: false },
|
|
2486
|
-
type: summarizerClientType,
|
|
2487
|
-
},
|
|
2488
|
-
[DriverHeader.summarizingClient]: true,
|
|
2489
|
-
[LoaderHeader.reconnect]: false,
|
|
2490
|
-
},
|
|
2491
|
-
url: "/_summarizer",
|
|
2492
|
-
};
|
|
2493
|
-
const fluidObject = await requestFluidObject(loaderRouter, request);
|
|
2494
|
-
const summarizer = fluidObject.ISummarizer;
|
|
2495
|
-
if (!summarizer) {
|
|
2496
|
-
throw new UsageError("Fluid object does not implement ISummarizer");
|
|
2497
|
-
}
|
|
2498
|
-
return summarizer;
|
|
2528
|
+
return createSummarizer(loader, `/${summarizerRequestUrl}`);
|
|
2499
2529
|
};
|
|
2500
2530
|
}
|
|
2501
2531
|
validateSummaryHeuristicConfiguration(configuration) {
|
|
@@ -2514,26 +2544,4 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
2514
2544
|
return killSwitch !== true && this.runtimeOptions.enableGroupedBatching;
|
|
2515
2545
|
}
|
|
2516
2546
|
}
|
|
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
2547
|
//# sourceMappingURL=containerRuntime.js.map
|