@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/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.isRuntimeMessage = exports.RuntimeMessage = exports.CompressionAlgorithms = exports.RuntimeHeaders = exports.DefaultSummaryConfiguration = exports.ContainerMessageType = void 0;
|
|
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");
|
|
@@ -28,7 +28,6 @@ const summarizerClientElection_1 = require("./summarizerClientElection");
|
|
|
28
28
|
const throttler_1 = require("./throttler");
|
|
29
29
|
const runWhileConnectedCoordinator_1 = require("./runWhileConnectedCoordinator");
|
|
30
30
|
const garbageCollection_1 = require("./garbageCollection");
|
|
31
|
-
const garbageCollectionConstants_1 = require("./garbageCollectionConstants");
|
|
32
31
|
const dataStore_1 = require("./dataStore");
|
|
33
32
|
const batchTracker_1 = require("./batchTracker");
|
|
34
33
|
const serializedSnapshotStorage_1 = require("./serializedSnapshotStorage");
|
|
@@ -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
|
*/
|
|
@@ -129,8 +138,7 @@ function getDeviceSpec() {
|
|
|
129
138
|
};
|
|
130
139
|
}
|
|
131
140
|
}
|
|
132
|
-
catch (_a) {
|
|
133
|
-
}
|
|
141
|
+
catch (_a) { }
|
|
134
142
|
return {};
|
|
135
143
|
}
|
|
136
144
|
exports.getDeviceSpec = getDeviceSpec;
|
|
@@ -159,6 +167,14 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
159
167
|
this.flushMicroTaskExists = false;
|
|
160
168
|
this.savedOps = [];
|
|
161
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;
|
|
162
178
|
this._disposed = false;
|
|
163
179
|
this.emitDirtyDocumentEvent = true;
|
|
164
180
|
this.defaultTelemetrySignalSampleCount = 100;
|
|
@@ -218,15 +234,21 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
218
234
|
this.nextSummaryNumber = loadSummaryNumber + 1;
|
|
219
235
|
this.messageAtLastSummary = metadata === null || metadata === void 0 ? void 0 : metadata.message;
|
|
220
236
|
this._connected = this.context.connected;
|
|
221
|
-
this.remoteMessageProcessor = new opLifecycle_1.RemoteMessageProcessor(new opLifecycle_1.OpSplitter(chunks), new opLifecycle_1.OpDecompressor());
|
|
222
|
-
this.handleContext = new containerHandleContext_1.ContainerFluidHandleContext("", this);
|
|
223
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);
|
|
224
243
|
if (this.summaryConfiguration.state === "enabled") {
|
|
225
244
|
this.validateSummaryHeuristicConfiguration(this.summaryConfiguration);
|
|
226
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;
|
|
227
250
|
this.summariesDisabled = this.isSummariesDisabled();
|
|
228
251
|
this.heuristicsDisabled = this.isHeuristicsDisabled();
|
|
229
|
-
this.summarizerClientElectionEnabled = this.isSummarizerClientElectionEnabled();
|
|
230
252
|
this.maxOpsSinceLastSummary = this.getMaxOpsSinceLastSummary();
|
|
231
253
|
this.initialSummarizerDelayMs = this.getInitialSummarizerDelayMs();
|
|
232
254
|
this.maxConsecutiveReconnects =
|
|
@@ -235,7 +257,8 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
235
257
|
const pendingRuntimeState = context.pendingLocalState;
|
|
236
258
|
const baseSnapshot = (_d = pendingRuntimeState === null || pendingRuntimeState === void 0 ? void 0 : pendingRuntimeState.baseSnapshot) !== null && _d !== void 0 ? _d : context.baseSnapshot;
|
|
237
259
|
const maxSnapshotCacheDurationMs = (_f = (_e = this._storage) === null || _e === void 0 ? void 0 : _e.policies) === null || _f === void 0 ? void 0 : _f.maximumCacheDurationMs;
|
|
238
|
-
if (maxSnapshotCacheDurationMs !== undefined &&
|
|
260
|
+
if (maxSnapshotCacheDurationMs !== undefined &&
|
|
261
|
+
maxSnapshotCacheDurationMs > 5 * 24 * 60 * 60 * 1000) {
|
|
239
262
|
// This is a runtime enforcement of what's already explicit in the policy's type itself,
|
|
240
263
|
// which dictates the value is either undefined or exactly 5 days in ms.
|
|
241
264
|
// As long as the actual value is less than 5 days, the assumptions GC makes here are valid.
|
|
@@ -272,43 +295,59 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
272
295
|
throwOnFailure: true,
|
|
273
296
|
// If GC should not run, let the summarizer node know so that it does not track GC state.
|
|
274
297
|
gcDisabled: !this.garbageCollector.shouldRunGC,
|
|
275
|
-
}
|
|
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());
|
|
276
303
|
if (baseSnapshot) {
|
|
277
304
|
this.summarizerNode.updateBaseSummaryState(baseSnapshot);
|
|
278
305
|
}
|
|
279
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));
|
|
280
|
-
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) => {
|
|
281
308
|
if (!this.disposed) {
|
|
282
|
-
this.submit(ContainerMessageType.BlobAttach, undefined, undefined, {
|
|
309
|
+
this.submit(ContainerMessageType.BlobAttach, undefined, undefined, {
|
|
310
|
+
localId,
|
|
311
|
+
blobId,
|
|
312
|
+
});
|
|
283
313
|
}
|
|
284
|
-
}, (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());
|
|
285
315
|
this.scheduleManager = new scheduleManager_1.ScheduleManager(context.deltaManager, this, () => this.clientId, telemetry_utils_1.ChildLogger.create(this.logger, "ScheduleManager"));
|
|
286
316
|
this.pendingStateManager = new pendingStateManager_1.PendingStateManager({
|
|
287
317
|
applyStashedOp: this.applyStashedOp.bind(this),
|
|
288
318
|
clientId: () => this.clientId,
|
|
289
319
|
close: this.closeFn,
|
|
290
320
|
connected: () => this.connected,
|
|
291
|
-
flush: this.flush.bind(this),
|
|
292
321
|
reSubmit: this.reSubmit.bind(this),
|
|
293
322
|
rollback: this.rollback.bind(this),
|
|
294
323
|
orderSequentially: this.orderSequentially.bind(this),
|
|
295
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;
|
|
296
331
|
this.outbox = new opLifecycle_1.Outbox({
|
|
297
332
|
shouldSend: () => this.canSendOps(),
|
|
298
333
|
pendingStateManager: this.pendingStateManager,
|
|
299
334
|
containerContext: this.context,
|
|
300
335
|
compressor: new opLifecycle_1.OpCompressor(this.mc.logger),
|
|
336
|
+
splitter: opSplitter,
|
|
301
337
|
config: {
|
|
302
|
-
compressionOptions
|
|
338
|
+
compressionOptions,
|
|
303
339
|
maxBatchSizeInBytes: runtimeOptions.maxBatchSizeInBytes,
|
|
340
|
+
enableOpReentryCheck: this.enableOpReentryCheck,
|
|
304
341
|
},
|
|
342
|
+
logger: this.mc.logger,
|
|
305
343
|
});
|
|
306
344
|
this.context.quorum.on("removeMember", (clientId) => {
|
|
307
345
|
this.remoteMessageProcessor.clearPartialMessagesFor(clientId);
|
|
308
346
|
});
|
|
309
347
|
this.summaryCollection = new summaryCollection_1.SummaryCollection(this.deltaManager, this.logger);
|
|
310
|
-
this.dirtyContainer =
|
|
311
|
-
|
|
348
|
+
this.dirtyContainer =
|
|
349
|
+
this.context.attachState !== container_definitions_1.AttachState.Attached ||
|
|
350
|
+
this.pendingStateManager.hasPendingMessages();
|
|
312
351
|
this.context.updateDirtyContainerState(this.dirtyContainer);
|
|
313
352
|
if (this.summariesDisabled) {
|
|
314
353
|
this.mc.logger.sendTelemetryEvent({ eventName: "SummariesDisabled" });
|
|
@@ -317,7 +356,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
317
356
|
const orderedClientLogger = telemetry_utils_1.ChildLogger.create(this.logger, "OrderedClientElection");
|
|
318
357
|
const orderedClientCollection = new orderedClientElection_1.OrderedClientCollection(orderedClientLogger, this.context.deltaManager, this.context.quorum);
|
|
319
358
|
const orderedClientElectionForSummarizer = new orderedClientElection_1.OrderedClientElection(orderedClientLogger, orderedClientCollection, electedSummarizerData !== null && electedSummarizerData !== void 0 ? electedSummarizerData : this.context.deltaManager.lastSequenceNumber, summarizerClientElection_1.SummarizerClientElection.isClientEligible);
|
|
320
|
-
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);
|
|
321
360
|
if (this.context.clientDetails.type === summarizerClientElection_1.summarizerClientType) {
|
|
322
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));
|
|
323
362
|
}
|
|
@@ -376,9 +415,14 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
376
415
|
(0, connectionTelemetry_1.ReportOpPerfTelemetry)(this.context.clientId, this.deltaManager, this.logger);
|
|
377
416
|
(0, batchTracker_1.BindBatchTracker)(this, this.logger);
|
|
378
417
|
}
|
|
379
|
-
get IContainerRuntime() {
|
|
380
|
-
|
|
418
|
+
get IContainerRuntime() {
|
|
419
|
+
return this;
|
|
420
|
+
}
|
|
421
|
+
get IFluidRouter() {
|
|
422
|
+
return this;
|
|
423
|
+
}
|
|
381
424
|
/**
|
|
425
|
+
* @deprecated - use loadRuntime instead.
|
|
382
426
|
* Load the stores from a snapshot and returns the runtime.
|
|
383
427
|
* @param context - Context of the container.
|
|
384
428
|
* @param registryEntries - Mapping to the stores.
|
|
@@ -389,7 +433,35 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
389
433
|
* allows mixin classes to leverage this method to define their own async initializer.
|
|
390
434
|
*/
|
|
391
435
|
static async load(context, registryEntries, requestHandler, runtimeOptions = {}, containerScope = context.scope, existing, containerRuntimeCtor = ContainerRuntime) {
|
|
392
|
-
|
|
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;
|
|
393
465
|
// If taggedLogger exists, use it. Otherwise, wrap the vanilla logger:
|
|
394
466
|
// back-compat: Remove the TaggedLoggerAdapter fallback once all the host are using loader > 0.45
|
|
395
467
|
const backCompatContext = context;
|
|
@@ -401,13 +473,15 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
401
473
|
});
|
|
402
474
|
const { summaryOptions = {}, gcOptions = {}, loadSequenceNumberVerification = "close", flushMode = defaultFlushMode, enableOfflineLoad = false, compressionOptions = {
|
|
403
475
|
minimumBatchSizeInBytes: Number.POSITIVE_INFINITY,
|
|
404
|
-
compressionAlgorithm: CompressionAlgorithms.lz4
|
|
405
|
-
}, maxBatchSizeInBytes = defaultMaxBatchSizeInBytes, } = runtimeOptions;
|
|
476
|
+
compressionAlgorithm: CompressionAlgorithms.lz4,
|
|
477
|
+
}, maxBatchSizeInBytes = defaultMaxBatchSizeInBytes, chunkSizeInBytes = Number.POSITIVE_INFINITY, enableOpReentryCheck = false, } = runtimeOptions;
|
|
406
478
|
const pendingRuntimeState = context.pendingLocalState;
|
|
407
479
|
const baseSnapshot = (_b = pendingRuntimeState === null || pendingRuntimeState === void 0 ? void 0 : pendingRuntimeState.baseSnapshot) !== null && _b !== void 0 ? _b : context.baseSnapshot;
|
|
408
|
-
const storage = !pendingRuntimeState
|
|
409
|
-
context.storage
|
|
410
|
-
new serializedSnapshotStorage_1.SerializedSnapshotStorage(() => {
|
|
480
|
+
const storage = !pendingRuntimeState
|
|
481
|
+
? context.storage
|
|
482
|
+
: new serializedSnapshotStorage_1.SerializedSnapshotStorage(() => {
|
|
483
|
+
return context.storage;
|
|
484
|
+
}, pendingRuntimeState.snapshotBlobs);
|
|
411
485
|
const registry = new dataStoreRegistry_1.FluidDataStoreRegistry(registryEntries);
|
|
412
486
|
const tryFetchBlob = async (blobName) => {
|
|
413
487
|
const blobId = baseSnapshot === null || baseSnapshot === void 0 ? void 0 : baseSnapshot.blobs[blobName];
|
|
@@ -438,7 +512,8 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
438
512
|
if (!pendingRuntimeState && runtimeSequenceNumber !== undefined) {
|
|
439
513
|
const protocolSequenceNumber = context.deltaManager.initialSequenceNumber;
|
|
440
514
|
// Unless bypass is explicitly set, then take action when sequence numbers mismatch.
|
|
441
|
-
if (loadSequenceNumberVerification !== "bypass" &&
|
|
515
|
+
if (loadSequenceNumberVerification !== "bypass" &&
|
|
516
|
+
runtimeSequenceNumber !== protocolSequenceNumber) {
|
|
442
517
|
// "Load from summary, runtime metadata sequenceNumber !== initialSequenceNumber"
|
|
443
518
|
const error = new container_utils_1.DataCorruptionError(
|
|
444
519
|
// pre-0.58 error message: SummaryMetadataMismatch
|
|
@@ -447,7 +522,9 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
447
522
|
logger.sendErrorEvent({ eventName: "SequenceNumberMismatch" }, error);
|
|
448
523
|
}
|
|
449
524
|
else {
|
|
525
|
+
// Call both close and dispose as closeFn implementation will no longer dispose runtime in future
|
|
450
526
|
context.closeFn(error);
|
|
527
|
+
(_d = context.disposeFn) === null || _d === void 0 ? void 0 : _d.call(context, error);
|
|
451
528
|
}
|
|
452
529
|
}
|
|
453
530
|
}
|
|
@@ -459,6 +536,8 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
459
536
|
enableOfflineLoad,
|
|
460
537
|
compressionOptions,
|
|
461
538
|
maxBatchSizeInBytes,
|
|
539
|
+
chunkSizeInBytes,
|
|
540
|
+
enableOpReentryCheck,
|
|
462
541
|
}, containerScope, logger, loadExisting, blobManagerSnapshot, storage, requestHandler);
|
|
463
542
|
if (pendingRuntimeState) {
|
|
464
543
|
await runtime.processSavedOps(pendingRuntimeState);
|
|
@@ -488,8 +567,18 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
488
567
|
// eslint-disable-next-line @typescript-eslint/unbound-method
|
|
489
568
|
return this.reSubmit;
|
|
490
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
|
+
}
|
|
491
575
|
get closeFn() {
|
|
492
|
-
|
|
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
|
+
};
|
|
493
582
|
}
|
|
494
583
|
get flushMode() {
|
|
495
584
|
return this._flushMode;
|
|
@@ -506,6 +595,23 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
506
595
|
get IFluidHandleContext() {
|
|
507
596
|
return this.handleContext;
|
|
508
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
|
+
}
|
|
509
615
|
get connected() {
|
|
510
616
|
return this._connected;
|
|
511
617
|
}
|
|
@@ -514,48 +620,20 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
514
620
|
var _a;
|
|
515
621
|
return (_a = this.summarizerClientElection) === null || _a === void 0 ? void 0 : _a.electedClientId;
|
|
516
622
|
}
|
|
517
|
-
get disposed() {
|
|
623
|
+
get disposed() {
|
|
624
|
+
return this._disposed;
|
|
625
|
+
}
|
|
518
626
|
get summarizer() {
|
|
519
627
|
(0, common_utils_1.assert)(this._summarizer !== undefined, 0x257 /* "This is not summarizing container" */);
|
|
520
628
|
return this._summarizer;
|
|
521
629
|
}
|
|
522
630
|
isSummariesDisabled() {
|
|
523
|
-
// back-compat: disableSummaries was moved from ISummaryRuntimeOptions
|
|
524
|
-
// to ISummaryConfiguration in 0.60.
|
|
525
|
-
if (this.runtimeOptions.summaryOptions.disableSummaries === true) {
|
|
526
|
-
return true;
|
|
527
|
-
}
|
|
528
631
|
return this.summaryConfiguration.state === "disabled";
|
|
529
632
|
}
|
|
530
633
|
isHeuristicsDisabled() {
|
|
531
|
-
var _a;
|
|
532
|
-
// back-compat: disableHeuristics was moved from ISummarizerOptions
|
|
533
|
-
// to ISummaryConfiguration in 0.60.
|
|
534
|
-
if (((_a = this.runtimeOptions.summaryOptions.summarizerOptions) === null || _a === void 0 ? void 0 : _a.disableHeuristics) === true) {
|
|
535
|
-
return true;
|
|
536
|
-
}
|
|
537
634
|
return this.summaryConfiguration.state === "disableHeuristics";
|
|
538
635
|
}
|
|
539
|
-
isSummarizerClientElectionEnabled() {
|
|
540
|
-
var _a;
|
|
541
|
-
if (this.mc.config.getBoolean("Fluid.ContainerRuntime.summarizerClientElection")) {
|
|
542
|
-
return (_a = this.mc.config.getBoolean("Fluid.ContainerRuntime.summarizerClientElection")) !== null && _a !== void 0 ? _a : true;
|
|
543
|
-
}
|
|
544
|
-
// back-compat: summarizerClientElection was moved from ISummaryRuntimeOptions
|
|
545
|
-
// to ISummaryConfiguration in 0.60.
|
|
546
|
-
if (this.runtimeOptions.summaryOptions.summarizerClientElection === true) {
|
|
547
|
-
return true;
|
|
548
|
-
}
|
|
549
|
-
return this.summaryConfiguration.state !== "disabled"
|
|
550
|
-
? this.summaryConfiguration.summarizerClientElection === true
|
|
551
|
-
: false;
|
|
552
|
-
}
|
|
553
636
|
getMaxOpsSinceLastSummary() {
|
|
554
|
-
// back-compat: maxOpsSinceLastSummary was moved from ISummaryRuntimeOptions
|
|
555
|
-
// to ISummaryConfiguration in 0.60.
|
|
556
|
-
if (this.runtimeOptions.summaryOptions.maxOpsSinceLastSummary !== undefined) {
|
|
557
|
-
return this.runtimeOptions.summaryOptions.maxOpsSinceLastSummary;
|
|
558
|
-
}
|
|
559
637
|
return this.summaryConfiguration.state !== "disabled"
|
|
560
638
|
? this.summaryConfiguration.maxOpsSinceLastSummary
|
|
561
639
|
: 0;
|
|
@@ -654,7 +732,8 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
654
732
|
status: 200,
|
|
655
733
|
mimeType: "fluid/object",
|
|
656
734
|
value: blob,
|
|
657
|
-
}
|
|
735
|
+
}
|
|
736
|
+
: (0, runtime_utils_1.create404Response)(request);
|
|
658
737
|
}
|
|
659
738
|
else if (requestParser.pathParts.length > 0) {
|
|
660
739
|
const dataStore = await this.getDataStoreFromRequest(id, request);
|
|
@@ -675,16 +754,20 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
675
754
|
return (_a = this.dataStores.aliases.get(maybeAlias)) !== null && _a !== void 0 ? _a : maybeAlias;
|
|
676
755
|
}
|
|
677
756
|
async getDataStoreFromRequest(id, request) {
|
|
678
|
-
var _a, _b, _c, _d
|
|
679
|
-
const
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
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
|
+
}
|
|
685
768
|
await this.dataStores.waitIfPendingAlias(id);
|
|
686
769
|
const internalId = this.internalId(id);
|
|
687
|
-
const dataStoreContext = await this.dataStores.getDataStore(internalId,
|
|
770
|
+
const dataStoreContext = await this.dataStores.getDataStore(internalId, headerData);
|
|
688
771
|
/**
|
|
689
772
|
* If GC should run and this an external app request with "externalRequest" header, we need to return
|
|
690
773
|
* an error if the data store being requested is marked as unreferenced as per the data store's base
|
|
@@ -693,7 +776,8 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
693
776
|
* This is a workaround to handle scenarios where a data store shared with an external app is deleted
|
|
694
777
|
* and marked as unreferenced by GC. Returning an error will fail to load the data store for the app.
|
|
695
778
|
*/
|
|
696
|
-
if (((
|
|
779
|
+
if (((_d = request.headers) === null || _d === void 0 ? void 0 : _d[RuntimeHeaders.externalRequest]) &&
|
|
780
|
+
this.garbageCollector.shouldRunGC) {
|
|
697
781
|
// The data store is referenced if used routes in the base summary has a route to self.
|
|
698
782
|
// Older documents may not have used routes in the summary. They are considered referenced.
|
|
699
783
|
const usedRoutes = (await dataStoreContext.getBaseGCDetails()).usedRoutes;
|
|
@@ -742,7 +826,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
742
826
|
}
|
|
743
827
|
const gcSummary = this.garbageCollector.summarize(fullTree, trackState, telemetryContext);
|
|
744
828
|
if (gcSummary !== undefined) {
|
|
745
|
-
(0, runtime_utils_1.addSummarizeResultToSummary)(summaryTree,
|
|
829
|
+
(0, runtime_utils_1.addSummarizeResultToSummary)(summaryTree, runtime_definitions_1.gcTreeKey, gcSummary);
|
|
746
830
|
}
|
|
747
831
|
}
|
|
748
832
|
// Track how many times the container tries to reconnect with pending messages.
|
|
@@ -867,9 +951,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
867
951
|
if (reconnection) {
|
|
868
952
|
this.consecutiveReconnects++;
|
|
869
953
|
if (!this.shouldContinueReconnecting()) {
|
|
870
|
-
this.closeFn(container_utils_1.DataProcessingError.create(
|
|
871
|
-
// eslint-disable-next-line max-len
|
|
872
|
-
"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, {
|
|
873
955
|
dataLoss: 1,
|
|
874
956
|
attempts: this.consecutiveReconnects,
|
|
875
957
|
pendingMessages: this.pendingStateManager.pendingMessagesCount,
|
|
@@ -903,7 +985,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
903
985
|
this.scheduleManager.beforeOpProcessing(message);
|
|
904
986
|
try {
|
|
905
987
|
let localOpMetadata;
|
|
906
|
-
if (local && runtimeMessage) {
|
|
988
|
+
if (local && runtimeMessage && message.type !== ContainerMessageType.ChunkedOp) {
|
|
907
989
|
localOpMetadata = this.pendingStateManager.processPendingLocalMessage(message);
|
|
908
990
|
}
|
|
909
991
|
// If there are no more pending messages after processing a local message,
|
|
@@ -976,7 +1058,8 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
976
1058
|
if (message.clientId === this.clientId && this.connected) {
|
|
977
1059
|
// Check to see if the signal was lost.
|
|
978
1060
|
if (this._perfSignalData.trackingSignalSequenceNumber !== undefined &&
|
|
979
|
-
envelope.clientSignalSequenceNumber >
|
|
1061
|
+
envelope.clientSignalSequenceNumber >
|
|
1062
|
+
this._perfSignalData.trackingSignalSequenceNumber) {
|
|
980
1063
|
this._perfSignalData.signalsLost++;
|
|
981
1064
|
this._perfSignalData.trackingSignalSequenceNumber = undefined;
|
|
982
1065
|
this.logger.sendErrorEvent({
|
|
@@ -987,7 +1070,8 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
987
1070
|
clientSignalSequenceNumber: envelope.clientSignalSequenceNumber,
|
|
988
1071
|
});
|
|
989
1072
|
}
|
|
990
|
-
else if (envelope.clientSignalSequenceNumber ===
|
|
1073
|
+
else if (envelope.clientSignalSequenceNumber ===
|
|
1074
|
+
this._perfSignalData.trackingSignalSequenceNumber) {
|
|
991
1075
|
this.sendSignalTelemetryEvent(envelope.clientSignalSequenceNumber);
|
|
992
1076
|
this._perfSignalData.trackingSignalSequenceNumber = undefined;
|
|
993
1077
|
}
|
|
@@ -1005,7 +1089,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1005
1089
|
async getRootDataStoreChannel(id, wait = true) {
|
|
1006
1090
|
await this.dataStores.waitIfPendingAlias(id);
|
|
1007
1091
|
const internalId = this.internalId(id);
|
|
1008
|
-
const context = await this.dataStores.getDataStore(internalId, wait
|
|
1092
|
+
const context = await this.dataStores.getDataStore(internalId, { wait });
|
|
1009
1093
|
(0, common_utils_1.assert)(await context.isRoot(), 0x12b /* "did not get root data store" */);
|
|
1010
1094
|
return context.realize();
|
|
1011
1095
|
}
|
|
@@ -1054,7 +1138,8 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1054
1138
|
finally {
|
|
1055
1139
|
this._orderSequentiallyCalls--;
|
|
1056
1140
|
}
|
|
1057
|
-
|
|
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) {
|
|
1058
1143
|
this.flush();
|
|
1059
1144
|
}
|
|
1060
1145
|
return result;
|
|
@@ -1073,7 +1158,9 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1073
1158
|
return this.dataStores.createDetachedDataStoreCore(pkg, false);
|
|
1074
1159
|
}
|
|
1075
1160
|
async _createDataStoreWithProps(pkg, props, id = (0, uuid_1.v4)()) {
|
|
1076
|
-
const fluidDataStore = await this.dataStores
|
|
1161
|
+
const fluidDataStore = await this.dataStores
|
|
1162
|
+
._createFluidDataStoreContext(Array.isArray(pkg) ? pkg : [pkg], id, props)
|
|
1163
|
+
.realize();
|
|
1077
1164
|
return (0, dataStore_1.channelToDataStore)(fluidDataStore, id, this, this.dataStores, this.mc.logger);
|
|
1078
1165
|
}
|
|
1079
1166
|
async _createDataStore(pkg, id = (0, uuid_1.v4)(), props) {
|
|
@@ -1210,7 +1297,10 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1210
1297
|
}
|
|
1211
1298
|
const telemetryContext = new runtime_utils_1.TelemetryContext();
|
|
1212
1299
|
const { stats, summary } = await this.summarizerNode.summarize(fullTree, trackState, telemetryContext);
|
|
1213
|
-
this.logger.sendTelemetryEvent({
|
|
1300
|
+
this.logger.sendTelemetryEvent({
|
|
1301
|
+
eventName: "SummarizeTelemetry",
|
|
1302
|
+
details: telemetryContext.serialize(),
|
|
1303
|
+
});
|
|
1214
1304
|
(0, common_utils_1.assert)(summary.type === protocol_definitions_1.SummaryType.Tree, 0x12f /* "Container Runtime's summarize should always return a tree" */);
|
|
1215
1305
|
return { stats, summary, gcStats };
|
|
1216
1306
|
}
|
|
@@ -1223,6 +1313,9 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1223
1313
|
async updateStateBeforeGC() {
|
|
1224
1314
|
return this.dataStores.updateStateBeforeGC();
|
|
1225
1315
|
}
|
|
1316
|
+
async getGCDataInternal(fullGC) {
|
|
1317
|
+
return this.dataStores.getGCData(fullGC);
|
|
1318
|
+
}
|
|
1226
1319
|
/**
|
|
1227
1320
|
* Implementation of IGarbageCollectionRuntime::getGCData.
|
|
1228
1321
|
* Generates and returns the GC data for this container.
|
|
@@ -1230,7 +1323,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1230
1323
|
*/
|
|
1231
1324
|
async getGCData(fullGC) {
|
|
1232
1325
|
const builder = new garbage_collector_1.GCDataBuilder();
|
|
1233
|
-
const dsGCData = await this.
|
|
1326
|
+
const dsGCData = await this.summarizerNode.getGCData(fullGC);
|
|
1234
1327
|
builder.addNodes(dsGCData.gcNodes);
|
|
1235
1328
|
const blobsGCData = this.blobManager.getGCData(fullGC);
|
|
1236
1329
|
builder.addNodes(blobsGCData.gcNodes);
|
|
@@ -1246,39 +1339,26 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1246
1339
|
// summarizing is required and asserted by the the summarizer node. We are the root and are
|
|
1247
1340
|
// always referenced, so the used routes is only self-route (empty string).
|
|
1248
1341
|
this.summarizerNode.updateUsedRoutes([""]);
|
|
1249
|
-
const
|
|
1250
|
-
|
|
1251
|
-
for (const route of usedRoutes) {
|
|
1252
|
-
if (this.isBlobPath(route)) {
|
|
1253
|
-
blobManagerUsedRoutes.push(route);
|
|
1254
|
-
}
|
|
1255
|
-
else {
|
|
1256
|
-
dataStoreUsedRoutes.push(route);
|
|
1257
|
-
}
|
|
1258
|
-
}
|
|
1259
|
-
this.blobManager.updateUsedRoutes(blobManagerUsedRoutes);
|
|
1260
|
-
this.dataStores.updateUsedRoutes(dataStoreUsedRoutes);
|
|
1342
|
+
const { dataStoreRoutes } = this.getDataStoreAndBlobManagerRoutes(usedRoutes);
|
|
1343
|
+
this.dataStores.updateUsedRoutes(dataStoreRoutes);
|
|
1261
1344
|
}
|
|
1262
1345
|
/**
|
|
1263
|
-
* This is called to update objects whose routes are unused.
|
|
1264
|
-
*
|
|
1265
|
-
* @param unusedRoutes - The routes that are unused in all data stores and attachment blobs in this Container.
|
|
1266
|
-
* @param tombstone - if true, the objects corresponding to unused routes are marked tombstones. Otherwise, they
|
|
1267
|
-
* 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.
|
|
1268
1348
|
*/
|
|
1269
|
-
updateUnusedRoutes(unusedRoutes
|
|
1270
|
-
const
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
}
|
|
1280
|
-
this.blobManager.
|
|
1281
|
-
this.dataStores.
|
|
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);
|
|
1282
1362
|
}
|
|
1283
1363
|
/**
|
|
1284
1364
|
* Returns a server generated referenced timestamp to be used to track unreferenced nodes by GC.
|
|
@@ -1307,7 +1387,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1307
1387
|
async getGCNodePackagePath(nodePath) {
|
|
1308
1388
|
switch (this.getNodeType(nodePath)) {
|
|
1309
1389
|
case garbageCollection_1.GCNodeType.Blob:
|
|
1310
|
-
return [
|
|
1390
|
+
return [blobManager_1.BlobManager.basePath];
|
|
1311
1391
|
case garbageCollection_1.GCNodeType.DataStore:
|
|
1312
1392
|
case garbageCollection_1.GCNodeType.SubDataStore:
|
|
1313
1393
|
return this.dataStores.getDataStorePackagePath(nodePath);
|
|
@@ -1325,6 +1405,25 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1325
1405
|
}
|
|
1326
1406
|
return true;
|
|
1327
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
|
+
}
|
|
1328
1427
|
/**
|
|
1329
1428
|
* Runs garbage collection and updates the reference / used state of the nodes in the container.
|
|
1330
1429
|
* @returns the statistics of the garbage collection run; undefined if GC did not run.
|
|
@@ -1396,7 +1495,6 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1396
1495
|
if (this.deltaManager.lastSequenceNumber !== summaryRefSeqNum) {
|
|
1397
1496
|
return {
|
|
1398
1497
|
continue: false,
|
|
1399
|
-
// eslint-disable-next-line max-len
|
|
1400
1498
|
error: `lastSequenceNumber changed before uploading to storage. ${this.deltaManager.lastSequenceNumber} !== ${summaryRefSeqNum}`,
|
|
1401
1499
|
};
|
|
1402
1500
|
}
|
|
@@ -1404,7 +1502,6 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1404
1502
|
if (lastAck !== this.summaryCollection.latestAck) {
|
|
1405
1503
|
return {
|
|
1406
1504
|
continue: false,
|
|
1407
|
-
// eslint-disable-next-line max-len
|
|
1408
1505
|
error: `Last summary changed while summarizing. ${this.summaryCollection.latestAck} !== ${lastAck}`,
|
|
1409
1506
|
};
|
|
1410
1507
|
}
|
|
@@ -1449,8 +1546,8 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1449
1546
|
const dataStoreTree = summaryTree.tree[runtime_definitions_1.channelsTreeName];
|
|
1450
1547
|
(0, common_utils_1.assert)(dataStoreTree.type === protocol_definitions_1.SummaryType.Tree, 0x1fc /* "summary is not a tree" */);
|
|
1451
1548
|
const handleCount = Object.values(dataStoreTree.tree).filter((value) => value.type === protocol_definitions_1.SummaryType.Handle).length;
|
|
1452
|
-
const gcSummaryTreeStats = summaryTree.tree[
|
|
1453
|
-
? (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])
|
|
1454
1551
|
: undefined;
|
|
1455
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);
|
|
1456
1553
|
const generateSummaryData = {
|
|
@@ -1471,8 +1568,8 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1471
1568
|
// latestSnapshotVersionId from storage and it does not match with the lastAck ackHandle, then use
|
|
1472
1569
|
// the one fetched from storage as parent as that is the latest.
|
|
1473
1570
|
let summaryContext;
|
|
1474
|
-
if ((lastAck === null || lastAck === void 0 ? void 0 : lastAck.summaryAck.contents.handle) !== latestSnapshotVersionId
|
|
1475
|
-
|
|
1571
|
+
if ((lastAck === null || lastAck === void 0 ? void 0 : lastAck.summaryAck.contents.handle) !== latestSnapshotVersionId &&
|
|
1572
|
+
latestSnapshotVersionId !== undefined) {
|
|
1476
1573
|
summaryContext = {
|
|
1477
1574
|
proposalHandle: undefined,
|
|
1478
1575
|
ackHandle: latestSnapshotVersionId,
|
|
@@ -1571,12 +1668,16 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1571
1668
|
}
|
|
1572
1669
|
submit(type, contents, localOpMetadata = undefined, metadata = undefined) {
|
|
1573
1670
|
this.verifyNotClosed();
|
|
1671
|
+
this.verifyCanSubmitOps();
|
|
1574
1672
|
// There should be no ops in detached container state!
|
|
1575
1673
|
(0, common_utils_1.assert)(this.attachState !== container_definitions_1.AttachState.Detached, 0x132 /* "sending ops in detached container" */);
|
|
1576
1674
|
const deserializedContent = { type, contents };
|
|
1577
1675
|
const serializedContent = JSON.stringify(deserializedContent);
|
|
1578
1676
|
if (this.deltaManager.readOnlyInfo.readonly) {
|
|
1579
|
-
this.logger.sendTelemetryEvent({
|
|
1677
|
+
this.logger.sendTelemetryEvent({
|
|
1678
|
+
eventName: "SubmitOpInReadonly",
|
|
1679
|
+
connected: this.connected,
|
|
1680
|
+
});
|
|
1580
1681
|
}
|
|
1581
1682
|
const message = {
|
|
1582
1683
|
contents: serializedContent,
|
|
@@ -1606,7 +1707,8 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1606
1707
|
// issue than sending.
|
|
1607
1708
|
// Please note that this does not change file format, so it can be disabled in the future if this
|
|
1608
1709
|
// optimization no longer makes sense (for example, batch compression may make it less appealing).
|
|
1609
|
-
if (this.currentlyBatching() &&
|
|
1710
|
+
if (this.currentlyBatching() &&
|
|
1711
|
+
type === ContainerMessageType.Attach &&
|
|
1610
1712
|
this.mc.config.getBoolean("Fluid.ContainerRuntime.disableAttachOpReorder") !== true) {
|
|
1611
1713
|
this.outbox.submitAttach(message);
|
|
1612
1714
|
}
|
|
@@ -1619,11 +1721,14 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1619
1721
|
else if (!this.flushMicroTaskExists) {
|
|
1620
1722
|
this.flushMicroTaskExists = true;
|
|
1621
1723
|
// Queue a microtask to detect the end of the turn and force a flush.
|
|
1622
|
-
|
|
1623
|
-
|
|
1724
|
+
Promise.resolve()
|
|
1725
|
+
.then(() => {
|
|
1624
1726
|
this.flushMicroTaskExists = false;
|
|
1625
1727
|
this.flush();
|
|
1626
|
-
})
|
|
1728
|
+
})
|
|
1729
|
+
.catch((error) => {
|
|
1730
|
+
this.closeFn(error);
|
|
1731
|
+
});
|
|
1627
1732
|
}
|
|
1628
1733
|
}
|
|
1629
1734
|
catch (error) {
|
|
@@ -1653,6 +1758,32 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1653
1758
|
throw new Error("Runtime is closed");
|
|
1654
1759
|
}
|
|
1655
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
|
+
}
|
|
1656
1787
|
/**
|
|
1657
1788
|
* Finds the right store and asks it to resubmit the message. This typically happens when we
|
|
1658
1789
|
* reconnect and there are pending messages.
|
|
@@ -1712,13 +1843,32 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1712
1843
|
// It should only be done by the summarizerNode, if required.
|
|
1713
1844
|
// When fetching from storage we will always get the latest version and do not use the ackHandle.
|
|
1714
1845
|
const snapshotTreeFetcher = async () => {
|
|
1715
|
-
const fetchResult = await this.
|
|
1846
|
+
const fetchResult = await this.fetchLatestSnapshotFromStorage(summaryLogger, {
|
|
1716
1847
|
eventName: "RefreshLatestSummaryGetSnapshot",
|
|
1717
1848
|
ackHandle,
|
|
1718
1849
|
summaryRefSeq,
|
|
1719
1850
|
fetchLatest: true,
|
|
1720
1851
|
});
|
|
1721
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
|
+
}
|
|
1722
1872
|
summaryLogger.sendTelemetryEvent({
|
|
1723
1873
|
eventName: "LatestSummaryRetrieved",
|
|
1724
1874
|
ackHandle,
|
|
@@ -1732,7 +1882,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1732
1882
|
};
|
|
1733
1883
|
const result = await this.summarizerNode.refreshLatestSummary(proposalHandle, summaryRefSeq, snapshotTreeFetcher, readAndParseBlob, summaryLogger);
|
|
1734
1884
|
// Notify the garbage collector so it can update its latest summary state.
|
|
1735
|
-
await this.garbageCollector.
|
|
1885
|
+
await this.garbageCollector.refreshLatestSummary(result, proposalHandle, summaryRefSeq, readAndParseBlob);
|
|
1736
1886
|
}
|
|
1737
1887
|
/**
|
|
1738
1888
|
* Fetches the latest snapshot from storage and uses it to refresh SummarizerNode's
|
|
@@ -1741,22 +1891,22 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1741
1891
|
* @returns downloaded snapshot's reference sequence number
|
|
1742
1892
|
*/
|
|
1743
1893
|
async refreshLatestSummaryAckFromServer(summaryLogger) {
|
|
1744
|
-
const { snapshotTree, versionId } = await this.
|
|
1894
|
+
const { snapshotTree, versionId } = await this.fetchLatestSnapshotFromStorage(summaryLogger, {
|
|
1745
1895
|
eventName: "RefreshLatestSummaryGetSnapshot",
|
|
1746
1896
|
fetchLatest: true,
|
|
1747
|
-
}
|
|
1897
|
+
});
|
|
1748
1898
|
const readAndParseBlob = async (id) => (0, driver_utils_1.readAndParse)(this.storage, id);
|
|
1749
1899
|
const latestSnapshotRefSeq = await (0, runtime_utils_1.seqFromTree)(snapshotTree, readAndParseBlob);
|
|
1750
1900
|
const result = await this.summarizerNode.refreshLatestSummary(undefined, latestSnapshotRefSeq, async () => snapshotTree, readAndParseBlob, summaryLogger);
|
|
1751
1901
|
// Notify the garbage collector so it can update its latest summary state.
|
|
1752
|
-
await this.garbageCollector.
|
|
1902
|
+
await this.garbageCollector.refreshLatestSummary(result, undefined, latestSnapshotRefSeq, readAndParseBlob);
|
|
1753
1903
|
return { latestSnapshotRefSeq, latestSnapshotVersionId: versionId };
|
|
1754
1904
|
}
|
|
1755
|
-
async
|
|
1905
|
+
async fetchLatestSnapshotFromStorage(logger, event) {
|
|
1756
1906
|
return telemetry_utils_1.PerformanceEvent.timedExecAsync(logger, event, async (perfEvent) => {
|
|
1757
1907
|
const stats = {};
|
|
1758
1908
|
const trace = common_utils_1.Trace.start();
|
|
1759
|
-
const versions = await this.storage.getVersions(
|
|
1909
|
+
const versions = await this.storage.getVersions(null, 1, "refreshLatestSummaryAckFromServer", driver_definitions_1.FetchSource.noCache);
|
|
1760
1910
|
(0, common_utils_1.assert)(!!versions && !!versions[0], 0x137 /* "Failed to get version from storage" */);
|
|
1761
1911
|
stats.getVersionDuration = trace.trace().duration;
|
|
1762
1912
|
const maybeSnapshot = await this.storage.getSnapshotTree(versions[0]);
|
|
@@ -1769,13 +1919,15 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1769
1919
|
notifyAttaching(snapshot) {
|
|
1770
1920
|
var _a;
|
|
1771
1921
|
if ((_a = this.mc.config.getBoolean("enableOfflineLoad")) !== null && _a !== void 0 ? _a : this.runtimeOptions.enableOfflineLoad) {
|
|
1772
|
-
this.baseSnapshotBlobs =
|
|
1922
|
+
this.baseSnapshotBlobs =
|
|
1923
|
+
serializedSnapshotStorage_1.SerializedSnapshotStorage.serializeTreeWithBlobContents(snapshot);
|
|
1773
1924
|
}
|
|
1774
1925
|
}
|
|
1775
1926
|
async initializeBaseSnapshotBlobs() {
|
|
1776
1927
|
var _a;
|
|
1777
1928
|
if (!((_a = this.mc.config.getBoolean("enableOfflineLoad")) !== null && _a !== void 0 ? _a : this.runtimeOptions.enableOfflineLoad) ||
|
|
1778
|
-
this.attachState !== container_definitions_1.AttachState.Attached ||
|
|
1929
|
+
this.attachState !== container_definitions_1.AttachState.Attached ||
|
|
1930
|
+
this.context.pendingLocalState) {
|
|
1779
1931
|
return;
|
|
1780
1932
|
}
|
|
1781
1933
|
(0, common_utils_1.assert)(!!this.context.baseSnapshot, 0x2e5 /* "Must have a base snapshot" */);
|
|
@@ -1786,13 +1938,13 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1786
1938
|
if (!((_a = this.mc.config.getBoolean("enableOfflineLoad")) !== null && _a !== void 0 ? _a : this.runtimeOptions.enableOfflineLoad)) {
|
|
1787
1939
|
throw new container_utils_1.UsageError("can't get state when offline load disabled");
|
|
1788
1940
|
}
|
|
1941
|
+
if (this._orderSequentiallyCalls !== 0) {
|
|
1942
|
+
throw new container_utils_1.UsageError("can't get state during orderSequentially");
|
|
1943
|
+
}
|
|
1789
1944
|
// Flush pending batch.
|
|
1790
1945
|
// getPendingLocalState() is only exposed through Container.closeAndGetPendingLocalState(), so it's safe
|
|
1791
1946
|
// to close current batch.
|
|
1792
1947
|
this.flush();
|
|
1793
|
-
if (this._orderSequentiallyCalls !== 0) {
|
|
1794
|
-
throw new container_utils_1.UsageError("can't get state during orderSequentially");
|
|
1795
|
-
}
|
|
1796
1948
|
const previousPendingState = this.context.pendingLocalState;
|
|
1797
1949
|
if (previousPendingState) {
|
|
1798
1950
|
return {
|
|
@@ -1873,6 +2025,7 @@ exports.ContainerRuntime = ContainerRuntime;
|
|
|
1873
2025
|
const waitForSeq = async (deltaManager, targetSeq) => new Promise((resolve, reject) => {
|
|
1874
2026
|
// TODO: remove cast to any when actual event is determined
|
|
1875
2027
|
deltaManager.on("closed", reject);
|
|
2028
|
+
deltaManager.on("disposed", reject);
|
|
1876
2029
|
// If we already reached target sequence number, simply resolve the promise.
|
|
1877
2030
|
if (deltaManager.lastSequenceNumber >= targetSeq) {
|
|
1878
2031
|
resolve();
|