@fluidframework/container-runtime 2.0.0-dev.1.4.6.106135 → 2.0.0-dev.2.3.0.115467
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.js +1 -1
- package/dist/blobManager.d.ts +20 -5
- package/dist/blobManager.d.ts.map +1 -1
- package/dist/blobManager.js +57 -15
- package/dist/blobManager.js.map +1 -1
- package/dist/containerRuntime.d.ts +88 -51
- package/dist/containerRuntime.d.ts.map +1 -1
- package/dist/containerRuntime.js +205 -300
- package/dist/containerRuntime.js.map +1 -1
- package/dist/dataStore.d.ts.map +1 -1
- package/dist/dataStore.js +6 -0
- package/dist/dataStore.js.map +1 -1
- package/dist/dataStoreContext.d.ts +14 -21
- package/dist/dataStoreContext.d.ts.map +1 -1
- package/dist/dataStoreContext.js +71 -57
- package/dist/dataStoreContext.js.map +1 -1
- package/dist/dataStoreContexts.js +1 -1
- package/dist/dataStoreContexts.js.map +1 -1
- package/dist/dataStores.d.ts +11 -10
- package/dist/dataStores.d.ts.map +1 -1
- package/dist/dataStores.js +51 -20
- package/dist/dataStores.js.map +1 -1
- package/dist/garbageCollection.d.ts +40 -32
- package/dist/garbageCollection.d.ts.map +1 -1
- package/dist/garbageCollection.js +227 -161
- package/dist/garbageCollection.js.map +1 -1
- package/dist/garbageCollectionConstants.d.ts +19 -0
- package/dist/garbageCollectionConstants.d.ts.map +1 -0
- package/dist/garbageCollectionConstants.js +34 -0
- package/dist/garbageCollectionConstants.js.map +1 -0
- package/dist/gcSweepReadyUsageDetection.d.ts.map +1 -1
- package/dist/gcSweepReadyUsageDetection.js +5 -14
- package/dist/gcSweepReadyUsageDetection.js.map +1 -1
- package/dist/index.d.ts +6 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -9
- package/dist/index.js.map +1 -1
- package/dist/opLifecycle/batchManager.d.ts +30 -0
- package/dist/opLifecycle/batchManager.d.ts.map +1 -0
- package/dist/{batchManager.js → opLifecycle/batchManager.js} +25 -15
- package/dist/opLifecycle/batchManager.js.map +1 -0
- package/dist/opLifecycle/definitions.d.ts +40 -0
- package/dist/opLifecycle/definitions.d.ts.map +1 -0
- package/dist/opLifecycle/definitions.js +7 -0
- package/dist/opLifecycle/definitions.js.map +1 -0
- package/dist/opLifecycle/index.d.ts +12 -0
- package/dist/opLifecycle/index.d.ts.map +1 -0
- package/dist/opLifecycle/index.js +21 -0
- package/dist/opLifecycle/index.js.map +1 -0
- package/dist/opLifecycle/opCompressor.d.ts +18 -0
- package/dist/opLifecycle/opCompressor.d.ts.map +1 -0
- package/dist/opLifecycle/opCompressor.js +53 -0
- package/dist/opLifecycle/opCompressor.js.map +1 -0
- package/dist/opLifecycle/opDecompressor.d.ts +20 -0
- package/dist/opLifecycle/opDecompressor.d.ts.map +1 -0
- package/dist/opLifecycle/opDecompressor.js +72 -0
- package/dist/opLifecycle/opDecompressor.js.map +1 -0
- package/dist/opLifecycle/opSplitter.d.ts +17 -0
- package/dist/opLifecycle/opSplitter.d.ts.map +1 -0
- package/dist/opLifecycle/opSplitter.js +61 -0
- package/dist/opLifecycle/opSplitter.js.map +1 -0
- package/dist/opLifecycle/outbox.d.ts +47 -0
- package/dist/opLifecycle/outbox.d.ts.map +1 -0
- package/dist/opLifecycle/outbox.js +153 -0
- package/dist/opLifecycle/outbox.js.map +1 -0
- package/dist/opLifecycle/remoteMessageProcessor.d.ts +26 -0
- package/dist/opLifecycle/remoteMessageProcessor.d.ts.map +1 -0
- package/dist/opLifecycle/remoteMessageProcessor.js +81 -0
- package/dist/opLifecycle/remoteMessageProcessor.js.map +1 -0
- 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 +6 -26
- package/dist/pendingStateManager.d.ts.map +1 -1
- package/dist/pendingStateManager.js +42 -62
- package/dist/pendingStateManager.js.map +1 -1
- package/dist/runningSummarizer.d.ts +3 -2
- package/dist/runningSummarizer.d.ts.map +1 -1
- package/dist/runningSummarizer.js +10 -3
- package/dist/runningSummarizer.js.map +1 -1
- package/dist/scheduleManager.js.map +1 -1
- package/dist/summarizer.js +7 -2
- package/dist/summarizer.js.map +1 -1
- package/dist/summarizerClientElection.js +1 -1
- package/dist/summarizerClientElection.js.map +1 -1
- package/dist/summarizerHeuristics.d.ts.map +1 -1
- package/dist/summarizerHeuristics.js +0 -3
- package/dist/summarizerHeuristics.js.map +1 -1
- package/dist/summarizerTypes.d.ts +19 -2
- package/dist/summarizerTypes.d.ts.map +1 -1
- package/dist/summarizerTypes.js.map +1 -1
- package/dist/summaryFormat.d.ts +4 -2
- package/dist/summaryFormat.d.ts.map +1 -1
- package/dist/summaryFormat.js +2 -2
- package/dist/summaryFormat.js.map +1 -1
- package/dist/summaryGenerator.d.ts.map +1 -1
- package/dist/summaryGenerator.js +3 -2
- package/dist/summaryGenerator.js.map +1 -1
- package/dist/summaryManager.d.ts.map +1 -1
- package/dist/summaryManager.js +10 -6
- package/dist/summaryManager.js.map +1 -1
- package/garbageCollection.md +27 -22
- package/lib/blobManager.d.ts +20 -5
- package/lib/blobManager.d.ts.map +1 -1
- package/lib/blobManager.js +59 -17
- package/lib/blobManager.js.map +1 -1
- package/lib/containerRuntime.d.ts +88 -51
- package/lib/containerRuntime.d.ts.map +1 -1
- package/lib/containerRuntime.js +203 -297
- package/lib/containerRuntime.js.map +1 -1
- package/lib/dataStore.d.ts.map +1 -1
- package/lib/dataStore.js +6 -0
- package/lib/dataStore.js.map +1 -1
- package/lib/dataStoreContext.d.ts +14 -21
- package/lib/dataStoreContext.d.ts.map +1 -1
- package/lib/dataStoreContext.js +75 -61
- package/lib/dataStoreContext.js.map +1 -1
- package/lib/dataStoreContexts.js +1 -1
- package/lib/dataStoreContexts.js.map +1 -1
- package/lib/dataStores.d.ts +11 -10
- package/lib/dataStores.d.ts.map +1 -1
- package/lib/dataStores.js +53 -22
- package/lib/dataStores.js.map +1 -1
- package/lib/garbageCollection.d.ts +40 -32
- package/lib/garbageCollection.d.ts.map +1 -1
- package/lib/garbageCollection.js +220 -154
- package/lib/garbageCollection.js.map +1 -1
- package/lib/garbageCollectionConstants.d.ts +19 -0
- package/lib/garbageCollectionConstants.d.ts.map +1 -0
- package/lib/garbageCollectionConstants.js +31 -0
- package/lib/garbageCollectionConstants.js.map +1 -0
- package/lib/gcSweepReadyUsageDetection.d.ts.map +1 -1
- package/lib/gcSweepReadyUsageDetection.js +4 -13
- package/lib/gcSweepReadyUsageDetection.js.map +1 -1
- package/lib/index.d.ts +6 -6
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +3 -4
- package/lib/index.js.map +1 -1
- package/lib/opLifecycle/batchManager.d.ts +30 -0
- package/lib/opLifecycle/batchManager.d.ts.map +1 -0
- package/lib/{batchManager.js → opLifecycle/batchManager.js} +25 -15
- package/lib/opLifecycle/batchManager.js.map +1 -0
- package/lib/opLifecycle/definitions.d.ts +40 -0
- package/lib/opLifecycle/definitions.d.ts.map +1 -0
- package/lib/opLifecycle/definitions.js +6 -0
- package/lib/opLifecycle/definitions.js.map +1 -0
- package/lib/opLifecycle/index.d.ts +12 -0
- package/lib/opLifecycle/index.d.ts.map +1 -0
- package/lib/opLifecycle/index.js +11 -0
- package/lib/opLifecycle/index.js.map +1 -0
- package/lib/opLifecycle/opCompressor.d.ts +18 -0
- package/lib/opLifecycle/opCompressor.d.ts.map +1 -0
- package/lib/opLifecycle/opCompressor.js +49 -0
- package/lib/opLifecycle/opCompressor.js.map +1 -0
- package/lib/opLifecycle/opDecompressor.d.ts +20 -0
- package/lib/opLifecycle/opDecompressor.d.ts.map +1 -0
- package/lib/opLifecycle/opDecompressor.js +68 -0
- package/lib/opLifecycle/opDecompressor.js.map +1 -0
- package/lib/opLifecycle/opSplitter.d.ts +17 -0
- package/lib/opLifecycle/opSplitter.d.ts.map +1 -0
- package/lib/opLifecycle/opSplitter.js +57 -0
- package/lib/opLifecycle/opSplitter.js.map +1 -0
- package/lib/opLifecycle/outbox.d.ts +47 -0
- package/lib/opLifecycle/outbox.d.ts.map +1 -0
- package/lib/opLifecycle/outbox.js +149 -0
- package/lib/opLifecycle/outbox.js.map +1 -0
- package/lib/opLifecycle/remoteMessageProcessor.d.ts +26 -0
- package/lib/opLifecycle/remoteMessageProcessor.d.ts.map +1 -0
- package/lib/opLifecycle/remoteMessageProcessor.js +76 -0
- package/lib/opLifecycle/remoteMessageProcessor.js.map +1 -0
- 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 +6 -26
- package/lib/pendingStateManager.d.ts.map +1 -1
- package/lib/pendingStateManager.js +42 -62
- package/lib/pendingStateManager.js.map +1 -1
- package/lib/runningSummarizer.d.ts +3 -2
- package/lib/runningSummarizer.d.ts.map +1 -1
- package/lib/runningSummarizer.js +10 -3
- package/lib/runningSummarizer.js.map +1 -1
- package/lib/scheduleManager.js.map +1 -1
- package/lib/summarizer.js +7 -2
- package/lib/summarizer.js.map +1 -1
- package/lib/summarizerClientElection.js +1 -1
- package/lib/summarizerClientElection.js.map +1 -1
- package/lib/summarizerHeuristics.d.ts.map +1 -1
- package/lib/summarizerHeuristics.js +0 -3
- package/lib/summarizerHeuristics.js.map +1 -1
- package/lib/summarizerTypes.d.ts +19 -2
- package/lib/summarizerTypes.d.ts.map +1 -1
- package/lib/summarizerTypes.js.map +1 -1
- package/lib/summaryFormat.d.ts +4 -2
- package/lib/summaryFormat.d.ts.map +1 -1
- package/lib/summaryFormat.js +1 -1
- package/lib/summaryFormat.js.map +1 -1
- package/lib/summaryGenerator.d.ts.map +1 -1
- package/lib/summaryGenerator.js +3 -2
- package/lib/summaryGenerator.js.map +1 -1
- package/lib/summaryManager.d.ts.map +1 -1
- package/lib/summaryManager.js +10 -6
- package/lib/summaryManager.js.map +1 -1
- package/package.json +32 -71
- package/prettier.config.cjs +8 -0
- package/src/blobManager.ts +74 -19
- package/src/containerRuntime.ts +286 -369
- package/src/dataStore.ts +13 -1
- package/src/dataStoreContext.ts +100 -76
- package/src/dataStoreContexts.ts +1 -1
- package/src/dataStores.ts +61 -22
- package/src/garbageCollection.ts +282 -163
- package/src/garbageCollectionConstants.ts +35 -0
- package/src/gcSweepReadyUsageDetection.ts +3 -11
- package/src/index.ts +9 -8
- package/src/{batchManager.ts → opLifecycle/batchManager.ts} +42 -28
- package/src/opLifecycle/definitions.ts +44 -0
- package/src/opLifecycle/index.ts +17 -0
- package/src/opLifecycle/opCompressor.ts +64 -0
- package/src/opLifecycle/opDecompressor.ts +84 -0
- package/src/opLifecycle/opSplitter.ts +78 -0
- package/src/opLifecycle/outbox.ts +204 -0
- package/src/opLifecycle/remoteMessageProcessor.ts +90 -0
- package/src/packageVersion.ts +1 -1
- package/src/pendingStateManager.ts +57 -96
- package/src/runningSummarizer.ts +11 -3
- package/src/scheduleManager.ts +1 -0
- package/src/summarizer.ts +6 -6
- package/src/summarizerClientElection.ts +1 -1
- package/src/summarizerHeuristics.ts +0 -3
- package/src/summarizerTypes.ts +20 -7
- package/src/summaryFormat.ts +5 -3
- package/src/summaryGenerator.ts +3 -2
- package/src/summaryManager.ts +18 -7
- package/dist/batchManager.d.ts +0 -37
- package/dist/batchManager.d.ts.map +0 -1
- package/dist/batchManager.js.map +0 -1
- package/lib/batchManager.d.ts +0 -37
- package/lib/batchManager.d.ts.map +0 -1
- package/lib/batchManager.js.map +0 -1
package/dist/containerRuntime.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.ContainerRuntime = exports.getDeviceSpec = exports.agentSchedulerId = exports.
|
|
3
|
+
exports.ContainerRuntime = exports.getDeviceSpec = exports.agentSchedulerId = exports.isRuntimeMessage = exports.RuntimeMessage = exports.CompressionAlgorithms = exports.RuntimeHeaders = exports.DefaultSummaryConfiguration = exports.ContainerMessageType = void 0;
|
|
4
4
|
const container_definitions_1 = require("@fluidframework/container-definitions");
|
|
5
5
|
const common_utils_1 = require("@fluidframework/common-utils");
|
|
6
6
|
const telemetry_utils_1 = require("@fluidframework/telemetry-utils");
|
|
@@ -18,7 +18,6 @@ const summarizer_1 = require("./summarizer");
|
|
|
18
18
|
const summaryManager_1 = require("./summaryManager");
|
|
19
19
|
const connectionTelemetry_1 = require("./connectionTelemetry");
|
|
20
20
|
const pendingStateManager_1 = require("./pendingStateManager");
|
|
21
|
-
const batchManager_1 = require("./batchManager");
|
|
22
21
|
const packageVersion_1 = require("./packageVersion");
|
|
23
22
|
const blobManager_1 = require("./blobManager");
|
|
24
23
|
const dataStores_1 = require("./dataStores");
|
|
@@ -29,10 +28,12 @@ const summarizerClientElection_1 = require("./summarizerClientElection");
|
|
|
29
28
|
const throttler_1 = require("./throttler");
|
|
30
29
|
const runWhileConnectedCoordinator_1 = require("./runWhileConnectedCoordinator");
|
|
31
30
|
const garbageCollection_1 = require("./garbageCollection");
|
|
31
|
+
const garbageCollectionConstants_1 = require("./garbageCollectionConstants");
|
|
32
32
|
const dataStore_1 = require("./dataStore");
|
|
33
33
|
const batchTracker_1 = require("./batchTracker");
|
|
34
34
|
const serializedSnapshotStorage_1 = require("./serializedSnapshotStorage");
|
|
35
35
|
const scheduleManager_1 = require("./scheduleManager");
|
|
36
|
+
const opLifecycle_1 = require("./opLifecycle");
|
|
36
37
|
var ContainerMessageType;
|
|
37
38
|
(function (ContainerMessageType) {
|
|
38
39
|
// An op to be delivered to store
|
|
@@ -61,6 +62,7 @@ exports.DefaultSummaryConfiguration = {
|
|
|
61
62
|
summarizerClientElection: false,
|
|
62
63
|
nonRuntimeOpWeight: 0.1,
|
|
63
64
|
runtimeOpWeight: 1.0,
|
|
65
|
+
nonRuntimeHeuristicThreshold: 20,
|
|
64
66
|
};
|
|
65
67
|
/**
|
|
66
68
|
* Accepted header keys for requests coming to the runtime.
|
|
@@ -77,8 +79,20 @@ var RuntimeHeaders;
|
|
|
77
79
|
/** True if the request is coming from an IFluidHandle. */
|
|
78
80
|
RuntimeHeaders["viaHandle"] = "viaHandle";
|
|
79
81
|
})(RuntimeHeaders = exports.RuntimeHeaders || (exports.RuntimeHeaders = {}));
|
|
82
|
+
/**
|
|
83
|
+
* Available compression algorithms for op compression.
|
|
84
|
+
*/
|
|
85
|
+
var CompressionAlgorithms;
|
|
86
|
+
(function (CompressionAlgorithms) {
|
|
87
|
+
CompressionAlgorithms["lz4"] = "lz4";
|
|
88
|
+
})(CompressionAlgorithms = exports.CompressionAlgorithms || (exports.CompressionAlgorithms = {}));
|
|
80
89
|
const maxConsecutiveReconnectsKey = "Fluid.ContainerRuntime.MaxConsecutiveReconnects";
|
|
81
90
|
const defaultFlushMode = runtime_definitions_1.FlushMode.TurnBased;
|
|
91
|
+
// The actual limit is 1Mb (socket.io and Kafka limits)
|
|
92
|
+
// We can't estimate it fully, as we
|
|
93
|
+
// - do not know what properties relay service will add
|
|
94
|
+
// - we do not stringify final op, thus we do not know how much escaping will be added.
|
|
95
|
+
const defaultMaxBatchSizeInBytes = 950 * 1024;
|
|
82
96
|
/**
|
|
83
97
|
* @deprecated - use ContainerRuntimeMessage instead
|
|
84
98
|
*/
|
|
@@ -96,45 +110,9 @@ var RuntimeMessage;
|
|
|
96
110
|
* @deprecated - please use version in driver-utils
|
|
97
111
|
*/
|
|
98
112
|
function isRuntimeMessage(message) {
|
|
99
|
-
|
|
100
|
-
return true;
|
|
101
|
-
}
|
|
102
|
-
return false;
|
|
113
|
+
return Object.values(RuntimeMessage).includes(message.type);
|
|
103
114
|
}
|
|
104
115
|
exports.isRuntimeMessage = isRuntimeMessage;
|
|
105
|
-
/**
|
|
106
|
-
* Unpacks runtime messages
|
|
107
|
-
*
|
|
108
|
-
* @remarks This API makes no promises regarding backward-compatability. This is internal API.
|
|
109
|
-
* @param message - message (as it observed in storage / service)
|
|
110
|
-
* @returns unpacked runtime message
|
|
111
|
-
*
|
|
112
|
-
* @internal
|
|
113
|
-
*/
|
|
114
|
-
function unpackRuntimeMessage(message) {
|
|
115
|
-
if (message.type === protocol_definitions_1.MessageType.Operation) {
|
|
116
|
-
// legacy op format?
|
|
117
|
-
if (message.contents.address !== undefined && message.contents.type === undefined) {
|
|
118
|
-
message.type = ContainerMessageType.FluidDataStoreOp;
|
|
119
|
-
}
|
|
120
|
-
else {
|
|
121
|
-
// new format
|
|
122
|
-
const innerContents = message.contents;
|
|
123
|
-
(0, common_utils_1.assert)(innerContents.type !== undefined, 0x121 /* "Undefined inner contents type!" */);
|
|
124
|
-
message.type = innerContents.type;
|
|
125
|
-
message.contents = innerContents.contents;
|
|
126
|
-
}
|
|
127
|
-
return true;
|
|
128
|
-
}
|
|
129
|
-
else {
|
|
130
|
-
// Legacy format, but it's already "unpacked",
|
|
131
|
-
// i.e. message.type is actually ContainerMessageType.
|
|
132
|
-
// Or it's non-runtime message.
|
|
133
|
-
// Nothing to do in such case.
|
|
134
|
-
return false;
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
exports.unpackRuntimeMessage = unpackRuntimeMessage;
|
|
138
116
|
/**
|
|
139
117
|
* Legacy ID for the built-in AgentScheduler. To minimize disruption while removing it, retaining this as a
|
|
140
118
|
* special-case for document dirty state. Ultimately we should have no special-cases from the
|
|
@@ -161,8 +139,11 @@ exports.getDeviceSpec = getDeviceSpec;
|
|
|
161
139
|
* It will define the store level mappings.
|
|
162
140
|
*/
|
|
163
141
|
class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
142
|
+
/**
|
|
143
|
+
* @internal
|
|
144
|
+
*/
|
|
164
145
|
constructor(context, registry, metadata, electedSummarizerData, chunks, dataStoreAliasMap, runtimeOptions, containerScope, logger, existing, blobManagerSnapshot, _storage, requestHandler, summaryConfiguration) {
|
|
165
|
-
var _a, _b, _c, _d;
|
|
146
|
+
var _a, _b, _c, _d, _e, _f;
|
|
166
147
|
if (summaryConfiguration === void 0) { summaryConfiguration = Object.assign(Object.assign({}, exports.DefaultSummaryConfiguration), (_a = runtimeOptions.summaryOptions) === null || _a === void 0 ? void 0 : _a.summaryConfigOverrides); }
|
|
167
148
|
super();
|
|
168
149
|
this.context = context;
|
|
@@ -175,7 +156,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
175
156
|
this.summaryConfiguration = summaryConfiguration;
|
|
176
157
|
this.defaultMaxConsecutiveReconnects = 7;
|
|
177
158
|
this._orderSequentiallyCalls = 0;
|
|
178
|
-
this.
|
|
159
|
+
this.flushMicroTaskExists = false;
|
|
179
160
|
this.savedOps = [];
|
|
180
161
|
this.consecutiveReconnects = 0;
|
|
181
162
|
this._disposed = false;
|
|
@@ -187,12 +168,6 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
187
168
|
signalTimestamp: 0,
|
|
188
169
|
trackingSignalSequenceNumber: undefined,
|
|
189
170
|
};
|
|
190
|
-
// Provide lower soft limit - we want to have some number of ops to get efficiency in compression & bandwidth usage,
|
|
191
|
-
// but at the same time we want to send these ops sooner, to reduce overall latency of processing a batch.
|
|
192
|
-
// So there is some ballance here, that depends on compression algorithm and its efficiency working with smaller
|
|
193
|
-
// payloads. That number represents final (compressed) bits (once compression is implemented).
|
|
194
|
-
this.pendingAttachBatch = new batchManager_1.BatchManager(64 * 1024);
|
|
195
|
-
this.pendingBatch = new batchManager_1.BatchManager();
|
|
196
171
|
this.summarizeOnDemand = (...args) => {
|
|
197
172
|
if (this.clientDetails.type === summarizerClientElection_1.summarizerClientType) {
|
|
198
173
|
return this.summarizer.summarizeOnDemand(...args);
|
|
@@ -221,9 +196,29 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
221
196
|
throw new container_utils_1.UsageError(`Can't summarize, disableSummaries: ${this.summariesDisabled}`);
|
|
222
197
|
}
|
|
223
198
|
};
|
|
199
|
+
let loadSummaryNumber;
|
|
200
|
+
// Get the container creation metadata. For new container, we initialize these. For existing containers,
|
|
201
|
+
// get the values from the metadata blob.
|
|
202
|
+
if (existing) {
|
|
203
|
+
this.createContainerMetadata = {
|
|
204
|
+
createContainerRuntimeVersion: metadata === null || metadata === void 0 ? void 0 : metadata.createContainerRuntimeVersion,
|
|
205
|
+
createContainerTimestamp: metadata === null || metadata === void 0 ? void 0 : metadata.createContainerTimestamp,
|
|
206
|
+
};
|
|
207
|
+
// summaryNumber was renamed from summaryCount. For older docs that haven't been opened for a long time,
|
|
208
|
+
// the count is reset to 0.
|
|
209
|
+
loadSummaryNumber = (_b = metadata === null || metadata === void 0 ? void 0 : metadata.summaryNumber) !== null && _b !== void 0 ? _b : 0;
|
|
210
|
+
}
|
|
211
|
+
else {
|
|
212
|
+
this.createContainerMetadata = {
|
|
213
|
+
createContainerRuntimeVersion: packageVersion_1.pkgVersion,
|
|
214
|
+
createContainerTimestamp: Date.now(),
|
|
215
|
+
};
|
|
216
|
+
loadSummaryNumber = 0;
|
|
217
|
+
}
|
|
218
|
+
this.nextSummaryNumber = loadSummaryNumber + 1;
|
|
224
219
|
this.messageAtLastSummary = metadata === null || metadata === void 0 ? void 0 : metadata.message;
|
|
225
220
|
this._connected = this.context.connected;
|
|
226
|
-
this.
|
|
221
|
+
this.remoteMessageProcessor = new opLifecycle_1.RemoteMessageProcessor(new opLifecycle_1.OpSplitter(chunks), new opLifecycle_1.OpDecompressor());
|
|
227
222
|
this.handleContext = new containerHandleContext_1.ContainerFluidHandleContext("", this);
|
|
228
223
|
this.mc = (0, telemetry_utils_1.loggerToMonitoringContext)(telemetry_utils_1.ChildLogger.create(this.logger, "ContainerRuntime"));
|
|
229
224
|
if (this.summaryConfiguration.state === "enabled") {
|
|
@@ -235,10 +230,17 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
235
230
|
this.maxOpsSinceLastSummary = this.getMaxOpsSinceLastSummary();
|
|
236
231
|
this.initialSummarizerDelayMs = this.getInitialSummarizerDelayMs();
|
|
237
232
|
this.maxConsecutiveReconnects =
|
|
238
|
-
(
|
|
233
|
+
(_c = this.mc.config.getNumber(maxConsecutiveReconnectsKey)) !== null && _c !== void 0 ? _c : this.defaultMaxConsecutiveReconnects;
|
|
239
234
|
this._flushMode = runtimeOptions.flushMode;
|
|
240
235
|
const pendingRuntimeState = context.pendingLocalState;
|
|
241
|
-
const baseSnapshot = (
|
|
236
|
+
const baseSnapshot = (_d = pendingRuntimeState === null || pendingRuntimeState === void 0 ? void 0 : pendingRuntimeState.baseSnapshot) !== null && _d !== void 0 ? _d : context.baseSnapshot;
|
|
237
|
+
const maxSnapshotCacheDurationMs = (_f = (_e = this._storage) === null || _e === void 0 ? void 0 : _e.policies) === null || _f === void 0 ? void 0 : _f.maximumCacheDurationMs;
|
|
238
|
+
if (maxSnapshotCacheDurationMs !== undefined && maxSnapshotCacheDurationMs > 5 * 24 * 60 * 60 * 1000) {
|
|
239
|
+
// This is a runtime enforcement of what's already explicit in the policy's type itself,
|
|
240
|
+
// which dictates the value is either undefined or exactly 5 days in ms.
|
|
241
|
+
// As long as the actual value is less than 5 days, the assumptions GC makes here are valid.
|
|
242
|
+
throw new container_utils_1.UsageError("Driver's maximumCacheDurationMs policy cannot exceed 5 days");
|
|
243
|
+
}
|
|
242
244
|
this.garbageCollector = garbageCollection_1.GarbageCollector.create({
|
|
243
245
|
runtime: this,
|
|
244
246
|
gcOptions: this.runtimeOptions.gcOptions,
|
|
@@ -246,6 +248,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
246
248
|
baseLogger: this.mc.logger,
|
|
247
249
|
existing,
|
|
248
250
|
metadata,
|
|
251
|
+
createContainerMetadata: this.createContainerMetadata,
|
|
249
252
|
isSummarizerClient: this.context.clientDetails.type === summarizerClientElection_1.summarizerClientType,
|
|
250
253
|
getNodePackagePath: async (nodePath) => this.getGCNodePackagePath(nodePath),
|
|
251
254
|
getLastSummaryTimestampMs: () => { var _a; return (_a = this.messageAtLastSummary) === null || _a === void 0 ? void 0 : _a.timestamp; },
|
|
@@ -271,28 +274,37 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
271
274
|
gcDisabled: !this.garbageCollector.shouldRunGC,
|
|
272
275
|
});
|
|
273
276
|
if (baseSnapshot) {
|
|
274
|
-
this.summarizerNode.
|
|
277
|
+
this.summarizerNode.updateBaseSummaryState(baseSnapshot);
|
|
275
278
|
}
|
|
276
|
-
this.dataStores = new dataStores_1.DataStores((0, dataStores_1.getSummaryForDatastores)(baseSnapshot, metadata), this, (attachMsg) => this.submit(ContainerMessageType.Attach, attachMsg), (id, createParam) => (summarizeInternal, getGCDataFn, getBaseGCDetailsFn) => this.summarizerNode.createChild(summarizeInternal, id, createParam, undefined, getGCDataFn, getBaseGCDetailsFn), (id) => this.summarizerNode.deleteChild(id), this.mc.logger, async () => this.garbageCollector.getBaseGCDetails(), (path, timestampMs, packagePath) => this.garbageCollector.nodeUpdated(path, "Changed", timestampMs, packagePath), new Map(dataStoreAliasMap)
|
|
279
|
+
this.dataStores = new dataStores_1.DataStores((0, dataStores_1.getSummaryForDatastores)(baseSnapshot, metadata), this, (attachMsg) => this.submit(ContainerMessageType.Attach, attachMsg), (id, createParam) => (summarizeInternal, getGCDataFn, getBaseGCDetailsFn) => this.summarizerNode.createChild(summarizeInternal, id, createParam, undefined, getGCDataFn, getBaseGCDetailsFn), (id) => this.summarizerNode.deleteChild(id), this.mc.logger, async () => this.garbageCollector.getBaseGCDetails(), (path, timestampMs, packagePath) => this.garbageCollector.nodeUpdated(path, "Changed", timestampMs, packagePath), new Map(dataStoreAliasMap));
|
|
277
280
|
this.blobManager = new blobManager_1.BlobManager(this.handleContext, blobManagerSnapshot, () => this.storage, (blobId, localId) => {
|
|
278
281
|
if (!this.disposed) {
|
|
279
282
|
this.submit(ContainerMessageType.BlobAttach, undefined, undefined, { blobId, localId });
|
|
280
283
|
}
|
|
281
284
|
}, (blobPath) => this.garbageCollector.nodeUpdated(blobPath, "Loaded"), this, pendingRuntimeState === null || pendingRuntimeState === void 0 ? void 0 : pendingRuntimeState.pendingAttachmentBlobs);
|
|
282
285
|
this.scheduleManager = new scheduleManager_1.ScheduleManager(context.deltaManager, this, () => this.clientId, telemetry_utils_1.ChildLogger.create(this.logger, "ScheduleManager"));
|
|
283
|
-
this.deltaSender = this.deltaManager;
|
|
284
286
|
this.pendingStateManager = new pendingStateManager_1.PendingStateManager({
|
|
285
287
|
applyStashedOp: this.applyStashedOp.bind(this),
|
|
286
288
|
clientId: () => this.clientId,
|
|
287
289
|
close: this.closeFn,
|
|
288
290
|
connected: () => this.connected,
|
|
289
291
|
flush: this.flush.bind(this),
|
|
290
|
-
flushMode: () => this.flushMode,
|
|
291
292
|
reSubmit: this.reSubmit.bind(this),
|
|
292
|
-
|
|
293
|
-
|
|
293
|
+
rollback: this.rollback.bind(this),
|
|
294
|
+
orderSequentially: this.orderSequentially.bind(this),
|
|
295
|
+
}, pendingRuntimeState === null || pendingRuntimeState === void 0 ? void 0 : pendingRuntimeState.pending);
|
|
296
|
+
this.outbox = new opLifecycle_1.Outbox({
|
|
297
|
+
shouldSend: () => this.canSendOps(),
|
|
298
|
+
pendingStateManager: this.pendingStateManager,
|
|
299
|
+
containerContext: this.context,
|
|
300
|
+
compressor: new opLifecycle_1.OpCompressor(this.mc.logger),
|
|
301
|
+
config: {
|
|
302
|
+
compressionOptions: runtimeOptions.compressionOptions,
|
|
303
|
+
maxBatchSizeInBytes: runtimeOptions.maxBatchSizeInBytes,
|
|
304
|
+
},
|
|
305
|
+
});
|
|
294
306
|
this.context.quorum.on("removeMember", (clientId) => {
|
|
295
|
-
this.
|
|
307
|
+
this.remoteMessageProcessor.clearPartialMessagesFor(clientId);
|
|
296
308
|
});
|
|
297
309
|
this.summaryCollection = new summaryCollection_1.SummaryCollection(this.deltaManager, this.logger);
|
|
298
310
|
this.dirtyContainer = this.context.attachState !== container_definitions_1.AttachState.Attached
|
|
@@ -314,7 +326,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
314
326
|
// if summaries are enabled and we are not the summarizer client.
|
|
315
327
|
const defaultAction = () => {
|
|
316
328
|
if (this.summaryCollection.opsSinceLastAck > this.maxOpsSinceLastSummary) {
|
|
317
|
-
this.logger.
|
|
329
|
+
this.logger.sendTelemetryEvent({ eventName: "SummaryStatus:Behind" });
|
|
318
330
|
// unregister default to no log on every op after falling behind
|
|
319
331
|
// and register summary ack handler to re-register this handler
|
|
320
332
|
// after successful summary
|
|
@@ -360,26 +372,6 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
360
372
|
});
|
|
361
373
|
// logging hardware telemetry
|
|
362
374
|
logger.sendTelemetryEvent(Object.assign({ eventName: "DeviceSpec" }, getDeviceSpec()));
|
|
363
|
-
let loadSummaryNumber;
|
|
364
|
-
// Get the container creation metadata. For new container, we initialize these. For existing containers,
|
|
365
|
-
// get the values from the metadata blob.
|
|
366
|
-
if (existing) {
|
|
367
|
-
this.createContainerMetadata = {
|
|
368
|
-
createContainerRuntimeVersion: metadata === null || metadata === void 0 ? void 0 : metadata.createContainerRuntimeVersion,
|
|
369
|
-
createContainerTimestamp: metadata === null || metadata === void 0 ? void 0 : metadata.createContainerTimestamp,
|
|
370
|
-
};
|
|
371
|
-
// summaryNumber was renamed from summaryCount. For older docs that haven't been opened for a long time,
|
|
372
|
-
// the count is reset to 0.
|
|
373
|
-
loadSummaryNumber = (_d = metadata === null || metadata === void 0 ? void 0 : metadata.summaryNumber) !== null && _d !== void 0 ? _d : 0;
|
|
374
|
-
}
|
|
375
|
-
else {
|
|
376
|
-
this.createContainerMetadata = {
|
|
377
|
-
createContainerRuntimeVersion: packageVersion_1.pkgVersion,
|
|
378
|
-
createContainerTimestamp: Date.now(),
|
|
379
|
-
};
|
|
380
|
-
loadSummaryNumber = 0;
|
|
381
|
-
}
|
|
382
|
-
this.nextSummaryNumber = loadSummaryNumber + 1;
|
|
383
375
|
this.logger.sendTelemetryEvent(Object.assign(Object.assign(Object.assign({ eventName: "ContainerLoadStats" }, this.createContainerMetadata), this.dataStores.containerLoadStats), { summaryNumber: loadSummaryNumber, summaryFormatVersion: metadata === null || metadata === void 0 ? void 0 : metadata.summaryFormatVersion, disableIsolatedChannels: metadata === null || metadata === void 0 ? void 0 : metadata.disableIsolatedChannels, gcVersion: metadata === null || metadata === void 0 ? void 0 : metadata.gcFeature }));
|
|
384
376
|
(0, connectionTelemetry_1.ReportOpPerfTelemetry)(this.context.clientId, this.deltaManager, this.logger);
|
|
385
377
|
(0, batchTracker_1.BindBatchTracker)(this, this.logger);
|
|
@@ -393,8 +385,10 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
393
385
|
* @param requestHandler - Request handlers for the container runtime
|
|
394
386
|
* @param runtimeOptions - Additional options to be passed to the runtime
|
|
395
387
|
* @param existing - (optional) When loading from an existing snapshot. Precedes context.existing if provided
|
|
388
|
+
* @param containerRuntimeCtor - (optional) Constructor to use to create the ContainerRuntime instance. This
|
|
389
|
+
* allows mixin classes to leverage this method to define their own async initializer.
|
|
396
390
|
*/
|
|
397
|
-
static async load(context, registryEntries, requestHandler, runtimeOptions = {}, containerScope = context.scope, existing) {
|
|
391
|
+
static async load(context, registryEntries, requestHandler, runtimeOptions = {}, containerScope = context.scope, existing, containerRuntimeCtor = ContainerRuntime) {
|
|
398
392
|
var _a, _b, _c;
|
|
399
393
|
// If taggedLogger exists, use it. Otherwise, wrap the vanilla logger:
|
|
400
394
|
// back-compat: Remove the TaggedLoggerAdapter fallback once all the host are using loader > 0.45
|
|
@@ -405,7 +399,10 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
405
399
|
runtimeVersion: packageVersion_1.pkgVersion,
|
|
406
400
|
},
|
|
407
401
|
});
|
|
408
|
-
const { summaryOptions = {}, gcOptions = {}, loadSequenceNumberVerification = "close", flushMode = defaultFlushMode, enableOfflineLoad = false,
|
|
402
|
+
const { summaryOptions = {}, gcOptions = {}, loadSequenceNumberVerification = "close", flushMode = defaultFlushMode, enableOfflineLoad = false, compressionOptions = {
|
|
403
|
+
minimumBatchSizeInBytes: Number.POSITIVE_INFINITY,
|
|
404
|
+
compressionAlgorithm: CompressionAlgorithms.lz4
|
|
405
|
+
}, maxBatchSizeInBytes = defaultMaxBatchSizeInBytes, } = runtimeOptions;
|
|
409
406
|
const pendingRuntimeState = context.pendingLocalState;
|
|
410
407
|
const baseSnapshot = (_b = pendingRuntimeState === null || pendingRuntimeState === void 0 ? void 0 : pendingRuntimeState.baseSnapshot) !== null && _b !== void 0 ? _b : context.baseSnapshot;
|
|
411
408
|
const storage = !pendingRuntimeState ?
|
|
@@ -454,19 +451,22 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
454
451
|
}
|
|
455
452
|
}
|
|
456
453
|
}
|
|
457
|
-
const runtime = new
|
|
454
|
+
const runtime = new containerRuntimeCtor(context, registry, metadata, electedSummarizerData, chunks !== null && chunks !== void 0 ? chunks : [], aliases !== null && aliases !== void 0 ? aliases : [], {
|
|
458
455
|
summaryOptions,
|
|
459
456
|
gcOptions,
|
|
460
457
|
loadSequenceNumberVerification,
|
|
461
458
|
flushMode,
|
|
462
459
|
enableOfflineLoad,
|
|
460
|
+
compressionOptions,
|
|
461
|
+
maxBatchSizeInBytes,
|
|
463
462
|
}, containerScope, logger, loadExisting, blobManagerSnapshot, storage, requestHandler);
|
|
464
463
|
if (pendingRuntimeState) {
|
|
465
464
|
await runtime.processSavedOps(pendingRuntimeState);
|
|
466
465
|
// delete these once runtime has seen them to save space
|
|
467
466
|
pendingRuntimeState.savedOps = [];
|
|
468
467
|
}
|
|
469
|
-
|
|
468
|
+
// Initialize the base state of the runtime before it's returned.
|
|
469
|
+
await runtime.initializeBaseState();
|
|
470
470
|
return runtime;
|
|
471
471
|
}
|
|
472
472
|
get options() {
|
|
@@ -515,9 +515,6 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
515
515
|
return (_a = this.summarizerClientElection) === null || _a === void 0 ? void 0 : _a.electedClientId;
|
|
516
516
|
}
|
|
517
517
|
get disposed() { return this._disposed; }
|
|
518
|
-
get emptyBatch() {
|
|
519
|
-
return this.pendingBatch.empty && this.pendingAttachBatch.empty;
|
|
520
|
-
}
|
|
521
518
|
get summarizer() {
|
|
522
519
|
(0, common_utils_1.assert)(this._summarizer !== undefined, 0x257 /* "This is not summarizing container" */);
|
|
523
520
|
return this._summarizer;
|
|
@@ -573,6 +570,13 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
573
570
|
? this.summaryConfiguration.initialSummarizerDelayMs
|
|
574
571
|
: 0;
|
|
575
572
|
}
|
|
573
|
+
/**
|
|
574
|
+
* Initializes the state from the base snapshot this container runtime loaded from.
|
|
575
|
+
*/
|
|
576
|
+
async initializeBaseState() {
|
|
577
|
+
await this.initializeBaseSnapshotBlobs();
|
|
578
|
+
await this.garbageCollector.initializeBaseState();
|
|
579
|
+
}
|
|
576
580
|
dispose(error) {
|
|
577
581
|
var _a;
|
|
578
582
|
if (this._disposed) {
|
|
@@ -671,13 +675,16 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
671
675
|
return (_a = this.dataStores.aliases.get(maybeAlias)) !== null && _a !== void 0 ? _a : maybeAlias;
|
|
672
676
|
}
|
|
673
677
|
async getDataStoreFromRequest(id, request) {
|
|
674
|
-
var _a, _b, _c;
|
|
678
|
+
var _a, _b, _c, _d, _e;
|
|
675
679
|
const wait = typeof ((_a = request.headers) === null || _a === void 0 ? void 0 : _a[RuntimeHeaders.wait]) === "boolean"
|
|
676
680
|
? (_b = request.headers) === null || _b === void 0 ? void 0 : _b[RuntimeHeaders.wait]
|
|
677
681
|
: true;
|
|
682
|
+
const viaHandle = typeof ((_c = request.headers) === null || _c === void 0 ? void 0 : _c[RuntimeHeaders.viaHandle]) === "boolean"
|
|
683
|
+
? (_d = request.headers) === null || _d === void 0 ? void 0 : _d[RuntimeHeaders.viaHandle]
|
|
684
|
+
: false;
|
|
678
685
|
await this.dataStores.waitIfPendingAlias(id);
|
|
679
686
|
const internalId = this.internalId(id);
|
|
680
|
-
const dataStoreContext = await this.dataStores.getDataStore(internalId, wait);
|
|
687
|
+
const dataStoreContext = await this.dataStores.getDataStore(internalId, wait, viaHandle);
|
|
681
688
|
/**
|
|
682
689
|
* If GC should run and this an external app request with "externalRequest" header, we need to return
|
|
683
690
|
* an error if the data store being requested is marked as unreferenced as per the data store's base
|
|
@@ -686,7 +693,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
686
693
|
* This is a workaround to handle scenarios where a data store shared with an external app is deleted
|
|
687
694
|
* and marked as unreferenced by GC. Returning an error will fail to load the data store for the app.
|
|
688
695
|
*/
|
|
689
|
-
if (((
|
|
696
|
+
if (((_e = request.headers) === null || _e === void 0 ? void 0 : _e[RuntimeHeaders.externalRequest]) && this.garbageCollector.shouldRunGC) {
|
|
690
697
|
// The data store is referenced if used routes in the base summary has a route to self.
|
|
691
698
|
// Older documents may not have used routes in the summary. They are considered referenced.
|
|
692
699
|
const usedRoutes = (await dataStoreContext.getBaseGCDetails()).usedRoutes;
|
|
@@ -715,8 +722,8 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
715
722
|
addContainerStateToSummary(summaryTree, fullTree, trackState, telemetryContext) {
|
|
716
723
|
var _a;
|
|
717
724
|
this.addMetadataToSummary(summaryTree);
|
|
718
|
-
if (this.
|
|
719
|
-
const content = JSON.stringify([...this.
|
|
725
|
+
if (this.remoteMessageProcessor.partialMessages.size > 0) {
|
|
726
|
+
const content = JSON.stringify([...this.remoteMessageProcessor.partialMessages]);
|
|
720
727
|
(0, runtime_utils_1.addBlobToSummary)(summaryTree, summaryFormat_1.chunksBlobName, content);
|
|
721
728
|
}
|
|
722
729
|
const dataStoreAliases = this.dataStores.aliases;
|
|
@@ -733,11 +740,9 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
733
740
|
if (Object.keys(blobManagerSummary.summary.tree).length > 0) {
|
|
734
741
|
(0, runtime_utils_1.addTreeToSummary)(summaryTree, summaryFormat_1.blobsTreeName, blobManagerSummary);
|
|
735
742
|
}
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
(0, runtime_utils_1.addSummarizeResultToSummary)(summaryTree, garbageCollection_1.gcTreeKey, gcSummary);
|
|
740
|
-
}
|
|
743
|
+
const gcSummary = this.garbageCollector.summarize(fullTree, trackState, telemetryContext);
|
|
744
|
+
if (gcSummary !== undefined) {
|
|
745
|
+
(0, runtime_utils_1.addSummarizeResultToSummary)(summaryTree, garbageCollectionConstants_1.gcTreeKey, gcSummary);
|
|
741
746
|
}
|
|
742
747
|
}
|
|
743
748
|
// Track how many times the container tries to reconnect with pending messages.
|
|
@@ -882,31 +887,21 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
882
887
|
process(messageArg, local) {
|
|
883
888
|
var _a;
|
|
884
889
|
this.verifyNotClosed();
|
|
885
|
-
// Do shallow copy of message, as methods below will modify it.
|
|
886
|
-
// There might be multiple container instances receiving same message
|
|
887
|
-
// We do not need to make deep copy, as each layer will just replace message.content itself,
|
|
888
|
-
// but would not modify contents details
|
|
889
|
-
let message = Object.assign({}, messageArg);
|
|
890
|
-
// back-compat: ADO #1385: eventually should become unconditional, but only for runtime messages!
|
|
891
|
-
// System message may have no contents, or in some cases (mostly for back-compat) they may have actual objects.
|
|
892
|
-
// Old ops may contain empty string (I assume noops).
|
|
893
|
-
if (typeof message.contents === "string" && message.contents !== "") {
|
|
894
|
-
message.contents = JSON.parse(message.contents);
|
|
895
|
-
}
|
|
896
|
-
// Caveat: This will return false for runtime message in very old format, that are used in snapshot tests
|
|
897
|
-
// This format was not shipped to production workflows.
|
|
898
|
-
const runtimeMessage = unpackRuntimeMessage(message);
|
|
899
890
|
if ((_a = this.mc.config.getBoolean("enableOfflineLoad")) !== null && _a !== void 0 ? _a : this.runtimeOptions.enableOfflineLoad) {
|
|
900
891
|
this.savedOps.push(messageArg);
|
|
901
892
|
}
|
|
893
|
+
// Whether or not the message is actually a runtime message.
|
|
894
|
+
// It may be a legacy runtime message (ie already unpacked and ContainerMessageType)
|
|
895
|
+
// or something different, like a system message.
|
|
896
|
+
const runtimeMessage = messageArg.type === protocol_definitions_1.MessageType.Operation;
|
|
897
|
+
// Do shallow copy of message, as the processing flow will modify it.
|
|
898
|
+
const messageCopy = Object.assign({}, messageArg);
|
|
899
|
+
const message = this.remoteMessageProcessor.process(messageCopy);
|
|
902
900
|
// Surround the actual processing of the operation with messages to the schedule manager indicating
|
|
903
901
|
// the beginning and end. This allows it to emit appropriate events and/or pause the processing of new
|
|
904
902
|
// messages once a batch has been fully processed.
|
|
905
903
|
this.scheduleManager.beforeOpProcessing(message);
|
|
906
904
|
try {
|
|
907
|
-
// Chunk processing must come first given that we will transform the message to the unchunked version
|
|
908
|
-
// once all pieces are available
|
|
909
|
-
message = this.processRemoteChunkedMessage(message);
|
|
910
905
|
let localOpMetadata;
|
|
911
906
|
if (local && runtimeMessage) {
|
|
912
907
|
localOpMetadata = this.pendingStateManager.processPendingLocalMessage(message);
|
|
@@ -1010,112 +1005,31 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1010
1005
|
async getRootDataStoreChannel(id, wait = true) {
|
|
1011
1006
|
await this.dataStores.waitIfPendingAlias(id);
|
|
1012
1007
|
const internalId = this.internalId(id);
|
|
1013
|
-
const context = await this.dataStores.getDataStore(internalId, wait);
|
|
1008
|
+
const context = await this.dataStores.getDataStore(internalId, wait, false /* viaHandle */);
|
|
1014
1009
|
(0, common_utils_1.assert)(await context.isRoot(), 0x12b /* "did not get root data store" */);
|
|
1015
1010
|
return context.realize();
|
|
1016
1011
|
}
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
this.mc.logger.sendTelemetryEvent({
|
|
1022
|
-
eventName: "FlushMode Updated",
|
|
1023
|
-
old: this._flushMode,
|
|
1024
|
-
new: mode,
|
|
1025
|
-
});
|
|
1026
|
-
// Flush any pending batches if switching to immediate
|
|
1027
|
-
if (mode === runtime_definitions_1.FlushMode.Immediate) {
|
|
1028
|
-
this.flush();
|
|
1029
|
-
}
|
|
1030
|
-
this._flushMode = mode;
|
|
1031
|
-
// Let the PendingStateManager know that FlushMode has been updated.
|
|
1032
|
-
this.pendingStateManager.onFlushModeUpdated(mode);
|
|
1033
|
-
}
|
|
1012
|
+
/**
|
|
1013
|
+
* Flush the pending ops manually.
|
|
1014
|
+
* This method is expected to be called at the end of a batch.
|
|
1015
|
+
*/
|
|
1034
1016
|
flush() {
|
|
1035
1017
|
(0, common_utils_1.assert)(this._orderSequentiallyCalls === 0, 0x24c /* "Cannot call `flush()` from `orderSequentially`'s callback" */);
|
|
1036
|
-
this.
|
|
1037
|
-
|
|
1038
|
-
(0, common_utils_1.assert)(this.emptyBatch, 0x3cf /* reentrancy */);
|
|
1039
|
-
}
|
|
1040
|
-
flushBatch(batch) {
|
|
1041
|
-
const length = batch.length;
|
|
1042
|
-
if (length > 1) {
|
|
1043
|
-
batch[0].metadata = Object.assign(Object.assign({}, batch[0].metadata), { batch: true });
|
|
1044
|
-
batch[length - 1].metadata = Object.assign(Object.assign({}, batch[length - 1].metadata), { batch: false });
|
|
1045
|
-
// This assert fires for the following reason (there might be more cases like that):
|
|
1046
|
-
// AgentScheduler will send ops in response to ConsensusRegisterCollection's "atomicChanged" event handler,
|
|
1047
|
-
// i.e. in the middle of op processing!
|
|
1048
|
-
// Sending ops while processing ops is not good idea - it's not defined when
|
|
1049
|
-
// referenceSequenceNumber changes in op processing sequence (at the beginning or end of op processing),
|
|
1050
|
-
// If we send ops in response to processing multiple ops, then we for sure hit this assert!
|
|
1051
|
-
// Tracked via ADO #1834
|
|
1052
|
-
// assert(batch[0].referenceSequenceNumber === batch[length - 1].referenceSequenceNumber,
|
|
1053
|
-
// "Batch should be generated synchronously, without processing ops in the middle!");
|
|
1054
|
-
}
|
|
1055
|
-
let clientSequenceNumber = -1;
|
|
1056
|
-
// Did we disconnect in the middle of turn-based batch?
|
|
1057
|
-
// If so, do nothing, as pending state manager will resubmit it correctly on reconnect.
|
|
1058
|
-
if (this.canSendOps()) {
|
|
1059
|
-
if (this.context.submitBatchFn !== undefined) {
|
|
1060
|
-
const batchToSend = [];
|
|
1061
|
-
for (const message of batch) {
|
|
1062
|
-
batchToSend.push({ contents: message.contents, metadata: message.metadata });
|
|
1063
|
-
}
|
|
1064
|
-
// returns clientSequenceNumber of last message in a batch
|
|
1065
|
-
clientSequenceNumber = this.context.submitBatchFn(batchToSend);
|
|
1066
|
-
}
|
|
1067
|
-
else {
|
|
1068
|
-
// Legacy path - supporting old loader versions. Can be removed only when LTS moves above
|
|
1069
|
-
// version that has support for batches (submitBatchFn)
|
|
1070
|
-
for (const message of batch) {
|
|
1071
|
-
clientSequenceNumber = this.context.submitFn(protocol_definitions_1.MessageType.Operation, message.deserializedContent, true, // batch
|
|
1072
|
-
message.metadata);
|
|
1073
|
-
}
|
|
1074
|
-
this.deltaSender.flush();
|
|
1075
|
-
}
|
|
1076
|
-
// Convert from clientSequenceNumber of last message in the batch to clientSequenceNumber of first message.
|
|
1077
|
-
clientSequenceNumber -= batch.length - 1;
|
|
1078
|
-
(0, common_utils_1.assert)(clientSequenceNumber >= 0, 0x3d0 /* clientSequenceNumber can't be negative */);
|
|
1079
|
-
}
|
|
1080
|
-
// Let the PendingStateManager know that a message was submitted.
|
|
1081
|
-
// In future, need to shift toward keeping batch as a whole!
|
|
1082
|
-
for (const message of batch) {
|
|
1083
|
-
this.pendingStateManager.onSubmitMessage(message.deserializedContent.type, clientSequenceNumber, message.referenceSequenceNumber, message.deserializedContent.contents, message.localOpMetadata, message.metadata);
|
|
1084
|
-
clientSequenceNumber++;
|
|
1085
|
-
}
|
|
1086
|
-
this.pendingStateManager.onFlush();
|
|
1018
|
+
this.outbox.flush();
|
|
1019
|
+
(0, common_utils_1.assert)(this.outbox.isEmpty, 0x3cf /* reentrancy */);
|
|
1087
1020
|
}
|
|
1088
1021
|
orderSequentially(callback) {
|
|
1089
|
-
// If flush mode is already TurnBased we are either
|
|
1090
|
-
// nested in another orderSequentially, or
|
|
1091
|
-
// the app is flushing manually, in which
|
|
1092
|
-
// case this invocation doesn't own
|
|
1093
|
-
// flushing.
|
|
1094
|
-
if (this.flushMode === runtime_definitions_1.FlushMode.TurnBased) {
|
|
1095
|
-
this.trackOrderSequentiallyCalls(callback);
|
|
1096
|
-
return;
|
|
1097
|
-
}
|
|
1098
|
-
const savedFlushMode = this.flushMode;
|
|
1099
|
-
this.setFlushMode(runtime_definitions_1.FlushMode.TurnBased);
|
|
1100
|
-
try {
|
|
1101
|
-
this.trackOrderSequentiallyCalls(callback);
|
|
1102
|
-
this.flush();
|
|
1103
|
-
}
|
|
1104
|
-
finally {
|
|
1105
|
-
this.setFlushMode(savedFlushMode);
|
|
1106
|
-
}
|
|
1107
|
-
}
|
|
1108
|
-
trackOrderSequentiallyCalls(callback) {
|
|
1109
1022
|
let checkpoint;
|
|
1023
|
+
let result;
|
|
1110
1024
|
if (this.mc.config.getBoolean("Fluid.ContainerRuntime.EnableRollback")) {
|
|
1111
1025
|
// Note: we are not touching this.pendingAttachBatch here, for two reasons:
|
|
1112
1026
|
// 1. It would not help, as we flush attach ops as they become available.
|
|
1113
1027
|
// 2. There is no way to undo process of data store creation.
|
|
1114
|
-
checkpoint = this.
|
|
1028
|
+
checkpoint = this.outbox.checkpoint().mainBatch;
|
|
1115
1029
|
}
|
|
1116
1030
|
try {
|
|
1117
1031
|
this._orderSequentiallyCalls++;
|
|
1118
|
-
callback();
|
|
1032
|
+
result = callback();
|
|
1119
1033
|
}
|
|
1120
1034
|
catch (error) {
|
|
1121
1035
|
if (checkpoint) {
|
|
@@ -1140,6 +1054,10 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1140
1054
|
finally {
|
|
1141
1055
|
this._orderSequentiallyCalls--;
|
|
1142
1056
|
}
|
|
1057
|
+
if (this.flushMode === runtime_definitions_1.FlushMode.Immediate && this._orderSequentiallyCalls === 0) {
|
|
1058
|
+
this.flush();
|
|
1059
|
+
}
|
|
1060
|
+
return result;
|
|
1143
1061
|
}
|
|
1144
1062
|
async createDataStore(pkg) {
|
|
1145
1063
|
const internalId = (0, uuid_1.v4)();
|
|
@@ -1166,6 +1084,12 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1166
1084
|
canSendOps() {
|
|
1167
1085
|
return this.connected && !this.deltaManager.readOnlyInfo.readonly;
|
|
1168
1086
|
}
|
|
1087
|
+
/**
|
|
1088
|
+
* Are we in the middle of batching ops together?
|
|
1089
|
+
*/
|
|
1090
|
+
currentlyBatching() {
|
|
1091
|
+
return this.flushMode === runtime_definitions_1.FlushMode.TurnBased || this._orderSequentiallyCalls !== 0;
|
|
1092
|
+
}
|
|
1169
1093
|
getQuorum() {
|
|
1170
1094
|
return this.context.quorum;
|
|
1171
1095
|
}
|
|
@@ -1316,28 +1240,33 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1316
1240
|
* Implementation of IGarbageCollectionRuntime::updateUsedRoutes.
|
|
1317
1241
|
* After GC has run, called to notify this container's nodes of routes that are used in it.
|
|
1318
1242
|
* @param usedRoutes - The routes that are used in all nodes in this Container.
|
|
1319
|
-
* @param gcTimestamp - The time when GC was run that generated these used routes. If any node node becomes
|
|
1320
|
-
* unreferenced as part of this GC run, this should be used to update the time when it happens.
|
|
1321
1243
|
*/
|
|
1322
|
-
updateUsedRoutes(usedRoutes
|
|
1244
|
+
updateUsedRoutes(usedRoutes) {
|
|
1323
1245
|
// Update our summarizer node's used routes. Updating used routes in summarizer node before
|
|
1324
1246
|
// summarizing is required and asserted by the the summarizer node. We are the root and are
|
|
1325
1247
|
// always referenced, so the used routes is only self-route (empty string).
|
|
1326
1248
|
this.summarizerNode.updateUsedRoutes([""]);
|
|
1249
|
+
const blobManagerUsedRoutes = [];
|
|
1327
1250
|
const dataStoreUsedRoutes = [];
|
|
1328
1251
|
for (const route of usedRoutes) {
|
|
1329
|
-
if (
|
|
1252
|
+
if (this.isBlobPath(route)) {
|
|
1253
|
+
blobManagerUsedRoutes.push(route);
|
|
1254
|
+
}
|
|
1255
|
+
else {
|
|
1330
1256
|
dataStoreUsedRoutes.push(route);
|
|
1331
1257
|
}
|
|
1332
1258
|
}
|
|
1333
|
-
|
|
1259
|
+
this.blobManager.updateUsedRoutes(blobManagerUsedRoutes);
|
|
1260
|
+
this.dataStores.updateUsedRoutes(dataStoreUsedRoutes);
|
|
1334
1261
|
}
|
|
1335
1262
|
/**
|
|
1336
|
-
*
|
|
1337
|
-
*
|
|
1338
|
-
* @param unusedRoutes - The routes that are unused in all data stores in this Container.
|
|
1263
|
+
* This is called to update objects whose routes are unused. The unused objects are either deleted or marked as
|
|
1264
|
+
* tombstones.
|
|
1265
|
+
* @param unusedRoutes - The routes that are unused in all data stores and attachment blobs in this Container.
|
|
1266
|
+
* @param tombstone - if true, the objects corresponding to unused routes are marked tombstones. Otherwise, they
|
|
1267
|
+
* are deleted.
|
|
1339
1268
|
*/
|
|
1340
|
-
|
|
1269
|
+
updateUnusedRoutes(unusedRoutes, tombstone) {
|
|
1341
1270
|
const blobManagerUnusedRoutes = [];
|
|
1342
1271
|
const dataStoreUnusedRoutes = [];
|
|
1343
1272
|
for (const route of unusedRoutes) {
|
|
@@ -1348,8 +1277,8 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1348
1277
|
dataStoreUnusedRoutes.push(route);
|
|
1349
1278
|
}
|
|
1350
1279
|
}
|
|
1351
|
-
this.blobManager.
|
|
1352
|
-
this.dataStores.
|
|
1280
|
+
this.blobManager.updateUnusedRoutes(blobManagerUnusedRoutes, tombstone);
|
|
1281
|
+
this.dataStores.updateUnusedRoutes(dataStoreUnusedRoutes, tombstone);
|
|
1353
1282
|
}
|
|
1354
1283
|
/**
|
|
1355
1284
|
* Returns a server generated referenced timestamp to be used to track unreferenced nodes by GC.
|
|
@@ -1429,21 +1358,14 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1429
1358
|
const summaryNumberLogger = telemetry_utils_1.ChildLogger.create(summaryLogger, undefined, {
|
|
1430
1359
|
all: { summaryNumber },
|
|
1431
1360
|
});
|
|
1432
|
-
(0, common_utils_1.assert)(this.
|
|
1361
|
+
(0, common_utils_1.assert)(this.outbox.isEmpty, 0x3d1 /* Can't trigger summary in the middle of a batch */);
|
|
1433
1362
|
let latestSnapshotVersionId;
|
|
1434
1363
|
if (refreshLatestAck) {
|
|
1435
1364
|
const latestSnapshotInfo = await this.refreshLatestSummaryAckFromServer(telemetry_utils_1.ChildLogger.create(summaryNumberLogger, undefined, { all: { safeSummary: true } }));
|
|
1436
1365
|
const latestSnapshotRefSeq = latestSnapshotInfo.latestSnapshotRefSeq;
|
|
1437
1366
|
latestSnapshotVersionId = latestSnapshotInfo.latestSnapshotVersionId;
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
await telemetry_utils_1.PerformanceEvent.timedExecAsync(summaryNumberLogger, {
|
|
1441
|
-
eventName: "WaitingForSeq",
|
|
1442
|
-
lastSequenceNumber: this.deltaManager.lastSequenceNumber,
|
|
1443
|
-
targetSequenceNumber: latestSnapshotRefSeq,
|
|
1444
|
-
lastKnownSeqNumber: this.deltaManager.lastKnownSeqNumber,
|
|
1445
|
-
}, async () => waitForSeq(this.deltaManager, latestSnapshotRefSeq), { start: true, end: true, cancel: "error" });
|
|
1446
|
-
}
|
|
1367
|
+
// We might need to catch up to the latest summary's reference sequence number before pausing.
|
|
1368
|
+
await this.waitForDeltaManagerToCatchup(latestSnapshotRefSeq, summaryNumberLogger);
|
|
1447
1369
|
}
|
|
1448
1370
|
try {
|
|
1449
1371
|
await this.deltaManager.inbound.pause();
|
|
@@ -1527,8 +1449,8 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1527
1449
|
const dataStoreTree = summaryTree.tree[runtime_definitions_1.channelsTreeName];
|
|
1528
1450
|
(0, common_utils_1.assert)(dataStoreTree.type === protocol_definitions_1.SummaryType.Tree, 0x1fc /* "summary is not a tree" */);
|
|
1529
1451
|
const handleCount = Object.values(dataStoreTree.tree).filter((value) => value.type === protocol_definitions_1.SummaryType.Handle).length;
|
|
1530
|
-
const gcSummaryTreeStats = summaryTree.tree[
|
|
1531
|
-
? (0, runtime_utils_1.calculateStats)(summaryTree.tree[
|
|
1452
|
+
const gcSummaryTreeStats = summaryTree.tree[garbageCollectionConstants_1.gcTreeKey]
|
|
1453
|
+
? (0, runtime_utils_1.calculateStats)(summaryTree.tree[garbageCollectionConstants_1.gcTreeKey])
|
|
1532
1454
|
: undefined;
|
|
1533
1455
|
const summaryStats = Object.assign({ dataStoreCount: this.dataStores.size, summarizedDataStoreCount: this.dataStores.size - handleCount, gcStateUpdatedDataStoreCount: (_a = summarizeResult.gcStats) === null || _a === void 0 ? void 0 : _a.updatedDataStoreCount, gcBlobNodeCount: gcSummaryTreeStats === null || gcSummaryTreeStats === void 0 ? void 0 : gcSummaryTreeStats.blobNodeCount, gcTotalBlobsSize: gcSummaryTreeStats === null || gcSummaryTreeStats === void 0 ? void 0 : gcSummaryTreeStats.totalBlobSize, summaryNumber }, partialStats);
|
|
1534
1456
|
const generateSummaryData = {
|
|
@@ -1609,40 +1531,8 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1609
1531
|
this.deltaManager.inbound.resume();
|
|
1610
1532
|
}
|
|
1611
1533
|
}
|
|
1612
|
-
processRemoteChunkedMessage(message) {
|
|
1613
|
-
if (message.type !== ContainerMessageType.ChunkedOp) {
|
|
1614
|
-
return message;
|
|
1615
|
-
}
|
|
1616
|
-
const clientId = message.clientId;
|
|
1617
|
-
const chunkedContent = message.contents;
|
|
1618
|
-
this.addChunk(clientId, chunkedContent);
|
|
1619
|
-
if (chunkedContent.chunkId === chunkedContent.totalChunks) {
|
|
1620
|
-
const newMessage = Object.assign({}, message);
|
|
1621
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
1622
|
-
const serializedContent = this.chunkMap.get(clientId).join("");
|
|
1623
|
-
newMessage.contents = JSON.parse(serializedContent);
|
|
1624
|
-
newMessage.type = chunkedContent.originalType;
|
|
1625
|
-
this.clearPartialChunks(clientId);
|
|
1626
|
-
return newMessage;
|
|
1627
|
-
}
|
|
1628
|
-
return message;
|
|
1629
|
-
}
|
|
1630
|
-
addChunk(clientId, chunkedContent) {
|
|
1631
|
-
let map = this.chunkMap.get(clientId);
|
|
1632
|
-
if (map === undefined) {
|
|
1633
|
-
map = [];
|
|
1634
|
-
this.chunkMap.set(clientId, map);
|
|
1635
|
-
}
|
|
1636
|
-
(0, common_utils_1.assert)(chunkedContent.chunkId === map.length + 1, 0x131 /* "Mismatch between new chunkId and expected chunkMap" */); // 1-based indexing
|
|
1637
|
-
map.push(chunkedContent.contents);
|
|
1638
|
-
}
|
|
1639
|
-
clearPartialChunks(clientId) {
|
|
1640
|
-
if (this.chunkMap.has(clientId)) {
|
|
1641
|
-
this.chunkMap.delete(clientId);
|
|
1642
|
-
}
|
|
1643
|
-
}
|
|
1644
1534
|
hasPendingMessages() {
|
|
1645
|
-
return this.pendingStateManager.hasPendingMessages() || !this.
|
|
1535
|
+
return this.pendingStateManager.hasPendingMessages() || !this.outbox.isEmpty;
|
|
1646
1536
|
}
|
|
1647
1537
|
updateDocumentDirtyState(dirty) {
|
|
1648
1538
|
if (this.attachState !== container_definitions_1.AttachState.Attached) {
|
|
@@ -1686,7 +1576,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1686
1576
|
const deserializedContent = { type, contents };
|
|
1687
1577
|
const serializedContent = JSON.stringify(deserializedContent);
|
|
1688
1578
|
if (this.deltaManager.readOnlyInfo.readonly) {
|
|
1689
|
-
this.logger.
|
|
1579
|
+
this.logger.sendTelemetryEvent({ eventName: "SubmitOpInReadonly", connected: this.connected });
|
|
1690
1580
|
}
|
|
1691
1581
|
const message = {
|
|
1692
1582
|
contents: serializedContent,
|
|
@@ -1716,44 +1606,24 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1716
1606
|
// issue than sending.
|
|
1717
1607
|
// Please note that this does not change file format, so it can be disabled in the future if this
|
|
1718
1608
|
// optimization no longer makes sense (for example, batch compression may make it less appealing).
|
|
1719
|
-
if (type === ContainerMessageType.Attach &&
|
|
1609
|
+
if (this.currentlyBatching() && type === ContainerMessageType.Attach &&
|
|
1720
1610
|
this.mc.config.getBoolean("Fluid.ContainerRuntime.disableAttachOpReorder") !== true) {
|
|
1721
|
-
|
|
1722
|
-
// BatchManager has two limits - soft limit & hard limit. Soft limit is only engaged
|
|
1723
|
-
// when queue is not empty.
|
|
1724
|
-
// Flush queue & retry. Failure on retry would mean - single message is bigger than hard limit
|
|
1725
|
-
this.flushBatch(this.pendingAttachBatch.popBatch());
|
|
1726
|
-
if (!this.pendingAttachBatch.push(message)) {
|
|
1727
|
-
throw new container_utils_1.GenericError("BatchTooLarge",
|
|
1728
|
-
/* error */ undefined, {
|
|
1729
|
-
opSize: message.contents.length,
|
|
1730
|
-
count: this.pendingAttachBatch.length,
|
|
1731
|
-
limit: this.pendingAttachBatch.limit,
|
|
1732
|
-
});
|
|
1733
|
-
}
|
|
1734
|
-
}
|
|
1611
|
+
this.outbox.submitAttach(message);
|
|
1735
1612
|
}
|
|
1736
1613
|
else {
|
|
1737
|
-
|
|
1738
|
-
throw new container_utils_1.GenericError("BatchTooLarge",
|
|
1739
|
-
/* error */ undefined, {
|
|
1740
|
-
opSize: message.contents.length,
|
|
1741
|
-
count: this.pendingBatch.length,
|
|
1742
|
-
limit: this.pendingBatch.limit,
|
|
1743
|
-
});
|
|
1744
|
-
}
|
|
1614
|
+
this.outbox.submit(message);
|
|
1745
1615
|
}
|
|
1746
|
-
if (this.
|
|
1616
|
+
if (!this.currentlyBatching()) {
|
|
1747
1617
|
this.flush();
|
|
1748
1618
|
}
|
|
1749
|
-
else if (!this.
|
|
1750
|
-
this.
|
|
1619
|
+
else if (!this.flushMicroTaskExists) {
|
|
1620
|
+
this.flushMicroTaskExists = true;
|
|
1751
1621
|
// Queue a microtask to detect the end of the turn and force a flush.
|
|
1752
1622
|
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
1753
1623
|
Promise.resolve().then(() => {
|
|
1754
|
-
this.
|
|
1624
|
+
this.flushMicroTaskExists = false;
|
|
1755
1625
|
this.flush();
|
|
1756
|
-
});
|
|
1626
|
+
}).catch((error) => { this.closeFn(error); });
|
|
1757
1627
|
}
|
|
1758
1628
|
}
|
|
1759
1629
|
catch (error) {
|
|
@@ -1768,7 +1638,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1768
1638
|
this.verifyNotClosed();
|
|
1769
1639
|
(0, common_utils_1.assert)(this.connected, 0x133 /* "Container disconnected when trying to submit system message" */);
|
|
1770
1640
|
// System message should not be sent in the middle of the batch.
|
|
1771
|
-
(0, common_utils_1.assert)(this.
|
|
1641
|
+
(0, common_utils_1.assert)(this.outbox.isEmpty, 0x3d4 /* System op in the middle of a batch */);
|
|
1772
1642
|
// back-compat: ADO #1385: Make this call unconditional in the future
|
|
1773
1643
|
return this.context.submitSummaryFn !== undefined
|
|
1774
1644
|
? this.context.submitSummaryFn(contents)
|
|
@@ -1823,18 +1693,41 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1823
1693
|
throw new Error(`Can't rollback ${type}`);
|
|
1824
1694
|
}
|
|
1825
1695
|
}
|
|
1696
|
+
async waitForDeltaManagerToCatchup(latestSnapshotRefSeq, summaryLogger) {
|
|
1697
|
+
if (latestSnapshotRefSeq > this.deltaManager.lastSequenceNumber) {
|
|
1698
|
+
// We need to catch up to the latest summary's reference sequence number before proceeding.
|
|
1699
|
+
await telemetry_utils_1.PerformanceEvent.timedExecAsync(summaryLogger, {
|
|
1700
|
+
eventName: "WaitingForSeq",
|
|
1701
|
+
lastSequenceNumber: this.deltaManager.lastSequenceNumber,
|
|
1702
|
+
targetSequenceNumber: latestSnapshotRefSeq,
|
|
1703
|
+
lastKnownSeqNumber: this.deltaManager.lastKnownSeqNumber,
|
|
1704
|
+
}, async () => waitForSeq(this.deltaManager, latestSnapshotRefSeq), { start: true, end: true, cancel: "error" });
|
|
1705
|
+
}
|
|
1706
|
+
}
|
|
1826
1707
|
/** Implementation of ISummarizerInternalsProvider.refreshLatestSummaryAck */
|
|
1827
|
-
async refreshLatestSummaryAck(
|
|
1708
|
+
async refreshLatestSummaryAck(options) {
|
|
1709
|
+
const { proposalHandle, ackHandle, summaryRefSeq, summaryLogger } = options;
|
|
1828
1710
|
const readAndParseBlob = async (id) => (0, driver_utils_1.readAndParse)(this.storage, id);
|
|
1829
1711
|
// The call to fetch the snapshot is very expensive and not always needed.
|
|
1830
1712
|
// It should only be done by the summarizerNode, if required.
|
|
1713
|
+
// When fetching from storage we will always get the latest version and do not use the ackHandle.
|
|
1831
1714
|
const snapshotTreeFetcher = async () => {
|
|
1832
|
-
const fetchResult = await this.fetchSnapshotFromStorage(
|
|
1715
|
+
const fetchResult = await this.fetchSnapshotFromStorage(null, summaryLogger, {
|
|
1833
1716
|
eventName: "RefreshLatestSummaryGetSnapshot",
|
|
1834
1717
|
ackHandle,
|
|
1835
1718
|
summaryRefSeq,
|
|
1836
|
-
fetchLatest:
|
|
1719
|
+
fetchLatest: true,
|
|
1720
|
+
});
|
|
1721
|
+
const latestSnapshotRefSeq = await (0, runtime_utils_1.seqFromTree)(fetchResult.snapshotTree, readAndParseBlob);
|
|
1722
|
+
summaryLogger.sendTelemetryEvent({
|
|
1723
|
+
eventName: "LatestSummaryRetrieved",
|
|
1724
|
+
ackHandle,
|
|
1725
|
+
lastSequenceNumber: latestSnapshotRefSeq,
|
|
1726
|
+
targetSequenceNumber: summaryRefSeq,
|
|
1837
1727
|
});
|
|
1728
|
+
// In case we had to retrieve the latest snapshot and it is different than summaryRefSeq,
|
|
1729
|
+
// wait for the delta manager to catch up before refreshing the latest Summary.
|
|
1730
|
+
await this.waitForDeltaManagerToCatchup(latestSnapshotRefSeq, summaryLogger);
|
|
1838
1731
|
return fetchResult.snapshotTree;
|
|
1839
1732
|
};
|
|
1840
1733
|
const result = await this.summarizerNode.refreshLatestSummary(proposalHandle, summaryRefSeq, snapshotTreeFetcher, readAndParseBlob, summaryLogger);
|
|
@@ -1879,7 +1772,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1879
1772
|
this.baseSnapshotBlobs = serializedSnapshotStorage_1.SerializedSnapshotStorage.serializeTreeWithBlobContents(snapshot);
|
|
1880
1773
|
}
|
|
1881
1774
|
}
|
|
1882
|
-
async
|
|
1775
|
+
async initializeBaseSnapshotBlobs() {
|
|
1883
1776
|
var _a;
|
|
1884
1777
|
if (!((_a = this.mc.config.getBoolean("enableOfflineLoad")) !== null && _a !== void 0 ? _a : this.runtimeOptions.enableOfflineLoad) ||
|
|
1885
1778
|
this.attachState !== container_definitions_1.AttachState.Attached || this.context.pendingLocalState) {
|
|
@@ -1897,6 +1790,9 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1897
1790
|
// getPendingLocalState() is only exposed through Container.closeAndGetPendingLocalState(), so it's safe
|
|
1898
1791
|
// to close current batch.
|
|
1899
1792
|
this.flush();
|
|
1793
|
+
if (this._orderSequentiallyCalls !== 0) {
|
|
1794
|
+
throw new container_utils_1.UsageError("can't get state during orderSequentially");
|
|
1795
|
+
}
|
|
1900
1796
|
const previousPendingState = this.context.pendingLocalState;
|
|
1901
1797
|
if (previousPendingState) {
|
|
1902
1798
|
return {
|
|
@@ -1964,6 +1860,9 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1964
1860
|
throw new container_utils_1.UsageError(`Summary heuristic configuration property "${prop}" cannot be less than 0`);
|
|
1965
1861
|
}
|
|
1966
1862
|
}
|
|
1863
|
+
if (configuration.minIdleTime > configuration.maxIdleTime) {
|
|
1864
|
+
throw new container_utils_1.UsageError(`"minIdleTime" [${configuration.minIdleTime}] cannot be greater than "maxIdleTime" [${configuration.maxIdleTime}]`);
|
|
1865
|
+
}
|
|
1967
1866
|
}
|
|
1968
1867
|
}
|
|
1969
1868
|
exports.ContainerRuntime = ContainerRuntime;
|
|
@@ -1974,12 +1873,18 @@ exports.ContainerRuntime = ContainerRuntime;
|
|
|
1974
1873
|
const waitForSeq = async (deltaManager, targetSeq) => new Promise((resolve, reject) => {
|
|
1975
1874
|
// TODO: remove cast to any when actual event is determined
|
|
1976
1875
|
deltaManager.on("closed", reject);
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1876
|
+
// If we already reached target sequence number, simply resolve the promise.
|
|
1877
|
+
if (deltaManager.lastSequenceNumber >= targetSeq) {
|
|
1878
|
+
resolve();
|
|
1879
|
+
}
|
|
1880
|
+
else {
|
|
1881
|
+
const handleOp = (message) => {
|
|
1882
|
+
if (message.sequenceNumber >= targetSeq) {
|
|
1883
|
+
resolve();
|
|
1884
|
+
deltaManager.off("op", handleOp);
|
|
1885
|
+
}
|
|
1886
|
+
};
|
|
1887
|
+
deltaManager.on("op", handleOp);
|
|
1888
|
+
}
|
|
1984
1889
|
});
|
|
1985
1890
|
//# sourceMappingURL=containerRuntime.js.map
|