@fluidframework/container-runtime 2.0.0-dev.2.2.0.111723 → 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 +62 -28
- package/dist/blobManager.d.ts.map +1 -1
- package/dist/blobManager.js +256 -102
- 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 +110 -78
- package/dist/containerRuntime.d.ts.map +1 -1
- package/dist/containerRuntime.js +336 -331
- 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 +40 -23
- 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 +69 -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 +57 -42
- package/dist/garbageCollection.d.ts.map +1 -1
- package/dist/garbageCollection.js +371 -239
- package/dist/garbageCollection.js.map +1 -1
- package/dist/garbageCollectionConstants.d.ts +23 -0
- package/dist/garbageCollectionConstants.d.ts.map +1 -0
- package/dist/garbageCollectionConstants.js +36 -0
- package/dist/garbageCollectionConstants.js.map +1 -0
- 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 +15 -11
- package/dist/gcSweepReadyUsageDetection.js.map +1 -1
- package/dist/index.d.ts +4 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -6
- package/dist/index.js.map +1 -1
- package/dist/opLifecycle/batchManager.d.ts +42 -0
- package/dist/opLifecycle/batchManager.d.ts.map +1 -0
- package/dist/opLifecycle/batchManager.js +124 -0
- package/dist/opLifecycle/batchManager.js.map +1 -0
- package/dist/opLifecycle/definitions.d.ts +64 -0
- package/dist/opLifecycle/definitions.d.ts.map +1 -0
- package/dist/opLifecycle/definitions.js +7 -0
- package/dist/opLifecycle/definitions.js.map +1 -0
- package/dist/opLifecycle/index.d.ts +12 -0
- package/dist/opLifecycle/index.d.ts.map +1 -0
- package/dist/opLifecycle/index.js +22 -0
- package/dist/opLifecycle/index.js.map +1 -0
- package/dist/{opCompressor.d.ts → opLifecycle/opCompressor.d.ts} +3 -3
- package/dist/opLifecycle/opCompressor.d.ts.map +1 -0
- package/dist/opLifecycle/opCompressor.js +67 -0
- package/dist/opLifecycle/opCompressor.js.map +1 -0
- package/dist/{opDecompressor.d.ts → opLifecycle/opDecompressor.d.ts} +2 -1
- package/dist/opLifecycle/opDecompressor.d.ts.map +1 -0
- package/dist/{opDecompressor.js → opLifecycle/opDecompressor.js} +37 -21
- package/dist/opLifecycle/opDecompressor.js.map +1 -0
- package/dist/opLifecycle/opSplitter.d.ts +49 -0
- package/dist/opLifecycle/opSplitter.d.ts.map +1 -0
- package/dist/opLifecycle/opSplitter.js +173 -0
- package/dist/opLifecycle/opSplitter.js.map +1 -0
- package/dist/opLifecycle/outbox.d.ts +52 -0
- package/dist/opLifecycle/outbox.d.ts.map +1 -0
- package/dist/opLifecycle/outbox.js +164 -0
- package/dist/opLifecycle/outbox.js.map +1 -0
- package/dist/opLifecycle/remoteMessageProcessor.d.ts +26 -0
- package/dist/opLifecycle/remoteMessageProcessor.d.ts.map +1 -0
- package/dist/opLifecycle/remoteMessageProcessor.js +96 -0
- package/dist/opLifecycle/remoteMessageProcessor.js.map +1 -0
- 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 +62 -28
- package/lib/blobManager.d.ts.map +1 -1
- package/lib/blobManager.js +259 -105
- 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 +110 -78
- package/lib/containerRuntime.d.ts.map +1 -1
- package/lib/containerRuntime.js +340 -334
- 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 +41 -24
- 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 +75 -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 +57 -42
- package/lib/garbageCollection.d.ts.map +1 -1
- package/lib/garbageCollection.js +364 -232
- package/lib/garbageCollection.js.map +1 -1
- package/lib/garbageCollectionConstants.d.ts +23 -0
- package/lib/garbageCollectionConstants.d.ts.map +1 -0
- package/lib/garbageCollectionConstants.js +33 -0
- package/lib/garbageCollectionConstants.js.map +1 -0
- 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 +15 -11
- package/lib/gcSweepReadyUsageDetection.js.map +1 -1
- package/lib/index.d.ts +4 -3
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +3 -3
- package/lib/index.js.map +1 -1
- package/lib/opLifecycle/batchManager.d.ts +42 -0
- package/lib/opLifecycle/batchManager.d.ts.map +1 -0
- package/lib/opLifecycle/batchManager.js +120 -0
- package/lib/opLifecycle/batchManager.js.map +1 -0
- package/lib/opLifecycle/definitions.d.ts +64 -0
- package/lib/opLifecycle/definitions.d.ts.map +1 -0
- package/lib/opLifecycle/definitions.js +6 -0
- package/lib/opLifecycle/definitions.js.map +1 -0
- package/lib/opLifecycle/index.d.ts +12 -0
- package/lib/opLifecycle/index.d.ts.map +1 -0
- package/lib/opLifecycle/index.js +11 -0
- package/lib/opLifecycle/index.js.map +1 -0
- package/lib/{opCompressor.d.ts → opLifecycle/opCompressor.d.ts} +3 -3
- package/lib/opLifecycle/opCompressor.d.ts.map +1 -0
- package/lib/opLifecycle/opCompressor.js +63 -0
- package/lib/opLifecycle/opCompressor.js.map +1 -0
- package/lib/{opDecompressor.d.ts → opLifecycle/opDecompressor.d.ts} +2 -1
- package/lib/opLifecycle/opDecompressor.d.ts.map +1 -0
- package/lib/{opDecompressor.js → opLifecycle/opDecompressor.js} +37 -21
- package/lib/opLifecycle/opDecompressor.js.map +1 -0
- package/lib/opLifecycle/opSplitter.d.ts +49 -0
- package/lib/opLifecycle/opSplitter.d.ts.map +1 -0
- package/lib/opLifecycle/opSplitter.js +168 -0
- package/lib/opLifecycle/opSplitter.js.map +1 -0
- package/lib/opLifecycle/outbox.d.ts +52 -0
- package/lib/opLifecycle/outbox.d.ts.map +1 -0
- package/lib/opLifecycle/outbox.js +160 -0
- package/lib/opLifecycle/outbox.js.map +1 -0
- package/lib/opLifecycle/remoteMessageProcessor.d.ts +26 -0
- package/lib/opLifecycle/remoteMessageProcessor.d.ts.map +1 -0
- package/lib/opLifecycle/remoteMessageProcessor.js +91 -0
- package/lib/opLifecycle/remoteMessageProcessor.js.map +1 -0
- 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 +28 -38
- package/prettier.config.cjs +1 -1
- package/src/batchTracker.ts +55 -50
- package/src/blobManager.ts +802 -541
- package/src/connectionTelemetry.ts +280 -249
- package/src/containerHandleContext.ts +27 -29
- package/src/containerRuntime.ts +3125 -2982
- package/src/dataStore.ts +172 -159
- package/src/dataStoreContext.ts +1049 -992
- package/src/dataStoreContexts.ts +178 -161
- package/src/dataStoreRegistry.ts +25 -20
- package/src/dataStores.ts +785 -711
- package/src/deltaScheduler.ts +158 -150
- package/src/garbageCollection.ts +1797 -1558
- package/src/garbageCollectionConstants.ts +38 -0
- package/src/garbageCollectionHelpers.ts +37 -0
- package/src/gcSweepReadyUsageDetection.ts +90 -84
- package/src/index.ts +68 -69
- package/src/opLifecycle/batchManager.ts +167 -0
- package/src/opLifecycle/definitions.ts +70 -0
- package/src/opLifecycle/index.ts +18 -0
- package/src/opLifecycle/opCompressor.ts +82 -0
- package/src/opLifecycle/opDecompressor.ts +124 -0
- package/src/opLifecycle/opSplitter.ts +238 -0
- package/src/opLifecycle/outbox.ts +228 -0
- package/src/opLifecycle/remoteMessageProcessor.ts +106 -0
- 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/dist/batchManager.d.ts +0 -42
- package/dist/batchManager.d.ts.map +0 -1
- package/dist/batchManager.js +0 -83
- package/dist/batchManager.js.map +0 -1
- package/dist/opCompressor.d.ts.map +0 -1
- package/dist/opCompressor.js +0 -50
- package/dist/opCompressor.js.map +0 -1
- package/dist/opDecompressor.d.ts.map +0 -1
- package/dist/opDecompressor.js.map +0 -1
- package/lib/batchManager.d.ts +0 -42
- package/lib/batchManager.d.ts.map +0 -1
- package/lib/batchManager.js +0 -79
- package/lib/batchManager.js.map +0 -1
- package/lib/opCompressor.d.ts.map +0 -1
- package/lib/opCompressor.js +0 -46
- package/lib/opCompressor.js.map +0 -1
- package/lib/opDecompressor.d.ts.map +0 -1
- package/lib/opDecompressor.js.map +0 -1
- package/src/batchManager.ts +0 -108
- package/src/opCompressor.ts +0 -59
- package/src/opDecompressor.ts +0 -82
package/dist/containerRuntime.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.ContainerRuntime = exports.getDeviceSpec = exports.agentSchedulerId = exports.
|
|
3
|
+
exports.ContainerRuntime = exports.getDeviceSpec = exports.agentSchedulerId = exports.isRuntimeMessage = exports.RuntimeMessage = exports.CompressionAlgorithms = exports.defaultRuntimeHeaderData = exports.TombstoneResponseHeaderKey = exports.AllowTombstoneRequestHeaderKey = exports.RuntimeHeaders = exports.DefaultSummaryConfiguration = exports.ContainerMessageType = void 0;
|
|
4
4
|
const container_definitions_1 = require("@fluidframework/container-definitions");
|
|
5
5
|
const common_utils_1 = require("@fluidframework/common-utils");
|
|
6
6
|
const telemetry_utils_1 = require("@fluidframework/telemetry-utils");
|
|
@@ -18,7 +18,6 @@ const summarizer_1 = require("./summarizer");
|
|
|
18
18
|
const summaryManager_1 = require("./summaryManager");
|
|
19
19
|
const connectionTelemetry_1 = require("./connectionTelemetry");
|
|
20
20
|
const pendingStateManager_1 = require("./pendingStateManager");
|
|
21
|
-
const batchManager_1 = require("./batchManager");
|
|
22
21
|
const packageVersion_1 = require("./packageVersion");
|
|
23
22
|
const blobManager_1 = require("./blobManager");
|
|
24
23
|
const dataStores_1 = require("./dataStores");
|
|
@@ -33,7 +32,7 @@ const dataStore_1 = require("./dataStore");
|
|
|
33
32
|
const batchTracker_1 = require("./batchTracker");
|
|
34
33
|
const serializedSnapshotStorage_1 = require("./serializedSnapshotStorage");
|
|
35
34
|
const scheduleManager_1 = require("./scheduleManager");
|
|
36
|
-
const
|
|
35
|
+
const opLifecycle_1 = require("./opLifecycle");
|
|
37
36
|
var ContainerMessageType;
|
|
38
37
|
(function (ContainerMessageType) {
|
|
39
38
|
// An op to be delivered to store
|
|
@@ -59,7 +58,6 @@ exports.DefaultSummaryConfiguration = {
|
|
|
59
58
|
maxAckWaitTime: 10 * 60 * 1000,
|
|
60
59
|
maxOpsSinceLastSummary: 7000,
|
|
61
60
|
initialSummarizerDelayMs: 5 * 1000,
|
|
62
|
-
summarizerClientElection: false,
|
|
63
61
|
nonRuntimeOpWeight: 0.1,
|
|
64
62
|
runtimeOpWeight: 1.0,
|
|
65
63
|
nonRuntimeHeuristicThreshold: 20,
|
|
@@ -79,6 +77,17 @@ var RuntimeHeaders;
|
|
|
79
77
|
/** True if the request is coming from an IFluidHandle. */
|
|
80
78
|
RuntimeHeaders["viaHandle"] = "viaHandle";
|
|
81
79
|
})(RuntimeHeaders = exports.RuntimeHeaders || (exports.RuntimeHeaders = {}));
|
|
80
|
+
/** True if a tombstoned object should be returned without erroring */
|
|
81
|
+
exports.AllowTombstoneRequestHeaderKey = "allowTombstone"; // Belongs in the enum above, but avoiding the breaking change
|
|
82
|
+
/** Tombstone error responses will have this header set to true */
|
|
83
|
+
exports.TombstoneResponseHeaderKey = "isTombstoned";
|
|
84
|
+
/** Default values for Runtime Headers */
|
|
85
|
+
exports.defaultRuntimeHeaderData = {
|
|
86
|
+
wait: true,
|
|
87
|
+
externalRequest: false,
|
|
88
|
+
viaHandle: false,
|
|
89
|
+
allowTombstone: false,
|
|
90
|
+
};
|
|
82
91
|
/**
|
|
83
92
|
* Available compression algorithms for op compression.
|
|
84
93
|
*/
|
|
@@ -113,38 +122,6 @@ function isRuntimeMessage(message) {
|
|
|
113
122
|
return Object.values(RuntimeMessage).includes(message.type);
|
|
114
123
|
}
|
|
115
124
|
exports.isRuntimeMessage = isRuntimeMessage;
|
|
116
|
-
/**
|
|
117
|
-
* Unpacks runtime messages
|
|
118
|
-
*
|
|
119
|
-
* @remarks This API makes no promises regarding backward-compatibility. This is internal API.
|
|
120
|
-
* @param message - message (as it observed in storage / service)
|
|
121
|
-
* @returns unpacked runtime message
|
|
122
|
-
*
|
|
123
|
-
* @internal
|
|
124
|
-
*/
|
|
125
|
-
function unpackRuntimeMessage(message) {
|
|
126
|
-
if (message.type === protocol_definitions_1.MessageType.Operation) {
|
|
127
|
-
// legacy op format?
|
|
128
|
-
if (message.contents.address !== undefined && message.contents.type === undefined) {
|
|
129
|
-
message.type = ContainerMessageType.FluidDataStoreOp;
|
|
130
|
-
}
|
|
131
|
-
else {
|
|
132
|
-
// new format
|
|
133
|
-
const innerContents = message.contents;
|
|
134
|
-
message.type = innerContents.type;
|
|
135
|
-
message.contents = innerContents.contents;
|
|
136
|
-
}
|
|
137
|
-
return true;
|
|
138
|
-
}
|
|
139
|
-
else {
|
|
140
|
-
// Legacy format, but it's already "unpacked",
|
|
141
|
-
// i.e. message.type is actually ContainerMessageType.
|
|
142
|
-
// Or it's non-runtime message.
|
|
143
|
-
// Nothing to do in such case.
|
|
144
|
-
return false;
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
exports.unpackRuntimeMessage = unpackRuntimeMessage;
|
|
148
125
|
/**
|
|
149
126
|
* Legacy ID for the built-in AgentScheduler. To minimize disruption while removing it, retaining this as a
|
|
150
127
|
* special-case for document dirty state. Ultimately we should have no special-cases from the
|
|
@@ -161,8 +138,7 @@ function getDeviceSpec() {
|
|
|
161
138
|
};
|
|
162
139
|
}
|
|
163
140
|
}
|
|
164
|
-
catch (_a) {
|
|
165
|
-
}
|
|
141
|
+
catch (_a) { }
|
|
166
142
|
return {};
|
|
167
143
|
}
|
|
168
144
|
exports.getDeviceSpec = getDeviceSpec;
|
|
@@ -171,6 +147,9 @@ exports.getDeviceSpec = getDeviceSpec;
|
|
|
171
147
|
* It will define the store level mappings.
|
|
172
148
|
*/
|
|
173
149
|
class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
150
|
+
/**
|
|
151
|
+
* @internal
|
|
152
|
+
*/
|
|
174
153
|
constructor(context, registry, metadata, electedSummarizerData, chunks, dataStoreAliasMap, runtimeOptions, containerScope, logger, existing, blobManagerSnapshot, _storage, requestHandler, summaryConfiguration) {
|
|
175
154
|
var _a, _b, _c, _d, _e, _f;
|
|
176
155
|
if (summaryConfiguration === void 0) { summaryConfiguration = Object.assign(Object.assign({}, exports.DefaultSummaryConfiguration), (_a = runtimeOptions.summaryOptions) === null || _a === void 0 ? void 0 : _a.summaryConfigOverrides); }
|
|
@@ -183,12 +162,19 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
183
162
|
this._storage = _storage;
|
|
184
163
|
this.requestHandler = requestHandler;
|
|
185
164
|
this.summaryConfiguration = summaryConfiguration;
|
|
186
|
-
this.opDecompressor = new opDecompressor_1.OpDecompressor();
|
|
187
165
|
this.defaultMaxConsecutiveReconnects = 7;
|
|
188
166
|
this._orderSequentiallyCalls = 0;
|
|
189
167
|
this.flushMicroTaskExists = false;
|
|
190
168
|
this.savedOps = [];
|
|
191
169
|
this.consecutiveReconnects = 0;
|
|
170
|
+
this.ensureNoDataModelChangesCalls = 0;
|
|
171
|
+
/**
|
|
172
|
+
* Tracks the number of detected reentrant ops to report,
|
|
173
|
+
* in order to self-throttle the telemetry events.
|
|
174
|
+
*
|
|
175
|
+
* This should be removed as part of ADO:2322
|
|
176
|
+
*/
|
|
177
|
+
this.opReentryCallsToReport = 5;
|
|
192
178
|
this._disposed = false;
|
|
193
179
|
this.emitDirtyDocumentEvent = true;
|
|
194
180
|
this.defaultTelemetrySignalSampleCount = 100;
|
|
@@ -248,38 +234,31 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
248
234
|
this.nextSummaryNumber = loadSummaryNumber + 1;
|
|
249
235
|
this.messageAtLastSummary = metadata === null || metadata === void 0 ? void 0 : metadata.message;
|
|
250
236
|
this._connected = this.context.connected;
|
|
251
|
-
this.chunkMap = new Map(chunks);
|
|
252
|
-
this.handleContext = new containerHandleContext_1.ContainerFluidHandleContext("", this);
|
|
253
237
|
this.mc = (0, telemetry_utils_1.loggerToMonitoringContext)(telemetry_utils_1.ChildLogger.create(this.logger, "ContainerRuntime"));
|
|
238
|
+
const opSplitter = new opLifecycle_1.OpSplitter(chunks, this.context.submitBatchFn, this.mc.config.getBoolean("Fluid.ContainerRuntime.DisableCompressionChunking") === true
|
|
239
|
+
? Number.POSITIVE_INFINITY
|
|
240
|
+
: runtimeOptions.chunkSizeInBytes, runtimeOptions.maxBatchSizeInBytes, this.mc.logger);
|
|
241
|
+
this.remoteMessageProcessor = new opLifecycle_1.RemoteMessageProcessor(opSplitter, new opLifecycle_1.OpDecompressor());
|
|
242
|
+
this.handleContext = new containerHandleContext_1.ContainerFluidHandleContext("", this);
|
|
254
243
|
if (this.summaryConfiguration.state === "enabled") {
|
|
255
244
|
this.validateSummaryHeuristicConfiguration(this.summaryConfiguration);
|
|
256
245
|
}
|
|
246
|
+
this.enableOpReentryCheck =
|
|
247
|
+
runtimeOptions.enableOpReentryCheck === true &&
|
|
248
|
+
// Allow for a break-glass config to override the options
|
|
249
|
+
this.mc.config.getBoolean("Fluid.ContainerRuntime.DisableOpReentryCheck") !== true;
|
|
257
250
|
this.summariesDisabled = this.isSummariesDisabled();
|
|
258
251
|
this.heuristicsDisabled = this.isHeuristicsDisabled();
|
|
259
|
-
this.summarizerClientElectionEnabled = this.isSummarizerClientElectionEnabled();
|
|
260
252
|
this.maxOpsSinceLastSummary = this.getMaxOpsSinceLastSummary();
|
|
261
253
|
this.initialSummarizerDelayMs = this.getInitialSummarizerDelayMs();
|
|
262
254
|
this.maxConsecutiveReconnects =
|
|
263
255
|
(_c = this.mc.config.getNumber(maxConsecutiveReconnectsKey)) !== null && _c !== void 0 ? _c : this.defaultMaxConsecutiveReconnects;
|
|
264
256
|
this._flushMode = runtimeOptions.flushMode;
|
|
265
|
-
// Provide lower soft limit - we want to have some number of ops to get efficiency in compression
|
|
266
|
-
// & bandwidth usage, but at the same time we want to send these ops sooner, to reduce overall
|
|
267
|
-
// latency of processing a batch.
|
|
268
|
-
// So there is some ballance here, that depends on compression algorithm and its efficiency working with smaller
|
|
269
|
-
// payloads. That number represents final (compressed) bits (once compression is implemented).
|
|
270
|
-
this.pendingAttachBatch = new batchManager_1.BatchManager(this.mc.logger, {
|
|
271
|
-
hardLimit: runtimeOptions.maxBatchSizeInBytes,
|
|
272
|
-
softLimit: 64 * 1024,
|
|
273
|
-
compressionOptions: runtimeOptions.compressionOptions
|
|
274
|
-
});
|
|
275
|
-
this.pendingBatch = new batchManager_1.BatchManager(this.mc.logger, {
|
|
276
|
-
hardLimit: runtimeOptions.maxBatchSizeInBytes,
|
|
277
|
-
compressionOptions: runtimeOptions.compressionOptions
|
|
278
|
-
});
|
|
279
257
|
const pendingRuntimeState = context.pendingLocalState;
|
|
280
258
|
const baseSnapshot = (_d = pendingRuntimeState === null || pendingRuntimeState === void 0 ? void 0 : pendingRuntimeState.baseSnapshot) !== null && _d !== void 0 ? _d : context.baseSnapshot;
|
|
281
259
|
const maxSnapshotCacheDurationMs = (_f = (_e = this._storage) === null || _e === void 0 ? void 0 : _e.policies) === null || _f === void 0 ? void 0 : _f.maximumCacheDurationMs;
|
|
282
|
-
if (maxSnapshotCacheDurationMs !== undefined &&
|
|
260
|
+
if (maxSnapshotCacheDurationMs !== undefined &&
|
|
261
|
+
maxSnapshotCacheDurationMs > 5 * 24 * 60 * 60 * 1000) {
|
|
283
262
|
// This is a runtime enforcement of what's already explicit in the policy's type itself,
|
|
284
263
|
// which dictates the value is either undefined or exactly 5 days in ms.
|
|
285
264
|
// As long as the actual value is less than 5 days, the assumptions GC makes here are valid.
|
|
@@ -316,34 +295,59 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
316
295
|
throwOnFailure: true,
|
|
317
296
|
// If GC should not run, let the summarizer node know so that it does not track GC state.
|
|
318
297
|
gcDisabled: !this.garbageCollector.shouldRunGC,
|
|
319
|
-
}
|
|
298
|
+
},
|
|
299
|
+
// Function to get GC data if needed. This will always be called by the root summarizer node to get GC data.
|
|
300
|
+
async (fullGC) => this.getGCDataInternal(fullGC),
|
|
301
|
+
// Function to get the GC details from the base snapshot we loaded from.
|
|
302
|
+
async () => this.garbageCollector.getBaseGCDetails());
|
|
320
303
|
if (baseSnapshot) {
|
|
321
304
|
this.summarizerNode.updateBaseSummaryState(baseSnapshot);
|
|
322
305
|
}
|
|
323
306
|
this.dataStores = new dataStores_1.DataStores((0, dataStores_1.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));
|
|
324
|
-
this.blobManager = new blobManager_1.BlobManager(this.handleContext, blobManagerSnapshot, () => this.storage, (
|
|
307
|
+
this.blobManager = new blobManager_1.BlobManager(this.handleContext, blobManagerSnapshot, () => this.storage, (localId, blobId) => {
|
|
325
308
|
if (!this.disposed) {
|
|
326
|
-
this.submit(ContainerMessageType.BlobAttach, undefined, undefined, {
|
|
309
|
+
this.submit(ContainerMessageType.BlobAttach, undefined, undefined, {
|
|
310
|
+
localId,
|
|
311
|
+
blobId,
|
|
312
|
+
});
|
|
327
313
|
}
|
|
328
|
-
}, (blobPath) => this.garbageCollector.nodeUpdated(blobPath, "Loaded"), this, pendingRuntimeState === null || pendingRuntimeState === void 0 ? void 0 : pendingRuntimeState.pendingAttachmentBlobs);
|
|
314
|
+
}, (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());
|
|
329
315
|
this.scheduleManager = new scheduleManager_1.ScheduleManager(context.deltaManager, this, () => this.clientId, telemetry_utils_1.ChildLogger.create(this.logger, "ScheduleManager"));
|
|
330
|
-
this.deltaSender = this.deltaManager;
|
|
331
316
|
this.pendingStateManager = new pendingStateManager_1.PendingStateManager({
|
|
332
317
|
applyStashedOp: this.applyStashedOp.bind(this),
|
|
333
318
|
clientId: () => this.clientId,
|
|
334
319
|
close: this.closeFn,
|
|
335
320
|
connected: () => this.connected,
|
|
336
|
-
flush: this.flush.bind(this),
|
|
337
321
|
reSubmit: this.reSubmit.bind(this),
|
|
338
322
|
rollback: this.rollback.bind(this),
|
|
339
323
|
orderSequentially: this.orderSequentially.bind(this),
|
|
340
324
|
}, pendingRuntimeState === null || pendingRuntimeState === void 0 ? void 0 : pendingRuntimeState.pending);
|
|
325
|
+
const compressionOptions = this.mc.config.getBoolean("Fluid.ContainerRuntime.DisableCompression") === true
|
|
326
|
+
? {
|
|
327
|
+
minimumBatchSizeInBytes: Number.POSITIVE_INFINITY,
|
|
328
|
+
compressionAlgorithm: CompressionAlgorithms.lz4,
|
|
329
|
+
}
|
|
330
|
+
: runtimeOptions.compressionOptions;
|
|
331
|
+
this.outbox = new opLifecycle_1.Outbox({
|
|
332
|
+
shouldSend: () => this.canSendOps(),
|
|
333
|
+
pendingStateManager: this.pendingStateManager,
|
|
334
|
+
containerContext: this.context,
|
|
335
|
+
compressor: new opLifecycle_1.OpCompressor(this.mc.logger),
|
|
336
|
+
splitter: opSplitter,
|
|
337
|
+
config: {
|
|
338
|
+
compressionOptions,
|
|
339
|
+
maxBatchSizeInBytes: runtimeOptions.maxBatchSizeInBytes,
|
|
340
|
+
enableOpReentryCheck: this.enableOpReentryCheck,
|
|
341
|
+
},
|
|
342
|
+
logger: this.mc.logger,
|
|
343
|
+
});
|
|
341
344
|
this.context.quorum.on("removeMember", (clientId) => {
|
|
342
|
-
this.
|
|
345
|
+
this.remoteMessageProcessor.clearPartialMessagesFor(clientId);
|
|
343
346
|
});
|
|
344
347
|
this.summaryCollection = new summaryCollection_1.SummaryCollection(this.deltaManager, this.logger);
|
|
345
|
-
this.dirtyContainer =
|
|
346
|
-
|
|
348
|
+
this.dirtyContainer =
|
|
349
|
+
this.context.attachState !== container_definitions_1.AttachState.Attached ||
|
|
350
|
+
this.pendingStateManager.hasPendingMessages();
|
|
347
351
|
this.context.updateDirtyContainerState(this.dirtyContainer);
|
|
348
352
|
if (this.summariesDisabled) {
|
|
349
353
|
this.mc.logger.sendTelemetryEvent({ eventName: "SummariesDisabled" });
|
|
@@ -352,7 +356,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
352
356
|
const orderedClientLogger = telemetry_utils_1.ChildLogger.create(this.logger, "OrderedClientElection");
|
|
353
357
|
const orderedClientCollection = new orderedClientElection_1.OrderedClientCollection(orderedClientLogger, this.context.deltaManager, this.context.quorum);
|
|
354
358
|
const orderedClientElectionForSummarizer = new orderedClientElection_1.OrderedClientElection(orderedClientLogger, orderedClientCollection, electedSummarizerData !== null && electedSummarizerData !== void 0 ? electedSummarizerData : this.context.deltaManager.lastSequenceNumber, summarizerClientElection_1.SummarizerClientElection.isClientEligible);
|
|
355
|
-
this.summarizerClientElection = new summarizerClientElection_1.SummarizerClientElection(orderedClientLogger, this.summaryCollection, orderedClientElectionForSummarizer, this.maxOpsSinceLastSummary
|
|
359
|
+
this.summarizerClientElection = new summarizerClientElection_1.SummarizerClientElection(orderedClientLogger, this.summaryCollection, orderedClientElectionForSummarizer, this.maxOpsSinceLastSummary);
|
|
356
360
|
if (this.context.clientDetails.type === summarizerClientElection_1.summarizerClientType) {
|
|
357
361
|
this._summarizer = new summarizer_1.Summarizer("/_summarizer", this /* ISummarizerRuntime */, () => this.summaryConfiguration, this /* ISummarizerInternalsProvider */, this.handleContext, this.summaryCollection, async (runtime) => runWhileConnectedCoordinator_1.RunWhileConnectedCoordinator.create(runtime));
|
|
358
362
|
}
|
|
@@ -411,18 +415,53 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
411
415
|
(0, connectionTelemetry_1.ReportOpPerfTelemetry)(this.context.clientId, this.deltaManager, this.logger);
|
|
412
416
|
(0, batchTracker_1.BindBatchTracker)(this, this.logger);
|
|
413
417
|
}
|
|
414
|
-
get IContainerRuntime() {
|
|
415
|
-
|
|
418
|
+
get IContainerRuntime() {
|
|
419
|
+
return this;
|
|
420
|
+
}
|
|
421
|
+
get IFluidRouter() {
|
|
422
|
+
return this;
|
|
423
|
+
}
|
|
416
424
|
/**
|
|
425
|
+
* @deprecated - use loadRuntime instead.
|
|
417
426
|
* Load the stores from a snapshot and returns the runtime.
|
|
418
427
|
* @param context - Context of the container.
|
|
419
428
|
* @param registryEntries - Mapping to the stores.
|
|
420
429
|
* @param requestHandler - Request handlers for the container runtime
|
|
421
430
|
* @param runtimeOptions - Additional options to be passed to the runtime
|
|
422
431
|
* @param existing - (optional) When loading from an existing snapshot. Precedes context.existing if provided
|
|
432
|
+
* @param containerRuntimeCtor - (optional) Constructor to use to create the ContainerRuntime instance. This
|
|
433
|
+
* allows mixin classes to leverage this method to define their own async initializer.
|
|
423
434
|
*/
|
|
424
|
-
static async load(context, registryEntries, requestHandler, runtimeOptions = {}, containerScope = context.scope, existing) {
|
|
425
|
-
|
|
435
|
+
static async load(context, registryEntries, requestHandler, runtimeOptions = {}, containerScope = context.scope, existing, containerRuntimeCtor = ContainerRuntime) {
|
|
436
|
+
let existingFlag = true;
|
|
437
|
+
if (!existing) {
|
|
438
|
+
existingFlag = false;
|
|
439
|
+
}
|
|
440
|
+
return this.loadRuntime({
|
|
441
|
+
context,
|
|
442
|
+
registryEntries,
|
|
443
|
+
existing: existingFlag,
|
|
444
|
+
requestHandler,
|
|
445
|
+
runtimeOptions,
|
|
446
|
+
containerScope,
|
|
447
|
+
containerRuntimeCtor,
|
|
448
|
+
});
|
|
449
|
+
}
|
|
450
|
+
/**
|
|
451
|
+
* Load the stores from a snapshot and returns the runtime.
|
|
452
|
+
* @param params - An object housing the runtime properties:
|
|
453
|
+
* - context - Context of the container.
|
|
454
|
+
* - registryEntries - Mapping to the stores.
|
|
455
|
+
* - existing - When loading from an existing snapshot
|
|
456
|
+
* - requestHandler - Request handlers for the container runtime
|
|
457
|
+
* - runtimeOptions - Additional options to be passed to the runtime
|
|
458
|
+
* - containerScope - runtime services provided with context
|
|
459
|
+
* - containerRuntimeCtor - Constructor to use to create the ContainerRuntime instance.
|
|
460
|
+
* This allows mixin classes to leverage this method to define their own async initializer.
|
|
461
|
+
*/
|
|
462
|
+
static async loadRuntime(params) {
|
|
463
|
+
var _a, _b, _c, _d;
|
|
464
|
+
const { context, registryEntries, existing, requestHandler, runtimeOptions = {}, containerScope = {}, containerRuntimeCtor = ContainerRuntime, } = params;
|
|
426
465
|
// If taggedLogger exists, use it. Otherwise, wrap the vanilla logger:
|
|
427
466
|
// back-compat: Remove the TaggedLoggerAdapter fallback once all the host are using loader > 0.45
|
|
428
467
|
const backCompatContext = context;
|
|
@@ -432,13 +471,17 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
432
471
|
runtimeVersion: packageVersion_1.pkgVersion,
|
|
433
472
|
},
|
|
434
473
|
});
|
|
435
|
-
const { summaryOptions = {}, gcOptions = {}, loadSequenceNumberVerification = "close", flushMode = defaultFlushMode, enableOfflineLoad = false, compressionOptions = {
|
|
436
|
-
|
|
474
|
+
const { summaryOptions = {}, gcOptions = {}, loadSequenceNumberVerification = "close", flushMode = defaultFlushMode, enableOfflineLoad = false, compressionOptions = {
|
|
475
|
+
minimumBatchSizeInBytes: Number.POSITIVE_INFINITY,
|
|
476
|
+
compressionAlgorithm: CompressionAlgorithms.lz4,
|
|
477
|
+
}, maxBatchSizeInBytes = defaultMaxBatchSizeInBytes, chunkSizeInBytes = Number.POSITIVE_INFINITY, enableOpReentryCheck = false, } = runtimeOptions;
|
|
437
478
|
const pendingRuntimeState = context.pendingLocalState;
|
|
438
479
|
const baseSnapshot = (_b = pendingRuntimeState === null || pendingRuntimeState === void 0 ? void 0 : pendingRuntimeState.baseSnapshot) !== null && _b !== void 0 ? _b : context.baseSnapshot;
|
|
439
|
-
const storage = !pendingRuntimeState
|
|
440
|
-
context.storage
|
|
441
|
-
new serializedSnapshotStorage_1.SerializedSnapshotStorage(() => {
|
|
480
|
+
const storage = !pendingRuntimeState
|
|
481
|
+
? context.storage
|
|
482
|
+
: new serializedSnapshotStorage_1.SerializedSnapshotStorage(() => {
|
|
483
|
+
return context.storage;
|
|
484
|
+
}, pendingRuntimeState.snapshotBlobs);
|
|
442
485
|
const registry = new dataStoreRegistry_1.FluidDataStoreRegistry(registryEntries);
|
|
443
486
|
const tryFetchBlob = async (blobName) => {
|
|
444
487
|
const blobId = baseSnapshot === null || baseSnapshot === void 0 ? void 0 : baseSnapshot.blobs[blobName];
|
|
@@ -469,7 +512,8 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
469
512
|
if (!pendingRuntimeState && runtimeSequenceNumber !== undefined) {
|
|
470
513
|
const protocolSequenceNumber = context.deltaManager.initialSequenceNumber;
|
|
471
514
|
// Unless bypass is explicitly set, then take action when sequence numbers mismatch.
|
|
472
|
-
if (loadSequenceNumberVerification !== "bypass" &&
|
|
515
|
+
if (loadSequenceNumberVerification !== "bypass" &&
|
|
516
|
+
runtimeSequenceNumber !== protocolSequenceNumber) {
|
|
473
517
|
// "Load from summary, runtime metadata sequenceNumber !== initialSequenceNumber"
|
|
474
518
|
const error = new container_utils_1.DataCorruptionError(
|
|
475
519
|
// pre-0.58 error message: SummaryMetadataMismatch
|
|
@@ -478,11 +522,13 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
478
522
|
logger.sendErrorEvent({ eventName: "SequenceNumberMismatch" }, error);
|
|
479
523
|
}
|
|
480
524
|
else {
|
|
525
|
+
// Call both close and dispose as closeFn implementation will no longer dispose runtime in future
|
|
481
526
|
context.closeFn(error);
|
|
527
|
+
(_d = context.disposeFn) === null || _d === void 0 ? void 0 : _d.call(context, error);
|
|
482
528
|
}
|
|
483
529
|
}
|
|
484
530
|
}
|
|
485
|
-
const runtime = new
|
|
531
|
+
const runtime = new containerRuntimeCtor(context, registry, metadata, electedSummarizerData, chunks !== null && chunks !== void 0 ? chunks : [], aliases !== null && aliases !== void 0 ? aliases : [], {
|
|
486
532
|
summaryOptions,
|
|
487
533
|
gcOptions,
|
|
488
534
|
loadSequenceNumberVerification,
|
|
@@ -490,6 +536,8 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
490
536
|
enableOfflineLoad,
|
|
491
537
|
compressionOptions,
|
|
492
538
|
maxBatchSizeInBytes,
|
|
539
|
+
chunkSizeInBytes,
|
|
540
|
+
enableOpReentryCheck,
|
|
493
541
|
}, containerScope, logger, loadExisting, blobManagerSnapshot, storage, requestHandler);
|
|
494
542
|
if (pendingRuntimeState) {
|
|
495
543
|
await runtime.processSavedOps(pendingRuntimeState);
|
|
@@ -519,8 +567,18 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
519
567
|
// eslint-disable-next-line @typescript-eslint/unbound-method
|
|
520
568
|
return this.reSubmit;
|
|
521
569
|
}
|
|
570
|
+
get disposeFn() {
|
|
571
|
+
var _a;
|
|
572
|
+
// In old loaders without dispose functionality, closeFn is equivalent but will also switch container to readonly mode
|
|
573
|
+
return (_a = this.context.disposeFn) !== null && _a !== void 0 ? _a : this.context.closeFn;
|
|
574
|
+
}
|
|
522
575
|
get closeFn() {
|
|
523
|
-
|
|
576
|
+
// Also call disposeFn to retain functionality of runtime being disposed on close
|
|
577
|
+
return (error) => {
|
|
578
|
+
var _a, _b;
|
|
579
|
+
this.context.closeFn(error);
|
|
580
|
+
(_b = (_a = this.context).disposeFn) === null || _b === void 0 ? void 0 : _b.call(_a, error);
|
|
581
|
+
};
|
|
524
582
|
}
|
|
525
583
|
get flushMode() {
|
|
526
584
|
return this._flushMode;
|
|
@@ -537,6 +595,23 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
537
595
|
get IFluidHandleContext() {
|
|
538
596
|
return this.handleContext;
|
|
539
597
|
}
|
|
598
|
+
/**
|
|
599
|
+
* Invokes the given callback and expects that no ops are submitted
|
|
600
|
+
* until execution finishes. If an op is submitted, an error will be raised.
|
|
601
|
+
*
|
|
602
|
+
* Can be disabled by feature gate `Fluid.ContainerRuntime.DisableOpReentryCheck`
|
|
603
|
+
*
|
|
604
|
+
* @param callback - the callback to be invoked
|
|
605
|
+
*/
|
|
606
|
+
ensureNoDataModelChanges(callback) {
|
|
607
|
+
this.ensureNoDataModelChangesCalls++;
|
|
608
|
+
try {
|
|
609
|
+
return callback();
|
|
610
|
+
}
|
|
611
|
+
finally {
|
|
612
|
+
this.ensureNoDataModelChangesCalls--;
|
|
613
|
+
}
|
|
614
|
+
}
|
|
540
615
|
get connected() {
|
|
541
616
|
return this._connected;
|
|
542
617
|
}
|
|
@@ -545,51 +620,20 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
545
620
|
var _a;
|
|
546
621
|
return (_a = this.summarizerClientElection) === null || _a === void 0 ? void 0 : _a.electedClientId;
|
|
547
622
|
}
|
|
548
|
-
get disposed() {
|
|
549
|
-
|
|
550
|
-
return this.pendingBatch.empty && this.pendingAttachBatch.empty;
|
|
623
|
+
get disposed() {
|
|
624
|
+
return this._disposed;
|
|
551
625
|
}
|
|
552
626
|
get summarizer() {
|
|
553
627
|
(0, common_utils_1.assert)(this._summarizer !== undefined, 0x257 /* "This is not summarizing container" */);
|
|
554
628
|
return this._summarizer;
|
|
555
629
|
}
|
|
556
630
|
isSummariesDisabled() {
|
|
557
|
-
// back-compat: disableSummaries was moved from ISummaryRuntimeOptions
|
|
558
|
-
// to ISummaryConfiguration in 0.60.
|
|
559
|
-
if (this.runtimeOptions.summaryOptions.disableSummaries === true) {
|
|
560
|
-
return true;
|
|
561
|
-
}
|
|
562
631
|
return this.summaryConfiguration.state === "disabled";
|
|
563
632
|
}
|
|
564
633
|
isHeuristicsDisabled() {
|
|
565
|
-
var _a;
|
|
566
|
-
// back-compat: disableHeuristics was moved from ISummarizerOptions
|
|
567
|
-
// to ISummaryConfiguration in 0.60.
|
|
568
|
-
if (((_a = this.runtimeOptions.summaryOptions.summarizerOptions) === null || _a === void 0 ? void 0 : _a.disableHeuristics) === true) {
|
|
569
|
-
return true;
|
|
570
|
-
}
|
|
571
634
|
return this.summaryConfiguration.state === "disableHeuristics";
|
|
572
635
|
}
|
|
573
|
-
isSummarizerClientElectionEnabled() {
|
|
574
|
-
var _a;
|
|
575
|
-
if (this.mc.config.getBoolean("Fluid.ContainerRuntime.summarizerClientElection")) {
|
|
576
|
-
return (_a = this.mc.config.getBoolean("Fluid.ContainerRuntime.summarizerClientElection")) !== null && _a !== void 0 ? _a : true;
|
|
577
|
-
}
|
|
578
|
-
// back-compat: summarizerClientElection was moved from ISummaryRuntimeOptions
|
|
579
|
-
// to ISummaryConfiguration in 0.60.
|
|
580
|
-
if (this.runtimeOptions.summaryOptions.summarizerClientElection === true) {
|
|
581
|
-
return true;
|
|
582
|
-
}
|
|
583
|
-
return this.summaryConfiguration.state !== "disabled"
|
|
584
|
-
? this.summaryConfiguration.summarizerClientElection === true
|
|
585
|
-
: false;
|
|
586
|
-
}
|
|
587
636
|
getMaxOpsSinceLastSummary() {
|
|
588
|
-
// back-compat: maxOpsSinceLastSummary was moved from ISummaryRuntimeOptions
|
|
589
|
-
// to ISummaryConfiguration in 0.60.
|
|
590
|
-
if (this.runtimeOptions.summaryOptions.maxOpsSinceLastSummary !== undefined) {
|
|
591
|
-
return this.runtimeOptions.summaryOptions.maxOpsSinceLastSummary;
|
|
592
|
-
}
|
|
593
637
|
return this.summaryConfiguration.state !== "disabled"
|
|
594
638
|
? this.summaryConfiguration.maxOpsSinceLastSummary
|
|
595
639
|
: 0;
|
|
@@ -688,7 +732,8 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
688
732
|
status: 200,
|
|
689
733
|
mimeType: "fluid/object",
|
|
690
734
|
value: blob,
|
|
691
|
-
}
|
|
735
|
+
}
|
|
736
|
+
: (0, runtime_utils_1.create404Response)(request);
|
|
692
737
|
}
|
|
693
738
|
else if (requestParser.pathParts.length > 0) {
|
|
694
739
|
const dataStore = await this.getDataStoreFromRequest(id, request);
|
|
@@ -709,16 +754,20 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
709
754
|
return (_a = this.dataStores.aliases.get(maybeAlias)) !== null && _a !== void 0 ? _a : maybeAlias;
|
|
710
755
|
}
|
|
711
756
|
async getDataStoreFromRequest(id, request) {
|
|
712
|
-
var _a, _b, _c, _d
|
|
713
|
-
const
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
757
|
+
var _a, _b, _c, _d;
|
|
758
|
+
const headerData = {};
|
|
759
|
+
if (typeof ((_a = request.headers) === null || _a === void 0 ? void 0 : _a[RuntimeHeaders.wait]) === "boolean") {
|
|
760
|
+
headerData.wait = request.headers[RuntimeHeaders.wait];
|
|
761
|
+
}
|
|
762
|
+
if (typeof ((_b = request.headers) === null || _b === void 0 ? void 0 : _b[RuntimeHeaders.viaHandle]) === "boolean") {
|
|
763
|
+
headerData.viaHandle = request.headers[RuntimeHeaders.viaHandle];
|
|
764
|
+
}
|
|
765
|
+
if (typeof ((_c = request.headers) === null || _c === void 0 ? void 0 : _c[exports.AllowTombstoneRequestHeaderKey]) === "boolean") {
|
|
766
|
+
headerData.allowTombstone = request.headers[exports.AllowTombstoneRequestHeaderKey];
|
|
767
|
+
}
|
|
719
768
|
await this.dataStores.waitIfPendingAlias(id);
|
|
720
769
|
const internalId = this.internalId(id);
|
|
721
|
-
const dataStoreContext = await this.dataStores.getDataStore(internalId,
|
|
770
|
+
const dataStoreContext = await this.dataStores.getDataStore(internalId, headerData);
|
|
722
771
|
/**
|
|
723
772
|
* If GC should run and this an external app request with "externalRequest" header, we need to return
|
|
724
773
|
* an error if the data store being requested is marked as unreferenced as per the data store's base
|
|
@@ -727,7 +776,8 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
727
776
|
* This is a workaround to handle scenarios where a data store shared with an external app is deleted
|
|
728
777
|
* and marked as unreferenced by GC. Returning an error will fail to load the data store for the app.
|
|
729
778
|
*/
|
|
730
|
-
if (((
|
|
779
|
+
if (((_d = request.headers) === null || _d === void 0 ? void 0 : _d[RuntimeHeaders.externalRequest]) &&
|
|
780
|
+
this.garbageCollector.shouldRunGC) {
|
|
731
781
|
// The data store is referenced if used routes in the base summary has a route to self.
|
|
732
782
|
// Older documents may not have used routes in the summary. They are considered referenced.
|
|
733
783
|
const usedRoutes = (await dataStoreContext.getBaseGCDetails()).usedRoutes;
|
|
@@ -756,8 +806,8 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
756
806
|
addContainerStateToSummary(summaryTree, fullTree, trackState, telemetryContext) {
|
|
757
807
|
var _a;
|
|
758
808
|
this.addMetadataToSummary(summaryTree);
|
|
759
|
-
if (this.
|
|
760
|
-
const content = JSON.stringify([...this.
|
|
809
|
+
if (this.remoteMessageProcessor.partialMessages.size > 0) {
|
|
810
|
+
const content = JSON.stringify([...this.remoteMessageProcessor.partialMessages]);
|
|
761
811
|
(0, runtime_utils_1.addBlobToSummary)(summaryTree, summaryFormat_1.chunksBlobName, content);
|
|
762
812
|
}
|
|
763
813
|
const dataStoreAliases = this.dataStores.aliases;
|
|
@@ -776,7 +826,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
776
826
|
}
|
|
777
827
|
const gcSummary = this.garbageCollector.summarize(fullTree, trackState, telemetryContext);
|
|
778
828
|
if (gcSummary !== undefined) {
|
|
779
|
-
(0, runtime_utils_1.addSummarizeResultToSummary)(summaryTree,
|
|
829
|
+
(0, runtime_utils_1.addSummarizeResultToSummary)(summaryTree, runtime_definitions_1.gcTreeKey, gcSummary);
|
|
780
830
|
}
|
|
781
831
|
}
|
|
782
832
|
// Track how many times the container tries to reconnect with pending messages.
|
|
@@ -901,9 +951,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
901
951
|
if (reconnection) {
|
|
902
952
|
this.consecutiveReconnects++;
|
|
903
953
|
if (!this.shouldContinueReconnecting()) {
|
|
904
|
-
this.closeFn(container_utils_1.DataProcessingError.create(
|
|
905
|
-
// eslint-disable-next-line max-len
|
|
906
|
-
"Runtime detected too many reconnects with no progress syncing local ops. Batch of ops is likely too large (over 1Mb)", "setConnectionState", undefined, {
|
|
954
|
+
this.closeFn(container_utils_1.DataProcessingError.create("Runtime detected too many reconnects with no progress syncing local ops.", "setConnectionState", undefined, {
|
|
907
955
|
dataLoss: 1,
|
|
908
956
|
attempts: this.consecutiveReconnects,
|
|
909
957
|
pendingMessages: this.pendingStateManager.pendingMessagesCount,
|
|
@@ -921,34 +969,23 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
921
969
|
process(messageArg, local) {
|
|
922
970
|
var _a;
|
|
923
971
|
this.verifyNotClosed();
|
|
924
|
-
// Do shallow copy of message, as methods below will modify it.
|
|
925
|
-
// There might be multiple container instances receiving same message
|
|
926
|
-
// We do not need to make deep copy, as each layer will just replace message.content itself,
|
|
927
|
-
// but would not modify contents details
|
|
928
|
-
let message = Object.assign({}, messageArg);
|
|
929
|
-
// back-compat: ADO #1385: eventually should become unconditional, but only for runtime messages!
|
|
930
|
-
// System message may have no contents, or in some cases (mostly for back-compat) they may have actual objects.
|
|
931
|
-
// Old ops may contain empty string (I assume noops).
|
|
932
|
-
if (typeof message.contents === "string" && message.contents !== "") {
|
|
933
|
-
message.contents = JSON.parse(message.contents);
|
|
934
|
-
}
|
|
935
|
-
message = this.opDecompressor.processMessage(message);
|
|
936
|
-
// Caveat: This will return false for runtime message in very old format, that are used in snapshot tests
|
|
937
|
-
// This format was not shipped to production workflows.
|
|
938
|
-
const runtimeMessage = unpackRuntimeMessage(message);
|
|
939
972
|
if ((_a = this.mc.config.getBoolean("enableOfflineLoad")) !== null && _a !== void 0 ? _a : this.runtimeOptions.enableOfflineLoad) {
|
|
940
973
|
this.savedOps.push(messageArg);
|
|
941
974
|
}
|
|
975
|
+
// Whether or not the message is actually a runtime message.
|
|
976
|
+
// It may be a legacy runtime message (ie already unpacked and ContainerMessageType)
|
|
977
|
+
// or something different, like a system message.
|
|
978
|
+
const runtimeMessage = messageArg.type === protocol_definitions_1.MessageType.Operation;
|
|
979
|
+
// Do shallow copy of message, as the processing flow will modify it.
|
|
980
|
+
const messageCopy = Object.assign({}, messageArg);
|
|
981
|
+
const message = this.remoteMessageProcessor.process(messageCopy);
|
|
942
982
|
// Surround the actual processing of the operation with messages to the schedule manager indicating
|
|
943
983
|
// the beginning and end. This allows it to emit appropriate events and/or pause the processing of new
|
|
944
984
|
// messages once a batch has been fully processed.
|
|
945
985
|
this.scheduleManager.beforeOpProcessing(message);
|
|
946
986
|
try {
|
|
947
|
-
// Chunk processing must come first given that we will transform the message to the unchunked version
|
|
948
|
-
// once all pieces are available
|
|
949
|
-
message = this.processRemoteChunkedMessage(message);
|
|
950
987
|
let localOpMetadata;
|
|
951
|
-
if (local && runtimeMessage) {
|
|
988
|
+
if (local && runtimeMessage && message.type !== ContainerMessageType.ChunkedOp) {
|
|
952
989
|
localOpMetadata = this.pendingStateManager.processPendingLocalMessage(message);
|
|
953
990
|
}
|
|
954
991
|
// If there are no more pending messages after processing a local message,
|
|
@@ -1021,7 +1058,8 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1021
1058
|
if (message.clientId === this.clientId && this.connected) {
|
|
1022
1059
|
// Check to see if the signal was lost.
|
|
1023
1060
|
if (this._perfSignalData.trackingSignalSequenceNumber !== undefined &&
|
|
1024
|
-
envelope.clientSignalSequenceNumber >
|
|
1061
|
+
envelope.clientSignalSequenceNumber >
|
|
1062
|
+
this._perfSignalData.trackingSignalSequenceNumber) {
|
|
1025
1063
|
this._perfSignalData.signalsLost++;
|
|
1026
1064
|
this._perfSignalData.trackingSignalSequenceNumber = undefined;
|
|
1027
1065
|
this.logger.sendErrorEvent({
|
|
@@ -1032,7 +1070,8 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1032
1070
|
clientSignalSequenceNumber: envelope.clientSignalSequenceNumber,
|
|
1033
1071
|
});
|
|
1034
1072
|
}
|
|
1035
|
-
else if (envelope.clientSignalSequenceNumber ===
|
|
1073
|
+
else if (envelope.clientSignalSequenceNumber ===
|
|
1074
|
+
this._perfSignalData.trackingSignalSequenceNumber) {
|
|
1036
1075
|
this.sendSignalTelemetryEvent(envelope.clientSignalSequenceNumber);
|
|
1037
1076
|
this._perfSignalData.trackingSignalSequenceNumber = undefined;
|
|
1038
1077
|
}
|
|
@@ -1050,7 +1089,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1050
1089
|
async getRootDataStoreChannel(id, wait = true) {
|
|
1051
1090
|
await this.dataStores.waitIfPendingAlias(id);
|
|
1052
1091
|
const internalId = this.internalId(id);
|
|
1053
|
-
const context = await this.dataStores.getDataStore(internalId, wait
|
|
1092
|
+
const context = await this.dataStores.getDataStore(internalId, { wait });
|
|
1054
1093
|
(0, common_utils_1.assert)(await context.isRoot(), 0x12b /* "did not get root data store" */);
|
|
1055
1094
|
return context.realize();
|
|
1056
1095
|
}
|
|
@@ -1060,74 +1099,21 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1060
1099
|
*/
|
|
1061
1100
|
flush() {
|
|
1062
1101
|
(0, common_utils_1.assert)(this._orderSequentiallyCalls === 0, 0x24c /* "Cannot call `flush()` from `orderSequentially`'s callback" */);
|
|
1063
|
-
this.
|
|
1064
|
-
|
|
1065
|
-
(0, common_utils_1.assert)(this.emptyBatch, 0x3cf /* reentrancy */);
|
|
1066
|
-
}
|
|
1067
|
-
flushBatch(batch) {
|
|
1068
|
-
var _a;
|
|
1069
|
-
const length = batch.length;
|
|
1070
|
-
if (length > 1) {
|
|
1071
|
-
batch[0].metadata = Object.assign(Object.assign({}, batch[0].metadata), { batch: true });
|
|
1072
|
-
batch[length - 1].metadata = Object.assign(Object.assign({}, batch[length - 1].metadata), { batch: false });
|
|
1073
|
-
// This assert fires for the following reason (there might be more cases like that):
|
|
1074
|
-
// AgentScheduler will send ops in response to ConsensusRegisterCollection's "atomicChanged" event handler,
|
|
1075
|
-
// i.e. in the middle of op processing!
|
|
1076
|
-
// Sending ops while processing ops is not good idea - it's not defined when
|
|
1077
|
-
// referenceSequenceNumber changes in op processing sequence (at the beginning or end of op processing),
|
|
1078
|
-
// If we send ops in response to processing multiple ops, then we for sure hit this assert!
|
|
1079
|
-
// Tracked via ADO #1834
|
|
1080
|
-
// assert(batch[0].referenceSequenceNumber === batch[length - 1].referenceSequenceNumber,
|
|
1081
|
-
// "Batch should be generated synchronously, without processing ops in the middle!");
|
|
1082
|
-
}
|
|
1083
|
-
let clientSequenceNumber = -1;
|
|
1084
|
-
// Did we disconnect in the middle of turn-based batch?
|
|
1085
|
-
// If so, do nothing, as pending state manager will resubmit it correctly on reconnect.
|
|
1086
|
-
if (this.canSendOps()) {
|
|
1087
|
-
if (this.context.submitBatchFn !== undefined) {
|
|
1088
|
-
const batchToSend = [];
|
|
1089
|
-
for (const message of batch) {
|
|
1090
|
-
batchToSend.push({ contents: message.contents, metadata: message.metadata });
|
|
1091
|
-
}
|
|
1092
|
-
// returns clientSequenceNumber of last message in a batch
|
|
1093
|
-
clientSequenceNumber = this.context.submitBatchFn(batchToSend);
|
|
1094
|
-
}
|
|
1095
|
-
else {
|
|
1096
|
-
// Legacy path - supporting old loader versions. Can be removed only when LTS moves above
|
|
1097
|
-
// version that has support for batches (submitBatchFn)
|
|
1098
|
-
for (const message of batch) {
|
|
1099
|
-
// Legacy path doesn't support compressed payloads and will submit uncompressed payload anyways
|
|
1100
|
-
if ((_a = message.metadata) === null || _a === void 0 ? void 0 : _a.compressed) {
|
|
1101
|
-
delete message.metadata.compressed;
|
|
1102
|
-
}
|
|
1103
|
-
clientSequenceNumber = this.context.submitFn(protocol_definitions_1.MessageType.Operation, message.deserializedContent, true, // batch
|
|
1104
|
-
message.metadata);
|
|
1105
|
-
}
|
|
1106
|
-
this.deltaSender.flush();
|
|
1107
|
-
}
|
|
1108
|
-
// Convert from clientSequenceNumber of last message in the batch to clientSequenceNumber of first message.
|
|
1109
|
-
clientSequenceNumber -= batch.length - 1;
|
|
1110
|
-
(0, common_utils_1.assert)(clientSequenceNumber >= 0, 0x3d0 /* clientSequenceNumber can't be negative */);
|
|
1111
|
-
}
|
|
1112
|
-
// Let the PendingStateManager know that a message was submitted.
|
|
1113
|
-
// In future, need to shift toward keeping batch as a whole!
|
|
1114
|
-
for (const message of batch) {
|
|
1115
|
-
this.pendingStateManager.onSubmitMessage(message.deserializedContent.type, clientSequenceNumber, message.referenceSequenceNumber, message.deserializedContent.contents, message.localOpMetadata, message.metadata);
|
|
1116
|
-
clientSequenceNumber++;
|
|
1117
|
-
}
|
|
1118
|
-
this.pendingStateManager.onFlush();
|
|
1102
|
+
this.outbox.flush();
|
|
1103
|
+
(0, common_utils_1.assert)(this.outbox.isEmpty, 0x3cf /* reentrancy */);
|
|
1119
1104
|
}
|
|
1120
1105
|
orderSequentially(callback) {
|
|
1121
1106
|
let checkpoint;
|
|
1107
|
+
let result;
|
|
1122
1108
|
if (this.mc.config.getBoolean("Fluid.ContainerRuntime.EnableRollback")) {
|
|
1123
1109
|
// Note: we are not touching this.pendingAttachBatch here, for two reasons:
|
|
1124
1110
|
// 1. It would not help, as we flush attach ops as they become available.
|
|
1125
1111
|
// 2. There is no way to undo process of data store creation.
|
|
1126
|
-
checkpoint = this.
|
|
1112
|
+
checkpoint = this.outbox.checkpoint().mainBatch;
|
|
1127
1113
|
}
|
|
1128
1114
|
try {
|
|
1129
1115
|
this._orderSequentiallyCalls++;
|
|
1130
|
-
callback();
|
|
1116
|
+
result = callback();
|
|
1131
1117
|
}
|
|
1132
1118
|
catch (error) {
|
|
1133
1119
|
if (checkpoint) {
|
|
@@ -1152,9 +1138,11 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1152
1138
|
finally {
|
|
1153
1139
|
this._orderSequentiallyCalls--;
|
|
1154
1140
|
}
|
|
1155
|
-
|
|
1141
|
+
// We don't flush on TurnBased since we expect all messages in the same JS turn to be part of the same batch
|
|
1142
|
+
if (this.flushMode !== runtime_definitions_1.FlushMode.TurnBased && this._orderSequentiallyCalls === 0) {
|
|
1156
1143
|
this.flush();
|
|
1157
1144
|
}
|
|
1145
|
+
return result;
|
|
1158
1146
|
}
|
|
1159
1147
|
async createDataStore(pkg) {
|
|
1160
1148
|
const internalId = (0, uuid_1.v4)();
|
|
@@ -1170,7 +1158,9 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1170
1158
|
return this.dataStores.createDetachedDataStoreCore(pkg, false);
|
|
1171
1159
|
}
|
|
1172
1160
|
async _createDataStoreWithProps(pkg, props, id = (0, uuid_1.v4)()) {
|
|
1173
|
-
const fluidDataStore = await this.dataStores
|
|
1161
|
+
const fluidDataStore = await this.dataStores
|
|
1162
|
+
._createFluidDataStoreContext(Array.isArray(pkg) ? pkg : [pkg], id, props)
|
|
1163
|
+
.realize();
|
|
1174
1164
|
return (0, dataStore_1.channelToDataStore)(fluidDataStore, id, this, this.dataStores, this.mc.logger);
|
|
1175
1165
|
}
|
|
1176
1166
|
async _createDataStore(pkg, id = (0, uuid_1.v4)(), props) {
|
|
@@ -1307,7 +1297,10 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1307
1297
|
}
|
|
1308
1298
|
const telemetryContext = new runtime_utils_1.TelemetryContext();
|
|
1309
1299
|
const { stats, summary } = await this.summarizerNode.summarize(fullTree, trackState, telemetryContext);
|
|
1310
|
-
this.logger.sendTelemetryEvent({
|
|
1300
|
+
this.logger.sendTelemetryEvent({
|
|
1301
|
+
eventName: "SummarizeTelemetry",
|
|
1302
|
+
details: telemetryContext.serialize(),
|
|
1303
|
+
});
|
|
1311
1304
|
(0, common_utils_1.assert)(summary.type === protocol_definitions_1.SummaryType.Tree, 0x12f /* "Container Runtime's summarize should always return a tree" */);
|
|
1312
1305
|
return { stats, summary, gcStats };
|
|
1313
1306
|
}
|
|
@@ -1320,6 +1313,9 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1320
1313
|
async updateStateBeforeGC() {
|
|
1321
1314
|
return this.dataStores.updateStateBeforeGC();
|
|
1322
1315
|
}
|
|
1316
|
+
async getGCDataInternal(fullGC) {
|
|
1317
|
+
return this.dataStores.getGCData(fullGC);
|
|
1318
|
+
}
|
|
1323
1319
|
/**
|
|
1324
1320
|
* Implementation of IGarbageCollectionRuntime::getGCData.
|
|
1325
1321
|
* Generates and returns the GC data for this container.
|
|
@@ -1327,7 +1323,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1327
1323
|
*/
|
|
1328
1324
|
async getGCData(fullGC) {
|
|
1329
1325
|
const builder = new garbage_collector_1.GCDataBuilder();
|
|
1330
|
-
const dsGCData = await this.
|
|
1326
|
+
const dsGCData = await this.summarizerNode.getGCData(fullGC);
|
|
1331
1327
|
builder.addNodes(dsGCData.gcNodes);
|
|
1332
1328
|
const blobsGCData = this.blobManager.getGCData(fullGC);
|
|
1333
1329
|
builder.addNodes(blobsGCData.gcNodes);
|
|
@@ -1343,37 +1339,26 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1343
1339
|
// summarizing is required and asserted by the the summarizer node. We are the root and are
|
|
1344
1340
|
// always referenced, so the used routes is only self-route (empty string).
|
|
1345
1341
|
this.summarizerNode.updateUsedRoutes([""]);
|
|
1346
|
-
const
|
|
1347
|
-
|
|
1348
|
-
if (route.split("/")[1] !== blobManager_1.BlobManager.basePath) {
|
|
1349
|
-
dataStoreUsedRoutes.push(route);
|
|
1350
|
-
}
|
|
1351
|
-
}
|
|
1352
|
-
return this.dataStores.updateUsedRoutes(dataStoreUsedRoutes);
|
|
1342
|
+
const { dataStoreRoutes } = this.getDataStoreAndBlobManagerRoutes(usedRoutes);
|
|
1343
|
+
this.dataStores.updateUsedRoutes(dataStoreRoutes);
|
|
1353
1344
|
}
|
|
1354
1345
|
/**
|
|
1355
|
-
* This is called to update objects whose routes are unused.
|
|
1356
|
-
*
|
|
1357
|
-
* @param unusedRoutes - The routes that are unused in all data stores in this Container.
|
|
1358
|
-
* @param tombstone - if true, the objects corresponding to unused routes are marked tombstones. Otherwise, they
|
|
1359
|
-
* are deleted.
|
|
1346
|
+
* This is called to update objects whose routes are unused.
|
|
1347
|
+
* @param unusedRoutes - Data store and attachment blob routes that are unused in this Container.
|
|
1360
1348
|
*/
|
|
1361
|
-
updateUnusedRoutes(unusedRoutes
|
|
1362
|
-
const
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
}
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
this.blobManager.deleteUnusedRoutes(blobManagerUnusedRoutes);
|
|
1375
|
-
}
|
|
1376
|
-
this.dataStores.updateUnusedRoutes(dataStoreUnusedRoutes, tombstone);
|
|
1349
|
+
updateUnusedRoutes(unusedRoutes) {
|
|
1350
|
+
const { blobManagerRoutes, dataStoreRoutes } = this.getDataStoreAndBlobManagerRoutes(unusedRoutes);
|
|
1351
|
+
this.blobManager.updateUnusedRoutes(blobManagerRoutes);
|
|
1352
|
+
this.dataStores.updateUnusedRoutes(dataStoreRoutes);
|
|
1353
|
+
}
|
|
1354
|
+
/**
|
|
1355
|
+
* This is called to update objects that are tombstones.
|
|
1356
|
+
* @param tombstonedRoutes - Data store and attachment blob routes that are tombstones in this Container.
|
|
1357
|
+
*/
|
|
1358
|
+
updateTombstonedRoutes(tombstonedRoutes) {
|
|
1359
|
+
const { blobManagerRoutes, dataStoreRoutes } = this.getDataStoreAndBlobManagerRoutes(tombstonedRoutes);
|
|
1360
|
+
this.blobManager.updateTombstonedRoutes(blobManagerRoutes);
|
|
1361
|
+
this.dataStores.updateTombstonedRoutes(dataStoreRoutes);
|
|
1377
1362
|
}
|
|
1378
1363
|
/**
|
|
1379
1364
|
* Returns a server generated referenced timestamp to be used to track unreferenced nodes by GC.
|
|
@@ -1402,7 +1387,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1402
1387
|
async getGCNodePackagePath(nodePath) {
|
|
1403
1388
|
switch (this.getNodeType(nodePath)) {
|
|
1404
1389
|
case garbageCollection_1.GCNodeType.Blob:
|
|
1405
|
-
return [
|
|
1390
|
+
return [blobManager_1.BlobManager.basePath];
|
|
1406
1391
|
case garbageCollection_1.GCNodeType.DataStore:
|
|
1407
1392
|
case garbageCollection_1.GCNodeType.SubDataStore:
|
|
1408
1393
|
return this.dataStores.getDataStorePackagePath(nodePath);
|
|
@@ -1420,6 +1405,25 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1420
1405
|
}
|
|
1421
1406
|
return true;
|
|
1422
1407
|
}
|
|
1408
|
+
/**
|
|
1409
|
+
* From a given list of routes, separate and return routes that belong to blob manager and data stores.
|
|
1410
|
+
* @param routes - A list of routes that can belong to data stores or blob manager.
|
|
1411
|
+
* @returns - Two route lists - One that contains routes for blob manager and another one that contains routes
|
|
1412
|
+
* for data stores.
|
|
1413
|
+
*/
|
|
1414
|
+
getDataStoreAndBlobManagerRoutes(routes) {
|
|
1415
|
+
const blobManagerRoutes = [];
|
|
1416
|
+
const dataStoreRoutes = [];
|
|
1417
|
+
for (const route of routes) {
|
|
1418
|
+
if (this.isBlobPath(route)) {
|
|
1419
|
+
blobManagerRoutes.push(route);
|
|
1420
|
+
}
|
|
1421
|
+
else {
|
|
1422
|
+
dataStoreRoutes.push(route);
|
|
1423
|
+
}
|
|
1424
|
+
}
|
|
1425
|
+
return { blobManagerRoutes, dataStoreRoutes };
|
|
1426
|
+
}
|
|
1423
1427
|
/**
|
|
1424
1428
|
* Runs garbage collection and updates the reference / used state of the nodes in the container.
|
|
1425
1429
|
* @returns the statistics of the garbage collection run; undefined if GC did not run.
|
|
@@ -1453,7 +1457,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1453
1457
|
const summaryNumberLogger = telemetry_utils_1.ChildLogger.create(summaryLogger, undefined, {
|
|
1454
1458
|
all: { summaryNumber },
|
|
1455
1459
|
});
|
|
1456
|
-
(0, common_utils_1.assert)(this.
|
|
1460
|
+
(0, common_utils_1.assert)(this.outbox.isEmpty, 0x3d1 /* Can't trigger summary in the middle of a batch */);
|
|
1457
1461
|
let latestSnapshotVersionId;
|
|
1458
1462
|
if (refreshLatestAck) {
|
|
1459
1463
|
const latestSnapshotInfo = await this.refreshLatestSummaryAckFromServer(telemetry_utils_1.ChildLogger.create(summaryNumberLogger, undefined, { all: { safeSummary: true } }));
|
|
@@ -1491,7 +1495,6 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1491
1495
|
if (this.deltaManager.lastSequenceNumber !== summaryRefSeqNum) {
|
|
1492
1496
|
return {
|
|
1493
1497
|
continue: false,
|
|
1494
|
-
// eslint-disable-next-line max-len
|
|
1495
1498
|
error: `lastSequenceNumber changed before uploading to storage. ${this.deltaManager.lastSequenceNumber} !== ${summaryRefSeqNum}`,
|
|
1496
1499
|
};
|
|
1497
1500
|
}
|
|
@@ -1499,7 +1502,6 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1499
1502
|
if (lastAck !== this.summaryCollection.latestAck) {
|
|
1500
1503
|
return {
|
|
1501
1504
|
continue: false,
|
|
1502
|
-
// eslint-disable-next-line max-len
|
|
1503
1505
|
error: `Last summary changed while summarizing. ${this.summaryCollection.latestAck} !== ${lastAck}`,
|
|
1504
1506
|
};
|
|
1505
1507
|
}
|
|
@@ -1544,8 +1546,8 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1544
1546
|
const dataStoreTree = summaryTree.tree[runtime_definitions_1.channelsTreeName];
|
|
1545
1547
|
(0, common_utils_1.assert)(dataStoreTree.type === protocol_definitions_1.SummaryType.Tree, 0x1fc /* "summary is not a tree" */);
|
|
1546
1548
|
const handleCount = Object.values(dataStoreTree.tree).filter((value) => value.type === protocol_definitions_1.SummaryType.Handle).length;
|
|
1547
|
-
const gcSummaryTreeStats = summaryTree.tree[
|
|
1548
|
-
? (0, runtime_utils_1.calculateStats)(summaryTree.tree[
|
|
1549
|
+
const gcSummaryTreeStats = summaryTree.tree[runtime_definitions_1.gcTreeKey]
|
|
1550
|
+
? (0, runtime_utils_1.calculateStats)(summaryTree.tree[runtime_definitions_1.gcTreeKey])
|
|
1549
1551
|
: undefined;
|
|
1550
1552
|
const summaryStats = Object.assign({ dataStoreCount: this.dataStores.size, summarizedDataStoreCount: this.dataStores.size - handleCount, gcStateUpdatedDataStoreCount: (_a = summarizeResult.gcStats) === null || _a === void 0 ? void 0 : _a.updatedDataStoreCount, gcBlobNodeCount: gcSummaryTreeStats === null || gcSummaryTreeStats === void 0 ? void 0 : gcSummaryTreeStats.blobNodeCount, gcTotalBlobsSize: gcSummaryTreeStats === null || gcSummaryTreeStats === void 0 ? void 0 : gcSummaryTreeStats.totalBlobSize, summaryNumber }, partialStats);
|
|
1551
1553
|
const generateSummaryData = {
|
|
@@ -1566,8 +1568,8 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1566
1568
|
// latestSnapshotVersionId from storage and it does not match with the lastAck ackHandle, then use
|
|
1567
1569
|
// the one fetched from storage as parent as that is the latest.
|
|
1568
1570
|
let summaryContext;
|
|
1569
|
-
if ((lastAck === null || lastAck === void 0 ? void 0 : lastAck.summaryAck.contents.handle) !== latestSnapshotVersionId
|
|
1570
|
-
|
|
1571
|
+
if ((lastAck === null || lastAck === void 0 ? void 0 : lastAck.summaryAck.contents.handle) !== latestSnapshotVersionId &&
|
|
1572
|
+
latestSnapshotVersionId !== undefined) {
|
|
1571
1573
|
summaryContext = {
|
|
1572
1574
|
proposalHandle: undefined,
|
|
1573
1575
|
ackHandle: latestSnapshotVersionId,
|
|
@@ -1626,40 +1628,8 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1626
1628
|
this.deltaManager.inbound.resume();
|
|
1627
1629
|
}
|
|
1628
1630
|
}
|
|
1629
|
-
processRemoteChunkedMessage(message) {
|
|
1630
|
-
if (message.type !== ContainerMessageType.ChunkedOp) {
|
|
1631
|
-
return message;
|
|
1632
|
-
}
|
|
1633
|
-
const clientId = message.clientId;
|
|
1634
|
-
const chunkedContent = message.contents;
|
|
1635
|
-
this.addChunk(clientId, chunkedContent);
|
|
1636
|
-
if (chunkedContent.chunkId === chunkedContent.totalChunks) {
|
|
1637
|
-
const newMessage = Object.assign({}, message);
|
|
1638
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
1639
|
-
const serializedContent = this.chunkMap.get(clientId).join("");
|
|
1640
|
-
newMessage.contents = JSON.parse(serializedContent);
|
|
1641
|
-
newMessage.type = chunkedContent.originalType;
|
|
1642
|
-
this.clearPartialChunks(clientId);
|
|
1643
|
-
return newMessage;
|
|
1644
|
-
}
|
|
1645
|
-
return message;
|
|
1646
|
-
}
|
|
1647
|
-
addChunk(clientId, chunkedContent) {
|
|
1648
|
-
let map = this.chunkMap.get(clientId);
|
|
1649
|
-
if (map === undefined) {
|
|
1650
|
-
map = [];
|
|
1651
|
-
this.chunkMap.set(clientId, map);
|
|
1652
|
-
}
|
|
1653
|
-
(0, common_utils_1.assert)(chunkedContent.chunkId === map.length + 1, 0x131 /* "Mismatch between new chunkId and expected chunkMap" */); // 1-based indexing
|
|
1654
|
-
map.push(chunkedContent.contents);
|
|
1655
|
-
}
|
|
1656
|
-
clearPartialChunks(clientId) {
|
|
1657
|
-
if (this.chunkMap.has(clientId)) {
|
|
1658
|
-
this.chunkMap.delete(clientId);
|
|
1659
|
-
}
|
|
1660
|
-
}
|
|
1661
1631
|
hasPendingMessages() {
|
|
1662
|
-
return this.pendingStateManager.hasPendingMessages() || !this.
|
|
1632
|
+
return this.pendingStateManager.hasPendingMessages() || !this.outbox.isEmpty;
|
|
1663
1633
|
}
|
|
1664
1634
|
updateDocumentDirtyState(dirty) {
|
|
1665
1635
|
if (this.attachState !== container_definitions_1.AttachState.Attached) {
|
|
@@ -1697,14 +1667,17 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1697
1667
|
return this.blobManager.createBlob(blob);
|
|
1698
1668
|
}
|
|
1699
1669
|
submit(type, contents, localOpMetadata = undefined, metadata = undefined) {
|
|
1700
|
-
var _a, _b, _c, _d;
|
|
1701
1670
|
this.verifyNotClosed();
|
|
1671
|
+
this.verifyCanSubmitOps();
|
|
1702
1672
|
// There should be no ops in detached container state!
|
|
1703
1673
|
(0, common_utils_1.assert)(this.attachState !== container_definitions_1.AttachState.Detached, 0x132 /* "sending ops in detached container" */);
|
|
1704
1674
|
const deserializedContent = { type, contents };
|
|
1705
1675
|
const serializedContent = JSON.stringify(deserializedContent);
|
|
1706
1676
|
if (this.deltaManager.readOnlyInfo.readonly) {
|
|
1707
|
-
this.logger.
|
|
1677
|
+
this.logger.sendTelemetryEvent({
|
|
1678
|
+
eventName: "SubmitOpInReadonly",
|
|
1679
|
+
connected: this.connected,
|
|
1680
|
+
});
|
|
1708
1681
|
}
|
|
1709
1682
|
const message = {
|
|
1710
1683
|
contents: serializedContent,
|
|
@@ -1734,44 +1707,28 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1734
1707
|
// issue than sending.
|
|
1735
1708
|
// Please note that this does not change file format, so it can be disabled in the future if this
|
|
1736
1709
|
// optimization no longer makes sense (for example, batch compression may make it less appealing).
|
|
1737
|
-
if (this.currentlyBatching() &&
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
// when queue is not empty.
|
|
1742
|
-
// Flush queue & retry. Failure on retry would mean - single message is bigger than hard limit
|
|
1743
|
-
this.flushBatch(this.pendingAttachBatch.popBatch());
|
|
1744
|
-
if (!this.pendingAttachBatch.push(message)) {
|
|
1745
|
-
throw new container_utils_1.GenericError("BatchTooLarge",
|
|
1746
|
-
/* error */ undefined, {
|
|
1747
|
-
opSize: (_b = ((_a = message.contents) === null || _a === void 0 ? void 0 : _a.length)) !== null && _b !== void 0 ? _b : 0,
|
|
1748
|
-
count: this.pendingAttachBatch.length,
|
|
1749
|
-
limit: this.pendingAttachBatch.options.hardLimit,
|
|
1750
|
-
});
|
|
1751
|
-
}
|
|
1752
|
-
}
|
|
1710
|
+
if (this.currentlyBatching() &&
|
|
1711
|
+
type === ContainerMessageType.Attach &&
|
|
1712
|
+
this.mc.config.getBoolean("Fluid.ContainerRuntime.disableAttachOpReorder") !== true) {
|
|
1713
|
+
this.outbox.submitAttach(message);
|
|
1753
1714
|
}
|
|
1754
1715
|
else {
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1716
|
+
this.outbox.submit(message);
|
|
1717
|
+
}
|
|
1718
|
+
if (!this.currentlyBatching()) {
|
|
1719
|
+
this.flush();
|
|
1720
|
+
}
|
|
1721
|
+
else if (!this.flushMicroTaskExists) {
|
|
1722
|
+
this.flushMicroTaskExists = true;
|
|
1723
|
+
// Queue a microtask to detect the end of the turn and force a flush.
|
|
1724
|
+
Promise.resolve()
|
|
1725
|
+
.then(() => {
|
|
1726
|
+
this.flushMicroTaskExists = false;
|
|
1764
1727
|
this.flush();
|
|
1765
|
-
}
|
|
1766
|
-
|
|
1767
|
-
this.
|
|
1768
|
-
|
|
1769
|
-
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
1770
|
-
Promise.resolve().then(() => {
|
|
1771
|
-
this.flushMicroTaskExists = false;
|
|
1772
|
-
this.flush();
|
|
1773
|
-
});
|
|
1774
|
-
}
|
|
1728
|
+
})
|
|
1729
|
+
.catch((error) => {
|
|
1730
|
+
this.closeFn(error);
|
|
1731
|
+
});
|
|
1775
1732
|
}
|
|
1776
1733
|
}
|
|
1777
1734
|
catch (error) {
|
|
@@ -1786,7 +1743,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1786
1743
|
this.verifyNotClosed();
|
|
1787
1744
|
(0, common_utils_1.assert)(this.connected, 0x133 /* "Container disconnected when trying to submit system message" */);
|
|
1788
1745
|
// System message should not be sent in the middle of the batch.
|
|
1789
|
-
(0, common_utils_1.assert)(this.
|
|
1746
|
+
(0, common_utils_1.assert)(this.outbox.isEmpty, 0x3d4 /* System op in the middle of a batch */);
|
|
1790
1747
|
// back-compat: ADO #1385: Make this call unconditional in the future
|
|
1791
1748
|
return this.context.submitSummaryFn !== undefined
|
|
1792
1749
|
? this.context.submitSummaryFn(contents)
|
|
@@ -1801,6 +1758,32 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1801
1758
|
throw new Error("Runtime is closed");
|
|
1802
1759
|
}
|
|
1803
1760
|
}
|
|
1761
|
+
verifyCanSubmitOps() {
|
|
1762
|
+
if (this.ensureNoDataModelChangesCalls > 0) {
|
|
1763
|
+
const errorMessage = "Op was submitted from within a `ensureNoDataModelChanges` callback";
|
|
1764
|
+
if (this.opReentryCallsToReport > 0) {
|
|
1765
|
+
this.mc.logger.sendTelemetryEvent({ eventName: "OpReentry" },
|
|
1766
|
+
// We need to capture the call stack in order to inspect the source of this usage pattern
|
|
1767
|
+
new container_utils_1.UsageError(errorMessage));
|
|
1768
|
+
this.opReentryCallsToReport--;
|
|
1769
|
+
}
|
|
1770
|
+
// Creating ops while processing ops can lead
|
|
1771
|
+
// to undefined behavior and events observed in the wrong order.
|
|
1772
|
+
// For example, we have two callbacks registered for a DDS, A and B.
|
|
1773
|
+
// Then if on change #1 callback A creates change #2, the invocation flow will be:
|
|
1774
|
+
//
|
|
1775
|
+
// A because of #1
|
|
1776
|
+
// A because of #2
|
|
1777
|
+
// B because of #2
|
|
1778
|
+
// B because of #1
|
|
1779
|
+
//
|
|
1780
|
+
// The runtime must enforce op coherence by not allowing ops to be submitted
|
|
1781
|
+
// while ops are being processed.
|
|
1782
|
+
if (this.enableOpReentryCheck) {
|
|
1783
|
+
throw new container_utils_1.UsageError(errorMessage);
|
|
1784
|
+
}
|
|
1785
|
+
}
|
|
1786
|
+
}
|
|
1804
1787
|
/**
|
|
1805
1788
|
* Finds the right store and asks it to resubmit the message. This typically happens when we
|
|
1806
1789
|
* reconnect and there are pending messages.
|
|
@@ -1860,13 +1843,32 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1860
1843
|
// It should only be done by the summarizerNode, if required.
|
|
1861
1844
|
// When fetching from storage we will always get the latest version and do not use the ackHandle.
|
|
1862
1845
|
const snapshotTreeFetcher = async () => {
|
|
1863
|
-
const fetchResult = await this.
|
|
1846
|
+
const fetchResult = await this.fetchLatestSnapshotFromStorage(summaryLogger, {
|
|
1864
1847
|
eventName: "RefreshLatestSummaryGetSnapshot",
|
|
1865
1848
|
ackHandle,
|
|
1866
1849
|
summaryRefSeq,
|
|
1867
1850
|
fetchLatest: true,
|
|
1868
1851
|
});
|
|
1869
1852
|
const latestSnapshotRefSeq = await (0, runtime_utils_1.seqFromTree)(fetchResult.snapshotTree, readAndParseBlob);
|
|
1853
|
+
/**
|
|
1854
|
+
* If the fetched snapshot is older than the one for which the ack was received, close the container.
|
|
1855
|
+
* This should never happen because an ack should be sent after the latest summary is updated in the server.
|
|
1856
|
+
* However, there are couple of scenarios where it's possible:
|
|
1857
|
+
* 1. A file was modified externally resulting in modifying the snapshot's sequence number. This can lead to
|
|
1858
|
+
* the document being unusable and we should not proceed.
|
|
1859
|
+
* 2. The server DB failed after the ack was sent which may delete the corresponding snapshot. Ideally, in
|
|
1860
|
+
* such cases, the file will be rolled back along with the ack and we will eventually reach a consistent
|
|
1861
|
+
* state.
|
|
1862
|
+
*/
|
|
1863
|
+
if (latestSnapshotRefSeq < summaryRefSeq) {
|
|
1864
|
+
const error = container_utils_1.DataProcessingError.create("Fetched snapshot is older than the received ack", "RefreshLatestSummaryAck", undefined /* sequencedMessage */, {
|
|
1865
|
+
ackHandle,
|
|
1866
|
+
summaryRefSeq,
|
|
1867
|
+
latestSnapshotRefSeq,
|
|
1868
|
+
});
|
|
1869
|
+
this.closeFn(error);
|
|
1870
|
+
throw error;
|
|
1871
|
+
}
|
|
1870
1872
|
summaryLogger.sendTelemetryEvent({
|
|
1871
1873
|
eventName: "LatestSummaryRetrieved",
|
|
1872
1874
|
ackHandle,
|
|
@@ -1880,7 +1882,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1880
1882
|
};
|
|
1881
1883
|
const result = await this.summarizerNode.refreshLatestSummary(proposalHandle, summaryRefSeq, snapshotTreeFetcher, readAndParseBlob, summaryLogger);
|
|
1882
1884
|
// Notify the garbage collector so it can update its latest summary state.
|
|
1883
|
-
await this.garbageCollector.
|
|
1885
|
+
await this.garbageCollector.refreshLatestSummary(result, proposalHandle, summaryRefSeq, readAndParseBlob);
|
|
1884
1886
|
}
|
|
1885
1887
|
/**
|
|
1886
1888
|
* Fetches the latest snapshot from storage and uses it to refresh SummarizerNode's
|
|
@@ -1889,22 +1891,22 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1889
1891
|
* @returns downloaded snapshot's reference sequence number
|
|
1890
1892
|
*/
|
|
1891
1893
|
async refreshLatestSummaryAckFromServer(summaryLogger) {
|
|
1892
|
-
const { snapshotTree, versionId } = await this.
|
|
1894
|
+
const { snapshotTree, versionId } = await this.fetchLatestSnapshotFromStorage(summaryLogger, {
|
|
1893
1895
|
eventName: "RefreshLatestSummaryGetSnapshot",
|
|
1894
1896
|
fetchLatest: true,
|
|
1895
|
-
}
|
|
1897
|
+
});
|
|
1896
1898
|
const readAndParseBlob = async (id) => (0, driver_utils_1.readAndParse)(this.storage, id);
|
|
1897
1899
|
const latestSnapshotRefSeq = await (0, runtime_utils_1.seqFromTree)(snapshotTree, readAndParseBlob);
|
|
1898
1900
|
const result = await this.summarizerNode.refreshLatestSummary(undefined, latestSnapshotRefSeq, async () => snapshotTree, readAndParseBlob, summaryLogger);
|
|
1899
1901
|
// Notify the garbage collector so it can update its latest summary state.
|
|
1900
|
-
await this.garbageCollector.
|
|
1902
|
+
await this.garbageCollector.refreshLatestSummary(result, undefined, latestSnapshotRefSeq, readAndParseBlob);
|
|
1901
1903
|
return { latestSnapshotRefSeq, latestSnapshotVersionId: versionId };
|
|
1902
1904
|
}
|
|
1903
|
-
async
|
|
1905
|
+
async fetchLatestSnapshotFromStorage(logger, event) {
|
|
1904
1906
|
return telemetry_utils_1.PerformanceEvent.timedExecAsync(logger, event, async (perfEvent) => {
|
|
1905
1907
|
const stats = {};
|
|
1906
1908
|
const trace = common_utils_1.Trace.start();
|
|
1907
|
-
const versions = await this.storage.getVersions(
|
|
1909
|
+
const versions = await this.storage.getVersions(null, 1, "refreshLatestSummaryAckFromServer", driver_definitions_1.FetchSource.noCache);
|
|
1908
1910
|
(0, common_utils_1.assert)(!!versions && !!versions[0], 0x137 /* "Failed to get version from storage" */);
|
|
1909
1911
|
stats.getVersionDuration = trace.trace().duration;
|
|
1910
1912
|
const maybeSnapshot = await this.storage.getSnapshotTree(versions[0]);
|
|
@@ -1917,13 +1919,15 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1917
1919
|
notifyAttaching(snapshot) {
|
|
1918
1920
|
var _a;
|
|
1919
1921
|
if ((_a = this.mc.config.getBoolean("enableOfflineLoad")) !== null && _a !== void 0 ? _a : this.runtimeOptions.enableOfflineLoad) {
|
|
1920
|
-
this.baseSnapshotBlobs =
|
|
1922
|
+
this.baseSnapshotBlobs =
|
|
1923
|
+
serializedSnapshotStorage_1.SerializedSnapshotStorage.serializeTreeWithBlobContents(snapshot);
|
|
1921
1924
|
}
|
|
1922
1925
|
}
|
|
1923
1926
|
async initializeBaseSnapshotBlobs() {
|
|
1924
1927
|
var _a;
|
|
1925
1928
|
if (!((_a = this.mc.config.getBoolean("enableOfflineLoad")) !== null && _a !== void 0 ? _a : this.runtimeOptions.enableOfflineLoad) ||
|
|
1926
|
-
this.attachState !== container_definitions_1.AttachState.Attached ||
|
|
1929
|
+
this.attachState !== container_definitions_1.AttachState.Attached ||
|
|
1930
|
+
this.context.pendingLocalState) {
|
|
1927
1931
|
return;
|
|
1928
1932
|
}
|
|
1929
1933
|
(0, common_utils_1.assert)(!!this.context.baseSnapshot, 0x2e5 /* "Must have a base snapshot" */);
|
|
@@ -1934,13 +1938,13 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1934
1938
|
if (!((_a = this.mc.config.getBoolean("enableOfflineLoad")) !== null && _a !== void 0 ? _a : this.runtimeOptions.enableOfflineLoad)) {
|
|
1935
1939
|
throw new container_utils_1.UsageError("can't get state when offline load disabled");
|
|
1936
1940
|
}
|
|
1941
|
+
if (this._orderSequentiallyCalls !== 0) {
|
|
1942
|
+
throw new container_utils_1.UsageError("can't get state during orderSequentially");
|
|
1943
|
+
}
|
|
1937
1944
|
// Flush pending batch.
|
|
1938
1945
|
// getPendingLocalState() is only exposed through Container.closeAndGetPendingLocalState(), so it's safe
|
|
1939
1946
|
// to close current batch.
|
|
1940
1947
|
this.flush();
|
|
1941
|
-
if (this._orderSequentiallyCalls !== 0) {
|
|
1942
|
-
throw new container_utils_1.UsageError("can't get state during orderSequentially");
|
|
1943
|
-
}
|
|
1944
1948
|
const previousPendingState = this.context.pendingLocalState;
|
|
1945
1949
|
if (previousPendingState) {
|
|
1946
1950
|
return {
|
|
@@ -2021,6 +2025,7 @@ exports.ContainerRuntime = ContainerRuntime;
|
|
|
2021
2025
|
const waitForSeq = async (deltaManager, targetSeq) => new Promise((resolve, reject) => {
|
|
2022
2026
|
// TODO: remove cast to any when actual event is determined
|
|
2023
2027
|
deltaManager.on("closed", reject);
|
|
2028
|
+
deltaManager.on("disposed", reject);
|
|
2024
2029
|
// If we already reached target sequence number, simply resolve the promise.
|
|
2025
2030
|
if (deltaManager.lastSequenceNumber >= targetSeq) {
|
|
2026
2031
|
resolve();
|