@fluidframework/container-runtime 2.0.0-dev.6.4.0.192049 → 2.0.0-dev.7.2.0.204906
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +119 -0
- package/api-extractor.json +1 -1
- package/api-report/container-runtime.api.md +866 -0
- package/dist/blobManager.d.ts +4 -6
- package/dist/blobManager.d.ts.map +1 -1
- package/dist/blobManager.js +56 -78
- package/dist/blobManager.js.map +1 -1
- package/dist/connectionTelemetry.d.ts.map +1 -1
- package/dist/connectionTelemetry.js +75 -42
- package/dist/connectionTelemetry.js.map +1 -1
- package/dist/container-runtime-alpha.d.ts +1744 -0
- package/dist/container-runtime-beta.d.ts +1744 -0
- package/dist/container-runtime-public.d.ts +1744 -0
- package/dist/container-runtime-untrimmed.d.ts +1805 -0
- package/dist/containerHandleContext.js +3 -3
- package/dist/containerHandleContext.js.map +1 -1
- package/dist/containerRuntime.d.ts +94 -102
- package/dist/containerRuntime.d.ts.map +1 -1
- package/dist/containerRuntime.js +478 -454
- package/dist/containerRuntime.js.map +1 -1
- package/dist/dataStore.js +11 -11
- package/dist/dataStore.js.map +1 -1
- package/dist/dataStoreContext.d.ts +2 -4
- package/dist/dataStoreContext.d.ts.map +1 -1
- package/dist/dataStoreContext.js +56 -59
- package/dist/dataStoreContext.js.map +1 -1
- package/dist/dataStoreRegistry.d.ts +3 -0
- package/dist/dataStoreRegistry.d.ts.map +1 -1
- package/dist/dataStoreRegistry.js +6 -3
- package/dist/dataStoreRegistry.js.map +1 -1
- package/dist/dataStores.d.ts +0 -2
- package/dist/dataStores.d.ts.map +1 -1
- package/dist/dataStores.js +3 -8
- package/dist/dataStores.js.map +1 -1
- package/dist/deltaManagerProxyBase.js +4 -4
- package/dist/deltaManagerProxyBase.js.map +1 -1
- package/dist/deltaManagerSummarizerProxy.js +6 -6
- package/dist/deltaManagerSummarizerProxy.js.map +1 -1
- package/dist/deltaScheduler.js.map +1 -1
- package/dist/error.d.ts.map +1 -1
- package/dist/error.js.map +1 -1
- package/dist/gc/garbageCollection.d.ts +6 -0
- package/dist/gc/garbageCollection.d.ts.map +1 -1
- package/dist/gc/garbageCollection.js +36 -25
- package/dist/gc/garbageCollection.js.map +1 -1
- package/dist/gc/gcConfigs.d.ts +1 -0
- package/dist/gc/gcConfigs.d.ts.map +1 -1
- package/dist/gc/gcConfigs.js +13 -3
- package/dist/gc/gcConfigs.js.map +1 -1
- package/dist/gc/gcDefinitions.d.ts +48 -28
- package/dist/gc/gcDefinitions.d.ts.map +1 -1
- package/dist/gc/gcDefinitions.js +10 -7
- package/dist/gc/gcDefinitions.js.map +1 -1
- package/dist/gc/gcSummaryDefinitions.d.ts +1 -1
- package/dist/gc/gcSummaryDefinitions.js.map +1 -1
- package/dist/gc/gcTelemetry.d.ts +3 -4
- package/dist/gc/gcTelemetry.d.ts.map +1 -1
- package/dist/gc/gcTelemetry.js +7 -8
- package/dist/gc/gcTelemetry.js.map +1 -1
- package/dist/gc/gcUnreferencedStateTracker.js +3 -3
- package/dist/gc/gcUnreferencedStateTracker.js.map +1 -1
- package/dist/gc/index.d.ts +2 -2
- package/dist/gc/index.d.ts.map +1 -1
- package/dist/gc/index.js +3 -7
- package/dist/gc/index.js.map +1 -1
- package/dist/id-compressor/appendOnlySortedMap.js.map +1 -1
- package/dist/id-compressor/idCompressor.js.map +1 -1
- package/dist/id-compressor/identifiers.d.ts +3 -3
- package/dist/id-compressor/identifiers.d.ts.map +1 -1
- package/dist/id-compressor/utilities.d.ts +3 -0
- package/dist/id-compressor/utilities.d.ts.map +1 -1
- package/dist/id-compressor/utilities.js +3 -0
- package/dist/id-compressor/utilities.js.map +1 -1
- package/dist/index.d.ts +5 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -2
- package/dist/index.js.map +1 -1
- package/dist/messageTypes.d.ts +137 -0
- package/dist/messageTypes.d.ts.map +1 -0
- package/dist/messageTypes.js +32 -0
- package/dist/messageTypes.js.map +1 -0
- package/dist/opLifecycle/batchManager.js +6 -6
- package/dist/opLifecycle/batchManager.js.map +1 -1
- package/dist/opLifecycle/definitions.d.ts +7 -3
- package/dist/opLifecycle/definitions.d.ts.map +1 -1
- package/dist/opLifecycle/definitions.js.map +1 -1
- package/dist/opLifecycle/opDecompressor.d.ts.map +1 -1
- package/dist/opLifecycle/opDecompressor.js +0 -4
- package/dist/opLifecycle/opDecompressor.js.map +1 -1
- package/dist/opLifecycle/opGroupingManager.d.ts.map +1 -1
- package/dist/opLifecycle/opGroupingManager.js.map +1 -1
- package/dist/opLifecycle/opSplitter.js +3 -3
- package/dist/opLifecycle/opSplitter.js.map +1 -1
- package/dist/opLifecycle/outbox.d.ts.map +1 -1
- package/dist/opLifecycle/outbox.js +7 -2
- package/dist/opLifecycle/outbox.js.map +1 -1
- package/dist/opLifecycle/remoteMessageProcessor.d.ts +17 -3
- package/dist/opLifecycle/remoteMessageProcessor.d.ts.map +1 -1
- package/dist/opLifecycle/remoteMessageProcessor.js +38 -25
- package/dist/opLifecycle/remoteMessageProcessor.js.map +1 -1
- package/dist/packageVersion.d.ts +1 -1
- package/dist/packageVersion.js +1 -1
- package/dist/packageVersion.js.map +1 -1
- package/dist/pendingStateManager.d.ts +4 -20
- package/dist/pendingStateManager.d.ts.map +1 -1
- package/dist/pendingStateManager.js +36 -46
- package/dist/pendingStateManager.js.map +1 -1
- package/dist/scheduleManager.js +6 -2
- package/dist/scheduleManager.js.map +1 -1
- package/dist/summary/orderedClientElection.d.ts +7 -4
- package/dist/summary/orderedClientElection.d.ts.map +1 -1
- package/dist/summary/orderedClientElection.js +54 -54
- package/dist/summary/orderedClientElection.js.map +1 -1
- package/dist/summary/runWhileConnectedCoordinator.d.ts +5 -0
- package/dist/summary/runWhileConnectedCoordinator.d.ts.map +1 -1
- package/dist/summary/runWhileConnectedCoordinator.js +7 -6
- package/dist/summary/runWhileConnectedCoordinator.js.map +1 -1
- package/dist/summary/runningSummarizer.d.ts.map +1 -1
- package/dist/summary/runningSummarizer.js +40 -38
- package/dist/summary/runningSummarizer.js.map +1 -1
- package/dist/summary/summarizer.d.ts +2 -0
- package/dist/summary/summarizer.d.ts.map +1 -1
- package/dist/summary/summarizer.js +18 -8
- package/dist/summary/summarizer.js.map +1 -1
- package/dist/summary/summarizerClientElection.js +6 -6
- package/dist/summary/summarizerClientElection.js.map +1 -1
- package/dist/summary/summarizerHeuristics.js +9 -9
- package/dist/summary/summarizerHeuristics.js.map +1 -1
- package/dist/summary/summarizerNode/summarizerNode.d.ts +1 -1
- package/dist/summary/summarizerNode/summarizerNode.js +8 -8
- package/dist/summary/summarizerNode/summarizerNode.js.map +1 -1
- package/dist/summary/summarizerNode/summarizerNodeUtils.d.ts +1 -1
- package/dist/summary/summarizerNode/summarizerNodeUtils.d.ts.map +1 -1
- package/dist/summary/summarizerNode/summarizerNodeUtils.js +3 -3
- package/dist/summary/summarizerNode/summarizerNodeUtils.js.map +1 -1
- package/dist/summary/summarizerNode/summarizerNodeWithGc.d.ts +2 -2
- package/dist/summary/summarizerNode/summarizerNodeWithGc.d.ts.map +1 -1
- package/dist/summary/summarizerTypes.d.ts +107 -22
- package/dist/summary/summarizerTypes.d.ts.map +1 -1
- package/dist/summary/summarizerTypes.js.map +1 -1
- package/dist/summary/summaryCollection.d.ts +18 -2
- package/dist/summary/summaryCollection.d.ts.map +1 -1
- package/dist/summary/summaryCollection.js +23 -21
- package/dist/summary/summaryCollection.js.map +1 -1
- package/dist/summary/summaryFormat.d.ts +15 -6
- package/dist/summary/summaryFormat.d.ts.map +1 -1
- package/dist/summary/summaryFormat.js.map +1 -1
- package/dist/summary/summaryGenerator.d.ts +3 -3
- package/dist/summary/summaryGenerator.d.ts.map +1 -1
- package/dist/summary/summaryGenerator.js.map +1 -1
- package/dist/summary/summaryManager.d.ts +2 -2
- package/dist/summary/summaryManager.d.ts.map +1 -1
- package/dist/summary/summaryManager.js +10 -10
- package/dist/summary/summaryManager.js.map +1 -1
- package/dist/throttler.js +16 -16
- package/dist/throttler.js.map +1 -1
- package/dist/tsdoc-metadata.json +1 -1
- package/lib/blobManager.d.ts +4 -6
- package/lib/blobManager.d.ts.map +1 -1
- package/lib/blobManager.js +58 -80
- package/lib/blobManager.js.map +1 -1
- package/lib/connectionTelemetry.d.ts.map +1 -1
- package/lib/connectionTelemetry.js +76 -43
- package/lib/connectionTelemetry.js.map +1 -1
- package/lib/containerHandleContext.js +3 -3
- package/lib/containerHandleContext.js.map +1 -1
- package/lib/containerRuntime.d.ts +94 -102
- package/lib/containerRuntime.d.ts.map +1 -1
- package/lib/containerRuntime.js +437 -418
- package/lib/containerRuntime.js.map +1 -1
- package/lib/dataStore.js +11 -11
- package/lib/dataStore.js.map +1 -1
- package/lib/dataStoreContext.d.ts +2 -4
- package/lib/dataStoreContext.d.ts.map +1 -1
- package/lib/dataStoreContext.js +57 -60
- package/lib/dataStoreContext.js.map +1 -1
- package/lib/dataStoreRegistry.d.ts +3 -0
- package/lib/dataStoreRegistry.d.ts.map +1 -1
- package/lib/dataStoreRegistry.js +6 -3
- package/lib/dataStoreRegistry.js.map +1 -1
- package/lib/dataStores.d.ts +0 -2
- package/lib/dataStores.d.ts.map +1 -1
- package/lib/dataStores.js +4 -9
- package/lib/dataStores.js.map +1 -1
- package/lib/deltaManagerProxyBase.js +4 -4
- package/lib/deltaManagerProxyBase.js.map +1 -1
- package/lib/deltaManagerSummarizerProxy.js +6 -6
- package/lib/deltaManagerSummarizerProxy.js.map +1 -1
- package/lib/deltaScheduler.js.map +1 -1
- package/lib/error.d.ts.map +1 -1
- package/lib/error.js.map +1 -1
- package/lib/gc/garbageCollection.d.ts +6 -0
- package/lib/gc/garbageCollection.d.ts.map +1 -1
- package/lib/gc/garbageCollection.js +36 -25
- package/lib/gc/garbageCollection.js.map +1 -1
- package/lib/gc/gcConfigs.d.ts +1 -0
- package/lib/gc/gcConfigs.d.ts.map +1 -1
- package/lib/gc/gcConfigs.js +15 -5
- package/lib/gc/gcConfigs.js.map +1 -1
- package/lib/gc/gcDefinitions.d.ts +48 -28
- package/lib/gc/gcDefinitions.d.ts.map +1 -1
- package/lib/gc/gcDefinitions.js +9 -6
- package/lib/gc/gcDefinitions.js.map +1 -1
- package/lib/gc/gcSummaryDefinitions.d.ts +1 -1
- package/lib/gc/gcSummaryDefinitions.js.map +1 -1
- package/lib/gc/gcTelemetry.d.ts +3 -4
- package/lib/gc/gcTelemetry.d.ts.map +1 -1
- package/lib/gc/gcTelemetry.js +7 -8
- package/lib/gc/gcTelemetry.js.map +1 -1
- package/lib/gc/gcUnreferencedStateTracker.js +3 -3
- package/lib/gc/gcUnreferencedStateTracker.js.map +1 -1
- package/lib/gc/index.d.ts +2 -2
- package/lib/gc/index.d.ts.map +1 -1
- package/lib/gc/index.js +2 -2
- package/lib/gc/index.js.map +1 -1
- package/lib/id-compressor/appendOnlySortedMap.js.map +1 -1
- package/lib/id-compressor/idCompressor.js.map +1 -1
- package/lib/id-compressor/identifiers.d.ts +3 -3
- package/lib/id-compressor/identifiers.d.ts.map +1 -1
- package/lib/id-compressor/utilities.d.ts +3 -0
- package/lib/id-compressor/utilities.d.ts.map +1 -1
- package/lib/id-compressor/utilities.js +3 -0
- package/lib/id-compressor/utilities.js.map +1 -1
- package/lib/index.d.ts +5 -3
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +3 -1
- package/lib/index.js.map +1 -1
- package/lib/messageTypes.d.ts +137 -0
- package/lib/messageTypes.d.ts.map +1 -0
- package/lib/messageTypes.js +29 -0
- package/lib/messageTypes.js.map +1 -0
- package/lib/opLifecycle/batchManager.js +6 -6
- package/lib/opLifecycle/batchManager.js.map +1 -1
- package/lib/opLifecycle/definitions.d.ts +7 -3
- package/lib/opLifecycle/definitions.d.ts.map +1 -1
- package/lib/opLifecycle/definitions.js.map +1 -1
- package/lib/opLifecycle/opDecompressor.d.ts.map +1 -1
- package/lib/opLifecycle/opDecompressor.js +0 -4
- package/lib/opLifecycle/opDecompressor.js.map +1 -1
- package/lib/opLifecycle/opGroupingManager.d.ts.map +1 -1
- package/lib/opLifecycle/opGroupingManager.js.map +1 -1
- package/lib/opLifecycle/opSplitter.js +1 -1
- package/lib/opLifecycle/opSplitter.js.map +1 -1
- package/lib/opLifecycle/outbox.d.ts.map +1 -1
- package/lib/opLifecycle/outbox.js +7 -2
- package/lib/opLifecycle/outbox.js.map +1 -1
- package/lib/opLifecycle/remoteMessageProcessor.d.ts +17 -3
- package/lib/opLifecycle/remoteMessageProcessor.d.ts.map +1 -1
- package/lib/opLifecycle/remoteMessageProcessor.js +37 -24
- package/lib/opLifecycle/remoteMessageProcessor.js.map +1 -1
- package/lib/packageVersion.d.ts +1 -1
- package/lib/packageVersion.js +1 -1
- package/lib/packageVersion.js.map +1 -1
- package/lib/pendingStateManager.d.ts +4 -20
- package/lib/pendingStateManager.d.ts.map +1 -1
- package/lib/pendingStateManager.js +35 -45
- package/lib/pendingStateManager.js.map +1 -1
- package/lib/scheduleManager.js +6 -2
- package/lib/scheduleManager.js.map +1 -1
- package/lib/summary/orderedClientElection.d.ts +7 -4
- package/lib/summary/orderedClientElection.d.ts.map +1 -1
- package/lib/summary/orderedClientElection.js +54 -54
- package/lib/summary/orderedClientElection.js.map +1 -1
- package/lib/summary/runWhileConnectedCoordinator.d.ts +5 -0
- package/lib/summary/runWhileConnectedCoordinator.d.ts.map +1 -1
- package/lib/summary/runWhileConnectedCoordinator.js +7 -6
- package/lib/summary/runWhileConnectedCoordinator.js.map +1 -1
- package/lib/summary/runningSummarizer.d.ts.map +1 -1
- package/lib/summary/runningSummarizer.js +40 -38
- package/lib/summary/runningSummarizer.js.map +1 -1
- package/lib/summary/summarizer.d.ts +2 -0
- package/lib/summary/summarizer.d.ts.map +1 -1
- package/lib/summary/summarizer.js +19 -9
- package/lib/summary/summarizer.js.map +1 -1
- package/lib/summary/summarizerClientElection.js +6 -6
- package/lib/summary/summarizerClientElection.js.map +1 -1
- package/lib/summary/summarizerHeuristics.js +9 -9
- package/lib/summary/summarizerHeuristics.js.map +1 -1
- package/lib/summary/summarizerNode/summarizerNode.d.ts +1 -1
- package/lib/summary/summarizerNode/summarizerNode.js +8 -8
- package/lib/summary/summarizerNode/summarizerNode.js.map +1 -1
- package/lib/summary/summarizerNode/summarizerNodeUtils.d.ts +1 -1
- package/lib/summary/summarizerNode/summarizerNodeUtils.d.ts.map +1 -1
- package/lib/summary/summarizerNode/summarizerNodeUtils.js +3 -3
- package/lib/summary/summarizerNode/summarizerNodeUtils.js.map +1 -1
- package/lib/summary/summarizerNode/summarizerNodeWithGc.d.ts +2 -2
- package/lib/summary/summarizerNode/summarizerNodeWithGc.d.ts.map +1 -1
- package/lib/summary/summarizerTypes.d.ts +107 -22
- package/lib/summary/summarizerTypes.d.ts.map +1 -1
- package/lib/summary/summarizerTypes.js.map +1 -1
- package/lib/summary/summaryCollection.d.ts +18 -2
- package/lib/summary/summaryCollection.d.ts.map +1 -1
- package/lib/summary/summaryCollection.js +23 -21
- package/lib/summary/summaryCollection.js.map +1 -1
- package/lib/summary/summaryFormat.d.ts +15 -6
- package/lib/summary/summaryFormat.d.ts.map +1 -1
- package/lib/summary/summaryFormat.js.map +1 -1
- package/lib/summary/summaryGenerator.d.ts +3 -3
- package/lib/summary/summaryGenerator.d.ts.map +1 -1
- package/lib/summary/summaryGenerator.js.map +1 -1
- package/lib/summary/summaryManager.d.ts +2 -2
- package/lib/summary/summaryManager.d.ts.map +1 -1
- package/lib/summary/summaryManager.js +9 -9
- package/lib/summary/summaryManager.js.map +1 -1
- package/lib/throttler.js +16 -16
- package/lib/throttler.js.map +1 -1
- package/package.json +32 -29
- package/src/blobManager.ts +67 -92
- package/src/connectionTelemetry.ts +97 -52
- package/src/containerRuntime.ts +351 -351
- package/src/dataStore.ts +3 -3
- package/src/dataStoreContext.ts +9 -13
- package/src/dataStoreRegistry.ts +3 -0
- package/src/dataStores.ts +5 -17
- package/src/error.ts +4 -1
- package/src/gc/garbageCollection.ts +25 -12
- package/src/gc/gcConfigs.ts +25 -7
- package/src/gc/gcDefinitions.ts +49 -29
- package/src/gc/gcSummaryDefinitions.ts +1 -1
- package/src/gc/gcTelemetry.ts +8 -8
- package/src/gc/index.ts +2 -6
- package/src/id-compressor/utilities.ts +3 -0
- package/src/index.ts +21 -5
- package/src/messageTypes.ts +228 -0
- package/src/opLifecycle/README.md +93 -68
- package/src/opLifecycle/definitions.ts +5 -1
- package/src/opLifecycle/opDecompressor.ts +0 -8
- package/src/opLifecycle/opGroupingManager.ts +2 -4
- package/src/opLifecycle/opSplitter.ts +2 -2
- package/src/opLifecycle/outbox.ts +3 -0
- package/src/opLifecycle/remoteMessageProcessor.ts +54 -33
- package/src/packageVersion.ts +1 -1
- package/src/pendingStateManager.ts +31 -52
- package/src/scheduleManager.ts +2 -0
- package/src/summary/orderedClientElection.ts +4 -1
- package/src/summary/runWhileConnectedCoordinator.ts +5 -1
- package/src/summary/runningSummarizer.ts +3 -1
- package/src/summary/summarizer.ts +21 -7
- package/src/summary/summarizerNode/summarizerNode.ts +1 -1
- package/src/summary/summarizerTypes.ts +96 -11
- package/src/summary/summaryCollection.ts +19 -1
- package/src/summary/summaryFormat.ts +11 -1
- package/src/summary/summaryGenerator.ts +3 -3
- package/src/summary/summaryManager.ts +2 -2
- package/src/gc/gcEarlyAdoption.md +0 -145
package/dist/containerRuntime.js
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
3
|
if (k2 === undefined) k2 = k;
|
|
4
|
-
Object.
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
5
9
|
}) : (function(o, m, k, k2) {
|
|
6
10
|
if (k2 === undefined) k2 = k;
|
|
7
11
|
o[k2] = m[k];
|
|
@@ -19,7 +23,7 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
|
|
19
23
|
return result;
|
|
20
24
|
};
|
|
21
25
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
22
|
-
exports.ContainerRuntime = exports.makeLegacySendBatchFn = exports.getDeviceSpec = exports.agentSchedulerId = exports.isRuntimeMessage = exports.RuntimeMessage = exports.defaultPendingOpsRetryDelayMs = exports.defaultPendingOpsWaitTimeoutMs = exports.CompressionAlgorithms = exports.defaultRuntimeHeaderData = exports.InactiveResponseHeaderKey = exports.TombstoneResponseHeaderKey = exports.AllowInactiveRequestHeaderKey = exports.AllowTombstoneRequestHeaderKey = exports.RuntimeHeaders = exports.DefaultSummaryConfiguration =
|
|
26
|
+
exports.ContainerRuntime = exports.TEST_requestSummarizer = exports.makeLegacySendBatchFn = exports.getDeviceSpec = exports.agentSchedulerId = exports.isRuntimeMessage = exports.RuntimeMessage = exports.defaultPendingOpsRetryDelayMs = exports.defaultPendingOpsWaitTimeoutMs = exports.CompressionAlgorithms = exports.defaultRuntimeHeaderData = exports.InactiveResponseHeaderKey = exports.TombstoneResponseHeaderKey = exports.AllowInactiveRequestHeaderKey = exports.AllowTombstoneRequestHeaderKey = exports.RuntimeHeaders = exports.DefaultSummaryConfiguration = void 0;
|
|
23
27
|
const container_definitions_1 = require("@fluidframework/container-definitions");
|
|
24
28
|
const core_utils_1 = require("@fluidframework/core-utils");
|
|
25
29
|
const client_utils_1 = require("@fluid-internal/client-utils");
|
|
@@ -45,32 +49,12 @@ const batchTracker_1 = require("./batchTracker");
|
|
|
45
49
|
const scheduleManager_1 = require("./scheduleManager");
|
|
46
50
|
const opLifecycle_1 = require("./opLifecycle");
|
|
47
51
|
const deltaManagerSummarizerProxy_1 = require("./deltaManagerSummarizerProxy");
|
|
48
|
-
|
|
49
|
-
(function (ContainerMessageType) {
|
|
50
|
-
// An op to be delivered to store
|
|
51
|
-
ContainerMessageType["FluidDataStoreOp"] = "component";
|
|
52
|
-
// Creates a new store
|
|
53
|
-
ContainerMessageType["Attach"] = "attach";
|
|
54
|
-
// Chunked operation.
|
|
55
|
-
ContainerMessageType["ChunkedOp"] = "chunkedOp";
|
|
56
|
-
// Signifies that a blob has been attached and should not be garbage collected by storage
|
|
57
|
-
ContainerMessageType["BlobAttach"] = "blobAttach";
|
|
58
|
-
// Ties our new clientId to our old one on reconnect
|
|
59
|
-
ContainerMessageType["Rejoin"] = "rejoin";
|
|
60
|
-
// Sets the alias of a root data store
|
|
61
|
-
ContainerMessageType["Alias"] = "alias";
|
|
62
|
-
/**
|
|
63
|
-
* An op containing an IdRange of Ids allocated using the runtime's IdCompressor since
|
|
64
|
-
* the last allocation op was sent.
|
|
65
|
-
* See the [IdCompressor README](./id-compressor/README.md) for more details.
|
|
66
|
-
*/
|
|
67
|
-
ContainerMessageType["IdAllocation"] = "idAllocation";
|
|
68
|
-
})(ContainerMessageType = exports.ContainerMessageType || (exports.ContainerMessageType = {}));
|
|
52
|
+
const messageTypes_1 = require("./messageTypes");
|
|
69
53
|
/**
|
|
70
54
|
* Utility to implement compat behaviors given an unknown message type
|
|
71
55
|
* The parameters are typed to support compile-time enforcement of handling all known types/behaviors
|
|
72
56
|
*
|
|
73
|
-
* @param _unknownContainerRuntimeMessageType - Typed as
|
|
57
|
+
* @param _unknownContainerRuntimeMessageType - Typed as something unexpected, to ensure all known types have been
|
|
74
58
|
* handled before calling this function (e.g. in a switch statement).
|
|
75
59
|
* @param compatBehavior - Typed redundantly with CompatModeBehavior to ensure handling is added when updating that type
|
|
76
60
|
*/
|
|
@@ -78,6 +62,15 @@ function compatBehaviorAllowsMessageType(_unknownContainerRuntimeMessageType, co
|
|
|
78
62
|
// undefined defaults to same behavior as "FailToProcess"
|
|
79
63
|
return compatBehavior === "Ignore";
|
|
80
64
|
}
|
|
65
|
+
function prepareLocalContainerRuntimeIdAllocationMessageForTransit(message) {
|
|
66
|
+
// Remove the stashedState from the op if it's a stashed op
|
|
67
|
+
if ("stashedState" in message.contents) {
|
|
68
|
+
delete message.contents.stashedState;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* @public
|
|
73
|
+
*/
|
|
81
74
|
exports.DefaultSummaryConfiguration = {
|
|
82
75
|
state: "enabled",
|
|
83
76
|
minIdleTime: 0,
|
|
@@ -94,6 +87,7 @@ exports.DefaultSummaryConfiguration = {
|
|
|
94
87
|
};
|
|
95
88
|
/**
|
|
96
89
|
* Accepted header keys for requests coming to the runtime.
|
|
90
|
+
* @public
|
|
97
91
|
*/
|
|
98
92
|
var RuntimeHeaders;
|
|
99
93
|
(function (RuntimeHeaders) {
|
|
@@ -101,14 +95,25 @@ var RuntimeHeaders;
|
|
|
101
95
|
RuntimeHeaders["wait"] = "wait";
|
|
102
96
|
/** True if the request is coming from an IFluidHandle. */
|
|
103
97
|
RuntimeHeaders["viaHandle"] = "viaHandle";
|
|
104
|
-
})(RuntimeHeaders
|
|
105
|
-
/** True if a tombstoned object should be returned without erroring
|
|
98
|
+
})(RuntimeHeaders || (exports.RuntimeHeaders = RuntimeHeaders = {}));
|
|
99
|
+
/** True if a tombstoned object should be returned without erroring
|
|
100
|
+
* @public
|
|
101
|
+
*/
|
|
106
102
|
exports.AllowTombstoneRequestHeaderKey = "allowTombstone"; // Belongs in the enum above, but avoiding the breaking change
|
|
107
|
-
/**
|
|
103
|
+
/**
|
|
104
|
+
* [IRRELEVANT IF throwOnInactiveLoad OPTION NOT SET] True if an inactive object should be returned without erroring
|
|
105
|
+
* @public
|
|
106
|
+
*/
|
|
108
107
|
exports.AllowInactiveRequestHeaderKey = "allowInactive"; // Belongs in the enum above, but avoiding the breaking change
|
|
109
|
-
/**
|
|
108
|
+
/**
|
|
109
|
+
* Tombstone error responses will have this header set to true
|
|
110
|
+
* @public
|
|
111
|
+
*/
|
|
110
112
|
exports.TombstoneResponseHeaderKey = "isTombstoned";
|
|
111
|
-
/**
|
|
113
|
+
/**
|
|
114
|
+
* Inactive error responses will have this header set to true
|
|
115
|
+
* @public
|
|
116
|
+
*/
|
|
112
117
|
exports.InactiveResponseHeaderKey = "isInactive";
|
|
113
118
|
/** Default values for Runtime Headers */
|
|
114
119
|
exports.defaultRuntimeHeaderData = {
|
|
@@ -118,11 +123,12 @@ exports.defaultRuntimeHeaderData = {
|
|
|
118
123
|
};
|
|
119
124
|
/**
|
|
120
125
|
* Available compression algorithms for op compression.
|
|
126
|
+
* @public
|
|
121
127
|
*/
|
|
122
128
|
var CompressionAlgorithms;
|
|
123
129
|
(function (CompressionAlgorithms) {
|
|
124
130
|
CompressionAlgorithms["lz4"] = "lz4";
|
|
125
|
-
})(CompressionAlgorithms
|
|
131
|
+
})(CompressionAlgorithms || (exports.CompressionAlgorithms = CompressionAlgorithms = {}));
|
|
126
132
|
const maxConsecutiveReconnectsKey = "Fluid.ContainerRuntime.MaxConsecutiveReconnects";
|
|
127
133
|
const defaultFlushMode = runtime_definitions_1.FlushMode.TurnBased;
|
|
128
134
|
// The actual limit is 1Mb (socket.io and Kafka limits)
|
|
@@ -147,7 +153,8 @@ exports.defaultPendingOpsRetryDelayMs = 1000;
|
|
|
147
153
|
*/
|
|
148
154
|
const defaultCloseSummarizerDelayMs = 5000; // 5 seconds
|
|
149
155
|
/**
|
|
150
|
-
* @deprecated
|
|
156
|
+
* @deprecated use ContainerRuntimeMessageType instead
|
|
157
|
+
* @public
|
|
151
158
|
*/
|
|
152
159
|
var RuntimeMessage;
|
|
153
160
|
(function (RuntimeMessage) {
|
|
@@ -158,9 +165,10 @@ var RuntimeMessage;
|
|
|
158
165
|
RuntimeMessage["Rejoin"] = "rejoin";
|
|
159
166
|
RuntimeMessage["Alias"] = "alias";
|
|
160
167
|
RuntimeMessage["Operation"] = "op";
|
|
161
|
-
})(RuntimeMessage
|
|
168
|
+
})(RuntimeMessage || (exports.RuntimeMessage = RuntimeMessage = {}));
|
|
162
169
|
/**
|
|
163
|
-
* @deprecated
|
|
170
|
+
* @deprecated please use version in driver-utils
|
|
171
|
+
* @public
|
|
164
172
|
*/
|
|
165
173
|
function isRuntimeMessage(message) {
|
|
166
174
|
return Object.values(RuntimeMessage).includes(message.type);
|
|
@@ -170,6 +178,7 @@ exports.isRuntimeMessage = isRuntimeMessage;
|
|
|
170
178
|
* Legacy ID for the built-in AgentScheduler. To minimize disruption while removing it, retaining this as a
|
|
171
179
|
* special-case for document dirty state. Ultimately we should have no special-cases from the
|
|
172
180
|
* ContainerRuntime's perspective.
|
|
181
|
+
* @public
|
|
173
182
|
*/
|
|
174
183
|
exports.agentSchedulerId = "_scheduler";
|
|
175
184
|
// safely check navigator and get the hardware spec value
|
|
@@ -201,20 +210,289 @@ const makeLegacySendBatchFn = (submitFn, deltaManager) => (batch) => {
|
|
|
201
210
|
deltaManager.flush();
|
|
202
211
|
};
|
|
203
212
|
exports.makeLegacySendBatchFn = makeLegacySendBatchFn;
|
|
213
|
+
const summarizerRequestUrl = "_summarizer";
|
|
214
|
+
/**
|
|
215
|
+
* Create and retrieve the summmarizer
|
|
216
|
+
*/
|
|
217
|
+
async function createSummarizer(loader, url) {
|
|
218
|
+
const request = {
|
|
219
|
+
headers: {
|
|
220
|
+
[container_definitions_1.LoaderHeader.cache]: false,
|
|
221
|
+
[container_definitions_1.LoaderHeader.clientDetails]: {
|
|
222
|
+
capabilities: { interactive: false },
|
|
223
|
+
type: summary_1.summarizerClientType,
|
|
224
|
+
},
|
|
225
|
+
[driver_definitions_1.DriverHeader.summarizingClient]: true,
|
|
226
|
+
[container_definitions_1.LoaderHeader.reconnect]: false,
|
|
227
|
+
},
|
|
228
|
+
url,
|
|
229
|
+
};
|
|
230
|
+
const resolvedContainer = await loader.resolve(request);
|
|
231
|
+
let fluidObject;
|
|
232
|
+
// Older containers may not have the "getEntryPoint" API
|
|
233
|
+
// ! This check will need to stay until LTS of loader moves past 2.0.0-internal.7.0.0
|
|
234
|
+
if (resolvedContainer.getEntryPoint !== undefined) {
|
|
235
|
+
fluidObject = await resolvedContainer.getEntryPoint();
|
|
236
|
+
}
|
|
237
|
+
else {
|
|
238
|
+
const response = await resolvedContainer.request({ url: `/${summarizerRequestUrl}` });
|
|
239
|
+
if (response.status !== 200 || response.mimeType !== "fluid/object") {
|
|
240
|
+
throw (0, runtime_utils_1.responseToException)(response, request);
|
|
241
|
+
}
|
|
242
|
+
fluidObject = response.value;
|
|
243
|
+
}
|
|
244
|
+
if (fluidObject?.ISummarizer === undefined) {
|
|
245
|
+
throw new telemetry_utils_1.UsageError("Fluid object does not implement ISummarizer");
|
|
246
|
+
}
|
|
247
|
+
return fluidObject.ISummarizer;
|
|
248
|
+
}
|
|
249
|
+
/**
|
|
250
|
+
* This function is not supported publicly and exists for e2e testing
|
|
251
|
+
* @internal
|
|
252
|
+
*/
|
|
253
|
+
async function TEST_requestSummarizer(loader, url) {
|
|
254
|
+
return createSummarizer(loader, url);
|
|
255
|
+
}
|
|
256
|
+
exports.TEST_requestSummarizer = TEST_requestSummarizer;
|
|
204
257
|
/**
|
|
205
258
|
* Represents the runtime of the container. Contains helper functions/state of the container.
|
|
206
259
|
* It will define the store level mappings.
|
|
260
|
+
* @public
|
|
207
261
|
*/
|
|
208
262
|
class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
263
|
+
/**
|
|
264
|
+
* @deprecated Will be removed in future major release. Migrate all usage of IFluidRouter to the "entryPoint" pattern. Refer to Removing-IFluidRouter.md
|
|
265
|
+
*/
|
|
266
|
+
get IFluidRouter() {
|
|
267
|
+
return this;
|
|
268
|
+
}
|
|
269
|
+
/**
|
|
270
|
+
* @deprecated use loadRuntime instead.
|
|
271
|
+
* Load the stores from a snapshot and returns the runtime.
|
|
272
|
+
* @param context - Context of the container.
|
|
273
|
+
* @param registryEntries - Mapping to the stores.
|
|
274
|
+
* @param requestHandler - Request handlers for the container runtime
|
|
275
|
+
* @param runtimeOptions - Additional options to be passed to the runtime
|
|
276
|
+
* @param existing - (optional) When loading from an existing snapshot. Precedes context.existing if provided
|
|
277
|
+
* @param containerRuntimeCtor - (optional) Constructor to use to create the ContainerRuntime instance. This
|
|
278
|
+
* allows mixin classes to leverage this method to define their own async initializer.
|
|
279
|
+
*/
|
|
280
|
+
static async load(context, registryEntries, requestHandler, runtimeOptions = {}, containerScope = context.scope, existing, containerRuntimeCtor = ContainerRuntime) {
|
|
281
|
+
let existingFlag = true;
|
|
282
|
+
if (!existing) {
|
|
283
|
+
existingFlag = false;
|
|
284
|
+
}
|
|
285
|
+
return this.loadRuntime({
|
|
286
|
+
context,
|
|
287
|
+
registryEntries,
|
|
288
|
+
existing: existingFlag,
|
|
289
|
+
runtimeOptions,
|
|
290
|
+
containerScope,
|
|
291
|
+
containerRuntimeCtor,
|
|
292
|
+
requestHandler,
|
|
293
|
+
provideEntryPoint: () => {
|
|
294
|
+
throw new telemetry_utils_1.UsageError("ContainerRuntime.load is deprecated and should no longer be used");
|
|
295
|
+
},
|
|
296
|
+
});
|
|
297
|
+
}
|
|
298
|
+
/**
|
|
299
|
+
* Load the stores from a snapshot and returns the runtime.
|
|
300
|
+
* @param params - An object housing the runtime properties:
|
|
301
|
+
* - context - Context of the container.
|
|
302
|
+
* - registryEntries - Mapping from data store types to their corresponding factories.
|
|
303
|
+
* - existing - Pass 'true' if loading from an existing snapshot.
|
|
304
|
+
* - requestHandler - (optional) Request handler for the request() method of the container runtime.
|
|
305
|
+
* Only relevant for back-compat while we remove the request() method and move fully to entryPoint as the main pattern.
|
|
306
|
+
* - runtimeOptions - Additional options to be passed to the runtime
|
|
307
|
+
* - containerScope - runtime services provided with context
|
|
308
|
+
* - containerRuntimeCtor - Constructor to use to create the ContainerRuntime instance.
|
|
309
|
+
* This allows mixin classes to leverage this method to define their own async initializer.
|
|
310
|
+
* - provideEntryPoint - Promise that resolves to an object which will act as entryPoint for the Container.
|
|
311
|
+
* This object should provide all the functionality that the Container is expected to provide to the loader layer.
|
|
312
|
+
*/
|
|
313
|
+
static async loadRuntime(params) {
|
|
314
|
+
const { context, registryEntries, existing, requestHandler, provideEntryPoint, runtimeOptions = {}, containerScope = {}, containerRuntimeCtor = ContainerRuntime, } = params;
|
|
315
|
+
// If taggedLogger exists, use it. Otherwise, wrap the vanilla logger:
|
|
316
|
+
// back-compat: Remove the TaggedLoggerAdapter fallback once all the host are using loader > 0.45
|
|
317
|
+
const backCompatContext = context;
|
|
318
|
+
const passLogger = backCompatContext.taggedLogger ??
|
|
319
|
+
// eslint-disable-next-line import/no-deprecated
|
|
320
|
+
new telemetry_utils_1.TaggedLoggerAdapter(backCompatContext.logger);
|
|
321
|
+
const logger = (0, telemetry_utils_1.createChildLogger)({
|
|
322
|
+
logger: passLogger,
|
|
323
|
+
properties: {
|
|
324
|
+
all: {
|
|
325
|
+
runtimeVersion: packageVersion_1.pkgVersion,
|
|
326
|
+
},
|
|
327
|
+
},
|
|
328
|
+
});
|
|
329
|
+
const { summaryOptions = {}, gcOptions = {}, loadSequenceNumberVerification = "close", flushMode = defaultFlushMode, compressionOptions = defaultCompressionConfig, maxBatchSizeInBytes = defaultMaxBatchSizeInBytes, enableRuntimeIdCompressor = false, chunkSizeInBytes = defaultChunkSizeInBytes, enableOpReentryCheck = false, enableGroupedBatching = false, } = runtimeOptions;
|
|
330
|
+
const registry = new dataStoreRegistry_1.FluidDataStoreRegistry(registryEntries);
|
|
331
|
+
const tryFetchBlob = async (blobName) => {
|
|
332
|
+
const blobId = context.baseSnapshot?.blobs[blobName];
|
|
333
|
+
if (context.baseSnapshot && blobId) {
|
|
334
|
+
// IContainerContext storage api return type still has undefined in 0.39 package version.
|
|
335
|
+
// So once we release 0.40 container-defn package we can remove this check.
|
|
336
|
+
(0, core_utils_1.assert)(context.storage !== undefined, 0x1f5 /* "Attached state should have storage" */);
|
|
337
|
+
return (0, driver_utils_1.readAndParse)(context.storage, blobId);
|
|
338
|
+
}
|
|
339
|
+
};
|
|
340
|
+
const [chunks, metadata, electedSummarizerData, aliases, serializedIdCompressor] = await Promise.all([
|
|
341
|
+
tryFetchBlob(summary_1.chunksBlobName),
|
|
342
|
+
tryFetchBlob(summary_1.metadataBlobName),
|
|
343
|
+
tryFetchBlob(summary_1.electedSummarizerBlobName),
|
|
344
|
+
tryFetchBlob(summary_1.aliasBlobName),
|
|
345
|
+
tryFetchBlob(summary_1.idCompressorBlobName),
|
|
346
|
+
]);
|
|
347
|
+
// read snapshot blobs needed for BlobManager to load
|
|
348
|
+
const blobManagerSnapshot = await blobManager_1.BlobManager.load(context.baseSnapshot?.trees[summary_1.blobsTreeName], async (id) => {
|
|
349
|
+
// IContainerContext storage api return type still has undefined in 0.39 package version.
|
|
350
|
+
// So once we release 0.40 container-defn package we can remove this check.
|
|
351
|
+
(0, core_utils_1.assert)(context.storage !== undefined, 0x256 /* "storage undefined in attached container" */);
|
|
352
|
+
return (0, driver_utils_1.readAndParse)(context.storage, id);
|
|
353
|
+
});
|
|
354
|
+
// Verify summary runtime sequence number matches protocol sequence number.
|
|
355
|
+
const runtimeSequenceNumber = metadata?.message?.sequenceNumber;
|
|
356
|
+
// When we load with pending state, we reuse an old snapshot so we don't expect these numbers to match
|
|
357
|
+
if (!context.pendingLocalState && runtimeSequenceNumber !== undefined) {
|
|
358
|
+
const protocolSequenceNumber = context.deltaManager.initialSequenceNumber;
|
|
359
|
+
// Unless bypass is explicitly set, then take action when sequence numbers mismatch.
|
|
360
|
+
if (loadSequenceNumberVerification !== "bypass" &&
|
|
361
|
+
runtimeSequenceNumber !== protocolSequenceNumber) {
|
|
362
|
+
// "Load from summary, runtime metadata sequenceNumber !== initialSequenceNumber"
|
|
363
|
+
const error = new telemetry_utils_1.DataCorruptionError(
|
|
364
|
+
// pre-0.58 error message: SummaryMetadataMismatch
|
|
365
|
+
"Summary metadata mismatch", { runtimeVersion: packageVersion_1.pkgVersion, runtimeSequenceNumber, protocolSequenceNumber });
|
|
366
|
+
if (loadSequenceNumberVerification === "log") {
|
|
367
|
+
logger.sendErrorEvent({ eventName: "SequenceNumberMismatch" }, error);
|
|
368
|
+
}
|
|
369
|
+
else {
|
|
370
|
+
context.closeFn(error);
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
const idCompressorEnabled = metadata?.idCompressorEnabled ?? runtimeOptions.enableRuntimeIdCompressor ?? false;
|
|
375
|
+
let idCompressor;
|
|
376
|
+
if (idCompressorEnabled) {
|
|
377
|
+
const { IdCompressor, createSessionId } = await Promise.resolve().then(() => __importStar(require("./id-compressor")));
|
|
378
|
+
idCompressor =
|
|
379
|
+
serializedIdCompressor !== undefined
|
|
380
|
+
? IdCompressor.deserialize(serializedIdCompressor, createSessionId())
|
|
381
|
+
: IdCompressor.create(logger);
|
|
382
|
+
}
|
|
383
|
+
const runtime = new containerRuntimeCtor(context, registry, metadata, electedSummarizerData, chunks ?? [], aliases ?? [], {
|
|
384
|
+
summaryOptions,
|
|
385
|
+
gcOptions,
|
|
386
|
+
loadSequenceNumberVerification,
|
|
387
|
+
flushMode,
|
|
388
|
+
compressionOptions,
|
|
389
|
+
maxBatchSizeInBytes,
|
|
390
|
+
chunkSizeInBytes,
|
|
391
|
+
enableRuntimeIdCompressor,
|
|
392
|
+
enableOpReentryCheck,
|
|
393
|
+
enableGroupedBatching,
|
|
394
|
+
}, containerScope, logger, existing, blobManagerSnapshot, context.storage, idCompressor, provideEntryPoint, requestHandler, undefined);
|
|
395
|
+
// Apply stashed ops with a reference sequence number equal to the sequence number of the snapshot,
|
|
396
|
+
// or zero. This must be done before Container replays saved ops.
|
|
397
|
+
await runtime.pendingStateManager.applyStashedOpsAt(runtimeSequenceNumber ?? 0);
|
|
398
|
+
// Initialize the base state of the runtime before it's returned.
|
|
399
|
+
await runtime.initializeBaseState();
|
|
400
|
+
return runtime;
|
|
401
|
+
}
|
|
402
|
+
get clientId() {
|
|
403
|
+
return this._getClientId();
|
|
404
|
+
}
|
|
405
|
+
get storage() {
|
|
406
|
+
return this._storage;
|
|
407
|
+
}
|
|
408
|
+
get flushMode() {
|
|
409
|
+
return this._flushMode;
|
|
410
|
+
}
|
|
411
|
+
get scope() {
|
|
412
|
+
return this.containerScope;
|
|
413
|
+
}
|
|
414
|
+
get IFluidDataStoreRegistry() {
|
|
415
|
+
return this.registry;
|
|
416
|
+
}
|
|
417
|
+
get attachState() {
|
|
418
|
+
return this._getAttachState();
|
|
419
|
+
}
|
|
420
|
+
get IFluidHandleContext() {
|
|
421
|
+
return this.handleContext;
|
|
422
|
+
}
|
|
423
|
+
/**
|
|
424
|
+
* Invokes the given callback and expects that no ops are submitted
|
|
425
|
+
* until execution finishes. If an op is submitted, an error will be raised.
|
|
426
|
+
*
|
|
427
|
+
* Can be disabled by feature gate `Fluid.ContainerRuntime.DisableOpReentryCheck`
|
|
428
|
+
*
|
|
429
|
+
* @param callback - the callback to be invoked
|
|
430
|
+
*/
|
|
431
|
+
ensureNoDataModelChanges(callback) {
|
|
432
|
+
this.ensureNoDataModelChangesCalls++;
|
|
433
|
+
try {
|
|
434
|
+
return callback();
|
|
435
|
+
}
|
|
436
|
+
finally {
|
|
437
|
+
this.ensureNoDataModelChangesCalls--;
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
get connected() {
|
|
441
|
+
return this._connected;
|
|
442
|
+
}
|
|
443
|
+
/** clientId of parent (non-summarizing) container that owns summarizer container */
|
|
444
|
+
get summarizerClientId() {
|
|
445
|
+
return this.summarizerClientElection?.electedClientId;
|
|
446
|
+
}
|
|
447
|
+
get disposed() {
|
|
448
|
+
return this._disposed;
|
|
449
|
+
}
|
|
450
|
+
get summarizer() {
|
|
451
|
+
(0, core_utils_1.assert)(this._summarizer !== undefined, 0x257 /* "This is not summarizing container" */);
|
|
452
|
+
return this._summarizer;
|
|
453
|
+
}
|
|
454
|
+
isSummariesDisabled() {
|
|
455
|
+
return this.summaryConfiguration.state === "disabled";
|
|
456
|
+
}
|
|
457
|
+
isHeuristicsDisabled() {
|
|
458
|
+
return this.summaryConfiguration.state === "disableHeuristics";
|
|
459
|
+
}
|
|
460
|
+
getMaxOpsSinceLastSummary() {
|
|
461
|
+
return this.summaryConfiguration.state !== "disabled"
|
|
462
|
+
? this.summaryConfiguration.maxOpsSinceLastSummary
|
|
463
|
+
: 0;
|
|
464
|
+
}
|
|
465
|
+
getInitialSummarizerDelayMs() {
|
|
466
|
+
// back-compat: initialSummarizerDelayMs was moved from ISummaryRuntimeOptions
|
|
467
|
+
// to ISummaryConfiguration in 0.60.
|
|
468
|
+
if (this.runtimeOptions.summaryOptions.initialSummarizerDelayMs !== undefined) {
|
|
469
|
+
return this.runtimeOptions.summaryOptions.initialSummarizerDelayMs;
|
|
470
|
+
}
|
|
471
|
+
return this.summaryConfiguration.state !== "disabled"
|
|
472
|
+
? this.summaryConfiguration.initialSummarizerDelayMs
|
|
473
|
+
: 0;
|
|
474
|
+
}
|
|
475
|
+
/** If false, loading or using a Tombstoned object should merely log, not fail */
|
|
476
|
+
get gcTombstoneEnforcementAllowed() {
|
|
477
|
+
return this.garbageCollector.tombstoneEnforcementAllowed;
|
|
478
|
+
}
|
|
479
|
+
/** If true, throw an error when a tombstone data store is retrieved */
|
|
480
|
+
get gcThrowOnTombstoneLoad() {
|
|
481
|
+
return this.garbageCollector.throwOnTombstoneLoad;
|
|
482
|
+
}
|
|
483
|
+
/** If true, throw an error when a tombstone data store is used. */
|
|
484
|
+
get gcThrowOnTombstoneUsage() {
|
|
485
|
+
return this.garbageCollector.throwOnTombstoneUsage;
|
|
486
|
+
}
|
|
209
487
|
/**
|
|
210
488
|
* @internal
|
|
211
489
|
*/
|
|
212
|
-
constructor(context, registry, metadata, electedSummarizerData, chunks, dataStoreAliasMap, runtimeOptions, containerScope, logger, existing, blobManagerSnapshot, _storage, idCompressor, requestHandler, summaryConfiguration = {
|
|
490
|
+
constructor(context, registry, metadata, electedSummarizerData, chunks, dataStoreAliasMap, runtimeOptions, containerScope, logger, existing, blobManagerSnapshot, _storage, idCompressor, provideEntryPoint, requestHandler, summaryConfiguration = {
|
|
213
491
|
// the defaults
|
|
214
492
|
...exports.DefaultSummaryConfiguration,
|
|
215
493
|
// the runtime configuration overrides
|
|
216
494
|
...runtimeOptions.summaryOptions?.summaryConfigOverrides,
|
|
217
|
-
}
|
|
495
|
+
}) {
|
|
218
496
|
super();
|
|
219
497
|
this.registry = registry;
|
|
220
498
|
this.runtimeOptions = runtimeOptions;
|
|
@@ -310,7 +588,6 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
310
588
|
// Note that we only need to pull the *initial* connected state from the context.
|
|
311
589
|
// Later updates come through calls to setConnectionState.
|
|
312
590
|
this._connected = connected;
|
|
313
|
-
this.gcTombstoneEnforcementAllowed = (0, gc_1.shouldAllowGcTombstoneEnforcement)(metadata?.gcFeatureMatrix?.tombstoneGeneration /* persisted */, this.runtimeOptions.gcOptions[gc_1.gcTombstoneGenerationOptionName] /* current */);
|
|
314
591
|
this.mc.logger.sendTelemetryEvent({
|
|
315
592
|
eventName: "GCFeatureMatrix",
|
|
316
593
|
metadataValue: JSON.stringify(metadata?.gcFeatureMatrix),
|
|
@@ -401,10 +678,10 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
401
678
|
if (baseSnapshot) {
|
|
402
679
|
this.summarizerNode.updateBaseSummaryState(baseSnapshot);
|
|
403
680
|
}
|
|
404
|
-
this.dataStores = new dataStores_1.DataStores((0, dataStores_1.getSummaryForDatastores)(baseSnapshot, metadata), this, (attachMsg) => this.submit({ type: ContainerMessageType.Attach, contents: attachMsg }), (id, createParam) => (summarizeInternal, getGCDataFn) => this.summarizerNode.createChild(summarizeInternal, id, createParam, undefined, getGCDataFn), (id) => this.summarizerNode.deleteChild(id), this.mc.logger, (path, timestampMs, packagePath) => this.garbageCollector.nodeUpdated(path, "Changed", timestampMs, packagePath), (path) => this.garbageCollector.isNodeDeleted(path), new Map(dataStoreAliasMap));
|
|
681
|
+
this.dataStores = new dataStores_1.DataStores((0, dataStores_1.getSummaryForDatastores)(baseSnapshot, metadata), this, (attachMsg) => this.submit({ type: messageTypes_1.ContainerMessageType.Attach, contents: attachMsg }), (id, createParam) => (summarizeInternal, getGCDataFn) => this.summarizerNode.createChild(summarizeInternal, id, createParam, undefined, getGCDataFn), (id) => this.summarizerNode.deleteChild(id), this.mc.logger, (path, timestampMs, packagePath) => this.garbageCollector.nodeUpdated(path, "Changed", timestampMs, packagePath), (path) => this.garbageCollector.isNodeDeleted(path), new Map(dataStoreAliasMap));
|
|
405
682
|
this.blobManager = new blobManager_1.BlobManager(this.handleContext, blobManagerSnapshot, () => this.storage, (localId, blobId) => {
|
|
406
683
|
if (!this.disposed) {
|
|
407
|
-
this.submit({ type: ContainerMessageType.BlobAttach, contents: undefined }, undefined, {
|
|
684
|
+
this.submit({ type: messageTypes_1.ContainerMessageType.BlobAttach, contents: undefined }, undefined, {
|
|
408
685
|
localId,
|
|
409
686
|
blobId,
|
|
410
687
|
});
|
|
@@ -506,7 +783,7 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
506
783
|
this.summaryCollection.on("default", defaultAction);
|
|
507
784
|
// Create the SummaryManager and mark the initial state
|
|
508
785
|
this.summaryManager = new summary_1.SummaryManager(this.summarizerClientElection, this, // IConnectedState
|
|
509
|
-
this.summaryCollection, this.logger, this.
|
|
786
|
+
this.summaryCollection, this.logger, this.formCreateSummarizerFn(loader), new throttler_1.Throttler(60 * 1000, // 60 sec delay window
|
|
510
787
|
30 * 1000, // 30 sec max delay
|
|
511
788
|
// throttling function increases exponentially (0ms, 40ms, 80ms, 160ms, etc)
|
|
512
789
|
(0, throttler_1.formExponentialFn)({ coefficient: 20, initialDelay: 0 })), {
|
|
@@ -518,26 +795,6 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
518
795
|
this.summaryManager.start();
|
|
519
796
|
}
|
|
520
797
|
}
|
|
521
|
-
this.deltaManager.on("readonly", (readonly) => {
|
|
522
|
-
// we accumulate ops while being in read-only state.
|
|
523
|
-
// once user gets write permissions and we have active connection, flush all pending ops.
|
|
524
|
-
// Note that the inner (non-proxy) delta manager is needed here to get the readonly information.
|
|
525
|
-
(0, core_utils_1.assert)(readonly === this.innerDeltaManager.readOnlyInfo.readonly, 0x124 /* "inconsistent readonly property/event state" */);
|
|
526
|
-
// We need to be very careful with when we (re)send pending ops, to ensure that we only send ops
|
|
527
|
-
// when we either never send an op, or attempted to send it but we know for sure it was not
|
|
528
|
-
// sequenced by server and will never be sequenced (i.e. was lost)
|
|
529
|
-
// For loss of connection, we wait for our own "join" op and use it a a barrier to know all the
|
|
530
|
-
// ops that made it from previous connection, before switching clientId and raising "connected" event
|
|
531
|
-
// But with read-only permissions, if we transition between read-only and r/w states while on same
|
|
532
|
-
// connection, then we have no good signal to tell us when it's safe to send ops we accumulated while
|
|
533
|
-
// being in read-only state.
|
|
534
|
-
// For that reason, we support getting to read-only state only when disconnected. This ensures that we
|
|
535
|
-
// can rely on same safety mechanism and resend ops only when we establish new connection.
|
|
536
|
-
// This is applicable for read-only permissions (event is raised before connection is properly registered),
|
|
537
|
-
// but it's an extra requirement for Container.forceReadonly() API
|
|
538
|
-
(0, core_utils_1.assert)(!readonly || !this.connected, 0x125 /* "Unsafe to transition to read-only state!" */);
|
|
539
|
-
this.replayPendingStates();
|
|
540
|
-
});
|
|
541
798
|
// logging hardware telemetry
|
|
542
799
|
logger.sendTelemetryEvent({
|
|
543
800
|
eventName: "DeviceSpec",
|
|
@@ -571,234 +828,9 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
571
828
|
(0, core_utils_1.assert)(this._summarizer !== undefined, 0x5bf /* Summarizer object is undefined in a summarizer client */);
|
|
572
829
|
return this._summarizer;
|
|
573
830
|
}
|
|
574
|
-
return
|
|
831
|
+
return provideEntryPoint(this);
|
|
575
832
|
});
|
|
576
833
|
}
|
|
577
|
-
/**
|
|
578
|
-
* @deprecated - Will be removed in future major release. Migrate all usage of IFluidRouter to the "entryPoint" pattern. Refer to Removing-IFluidRouter.md
|
|
579
|
-
*/
|
|
580
|
-
get IFluidRouter() {
|
|
581
|
-
return this;
|
|
582
|
-
}
|
|
583
|
-
/**
|
|
584
|
-
* @deprecated - use loadRuntime instead.
|
|
585
|
-
* Load the stores from a snapshot and returns the runtime.
|
|
586
|
-
* @param context - Context of the container.
|
|
587
|
-
* @param registryEntries - Mapping to the stores.
|
|
588
|
-
* @param requestHandler - Request handlers for the container runtime
|
|
589
|
-
* @param runtimeOptions - Additional options to be passed to the runtime
|
|
590
|
-
* @param existing - (optional) When loading from an existing snapshot. Precedes context.existing if provided
|
|
591
|
-
* @param containerRuntimeCtor - (optional) Constructor to use to create the ContainerRuntime instance. This
|
|
592
|
-
* allows mixin classes to leverage this method to define their own async initializer.
|
|
593
|
-
*/
|
|
594
|
-
static async load(context, registryEntries, requestHandler, runtimeOptions = {}, containerScope = context.scope, existing, containerRuntimeCtor = ContainerRuntime) {
|
|
595
|
-
let existingFlag = true;
|
|
596
|
-
if (!existing) {
|
|
597
|
-
existingFlag = false;
|
|
598
|
-
}
|
|
599
|
-
return this.loadRuntime({
|
|
600
|
-
context,
|
|
601
|
-
registryEntries,
|
|
602
|
-
existing: existingFlag,
|
|
603
|
-
requestHandler,
|
|
604
|
-
runtimeOptions,
|
|
605
|
-
containerScope,
|
|
606
|
-
containerRuntimeCtor,
|
|
607
|
-
});
|
|
608
|
-
}
|
|
609
|
-
/**
|
|
610
|
-
* Load the stores from a snapshot and returns the runtime.
|
|
611
|
-
* @param params - An object housing the runtime properties:
|
|
612
|
-
* - context - Context of the container.
|
|
613
|
-
* - registryEntries - Mapping from data store types to their corresponding factories.
|
|
614
|
-
* - existing - Pass 'true' if loading from an existing snapshot.
|
|
615
|
-
* - requestHandler - (optional) Request handler for the request() method of the container runtime.
|
|
616
|
-
* Only relevant for back-compat while we remove the request() method and move fully to entryPoint as the main pattern.
|
|
617
|
-
* - runtimeOptions - Additional options to be passed to the runtime
|
|
618
|
-
* - containerScope - runtime services provided with context
|
|
619
|
-
* - containerRuntimeCtor - Constructor to use to create the ContainerRuntime instance.
|
|
620
|
-
* This allows mixin classes to leverage this method to define their own async initializer.
|
|
621
|
-
* - initializeEntryPoint - Promise that resolves to an object which will act as entryPoint for the Container.
|
|
622
|
-
* This object should provide all the functionality that the Container is expected to provide to the loader layer.
|
|
623
|
-
*/
|
|
624
|
-
static async loadRuntime(params) {
|
|
625
|
-
const { context, registryEntries, existing, requestHandler, runtimeOptions = {}, containerScope = {}, containerRuntimeCtor = ContainerRuntime, } = params;
|
|
626
|
-
const initializeEntryPoint = params.initializeEntryPoint ??
|
|
627
|
-
(async (containerRuntime) => ({
|
|
628
|
-
get IFluidRouter() {
|
|
629
|
-
return this;
|
|
630
|
-
},
|
|
631
|
-
async request(req) {
|
|
632
|
-
return containerRuntime.request(req);
|
|
633
|
-
},
|
|
634
|
-
}));
|
|
635
|
-
// If taggedLogger exists, use it. Otherwise, wrap the vanilla logger:
|
|
636
|
-
// back-compat: Remove the TaggedLoggerAdapter fallback once all the host are using loader > 0.45
|
|
637
|
-
const backCompatContext = context;
|
|
638
|
-
const passLogger = backCompatContext.taggedLogger ??
|
|
639
|
-
// eslint-disable-next-line import/no-deprecated
|
|
640
|
-
new telemetry_utils_1.TaggedLoggerAdapter(backCompatContext.logger);
|
|
641
|
-
const logger = (0, telemetry_utils_1.createChildLogger)({
|
|
642
|
-
logger: passLogger,
|
|
643
|
-
properties: {
|
|
644
|
-
all: {
|
|
645
|
-
runtimeVersion: packageVersion_1.pkgVersion,
|
|
646
|
-
},
|
|
647
|
-
},
|
|
648
|
-
});
|
|
649
|
-
const { summaryOptions = {}, gcOptions = {}, loadSequenceNumberVerification = "close", flushMode = defaultFlushMode, compressionOptions = defaultCompressionConfig, maxBatchSizeInBytes = defaultMaxBatchSizeInBytes, enableRuntimeIdCompressor = false, chunkSizeInBytes = defaultChunkSizeInBytes, enableOpReentryCheck = false, enableGroupedBatching = false, } = runtimeOptions;
|
|
650
|
-
const registry = new dataStoreRegistry_1.FluidDataStoreRegistry(registryEntries);
|
|
651
|
-
const tryFetchBlob = async (blobName) => {
|
|
652
|
-
const blobId = context.baseSnapshot?.blobs[blobName];
|
|
653
|
-
if (context.baseSnapshot && blobId) {
|
|
654
|
-
// IContainerContext storage api return type still has undefined in 0.39 package version.
|
|
655
|
-
// So once we release 0.40 container-defn package we can remove this check.
|
|
656
|
-
(0, core_utils_1.assert)(context.storage !== undefined, 0x1f5 /* "Attached state should have storage" */);
|
|
657
|
-
return (0, driver_utils_1.readAndParse)(context.storage, blobId);
|
|
658
|
-
}
|
|
659
|
-
};
|
|
660
|
-
const [chunks, metadata, electedSummarizerData, aliases, serializedIdCompressor] = await Promise.all([
|
|
661
|
-
tryFetchBlob(summary_1.chunksBlobName),
|
|
662
|
-
tryFetchBlob(summary_1.metadataBlobName),
|
|
663
|
-
tryFetchBlob(summary_1.electedSummarizerBlobName),
|
|
664
|
-
tryFetchBlob(summary_1.aliasBlobName),
|
|
665
|
-
tryFetchBlob(summary_1.idCompressorBlobName),
|
|
666
|
-
]);
|
|
667
|
-
// read snapshot blobs needed for BlobManager to load
|
|
668
|
-
const blobManagerSnapshot = await blobManager_1.BlobManager.load(context.baseSnapshot?.trees[summary_1.blobsTreeName], async (id) => {
|
|
669
|
-
// IContainerContext storage api return type still has undefined in 0.39 package version.
|
|
670
|
-
// So once we release 0.40 container-defn package we can remove this check.
|
|
671
|
-
(0, core_utils_1.assert)(context.storage !== undefined, 0x256 /* "storage undefined in attached container" */);
|
|
672
|
-
return (0, driver_utils_1.readAndParse)(context.storage, id);
|
|
673
|
-
});
|
|
674
|
-
// Verify summary runtime sequence number matches protocol sequence number.
|
|
675
|
-
const runtimeSequenceNumber = metadata?.message?.sequenceNumber;
|
|
676
|
-
// When we load with pending state, we reuse an old snapshot so we don't expect these numbers to match
|
|
677
|
-
if (!context.pendingLocalState && runtimeSequenceNumber !== undefined) {
|
|
678
|
-
const protocolSequenceNumber = context.deltaManager.initialSequenceNumber;
|
|
679
|
-
// Unless bypass is explicitly set, then take action when sequence numbers mismatch.
|
|
680
|
-
if (loadSequenceNumberVerification !== "bypass" &&
|
|
681
|
-
runtimeSequenceNumber !== protocolSequenceNumber) {
|
|
682
|
-
// "Load from summary, runtime metadata sequenceNumber !== initialSequenceNumber"
|
|
683
|
-
const error = new telemetry_utils_1.DataCorruptionError(
|
|
684
|
-
// pre-0.58 error message: SummaryMetadataMismatch
|
|
685
|
-
"Summary metadata mismatch", { runtimeVersion: packageVersion_1.pkgVersion, runtimeSequenceNumber, protocolSequenceNumber });
|
|
686
|
-
if (loadSequenceNumberVerification === "log") {
|
|
687
|
-
logger.sendErrorEvent({ eventName: "SequenceNumberMismatch" }, error);
|
|
688
|
-
}
|
|
689
|
-
else {
|
|
690
|
-
context.closeFn(error);
|
|
691
|
-
}
|
|
692
|
-
}
|
|
693
|
-
}
|
|
694
|
-
const idCompressorEnabled = metadata?.idCompressorEnabled ?? runtimeOptions.enableRuntimeIdCompressor ?? false;
|
|
695
|
-
let idCompressor;
|
|
696
|
-
if (idCompressorEnabled) {
|
|
697
|
-
const { IdCompressor, createSessionId } = await Promise.resolve().then(() => __importStar(require("./id-compressor")));
|
|
698
|
-
idCompressor =
|
|
699
|
-
serializedIdCompressor !== undefined
|
|
700
|
-
? IdCompressor.deserialize(serializedIdCompressor, createSessionId())
|
|
701
|
-
: IdCompressor.create(logger);
|
|
702
|
-
}
|
|
703
|
-
const runtime = new containerRuntimeCtor(context, registry, metadata, electedSummarizerData, chunks ?? [], aliases ?? [], {
|
|
704
|
-
summaryOptions,
|
|
705
|
-
gcOptions,
|
|
706
|
-
loadSequenceNumberVerification,
|
|
707
|
-
flushMode,
|
|
708
|
-
compressionOptions,
|
|
709
|
-
maxBatchSizeInBytes,
|
|
710
|
-
chunkSizeInBytes,
|
|
711
|
-
enableRuntimeIdCompressor,
|
|
712
|
-
enableOpReentryCheck,
|
|
713
|
-
enableGroupedBatching,
|
|
714
|
-
}, containerScope, logger, existing, blobManagerSnapshot, context.storage, idCompressor, requestHandler, undefined, // summaryConfiguration
|
|
715
|
-
initializeEntryPoint);
|
|
716
|
-
await runtime.blobManager.processStashedChanges();
|
|
717
|
-
// It's possible to have ops with a reference sequence number of 0. Op sequence numbers start
|
|
718
|
-
// at 1, so we won't see a replayed saved op with a sequence number of 0.
|
|
719
|
-
await runtime.pendingStateManager.applyStashedOpsAt(0);
|
|
720
|
-
// Initialize the base state of the runtime before it's returned.
|
|
721
|
-
await runtime.initializeBaseState();
|
|
722
|
-
return runtime;
|
|
723
|
-
}
|
|
724
|
-
get clientId() {
|
|
725
|
-
return this._getClientId();
|
|
726
|
-
}
|
|
727
|
-
get storage() {
|
|
728
|
-
return this._storage;
|
|
729
|
-
}
|
|
730
|
-
/** @deprecated - The functionality is no longer exposed publicly */
|
|
731
|
-
get reSubmitFn() {
|
|
732
|
-
return (type, contents, localOpMetadata, opMetadata) => this.reSubmitCore({ type, contents }, localOpMetadata, opMetadata);
|
|
733
|
-
// Note: compatDetails is not included in this deprecated API
|
|
734
|
-
}
|
|
735
|
-
get flushMode() {
|
|
736
|
-
return this._flushMode;
|
|
737
|
-
}
|
|
738
|
-
get scope() {
|
|
739
|
-
return this.containerScope;
|
|
740
|
-
}
|
|
741
|
-
get IFluidDataStoreRegistry() {
|
|
742
|
-
return this.registry;
|
|
743
|
-
}
|
|
744
|
-
get attachState() {
|
|
745
|
-
return this._getAttachState();
|
|
746
|
-
}
|
|
747
|
-
get IFluidHandleContext() {
|
|
748
|
-
return this.handleContext;
|
|
749
|
-
}
|
|
750
|
-
/**
|
|
751
|
-
* Invokes the given callback and expects that no ops are submitted
|
|
752
|
-
* until execution finishes. If an op is submitted, an error will be raised.
|
|
753
|
-
*
|
|
754
|
-
* Can be disabled by feature gate `Fluid.ContainerRuntime.DisableOpReentryCheck`
|
|
755
|
-
*
|
|
756
|
-
* @param callback - the callback to be invoked
|
|
757
|
-
*/
|
|
758
|
-
ensureNoDataModelChanges(callback) {
|
|
759
|
-
this.ensureNoDataModelChangesCalls++;
|
|
760
|
-
try {
|
|
761
|
-
return callback();
|
|
762
|
-
}
|
|
763
|
-
finally {
|
|
764
|
-
this.ensureNoDataModelChangesCalls--;
|
|
765
|
-
}
|
|
766
|
-
}
|
|
767
|
-
get connected() {
|
|
768
|
-
return this._connected;
|
|
769
|
-
}
|
|
770
|
-
/** clientId of parent (non-summarizing) container that owns summarizer container */
|
|
771
|
-
get summarizerClientId() {
|
|
772
|
-
return this.summarizerClientElection?.electedClientId;
|
|
773
|
-
}
|
|
774
|
-
get disposed() {
|
|
775
|
-
return this._disposed;
|
|
776
|
-
}
|
|
777
|
-
get summarizer() {
|
|
778
|
-
(0, core_utils_1.assert)(this._summarizer !== undefined, 0x257 /* "This is not summarizing container" */);
|
|
779
|
-
return this._summarizer;
|
|
780
|
-
}
|
|
781
|
-
isSummariesDisabled() {
|
|
782
|
-
return this.summaryConfiguration.state === "disabled";
|
|
783
|
-
}
|
|
784
|
-
isHeuristicsDisabled() {
|
|
785
|
-
return this.summaryConfiguration.state === "disableHeuristics";
|
|
786
|
-
}
|
|
787
|
-
getMaxOpsSinceLastSummary() {
|
|
788
|
-
return this.summaryConfiguration.state !== "disabled"
|
|
789
|
-
? this.summaryConfiguration.maxOpsSinceLastSummary
|
|
790
|
-
: 0;
|
|
791
|
-
}
|
|
792
|
-
getInitialSummarizerDelayMs() {
|
|
793
|
-
// back-compat: initialSummarizerDelayMs was moved from ISummaryRuntimeOptions
|
|
794
|
-
// to ISummaryConfiguration in 0.60.
|
|
795
|
-
if (this.runtimeOptions.summaryOptions.initialSummarizerDelayMs !== undefined) {
|
|
796
|
-
return this.runtimeOptions.summaryOptions.initialSummarizerDelayMs;
|
|
797
|
-
}
|
|
798
|
-
return this.summaryConfiguration.state !== "disabled"
|
|
799
|
-
? this.summaryConfiguration.initialSummarizerDelayMs
|
|
800
|
-
: 0;
|
|
801
|
-
}
|
|
802
834
|
/**
|
|
803
835
|
* Initializes the state from the base snapshot this container runtime loaded from.
|
|
804
836
|
*/
|
|
@@ -829,13 +861,13 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
829
861
|
/**
|
|
830
862
|
* Notifies this object about the request made to the container.
|
|
831
863
|
* @param request - Request made to the handler.
|
|
832
|
-
* @deprecated
|
|
864
|
+
* @deprecated Will be removed in future major release. Migrate all usage of IFluidRouter to the "entryPoint" pattern. Refer to Removing-IFluidRouter.md
|
|
833
865
|
*/
|
|
834
866
|
async request(request) {
|
|
835
867
|
try {
|
|
836
868
|
const parser = runtime_utils_1.RequestParser.create(request);
|
|
837
869
|
const id = parser.pathParts[0];
|
|
838
|
-
if (id ===
|
|
870
|
+
if (id === summarizerRequestUrl && parser.pathParts.length === 1) {
|
|
839
871
|
if (this._summarizer !== undefined) {
|
|
840
872
|
return {
|
|
841
873
|
status: 200,
|
|
@@ -846,6 +878,7 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
846
878
|
return (0, runtime_utils_1.create404Response)(request);
|
|
847
879
|
}
|
|
848
880
|
if (this.requestHandler !== undefined) {
|
|
881
|
+
// eslint-disable-next-line @typescript-eslint/return-await -- Adding an await here causes test failures
|
|
849
882
|
return this.requestHandler(parser, this);
|
|
850
883
|
}
|
|
851
884
|
return (0, runtime_utils_1.create404Response)(request);
|
|
@@ -863,6 +896,7 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
863
896
|
const requestParser = runtime_utils_1.RequestParser.create(request);
|
|
864
897
|
const id = requestParser.pathParts[0];
|
|
865
898
|
if (id === "_channels") {
|
|
899
|
+
// eslint-disable-next-line @typescript-eslint/return-await -- Adding an await here causes test failures
|
|
866
900
|
return this.resolveHandle(requestParser.createSubRequest(1));
|
|
867
901
|
}
|
|
868
902
|
if (id === blobManager_1.BlobManager.basePath && requestParser.isLeaf(2)) {
|
|
@@ -876,11 +910,14 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
876
910
|
: (0, runtime_utils_1.create404Response)(request);
|
|
877
911
|
}
|
|
878
912
|
else if (requestParser.pathParts.length > 0) {
|
|
879
|
-
|
|
913
|
+
// Differentiate between requesting the dataStore directly, or one of its children
|
|
914
|
+
const requestForChild = !requestParser.isLeaf(1);
|
|
915
|
+
const dataStore = await this.getDataStoreFromRequest(id, request, requestForChild);
|
|
880
916
|
const subRequest = requestParser.createSubRequest(1);
|
|
881
917
|
// We always expect createSubRequest to include a leading slash, but asserting here to protect against
|
|
882
918
|
// unintentionally modifying the url if that changes.
|
|
883
919
|
(0, core_utils_1.assert)(subRequest.url.startsWith("/"), 0x126 /* "Expected createSubRequest url to include a leading slash" */);
|
|
920
|
+
// eslint-disable-next-line @typescript-eslint/return-await -- Adding an await here causes test failures
|
|
884
921
|
return dataStore.request(subRequest);
|
|
885
922
|
}
|
|
886
923
|
return (0, runtime_utils_1.create404Response)(request);
|
|
@@ -898,7 +935,7 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
898
935
|
internalId(maybeAlias) {
|
|
899
936
|
return this.dataStores.aliases.get(maybeAlias) ?? maybeAlias;
|
|
900
937
|
}
|
|
901
|
-
async getDataStoreFromRequest(id, request) {
|
|
938
|
+
async getDataStoreFromRequest(id, request, requestForChild) {
|
|
902
939
|
const headerData = {};
|
|
903
940
|
if (typeof request.headers?.[RuntimeHeaders.wait] === "boolean") {
|
|
904
941
|
headerData.wait = request.headers[RuntimeHeaders.wait];
|
|
@@ -909,6 +946,10 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
909
946
|
if (typeof request.headers?.[exports.AllowTombstoneRequestHeaderKey] === "boolean") {
|
|
910
947
|
headerData.allowTombstone = request.headers[exports.AllowTombstoneRequestHeaderKey];
|
|
911
948
|
}
|
|
949
|
+
// We allow Tombstone requests for sub-DataStore objects
|
|
950
|
+
if (requestForChild) {
|
|
951
|
+
headerData.allowTombstone = true;
|
|
952
|
+
}
|
|
912
953
|
await this.dataStores.waitIfPendingAlias(id);
|
|
913
954
|
const internalId = this.internalId(id);
|
|
914
955
|
const dataStoreContext = await this.dataStores.getDataStore(internalId, headerData);
|
|
@@ -999,7 +1040,7 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
999
1040
|
// in their own batches before the originating batch is sent.
|
|
1000
1041
|
// Therefore, receiving them while attempting to send the originating batch
|
|
1001
1042
|
// does not mean that the container is making any progress.
|
|
1002
|
-
if (message?.type !== ContainerMessageType.ChunkedOp) {
|
|
1043
|
+
if (message?.type !== messageTypes_1.ContainerMessageType.ChunkedOp) {
|
|
1003
1044
|
this.consecutiveReconnects = 0;
|
|
1004
1045
|
}
|
|
1005
1046
|
}
|
|
@@ -1047,39 +1088,40 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
1047
1088
|
* Parse an op's type and actual content from given serialized content
|
|
1048
1089
|
* ! Note: this format needs to be in-line with what is set in the "ContainerRuntime.submit(...)" method
|
|
1049
1090
|
*/
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1091
|
+
// TODO: markfields: confirm Local- versus Outbound- ContainerRuntimeMessage typing
|
|
1092
|
+
parseLocalOpContent(serializedContents) {
|
|
1093
|
+
(0, core_utils_1.assert)(serializedContents !== undefined, 0x6d5 /* content must be defined */);
|
|
1094
|
+
const message = JSON.parse(serializedContents);
|
|
1095
|
+
(0, core_utils_1.assert)(message.type !== undefined, 0x6d6 /* incorrect op content format */);
|
|
1096
|
+
return message;
|
|
1097
|
+
}
|
|
1098
|
+
async applyStashedOp(serializedOpContent) {
|
|
1057
1099
|
// Need to parse from string for back-compat
|
|
1058
|
-
const
|
|
1059
|
-
switch (type) {
|
|
1060
|
-
case ContainerMessageType.FluidDataStoreOp:
|
|
1061
|
-
return this.dataStores.applyStashedOp(contents);
|
|
1062
|
-
case ContainerMessageType.Attach:
|
|
1063
|
-
return this.dataStores.applyStashedAttachOp(contents);
|
|
1064
|
-
case ContainerMessageType.IdAllocation:
|
|
1100
|
+
const opContents = this.parseLocalOpContent(serializedOpContent);
|
|
1101
|
+
switch (opContents.type) {
|
|
1102
|
+
case messageTypes_1.ContainerMessageType.FluidDataStoreOp:
|
|
1103
|
+
return this.dataStores.applyStashedOp(opContents.contents);
|
|
1104
|
+
case messageTypes_1.ContainerMessageType.Attach:
|
|
1105
|
+
return this.dataStores.applyStashedAttachOp(opContents.contents);
|
|
1106
|
+
case messageTypes_1.ContainerMessageType.IdAllocation:
|
|
1065
1107
|
(0, core_utils_1.assert)(this.idCompressor !== undefined, 0x67b /* IdCompressor should be defined if enabled */);
|
|
1066
|
-
return this.applyStashedIdAllocationOp(contents);
|
|
1067
|
-
case ContainerMessageType.Alias:
|
|
1068
|
-
case ContainerMessageType.BlobAttach:
|
|
1108
|
+
return this.applyStashedIdAllocationOp(opContents.contents);
|
|
1109
|
+
case messageTypes_1.ContainerMessageType.Alias:
|
|
1110
|
+
case messageTypes_1.ContainerMessageType.BlobAttach:
|
|
1069
1111
|
return;
|
|
1070
|
-
case ContainerMessageType.ChunkedOp:
|
|
1112
|
+
case messageTypes_1.ContainerMessageType.ChunkedOp:
|
|
1071
1113
|
throw new Error("chunkedOp not expected here");
|
|
1072
|
-
case ContainerMessageType.Rejoin:
|
|
1114
|
+
case messageTypes_1.ContainerMessageType.Rejoin:
|
|
1073
1115
|
throw new Error("rejoin not expected here");
|
|
1074
1116
|
default: {
|
|
1075
1117
|
// This should be extremely rare for stashed ops.
|
|
1076
1118
|
// It would require a newer runtime stashing ops and then an older one applying them,
|
|
1077
1119
|
// e.g. if an app rolled back its container version
|
|
1078
|
-
const compatBehavior = compatDetails?.behavior;
|
|
1079
|
-
if (!compatBehaviorAllowsMessageType(type, compatBehavior)) {
|
|
1120
|
+
const compatBehavior = opContents.compatDetails?.behavior;
|
|
1121
|
+
if (!compatBehaviorAllowsMessageType(opContents.type, compatBehavior)) {
|
|
1080
1122
|
const error = telemetry_utils_1.DataProcessingError.create("Stashed runtime message of unknown type", "applyStashedOp", undefined /* sequencedMessage */, {
|
|
1081
1123
|
messageDetails: JSON.stringify({
|
|
1082
|
-
type,
|
|
1124
|
+
type: opContents.type,
|
|
1083
1125
|
compatBehavior,
|
|
1084
1126
|
}),
|
|
1085
1127
|
});
|
|
@@ -1098,6 +1140,23 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
1098
1140
|
// Don't propagate "disconnected" event because we didn't propagate the previous "connected" event
|
|
1099
1141
|
return;
|
|
1100
1142
|
}
|
|
1143
|
+
// If there are stashed blobs in the pending state, we need to delay
|
|
1144
|
+
// propagation of the "connected" event until we have uploaded them to
|
|
1145
|
+
// ensure we don't submit ops referencing a blob that has not been uploaded
|
|
1146
|
+
const connecting = connected && !this._connected;
|
|
1147
|
+
if (connecting && this.blobManager.hasPendingStashedBlobs()) {
|
|
1148
|
+
(0, core_utils_1.assert)(!this.delayConnectClientId, 0x791 /* Connect event delay must be canceled before subsequent connect event */);
|
|
1149
|
+
(0, core_utils_1.assert)(!!clientId, 0x792 /* Must have clientId when connecting */);
|
|
1150
|
+
this.delayConnectClientId = clientId;
|
|
1151
|
+
this.blobManager.processStashedChanges().then(() => {
|
|
1152
|
+
// make sure we didn't reconnect before the promise resolved
|
|
1153
|
+
if (this.delayConnectClientId === clientId && !this.disposed) {
|
|
1154
|
+
this.delayConnectClientId = undefined;
|
|
1155
|
+
this.setConnectionStateCore(connected, clientId);
|
|
1156
|
+
}
|
|
1157
|
+
}, (error) => this.closeFn(error));
|
|
1158
|
+
return;
|
|
1159
|
+
}
|
|
1101
1160
|
this.setConnectionStateCore(connected, clientId);
|
|
1102
1161
|
}
|
|
1103
1162
|
setConnectionStateCore(connected, clientId) {
|
|
@@ -1150,18 +1209,33 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
1150
1209
|
// or something different, like a system message.
|
|
1151
1210
|
const modernRuntimeMessage = messageArg.type === protocol_definitions_1.MessageType.Operation;
|
|
1152
1211
|
// Do shallow copy of message, as the processing flow will modify it.
|
|
1212
|
+
// There might be multiple container instances receiving the same message.
|
|
1213
|
+
// We do not need to make a deep copy. Each layer will just replace message.contents itself,
|
|
1214
|
+
// but will not modify the contents object (likely it will replace it on the message).
|
|
1153
1215
|
const messageCopy = { ...messageArg };
|
|
1154
1216
|
for (const message of this.remoteMessageProcessor.process(messageCopy)) {
|
|
1155
|
-
|
|
1217
|
+
if (modernRuntimeMessage) {
|
|
1218
|
+
this.processCore({
|
|
1219
|
+
// Cast it since we expect it to be this based on modernRuntimeMessage computation above.
|
|
1220
|
+
// There is nothing really ensuring that anytime original message.type is Operation that
|
|
1221
|
+
// the result messages will be so. In the end modern bool being true only directs to
|
|
1222
|
+
// throw error if ultimately unrecognized without compat details saying otherwise.
|
|
1223
|
+
message: message,
|
|
1224
|
+
local,
|
|
1225
|
+
modernRuntimeMessage,
|
|
1226
|
+
});
|
|
1227
|
+
}
|
|
1228
|
+
else {
|
|
1229
|
+
// Unrecognized message will be ignored.
|
|
1230
|
+
this.processCore({ message, local, modernRuntimeMessage });
|
|
1231
|
+
}
|
|
1156
1232
|
}
|
|
1157
1233
|
}
|
|
1158
1234
|
/**
|
|
1159
1235
|
* Direct the message to the correct subsystem for processing, and implement other side effects
|
|
1160
|
-
* @param message - The unpacked message. Likely a ContainerRuntimeMessage, but could also be a system op
|
|
1161
|
-
* @param local - Did this client send the op?
|
|
1162
|
-
* @param modernRuntimeMessage - Does this appear like a current ContainerRuntimeMessage?
|
|
1163
1236
|
*/
|
|
1164
|
-
processCore(
|
|
1237
|
+
processCore(messageWithContext) {
|
|
1238
|
+
const { message, local } = messageWithContext;
|
|
1165
1239
|
// Surround the actual processing of the operation with messages to the schedule manager indicating
|
|
1166
1240
|
// the beginning and end. This allows it to emit appropriate events and/or pause the processing of new
|
|
1167
1241
|
// messages once a batch has been fully processed.
|
|
@@ -1169,16 +1243,18 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
1169
1243
|
this._processedClientSequenceNumber = message.clientSequenceNumber;
|
|
1170
1244
|
try {
|
|
1171
1245
|
let localOpMetadata;
|
|
1172
|
-
if (local &&
|
|
1173
|
-
|
|
1246
|
+
if (local &&
|
|
1247
|
+
messageWithContext.modernRuntimeMessage &&
|
|
1248
|
+
message.type !== messageTypes_1.ContainerMessageType.ChunkedOp) {
|
|
1249
|
+
localOpMetadata = this.pendingStateManager.processPendingLocalMessage(messageWithContext.message);
|
|
1174
1250
|
}
|
|
1175
1251
|
// If there are no more pending messages after processing a local message,
|
|
1176
1252
|
// the document is no longer dirty.
|
|
1177
1253
|
if (!this.hasPendingMessages()) {
|
|
1178
1254
|
this.updateDocumentDirtyState(false);
|
|
1179
1255
|
}
|
|
1180
|
-
this.validateAndProcessRuntimeMessage(
|
|
1181
|
-
this.emit("op", message, modernRuntimeMessage);
|
|
1256
|
+
this.validateAndProcessRuntimeMessage(messageWithContext, localOpMetadata);
|
|
1257
|
+
this.emit("op", message, messageWithContext.modernRuntimeMessage);
|
|
1182
1258
|
this.scheduleManager.afterOpProcessing(undefined, message);
|
|
1183
1259
|
if (local) {
|
|
1184
1260
|
// If we have processed a local op, this means that the container is
|
|
@@ -1193,41 +1269,42 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
1193
1269
|
}
|
|
1194
1270
|
}
|
|
1195
1271
|
/**
|
|
1196
|
-
* Assuming the given message is also a
|
|
1272
|
+
* Assuming the given message is also a TypedContainerRuntimeMessage,
|
|
1197
1273
|
* checks its type and dispatches the message to the appropriate handler in the runtime.
|
|
1198
|
-
* Throws a DataProcessingError if the message doesn't conform to
|
|
1274
|
+
* Throws a DataProcessingError if the message looks like but doesn't conform to a known TypedContainerRuntimeMessage type.
|
|
1199
1275
|
*/
|
|
1200
|
-
validateAndProcessRuntimeMessage(
|
|
1201
|
-
//
|
|
1202
|
-
const {
|
|
1203
|
-
switch (
|
|
1204
|
-
case ContainerMessageType.Attach:
|
|
1205
|
-
this.dataStores.processAttachMessage(message, local);
|
|
1276
|
+
validateAndProcessRuntimeMessage(messageWithContext, localOpMetadata) {
|
|
1277
|
+
// TODO: destructure message and modernRuntimeMessage once using typescript 5.2.2+
|
|
1278
|
+
const { local } = messageWithContext;
|
|
1279
|
+
switch (messageWithContext.message.type) {
|
|
1280
|
+
case messageTypes_1.ContainerMessageType.Attach:
|
|
1281
|
+
this.dataStores.processAttachMessage(messageWithContext.message, local);
|
|
1206
1282
|
break;
|
|
1207
|
-
case ContainerMessageType.Alias:
|
|
1208
|
-
this.dataStores.processAliasMessage(message, localOpMetadata, local);
|
|
1283
|
+
case messageTypes_1.ContainerMessageType.Alias:
|
|
1284
|
+
this.dataStores.processAliasMessage(messageWithContext.message, localOpMetadata, local);
|
|
1209
1285
|
break;
|
|
1210
|
-
case ContainerMessageType.FluidDataStoreOp:
|
|
1211
|
-
this.dataStores.processFluidDataStoreOp(message, local, localOpMetadata);
|
|
1286
|
+
case messageTypes_1.ContainerMessageType.FluidDataStoreOp:
|
|
1287
|
+
this.dataStores.processFluidDataStoreOp(messageWithContext.message, local, localOpMetadata);
|
|
1212
1288
|
break;
|
|
1213
|
-
case ContainerMessageType.BlobAttach:
|
|
1214
|
-
this.blobManager.processBlobAttachOp(message, local);
|
|
1289
|
+
case messageTypes_1.ContainerMessageType.BlobAttach:
|
|
1290
|
+
this.blobManager.processBlobAttachOp(messageWithContext.message, local);
|
|
1215
1291
|
break;
|
|
1216
|
-
case ContainerMessageType.IdAllocation:
|
|
1292
|
+
case messageTypes_1.ContainerMessageType.IdAllocation:
|
|
1217
1293
|
(0, core_utils_1.assert)(this.idCompressor !== undefined, 0x67c /* IdCompressor should be defined if enabled */);
|
|
1218
|
-
this.idCompressor.finalizeCreationRange(message.contents);
|
|
1294
|
+
this.idCompressor.finalizeCreationRange(messageWithContext.message.contents);
|
|
1219
1295
|
break;
|
|
1220
|
-
case ContainerMessageType.ChunkedOp:
|
|
1221
|
-
case ContainerMessageType.Rejoin:
|
|
1296
|
+
case messageTypes_1.ContainerMessageType.ChunkedOp:
|
|
1297
|
+
case messageTypes_1.ContainerMessageType.Rejoin:
|
|
1222
1298
|
break;
|
|
1223
1299
|
default: {
|
|
1224
1300
|
// If we didn't necessarily expect a runtime message type, then no worries - just return
|
|
1225
1301
|
// e.g. this case applies to system ops, or legacy ops that would have fallen into the above cases anyway.
|
|
1226
|
-
if (!
|
|
1302
|
+
if (!messageWithContext.modernRuntimeMessage) {
|
|
1227
1303
|
return;
|
|
1228
1304
|
}
|
|
1229
|
-
const compatBehavior = compatDetails?.behavior;
|
|
1230
|
-
if (!compatBehaviorAllowsMessageType(
|
|
1305
|
+
const compatBehavior = messageWithContext.message.compatDetails?.behavior;
|
|
1306
|
+
if (!compatBehaviorAllowsMessageType(messageWithContext.message.type, compatBehavior)) {
|
|
1307
|
+
const { message } = messageWithContext;
|
|
1231
1308
|
const error = telemetry_utils_1.DataProcessingError.create(
|
|
1232
1309
|
// Former assert 0x3ce
|
|
1233
1310
|
"Runtime message of unknown type", "OpProcessing", message, {
|
|
@@ -1303,8 +1380,9 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
1303
1380
|
* Returns the runtime of the data store.
|
|
1304
1381
|
* @param id - Id supplied during creating the data store.
|
|
1305
1382
|
* @param wait - True if you want to wait for it.
|
|
1306
|
-
* @deprecated
|
|
1383
|
+
* @deprecated Use getAliasedDataStoreEntryPoint instead to get an aliased data store's entry point.
|
|
1307
1384
|
*/
|
|
1385
|
+
// eslint-disable-next-line import/no-deprecated
|
|
1308
1386
|
async getRootDataStore(id, wait = true) {
|
|
1309
1387
|
return this.getRootDataStoreChannel(id, wait);
|
|
1310
1388
|
}
|
|
@@ -1437,13 +1515,13 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
1437
1515
|
isContainerMessageDirtyable({ type, contents }) {
|
|
1438
1516
|
// For legacy purposes, exclude the old built-in AgentScheduler from dirty consideration as a special-case.
|
|
1439
1517
|
// Ultimately we should have no special-cases from the ContainerRuntime's perspective.
|
|
1440
|
-
if (type === ContainerMessageType.Attach) {
|
|
1518
|
+
if (type === messageTypes_1.ContainerMessageType.Attach) {
|
|
1441
1519
|
const attachMessage = contents;
|
|
1442
1520
|
if (attachMessage.id === exports.agentSchedulerId) {
|
|
1443
1521
|
return false;
|
|
1444
1522
|
}
|
|
1445
1523
|
}
|
|
1446
|
-
else if (type === ContainerMessageType.FluidDataStoreOp) {
|
|
1524
|
+
else if (type === messageTypes_1.ContainerMessageType.FluidDataStoreOp) {
|
|
1447
1525
|
const envelope = contents;
|
|
1448
1526
|
if (envelope.address === exports.agentSchedulerId) {
|
|
1449
1527
|
return false;
|
|
@@ -1601,7 +1679,7 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
1601
1679
|
this.dataStores.updateUnusedRoutes(dataStoreRoutes);
|
|
1602
1680
|
}
|
|
1603
1681
|
/**
|
|
1604
|
-
* @deprecated
|
|
1682
|
+
* @deprecated Replaced by deleteSweepReadyNodes.
|
|
1605
1683
|
*/
|
|
1606
1684
|
deleteUnusedNodes(unusedRoutes) {
|
|
1607
1685
|
throw new Error("deleteUnusedRoutes should not be called");
|
|
@@ -1723,16 +1801,13 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
1723
1801
|
},
|
|
1724
1802
|
});
|
|
1725
1803
|
(0, core_utils_1.assert)(this.outbox.isEmpty, 0x3d1 /* Can't trigger summary in the middle of a batch */);
|
|
1804
|
+
// We close the summarizer and download a new snapshot and reload the container
|
|
1726
1805
|
let latestSnapshotVersionId;
|
|
1727
|
-
if (refreshLatestAck) {
|
|
1728
|
-
|
|
1806
|
+
if (refreshLatestAck === true) {
|
|
1807
|
+
return this.prefetchLatestSummaryThenClose((0, telemetry_utils_1.createChildLogger)({
|
|
1729
1808
|
logger: summaryNumberLogger,
|
|
1730
1809
|
properties: { all: { safeSummary: true } },
|
|
1731
1810
|
}));
|
|
1732
|
-
const latestSnapshotRefSeq = latestSnapshotInfo.latestSnapshotRefSeq;
|
|
1733
|
-
latestSnapshotVersionId = latestSnapshotInfo.latestSnapshotVersionId;
|
|
1734
|
-
// We might need to catch up to the latest summary's reference sequence number before pausing.
|
|
1735
|
-
await this.waitForDeltaManagerToCatchup(latestSnapshotRefSeq, summaryNumberLogger);
|
|
1736
1811
|
}
|
|
1737
1812
|
// If there are pending (unacked ops), the summary will not be eventual consistent and it may even be
|
|
1738
1813
|
// incorrect. So, wait for the container to be saved with a timeout. If the container is not saved
|
|
@@ -2052,21 +2127,21 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
2052
2127
|
address: id,
|
|
2053
2128
|
contents,
|
|
2054
2129
|
};
|
|
2055
|
-
this.submit({ type: ContainerMessageType.FluidDataStoreOp, contents: envelope }, localOpMetadata);
|
|
2130
|
+
this.submit({ type: messageTypes_1.ContainerMessageType.FluidDataStoreOp, contents: envelope }, localOpMetadata);
|
|
2056
2131
|
}
|
|
2057
2132
|
submitDataStoreAliasOp(contents, localOpMetadata) {
|
|
2058
2133
|
const aliasMessage = contents;
|
|
2059
2134
|
if (!(0, dataStore_1.isDataStoreAliasMessage)(aliasMessage)) {
|
|
2060
2135
|
throw new telemetry_utils_1.UsageError("malformedDataStoreAliasMessage");
|
|
2061
2136
|
}
|
|
2062
|
-
this.submit({ type: ContainerMessageType.Alias, contents }, localOpMetadata);
|
|
2137
|
+
this.submit({ type: messageTypes_1.ContainerMessageType.Alias, contents }, localOpMetadata);
|
|
2063
2138
|
}
|
|
2064
2139
|
async uploadBlob(blob, signal) {
|
|
2065
2140
|
this.verifyNotClosed();
|
|
2066
2141
|
return this.blobManager.createBlob(blob, signal);
|
|
2067
2142
|
}
|
|
2068
2143
|
maybeSubmitIdAllocationOp(type) {
|
|
2069
|
-
if (type !== ContainerMessageType.IdAllocation) {
|
|
2144
|
+
if (type !== messageTypes_1.ContainerMessageType.IdAllocation) {
|
|
2070
2145
|
let idAllocationBatchMessage;
|
|
2071
2146
|
let idRange;
|
|
2072
2147
|
if (this.idCompressorEnabled) {
|
|
@@ -2077,7 +2152,7 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
2077
2152
|
}
|
|
2078
2153
|
if (idRange !== undefined) {
|
|
2079
2154
|
const idAllocationMessage = {
|
|
2080
|
-
type: ContainerMessageType.IdAllocation,
|
|
2155
|
+
type: messageTypes_1.ContainerMessageType.IdAllocation,
|
|
2081
2156
|
contents: idRange,
|
|
2082
2157
|
};
|
|
2083
2158
|
idAllocationBatchMessage = {
|
|
@@ -2085,7 +2160,7 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
2085
2160
|
referenceSequenceNumber: this.deltaManager.lastSequenceNumber,
|
|
2086
2161
|
metadata: undefined,
|
|
2087
2162
|
localOpMetadata: this.idCompressor?.serialize(true),
|
|
2088
|
-
type: ContainerMessageType.IdAllocation,
|
|
2163
|
+
type: messageTypes_1.ContainerMessageType.IdAllocation,
|
|
2089
2164
|
};
|
|
2090
2165
|
}
|
|
2091
2166
|
if (idAllocationBatchMessage !== undefined) {
|
|
@@ -2142,11 +2217,11 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
2142
2217
|
// Please note that this does not change file format, so it can be disabled in the future if this
|
|
2143
2218
|
// optimization no longer makes sense (for example, batch compression may make it less appealing).
|
|
2144
2219
|
if (this.currentlyBatching() &&
|
|
2145
|
-
type === ContainerMessageType.Attach &&
|
|
2220
|
+
type === messageTypes_1.ContainerMessageType.Attach &&
|
|
2146
2221
|
this.disableAttachReorder !== true) {
|
|
2147
2222
|
this.outbox.submitAttach(message);
|
|
2148
2223
|
}
|
|
2149
|
-
else if (type === ContainerMessageType.BlobAttach) {
|
|
2224
|
+
else if (type === messageTypes_1.ContainerMessageType.BlobAttach) {
|
|
2150
2225
|
// BlobAttach ops must have their metadata visible and cannot be grouped (see opGroupingManager.ts)
|
|
2151
2226
|
this.outbox.submitBlobAttach(message);
|
|
2152
2227
|
}
|
|
@@ -2256,40 +2331,37 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
2256
2331
|
}
|
|
2257
2332
|
reSubmit(message) {
|
|
2258
2333
|
// Need to parse from string for back-compat
|
|
2259
|
-
const containerRuntimeMessage = this.
|
|
2334
|
+
const containerRuntimeMessage = this.parseLocalOpContent(message.content);
|
|
2260
2335
|
this.reSubmitCore(containerRuntimeMessage, message.localOpMetadata, message.opMetadata);
|
|
2261
2336
|
}
|
|
2262
2337
|
/**
|
|
2263
2338
|
* Finds the right store and asks it to resubmit the message. This typically happens when we
|
|
2264
2339
|
* reconnect and there are pending messages.
|
|
2265
|
-
* @param message - The original
|
|
2340
|
+
* @param message - The original LocalContainerRuntimeMessage.
|
|
2266
2341
|
* @param localOpMetadata - The local metadata associated with the original message.
|
|
2267
2342
|
*/
|
|
2268
2343
|
reSubmitCore(message, localOpMetadata, opMetadata) {
|
|
2269
|
-
const contents = message.contents;
|
|
2270
2344
|
switch (message.type) {
|
|
2271
|
-
case ContainerMessageType.FluidDataStoreOp:
|
|
2345
|
+
case messageTypes_1.ContainerMessageType.FluidDataStoreOp:
|
|
2272
2346
|
// For Operations, call resubmitDataStoreOp which will find the right store
|
|
2273
2347
|
// and trigger resubmission on it.
|
|
2274
|
-
this.dataStores.resubmitDataStoreOp(contents, localOpMetadata);
|
|
2348
|
+
this.dataStores.resubmitDataStoreOp(message.contents, localOpMetadata);
|
|
2275
2349
|
break;
|
|
2276
|
-
case ContainerMessageType.Attach:
|
|
2277
|
-
case ContainerMessageType.Alias:
|
|
2350
|
+
case messageTypes_1.ContainerMessageType.Attach:
|
|
2351
|
+
case messageTypes_1.ContainerMessageType.Alias:
|
|
2278
2352
|
this.submit(message, localOpMetadata);
|
|
2279
2353
|
break;
|
|
2280
|
-
case ContainerMessageType.IdAllocation:
|
|
2281
|
-
|
|
2282
|
-
if (contents.stashedState !== undefined) {
|
|
2283
|
-
delete contents.stashedState;
|
|
2284
|
-
}
|
|
2354
|
+
case messageTypes_1.ContainerMessageType.IdAllocation: {
|
|
2355
|
+
prepareLocalContainerRuntimeIdAllocationMessageForTransit(message);
|
|
2285
2356
|
this.submit(message, localOpMetadata);
|
|
2286
2357
|
break;
|
|
2287
|
-
|
|
2358
|
+
}
|
|
2359
|
+
case messageTypes_1.ContainerMessageType.ChunkedOp:
|
|
2288
2360
|
throw new Error(`chunkedOp not expected here`);
|
|
2289
|
-
case ContainerMessageType.BlobAttach:
|
|
2361
|
+
case messageTypes_1.ContainerMessageType.BlobAttach:
|
|
2290
2362
|
this.blobManager.reSubmit(opMetadata);
|
|
2291
2363
|
break;
|
|
2292
|
-
case ContainerMessageType.Rejoin:
|
|
2364
|
+
case messageTypes_1.ContainerMessageType.Rejoin:
|
|
2293
2365
|
this.submit(message);
|
|
2294
2366
|
break;
|
|
2295
2367
|
default: {
|
|
@@ -2317,9 +2389,9 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
2317
2389
|
}
|
|
2318
2390
|
rollback(content, localOpMetadata) {
|
|
2319
2391
|
// Need to parse from string for back-compat
|
|
2320
|
-
const { type, contents } = this.
|
|
2392
|
+
const { type, contents } = this.parseLocalOpContent(content);
|
|
2321
2393
|
switch (type) {
|
|
2322
|
-
case ContainerMessageType.FluidDataStoreOp:
|
|
2394
|
+
case messageTypes_1.ContainerMessageType.FluidDataStoreOp:
|
|
2323
2395
|
// For operations, call rollbackDataStoreOp which will find the right store
|
|
2324
2396
|
// and trigger rollback on it.
|
|
2325
2397
|
this.dataStores.rollbackDataStoreOp(contents, localOpMetadata);
|
|
@@ -2329,17 +2401,6 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
2329
2401
|
throw new Error(`Can't rollback ${type}`);
|
|
2330
2402
|
}
|
|
2331
2403
|
}
|
|
2332
|
-
async waitForDeltaManagerToCatchup(latestSnapshotRefSeq, summaryLogger) {
|
|
2333
|
-
if (latestSnapshotRefSeq > this.deltaManager.lastSequenceNumber) {
|
|
2334
|
-
// We need to catch up to the latest summary's reference sequence number before proceeding.
|
|
2335
|
-
await telemetry_utils_1.PerformanceEvent.timedExecAsync(summaryLogger, {
|
|
2336
|
-
eventName: "WaitingForSeq",
|
|
2337
|
-
lastSequenceNumber: this.deltaManager.lastSequenceNumber,
|
|
2338
|
-
targetSequenceNumber: latestSnapshotRefSeq,
|
|
2339
|
-
lastKnownSeqNumber: this.deltaManager.lastKnownSeqNumber,
|
|
2340
|
-
}, async () => waitForSeq(this.deltaManager, latestSnapshotRefSeq), { start: true, end: true, cancel: "error" });
|
|
2341
|
-
}
|
|
2342
|
-
}
|
|
2343
2404
|
/** Implementation of ISummarizerInternalsProvider.refreshLatestSummaryAck */
|
|
2344
2405
|
async refreshLatestSummaryAck(options) {
|
|
2345
2406
|
const { proposalHandle, ackHandle, summaryRefSeq, summaryLogger } = options;
|
|
@@ -2354,11 +2415,11 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
2354
2415
|
* and then close as the current main client is likely to be re-elected as the parent summarizer again.
|
|
2355
2416
|
*/
|
|
2356
2417
|
if (!result.isSummaryTracked && result.isSummaryNewer) {
|
|
2357
|
-
const fetchResult = await this.
|
|
2418
|
+
const fetchResult = await this.fetchLatestSnapshotFromStorage(summaryLogger, {
|
|
2358
2419
|
eventName: "RefreshLatestSummaryAckFetch",
|
|
2359
2420
|
ackHandle,
|
|
2360
2421
|
targetSequenceNumber: summaryRefSeq,
|
|
2361
|
-
}, readAndParseBlob
|
|
2422
|
+
}, readAndParseBlob);
|
|
2362
2423
|
/**
|
|
2363
2424
|
* If the fetched snapshot is older than the one for which the ack was received, close the container.
|
|
2364
2425
|
* This should never happen because an ack should be sent after the latest summary is updated in the server.
|
|
@@ -2385,41 +2446,42 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
2385
2446
|
await this.garbageCollector.refreshLatestSummary(result);
|
|
2386
2447
|
}
|
|
2387
2448
|
/**
|
|
2388
|
-
* Fetches the latest snapshot from storage
|
|
2389
|
-
*
|
|
2449
|
+
* Fetches the latest snapshot from storage to refresh the cache as a performance optimization and closes the
|
|
2450
|
+
* summarizer to reload from new state.
|
|
2390
2451
|
* @param summaryLogger - logger to use when fetching snapshot from storage
|
|
2391
|
-
* @returns
|
|
2452
|
+
* @returns a generic summarization error
|
|
2392
2453
|
*/
|
|
2393
|
-
async
|
|
2454
|
+
async prefetchLatestSummaryThenClose(summaryLogger) {
|
|
2394
2455
|
const readAndParseBlob = async (id) => (0, driver_utils_1.readAndParse)(this.storage, id);
|
|
2395
|
-
|
|
2456
|
+
// This is a performance optimization as the same parent is likely to be elected again, and would use its
|
|
2457
|
+
// cache to fetch the snapshot instead of the network.
|
|
2458
|
+
await this.fetchLatestSnapshotFromStorage(summaryLogger, {
|
|
2396
2459
|
eventName: "RefreshLatestSummaryFromServerFetch",
|
|
2397
|
-
}, readAndParseBlob
|
|
2460
|
+
}, readAndParseBlob);
|
|
2398
2461
|
await this.closeStaleSummarizer("RefreshLatestSummaryFromServerFetch");
|
|
2399
|
-
return {
|
|
2462
|
+
return {
|
|
2463
|
+
stage: "base",
|
|
2464
|
+
error: "summary state stale - Unsupported option 'refreshLatestAck'",
|
|
2465
|
+
referenceSequenceNumber: this.deltaManager.lastSequenceNumber,
|
|
2466
|
+
minimumSequenceNumber: this.deltaManager.minimumSequenceNumber,
|
|
2467
|
+
};
|
|
2400
2468
|
}
|
|
2401
2469
|
async closeStaleSummarizer(codePath) {
|
|
2402
|
-
this.mc.logger.sendTelemetryEvent({
|
|
2403
|
-
eventName: "ClosingSummarizerOnSummaryStale",
|
|
2404
|
-
codePath,
|
|
2405
|
-
message: "Stopping fetch from storage",
|
|
2406
|
-
closeSummarizerDelayMs: this.closeSummarizerDelayMs,
|
|
2407
|
-
}, new telemetry_utils_1.GenericError("Restarting summarizer instead of refreshing"));
|
|
2408
2470
|
// Delay before restarting summarizer to prevent the summarizer from restarting too frequently.
|
|
2409
2471
|
await (0, core_utils_1.delay)(this.closeSummarizerDelayMs);
|
|
2410
2472
|
this._summarizer?.stop("latestSummaryStateStale");
|
|
2411
2473
|
this.disposeFn();
|
|
2412
2474
|
}
|
|
2413
2475
|
/**
|
|
2414
|
-
* Downloads
|
|
2476
|
+
* Downloads the latest snapshot from storage.
|
|
2415
2477
|
* By default, it also closes the container after downloading the snapshot. However, this may be
|
|
2416
2478
|
* overridden via options.
|
|
2417
2479
|
*/
|
|
2418
|
-
async
|
|
2480
|
+
async fetchLatestSnapshotFromStorage(logger, event, readAndParseBlob) {
|
|
2419
2481
|
return telemetry_utils_1.PerformanceEvent.timedExecAsync(logger, event, async (perfEvent) => {
|
|
2420
2482
|
const stats = {};
|
|
2421
2483
|
const trace = client_utils_1.Trace.start();
|
|
2422
|
-
const versions = await this.storage.getVersions(
|
|
2484
|
+
const versions = await this.storage.getVersions(null, 1, "prefetchLatestSummaryBeforeClose", driver_definitions_1.FetchSource.noCache);
|
|
2423
2485
|
(0, core_utils_1.assert)(!!versions && !!versions[0], 0x137 /* "Failed to get version from storage" */);
|
|
2424
2486
|
stats.getVersionDuration = trace.trace().duration;
|
|
2425
2487
|
const maybeSnapshot = await this.storage.getSnapshotTree(versions[0]);
|
|
@@ -2447,15 +2509,17 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
2447
2509
|
if (this._orderSequentiallyCalls !== 0) {
|
|
2448
2510
|
throw new telemetry_utils_1.UsageError("can't get state during orderSequentially");
|
|
2449
2511
|
}
|
|
2450
|
-
const pendingAttachmentBlobs = await this.blobManager.getPendingBlobs(waitBlobsToAttach);
|
|
2451
|
-
const pending = this.pendingStateManager.getLocalState();
|
|
2452
|
-
if (!pendingAttachmentBlobs && !this.hasPendingMessages()) {
|
|
2453
|
-
return; // no pending state to save
|
|
2454
|
-
}
|
|
2455
2512
|
// Flush pending batch.
|
|
2456
2513
|
// getPendingLocalState() is only exposed through Container.closeAndGetPendingLocalState(), so it's safe
|
|
2457
2514
|
// to close current batch.
|
|
2458
2515
|
this.flush();
|
|
2516
|
+
const pendingAttachmentBlobs = waitBlobsToAttach
|
|
2517
|
+
? await this.blobManager.attachAndGetPendingBlobs()
|
|
2518
|
+
: undefined;
|
|
2519
|
+
const pending = this.pendingStateManager.getLocalState();
|
|
2520
|
+
if (!pendingAttachmentBlobs && !this.hasPendingMessages()) {
|
|
2521
|
+
return; // no pending state to save
|
|
2522
|
+
}
|
|
2459
2523
|
const pendingState = {
|
|
2460
2524
|
pending,
|
|
2461
2525
|
pendingAttachmentBlobs,
|
|
@@ -2496,29 +2560,11 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
2496
2560
|
}
|
|
2497
2561
|
}
|
|
2498
2562
|
/**
|
|
2499
|
-
*
|
|
2500
|
-
|
|
2501
|
-
|
|
2502
|
-
formRequestSummarizerFn(loaderRouter) {
|
|
2563
|
+
* Forms a function that will create and retrieve a Summarizer.
|
|
2564
|
+
*/
|
|
2565
|
+
formCreateSummarizerFn(loader) {
|
|
2503
2566
|
return async () => {
|
|
2504
|
-
|
|
2505
|
-
headers: {
|
|
2506
|
-
[container_definitions_1.LoaderHeader.cache]: false,
|
|
2507
|
-
[container_definitions_1.LoaderHeader.clientDetails]: {
|
|
2508
|
-
capabilities: { interactive: false },
|
|
2509
|
-
type: summary_1.summarizerClientType,
|
|
2510
|
-
},
|
|
2511
|
-
[driver_definitions_1.DriverHeader.summarizingClient]: true,
|
|
2512
|
-
[container_definitions_1.LoaderHeader.reconnect]: false,
|
|
2513
|
-
},
|
|
2514
|
-
url: "/_summarizer",
|
|
2515
|
-
};
|
|
2516
|
-
const fluidObject = await (0, runtime_utils_1.requestFluidObject)(loaderRouter, request);
|
|
2517
|
-
const summarizer = fluidObject.ISummarizer;
|
|
2518
|
-
if (!summarizer) {
|
|
2519
|
-
throw new telemetry_utils_1.UsageError("Fluid object does not implement ISummarizer");
|
|
2520
|
-
}
|
|
2521
|
-
return summarizer;
|
|
2567
|
+
return createSummarizer(loader, `/${summarizerRequestUrl}`);
|
|
2522
2568
|
};
|
|
2523
2569
|
}
|
|
2524
2570
|
validateSummaryHeuristicConfiguration(configuration) {
|
|
@@ -2538,26 +2584,4 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
2538
2584
|
}
|
|
2539
2585
|
}
|
|
2540
2586
|
exports.ContainerRuntime = ContainerRuntime;
|
|
2541
|
-
/**
|
|
2542
|
-
* Wait for a specific sequence number. Promise should resolve when we reach that number,
|
|
2543
|
-
* or reject if closed.
|
|
2544
|
-
*/
|
|
2545
|
-
const waitForSeq = async (deltaManager, targetSeq) => new Promise((resolve, reject) => {
|
|
2546
|
-
// TODO: remove cast to any when actual event is determined
|
|
2547
|
-
deltaManager.on("closed", reject);
|
|
2548
|
-
deltaManager.on("disposed", reject);
|
|
2549
|
-
// If we already reached target sequence number, simply resolve the promise.
|
|
2550
|
-
if (deltaManager.lastSequenceNumber >= targetSeq) {
|
|
2551
|
-
resolve();
|
|
2552
|
-
}
|
|
2553
|
-
else {
|
|
2554
|
-
const handleOp = (message) => {
|
|
2555
|
-
if (message.sequenceNumber >= targetSeq) {
|
|
2556
|
-
resolve();
|
|
2557
|
-
deltaManager.off("op", handleOp);
|
|
2558
|
-
}
|
|
2559
|
-
};
|
|
2560
|
-
deltaManager.on("op", handleOp);
|
|
2561
|
-
}
|
|
2562
|
-
});
|
|
2563
2587
|
//# sourceMappingURL=containerRuntime.js.map
|