@fluidframework/container-runtime 2.0.0-dev.2.3.0.115467 → 2.0.0-dev.3.1.0.125672
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/.eslintrc.js +21 -10
- package/.mocharc.js +2 -2
- package/api-extractor.json +2 -2
- package/dist/batchTracker.d.ts +1 -2
- package/dist/batchTracker.d.ts.map +1 -1
- package/dist/batchTracker.js +2 -1
- package/dist/batchTracker.js.map +1 -1
- package/dist/blobManager.d.ts +53 -34
- package/dist/blobManager.d.ts.map +1 -1
- package/dist/blobManager.js +236 -124
- package/dist/blobManager.js.map +1 -1
- package/dist/connectionTelemetry.d.ts.map +1 -1
- package/dist/connectionTelemetry.js +11 -9
- package/dist/connectionTelemetry.js.map +1 -1
- package/dist/containerHandleContext.d.ts.map +1 -1
- package/dist/containerHandleContext.js +3 -1
- package/dist/containerHandleContext.js.map +1 -1
- package/dist/containerRuntime.d.ts +95 -46
- package/dist/containerRuntime.d.ts.map +1 -1
- package/dist/containerRuntime.js +288 -135
- package/dist/containerRuntime.js.map +1 -1
- package/dist/dataStore.d.ts.map +1 -1
- package/dist/dataStore.js +11 -9
- package/dist/dataStore.js.map +1 -1
- package/dist/dataStoreContext.d.ts +2 -1
- package/dist/dataStoreContext.d.ts.map +1 -1
- package/dist/dataStoreContext.js +38 -21
- package/dist/dataStoreContext.js.map +1 -1
- package/dist/dataStoreContexts.d.ts.map +1 -1
- package/dist/dataStoreContexts.js +7 -3
- package/dist/dataStoreContexts.js.map +1 -1
- package/dist/dataStoreRegistry.d.ts.map +1 -1
- package/dist/dataStoreRegistry.js +3 -1
- package/dist/dataStoreRegistry.js.map +1 -1
- package/dist/dataStores.d.ts +12 -9
- package/dist/dataStores.d.ts.map +1 -1
- package/dist/dataStores.js +68 -46
- package/dist/dataStores.js.map +1 -1
- package/dist/deltaScheduler.d.ts.map +1 -1
- package/dist/deltaScheduler.js +8 -3
- package/dist/deltaScheduler.js.map +1 -1
- package/dist/garbageCollection.d.ts +50 -26
- package/dist/garbageCollection.d.ts.map +1 -1
- package/dist/garbageCollection.js +348 -196
- package/dist/garbageCollection.js.map +1 -1
- package/dist/garbageCollectionConstants.d.ts +7 -3
- package/dist/garbageCollectionConstants.d.ts.map +1 -1
- package/dist/garbageCollectionConstants.js +10 -8
- package/dist/garbageCollectionConstants.js.map +1 -1
- package/dist/garbageCollectionHelpers.d.ts +15 -0
- package/dist/garbageCollectionHelpers.d.ts.map +1 -0
- package/dist/garbageCollectionHelpers.js +27 -0
- package/dist/garbageCollectionHelpers.js.map +1 -0
- package/dist/gcSweepReadyUsageDetection.d.ts +5 -5
- package/dist/gcSweepReadyUsageDetection.d.ts.map +1 -1
- package/dist/gcSweepReadyUsageDetection.js +14 -10
- package/dist/gcSweepReadyUsageDetection.js.map +1 -1
- package/dist/index.d.ts +3 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -5
- package/dist/index.js.map +1 -1
- package/dist/opLifecycle/batchManager.d.ts +13 -1
- package/dist/opLifecycle/batchManager.d.ts.map +1 -1
- package/dist/opLifecycle/batchManager.js +48 -7
- package/dist/opLifecycle/batchManager.js.map +1 -1
- package/dist/opLifecycle/definitions.d.ts +25 -1
- package/dist/opLifecycle/definitions.d.ts.map +1 -1
- package/dist/opLifecycle/definitions.js.map +1 -1
- package/dist/opLifecycle/index.d.ts +2 -2
- package/dist/opLifecycle/index.d.ts.map +1 -1
- package/dist/opLifecycle/index.js +2 -1
- package/dist/opLifecycle/index.js.map +1 -1
- package/dist/opLifecycle/opCompressor.d.ts +1 -1
- package/dist/opLifecycle/opCompressor.d.ts.map +1 -1
- package/dist/opLifecycle/opCompressor.js +24 -10
- package/dist/opLifecycle/opCompressor.js.map +1 -1
- package/dist/opLifecycle/opDecompressor.d.ts +2 -1
- package/dist/opLifecycle/opDecompressor.d.ts.map +1 -1
- package/dist/opLifecycle/opDecompressor.js +33 -17
- package/dist/opLifecycle/opDecompressor.js.map +1 -1
- package/dist/opLifecycle/opSplitter.d.ts +34 -2
- package/dist/opLifecycle/opSplitter.d.ts.map +1 -1
- package/dist/opLifecycle/opSplitter.js +117 -5
- package/dist/opLifecycle/opSplitter.js.map +1 -1
- package/dist/opLifecycle/outbox.d.ts +5 -0
- package/dist/opLifecycle/outbox.d.ts.map +1 -1
- package/dist/opLifecycle/outbox.js +38 -27
- package/dist/opLifecycle/outbox.js.map +1 -1
- package/dist/opLifecycle/remoteMessageProcessor.d.ts.map +1 -1
- package/dist/opLifecycle/remoteMessageProcessor.js +17 -2
- package/dist/opLifecycle/remoteMessageProcessor.js.map +1 -1
- package/dist/opProperties.d.ts.map +1 -1
- package/dist/opProperties.js +1 -3
- package/dist/opProperties.js.map +1 -1
- package/dist/orderedClientElection.d.ts.map +1 -1
- package/dist/orderedClientElection.js +10 -4
- package/dist/orderedClientElection.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 -13
- package/dist/pendingStateManager.d.ts.map +1 -1
- package/dist/pendingStateManager.js +134 -161
- package/dist/pendingStateManager.js.map +1 -1
- package/dist/runWhileConnectedCoordinator.d.ts.map +1 -1
- package/dist/runWhileConnectedCoordinator.js.map +1 -1
- package/dist/runningSummarizer.d.ts.map +1 -1
- package/dist/runningSummarizer.js +34 -22
- package/dist/runningSummarizer.js.map +1 -1
- package/dist/scheduleManager.d.ts +0 -1
- package/dist/scheduleManager.d.ts.map +1 -1
- package/dist/scheduleManager.js +11 -21
- package/dist/scheduleManager.js.map +1 -1
- package/dist/serializedSnapshotStorage.d.ts.map +1 -1
- package/dist/serializedSnapshotStorage.js +3 -1
- package/dist/serializedSnapshotStorage.js.map +1 -1
- package/dist/summarizer.d.ts +2 -3
- package/dist/summarizer.d.ts.map +1 -1
- package/dist/summarizer.js +39 -18
- package/dist/summarizer.js.map +1 -1
- package/dist/summarizerClientElection.d.ts +1 -2
- package/dist/summarizerClientElection.d.ts.map +1 -1
- package/dist/summarizerClientElection.js +3 -30
- package/dist/summarizerClientElection.js.map +1 -1
- package/dist/summarizerHandle.d.ts.map +1 -1
- package/dist/summarizerHandle.js.map +1 -1
- package/dist/summarizerHeuristics.d.ts.map +1 -1
- package/dist/summarizerHeuristics.js +6 -9
- package/dist/summarizerHeuristics.js.map +1 -1
- package/dist/summarizerTypes.d.ts +22 -25
- package/dist/summarizerTypes.d.ts.map +1 -1
- package/dist/summarizerTypes.js.map +1 -1
- package/dist/summaryCollection.d.ts.map +1 -1
- package/dist/summaryCollection.js +18 -8
- package/dist/summaryCollection.js.map +1 -1
- package/dist/summaryFormat.d.ts.map +1 -1
- package/dist/summaryFormat.js +18 -11
- package/dist/summaryFormat.js.map +1 -1
- package/dist/summaryGenerator.d.ts.map +1 -1
- package/dist/summaryGenerator.js +32 -14
- package/dist/summaryGenerator.js.map +1 -1
- package/dist/summaryManager.d.ts.map +1 -1
- package/dist/summaryManager.js +21 -9
- package/dist/summaryManager.js.map +1 -1
- package/dist/throttler.d.ts +2 -2
- package/dist/throttler.d.ts.map +1 -1
- package/dist/throttler.js +4 -4
- package/dist/throttler.js.map +1 -1
- package/garbageCollection.md +15 -2
- package/lib/batchTracker.d.ts +1 -2
- package/lib/batchTracker.d.ts.map +1 -1
- package/lib/batchTracker.js +2 -1
- package/lib/batchTracker.js.map +1 -1
- package/lib/blobManager.d.ts +53 -34
- package/lib/blobManager.d.ts.map +1 -1
- package/lib/blobManager.js +239 -127
- package/lib/blobManager.js.map +1 -1
- package/lib/connectionTelemetry.d.ts.map +1 -1
- package/lib/connectionTelemetry.js +11 -9
- package/lib/connectionTelemetry.js.map +1 -1
- package/lib/containerHandleContext.d.ts.map +1 -1
- package/lib/containerHandleContext.js +3 -1
- package/lib/containerHandleContext.js.map +1 -1
- package/lib/containerRuntime.d.ts +95 -46
- package/lib/containerRuntime.d.ts.map +1 -1
- package/lib/containerRuntime.js +291 -138
- package/lib/containerRuntime.js.map +1 -1
- package/lib/dataStore.d.ts.map +1 -1
- package/lib/dataStore.js +11 -9
- package/lib/dataStore.js.map +1 -1
- package/lib/dataStoreContext.d.ts +2 -1
- package/lib/dataStoreContext.d.ts.map +1 -1
- package/lib/dataStoreContext.js +40 -23
- package/lib/dataStoreContext.js.map +1 -1
- package/lib/dataStoreContexts.d.ts.map +1 -1
- package/lib/dataStoreContexts.js +7 -3
- package/lib/dataStoreContexts.js.map +1 -1
- package/lib/dataStoreRegistry.d.ts.map +1 -1
- package/lib/dataStoreRegistry.js +3 -1
- package/lib/dataStoreRegistry.js.map +1 -1
- package/lib/dataStores.d.ts +12 -9
- package/lib/dataStores.d.ts.map +1 -1
- package/lib/dataStores.js +74 -52
- package/lib/dataStores.js.map +1 -1
- package/lib/deltaScheduler.d.ts.map +1 -1
- package/lib/deltaScheduler.js +9 -4
- package/lib/deltaScheduler.js.map +1 -1
- package/lib/garbageCollection.d.ts +50 -26
- package/lib/garbageCollection.d.ts.map +1 -1
- package/lib/garbageCollection.js +347 -195
- package/lib/garbageCollection.js.map +1 -1
- package/lib/garbageCollectionConstants.d.ts +7 -3
- package/lib/garbageCollectionConstants.d.ts.map +1 -1
- package/lib/garbageCollectionConstants.js +9 -7
- package/lib/garbageCollectionConstants.js.map +1 -1
- package/lib/garbageCollectionHelpers.d.ts +15 -0
- package/lib/garbageCollectionHelpers.d.ts.map +1 -0
- package/lib/garbageCollectionHelpers.js +23 -0
- package/lib/garbageCollectionHelpers.js.map +1 -0
- package/lib/gcSweepReadyUsageDetection.d.ts +5 -5
- package/lib/gcSweepReadyUsageDetection.d.ts.map +1 -1
- package/lib/gcSweepReadyUsageDetection.js +14 -10
- package/lib/gcSweepReadyUsageDetection.js.map +1 -1
- package/lib/index.d.ts +3 -4
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +2 -3
- package/lib/index.js.map +1 -1
- package/lib/opLifecycle/batchManager.d.ts +13 -1
- package/lib/opLifecycle/batchManager.d.ts.map +1 -1
- package/lib/opLifecycle/batchManager.js +48 -7
- package/lib/opLifecycle/batchManager.js.map +1 -1
- package/lib/opLifecycle/definitions.d.ts +25 -1
- package/lib/opLifecycle/definitions.d.ts.map +1 -1
- package/lib/opLifecycle/definitions.js.map +1 -1
- package/lib/opLifecycle/index.d.ts +2 -2
- package/lib/opLifecycle/index.d.ts.map +1 -1
- package/lib/opLifecycle/index.js +1 -1
- package/lib/opLifecycle/index.js.map +1 -1
- package/lib/opLifecycle/opCompressor.d.ts +1 -1
- package/lib/opLifecycle/opCompressor.d.ts.map +1 -1
- package/lib/opLifecycle/opCompressor.js +24 -10
- package/lib/opLifecycle/opCompressor.js.map +1 -1
- package/lib/opLifecycle/opDecompressor.d.ts +2 -1
- package/lib/opLifecycle/opDecompressor.d.ts.map +1 -1
- package/lib/opLifecycle/opDecompressor.js +33 -17
- package/lib/opLifecycle/opDecompressor.js.map +1 -1
- package/lib/opLifecycle/opSplitter.d.ts +34 -2
- package/lib/opLifecycle/opSplitter.d.ts.map +1 -1
- package/lib/opLifecycle/opSplitter.js +116 -5
- package/lib/opLifecycle/opSplitter.js.map +1 -1
- package/lib/opLifecycle/outbox.d.ts +5 -0
- package/lib/opLifecycle/outbox.d.ts.map +1 -1
- package/lib/opLifecycle/outbox.js +38 -27
- package/lib/opLifecycle/outbox.js.map +1 -1
- package/lib/opLifecycle/remoteMessageProcessor.d.ts.map +1 -1
- package/lib/opLifecycle/remoteMessageProcessor.js +17 -2
- package/lib/opLifecycle/remoteMessageProcessor.js.map +1 -1
- package/lib/opProperties.d.ts.map +1 -1
- package/lib/opProperties.js +1 -3
- package/lib/opProperties.js.map +1 -1
- package/lib/orderedClientElection.d.ts.map +1 -1
- package/lib/orderedClientElection.js +10 -4
- package/lib/orderedClientElection.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 -13
- package/lib/pendingStateManager.d.ts.map +1 -1
- package/lib/pendingStateManager.js +134 -161
- package/lib/pendingStateManager.js.map +1 -1
- package/lib/runWhileConnectedCoordinator.d.ts.map +1 -1
- package/lib/runWhileConnectedCoordinator.js.map +1 -1
- package/lib/runningSummarizer.d.ts.map +1 -1
- package/lib/runningSummarizer.js +35 -23
- package/lib/runningSummarizer.js.map +1 -1
- package/lib/scheduleManager.d.ts +0 -1
- package/lib/scheduleManager.d.ts.map +1 -1
- package/lib/scheduleManager.js +11 -21
- package/lib/scheduleManager.js.map +1 -1
- package/lib/serializedSnapshotStorage.d.ts.map +1 -1
- package/lib/serializedSnapshotStorage.js +3 -1
- package/lib/serializedSnapshotStorage.js.map +1 -1
- package/lib/summarizer.d.ts +2 -3
- package/lib/summarizer.d.ts.map +1 -1
- package/lib/summarizer.js +39 -18
- package/lib/summarizer.js.map +1 -1
- package/lib/summarizerClientElection.d.ts +1 -2
- package/lib/summarizerClientElection.d.ts.map +1 -1
- package/lib/summarizerClientElection.js +3 -30
- package/lib/summarizerClientElection.js.map +1 -1
- package/lib/summarizerHandle.d.ts.map +1 -1
- package/lib/summarizerHandle.js.map +1 -1
- package/lib/summarizerHeuristics.d.ts.map +1 -1
- package/lib/summarizerHeuristics.js +6 -9
- package/lib/summarizerHeuristics.js.map +1 -1
- package/lib/summarizerTypes.d.ts +22 -25
- package/lib/summarizerTypes.d.ts.map +1 -1
- package/lib/summarizerTypes.js.map +1 -1
- package/lib/summaryCollection.d.ts.map +1 -1
- package/lib/summaryCollection.js +18 -8
- package/lib/summaryCollection.js.map +1 -1
- package/lib/summaryFormat.d.ts.map +1 -1
- package/lib/summaryFormat.js +20 -13
- package/lib/summaryFormat.js.map +1 -1
- package/lib/summaryGenerator.d.ts.map +1 -1
- package/lib/summaryGenerator.js +32 -14
- package/lib/summaryGenerator.js.map +1 -1
- package/lib/summaryManager.d.ts.map +1 -1
- package/lib/summaryManager.js +21 -9
- package/lib/summaryManager.js.map +1 -1
- package/lib/throttler.d.ts +2 -2
- package/lib/throttler.d.ts.map +1 -1
- package/lib/throttler.js +4 -4
- package/lib/throttler.js.map +1 -1
- package/package.json +27 -24
- package/prettier.config.cjs +1 -1
- package/src/batchTracker.ts +55 -50
- package/src/blobManager.ts +799 -593
- package/src/connectionTelemetry.ts +280 -249
- package/src/containerHandleContext.ts +27 -29
- package/src/containerRuntime.ts +3123 -2793
- package/src/dataStore.ts +172 -159
- package/src/dataStoreContext.ts +1048 -991
- package/src/dataStoreContexts.ts +178 -161
- package/src/dataStoreRegistry.ts +25 -20
- package/src/dataStores.ts +784 -711
- package/src/deltaScheduler.ts +158 -150
- package/src/garbageCollection.ts +1795 -1546
- package/src/garbageCollectionConstants.ts +10 -7
- package/src/garbageCollectionHelpers.ts +37 -0
- package/src/gcSweepReadyUsageDetection.ts +89 -83
- package/src/index.ts +67 -69
- package/src/opLifecycle/batchManager.ts +148 -86
- package/src/opLifecycle/definitions.ts +45 -19
- package/src/opLifecycle/index.ts +6 -5
- package/src/opLifecycle/opCompressor.ts +57 -39
- package/src/opLifecycle/opDecompressor.ts +104 -64
- package/src/opLifecycle/opSplitter.ts +226 -66
- package/src/opLifecycle/outbox.ts +206 -182
- package/src/opLifecycle/remoteMessageProcessor.ts +63 -47
- package/src/opProperties.ts +11 -9
- package/src/orderedClientElection.ts +489 -457
- package/src/packageVersion.ts +1 -1
- package/src/pendingStateManager.ts +379 -381
- package/src/runWhileConnectedCoordinator.ts +78 -71
- package/src/runningSummarizer.ts +619 -582
- package/src/scheduleManager.ts +299 -280
- package/src/serializedSnapshotStorage.ts +116 -111
- package/src/summarizer.ts +417 -381
- package/src/summarizerClientElection.ts +107 -129
- package/src/summarizerHandle.ts +11 -9
- package/src/summarizerHeuristics.ts +183 -186
- package/src/summarizerTypes.ts +344 -333
- package/src/summaryCollection.ts +378 -349
- package/src/summaryFormat.ts +146 -127
- package/src/summaryGenerator.ts +464 -406
- package/src/summaryManager.ts +377 -348
- package/src/throttler.ts +131 -122
- package/tsconfig.esnext.json +6 -6
- package/tsconfig.json +9 -13
package/lib/containerRuntime.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { AttachState, LoaderHeader, } from "@fluidframework/container-definitions";
|
|
2
|
-
import { assert, Trace, TypedEventEmitter, unreachableCase
|
|
2
|
+
import { assert, Trace, TypedEventEmitter, unreachableCase } from "@fluidframework/common-utils";
|
|
3
3
|
import { ChildLogger, raiseConnectedEvent, PerformanceEvent, TaggedLoggerAdapter, loggerToMonitoringContext, wrapError, } from "@fluidframework/telemetry-utils";
|
|
4
4
|
import { DriverHeader, FetchSource, } from "@fluidframework/driver-definitions";
|
|
5
5
|
import { readAndParse } from "@fluidframework/driver-utils";
|
|
6
6
|
import { DataCorruptionError, DataProcessingError, GenericError, UsageError, } from "@fluidframework/container-utils";
|
|
7
7
|
import { MessageType, SummaryType, } from "@fluidframework/protocol-definitions";
|
|
8
|
-
import { FlushMode, channelsTreeName, } from "@fluidframework/runtime-definitions";
|
|
8
|
+
import { FlushMode, gcTreeKey, channelsTreeName, } from "@fluidframework/runtime-definitions";
|
|
9
9
|
import { addBlobToSummary, addSummarizeResultToSummary, addTreeToSummary, createRootSummarizerNodeWithGC, RequestParser, create404Response, exceptionToResponse, requestFluidObject, responseToException, seqFromTree, calculateStats, TelemetryContext, } from "@fluidframework/runtime-utils";
|
|
10
10
|
import { GCDataBuilder, trimLeadingAndTrailingSlashes } from "@fluidframework/garbage-collector";
|
|
11
11
|
import { v4 as uuid } from "uuid";
|
|
@@ -13,22 +13,21 @@ import { ContainerFluidHandleContext } from "./containerHandleContext";
|
|
|
13
13
|
import { FluidDataStoreRegistry } from "./dataStoreRegistry";
|
|
14
14
|
import { Summarizer } from "./summarizer";
|
|
15
15
|
import { SummaryManager } from "./summaryManager";
|
|
16
|
-
import { ReportOpPerfTelemetry
|
|
17
|
-
import { PendingStateManager
|
|
16
|
+
import { ReportOpPerfTelemetry } from "./connectionTelemetry";
|
|
17
|
+
import { PendingStateManager } from "./pendingStateManager";
|
|
18
18
|
import { pkgVersion } from "./packageVersion";
|
|
19
19
|
import { BlobManager } from "./blobManager";
|
|
20
20
|
import { DataStores, getSummaryForDatastores } from "./dataStores";
|
|
21
21
|
import { aliasBlobName, blobsTreeName, chunksBlobName, electedSummarizerBlobName, extractSummaryMetadataMessage, metadataBlobName, wrapSummaryInChannelsTree, } from "./summaryFormat";
|
|
22
22
|
import { SummaryCollection } from "./summaryCollection";
|
|
23
|
-
import { OrderedClientCollection, OrderedClientElection } from "./orderedClientElection";
|
|
23
|
+
import { OrderedClientCollection, OrderedClientElection, } from "./orderedClientElection";
|
|
24
24
|
import { SummarizerClientElection, summarizerClientType } from "./summarizerClientElection";
|
|
25
25
|
import { formExponentialFn, Throttler } from "./throttler";
|
|
26
26
|
import { RunWhileConnectedCoordinator } from "./runWhileConnectedCoordinator";
|
|
27
27
|
import { GarbageCollector, GCNodeType, } from "./garbageCollection";
|
|
28
|
-
import {
|
|
29
|
-
import { channelToDataStore, isDataStoreAliasMessage, } from "./dataStore";
|
|
28
|
+
import { channelToDataStore, isDataStoreAliasMessage } from "./dataStore";
|
|
30
29
|
import { BindBatchTracker } from "./batchTracker";
|
|
31
|
-
import { SerializedSnapshotStorage } from "./serializedSnapshotStorage";
|
|
30
|
+
import { SerializedSnapshotStorage, } from "./serializedSnapshotStorage";
|
|
32
31
|
import { ScheduleManager } from "./scheduleManager";
|
|
33
32
|
import { OpCompressor, OpDecompressor, Outbox, OpSplitter, RemoteMessageProcessor, } from "./opLifecycle";
|
|
34
33
|
export var ContainerMessageType;
|
|
@@ -56,7 +55,6 @@ export const DefaultSummaryConfiguration = {
|
|
|
56
55
|
maxAckWaitTime: 10 * 60 * 1000,
|
|
57
56
|
maxOpsSinceLastSummary: 7000,
|
|
58
57
|
initialSummarizerDelayMs: 5 * 1000,
|
|
59
|
-
summarizerClientElection: false,
|
|
60
58
|
nonRuntimeOpWeight: 0.1,
|
|
61
59
|
runtimeOpWeight: 1.0,
|
|
62
60
|
nonRuntimeHeuristicThreshold: 20,
|
|
@@ -76,6 +74,17 @@ export var RuntimeHeaders;
|
|
|
76
74
|
/** True if the request is coming from an IFluidHandle. */
|
|
77
75
|
RuntimeHeaders["viaHandle"] = "viaHandle";
|
|
78
76
|
})(RuntimeHeaders || (RuntimeHeaders = {}));
|
|
77
|
+
/** True if a tombstoned object should be returned without erroring */
|
|
78
|
+
export const AllowTombstoneRequestHeaderKey = "allowTombstone"; // Belongs in the enum above, but avoiding the breaking change
|
|
79
|
+
/** Tombstone error responses will have this header set to true */
|
|
80
|
+
export const TombstoneResponseHeaderKey = "isTombstoned";
|
|
81
|
+
/** Default values for Runtime Headers */
|
|
82
|
+
export const defaultRuntimeHeaderData = {
|
|
83
|
+
wait: true,
|
|
84
|
+
externalRequest: false,
|
|
85
|
+
viaHandle: false,
|
|
86
|
+
allowTombstone: false,
|
|
87
|
+
};
|
|
79
88
|
/**
|
|
80
89
|
* Available compression algorithms for op compression.
|
|
81
90
|
*/
|
|
@@ -125,8 +134,7 @@ export function getDeviceSpec() {
|
|
|
125
134
|
};
|
|
126
135
|
}
|
|
127
136
|
}
|
|
128
|
-
catch (_a) {
|
|
129
|
-
}
|
|
137
|
+
catch (_a) { }
|
|
130
138
|
return {};
|
|
131
139
|
}
|
|
132
140
|
/**
|
|
@@ -154,6 +162,14 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
154
162
|
this.flushMicroTaskExists = false;
|
|
155
163
|
this.savedOps = [];
|
|
156
164
|
this.consecutiveReconnects = 0;
|
|
165
|
+
this.ensureNoDataModelChangesCalls = 0;
|
|
166
|
+
/**
|
|
167
|
+
* Tracks the number of detected reentrant ops to report,
|
|
168
|
+
* in order to self-throttle the telemetry events.
|
|
169
|
+
*
|
|
170
|
+
* This should be removed as part of ADO:2322
|
|
171
|
+
*/
|
|
172
|
+
this.opReentryCallsToReport = 5;
|
|
157
173
|
this._disposed = false;
|
|
158
174
|
this.emitDirtyDocumentEvent = true;
|
|
159
175
|
this.defaultTelemetrySignalSampleCount = 100;
|
|
@@ -213,15 +229,21 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
213
229
|
this.nextSummaryNumber = loadSummaryNumber + 1;
|
|
214
230
|
this.messageAtLastSummary = metadata === null || metadata === void 0 ? void 0 : metadata.message;
|
|
215
231
|
this._connected = this.context.connected;
|
|
216
|
-
this.remoteMessageProcessor = new RemoteMessageProcessor(new OpSplitter(chunks), new OpDecompressor());
|
|
217
|
-
this.handleContext = new ContainerFluidHandleContext("", this);
|
|
218
232
|
this.mc = loggerToMonitoringContext(ChildLogger.create(this.logger, "ContainerRuntime"));
|
|
233
|
+
const opSplitter = new OpSplitter(chunks, this.context.submitBatchFn, this.mc.config.getBoolean("Fluid.ContainerRuntime.DisableCompressionChunking") === true
|
|
234
|
+
? Number.POSITIVE_INFINITY
|
|
235
|
+
: runtimeOptions.chunkSizeInBytes, runtimeOptions.maxBatchSizeInBytes, this.mc.logger);
|
|
236
|
+
this.remoteMessageProcessor = new RemoteMessageProcessor(opSplitter, new OpDecompressor());
|
|
237
|
+
this.handleContext = new ContainerFluidHandleContext("", this);
|
|
219
238
|
if (this.summaryConfiguration.state === "enabled") {
|
|
220
239
|
this.validateSummaryHeuristicConfiguration(this.summaryConfiguration);
|
|
221
240
|
}
|
|
241
|
+
this.enableOpReentryCheck =
|
|
242
|
+
runtimeOptions.enableOpReentryCheck === true &&
|
|
243
|
+
// Allow for a break-glass config to override the options
|
|
244
|
+
this.mc.config.getBoolean("Fluid.ContainerRuntime.DisableOpReentryCheck") !== true;
|
|
222
245
|
this.summariesDisabled = this.isSummariesDisabled();
|
|
223
246
|
this.heuristicsDisabled = this.isHeuristicsDisabled();
|
|
224
|
-
this.summarizerClientElectionEnabled = this.isSummarizerClientElectionEnabled();
|
|
225
247
|
this.maxOpsSinceLastSummary = this.getMaxOpsSinceLastSummary();
|
|
226
248
|
this.initialSummarizerDelayMs = this.getInitialSummarizerDelayMs();
|
|
227
249
|
this.maxConsecutiveReconnects =
|
|
@@ -230,7 +252,8 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
230
252
|
const pendingRuntimeState = context.pendingLocalState;
|
|
231
253
|
const baseSnapshot = (_d = pendingRuntimeState === null || pendingRuntimeState === void 0 ? void 0 : pendingRuntimeState.baseSnapshot) !== null && _d !== void 0 ? _d : context.baseSnapshot;
|
|
232
254
|
const maxSnapshotCacheDurationMs = (_f = (_e = this._storage) === null || _e === void 0 ? void 0 : _e.policies) === null || _f === void 0 ? void 0 : _f.maximumCacheDurationMs;
|
|
233
|
-
if (maxSnapshotCacheDurationMs !== undefined &&
|
|
255
|
+
if (maxSnapshotCacheDurationMs !== undefined &&
|
|
256
|
+
maxSnapshotCacheDurationMs > 5 * 24 * 60 * 60 * 1000) {
|
|
234
257
|
// This is a runtime enforcement of what's already explicit in the policy's type itself,
|
|
235
258
|
// which dictates the value is either undefined or exactly 5 days in ms.
|
|
236
259
|
// As long as the actual value is less than 5 days, the assumptions GC makes here are valid.
|
|
@@ -267,43 +290,59 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
267
290
|
throwOnFailure: true,
|
|
268
291
|
// If GC should not run, let the summarizer node know so that it does not track GC state.
|
|
269
292
|
gcDisabled: !this.garbageCollector.shouldRunGC,
|
|
270
|
-
}
|
|
293
|
+
},
|
|
294
|
+
// Function to get GC data if needed. This will always be called by the root summarizer node to get GC data.
|
|
295
|
+
async (fullGC) => this.getGCDataInternal(fullGC),
|
|
296
|
+
// Function to get the GC details from the base snapshot we loaded from.
|
|
297
|
+
async () => this.garbageCollector.getBaseGCDetails());
|
|
271
298
|
if (baseSnapshot) {
|
|
272
299
|
this.summarizerNode.updateBaseSummaryState(baseSnapshot);
|
|
273
300
|
}
|
|
274
301
|
this.dataStores = new DataStores(getSummaryForDatastores(baseSnapshot, metadata), this, (attachMsg) => this.submit(ContainerMessageType.Attach, attachMsg), (id, createParam) => (summarizeInternal, getGCDataFn, getBaseGCDetailsFn) => this.summarizerNode.createChild(summarizeInternal, id, createParam, undefined, getGCDataFn, getBaseGCDetailsFn), (id) => this.summarizerNode.deleteChild(id), this.mc.logger, async () => this.garbageCollector.getBaseGCDetails(), (path, timestampMs, packagePath) => this.garbageCollector.nodeUpdated(path, "Changed", timestampMs, packagePath), new Map(dataStoreAliasMap));
|
|
275
|
-
this.blobManager = new BlobManager(this.handleContext, blobManagerSnapshot, () => this.storage, (
|
|
302
|
+
this.blobManager = new BlobManager(this.handleContext, blobManagerSnapshot, () => this.storage, (localId, blobId) => {
|
|
276
303
|
if (!this.disposed) {
|
|
277
|
-
this.submit(ContainerMessageType.BlobAttach, undefined, undefined, {
|
|
304
|
+
this.submit(ContainerMessageType.BlobAttach, undefined, undefined, {
|
|
305
|
+
localId,
|
|
306
|
+
blobId,
|
|
307
|
+
});
|
|
278
308
|
}
|
|
279
|
-
}, (blobPath) => this.garbageCollector.nodeUpdated(blobPath, "Loaded"), this, pendingRuntimeState === null || pendingRuntimeState === void 0 ? void 0 : pendingRuntimeState.pendingAttachmentBlobs);
|
|
309
|
+
}, (blobPath) => this.garbageCollector.nodeUpdated(blobPath, "Loaded"), (fromPath, toPath) => this.garbageCollector.addedOutboundReference(fromPath, toPath), (blobPath) => this.garbageCollector.isNodeDeleted(blobPath), this, pendingRuntimeState === null || pendingRuntimeState === void 0 ? void 0 : pendingRuntimeState.pendingAttachmentBlobs, () => this.getCurrentReferenceTimestampMs());
|
|
280
310
|
this.scheduleManager = new ScheduleManager(context.deltaManager, this, () => this.clientId, ChildLogger.create(this.logger, "ScheduleManager"));
|
|
281
311
|
this.pendingStateManager = new PendingStateManager({
|
|
282
312
|
applyStashedOp: this.applyStashedOp.bind(this),
|
|
283
313
|
clientId: () => this.clientId,
|
|
284
314
|
close: this.closeFn,
|
|
285
315
|
connected: () => this.connected,
|
|
286
|
-
flush: this.flush.bind(this),
|
|
287
316
|
reSubmit: this.reSubmit.bind(this),
|
|
288
317
|
rollback: this.rollback.bind(this),
|
|
289
318
|
orderSequentially: this.orderSequentially.bind(this),
|
|
290
319
|
}, pendingRuntimeState === null || pendingRuntimeState === void 0 ? void 0 : pendingRuntimeState.pending);
|
|
320
|
+
const compressionOptions = this.mc.config.getBoolean("Fluid.ContainerRuntime.DisableCompression") === true
|
|
321
|
+
? {
|
|
322
|
+
minimumBatchSizeInBytes: Number.POSITIVE_INFINITY,
|
|
323
|
+
compressionAlgorithm: CompressionAlgorithms.lz4,
|
|
324
|
+
}
|
|
325
|
+
: runtimeOptions.compressionOptions;
|
|
291
326
|
this.outbox = new Outbox({
|
|
292
327
|
shouldSend: () => this.canSendOps(),
|
|
293
328
|
pendingStateManager: this.pendingStateManager,
|
|
294
329
|
containerContext: this.context,
|
|
295
330
|
compressor: new OpCompressor(this.mc.logger),
|
|
331
|
+
splitter: opSplitter,
|
|
296
332
|
config: {
|
|
297
|
-
compressionOptions
|
|
333
|
+
compressionOptions,
|
|
298
334
|
maxBatchSizeInBytes: runtimeOptions.maxBatchSizeInBytes,
|
|
335
|
+
enableOpReentryCheck: this.enableOpReentryCheck,
|
|
299
336
|
},
|
|
337
|
+
logger: this.mc.logger,
|
|
300
338
|
});
|
|
301
339
|
this.context.quorum.on("removeMember", (clientId) => {
|
|
302
340
|
this.remoteMessageProcessor.clearPartialMessagesFor(clientId);
|
|
303
341
|
});
|
|
304
342
|
this.summaryCollection = new SummaryCollection(this.deltaManager, this.logger);
|
|
305
|
-
this.dirtyContainer =
|
|
306
|
-
|
|
343
|
+
this.dirtyContainer =
|
|
344
|
+
this.context.attachState !== AttachState.Attached ||
|
|
345
|
+
this.pendingStateManager.hasPendingMessages();
|
|
307
346
|
this.context.updateDirtyContainerState(this.dirtyContainer);
|
|
308
347
|
if (this.summariesDisabled) {
|
|
309
348
|
this.mc.logger.sendTelemetryEvent({ eventName: "SummariesDisabled" });
|
|
@@ -312,7 +351,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
312
351
|
const orderedClientLogger = ChildLogger.create(this.logger, "OrderedClientElection");
|
|
313
352
|
const orderedClientCollection = new OrderedClientCollection(orderedClientLogger, this.context.deltaManager, this.context.quorum);
|
|
314
353
|
const orderedClientElectionForSummarizer = new OrderedClientElection(orderedClientLogger, orderedClientCollection, electedSummarizerData !== null && electedSummarizerData !== void 0 ? electedSummarizerData : this.context.deltaManager.lastSequenceNumber, SummarizerClientElection.isClientEligible);
|
|
315
|
-
this.summarizerClientElection = new SummarizerClientElection(orderedClientLogger, this.summaryCollection, orderedClientElectionForSummarizer, this.maxOpsSinceLastSummary
|
|
354
|
+
this.summarizerClientElection = new SummarizerClientElection(orderedClientLogger, this.summaryCollection, orderedClientElectionForSummarizer, this.maxOpsSinceLastSummary);
|
|
316
355
|
if (this.context.clientDetails.type === summarizerClientType) {
|
|
317
356
|
this._summarizer = new Summarizer("/_summarizer", this /* ISummarizerRuntime */, () => this.summaryConfiguration, this /* ISummarizerInternalsProvider */, this.handleContext, this.summaryCollection, async (runtime) => RunWhileConnectedCoordinator.create(runtime));
|
|
318
357
|
}
|
|
@@ -371,9 +410,14 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
371
410
|
ReportOpPerfTelemetry(this.context.clientId, this.deltaManager, this.logger);
|
|
372
411
|
BindBatchTracker(this, this.logger);
|
|
373
412
|
}
|
|
374
|
-
get IContainerRuntime() {
|
|
375
|
-
|
|
413
|
+
get IContainerRuntime() {
|
|
414
|
+
return this;
|
|
415
|
+
}
|
|
416
|
+
get IFluidRouter() {
|
|
417
|
+
return this;
|
|
418
|
+
}
|
|
376
419
|
/**
|
|
420
|
+
* @deprecated - use loadRuntime instead.
|
|
377
421
|
* Load the stores from a snapshot and returns the runtime.
|
|
378
422
|
* @param context - Context of the container.
|
|
379
423
|
* @param registryEntries - Mapping to the stores.
|
|
@@ -384,7 +428,35 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
384
428
|
* allows mixin classes to leverage this method to define their own async initializer.
|
|
385
429
|
*/
|
|
386
430
|
static async load(context, registryEntries, requestHandler, runtimeOptions = {}, containerScope = context.scope, existing, containerRuntimeCtor = ContainerRuntime) {
|
|
387
|
-
|
|
431
|
+
let existingFlag = true;
|
|
432
|
+
if (!existing) {
|
|
433
|
+
existingFlag = false;
|
|
434
|
+
}
|
|
435
|
+
return this.loadRuntime({
|
|
436
|
+
context,
|
|
437
|
+
registryEntries,
|
|
438
|
+
existing: existingFlag,
|
|
439
|
+
requestHandler,
|
|
440
|
+
runtimeOptions,
|
|
441
|
+
containerScope,
|
|
442
|
+
containerRuntimeCtor,
|
|
443
|
+
});
|
|
444
|
+
}
|
|
445
|
+
/**
|
|
446
|
+
* Load the stores from a snapshot and returns the runtime.
|
|
447
|
+
* @param params - An object housing the runtime properties:
|
|
448
|
+
* - context - Context of the container.
|
|
449
|
+
* - registryEntries - Mapping to the stores.
|
|
450
|
+
* - existing - When loading from an existing snapshot
|
|
451
|
+
* - requestHandler - Request handlers for the container runtime
|
|
452
|
+
* - runtimeOptions - Additional options to be passed to the runtime
|
|
453
|
+
* - containerScope - runtime services provided with context
|
|
454
|
+
* - containerRuntimeCtor - Constructor to use to create the ContainerRuntime instance.
|
|
455
|
+
* This allows mixin classes to leverage this method to define their own async initializer.
|
|
456
|
+
*/
|
|
457
|
+
static async loadRuntime(params) {
|
|
458
|
+
var _a, _b, _c, _d;
|
|
459
|
+
const { context, registryEntries, existing, requestHandler, runtimeOptions = {}, containerScope = {}, containerRuntimeCtor = ContainerRuntime, } = params;
|
|
388
460
|
// If taggedLogger exists, use it. Otherwise, wrap the vanilla logger:
|
|
389
461
|
// back-compat: Remove the TaggedLoggerAdapter fallback once all the host are using loader > 0.45
|
|
390
462
|
const backCompatContext = context;
|
|
@@ -396,13 +468,15 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
396
468
|
});
|
|
397
469
|
const { summaryOptions = {}, gcOptions = {}, loadSequenceNumberVerification = "close", flushMode = defaultFlushMode, enableOfflineLoad = false, compressionOptions = {
|
|
398
470
|
minimumBatchSizeInBytes: Number.POSITIVE_INFINITY,
|
|
399
|
-
compressionAlgorithm: CompressionAlgorithms.lz4
|
|
400
|
-
}, maxBatchSizeInBytes = defaultMaxBatchSizeInBytes, } = runtimeOptions;
|
|
471
|
+
compressionAlgorithm: CompressionAlgorithms.lz4,
|
|
472
|
+
}, maxBatchSizeInBytes = defaultMaxBatchSizeInBytes, chunkSizeInBytes = Number.POSITIVE_INFINITY, enableOpReentryCheck = false, } = runtimeOptions;
|
|
401
473
|
const pendingRuntimeState = context.pendingLocalState;
|
|
402
474
|
const baseSnapshot = (_b = pendingRuntimeState === null || pendingRuntimeState === void 0 ? void 0 : pendingRuntimeState.baseSnapshot) !== null && _b !== void 0 ? _b : context.baseSnapshot;
|
|
403
|
-
const storage = !pendingRuntimeState
|
|
404
|
-
context.storage
|
|
405
|
-
new SerializedSnapshotStorage(() => {
|
|
475
|
+
const storage = !pendingRuntimeState
|
|
476
|
+
? context.storage
|
|
477
|
+
: new SerializedSnapshotStorage(() => {
|
|
478
|
+
return context.storage;
|
|
479
|
+
}, pendingRuntimeState.snapshotBlobs);
|
|
406
480
|
const registry = new FluidDataStoreRegistry(registryEntries);
|
|
407
481
|
const tryFetchBlob = async (blobName) => {
|
|
408
482
|
const blobId = baseSnapshot === null || baseSnapshot === void 0 ? void 0 : baseSnapshot.blobs[blobName];
|
|
@@ -433,7 +507,8 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
433
507
|
if (!pendingRuntimeState && runtimeSequenceNumber !== undefined) {
|
|
434
508
|
const protocolSequenceNumber = context.deltaManager.initialSequenceNumber;
|
|
435
509
|
// Unless bypass is explicitly set, then take action when sequence numbers mismatch.
|
|
436
|
-
if (loadSequenceNumberVerification !== "bypass" &&
|
|
510
|
+
if (loadSequenceNumberVerification !== "bypass" &&
|
|
511
|
+
runtimeSequenceNumber !== protocolSequenceNumber) {
|
|
437
512
|
// "Load from summary, runtime metadata sequenceNumber !== initialSequenceNumber"
|
|
438
513
|
const error = new DataCorruptionError(
|
|
439
514
|
// pre-0.58 error message: SummaryMetadataMismatch
|
|
@@ -442,7 +517,9 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
442
517
|
logger.sendErrorEvent({ eventName: "SequenceNumberMismatch" }, error);
|
|
443
518
|
}
|
|
444
519
|
else {
|
|
520
|
+
// Call both close and dispose as closeFn implementation will no longer dispose runtime in future
|
|
445
521
|
context.closeFn(error);
|
|
522
|
+
(_d = context.disposeFn) === null || _d === void 0 ? void 0 : _d.call(context, error);
|
|
446
523
|
}
|
|
447
524
|
}
|
|
448
525
|
}
|
|
@@ -454,6 +531,8 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
454
531
|
enableOfflineLoad,
|
|
455
532
|
compressionOptions,
|
|
456
533
|
maxBatchSizeInBytes,
|
|
534
|
+
chunkSizeInBytes,
|
|
535
|
+
enableOpReentryCheck,
|
|
457
536
|
}, containerScope, logger, loadExisting, blobManagerSnapshot, storage, requestHandler);
|
|
458
537
|
if (pendingRuntimeState) {
|
|
459
538
|
await runtime.processSavedOps(pendingRuntimeState);
|
|
@@ -483,8 +562,18 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
483
562
|
// eslint-disable-next-line @typescript-eslint/unbound-method
|
|
484
563
|
return this.reSubmit;
|
|
485
564
|
}
|
|
565
|
+
get disposeFn() {
|
|
566
|
+
var _a;
|
|
567
|
+
// In old loaders without dispose functionality, closeFn is equivalent but will also switch container to readonly mode
|
|
568
|
+
return (_a = this.context.disposeFn) !== null && _a !== void 0 ? _a : this.context.closeFn;
|
|
569
|
+
}
|
|
486
570
|
get closeFn() {
|
|
487
|
-
|
|
571
|
+
// Also call disposeFn to retain functionality of runtime being disposed on close
|
|
572
|
+
return (error) => {
|
|
573
|
+
var _a, _b;
|
|
574
|
+
this.context.closeFn(error);
|
|
575
|
+
(_b = (_a = this.context).disposeFn) === null || _b === void 0 ? void 0 : _b.call(_a, error);
|
|
576
|
+
};
|
|
488
577
|
}
|
|
489
578
|
get flushMode() {
|
|
490
579
|
return this._flushMode;
|
|
@@ -501,6 +590,23 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
501
590
|
get IFluidHandleContext() {
|
|
502
591
|
return this.handleContext;
|
|
503
592
|
}
|
|
593
|
+
/**
|
|
594
|
+
* Invokes the given callback and expects that no ops are submitted
|
|
595
|
+
* until execution finishes. If an op is submitted, an error will be raised.
|
|
596
|
+
*
|
|
597
|
+
* Can be disabled by feature gate `Fluid.ContainerRuntime.DisableOpReentryCheck`
|
|
598
|
+
*
|
|
599
|
+
* @param callback - the callback to be invoked
|
|
600
|
+
*/
|
|
601
|
+
ensureNoDataModelChanges(callback) {
|
|
602
|
+
this.ensureNoDataModelChangesCalls++;
|
|
603
|
+
try {
|
|
604
|
+
return callback();
|
|
605
|
+
}
|
|
606
|
+
finally {
|
|
607
|
+
this.ensureNoDataModelChangesCalls--;
|
|
608
|
+
}
|
|
609
|
+
}
|
|
504
610
|
get connected() {
|
|
505
611
|
return this._connected;
|
|
506
612
|
}
|
|
@@ -509,48 +615,20 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
509
615
|
var _a;
|
|
510
616
|
return (_a = this.summarizerClientElection) === null || _a === void 0 ? void 0 : _a.electedClientId;
|
|
511
617
|
}
|
|
512
|
-
get disposed() {
|
|
618
|
+
get disposed() {
|
|
619
|
+
return this._disposed;
|
|
620
|
+
}
|
|
513
621
|
get summarizer() {
|
|
514
622
|
assert(this._summarizer !== undefined, 0x257 /* "This is not summarizing container" */);
|
|
515
623
|
return this._summarizer;
|
|
516
624
|
}
|
|
517
625
|
isSummariesDisabled() {
|
|
518
|
-
// back-compat: disableSummaries was moved from ISummaryRuntimeOptions
|
|
519
|
-
// to ISummaryConfiguration in 0.60.
|
|
520
|
-
if (this.runtimeOptions.summaryOptions.disableSummaries === true) {
|
|
521
|
-
return true;
|
|
522
|
-
}
|
|
523
626
|
return this.summaryConfiguration.state === "disabled";
|
|
524
627
|
}
|
|
525
628
|
isHeuristicsDisabled() {
|
|
526
|
-
var _a;
|
|
527
|
-
// back-compat: disableHeuristics was moved from ISummarizerOptions
|
|
528
|
-
// to ISummaryConfiguration in 0.60.
|
|
529
|
-
if (((_a = this.runtimeOptions.summaryOptions.summarizerOptions) === null || _a === void 0 ? void 0 : _a.disableHeuristics) === true) {
|
|
530
|
-
return true;
|
|
531
|
-
}
|
|
532
629
|
return this.summaryConfiguration.state === "disableHeuristics";
|
|
533
630
|
}
|
|
534
|
-
isSummarizerClientElectionEnabled() {
|
|
535
|
-
var _a;
|
|
536
|
-
if (this.mc.config.getBoolean("Fluid.ContainerRuntime.summarizerClientElection")) {
|
|
537
|
-
return (_a = this.mc.config.getBoolean("Fluid.ContainerRuntime.summarizerClientElection")) !== null && _a !== void 0 ? _a : true;
|
|
538
|
-
}
|
|
539
|
-
// back-compat: summarizerClientElection was moved from ISummaryRuntimeOptions
|
|
540
|
-
// to ISummaryConfiguration in 0.60.
|
|
541
|
-
if (this.runtimeOptions.summaryOptions.summarizerClientElection === true) {
|
|
542
|
-
return true;
|
|
543
|
-
}
|
|
544
|
-
return this.summaryConfiguration.state !== "disabled"
|
|
545
|
-
? this.summaryConfiguration.summarizerClientElection === true
|
|
546
|
-
: false;
|
|
547
|
-
}
|
|
548
631
|
getMaxOpsSinceLastSummary() {
|
|
549
|
-
// back-compat: maxOpsSinceLastSummary was moved from ISummaryRuntimeOptions
|
|
550
|
-
// to ISummaryConfiguration in 0.60.
|
|
551
|
-
if (this.runtimeOptions.summaryOptions.maxOpsSinceLastSummary !== undefined) {
|
|
552
|
-
return this.runtimeOptions.summaryOptions.maxOpsSinceLastSummary;
|
|
553
|
-
}
|
|
554
632
|
return this.summaryConfiguration.state !== "disabled"
|
|
555
633
|
? this.summaryConfiguration.maxOpsSinceLastSummary
|
|
556
634
|
: 0;
|
|
@@ -649,7 +727,8 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
649
727
|
status: 200,
|
|
650
728
|
mimeType: "fluid/object",
|
|
651
729
|
value: blob,
|
|
652
|
-
}
|
|
730
|
+
}
|
|
731
|
+
: create404Response(request);
|
|
653
732
|
}
|
|
654
733
|
else if (requestParser.pathParts.length > 0) {
|
|
655
734
|
const dataStore = await this.getDataStoreFromRequest(id, request);
|
|
@@ -670,16 +749,20 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
670
749
|
return (_a = this.dataStores.aliases.get(maybeAlias)) !== null && _a !== void 0 ? _a : maybeAlias;
|
|
671
750
|
}
|
|
672
751
|
async getDataStoreFromRequest(id, request) {
|
|
673
|
-
var _a, _b, _c, _d
|
|
674
|
-
const
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
752
|
+
var _a, _b, _c, _d;
|
|
753
|
+
const headerData = {};
|
|
754
|
+
if (typeof ((_a = request.headers) === null || _a === void 0 ? void 0 : _a[RuntimeHeaders.wait]) === "boolean") {
|
|
755
|
+
headerData.wait = request.headers[RuntimeHeaders.wait];
|
|
756
|
+
}
|
|
757
|
+
if (typeof ((_b = request.headers) === null || _b === void 0 ? void 0 : _b[RuntimeHeaders.viaHandle]) === "boolean") {
|
|
758
|
+
headerData.viaHandle = request.headers[RuntimeHeaders.viaHandle];
|
|
759
|
+
}
|
|
760
|
+
if (typeof ((_c = request.headers) === null || _c === void 0 ? void 0 : _c[AllowTombstoneRequestHeaderKey]) === "boolean") {
|
|
761
|
+
headerData.allowTombstone = request.headers[AllowTombstoneRequestHeaderKey];
|
|
762
|
+
}
|
|
680
763
|
await this.dataStores.waitIfPendingAlias(id);
|
|
681
764
|
const internalId = this.internalId(id);
|
|
682
|
-
const dataStoreContext = await this.dataStores.getDataStore(internalId,
|
|
765
|
+
const dataStoreContext = await this.dataStores.getDataStore(internalId, headerData);
|
|
683
766
|
/**
|
|
684
767
|
* If GC should run and this an external app request with "externalRequest" header, we need to return
|
|
685
768
|
* an error if the data store being requested is marked as unreferenced as per the data store's base
|
|
@@ -688,7 +771,8 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
688
771
|
* This is a workaround to handle scenarios where a data store shared with an external app is deleted
|
|
689
772
|
* and marked as unreferenced by GC. Returning an error will fail to load the data store for the app.
|
|
690
773
|
*/
|
|
691
|
-
if (((
|
|
774
|
+
if (((_d = request.headers) === null || _d === void 0 ? void 0 : _d[RuntimeHeaders.externalRequest]) &&
|
|
775
|
+
this.garbageCollector.shouldRunGC) {
|
|
692
776
|
// The data store is referenced if used routes in the base summary has a route to self.
|
|
693
777
|
// Older documents may not have used routes in the summary. They are considered referenced.
|
|
694
778
|
const usedRoutes = (await dataStoreContext.getBaseGCDetails()).usedRoutes;
|
|
@@ -862,9 +946,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
862
946
|
if (reconnection) {
|
|
863
947
|
this.consecutiveReconnects++;
|
|
864
948
|
if (!this.shouldContinueReconnecting()) {
|
|
865
|
-
this.closeFn(DataProcessingError.create(
|
|
866
|
-
// eslint-disable-next-line max-len
|
|
867
|
-
"Runtime detected too many reconnects with no progress syncing local ops. Batch of ops is likely too large (over 1Mb)", "setConnectionState", undefined, {
|
|
949
|
+
this.closeFn(DataProcessingError.create("Runtime detected too many reconnects with no progress syncing local ops.", "setConnectionState", undefined, {
|
|
868
950
|
dataLoss: 1,
|
|
869
951
|
attempts: this.consecutiveReconnects,
|
|
870
952
|
pendingMessages: this.pendingStateManager.pendingMessagesCount,
|
|
@@ -898,7 +980,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
898
980
|
this.scheduleManager.beforeOpProcessing(message);
|
|
899
981
|
try {
|
|
900
982
|
let localOpMetadata;
|
|
901
|
-
if (local && runtimeMessage) {
|
|
983
|
+
if (local && runtimeMessage && message.type !== ContainerMessageType.ChunkedOp) {
|
|
902
984
|
localOpMetadata = this.pendingStateManager.processPendingLocalMessage(message);
|
|
903
985
|
}
|
|
904
986
|
// If there are no more pending messages after processing a local message,
|
|
@@ -971,7 +1053,8 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
971
1053
|
if (message.clientId === this.clientId && this.connected) {
|
|
972
1054
|
// Check to see if the signal was lost.
|
|
973
1055
|
if (this._perfSignalData.trackingSignalSequenceNumber !== undefined &&
|
|
974
|
-
envelope.clientSignalSequenceNumber >
|
|
1056
|
+
envelope.clientSignalSequenceNumber >
|
|
1057
|
+
this._perfSignalData.trackingSignalSequenceNumber) {
|
|
975
1058
|
this._perfSignalData.signalsLost++;
|
|
976
1059
|
this._perfSignalData.trackingSignalSequenceNumber = undefined;
|
|
977
1060
|
this.logger.sendErrorEvent({
|
|
@@ -982,7 +1065,8 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
982
1065
|
clientSignalSequenceNumber: envelope.clientSignalSequenceNumber,
|
|
983
1066
|
});
|
|
984
1067
|
}
|
|
985
|
-
else if (envelope.clientSignalSequenceNumber ===
|
|
1068
|
+
else if (envelope.clientSignalSequenceNumber ===
|
|
1069
|
+
this._perfSignalData.trackingSignalSequenceNumber) {
|
|
986
1070
|
this.sendSignalTelemetryEvent(envelope.clientSignalSequenceNumber);
|
|
987
1071
|
this._perfSignalData.trackingSignalSequenceNumber = undefined;
|
|
988
1072
|
}
|
|
@@ -1000,7 +1084,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1000
1084
|
async getRootDataStoreChannel(id, wait = true) {
|
|
1001
1085
|
await this.dataStores.waitIfPendingAlias(id);
|
|
1002
1086
|
const internalId = this.internalId(id);
|
|
1003
|
-
const context = await this.dataStores.getDataStore(internalId, wait
|
|
1087
|
+
const context = await this.dataStores.getDataStore(internalId, { wait });
|
|
1004
1088
|
assert(await context.isRoot(), 0x12b /* "did not get root data store" */);
|
|
1005
1089
|
return context.realize();
|
|
1006
1090
|
}
|
|
@@ -1049,7 +1133,8 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1049
1133
|
finally {
|
|
1050
1134
|
this._orderSequentiallyCalls--;
|
|
1051
1135
|
}
|
|
1052
|
-
|
|
1136
|
+
// We don't flush on TurnBased since we expect all messages in the same JS turn to be part of the same batch
|
|
1137
|
+
if (this.flushMode !== FlushMode.TurnBased && this._orderSequentiallyCalls === 0) {
|
|
1053
1138
|
this.flush();
|
|
1054
1139
|
}
|
|
1055
1140
|
return result;
|
|
@@ -1068,7 +1153,9 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1068
1153
|
return this.dataStores.createDetachedDataStoreCore(pkg, false);
|
|
1069
1154
|
}
|
|
1070
1155
|
async _createDataStoreWithProps(pkg, props, id = uuid()) {
|
|
1071
|
-
const fluidDataStore = await this.dataStores
|
|
1156
|
+
const fluidDataStore = await this.dataStores
|
|
1157
|
+
._createFluidDataStoreContext(Array.isArray(pkg) ? pkg : [pkg], id, props)
|
|
1158
|
+
.realize();
|
|
1072
1159
|
return channelToDataStore(fluidDataStore, id, this, this.dataStores, this.mc.logger);
|
|
1073
1160
|
}
|
|
1074
1161
|
async _createDataStore(pkg, id = uuid(), props) {
|
|
@@ -1205,7 +1292,10 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1205
1292
|
}
|
|
1206
1293
|
const telemetryContext = new TelemetryContext();
|
|
1207
1294
|
const { stats, summary } = await this.summarizerNode.summarize(fullTree, trackState, telemetryContext);
|
|
1208
|
-
this.logger.sendTelemetryEvent({
|
|
1295
|
+
this.logger.sendTelemetryEvent({
|
|
1296
|
+
eventName: "SummarizeTelemetry",
|
|
1297
|
+
details: telemetryContext.serialize(),
|
|
1298
|
+
});
|
|
1209
1299
|
assert(summary.type === SummaryType.Tree, 0x12f /* "Container Runtime's summarize should always return a tree" */);
|
|
1210
1300
|
return { stats, summary, gcStats };
|
|
1211
1301
|
}
|
|
@@ -1218,6 +1308,9 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1218
1308
|
async updateStateBeforeGC() {
|
|
1219
1309
|
return this.dataStores.updateStateBeforeGC();
|
|
1220
1310
|
}
|
|
1311
|
+
async getGCDataInternal(fullGC) {
|
|
1312
|
+
return this.dataStores.getGCData(fullGC);
|
|
1313
|
+
}
|
|
1221
1314
|
/**
|
|
1222
1315
|
* Implementation of IGarbageCollectionRuntime::getGCData.
|
|
1223
1316
|
* Generates and returns the GC data for this container.
|
|
@@ -1225,7 +1318,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1225
1318
|
*/
|
|
1226
1319
|
async getGCData(fullGC) {
|
|
1227
1320
|
const builder = new GCDataBuilder();
|
|
1228
|
-
const dsGCData = await this.
|
|
1321
|
+
const dsGCData = await this.summarizerNode.getGCData(fullGC);
|
|
1229
1322
|
builder.addNodes(dsGCData.gcNodes);
|
|
1230
1323
|
const blobsGCData = this.blobManager.getGCData(fullGC);
|
|
1231
1324
|
builder.addNodes(blobsGCData.gcNodes);
|
|
@@ -1241,39 +1334,26 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1241
1334
|
// summarizing is required and asserted by the the summarizer node. We are the root and are
|
|
1242
1335
|
// always referenced, so the used routes is only self-route (empty string).
|
|
1243
1336
|
this.summarizerNode.updateUsedRoutes([""]);
|
|
1244
|
-
const
|
|
1245
|
-
|
|
1246
|
-
for (const route of usedRoutes) {
|
|
1247
|
-
if (this.isBlobPath(route)) {
|
|
1248
|
-
blobManagerUsedRoutes.push(route);
|
|
1249
|
-
}
|
|
1250
|
-
else {
|
|
1251
|
-
dataStoreUsedRoutes.push(route);
|
|
1252
|
-
}
|
|
1253
|
-
}
|
|
1254
|
-
this.blobManager.updateUsedRoutes(blobManagerUsedRoutes);
|
|
1255
|
-
this.dataStores.updateUsedRoutes(dataStoreUsedRoutes);
|
|
1337
|
+
const { dataStoreRoutes } = this.getDataStoreAndBlobManagerRoutes(usedRoutes);
|
|
1338
|
+
this.dataStores.updateUsedRoutes(dataStoreRoutes);
|
|
1256
1339
|
}
|
|
1257
1340
|
/**
|
|
1258
|
-
* This is called to update objects whose routes are unused.
|
|
1259
|
-
*
|
|
1260
|
-
* @param unusedRoutes - The routes that are unused in all data stores and attachment blobs in this Container.
|
|
1261
|
-
* @param tombstone - if true, the objects corresponding to unused routes are marked tombstones. Otherwise, they
|
|
1262
|
-
* are deleted.
|
|
1341
|
+
* This is called to update objects whose routes are unused.
|
|
1342
|
+
* @param unusedRoutes - Data store and attachment blob routes that are unused in this Container.
|
|
1263
1343
|
*/
|
|
1264
|
-
updateUnusedRoutes(unusedRoutes
|
|
1265
|
-
const
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
}
|
|
1275
|
-
this.blobManager.
|
|
1276
|
-
this.dataStores.
|
|
1344
|
+
updateUnusedRoutes(unusedRoutes) {
|
|
1345
|
+
const { blobManagerRoutes, dataStoreRoutes } = this.getDataStoreAndBlobManagerRoutes(unusedRoutes);
|
|
1346
|
+
this.blobManager.updateUnusedRoutes(blobManagerRoutes);
|
|
1347
|
+
this.dataStores.updateUnusedRoutes(dataStoreRoutes);
|
|
1348
|
+
}
|
|
1349
|
+
/**
|
|
1350
|
+
* This is called to update objects that are tombstones.
|
|
1351
|
+
* @param tombstonedRoutes - Data store and attachment blob routes that are tombstones in this Container.
|
|
1352
|
+
*/
|
|
1353
|
+
updateTombstonedRoutes(tombstonedRoutes) {
|
|
1354
|
+
const { blobManagerRoutes, dataStoreRoutes } = this.getDataStoreAndBlobManagerRoutes(tombstonedRoutes);
|
|
1355
|
+
this.blobManager.updateTombstonedRoutes(blobManagerRoutes);
|
|
1356
|
+
this.dataStores.updateTombstonedRoutes(dataStoreRoutes);
|
|
1277
1357
|
}
|
|
1278
1358
|
/**
|
|
1279
1359
|
* Returns a server generated referenced timestamp to be used to track unreferenced nodes by GC.
|
|
@@ -1302,7 +1382,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1302
1382
|
async getGCNodePackagePath(nodePath) {
|
|
1303
1383
|
switch (this.getNodeType(nodePath)) {
|
|
1304
1384
|
case GCNodeType.Blob:
|
|
1305
|
-
return [
|
|
1385
|
+
return [BlobManager.basePath];
|
|
1306
1386
|
case GCNodeType.DataStore:
|
|
1307
1387
|
case GCNodeType.SubDataStore:
|
|
1308
1388
|
return this.dataStores.getDataStorePackagePath(nodePath);
|
|
@@ -1320,6 +1400,25 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1320
1400
|
}
|
|
1321
1401
|
return true;
|
|
1322
1402
|
}
|
|
1403
|
+
/**
|
|
1404
|
+
* From a given list of routes, separate and return routes that belong to blob manager and data stores.
|
|
1405
|
+
* @param routes - A list of routes that can belong to data stores or blob manager.
|
|
1406
|
+
* @returns - Two route lists - One that contains routes for blob manager and another one that contains routes
|
|
1407
|
+
* for data stores.
|
|
1408
|
+
*/
|
|
1409
|
+
getDataStoreAndBlobManagerRoutes(routes) {
|
|
1410
|
+
const blobManagerRoutes = [];
|
|
1411
|
+
const dataStoreRoutes = [];
|
|
1412
|
+
for (const route of routes) {
|
|
1413
|
+
if (this.isBlobPath(route)) {
|
|
1414
|
+
blobManagerRoutes.push(route);
|
|
1415
|
+
}
|
|
1416
|
+
else {
|
|
1417
|
+
dataStoreRoutes.push(route);
|
|
1418
|
+
}
|
|
1419
|
+
}
|
|
1420
|
+
return { blobManagerRoutes, dataStoreRoutes };
|
|
1421
|
+
}
|
|
1323
1422
|
/**
|
|
1324
1423
|
* Runs garbage collection and updates the reference / used state of the nodes in the container.
|
|
1325
1424
|
* @returns the statistics of the garbage collection run; undefined if GC did not run.
|
|
@@ -1391,7 +1490,6 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1391
1490
|
if (this.deltaManager.lastSequenceNumber !== summaryRefSeqNum) {
|
|
1392
1491
|
return {
|
|
1393
1492
|
continue: false,
|
|
1394
|
-
// eslint-disable-next-line max-len
|
|
1395
1493
|
error: `lastSequenceNumber changed before uploading to storage. ${this.deltaManager.lastSequenceNumber} !== ${summaryRefSeqNum}`,
|
|
1396
1494
|
};
|
|
1397
1495
|
}
|
|
@@ -1399,7 +1497,6 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1399
1497
|
if (lastAck !== this.summaryCollection.latestAck) {
|
|
1400
1498
|
return {
|
|
1401
1499
|
continue: false,
|
|
1402
|
-
// eslint-disable-next-line max-len
|
|
1403
1500
|
error: `Last summary changed while summarizing. ${this.summaryCollection.latestAck} !== ${lastAck}`,
|
|
1404
1501
|
};
|
|
1405
1502
|
}
|
|
@@ -1466,8 +1563,8 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1466
1563
|
// latestSnapshotVersionId from storage and it does not match with the lastAck ackHandle, then use
|
|
1467
1564
|
// the one fetched from storage as parent as that is the latest.
|
|
1468
1565
|
let summaryContext;
|
|
1469
|
-
if ((lastAck === null || lastAck === void 0 ? void 0 : lastAck.summaryAck.contents.handle) !== latestSnapshotVersionId
|
|
1470
|
-
|
|
1566
|
+
if ((lastAck === null || lastAck === void 0 ? void 0 : lastAck.summaryAck.contents.handle) !== latestSnapshotVersionId &&
|
|
1567
|
+
latestSnapshotVersionId !== undefined) {
|
|
1471
1568
|
summaryContext = {
|
|
1472
1569
|
proposalHandle: undefined,
|
|
1473
1570
|
ackHandle: latestSnapshotVersionId,
|
|
@@ -1566,12 +1663,16 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1566
1663
|
}
|
|
1567
1664
|
submit(type, contents, localOpMetadata = undefined, metadata = undefined) {
|
|
1568
1665
|
this.verifyNotClosed();
|
|
1666
|
+
this.verifyCanSubmitOps();
|
|
1569
1667
|
// There should be no ops in detached container state!
|
|
1570
1668
|
assert(this.attachState !== AttachState.Detached, 0x132 /* "sending ops in detached container" */);
|
|
1571
1669
|
const deserializedContent = { type, contents };
|
|
1572
1670
|
const serializedContent = JSON.stringify(deserializedContent);
|
|
1573
1671
|
if (this.deltaManager.readOnlyInfo.readonly) {
|
|
1574
|
-
this.logger.sendTelemetryEvent({
|
|
1672
|
+
this.logger.sendTelemetryEvent({
|
|
1673
|
+
eventName: "SubmitOpInReadonly",
|
|
1674
|
+
connected: this.connected,
|
|
1675
|
+
});
|
|
1575
1676
|
}
|
|
1576
1677
|
const message = {
|
|
1577
1678
|
contents: serializedContent,
|
|
@@ -1601,7 +1702,8 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1601
1702
|
// issue than sending.
|
|
1602
1703
|
// Please note that this does not change file format, so it can be disabled in the future if this
|
|
1603
1704
|
// optimization no longer makes sense (for example, batch compression may make it less appealing).
|
|
1604
|
-
if (this.currentlyBatching() &&
|
|
1705
|
+
if (this.currentlyBatching() &&
|
|
1706
|
+
type === ContainerMessageType.Attach &&
|
|
1605
1707
|
this.mc.config.getBoolean("Fluid.ContainerRuntime.disableAttachOpReorder") !== true) {
|
|
1606
1708
|
this.outbox.submitAttach(message);
|
|
1607
1709
|
}
|
|
@@ -1614,11 +1716,14 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1614
1716
|
else if (!this.flushMicroTaskExists) {
|
|
1615
1717
|
this.flushMicroTaskExists = true;
|
|
1616
1718
|
// Queue a microtask to detect the end of the turn and force a flush.
|
|
1617
|
-
|
|
1618
|
-
|
|
1719
|
+
Promise.resolve()
|
|
1720
|
+
.then(() => {
|
|
1619
1721
|
this.flushMicroTaskExists = false;
|
|
1620
1722
|
this.flush();
|
|
1621
|
-
})
|
|
1723
|
+
})
|
|
1724
|
+
.catch((error) => {
|
|
1725
|
+
this.closeFn(error);
|
|
1726
|
+
});
|
|
1622
1727
|
}
|
|
1623
1728
|
}
|
|
1624
1729
|
catch (error) {
|
|
@@ -1648,6 +1753,32 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1648
1753
|
throw new Error("Runtime is closed");
|
|
1649
1754
|
}
|
|
1650
1755
|
}
|
|
1756
|
+
verifyCanSubmitOps() {
|
|
1757
|
+
if (this.ensureNoDataModelChangesCalls > 0) {
|
|
1758
|
+
const errorMessage = "Op was submitted from within a `ensureNoDataModelChanges` callback";
|
|
1759
|
+
if (this.opReentryCallsToReport > 0) {
|
|
1760
|
+
this.mc.logger.sendTelemetryEvent({ eventName: "OpReentry" },
|
|
1761
|
+
// We need to capture the call stack in order to inspect the source of this usage pattern
|
|
1762
|
+
new UsageError(errorMessage));
|
|
1763
|
+
this.opReentryCallsToReport--;
|
|
1764
|
+
}
|
|
1765
|
+
// Creating ops while processing ops can lead
|
|
1766
|
+
// to undefined behavior and events observed in the wrong order.
|
|
1767
|
+
// For example, we have two callbacks registered for a DDS, A and B.
|
|
1768
|
+
// Then if on change #1 callback A creates change #2, the invocation flow will be:
|
|
1769
|
+
//
|
|
1770
|
+
// A because of #1
|
|
1771
|
+
// A because of #2
|
|
1772
|
+
// B because of #2
|
|
1773
|
+
// B because of #1
|
|
1774
|
+
//
|
|
1775
|
+
// The runtime must enforce op coherence by not allowing ops to be submitted
|
|
1776
|
+
// while ops are being processed.
|
|
1777
|
+
if (this.enableOpReentryCheck) {
|
|
1778
|
+
throw new UsageError(errorMessage);
|
|
1779
|
+
}
|
|
1780
|
+
}
|
|
1781
|
+
}
|
|
1651
1782
|
/**
|
|
1652
1783
|
* Finds the right store and asks it to resubmit the message. This typically happens when we
|
|
1653
1784
|
* reconnect and there are pending messages.
|
|
@@ -1707,13 +1838,32 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1707
1838
|
// It should only be done by the summarizerNode, if required.
|
|
1708
1839
|
// When fetching from storage we will always get the latest version and do not use the ackHandle.
|
|
1709
1840
|
const snapshotTreeFetcher = async () => {
|
|
1710
|
-
const fetchResult = await this.
|
|
1841
|
+
const fetchResult = await this.fetchLatestSnapshotFromStorage(summaryLogger, {
|
|
1711
1842
|
eventName: "RefreshLatestSummaryGetSnapshot",
|
|
1712
1843
|
ackHandle,
|
|
1713
1844
|
summaryRefSeq,
|
|
1714
1845
|
fetchLatest: true,
|
|
1715
1846
|
});
|
|
1716
1847
|
const latestSnapshotRefSeq = await seqFromTree(fetchResult.snapshotTree, readAndParseBlob);
|
|
1848
|
+
/**
|
|
1849
|
+
* If the fetched snapshot is older than the one for which the ack was received, close the container.
|
|
1850
|
+
* This should never happen because an ack should be sent after the latest summary is updated in the server.
|
|
1851
|
+
* However, there are couple of scenarios where it's possible:
|
|
1852
|
+
* 1. A file was modified externally resulting in modifying the snapshot's sequence number. This can lead to
|
|
1853
|
+
* the document being unusable and we should not proceed.
|
|
1854
|
+
* 2. The server DB failed after the ack was sent which may delete the corresponding snapshot. Ideally, in
|
|
1855
|
+
* such cases, the file will be rolled back along with the ack and we will eventually reach a consistent
|
|
1856
|
+
* state.
|
|
1857
|
+
*/
|
|
1858
|
+
if (latestSnapshotRefSeq < summaryRefSeq) {
|
|
1859
|
+
const error = DataProcessingError.create("Fetched snapshot is older than the received ack", "RefreshLatestSummaryAck", undefined /* sequencedMessage */, {
|
|
1860
|
+
ackHandle,
|
|
1861
|
+
summaryRefSeq,
|
|
1862
|
+
latestSnapshotRefSeq,
|
|
1863
|
+
});
|
|
1864
|
+
this.closeFn(error);
|
|
1865
|
+
throw error;
|
|
1866
|
+
}
|
|
1717
1867
|
summaryLogger.sendTelemetryEvent({
|
|
1718
1868
|
eventName: "LatestSummaryRetrieved",
|
|
1719
1869
|
ackHandle,
|
|
@@ -1727,7 +1877,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1727
1877
|
};
|
|
1728
1878
|
const result = await this.summarizerNode.refreshLatestSummary(proposalHandle, summaryRefSeq, snapshotTreeFetcher, readAndParseBlob, summaryLogger);
|
|
1729
1879
|
// Notify the garbage collector so it can update its latest summary state.
|
|
1730
|
-
await this.garbageCollector.
|
|
1880
|
+
await this.garbageCollector.refreshLatestSummary(result, proposalHandle, summaryRefSeq, readAndParseBlob);
|
|
1731
1881
|
}
|
|
1732
1882
|
/**
|
|
1733
1883
|
* Fetches the latest snapshot from storage and uses it to refresh SummarizerNode's
|
|
@@ -1736,22 +1886,22 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1736
1886
|
* @returns downloaded snapshot's reference sequence number
|
|
1737
1887
|
*/
|
|
1738
1888
|
async refreshLatestSummaryAckFromServer(summaryLogger) {
|
|
1739
|
-
const { snapshotTree, versionId } = await this.
|
|
1889
|
+
const { snapshotTree, versionId } = await this.fetchLatestSnapshotFromStorage(summaryLogger, {
|
|
1740
1890
|
eventName: "RefreshLatestSummaryGetSnapshot",
|
|
1741
1891
|
fetchLatest: true,
|
|
1742
|
-
}
|
|
1892
|
+
});
|
|
1743
1893
|
const readAndParseBlob = async (id) => readAndParse(this.storage, id);
|
|
1744
1894
|
const latestSnapshotRefSeq = await seqFromTree(snapshotTree, readAndParseBlob);
|
|
1745
1895
|
const result = await this.summarizerNode.refreshLatestSummary(undefined, latestSnapshotRefSeq, async () => snapshotTree, readAndParseBlob, summaryLogger);
|
|
1746
1896
|
// Notify the garbage collector so it can update its latest summary state.
|
|
1747
|
-
await this.garbageCollector.
|
|
1897
|
+
await this.garbageCollector.refreshLatestSummary(result, undefined, latestSnapshotRefSeq, readAndParseBlob);
|
|
1748
1898
|
return { latestSnapshotRefSeq, latestSnapshotVersionId: versionId };
|
|
1749
1899
|
}
|
|
1750
|
-
async
|
|
1900
|
+
async fetchLatestSnapshotFromStorage(logger, event) {
|
|
1751
1901
|
return PerformanceEvent.timedExecAsync(logger, event, async (perfEvent) => {
|
|
1752
1902
|
const stats = {};
|
|
1753
1903
|
const trace = Trace.start();
|
|
1754
|
-
const versions = await this.storage.getVersions(
|
|
1904
|
+
const versions = await this.storage.getVersions(null, 1, "refreshLatestSummaryAckFromServer", FetchSource.noCache);
|
|
1755
1905
|
assert(!!versions && !!versions[0], 0x137 /* "Failed to get version from storage" */);
|
|
1756
1906
|
stats.getVersionDuration = trace.trace().duration;
|
|
1757
1907
|
const maybeSnapshot = await this.storage.getSnapshotTree(versions[0]);
|
|
@@ -1764,13 +1914,15 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1764
1914
|
notifyAttaching(snapshot) {
|
|
1765
1915
|
var _a;
|
|
1766
1916
|
if ((_a = this.mc.config.getBoolean("enableOfflineLoad")) !== null && _a !== void 0 ? _a : this.runtimeOptions.enableOfflineLoad) {
|
|
1767
|
-
this.baseSnapshotBlobs =
|
|
1917
|
+
this.baseSnapshotBlobs =
|
|
1918
|
+
SerializedSnapshotStorage.serializeTreeWithBlobContents(snapshot);
|
|
1768
1919
|
}
|
|
1769
1920
|
}
|
|
1770
1921
|
async initializeBaseSnapshotBlobs() {
|
|
1771
1922
|
var _a;
|
|
1772
1923
|
if (!((_a = this.mc.config.getBoolean("enableOfflineLoad")) !== null && _a !== void 0 ? _a : this.runtimeOptions.enableOfflineLoad) ||
|
|
1773
|
-
this.attachState !== AttachState.Attached ||
|
|
1924
|
+
this.attachState !== AttachState.Attached ||
|
|
1925
|
+
this.context.pendingLocalState) {
|
|
1774
1926
|
return;
|
|
1775
1927
|
}
|
|
1776
1928
|
assert(!!this.context.baseSnapshot, 0x2e5 /* "Must have a base snapshot" */);
|
|
@@ -1781,13 +1933,13 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1781
1933
|
if (!((_a = this.mc.config.getBoolean("enableOfflineLoad")) !== null && _a !== void 0 ? _a : this.runtimeOptions.enableOfflineLoad)) {
|
|
1782
1934
|
throw new UsageError("can't get state when offline load disabled");
|
|
1783
1935
|
}
|
|
1936
|
+
if (this._orderSequentiallyCalls !== 0) {
|
|
1937
|
+
throw new UsageError("can't get state during orderSequentially");
|
|
1938
|
+
}
|
|
1784
1939
|
// Flush pending batch.
|
|
1785
1940
|
// getPendingLocalState() is only exposed through Container.closeAndGetPendingLocalState(), so it's safe
|
|
1786
1941
|
// to close current batch.
|
|
1787
1942
|
this.flush();
|
|
1788
|
-
if (this._orderSequentiallyCalls !== 0) {
|
|
1789
|
-
throw new UsageError("can't get state during orderSequentially");
|
|
1790
|
-
}
|
|
1791
1943
|
const previousPendingState = this.context.pendingLocalState;
|
|
1792
1944
|
if (previousPendingState) {
|
|
1793
1945
|
return {
|
|
@@ -1867,6 +2019,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1867
2019
|
const waitForSeq = async (deltaManager, targetSeq) => new Promise((resolve, reject) => {
|
|
1868
2020
|
// TODO: remove cast to any when actual event is determined
|
|
1869
2021
|
deltaManager.on("closed", reject);
|
|
2022
|
+
deltaManager.on("disposed", reject);
|
|
1870
2023
|
// If we already reached target sequence number, simply resolve the promise.
|
|
1871
2024
|
if (deltaManager.lastSequenceNumber >= targetSeq) {
|
|
1872
2025
|
resolve();
|