@fluidframework/container-runtime 2.0.0-dev.6.4.0.192049 → 2.0.0-dev.7.2.0.204906
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 +866 -0
- package/dist/blobManager.d.ts +4 -6
- package/dist/blobManager.d.ts.map +1 -1
- package/dist/blobManager.js +56 -78
- 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 +1744 -0
- package/dist/container-runtime-beta.d.ts +1744 -0
- package/dist/container-runtime-public.d.ts +1744 -0
- package/dist/container-runtime-untrimmed.d.ts +1805 -0
- package/dist/containerHandleContext.js +3 -3
- package/dist/containerHandleContext.js.map +1 -1
- package/dist/containerRuntime.d.ts +94 -102
- package/dist/containerRuntime.d.ts.map +1 -1
- package/dist/containerRuntime.js +478 -454
- 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 +56 -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.d.ts +0 -2
- package/dist/dataStores.d.ts.map +1 -1
- package/dist/dataStores.js +3 -8
- 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 +6 -0
- package/dist/gc/garbageCollection.d.ts.map +1 -1
- package/dist/gc/garbageCollection.js +36 -25
- package/dist/gc/garbageCollection.js.map +1 -1
- package/dist/gc/gcConfigs.d.ts +1 -0
- package/dist/gc/gcConfigs.d.ts.map +1 -1
- package/dist/gc/gcConfigs.js +13 -3
- package/dist/gc/gcConfigs.js.map +1 -1
- package/dist/gc/gcDefinitions.d.ts +48 -28
- 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 +3 -4
- package/dist/gc/gcTelemetry.d.ts.map +1 -1
- package/dist/gc/gcTelemetry.js +7 -8
- 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 -7
- 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 -6
- package/lib/blobManager.d.ts.map +1 -1
- package/lib/blobManager.js +58 -80
- 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 +94 -102
- package/lib/containerRuntime.d.ts.map +1 -1
- package/lib/containerRuntime.js +437 -418
- 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 +57 -60
- 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.d.ts +0 -2
- package/lib/dataStores.d.ts.map +1 -1
- package/lib/dataStores.js +4 -9
- 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 +6 -0
- package/lib/gc/garbageCollection.d.ts.map +1 -1
- package/lib/gc/garbageCollection.js +36 -25
- package/lib/gc/garbageCollection.js.map +1 -1
- package/lib/gc/gcConfigs.d.ts +1 -0
- package/lib/gc/gcConfigs.d.ts.map +1 -1
- package/lib/gc/gcConfigs.js +15 -5
- package/lib/gc/gcConfigs.js.map +1 -1
- package/lib/gc/gcDefinitions.d.ts +48 -28
- 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 +3 -4
- package/lib/gc/gcTelemetry.d.ts.map +1 -1
- package/lib/gc/gcTelemetry.js +7 -8
- 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/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 +32 -29
- package/src/blobManager.ts +67 -92
- package/src/connectionTelemetry.ts +97 -52
- package/src/containerRuntime.ts +351 -351
- package/src/dataStore.ts +3 -3
- package/src/dataStoreContext.ts +9 -13
- package/src/dataStoreRegistry.ts +3 -0
- package/src/dataStores.ts +5 -17
- package/src/error.ts +4 -1
- package/src/gc/garbageCollection.ts +25 -12
- package/src/gc/gcConfigs.ts +25 -7
- package/src/gc/gcDefinitions.ts +49 -29
- package/src/gc/gcSummaryDefinitions.ts +1 -1
- package/src/gc/gcTelemetry.ts +8 -8
- package/src/gc/index.ts +2 -6
- 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";
|
|
@@ -19,38 +19,18 @@ import { BlobManager } from "./blobManager";
|
|
|
19
19
|
import { DataStores, getSummaryForDatastores } from "./dataStores";
|
|
20
20
|
import { aliasBlobName, blobsTreeName, chunksBlobName, createRootSummarizerNodeWithGC, electedSummarizerBlobName, extractSummaryMetadataMessage, idCompressorBlobName, metadataBlobName, Summarizer, SummaryManager, wrapSummaryInChannelsTree, SummaryCollection, OrderedClientCollection, OrderedClientElection, SummarizerClientElection, summarizerClientType, RunWhileConnectedCoordinator, RetriableSummaryError, } from "./summary";
|
|
21
21
|
import { formExponentialFn, Throttler } from "./throttler";
|
|
22
|
-
import { GarbageCollector, GCNodeType, gcTombstoneGenerationOptionName,
|
|
22
|
+
import { GarbageCollector, GCNodeType, gcTombstoneGenerationOptionName, trimLeadingAndTrailingSlashes, } from "./gc";
|
|
23
23
|
import { channelToDataStore, isDataStoreAliasMessage } from "./dataStore";
|
|
24
24
|
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,288 @@ 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
|
+
}
|
|
447
|
+
/** If false, loading or using a Tombstoned object should merely log, not fail */
|
|
448
|
+
get gcTombstoneEnforcementAllowed() {
|
|
449
|
+
return this.garbageCollector.tombstoneEnforcementAllowed;
|
|
450
|
+
}
|
|
451
|
+
/** If true, throw an error when a tombstone data store is retrieved */
|
|
452
|
+
get gcThrowOnTombstoneLoad() {
|
|
453
|
+
return this.garbageCollector.throwOnTombstoneLoad;
|
|
454
|
+
}
|
|
455
|
+
/** If true, throw an error when a tombstone data store is used. */
|
|
456
|
+
get gcThrowOnTombstoneUsage() {
|
|
457
|
+
return this.garbageCollector.throwOnTombstoneUsage;
|
|
458
|
+
}
|
|
186
459
|
/**
|
|
187
460
|
* @internal
|
|
188
461
|
*/
|
|
189
|
-
constructor(context, registry, metadata, electedSummarizerData, chunks, dataStoreAliasMap, runtimeOptions, containerScope, logger, existing, blobManagerSnapshot, _storage, idCompressor, requestHandler, summaryConfiguration = {
|
|
462
|
+
constructor(context, registry, metadata, electedSummarizerData, chunks, dataStoreAliasMap, runtimeOptions, containerScope, logger, existing, blobManagerSnapshot, _storage, idCompressor, provideEntryPoint, requestHandler, summaryConfiguration = {
|
|
190
463
|
// the defaults
|
|
191
464
|
...DefaultSummaryConfiguration,
|
|
192
465
|
// the runtime configuration overrides
|
|
193
466
|
...runtimeOptions.summaryOptions?.summaryConfigOverrides,
|
|
194
|
-
}
|
|
467
|
+
}) {
|
|
195
468
|
super();
|
|
196
469
|
this.registry = registry;
|
|
197
470
|
this.runtimeOptions = runtimeOptions;
|
|
@@ -287,7 +560,6 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
287
560
|
// Note that we only need to pull the *initial* connected state from the context.
|
|
288
561
|
// Later updates come through calls to setConnectionState.
|
|
289
562
|
this._connected = connected;
|
|
290
|
-
this.gcTombstoneEnforcementAllowed = shouldAllowGcTombstoneEnforcement(metadata?.gcFeatureMatrix?.tombstoneGeneration /* persisted */, this.runtimeOptions.gcOptions[gcTombstoneGenerationOptionName] /* current */);
|
|
291
563
|
this.mc.logger.sendTelemetryEvent({
|
|
292
564
|
eventName: "GCFeatureMatrix",
|
|
293
565
|
metadataValue: JSON.stringify(metadata?.gcFeatureMatrix),
|
|
@@ -483,7 +755,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
483
755
|
this.summaryCollection.on("default", defaultAction);
|
|
484
756
|
// Create the SummaryManager and mark the initial state
|
|
485
757
|
this.summaryManager = new SummaryManager(this.summarizerClientElection, this, // IConnectedState
|
|
486
|
-
this.summaryCollection, this.logger, this.
|
|
758
|
+
this.summaryCollection, this.logger, this.formCreateSummarizerFn(loader), new Throttler(60 * 1000, // 60 sec delay window
|
|
487
759
|
30 * 1000, // 30 sec max delay
|
|
488
760
|
// throttling function increases exponentially (0ms, 40ms, 80ms, 160ms, etc)
|
|
489
761
|
formExponentialFn({ coefficient: 20, initialDelay: 0 })), {
|
|
@@ -495,26 +767,6 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
495
767
|
this.summaryManager.start();
|
|
496
768
|
}
|
|
497
769
|
}
|
|
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
770
|
// logging hardware telemetry
|
|
519
771
|
logger.sendTelemetryEvent({
|
|
520
772
|
eventName: "DeviceSpec",
|
|
@@ -548,233 +800,8 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
548
800
|
assert(this._summarizer !== undefined, 0x5bf /* Summarizer object is undefined in a summarizer client */);
|
|
549
801
|
return this._summarizer;
|
|
550
802
|
}
|
|
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);
|
|
803
|
+
return provideEntryPoint(this);
|
|
650
804
|
});
|
|
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
805
|
}
|
|
779
806
|
/**
|
|
780
807
|
* Initializes the state from the base snapshot this container runtime loaded from.
|
|
@@ -806,13 +833,13 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
806
833
|
/**
|
|
807
834
|
* Notifies this object about the request made to the container.
|
|
808
835
|
* @param request - Request made to the handler.
|
|
809
|
-
* @deprecated
|
|
836
|
+
* @deprecated Will be removed in future major release. Migrate all usage of IFluidRouter to the "entryPoint" pattern. Refer to Removing-IFluidRouter.md
|
|
810
837
|
*/
|
|
811
838
|
async request(request) {
|
|
812
839
|
try {
|
|
813
840
|
const parser = RequestParser.create(request);
|
|
814
841
|
const id = parser.pathParts[0];
|
|
815
|
-
if (id ===
|
|
842
|
+
if (id === summarizerRequestUrl && parser.pathParts.length === 1) {
|
|
816
843
|
if (this._summarizer !== undefined) {
|
|
817
844
|
return {
|
|
818
845
|
status: 200,
|
|
@@ -823,6 +850,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
823
850
|
return create404Response(request);
|
|
824
851
|
}
|
|
825
852
|
if (this.requestHandler !== undefined) {
|
|
853
|
+
// eslint-disable-next-line @typescript-eslint/return-await -- Adding an await here causes test failures
|
|
826
854
|
return this.requestHandler(parser, this);
|
|
827
855
|
}
|
|
828
856
|
return create404Response(request);
|
|
@@ -840,6 +868,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
840
868
|
const requestParser = RequestParser.create(request);
|
|
841
869
|
const id = requestParser.pathParts[0];
|
|
842
870
|
if (id === "_channels") {
|
|
871
|
+
// eslint-disable-next-line @typescript-eslint/return-await -- Adding an await here causes test failures
|
|
843
872
|
return this.resolveHandle(requestParser.createSubRequest(1));
|
|
844
873
|
}
|
|
845
874
|
if (id === BlobManager.basePath && requestParser.isLeaf(2)) {
|
|
@@ -853,11 +882,14 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
853
882
|
: create404Response(request);
|
|
854
883
|
}
|
|
855
884
|
else if (requestParser.pathParts.length > 0) {
|
|
856
|
-
|
|
885
|
+
// Differentiate between requesting the dataStore directly, or one of its children
|
|
886
|
+
const requestForChild = !requestParser.isLeaf(1);
|
|
887
|
+
const dataStore = await this.getDataStoreFromRequest(id, request, requestForChild);
|
|
857
888
|
const subRequest = requestParser.createSubRequest(1);
|
|
858
889
|
// We always expect createSubRequest to include a leading slash, but asserting here to protect against
|
|
859
890
|
// unintentionally modifying the url if that changes.
|
|
860
891
|
assert(subRequest.url.startsWith("/"), 0x126 /* "Expected createSubRequest url to include a leading slash" */);
|
|
892
|
+
// eslint-disable-next-line @typescript-eslint/return-await -- Adding an await here causes test failures
|
|
861
893
|
return dataStore.request(subRequest);
|
|
862
894
|
}
|
|
863
895
|
return create404Response(request);
|
|
@@ -875,7 +907,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
875
907
|
internalId(maybeAlias) {
|
|
876
908
|
return this.dataStores.aliases.get(maybeAlias) ?? maybeAlias;
|
|
877
909
|
}
|
|
878
|
-
async getDataStoreFromRequest(id, request) {
|
|
910
|
+
async getDataStoreFromRequest(id, request, requestForChild) {
|
|
879
911
|
const headerData = {};
|
|
880
912
|
if (typeof request.headers?.[RuntimeHeaders.wait] === "boolean") {
|
|
881
913
|
headerData.wait = request.headers[RuntimeHeaders.wait];
|
|
@@ -886,6 +918,10 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
886
918
|
if (typeof request.headers?.[AllowTombstoneRequestHeaderKey] === "boolean") {
|
|
887
919
|
headerData.allowTombstone = request.headers[AllowTombstoneRequestHeaderKey];
|
|
888
920
|
}
|
|
921
|
+
// We allow Tombstone requests for sub-DataStore objects
|
|
922
|
+
if (requestForChild) {
|
|
923
|
+
headerData.allowTombstone = true;
|
|
924
|
+
}
|
|
889
925
|
await this.dataStores.waitIfPendingAlias(id);
|
|
890
926
|
const internalId = this.internalId(id);
|
|
891
927
|
const dataStoreContext = await this.dataStores.getDataStore(internalId, headerData);
|
|
@@ -1024,23 +1060,24 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1024
1060
|
* Parse an op's type and actual content from given serialized content
|
|
1025
1061
|
* ! Note: this format needs to be in-line with what is set in the "ContainerRuntime.submit(...)" method
|
|
1026
1062
|
*/
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1063
|
+
// TODO: markfields: confirm Local- versus Outbound- ContainerRuntimeMessage typing
|
|
1064
|
+
parseLocalOpContent(serializedContents) {
|
|
1065
|
+
assert(serializedContents !== undefined, 0x6d5 /* content must be defined */);
|
|
1066
|
+
const message = JSON.parse(serializedContents);
|
|
1067
|
+
assert(message.type !== undefined, 0x6d6 /* incorrect op content format */);
|
|
1068
|
+
return message;
|
|
1069
|
+
}
|
|
1070
|
+
async applyStashedOp(serializedOpContent) {
|
|
1034
1071
|
// Need to parse from string for back-compat
|
|
1035
|
-
const
|
|
1036
|
-
switch (type) {
|
|
1072
|
+
const opContents = this.parseLocalOpContent(serializedOpContent);
|
|
1073
|
+
switch (opContents.type) {
|
|
1037
1074
|
case ContainerMessageType.FluidDataStoreOp:
|
|
1038
|
-
return this.dataStores.applyStashedOp(contents);
|
|
1075
|
+
return this.dataStores.applyStashedOp(opContents.contents);
|
|
1039
1076
|
case ContainerMessageType.Attach:
|
|
1040
|
-
return this.dataStores.applyStashedAttachOp(contents);
|
|
1077
|
+
return this.dataStores.applyStashedAttachOp(opContents.contents);
|
|
1041
1078
|
case ContainerMessageType.IdAllocation:
|
|
1042
1079
|
assert(this.idCompressor !== undefined, 0x67b /* IdCompressor should be defined if enabled */);
|
|
1043
|
-
return this.applyStashedIdAllocationOp(contents);
|
|
1080
|
+
return this.applyStashedIdAllocationOp(opContents.contents);
|
|
1044
1081
|
case ContainerMessageType.Alias:
|
|
1045
1082
|
case ContainerMessageType.BlobAttach:
|
|
1046
1083
|
return;
|
|
@@ -1052,11 +1089,11 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1052
1089
|
// This should be extremely rare for stashed ops.
|
|
1053
1090
|
// It would require a newer runtime stashing ops and then an older one applying them,
|
|
1054
1091
|
// e.g. if an app rolled back its container version
|
|
1055
|
-
const compatBehavior = compatDetails?.behavior;
|
|
1056
|
-
if (!compatBehaviorAllowsMessageType(type, compatBehavior)) {
|
|
1092
|
+
const compatBehavior = opContents.compatDetails?.behavior;
|
|
1093
|
+
if (!compatBehaviorAllowsMessageType(opContents.type, compatBehavior)) {
|
|
1057
1094
|
const error = DataProcessingError.create("Stashed runtime message of unknown type", "applyStashedOp", undefined /* sequencedMessage */, {
|
|
1058
1095
|
messageDetails: JSON.stringify({
|
|
1059
|
-
type,
|
|
1096
|
+
type: opContents.type,
|
|
1060
1097
|
compatBehavior,
|
|
1061
1098
|
}),
|
|
1062
1099
|
});
|
|
@@ -1075,6 +1112,23 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1075
1112
|
// Don't propagate "disconnected" event because we didn't propagate the previous "connected" event
|
|
1076
1113
|
return;
|
|
1077
1114
|
}
|
|
1115
|
+
// If there are stashed blobs in the pending state, we need to delay
|
|
1116
|
+
// propagation of the "connected" event until we have uploaded them to
|
|
1117
|
+
// ensure we don't submit ops referencing a blob that has not been uploaded
|
|
1118
|
+
const connecting = connected && !this._connected;
|
|
1119
|
+
if (connecting && this.blobManager.hasPendingStashedBlobs()) {
|
|
1120
|
+
assert(!this.delayConnectClientId, 0x791 /* Connect event delay must be canceled before subsequent connect event */);
|
|
1121
|
+
assert(!!clientId, 0x792 /* Must have clientId when connecting */);
|
|
1122
|
+
this.delayConnectClientId = clientId;
|
|
1123
|
+
this.blobManager.processStashedChanges().then(() => {
|
|
1124
|
+
// make sure we didn't reconnect before the promise resolved
|
|
1125
|
+
if (this.delayConnectClientId === clientId && !this.disposed) {
|
|
1126
|
+
this.delayConnectClientId = undefined;
|
|
1127
|
+
this.setConnectionStateCore(connected, clientId);
|
|
1128
|
+
}
|
|
1129
|
+
}, (error) => this.closeFn(error));
|
|
1130
|
+
return;
|
|
1131
|
+
}
|
|
1078
1132
|
this.setConnectionStateCore(connected, clientId);
|
|
1079
1133
|
}
|
|
1080
1134
|
setConnectionStateCore(connected, clientId) {
|
|
@@ -1127,18 +1181,33 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1127
1181
|
// or something different, like a system message.
|
|
1128
1182
|
const modernRuntimeMessage = messageArg.type === MessageType.Operation;
|
|
1129
1183
|
// Do shallow copy of message, as the processing flow will modify it.
|
|
1184
|
+
// There might be multiple container instances receiving the same message.
|
|
1185
|
+
// We do not need to make a deep copy. Each layer will just replace message.contents itself,
|
|
1186
|
+
// but will not modify the contents object (likely it will replace it on the message).
|
|
1130
1187
|
const messageCopy = { ...messageArg };
|
|
1131
1188
|
for (const message of this.remoteMessageProcessor.process(messageCopy)) {
|
|
1132
|
-
|
|
1189
|
+
if (modernRuntimeMessage) {
|
|
1190
|
+
this.processCore({
|
|
1191
|
+
// Cast it since we expect it to be this based on modernRuntimeMessage computation above.
|
|
1192
|
+
// There is nothing really ensuring that anytime original message.type is Operation that
|
|
1193
|
+
// the result messages will be so. In the end modern bool being true only directs to
|
|
1194
|
+
// throw error if ultimately unrecognized without compat details saying otherwise.
|
|
1195
|
+
message: message,
|
|
1196
|
+
local,
|
|
1197
|
+
modernRuntimeMessage,
|
|
1198
|
+
});
|
|
1199
|
+
}
|
|
1200
|
+
else {
|
|
1201
|
+
// Unrecognized message will be ignored.
|
|
1202
|
+
this.processCore({ message, local, modernRuntimeMessage });
|
|
1203
|
+
}
|
|
1133
1204
|
}
|
|
1134
1205
|
}
|
|
1135
1206
|
/**
|
|
1136
1207
|
* 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
1208
|
*/
|
|
1141
|
-
processCore(
|
|
1209
|
+
processCore(messageWithContext) {
|
|
1210
|
+
const { message, local } = messageWithContext;
|
|
1142
1211
|
// Surround the actual processing of the operation with messages to the schedule manager indicating
|
|
1143
1212
|
// the beginning and end. This allows it to emit appropriate events and/or pause the processing of new
|
|
1144
1213
|
// messages once a batch has been fully processed.
|
|
@@ -1146,16 +1215,18 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1146
1215
|
this._processedClientSequenceNumber = message.clientSequenceNumber;
|
|
1147
1216
|
try {
|
|
1148
1217
|
let localOpMetadata;
|
|
1149
|
-
if (local &&
|
|
1150
|
-
|
|
1218
|
+
if (local &&
|
|
1219
|
+
messageWithContext.modernRuntimeMessage &&
|
|
1220
|
+
message.type !== ContainerMessageType.ChunkedOp) {
|
|
1221
|
+
localOpMetadata = this.pendingStateManager.processPendingLocalMessage(messageWithContext.message);
|
|
1151
1222
|
}
|
|
1152
1223
|
// If there are no more pending messages after processing a local message,
|
|
1153
1224
|
// the document is no longer dirty.
|
|
1154
1225
|
if (!this.hasPendingMessages()) {
|
|
1155
1226
|
this.updateDocumentDirtyState(false);
|
|
1156
1227
|
}
|
|
1157
|
-
this.validateAndProcessRuntimeMessage(
|
|
1158
|
-
this.emit("op", message, modernRuntimeMessage);
|
|
1228
|
+
this.validateAndProcessRuntimeMessage(messageWithContext, localOpMetadata);
|
|
1229
|
+
this.emit("op", message, messageWithContext.modernRuntimeMessage);
|
|
1159
1230
|
this.scheduleManager.afterOpProcessing(undefined, message);
|
|
1160
1231
|
if (local) {
|
|
1161
1232
|
// If we have processed a local op, this means that the container is
|
|
@@ -1170,29 +1241,29 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1170
1241
|
}
|
|
1171
1242
|
}
|
|
1172
1243
|
/**
|
|
1173
|
-
* Assuming the given message is also a
|
|
1244
|
+
* Assuming the given message is also a TypedContainerRuntimeMessage,
|
|
1174
1245
|
* 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
|
|
1246
|
+
* Throws a DataProcessingError if the message looks like but doesn't conform to a known TypedContainerRuntimeMessage type.
|
|
1176
1247
|
*/
|
|
1177
|
-
validateAndProcessRuntimeMessage(
|
|
1178
|
-
//
|
|
1179
|
-
const {
|
|
1180
|
-
switch (
|
|
1248
|
+
validateAndProcessRuntimeMessage(messageWithContext, localOpMetadata) {
|
|
1249
|
+
// TODO: destructure message and modernRuntimeMessage once using typescript 5.2.2+
|
|
1250
|
+
const { local } = messageWithContext;
|
|
1251
|
+
switch (messageWithContext.message.type) {
|
|
1181
1252
|
case ContainerMessageType.Attach:
|
|
1182
|
-
this.dataStores.processAttachMessage(message, local);
|
|
1253
|
+
this.dataStores.processAttachMessage(messageWithContext.message, local);
|
|
1183
1254
|
break;
|
|
1184
1255
|
case ContainerMessageType.Alias:
|
|
1185
|
-
this.dataStores.processAliasMessage(message, localOpMetadata, local);
|
|
1256
|
+
this.dataStores.processAliasMessage(messageWithContext.message, localOpMetadata, local);
|
|
1186
1257
|
break;
|
|
1187
1258
|
case ContainerMessageType.FluidDataStoreOp:
|
|
1188
|
-
this.dataStores.processFluidDataStoreOp(message, local, localOpMetadata);
|
|
1259
|
+
this.dataStores.processFluidDataStoreOp(messageWithContext.message, local, localOpMetadata);
|
|
1189
1260
|
break;
|
|
1190
1261
|
case ContainerMessageType.BlobAttach:
|
|
1191
|
-
this.blobManager.processBlobAttachOp(message, local);
|
|
1262
|
+
this.blobManager.processBlobAttachOp(messageWithContext.message, local);
|
|
1192
1263
|
break;
|
|
1193
1264
|
case ContainerMessageType.IdAllocation:
|
|
1194
1265
|
assert(this.idCompressor !== undefined, 0x67c /* IdCompressor should be defined if enabled */);
|
|
1195
|
-
this.idCompressor.finalizeCreationRange(message.contents);
|
|
1266
|
+
this.idCompressor.finalizeCreationRange(messageWithContext.message.contents);
|
|
1196
1267
|
break;
|
|
1197
1268
|
case ContainerMessageType.ChunkedOp:
|
|
1198
1269
|
case ContainerMessageType.Rejoin:
|
|
@@ -1200,11 +1271,12 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1200
1271
|
default: {
|
|
1201
1272
|
// If we didn't necessarily expect a runtime message type, then no worries - just return
|
|
1202
1273
|
// e.g. this case applies to system ops, or legacy ops that would have fallen into the above cases anyway.
|
|
1203
|
-
if (!
|
|
1274
|
+
if (!messageWithContext.modernRuntimeMessage) {
|
|
1204
1275
|
return;
|
|
1205
1276
|
}
|
|
1206
|
-
const compatBehavior = compatDetails?.behavior;
|
|
1207
|
-
if (!compatBehaviorAllowsMessageType(
|
|
1277
|
+
const compatBehavior = messageWithContext.message.compatDetails?.behavior;
|
|
1278
|
+
if (!compatBehaviorAllowsMessageType(messageWithContext.message.type, compatBehavior)) {
|
|
1279
|
+
const { message } = messageWithContext;
|
|
1208
1280
|
const error = DataProcessingError.create(
|
|
1209
1281
|
// Former assert 0x3ce
|
|
1210
1282
|
"Runtime message of unknown type", "OpProcessing", message, {
|
|
@@ -1280,8 +1352,9 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1280
1352
|
* Returns the runtime of the data store.
|
|
1281
1353
|
* @param id - Id supplied during creating the data store.
|
|
1282
1354
|
* @param wait - True if you want to wait for it.
|
|
1283
|
-
* @deprecated
|
|
1355
|
+
* @deprecated Use getAliasedDataStoreEntryPoint instead to get an aliased data store's entry point.
|
|
1284
1356
|
*/
|
|
1357
|
+
// eslint-disable-next-line import/no-deprecated
|
|
1285
1358
|
async getRootDataStore(id, wait = true) {
|
|
1286
1359
|
return this.getRootDataStoreChannel(id, wait);
|
|
1287
1360
|
}
|
|
@@ -1578,7 +1651,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1578
1651
|
this.dataStores.updateUnusedRoutes(dataStoreRoutes);
|
|
1579
1652
|
}
|
|
1580
1653
|
/**
|
|
1581
|
-
* @deprecated
|
|
1654
|
+
* @deprecated Replaced by deleteSweepReadyNodes.
|
|
1582
1655
|
*/
|
|
1583
1656
|
deleteUnusedNodes(unusedRoutes) {
|
|
1584
1657
|
throw new Error("deleteUnusedRoutes should not be called");
|
|
@@ -1700,16 +1773,13 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1700
1773
|
},
|
|
1701
1774
|
});
|
|
1702
1775
|
assert(this.outbox.isEmpty, 0x3d1 /* Can't trigger summary in the middle of a batch */);
|
|
1776
|
+
// We close the summarizer and download a new snapshot and reload the container
|
|
1703
1777
|
let latestSnapshotVersionId;
|
|
1704
|
-
if (refreshLatestAck) {
|
|
1705
|
-
|
|
1778
|
+
if (refreshLatestAck === true) {
|
|
1779
|
+
return this.prefetchLatestSummaryThenClose(createChildLogger({
|
|
1706
1780
|
logger: summaryNumberLogger,
|
|
1707
1781
|
properties: { all: { safeSummary: true } },
|
|
1708
1782
|
}));
|
|
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
1783
|
}
|
|
1714
1784
|
// If there are pending (unacked ops), the summary will not be eventual consistent and it may even be
|
|
1715
1785
|
// incorrect. So, wait for the container to be saved with a timeout. If the container is not saved
|
|
@@ -2233,34 +2303,31 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
2233
2303
|
}
|
|
2234
2304
|
reSubmit(message) {
|
|
2235
2305
|
// Need to parse from string for back-compat
|
|
2236
|
-
const containerRuntimeMessage = this.
|
|
2306
|
+
const containerRuntimeMessage = this.parseLocalOpContent(message.content);
|
|
2237
2307
|
this.reSubmitCore(containerRuntimeMessage, message.localOpMetadata, message.opMetadata);
|
|
2238
2308
|
}
|
|
2239
2309
|
/**
|
|
2240
2310
|
* Finds the right store and asks it to resubmit the message. This typically happens when we
|
|
2241
2311
|
* reconnect and there are pending messages.
|
|
2242
|
-
* @param message - The original
|
|
2312
|
+
* @param message - The original LocalContainerRuntimeMessage.
|
|
2243
2313
|
* @param localOpMetadata - The local metadata associated with the original message.
|
|
2244
2314
|
*/
|
|
2245
2315
|
reSubmitCore(message, localOpMetadata, opMetadata) {
|
|
2246
|
-
const contents = message.contents;
|
|
2247
2316
|
switch (message.type) {
|
|
2248
2317
|
case ContainerMessageType.FluidDataStoreOp:
|
|
2249
2318
|
// For Operations, call resubmitDataStoreOp which will find the right store
|
|
2250
2319
|
// and trigger resubmission on it.
|
|
2251
|
-
this.dataStores.resubmitDataStoreOp(contents, localOpMetadata);
|
|
2320
|
+
this.dataStores.resubmitDataStoreOp(message.contents, localOpMetadata);
|
|
2252
2321
|
break;
|
|
2253
2322
|
case ContainerMessageType.Attach:
|
|
2254
2323
|
case ContainerMessageType.Alias:
|
|
2255
2324
|
this.submit(message, localOpMetadata);
|
|
2256
2325
|
break;
|
|
2257
|
-
case ContainerMessageType.IdAllocation:
|
|
2258
|
-
|
|
2259
|
-
if (contents.stashedState !== undefined) {
|
|
2260
|
-
delete contents.stashedState;
|
|
2261
|
-
}
|
|
2326
|
+
case ContainerMessageType.IdAllocation: {
|
|
2327
|
+
prepareLocalContainerRuntimeIdAllocationMessageForTransit(message);
|
|
2262
2328
|
this.submit(message, localOpMetadata);
|
|
2263
2329
|
break;
|
|
2330
|
+
}
|
|
2264
2331
|
case ContainerMessageType.ChunkedOp:
|
|
2265
2332
|
throw new Error(`chunkedOp not expected here`);
|
|
2266
2333
|
case ContainerMessageType.BlobAttach:
|
|
@@ -2294,7 +2361,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
2294
2361
|
}
|
|
2295
2362
|
rollback(content, localOpMetadata) {
|
|
2296
2363
|
// Need to parse from string for back-compat
|
|
2297
|
-
const { type, contents } = this.
|
|
2364
|
+
const { type, contents } = this.parseLocalOpContent(content);
|
|
2298
2365
|
switch (type) {
|
|
2299
2366
|
case ContainerMessageType.FluidDataStoreOp:
|
|
2300
2367
|
// For operations, call rollbackDataStoreOp which will find the right store
|
|
@@ -2306,17 +2373,6 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
2306
2373
|
throw new Error(`Can't rollback ${type}`);
|
|
2307
2374
|
}
|
|
2308
2375
|
}
|
|
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
2376
|
/** Implementation of ISummarizerInternalsProvider.refreshLatestSummaryAck */
|
|
2321
2377
|
async refreshLatestSummaryAck(options) {
|
|
2322
2378
|
const { proposalHandle, ackHandle, summaryRefSeq, summaryLogger } = options;
|
|
@@ -2331,11 +2387,11 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
2331
2387
|
* and then close as the current main client is likely to be re-elected as the parent summarizer again.
|
|
2332
2388
|
*/
|
|
2333
2389
|
if (!result.isSummaryTracked && result.isSummaryNewer) {
|
|
2334
|
-
const fetchResult = await this.
|
|
2390
|
+
const fetchResult = await this.fetchLatestSnapshotFromStorage(summaryLogger, {
|
|
2335
2391
|
eventName: "RefreshLatestSummaryAckFetch",
|
|
2336
2392
|
ackHandle,
|
|
2337
2393
|
targetSequenceNumber: summaryRefSeq,
|
|
2338
|
-
}, readAndParseBlob
|
|
2394
|
+
}, readAndParseBlob);
|
|
2339
2395
|
/**
|
|
2340
2396
|
* If the fetched snapshot is older than the one for which the ack was received, close the container.
|
|
2341
2397
|
* This should never happen because an ack should be sent after the latest summary is updated in the server.
|
|
@@ -2362,41 +2418,42 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
2362
2418
|
await this.garbageCollector.refreshLatestSummary(result);
|
|
2363
2419
|
}
|
|
2364
2420
|
/**
|
|
2365
|
-
* Fetches the latest snapshot from storage
|
|
2366
|
-
*
|
|
2421
|
+
* Fetches the latest snapshot from storage to refresh the cache as a performance optimization and closes the
|
|
2422
|
+
* summarizer to reload from new state.
|
|
2367
2423
|
* @param summaryLogger - logger to use when fetching snapshot from storage
|
|
2368
|
-
* @returns
|
|
2424
|
+
* @returns a generic summarization error
|
|
2369
2425
|
*/
|
|
2370
|
-
async
|
|
2426
|
+
async prefetchLatestSummaryThenClose(summaryLogger) {
|
|
2371
2427
|
const readAndParseBlob = async (id) => readAndParse(this.storage, id);
|
|
2372
|
-
|
|
2428
|
+
// This is a performance optimization as the same parent is likely to be elected again, and would use its
|
|
2429
|
+
// cache to fetch the snapshot instead of the network.
|
|
2430
|
+
await this.fetchLatestSnapshotFromStorage(summaryLogger, {
|
|
2373
2431
|
eventName: "RefreshLatestSummaryFromServerFetch",
|
|
2374
|
-
}, readAndParseBlob
|
|
2432
|
+
}, readAndParseBlob);
|
|
2375
2433
|
await this.closeStaleSummarizer("RefreshLatestSummaryFromServerFetch");
|
|
2376
|
-
return {
|
|
2434
|
+
return {
|
|
2435
|
+
stage: "base",
|
|
2436
|
+
error: "summary state stale - Unsupported option 'refreshLatestAck'",
|
|
2437
|
+
referenceSequenceNumber: this.deltaManager.lastSequenceNumber,
|
|
2438
|
+
minimumSequenceNumber: this.deltaManager.minimumSequenceNumber,
|
|
2439
|
+
};
|
|
2377
2440
|
}
|
|
2378
2441
|
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
2442
|
// Delay before restarting summarizer to prevent the summarizer from restarting too frequently.
|
|
2386
2443
|
await delay(this.closeSummarizerDelayMs);
|
|
2387
2444
|
this._summarizer?.stop("latestSummaryStateStale");
|
|
2388
2445
|
this.disposeFn();
|
|
2389
2446
|
}
|
|
2390
2447
|
/**
|
|
2391
|
-
* Downloads
|
|
2448
|
+
* Downloads the latest snapshot from storage.
|
|
2392
2449
|
* By default, it also closes the container after downloading the snapshot. However, this may be
|
|
2393
2450
|
* overridden via options.
|
|
2394
2451
|
*/
|
|
2395
|
-
async
|
|
2452
|
+
async fetchLatestSnapshotFromStorage(logger, event, readAndParseBlob) {
|
|
2396
2453
|
return PerformanceEvent.timedExecAsync(logger, event, async (perfEvent) => {
|
|
2397
2454
|
const stats = {};
|
|
2398
2455
|
const trace = Trace.start();
|
|
2399
|
-
const versions = await this.storage.getVersions(
|
|
2456
|
+
const versions = await this.storage.getVersions(null, 1, "prefetchLatestSummaryBeforeClose", FetchSource.noCache);
|
|
2400
2457
|
assert(!!versions && !!versions[0], 0x137 /* "Failed to get version from storage" */);
|
|
2401
2458
|
stats.getVersionDuration = trace.trace().duration;
|
|
2402
2459
|
const maybeSnapshot = await this.storage.getSnapshotTree(versions[0]);
|
|
@@ -2424,15 +2481,17 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
2424
2481
|
if (this._orderSequentiallyCalls !== 0) {
|
|
2425
2482
|
throw new UsageError("can't get state during orderSequentially");
|
|
2426
2483
|
}
|
|
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
2484
|
// Flush pending batch.
|
|
2433
2485
|
// getPendingLocalState() is only exposed through Container.closeAndGetPendingLocalState(), so it's safe
|
|
2434
2486
|
// to close current batch.
|
|
2435
2487
|
this.flush();
|
|
2488
|
+
const pendingAttachmentBlobs = waitBlobsToAttach
|
|
2489
|
+
? await this.blobManager.attachAndGetPendingBlobs()
|
|
2490
|
+
: undefined;
|
|
2491
|
+
const pending = this.pendingStateManager.getLocalState();
|
|
2492
|
+
if (!pendingAttachmentBlobs && !this.hasPendingMessages()) {
|
|
2493
|
+
return; // no pending state to save
|
|
2494
|
+
}
|
|
2436
2495
|
const pendingState = {
|
|
2437
2496
|
pending,
|
|
2438
2497
|
pendingAttachmentBlobs,
|
|
@@ -2473,29 +2532,11 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
2473
2532
|
}
|
|
2474
2533
|
}
|
|
2475
2534
|
/**
|
|
2476
|
-
*
|
|
2477
|
-
|
|
2478
|
-
|
|
2479
|
-
formRequestSummarizerFn(loaderRouter) {
|
|
2535
|
+
* Forms a function that will create and retrieve a Summarizer.
|
|
2536
|
+
*/
|
|
2537
|
+
formCreateSummarizerFn(loader) {
|
|
2480
2538
|
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;
|
|
2539
|
+
return createSummarizer(loader, `/${summarizerRequestUrl}`);
|
|
2499
2540
|
};
|
|
2500
2541
|
}
|
|
2501
2542
|
validateSummaryHeuristicConfiguration(configuration) {
|
|
@@ -2514,26 +2555,4 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
2514
2555
|
return killSwitch !== true && this.runtimeOptions.enableGroupedBatching;
|
|
2515
2556
|
}
|
|
2516
2557
|
}
|
|
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
2558
|
//# sourceMappingURL=containerRuntime.js.map
|