@fluidframework/container-runtime 2.0.0-internal.2.1.2 → 2.0.0-internal.2.2.1
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 +39 -40
- package/dist/containerRuntime.d.ts.map +1 -1
- package/dist/containerRuntime.js +115 -278
- package/dist/containerRuntime.js.map +1 -1
- package/dist/dataStoreContext.d.ts +3 -1
- package/dist/dataStoreContext.d.ts.map +1 -1
- package/dist/dataStoreContext.js +21 -3
- package/dist/dataStoreContext.js.map +1 -1
- package/dist/dataStores.d.ts +8 -5
- package/dist/dataStores.d.ts.map +1 -1
- package/dist/dataStores.js +26 -13
- package/dist/dataStores.js.map +1 -1
- package/dist/garbageCollection.d.ts +15 -17
- package/dist/garbageCollection.d.ts.map +1 -1
- package/dist/garbageCollection.js +92 -106
- 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.js +2 -2
- package/dist/gcSweepReadyUsageDetection.js.map +1 -1
- package/dist/index.d.ts +4 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -6
- 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 -10
- 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/summaryFormat.js +2 -2
- package/dist/summaryFormat.js.map +1 -1
- 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 +39 -40
- package/lib/containerRuntime.d.ts.map +1 -1
- package/lib/containerRuntime.js +113 -275
- package/lib/containerRuntime.js.map +1 -1
- package/lib/dataStoreContext.d.ts +3 -1
- package/lib/dataStoreContext.d.ts.map +1 -1
- package/lib/dataStoreContext.js +23 -5
- package/lib/dataStoreContext.js.map +1 -1
- package/lib/dataStores.d.ts +8 -5
- package/lib/dataStores.d.ts.map +1 -1
- package/lib/dataStores.js +28 -15
- package/lib/dataStores.js.map +1 -1
- package/lib/garbageCollection.d.ts +15 -17
- package/lib/garbageCollection.d.ts.map +1 -1
- package/lib/garbageCollection.js +72 -86
- 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.js +1 -1
- package/lib/gcSweepReadyUsageDetection.js.map +1 -1
- package/lib/index.d.ts +4 -2
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +3 -2
- 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 -10
- 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/summaryFormat.js +1 -1
- package/lib/summaryFormat.js.map +1 -1
- package/package.json +22 -19
- package/prettier.config.cjs +8 -0
- package/src/blobManager.ts +74 -19
- package/src/containerRuntime.ts +144 -341
- package/src/dataStoreContext.ts +33 -5
- package/src/dataStores.ts +32 -16
- package/src/garbageCollection.ts +106 -82
- package/src/garbageCollectionConstants.ts +35 -0
- package/src/gcSweepReadyUsageDetection.ts +1 -1
- package/src/index.ts +6 -4
- package/src/{batchManager.ts → opLifecycle/batchManager.ts} +41 -23
- 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/summaryFormat.ts +1 -1
- package/dist/batchManager.d.ts +0 -36
- package/dist/batchManager.d.ts.map +0 -1
- package/dist/batchManager.js.map +0 -1
- package/lib/batchManager.d.ts +0 -36
- 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");
|
|
@@ -12,14 +12,12 @@ const runtime_definitions_1 = require("@fluidframework/runtime-definitions");
|
|
|
12
12
|
const runtime_utils_1 = require("@fluidframework/runtime-utils");
|
|
13
13
|
const garbage_collector_1 = require("@fluidframework/garbage-collector");
|
|
14
14
|
const uuid_1 = require("uuid");
|
|
15
|
-
const lz4js_1 = require("lz4js");
|
|
16
15
|
const containerHandleContext_1 = require("./containerHandleContext");
|
|
17
16
|
const dataStoreRegistry_1 = require("./dataStoreRegistry");
|
|
18
17
|
const summarizer_1 = require("./summarizer");
|
|
19
18
|
const summaryManager_1 = require("./summaryManager");
|
|
20
19
|
const connectionTelemetry_1 = require("./connectionTelemetry");
|
|
21
20
|
const pendingStateManager_1 = require("./pendingStateManager");
|
|
22
|
-
const batchManager_1 = require("./batchManager");
|
|
23
21
|
const packageVersion_1 = require("./packageVersion");
|
|
24
22
|
const blobManager_1 = require("./blobManager");
|
|
25
23
|
const dataStores_1 = require("./dataStores");
|
|
@@ -30,10 +28,12 @@ const summarizerClientElection_1 = require("./summarizerClientElection");
|
|
|
30
28
|
const throttler_1 = require("./throttler");
|
|
31
29
|
const runWhileConnectedCoordinator_1 = require("./runWhileConnectedCoordinator");
|
|
32
30
|
const garbageCollection_1 = require("./garbageCollection");
|
|
31
|
+
const garbageCollectionConstants_1 = require("./garbageCollectionConstants");
|
|
33
32
|
const dataStore_1 = require("./dataStore");
|
|
34
33
|
const batchTracker_1 = require("./batchTracker");
|
|
35
34
|
const serializedSnapshotStorage_1 = require("./serializedSnapshotStorage");
|
|
36
35
|
const scheduleManager_1 = require("./scheduleManager");
|
|
36
|
+
const opLifecycle_1 = require("./opLifecycle");
|
|
37
37
|
var ContainerMessageType;
|
|
38
38
|
(function (ContainerMessageType) {
|
|
39
39
|
// An op to be delivered to store
|
|
@@ -79,6 +79,13 @@ var RuntimeHeaders;
|
|
|
79
79
|
/** True if the request is coming from an IFluidHandle. */
|
|
80
80
|
RuntimeHeaders["viaHandle"] = "viaHandle";
|
|
81
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 = {}));
|
|
82
89
|
const maxConsecutiveReconnectsKey = "Fluid.ContainerRuntime.MaxConsecutiveReconnects";
|
|
83
90
|
const defaultFlushMode = runtime_definitions_1.FlushMode.TurnBased;
|
|
84
91
|
// The actual limit is 1Mb (socket.io and Kafka limits)
|
|
@@ -106,48 +113,6 @@ function isRuntimeMessage(message) {
|
|
|
106
113
|
return Object.values(RuntimeMessage).includes(message.type);
|
|
107
114
|
}
|
|
108
115
|
exports.isRuntimeMessage = isRuntimeMessage;
|
|
109
|
-
/**
|
|
110
|
-
* Unpacks runtime messages
|
|
111
|
-
*
|
|
112
|
-
* @remarks This API makes no promises regarding backward-compatibility. This is internal API.
|
|
113
|
-
* @param message - message (as it observed in storage / service)
|
|
114
|
-
* @returns unpacked runtime message
|
|
115
|
-
*
|
|
116
|
-
* @internal
|
|
117
|
-
*/
|
|
118
|
-
function unpackRuntimeMessage(message) {
|
|
119
|
-
var _a;
|
|
120
|
-
if ((_a = message.metadata) === null || _a === void 0 ? void 0 : _a.compressed) {
|
|
121
|
-
const contents = common_utils_1.IsoBuffer.from(message.contents.contents, "base64");
|
|
122
|
-
const decompressedMessage = (0, lz4js_1.decompress)(contents);
|
|
123
|
-
const intoString = new TextDecoder().decode(decompressedMessage);
|
|
124
|
-
const asObj = JSON.parse(intoString);
|
|
125
|
-
message.contents.contents = asObj;
|
|
126
|
-
message.metadata.compressed = false;
|
|
127
|
-
}
|
|
128
|
-
if (message.type === protocol_definitions_1.MessageType.Operation) {
|
|
129
|
-
// legacy op format?
|
|
130
|
-
if (message.contents.address !== undefined && message.contents.type === undefined) {
|
|
131
|
-
message.type = ContainerMessageType.FluidDataStoreOp;
|
|
132
|
-
}
|
|
133
|
-
else {
|
|
134
|
-
// new format
|
|
135
|
-
const innerContents = message.contents;
|
|
136
|
-
(0, common_utils_1.assert)(innerContents.type !== undefined, 0x121 /* "Undefined inner contents type!" */);
|
|
137
|
-
message.type = innerContents.type;
|
|
138
|
-
message.contents = innerContents.contents;
|
|
139
|
-
}
|
|
140
|
-
return true;
|
|
141
|
-
}
|
|
142
|
-
else {
|
|
143
|
-
// Legacy format, but it's already "unpacked",
|
|
144
|
-
// i.e. message.type is actually ContainerMessageType.
|
|
145
|
-
// Or it's non-runtime message.
|
|
146
|
-
// Nothing to do in such case.
|
|
147
|
-
return false;
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
exports.unpackRuntimeMessage = unpackRuntimeMessage;
|
|
151
116
|
/**
|
|
152
117
|
* Legacy ID for the built-in AgentScheduler. To minimize disruption while removing it, retaining this as a
|
|
153
118
|
* special-case for document dirty state. Ultimately we should have no special-cases from the
|
|
@@ -174,6 +139,9 @@ exports.getDeviceSpec = getDeviceSpec;
|
|
|
174
139
|
* It will define the store level mappings.
|
|
175
140
|
*/
|
|
176
141
|
class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
142
|
+
/**
|
|
143
|
+
* @internal
|
|
144
|
+
*/
|
|
177
145
|
constructor(context, registry, metadata, electedSummarizerData, chunks, dataStoreAliasMap, runtimeOptions, containerScope, logger, existing, blobManagerSnapshot, _storage, requestHandler, summaryConfiguration) {
|
|
178
146
|
var _a, _b, _c, _d, _e, _f;
|
|
179
147
|
if (summaryConfiguration === void 0) { summaryConfiguration = Object.assign(Object.assign({}, exports.DefaultSummaryConfiguration), (_a = runtimeOptions.summaryOptions) === null || _a === void 0 ? void 0 : _a.summaryConfigOverrides); }
|
|
@@ -191,7 +159,6 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
191
159
|
this.flushMicroTaskExists = false;
|
|
192
160
|
this.savedOps = [];
|
|
193
161
|
this.consecutiveReconnects = 0;
|
|
194
|
-
this.compressedOpCount = 0;
|
|
195
162
|
this._disposed = false;
|
|
196
163
|
this.emitDirtyDocumentEvent = true;
|
|
197
164
|
this.defaultTelemetrySignalSampleCount = 100;
|
|
@@ -229,9 +196,29 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
229
196
|
throw new container_utils_1.UsageError(`Can't summarize, disableSummaries: ${this.summariesDisabled}`);
|
|
230
197
|
}
|
|
231
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;
|
|
232
219
|
this.messageAtLastSummary = metadata === null || metadata === void 0 ? void 0 : metadata.message;
|
|
233
220
|
this._connected = this.context.connected;
|
|
234
|
-
this.
|
|
221
|
+
this.remoteMessageProcessor = new opLifecycle_1.RemoteMessageProcessor(new opLifecycle_1.OpSplitter(chunks), new opLifecycle_1.OpDecompressor());
|
|
235
222
|
this.handleContext = new containerHandleContext_1.ContainerFluidHandleContext("", this);
|
|
236
223
|
this.mc = (0, telemetry_utils_1.loggerToMonitoringContext)(telemetry_utils_1.ChildLogger.create(this.logger, "ContainerRuntime"));
|
|
237
224
|
if (this.summaryConfiguration.state === "enabled") {
|
|
@@ -243,18 +230,11 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
243
230
|
this.maxOpsSinceLastSummary = this.getMaxOpsSinceLastSummary();
|
|
244
231
|
this.initialSummarizerDelayMs = this.getInitialSummarizerDelayMs();
|
|
245
232
|
this.maxConsecutiveReconnects =
|
|
246
|
-
(
|
|
233
|
+
(_c = this.mc.config.getNumber(maxConsecutiveReconnectsKey)) !== null && _c !== void 0 ? _c : this.defaultMaxConsecutiveReconnects;
|
|
247
234
|
this._flushMode = runtimeOptions.flushMode;
|
|
248
|
-
// Provide lower soft limit - we want to have some number of ops to get efficiency in compression
|
|
249
|
-
// & bandwidth usage, but at the same time we want to send these ops sooner, to reduce overall
|
|
250
|
-
// latency of processing a batch.
|
|
251
|
-
// So there is some ballance here, that depends on compression algorithm and its efficiency working with smaller
|
|
252
|
-
// payloads. That number represents final (compressed) bits (once compression is implemented).
|
|
253
|
-
this.pendingAttachBatch = new batchManager_1.BatchManager(runtimeOptions.maxBatchSizeInBytes, 64 * 1024);
|
|
254
|
-
this.pendingBatch = new batchManager_1.BatchManager(runtimeOptions.maxBatchSizeInBytes);
|
|
255
235
|
const pendingRuntimeState = context.pendingLocalState;
|
|
256
|
-
const baseSnapshot = (
|
|
257
|
-
const maxSnapshotCacheDurationMs = (
|
|
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;
|
|
258
238
|
if (maxSnapshotCacheDurationMs !== undefined && maxSnapshotCacheDurationMs > 5 * 24 * 60 * 60 * 1000) {
|
|
259
239
|
// This is a runtime enforcement of what's already explicit in the policy's type itself,
|
|
260
240
|
// which dictates the value is either undefined or exactly 5 days in ms.
|
|
@@ -268,6 +248,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
268
248
|
baseLogger: this.mc.logger,
|
|
269
249
|
existing,
|
|
270
250
|
metadata,
|
|
251
|
+
createContainerMetadata: this.createContainerMetadata,
|
|
271
252
|
isSummarizerClient: this.context.clientDetails.type === summarizerClientElection_1.summarizerClientType,
|
|
272
253
|
getNodePackagePath: async (nodePath) => this.getGCNodePackagePath(nodePath),
|
|
273
254
|
getLastSummaryTimestampMs: () => { var _a; return (_a = this.messageAtLastSummary) === null || _a === void 0 ? void 0 : _a.timestamp; },
|
|
@@ -302,7 +283,6 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
302
283
|
}
|
|
303
284
|
}, (blobPath) => this.garbageCollector.nodeUpdated(blobPath, "Loaded"), this, pendingRuntimeState === null || pendingRuntimeState === void 0 ? void 0 : pendingRuntimeState.pendingAttachmentBlobs);
|
|
304
285
|
this.scheduleManager = new scheduleManager_1.ScheduleManager(context.deltaManager, this, () => this.clientId, telemetry_utils_1.ChildLogger.create(this.logger, "ScheduleManager"));
|
|
305
|
-
this.deltaSender = this.deltaManager;
|
|
306
286
|
this.pendingStateManager = new pendingStateManager_1.PendingStateManager({
|
|
307
287
|
applyStashedOp: this.applyStashedOp.bind(this),
|
|
308
288
|
clientId: () => this.clientId,
|
|
@@ -313,8 +293,18 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
313
293
|
rollback: this.rollback.bind(this),
|
|
314
294
|
orderSequentially: this.orderSequentially.bind(this),
|
|
315
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
|
+
});
|
|
316
306
|
this.context.quorum.on("removeMember", (clientId) => {
|
|
317
|
-
this.
|
|
307
|
+
this.remoteMessageProcessor.clearPartialMessagesFor(clientId);
|
|
318
308
|
});
|
|
319
309
|
this.summaryCollection = new summaryCollection_1.SummaryCollection(this.deltaManager, this.logger);
|
|
320
310
|
this.dirtyContainer = this.context.attachState !== container_definitions_1.AttachState.Attached
|
|
@@ -382,26 +372,6 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
382
372
|
});
|
|
383
373
|
// logging hardware telemetry
|
|
384
374
|
logger.sendTelemetryEvent(Object.assign({ eventName: "DeviceSpec" }, getDeviceSpec()));
|
|
385
|
-
let loadSummaryNumber;
|
|
386
|
-
// Get the container creation metadata. For new container, we initialize these. For existing containers,
|
|
387
|
-
// get the values from the metadata blob.
|
|
388
|
-
if (existing) {
|
|
389
|
-
this.createContainerMetadata = {
|
|
390
|
-
createContainerRuntimeVersion: metadata === null || metadata === void 0 ? void 0 : metadata.createContainerRuntimeVersion,
|
|
391
|
-
createContainerTimestamp: metadata === null || metadata === void 0 ? void 0 : metadata.createContainerTimestamp,
|
|
392
|
-
};
|
|
393
|
-
// summaryNumber was renamed from summaryCount. For older docs that haven't been opened for a long time,
|
|
394
|
-
// the count is reset to 0.
|
|
395
|
-
loadSummaryNumber = (_f = metadata === null || metadata === void 0 ? void 0 : metadata.summaryNumber) !== null && _f !== void 0 ? _f : 0;
|
|
396
|
-
}
|
|
397
|
-
else {
|
|
398
|
-
this.createContainerMetadata = {
|
|
399
|
-
createContainerRuntimeVersion: packageVersion_1.pkgVersion,
|
|
400
|
-
createContainerTimestamp: Date.now(),
|
|
401
|
-
};
|
|
402
|
-
loadSummaryNumber = 0;
|
|
403
|
-
}
|
|
404
|
-
this.nextSummaryNumber = loadSummaryNumber + 1;
|
|
405
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 }));
|
|
406
376
|
(0, connectionTelemetry_1.ReportOpPerfTelemetry)(this.context.clientId, this.deltaManager, this.logger);
|
|
407
377
|
(0, batchTracker_1.BindBatchTracker)(this, this.logger);
|
|
@@ -415,8 +385,10 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
415
385
|
* @param requestHandler - Request handlers for the container runtime
|
|
416
386
|
* @param runtimeOptions - Additional options to be passed to the runtime
|
|
417
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.
|
|
418
390
|
*/
|
|
419
|
-
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) {
|
|
420
392
|
var _a, _b, _c;
|
|
421
393
|
// If taggedLogger exists, use it. Otherwise, wrap the vanilla logger:
|
|
422
394
|
// back-compat: Remove the TaggedLoggerAdapter fallback once all the host are using loader > 0.45
|
|
@@ -427,7 +399,10 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
427
399
|
runtimeVersion: packageVersion_1.pkgVersion,
|
|
428
400
|
},
|
|
429
401
|
});
|
|
430
|
-
const { summaryOptions = {}, gcOptions = {}, loadSequenceNumberVerification = "close", flushMode = defaultFlushMode, enableOfflineLoad = false, compressionOptions = {
|
|
402
|
+
const { summaryOptions = {}, gcOptions = {}, loadSequenceNumberVerification = "close", flushMode = defaultFlushMode, enableOfflineLoad = false, compressionOptions = {
|
|
403
|
+
minimumBatchSizeInBytes: Number.POSITIVE_INFINITY,
|
|
404
|
+
compressionAlgorithm: CompressionAlgorithms.lz4
|
|
405
|
+
}, maxBatchSizeInBytes = defaultMaxBatchSizeInBytes, } = runtimeOptions;
|
|
431
406
|
const pendingRuntimeState = context.pendingLocalState;
|
|
432
407
|
const baseSnapshot = (_b = pendingRuntimeState === null || pendingRuntimeState === void 0 ? void 0 : pendingRuntimeState.baseSnapshot) !== null && _b !== void 0 ? _b : context.baseSnapshot;
|
|
433
408
|
const storage = !pendingRuntimeState ?
|
|
@@ -476,7 +451,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
476
451
|
}
|
|
477
452
|
}
|
|
478
453
|
}
|
|
479
|
-
const runtime = new
|
|
454
|
+
const runtime = new containerRuntimeCtor(context, registry, metadata, electedSummarizerData, chunks !== null && chunks !== void 0 ? chunks : [], aliases !== null && aliases !== void 0 ? aliases : [], {
|
|
480
455
|
summaryOptions,
|
|
481
456
|
gcOptions,
|
|
482
457
|
loadSequenceNumberVerification,
|
|
@@ -490,7 +465,8 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
490
465
|
// delete these once runtime has seen them to save space
|
|
491
466
|
pendingRuntimeState.savedOps = [];
|
|
492
467
|
}
|
|
493
|
-
|
|
468
|
+
// Initialize the base state of the runtime before it's returned.
|
|
469
|
+
await runtime.initializeBaseState();
|
|
494
470
|
return runtime;
|
|
495
471
|
}
|
|
496
472
|
get options() {
|
|
@@ -539,9 +515,6 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
539
515
|
return (_a = this.summarizerClientElection) === null || _a === void 0 ? void 0 : _a.electedClientId;
|
|
540
516
|
}
|
|
541
517
|
get disposed() { return this._disposed; }
|
|
542
|
-
get emptyBatch() {
|
|
543
|
-
return this.pendingBatch.empty && this.pendingAttachBatch.empty;
|
|
544
|
-
}
|
|
545
518
|
get summarizer() {
|
|
546
519
|
(0, common_utils_1.assert)(this._summarizer !== undefined, 0x257 /* "This is not summarizing container" */);
|
|
547
520
|
return this._summarizer;
|
|
@@ -597,6 +570,13 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
597
570
|
? this.summaryConfiguration.initialSummarizerDelayMs
|
|
598
571
|
: 0;
|
|
599
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
|
+
}
|
|
600
580
|
dispose(error) {
|
|
601
581
|
var _a;
|
|
602
582
|
if (this._disposed) {
|
|
@@ -742,8 +722,8 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
742
722
|
addContainerStateToSummary(summaryTree, fullTree, trackState, telemetryContext) {
|
|
743
723
|
var _a;
|
|
744
724
|
this.addMetadataToSummary(summaryTree);
|
|
745
|
-
if (this.
|
|
746
|
-
const content = JSON.stringify([...this.
|
|
725
|
+
if (this.remoteMessageProcessor.partialMessages.size > 0) {
|
|
726
|
+
const content = JSON.stringify([...this.remoteMessageProcessor.partialMessages]);
|
|
747
727
|
(0, runtime_utils_1.addBlobToSummary)(summaryTree, summaryFormat_1.chunksBlobName, content);
|
|
748
728
|
}
|
|
749
729
|
const dataStoreAliases = this.dataStores.aliases;
|
|
@@ -762,7 +742,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
762
742
|
}
|
|
763
743
|
const gcSummary = this.garbageCollector.summarize(fullTree, trackState, telemetryContext);
|
|
764
744
|
if (gcSummary !== undefined) {
|
|
765
|
-
(0, runtime_utils_1.addSummarizeResultToSummary)(summaryTree,
|
|
745
|
+
(0, runtime_utils_1.addSummarizeResultToSummary)(summaryTree, garbageCollectionConstants_1.gcTreeKey, gcSummary);
|
|
766
746
|
}
|
|
767
747
|
}
|
|
768
748
|
// Track how many times the container tries to reconnect with pending messages.
|
|
@@ -907,31 +887,21 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
907
887
|
process(messageArg, local) {
|
|
908
888
|
var _a;
|
|
909
889
|
this.verifyNotClosed();
|
|
910
|
-
// Do shallow copy of message, as methods below will modify it.
|
|
911
|
-
// There might be multiple container instances receiving same message
|
|
912
|
-
// We do not need to make deep copy, as each layer will just replace message.content itself,
|
|
913
|
-
// but would not modify contents details
|
|
914
|
-
let message = Object.assign({}, messageArg);
|
|
915
|
-
// back-compat: ADO #1385: eventually should become unconditional, but only for runtime messages!
|
|
916
|
-
// System message may have no contents, or in some cases (mostly for back-compat) they may have actual objects.
|
|
917
|
-
// Old ops may contain empty string (I assume noops).
|
|
918
|
-
if (typeof message.contents === "string" && message.contents !== "") {
|
|
919
|
-
message.contents = JSON.parse(message.contents);
|
|
920
|
-
}
|
|
921
|
-
// Caveat: This will return false for runtime message in very old format, that are used in snapshot tests
|
|
922
|
-
// This format was not shipped to production workflows.
|
|
923
|
-
const runtimeMessage = unpackRuntimeMessage(message);
|
|
924
890
|
if ((_a = this.mc.config.getBoolean("enableOfflineLoad")) !== null && _a !== void 0 ? _a : this.runtimeOptions.enableOfflineLoad) {
|
|
925
891
|
this.savedOps.push(messageArg);
|
|
926
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);
|
|
927
900
|
// Surround the actual processing of the operation with messages to the schedule manager indicating
|
|
928
901
|
// the beginning and end. This allows it to emit appropriate events and/or pause the processing of new
|
|
929
902
|
// messages once a batch has been fully processed.
|
|
930
903
|
this.scheduleManager.beforeOpProcessing(message);
|
|
931
904
|
try {
|
|
932
|
-
// Chunk processing must come first given that we will transform the message to the unchunked version
|
|
933
|
-
// once all pieces are available
|
|
934
|
-
message = this.processRemoteChunkedMessage(message);
|
|
935
905
|
let localOpMetadata;
|
|
936
906
|
if (local && runtimeMessage) {
|
|
937
907
|
localOpMetadata = this.pendingStateManager.processPendingLocalMessage(message);
|
|
@@ -1045,83 +1015,8 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1045
1015
|
*/
|
|
1046
1016
|
flush() {
|
|
1047
1017
|
(0, common_utils_1.assert)(this._orderSequentiallyCalls === 0, 0x24c /* "Cannot call `flush()` from `orderSequentially`'s callback" */);
|
|
1048
|
-
this.
|
|
1049
|
-
|
|
1050
|
-
(0, common_utils_1.assert)(this.emptyBatch, 0x3cf /* reentrancy */);
|
|
1051
|
-
}
|
|
1052
|
-
flushBatch(batch) {
|
|
1053
|
-
const length = batch.length;
|
|
1054
|
-
if (length > 1) {
|
|
1055
|
-
batch[0].metadata = Object.assign(Object.assign({}, batch[0].metadata), { batch: true });
|
|
1056
|
-
batch[length - 1].metadata = Object.assign(Object.assign({}, batch[length - 1].metadata), { batch: false });
|
|
1057
|
-
// This assert fires for the following reason (there might be more cases like that):
|
|
1058
|
-
// AgentScheduler will send ops in response to ConsensusRegisterCollection's "atomicChanged" event handler,
|
|
1059
|
-
// i.e. in the middle of op processing!
|
|
1060
|
-
// Sending ops while processing ops is not good idea - it's not defined when
|
|
1061
|
-
// referenceSequenceNumber changes in op processing sequence (at the beginning or end of op processing),
|
|
1062
|
-
// If we send ops in response to processing multiple ops, then we for sure hit this assert!
|
|
1063
|
-
// Tracked via ADO #1834
|
|
1064
|
-
// assert(batch[0].referenceSequenceNumber === batch[length - 1].referenceSequenceNumber,
|
|
1065
|
-
// "Batch should be generated synchronously, without processing ops in the middle!");
|
|
1066
|
-
}
|
|
1067
|
-
let clientSequenceNumber = -1;
|
|
1068
|
-
// Did we disconnect in the middle of turn-based batch?
|
|
1069
|
-
// If so, do nothing, as pending state manager will resubmit it correctly on reconnect.
|
|
1070
|
-
if (this.canSendOps()) {
|
|
1071
|
-
if (this.context.submitBatchFn !== undefined) {
|
|
1072
|
-
const batchToSend = [];
|
|
1073
|
-
for (const message of batch) {
|
|
1074
|
-
let contents = message.contents;
|
|
1075
|
-
let metadata = message.metadata;
|
|
1076
|
-
if (this.runtimeOptions.compressionOptions.minimumSize &&
|
|
1077
|
-
this.runtimeOptions.compressionOptions.minimumSize < message.contents.length) {
|
|
1078
|
-
this.compressedOpCount++;
|
|
1079
|
-
const copiedMessage = Object.assign({}, message.deserializedContent);
|
|
1080
|
-
const compressionStart = Date.now();
|
|
1081
|
-
const contentsAsBuffer = new TextEncoder().encode(JSON.stringify(copiedMessage.contents));
|
|
1082
|
-
const compressedContents = (0, lz4js_1.compress)(contentsAsBuffer);
|
|
1083
|
-
const compressedContent = common_utils_1.IsoBuffer.from(compressedContents).toString("base64");
|
|
1084
|
-
const duration = Date.now() - compressionStart;
|
|
1085
|
-
if (this.compressedOpCount % 100) {
|
|
1086
|
-
this.mc.logger.sendPerformanceEvent({
|
|
1087
|
-
eventName: "compressedOp",
|
|
1088
|
-
duration,
|
|
1089
|
-
sizeBeforeCompression: message.contents.length,
|
|
1090
|
-
sizeAfterCompression: compressedContent.length,
|
|
1091
|
-
});
|
|
1092
|
-
}
|
|
1093
|
-
copiedMessage.contents = compressedContent;
|
|
1094
|
-
const stringifiedContents = JSON.stringify(copiedMessage);
|
|
1095
|
-
if (stringifiedContents.length < message.contents.length) {
|
|
1096
|
-
contents = JSON.stringify(copiedMessage);
|
|
1097
|
-
metadata = Object.assign(Object.assign({}, message.metadata), { compressed: true });
|
|
1098
|
-
}
|
|
1099
|
-
}
|
|
1100
|
-
batchToSend.push({ contents, metadata });
|
|
1101
|
-
}
|
|
1102
|
-
// returns clientSequenceNumber of last message in a batch
|
|
1103
|
-
clientSequenceNumber = this.context.submitBatchFn(batchToSend);
|
|
1104
|
-
}
|
|
1105
|
-
else {
|
|
1106
|
-
// Legacy path - supporting old loader versions. Can be removed only when LTS moves above
|
|
1107
|
-
// version that has support for batches (submitBatchFn)
|
|
1108
|
-
for (const message of batch) {
|
|
1109
|
-
clientSequenceNumber = this.context.submitFn(protocol_definitions_1.MessageType.Operation, message.deserializedContent, true, // batch
|
|
1110
|
-
message.metadata);
|
|
1111
|
-
}
|
|
1112
|
-
this.deltaSender.flush();
|
|
1113
|
-
}
|
|
1114
|
-
// Convert from clientSequenceNumber of last message in the batch to clientSequenceNumber of first message.
|
|
1115
|
-
clientSequenceNumber -= batch.length - 1;
|
|
1116
|
-
(0, common_utils_1.assert)(clientSequenceNumber >= 0, 0x3d0 /* clientSequenceNumber can't be negative */);
|
|
1117
|
-
}
|
|
1118
|
-
// Let the PendingStateManager know that a message was submitted.
|
|
1119
|
-
// In future, need to shift toward keeping batch as a whole!
|
|
1120
|
-
for (const message of batch) {
|
|
1121
|
-
this.pendingStateManager.onSubmitMessage(message.deserializedContent.type, clientSequenceNumber, message.referenceSequenceNumber, message.deserializedContent.contents, message.localOpMetadata, message.metadata);
|
|
1122
|
-
clientSequenceNumber++;
|
|
1123
|
-
}
|
|
1124
|
-
this.pendingStateManager.onFlush();
|
|
1018
|
+
this.outbox.flush();
|
|
1019
|
+
(0, common_utils_1.assert)(this.outbox.isEmpty, 0x3cf /* reentrancy */);
|
|
1125
1020
|
}
|
|
1126
1021
|
orderSequentially(callback) {
|
|
1127
1022
|
let checkpoint;
|
|
@@ -1129,7 +1024,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1129
1024
|
// Note: we are not touching this.pendingAttachBatch here, for two reasons:
|
|
1130
1025
|
// 1. It would not help, as we flush attach ops as they become available.
|
|
1131
1026
|
// 2. There is no way to undo process of data store creation.
|
|
1132
|
-
checkpoint = this.
|
|
1027
|
+
checkpoint = this.outbox.checkpoint().mainBatch;
|
|
1133
1028
|
}
|
|
1134
1029
|
try {
|
|
1135
1030
|
this._orderSequentiallyCalls++;
|
|
@@ -1349,33 +1244,27 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1349
1244
|
// summarizing is required and asserted by the the summarizer node. We are the root and are
|
|
1350
1245
|
// always referenced, so the used routes is only self-route (empty string).
|
|
1351
1246
|
this.summarizerNode.updateUsedRoutes([""]);
|
|
1247
|
+
const blobManagerUsedRoutes = [];
|
|
1352
1248
|
const dataStoreUsedRoutes = [];
|
|
1353
1249
|
for (const route of usedRoutes) {
|
|
1354
|
-
if (
|
|
1250
|
+
if (this.isBlobPath(route)) {
|
|
1251
|
+
blobManagerUsedRoutes.push(route);
|
|
1252
|
+
}
|
|
1253
|
+
else {
|
|
1355
1254
|
dataStoreUsedRoutes.push(route);
|
|
1356
1255
|
}
|
|
1357
1256
|
}
|
|
1358
|
-
|
|
1257
|
+
this.blobManager.updateUsedRoutes(blobManagerUsedRoutes);
|
|
1258
|
+
this.dataStores.updateUsedRoutes(dataStoreUsedRoutes);
|
|
1359
1259
|
}
|
|
1360
1260
|
/**
|
|
1361
|
-
*
|
|
1362
|
-
*
|
|
1363
|
-
* @param unusedRoutes - The routes that are unused in all data stores and blobs in this Container.
|
|
1261
|
+
* This is called to update objects whose routes are unused. The unused objects are either deleted or marked as
|
|
1262
|
+
* tombstones.
|
|
1263
|
+
* @param unusedRoutes - The routes that are unused in all data stores and attachment blobs in this Container.
|
|
1264
|
+
* @param tombstone - if true, the objects corresponding to unused routes are marked tombstones. Otherwise, they
|
|
1265
|
+
* are deleted.
|
|
1364
1266
|
*/
|
|
1365
|
-
|
|
1366
|
-
var _a;
|
|
1367
|
-
/**
|
|
1368
|
-
* When running GC in tombstone mode, this is called to tombstone datastore routes that are unused. This
|
|
1369
|
-
* enables testing scenarios without actually deleting content. The content acts as if it's deleted to the
|
|
1370
|
-
* external user, but the internal runtime does not delete it in summarizes, etc.
|
|
1371
|
-
*/
|
|
1372
|
-
const tombstone = (_a = this.mc.config.getBoolean(garbageCollection_1.testTombstoneKey)) !== null && _a !== void 0 ? _a : false;
|
|
1373
|
-
// TODO: add blobs
|
|
1374
|
-
if (tombstone) {
|
|
1375
|
-
// If blob routes are passed in here, tombstone will fail and hit an assert
|
|
1376
|
-
this.dataStores.deleteUnusedRoutes(unusedRoutes, tombstone);
|
|
1377
|
-
return;
|
|
1378
|
-
}
|
|
1267
|
+
updateUnusedRoutes(unusedRoutes, tombstone) {
|
|
1379
1268
|
const blobManagerUnusedRoutes = [];
|
|
1380
1269
|
const dataStoreUnusedRoutes = [];
|
|
1381
1270
|
for (const route of unusedRoutes) {
|
|
@@ -1386,8 +1275,8 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1386
1275
|
dataStoreUnusedRoutes.push(route);
|
|
1387
1276
|
}
|
|
1388
1277
|
}
|
|
1389
|
-
this.blobManager.
|
|
1390
|
-
this.dataStores.
|
|
1278
|
+
this.blobManager.updateUnusedRoutes(blobManagerUnusedRoutes, tombstone);
|
|
1279
|
+
this.dataStores.updateUnusedRoutes(dataStoreUnusedRoutes, tombstone);
|
|
1391
1280
|
}
|
|
1392
1281
|
/**
|
|
1393
1282
|
* Returns a server generated referenced timestamp to be used to track unreferenced nodes by GC.
|
|
@@ -1467,7 +1356,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1467
1356
|
const summaryNumberLogger = telemetry_utils_1.ChildLogger.create(summaryLogger, undefined, {
|
|
1468
1357
|
all: { summaryNumber },
|
|
1469
1358
|
});
|
|
1470
|
-
(0, common_utils_1.assert)(this.
|
|
1359
|
+
(0, common_utils_1.assert)(this.outbox.isEmpty, 0x3d1 /* Can't trigger summary in the middle of a batch */);
|
|
1471
1360
|
let latestSnapshotVersionId;
|
|
1472
1361
|
if (refreshLatestAck) {
|
|
1473
1362
|
const latestSnapshotInfo = await this.refreshLatestSummaryAckFromServer(telemetry_utils_1.ChildLogger.create(summaryNumberLogger, undefined, { all: { safeSummary: true } }));
|
|
@@ -1558,8 +1447,8 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1558
1447
|
const dataStoreTree = summaryTree.tree[runtime_definitions_1.channelsTreeName];
|
|
1559
1448
|
(0, common_utils_1.assert)(dataStoreTree.type === protocol_definitions_1.SummaryType.Tree, 0x1fc /* "summary is not a tree" */);
|
|
1560
1449
|
const handleCount = Object.values(dataStoreTree.tree).filter((value) => value.type === protocol_definitions_1.SummaryType.Handle).length;
|
|
1561
|
-
const gcSummaryTreeStats = summaryTree.tree[
|
|
1562
|
-
? (0, runtime_utils_1.calculateStats)(summaryTree.tree[
|
|
1450
|
+
const gcSummaryTreeStats = summaryTree.tree[garbageCollectionConstants_1.gcTreeKey]
|
|
1451
|
+
? (0, runtime_utils_1.calculateStats)(summaryTree.tree[garbageCollectionConstants_1.gcTreeKey])
|
|
1563
1452
|
: undefined;
|
|
1564
1453
|
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);
|
|
1565
1454
|
const generateSummaryData = {
|
|
@@ -1640,40 +1529,8 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1640
1529
|
this.deltaManager.inbound.resume();
|
|
1641
1530
|
}
|
|
1642
1531
|
}
|
|
1643
|
-
processRemoteChunkedMessage(message) {
|
|
1644
|
-
if (message.type !== ContainerMessageType.ChunkedOp) {
|
|
1645
|
-
return message;
|
|
1646
|
-
}
|
|
1647
|
-
const clientId = message.clientId;
|
|
1648
|
-
const chunkedContent = message.contents;
|
|
1649
|
-
this.addChunk(clientId, chunkedContent);
|
|
1650
|
-
if (chunkedContent.chunkId === chunkedContent.totalChunks) {
|
|
1651
|
-
const newMessage = Object.assign({}, message);
|
|
1652
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
1653
|
-
const serializedContent = this.chunkMap.get(clientId).join("");
|
|
1654
|
-
newMessage.contents = JSON.parse(serializedContent);
|
|
1655
|
-
newMessage.type = chunkedContent.originalType;
|
|
1656
|
-
this.clearPartialChunks(clientId);
|
|
1657
|
-
return newMessage;
|
|
1658
|
-
}
|
|
1659
|
-
return message;
|
|
1660
|
-
}
|
|
1661
|
-
addChunk(clientId, chunkedContent) {
|
|
1662
|
-
let map = this.chunkMap.get(clientId);
|
|
1663
|
-
if (map === undefined) {
|
|
1664
|
-
map = [];
|
|
1665
|
-
this.chunkMap.set(clientId, map);
|
|
1666
|
-
}
|
|
1667
|
-
(0, common_utils_1.assert)(chunkedContent.chunkId === map.length + 1, 0x131 /* "Mismatch between new chunkId and expected chunkMap" */); // 1-based indexing
|
|
1668
|
-
map.push(chunkedContent.contents);
|
|
1669
|
-
}
|
|
1670
|
-
clearPartialChunks(clientId) {
|
|
1671
|
-
if (this.chunkMap.has(clientId)) {
|
|
1672
|
-
this.chunkMap.delete(clientId);
|
|
1673
|
-
}
|
|
1674
|
-
}
|
|
1675
1532
|
hasPendingMessages() {
|
|
1676
|
-
return this.pendingStateManager.hasPendingMessages() || !this.
|
|
1533
|
+
return this.pendingStateManager.hasPendingMessages() || !this.outbox.isEmpty;
|
|
1677
1534
|
}
|
|
1678
1535
|
updateDocumentDirtyState(dirty) {
|
|
1679
1536
|
if (this.attachState !== container_definitions_1.AttachState.Attached) {
|
|
@@ -1717,7 +1574,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1717
1574
|
const deserializedContent = { type, contents };
|
|
1718
1575
|
const serializedContent = JSON.stringify(deserializedContent);
|
|
1719
1576
|
if (this.deltaManager.readOnlyInfo.readonly) {
|
|
1720
|
-
this.logger.
|
|
1577
|
+
this.logger.sendTelemetryEvent({ eventName: "SubmitOpInReadonly", connected: this.connected });
|
|
1721
1578
|
}
|
|
1722
1579
|
const message = {
|
|
1723
1580
|
contents: serializedContent,
|
|
@@ -1748,43 +1605,23 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1748
1605
|
// Please note that this does not change file format, so it can be disabled in the future if this
|
|
1749
1606
|
// optimization no longer makes sense (for example, batch compression may make it less appealing).
|
|
1750
1607
|
if (this.currentlyBatching() && type === ContainerMessageType.Attach &&
|
|
1751
|
-
this.mc.config.getBoolean("Fluid.ContainerRuntime.
|
|
1752
|
-
|
|
1753
|
-
// BatchManager has two limits - soft limit & hard limit. Soft limit is only engaged
|
|
1754
|
-
// when queue is not empty.
|
|
1755
|
-
// Flush queue & retry. Failure on retry would mean - single message is bigger than hard limit
|
|
1756
|
-
this.flushBatch(this.pendingAttachBatch.popBatch());
|
|
1757
|
-
if (!this.pendingAttachBatch.push(message)) {
|
|
1758
|
-
throw new container_utils_1.GenericError("BatchTooLarge",
|
|
1759
|
-
/* error */ undefined, {
|
|
1760
|
-
opSize: message.contents.length,
|
|
1761
|
-
count: this.pendingAttachBatch.length,
|
|
1762
|
-
limit: this.pendingAttachBatch.limit,
|
|
1763
|
-
});
|
|
1764
|
-
}
|
|
1765
|
-
}
|
|
1608
|
+
this.mc.config.getBoolean("Fluid.ContainerRuntime.disableAttachOpReorder") !== true) {
|
|
1609
|
+
this.outbox.submitAttach(message);
|
|
1766
1610
|
}
|
|
1767
1611
|
else {
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1612
|
+
this.outbox.submit(message);
|
|
1613
|
+
}
|
|
1614
|
+
if (!this.currentlyBatching()) {
|
|
1615
|
+
this.flush();
|
|
1616
|
+
}
|
|
1617
|
+
else if (!this.flushMicroTaskExists) {
|
|
1618
|
+
this.flushMicroTaskExists = true;
|
|
1619
|
+
// Queue a microtask to detect the end of the turn and force a flush.
|
|
1620
|
+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
1621
|
+
Promise.resolve().then(() => {
|
|
1622
|
+
this.flushMicroTaskExists = false;
|
|
1777
1623
|
this.flush();
|
|
1778
|
-
}
|
|
1779
|
-
else if (!this.flushMicroTaskExists) {
|
|
1780
|
-
this.flushMicroTaskExists = true;
|
|
1781
|
-
// Queue a microtask to detect the end of the turn and force a flush.
|
|
1782
|
-
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
1783
|
-
Promise.resolve().then(() => {
|
|
1784
|
-
this.flushMicroTaskExists = false;
|
|
1785
|
-
this.flush();
|
|
1786
|
-
});
|
|
1787
|
-
}
|
|
1624
|
+
}).catch((error) => { this.closeFn(error); });
|
|
1788
1625
|
}
|
|
1789
1626
|
}
|
|
1790
1627
|
catch (error) {
|
|
@@ -1799,7 +1636,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1799
1636
|
this.verifyNotClosed();
|
|
1800
1637
|
(0, common_utils_1.assert)(this.connected, 0x133 /* "Container disconnected when trying to submit system message" */);
|
|
1801
1638
|
// System message should not be sent in the middle of the batch.
|
|
1802
|
-
(0, common_utils_1.assert)(this.
|
|
1639
|
+
(0, common_utils_1.assert)(this.outbox.isEmpty, 0x3d4 /* System op in the middle of a batch */);
|
|
1803
1640
|
// back-compat: ADO #1385: Make this call unconditional in the future
|
|
1804
1641
|
return this.context.submitSummaryFn !== undefined
|
|
1805
1642
|
? this.context.submitSummaryFn(contents)
|
|
@@ -1933,7 +1770,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1933
1770
|
this.baseSnapshotBlobs = serializedSnapshotStorage_1.SerializedSnapshotStorage.serializeTreeWithBlobContents(snapshot);
|
|
1934
1771
|
}
|
|
1935
1772
|
}
|
|
1936
|
-
async
|
|
1773
|
+
async initializeBaseSnapshotBlobs() {
|
|
1937
1774
|
var _a;
|
|
1938
1775
|
if (!((_a = this.mc.config.getBoolean("enableOfflineLoad")) !== null && _a !== void 0 ? _a : this.runtimeOptions.enableOfflineLoad) ||
|
|
1939
1776
|
this.attachState !== container_definitions_1.AttachState.Attached || this.context.pendingLocalState) {
|