@fluidframework/container-runtime 2.0.0-dev.1.4.6.106135 → 2.0.0-dev.2.2.0.111723
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/batchManager.d.ts +11 -6
- package/dist/batchManager.d.ts.map +1 -1
- package/dist/batchManager.js +23 -13
- package/dist/batchManager.js.map +1 -1
- package/dist/containerRuntime.d.ts +74 -20
- package/dist/containerRuntime.d.ts.map +1 -1
- package/dist/containerRuntime.js +190 -137
- 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 +50 -20
- package/dist/dataStores.js.map +1 -1
- package/dist/garbageCollection.d.ts +36 -19
- package/dist/garbageCollection.d.ts.map +1 -1
- package/dist/garbageCollection.js +207 -121
- package/dist/garbageCollection.js.map +1 -1
- package/dist/gcSweepReadyUsageDetection.d.ts.map +1 -1
- package/dist/gcSweepReadyUsageDetection.js +3 -12
- package/dist/gcSweepReadyUsageDetection.js.map +1 -1
- package/dist/index.d.ts +4 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -5
- package/dist/index.js.map +1 -1
- package/dist/opCompressor.d.ts +18 -0
- package/dist/opCompressor.d.ts.map +1 -0
- package/dist/opCompressor.js +50 -0
- package/dist/opCompressor.js.map +1 -0
- package/dist/opDecompressor.d.ts +20 -0
- package/dist/opDecompressor.d.ts.map +1 -0
- package/dist/opDecompressor.js +72 -0
- package/dist/opDecompressor.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.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/batchManager.d.ts +11 -6
- package/lib/batchManager.d.ts.map +1 -1
- package/lib/batchManager.js +23 -13
- package/lib/batchManager.js.map +1 -1
- package/lib/containerRuntime.d.ts +74 -20
- package/lib/containerRuntime.d.ts.map +1 -1
- package/lib/containerRuntime.js +189 -136
- 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 -23
- package/lib/dataStores.js.map +1 -1
- package/lib/garbageCollection.d.ts +36 -19
- package/lib/garbageCollection.d.ts.map +1 -1
- package/lib/garbageCollection.js +207 -121
- package/lib/garbageCollection.js.map +1 -1
- package/lib/gcSweepReadyUsageDetection.d.ts.map +1 -1
- package/lib/gcSweepReadyUsageDetection.js +3 -12
- package/lib/gcSweepReadyUsageDetection.js.map +1 -1
- package/lib/index.d.ts +4 -6
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +2 -4
- package/lib/index.js.map +1 -1
- package/lib/opCompressor.d.ts +18 -0
- package/lib/opCompressor.d.ts.map +1 -0
- package/lib/opCompressor.js +46 -0
- package/lib/opCompressor.js.map +1 -0
- package/lib/opDecompressor.d.ts +20 -0
- package/lib/opDecompressor.d.ts.map +1 -0
- package/lib/opDecompressor.js +68 -0
- package/lib/opDecompressor.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.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 +37 -63
- package/prettier.config.cjs +8 -0
- package/src/batchManager.ts +32 -15
- package/src/containerRuntime.ts +260 -156
- package/src/dataStore.ts +13 -1
- package/src/dataStoreContext.ts +100 -76
- package/src/dataStoreContexts.ts +1 -1
- package/src/dataStores.ts +61 -23
- package/src/garbageCollection.ts +255 -126
- package/src/gcSweepReadyUsageDetection.ts +2 -10
- package/src/index.ts +4 -4
- package/src/opCompressor.ts +59 -0
- package/src/opDecompressor.ts +82 -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 +4 -2
- package/src/summaryGenerator.ts +3 -2
- package/src/summaryManager.ts +18 -7
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.unpackRuntimeMessage = exports.isRuntimeMessage = exports.RuntimeMessage = exports.RuntimeHeaders = exports.DefaultSummaryConfiguration = exports.ContainerMessageType = void 0;
|
|
3
|
+
exports.ContainerRuntime = exports.getDeviceSpec = exports.agentSchedulerId = exports.unpackRuntimeMessage = 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");
|
|
@@ -33,6 +33,7 @@ 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 opDecompressor_1 = require("./opDecompressor");
|
|
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,16 +110,13 @@ 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
116
|
/**
|
|
106
117
|
* Unpacks runtime messages
|
|
107
118
|
*
|
|
108
|
-
* @remarks This API makes no promises regarding backward-
|
|
119
|
+
* @remarks This API makes no promises regarding backward-compatibility. This is internal API.
|
|
109
120
|
* @param message - message (as it observed in storage / service)
|
|
110
121
|
* @returns unpacked runtime message
|
|
111
122
|
*
|
|
@@ -120,7 +131,6 @@ function unpackRuntimeMessage(message) {
|
|
|
120
131
|
else {
|
|
121
132
|
// new format
|
|
122
133
|
const innerContents = message.contents;
|
|
123
|
-
(0, common_utils_1.assert)(innerContents.type !== undefined, 0x121 /* "Undefined inner contents type!" */);
|
|
124
134
|
message.type = innerContents.type;
|
|
125
135
|
message.contents = innerContents.contents;
|
|
126
136
|
}
|
|
@@ -162,7 +172,7 @@ exports.getDeviceSpec = getDeviceSpec;
|
|
|
162
172
|
*/
|
|
163
173
|
class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
164
174
|
constructor(context, registry, metadata, electedSummarizerData, chunks, dataStoreAliasMap, runtimeOptions, containerScope, logger, existing, blobManagerSnapshot, _storage, requestHandler, summaryConfiguration) {
|
|
165
|
-
var _a, _b, _c, _d;
|
|
175
|
+
var _a, _b, _c, _d, _e, _f;
|
|
166
176
|
if (summaryConfiguration === void 0) { summaryConfiguration = Object.assign(Object.assign({}, exports.DefaultSummaryConfiguration), (_a = runtimeOptions.summaryOptions) === null || _a === void 0 ? void 0 : _a.summaryConfigOverrides); }
|
|
167
177
|
super();
|
|
168
178
|
this.context = context;
|
|
@@ -173,9 +183,10 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
173
183
|
this._storage = _storage;
|
|
174
184
|
this.requestHandler = requestHandler;
|
|
175
185
|
this.summaryConfiguration = summaryConfiguration;
|
|
186
|
+
this.opDecompressor = new opDecompressor_1.OpDecompressor();
|
|
176
187
|
this.defaultMaxConsecutiveReconnects = 7;
|
|
177
188
|
this._orderSequentiallyCalls = 0;
|
|
178
|
-
this.
|
|
189
|
+
this.flushMicroTaskExists = false;
|
|
179
190
|
this.savedOps = [];
|
|
180
191
|
this.consecutiveReconnects = 0;
|
|
181
192
|
this._disposed = false;
|
|
@@ -187,12 +198,6 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
187
198
|
signalTimestamp: 0,
|
|
188
199
|
trackingSignalSequenceNumber: undefined,
|
|
189
200
|
};
|
|
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
201
|
this.summarizeOnDemand = (...args) => {
|
|
197
202
|
if (this.clientDetails.type === summarizerClientElection_1.summarizerClientType) {
|
|
198
203
|
return this.summarizer.summarizeOnDemand(...args);
|
|
@@ -221,6 +226,26 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
221
226
|
throw new container_utils_1.UsageError(`Can't summarize, disableSummaries: ${this.summariesDisabled}`);
|
|
222
227
|
}
|
|
223
228
|
};
|
|
229
|
+
let loadSummaryNumber;
|
|
230
|
+
// Get the container creation metadata. For new container, we initialize these. For existing containers,
|
|
231
|
+
// get the values from the metadata blob.
|
|
232
|
+
if (existing) {
|
|
233
|
+
this.createContainerMetadata = {
|
|
234
|
+
createContainerRuntimeVersion: metadata === null || metadata === void 0 ? void 0 : metadata.createContainerRuntimeVersion,
|
|
235
|
+
createContainerTimestamp: metadata === null || metadata === void 0 ? void 0 : metadata.createContainerTimestamp,
|
|
236
|
+
};
|
|
237
|
+
// summaryNumber was renamed from summaryCount. For older docs that haven't been opened for a long time,
|
|
238
|
+
// the count is reset to 0.
|
|
239
|
+
loadSummaryNumber = (_b = metadata === null || metadata === void 0 ? void 0 : metadata.summaryNumber) !== null && _b !== void 0 ? _b : 0;
|
|
240
|
+
}
|
|
241
|
+
else {
|
|
242
|
+
this.createContainerMetadata = {
|
|
243
|
+
createContainerRuntimeVersion: packageVersion_1.pkgVersion,
|
|
244
|
+
createContainerTimestamp: Date.now(),
|
|
245
|
+
};
|
|
246
|
+
loadSummaryNumber = 0;
|
|
247
|
+
}
|
|
248
|
+
this.nextSummaryNumber = loadSummaryNumber + 1;
|
|
224
249
|
this.messageAtLastSummary = metadata === null || metadata === void 0 ? void 0 : metadata.message;
|
|
225
250
|
this._connected = this.context.connected;
|
|
226
251
|
this.chunkMap = new Map(chunks);
|
|
@@ -235,10 +260,31 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
235
260
|
this.maxOpsSinceLastSummary = this.getMaxOpsSinceLastSummary();
|
|
236
261
|
this.initialSummarizerDelayMs = this.getInitialSummarizerDelayMs();
|
|
237
262
|
this.maxConsecutiveReconnects =
|
|
238
|
-
(
|
|
263
|
+
(_c = this.mc.config.getNumber(maxConsecutiveReconnectsKey)) !== null && _c !== void 0 ? _c : this.defaultMaxConsecutiveReconnects;
|
|
239
264
|
this._flushMode = runtimeOptions.flushMode;
|
|
265
|
+
// Provide lower soft limit - we want to have some number of ops to get efficiency in compression
|
|
266
|
+
// & bandwidth usage, but at the same time we want to send these ops sooner, to reduce overall
|
|
267
|
+
// latency of processing a batch.
|
|
268
|
+
// So there is some ballance here, that depends on compression algorithm and its efficiency working with smaller
|
|
269
|
+
// payloads. That number represents final (compressed) bits (once compression is implemented).
|
|
270
|
+
this.pendingAttachBatch = new batchManager_1.BatchManager(this.mc.logger, {
|
|
271
|
+
hardLimit: runtimeOptions.maxBatchSizeInBytes,
|
|
272
|
+
softLimit: 64 * 1024,
|
|
273
|
+
compressionOptions: runtimeOptions.compressionOptions
|
|
274
|
+
});
|
|
275
|
+
this.pendingBatch = new batchManager_1.BatchManager(this.mc.logger, {
|
|
276
|
+
hardLimit: runtimeOptions.maxBatchSizeInBytes,
|
|
277
|
+
compressionOptions: runtimeOptions.compressionOptions
|
|
278
|
+
});
|
|
240
279
|
const pendingRuntimeState = context.pendingLocalState;
|
|
241
|
-
const baseSnapshot = (
|
|
280
|
+
const baseSnapshot = (_d = pendingRuntimeState === null || pendingRuntimeState === void 0 ? void 0 : pendingRuntimeState.baseSnapshot) !== null && _d !== void 0 ? _d : context.baseSnapshot;
|
|
281
|
+
const maxSnapshotCacheDurationMs = (_f = (_e = this._storage) === null || _e === void 0 ? void 0 : _e.policies) === null || _f === void 0 ? void 0 : _f.maximumCacheDurationMs;
|
|
282
|
+
if (maxSnapshotCacheDurationMs !== undefined && maxSnapshotCacheDurationMs > 5 * 24 * 60 * 60 * 1000) {
|
|
283
|
+
// This is a runtime enforcement of what's already explicit in the policy's type itself,
|
|
284
|
+
// which dictates the value is either undefined or exactly 5 days in ms.
|
|
285
|
+
// As long as the actual value is less than 5 days, the assumptions GC makes here are valid.
|
|
286
|
+
throw new container_utils_1.UsageError("Driver's maximumCacheDurationMs policy cannot exceed 5 days");
|
|
287
|
+
}
|
|
242
288
|
this.garbageCollector = garbageCollection_1.GarbageCollector.create({
|
|
243
289
|
runtime: this,
|
|
244
290
|
gcOptions: this.runtimeOptions.gcOptions,
|
|
@@ -246,6 +292,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
246
292
|
baseLogger: this.mc.logger,
|
|
247
293
|
existing,
|
|
248
294
|
metadata,
|
|
295
|
+
createContainerMetadata: this.createContainerMetadata,
|
|
249
296
|
isSummarizerClient: this.context.clientDetails.type === summarizerClientElection_1.summarizerClientType,
|
|
250
297
|
getNodePackagePath: async (nodePath) => this.getGCNodePackagePath(nodePath),
|
|
251
298
|
getLastSummaryTimestampMs: () => { var _a; return (_a = this.messageAtLastSummary) === null || _a === void 0 ? void 0 : _a.timestamp; },
|
|
@@ -271,9 +318,9 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
271
318
|
gcDisabled: !this.garbageCollector.shouldRunGC,
|
|
272
319
|
});
|
|
273
320
|
if (baseSnapshot) {
|
|
274
|
-
this.summarizerNode.
|
|
321
|
+
this.summarizerNode.updateBaseSummaryState(baseSnapshot);
|
|
275
322
|
}
|
|
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)
|
|
323
|
+
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
324
|
this.blobManager = new blobManager_1.BlobManager(this.handleContext, blobManagerSnapshot, () => this.storage, (blobId, localId) => {
|
|
278
325
|
if (!this.disposed) {
|
|
279
326
|
this.submit(ContainerMessageType.BlobAttach, undefined, undefined, { blobId, localId });
|
|
@@ -287,10 +334,10 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
287
334
|
close: this.closeFn,
|
|
288
335
|
connected: () => this.connected,
|
|
289
336
|
flush: this.flush.bind(this),
|
|
290
|
-
flushMode: () => this.flushMode,
|
|
291
337
|
reSubmit: this.reSubmit.bind(this),
|
|
292
|
-
|
|
293
|
-
|
|
338
|
+
rollback: this.rollback.bind(this),
|
|
339
|
+
orderSequentially: this.orderSequentially.bind(this),
|
|
340
|
+
}, pendingRuntimeState === null || pendingRuntimeState === void 0 ? void 0 : pendingRuntimeState.pending);
|
|
294
341
|
this.context.quorum.on("removeMember", (clientId) => {
|
|
295
342
|
this.clearPartialChunks(clientId);
|
|
296
343
|
});
|
|
@@ -314,7 +361,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
314
361
|
// if summaries are enabled and we are not the summarizer client.
|
|
315
362
|
const defaultAction = () => {
|
|
316
363
|
if (this.summaryCollection.opsSinceLastAck > this.maxOpsSinceLastSummary) {
|
|
317
|
-
this.logger.
|
|
364
|
+
this.logger.sendTelemetryEvent({ eventName: "SummaryStatus:Behind" });
|
|
318
365
|
// unregister default to no log on every op after falling behind
|
|
319
366
|
// and register summary ack handler to re-register this handler
|
|
320
367
|
// after successful summary
|
|
@@ -360,26 +407,6 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
360
407
|
});
|
|
361
408
|
// logging hardware telemetry
|
|
362
409
|
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
410
|
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
411
|
(0, connectionTelemetry_1.ReportOpPerfTelemetry)(this.context.clientId, this.deltaManager, this.logger);
|
|
385
412
|
(0, batchTracker_1.BindBatchTracker)(this, this.logger);
|
|
@@ -405,7 +432,8 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
405
432
|
runtimeVersion: packageVersion_1.pkgVersion,
|
|
406
433
|
},
|
|
407
434
|
});
|
|
408
|
-
const { summaryOptions = {}, gcOptions = {}, loadSequenceNumberVerification = "close", flushMode = defaultFlushMode, enableOfflineLoad = false,
|
|
435
|
+
const { summaryOptions = {}, gcOptions = {}, loadSequenceNumberVerification = "close", flushMode = defaultFlushMode, enableOfflineLoad = false, compressionOptions = { minimumBatchSizeInBytes: Number.POSITIVE_INFINITY,
|
|
436
|
+
compressionAlgorithm: CompressionAlgorithms.lz4 }, maxBatchSizeInBytes = defaultMaxBatchSizeInBytes, } = runtimeOptions;
|
|
409
437
|
const pendingRuntimeState = context.pendingLocalState;
|
|
410
438
|
const baseSnapshot = (_b = pendingRuntimeState === null || pendingRuntimeState === void 0 ? void 0 : pendingRuntimeState.baseSnapshot) !== null && _b !== void 0 ? _b : context.baseSnapshot;
|
|
411
439
|
const storage = !pendingRuntimeState ?
|
|
@@ -460,13 +488,16 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
460
488
|
loadSequenceNumberVerification,
|
|
461
489
|
flushMode,
|
|
462
490
|
enableOfflineLoad,
|
|
491
|
+
compressionOptions,
|
|
492
|
+
maxBatchSizeInBytes,
|
|
463
493
|
}, containerScope, logger, loadExisting, blobManagerSnapshot, storage, requestHandler);
|
|
464
494
|
if (pendingRuntimeState) {
|
|
465
495
|
await runtime.processSavedOps(pendingRuntimeState);
|
|
466
496
|
// delete these once runtime has seen them to save space
|
|
467
497
|
pendingRuntimeState.savedOps = [];
|
|
468
498
|
}
|
|
469
|
-
|
|
499
|
+
// Initialize the base state of the runtime before it's returned.
|
|
500
|
+
await runtime.initializeBaseState();
|
|
470
501
|
return runtime;
|
|
471
502
|
}
|
|
472
503
|
get options() {
|
|
@@ -573,6 +604,13 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
573
604
|
? this.summaryConfiguration.initialSummarizerDelayMs
|
|
574
605
|
: 0;
|
|
575
606
|
}
|
|
607
|
+
/**
|
|
608
|
+
* Initializes the state from the base snapshot this container runtime loaded from.
|
|
609
|
+
*/
|
|
610
|
+
async initializeBaseState() {
|
|
611
|
+
await this.initializeBaseSnapshotBlobs();
|
|
612
|
+
await this.garbageCollector.initializeBaseState();
|
|
613
|
+
}
|
|
576
614
|
dispose(error) {
|
|
577
615
|
var _a;
|
|
578
616
|
if (this._disposed) {
|
|
@@ -671,13 +709,16 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
671
709
|
return (_a = this.dataStores.aliases.get(maybeAlias)) !== null && _a !== void 0 ? _a : maybeAlias;
|
|
672
710
|
}
|
|
673
711
|
async getDataStoreFromRequest(id, request) {
|
|
674
|
-
var _a, _b, _c;
|
|
712
|
+
var _a, _b, _c, _d, _e;
|
|
675
713
|
const wait = typeof ((_a = request.headers) === null || _a === void 0 ? void 0 : _a[RuntimeHeaders.wait]) === "boolean"
|
|
676
714
|
? (_b = request.headers) === null || _b === void 0 ? void 0 : _b[RuntimeHeaders.wait]
|
|
677
715
|
: true;
|
|
716
|
+
const viaHandle = typeof ((_c = request.headers) === null || _c === void 0 ? void 0 : _c[RuntimeHeaders.viaHandle]) === "boolean"
|
|
717
|
+
? (_d = request.headers) === null || _d === void 0 ? void 0 : _d[RuntimeHeaders.viaHandle]
|
|
718
|
+
: false;
|
|
678
719
|
await this.dataStores.waitIfPendingAlias(id);
|
|
679
720
|
const internalId = this.internalId(id);
|
|
680
|
-
const dataStoreContext = await this.dataStores.getDataStore(internalId, wait);
|
|
721
|
+
const dataStoreContext = await this.dataStores.getDataStore(internalId, wait, viaHandle);
|
|
681
722
|
/**
|
|
682
723
|
* If GC should run and this an external app request with "externalRequest" header, we need to return
|
|
683
724
|
* an error if the data store being requested is marked as unreferenced as per the data store's base
|
|
@@ -686,7 +727,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
686
727
|
* This is a workaround to handle scenarios where a data store shared with an external app is deleted
|
|
687
728
|
* and marked as unreferenced by GC. Returning an error will fail to load the data store for the app.
|
|
688
729
|
*/
|
|
689
|
-
if (((
|
|
730
|
+
if (((_e = request.headers) === null || _e === void 0 ? void 0 : _e[RuntimeHeaders.externalRequest]) && this.garbageCollector.shouldRunGC) {
|
|
690
731
|
// The data store is referenced if used routes in the base summary has a route to self.
|
|
691
732
|
// Older documents may not have used routes in the summary. They are considered referenced.
|
|
692
733
|
const usedRoutes = (await dataStoreContext.getBaseGCDetails()).usedRoutes;
|
|
@@ -733,11 +774,9 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
733
774
|
if (Object.keys(blobManagerSummary.summary.tree).length > 0) {
|
|
734
775
|
(0, runtime_utils_1.addTreeToSummary)(summaryTree, summaryFormat_1.blobsTreeName, blobManagerSummary);
|
|
735
776
|
}
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
(0, runtime_utils_1.addSummarizeResultToSummary)(summaryTree, garbageCollection_1.gcTreeKey, gcSummary);
|
|
740
|
-
}
|
|
777
|
+
const gcSummary = this.garbageCollector.summarize(fullTree, trackState, telemetryContext);
|
|
778
|
+
if (gcSummary !== undefined) {
|
|
779
|
+
(0, runtime_utils_1.addSummarizeResultToSummary)(summaryTree, garbageCollection_1.gcTreeKey, gcSummary);
|
|
741
780
|
}
|
|
742
781
|
}
|
|
743
782
|
// Track how many times the container tries to reconnect with pending messages.
|
|
@@ -893,6 +932,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
893
932
|
if (typeof message.contents === "string" && message.contents !== "") {
|
|
894
933
|
message.contents = JSON.parse(message.contents);
|
|
895
934
|
}
|
|
935
|
+
message = this.opDecompressor.processMessage(message);
|
|
896
936
|
// Caveat: This will return false for runtime message in very old format, that are used in snapshot tests
|
|
897
937
|
// This format was not shipped to production workflows.
|
|
898
938
|
const runtimeMessage = unpackRuntimeMessage(message);
|
|
@@ -1010,27 +1050,14 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1010
1050
|
async getRootDataStoreChannel(id, wait = true) {
|
|
1011
1051
|
await this.dataStores.waitIfPendingAlias(id);
|
|
1012
1052
|
const internalId = this.internalId(id);
|
|
1013
|
-
const context = await this.dataStores.getDataStore(internalId, wait);
|
|
1053
|
+
const context = await this.dataStores.getDataStore(internalId, wait, false /* viaHandle */);
|
|
1014
1054
|
(0, common_utils_1.assert)(await context.isRoot(), 0x12b /* "did not get root data store" */);
|
|
1015
1055
|
return context.realize();
|
|
1016
1056
|
}
|
|
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
|
-
}
|
|
1057
|
+
/**
|
|
1058
|
+
* Flush the pending ops manually.
|
|
1059
|
+
* This method is expected to be called at the end of a batch.
|
|
1060
|
+
*/
|
|
1034
1061
|
flush() {
|
|
1035
1062
|
(0, common_utils_1.assert)(this._orderSequentiallyCalls === 0, 0x24c /* "Cannot call `flush()` from `orderSequentially`'s callback" */);
|
|
1036
1063
|
this.flushBatch(this.pendingAttachBatch.popBatch());
|
|
@@ -1038,6 +1065,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1038
1065
|
(0, common_utils_1.assert)(this.emptyBatch, 0x3cf /* reentrancy */);
|
|
1039
1066
|
}
|
|
1040
1067
|
flushBatch(batch) {
|
|
1068
|
+
var _a;
|
|
1041
1069
|
const length = batch.length;
|
|
1042
1070
|
if (length > 1) {
|
|
1043
1071
|
batch[0].metadata = Object.assign(Object.assign({}, batch[0].metadata), { batch: true });
|
|
@@ -1068,6 +1096,10 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1068
1096
|
// Legacy path - supporting old loader versions. Can be removed only when LTS moves above
|
|
1069
1097
|
// version that has support for batches (submitBatchFn)
|
|
1070
1098
|
for (const message of batch) {
|
|
1099
|
+
// Legacy path doesn't support compressed payloads and will submit uncompressed payload anyways
|
|
1100
|
+
if ((_a = message.metadata) === null || _a === void 0 ? void 0 : _a.compressed) {
|
|
1101
|
+
delete message.metadata.compressed;
|
|
1102
|
+
}
|
|
1071
1103
|
clientSequenceNumber = this.context.submitFn(protocol_definitions_1.MessageType.Operation, message.deserializedContent, true, // batch
|
|
1072
1104
|
message.metadata);
|
|
1073
1105
|
}
|
|
@@ -1086,26 +1118,6 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1086
1118
|
this.pendingStateManager.onFlush();
|
|
1087
1119
|
}
|
|
1088
1120
|
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
1121
|
let checkpoint;
|
|
1110
1122
|
if (this.mc.config.getBoolean("Fluid.ContainerRuntime.EnableRollback")) {
|
|
1111
1123
|
// Note: we are not touching this.pendingAttachBatch here, for two reasons:
|
|
@@ -1140,6 +1152,9 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1140
1152
|
finally {
|
|
1141
1153
|
this._orderSequentiallyCalls--;
|
|
1142
1154
|
}
|
|
1155
|
+
if (this.flushMode === runtime_definitions_1.FlushMode.Immediate && this._orderSequentiallyCalls === 0) {
|
|
1156
|
+
this.flush();
|
|
1157
|
+
}
|
|
1143
1158
|
}
|
|
1144
1159
|
async createDataStore(pkg) {
|
|
1145
1160
|
const internalId = (0, uuid_1.v4)();
|
|
@@ -1166,6 +1181,12 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1166
1181
|
canSendOps() {
|
|
1167
1182
|
return this.connected && !this.deltaManager.readOnlyInfo.readonly;
|
|
1168
1183
|
}
|
|
1184
|
+
/**
|
|
1185
|
+
* Are we in the middle of batching ops together?
|
|
1186
|
+
*/
|
|
1187
|
+
currentlyBatching() {
|
|
1188
|
+
return this.flushMode === runtime_definitions_1.FlushMode.TurnBased || this._orderSequentiallyCalls !== 0;
|
|
1189
|
+
}
|
|
1169
1190
|
getQuorum() {
|
|
1170
1191
|
return this.context.quorum;
|
|
1171
1192
|
}
|
|
@@ -1316,10 +1337,8 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1316
1337
|
* Implementation of IGarbageCollectionRuntime::updateUsedRoutes.
|
|
1317
1338
|
* After GC has run, called to notify this container's nodes of routes that are used in it.
|
|
1318
1339
|
* @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
1340
|
*/
|
|
1322
|
-
updateUsedRoutes(usedRoutes
|
|
1341
|
+
updateUsedRoutes(usedRoutes) {
|
|
1323
1342
|
// Update our summarizer node's used routes. Updating used routes in summarizer node before
|
|
1324
1343
|
// summarizing is required and asserted by the the summarizer node. We are the root and are
|
|
1325
1344
|
// always referenced, so the used routes is only self-route (empty string).
|
|
@@ -1330,14 +1349,16 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1330
1349
|
dataStoreUsedRoutes.push(route);
|
|
1331
1350
|
}
|
|
1332
1351
|
}
|
|
1333
|
-
return this.dataStores.updateUsedRoutes(dataStoreUsedRoutes
|
|
1352
|
+
return this.dataStores.updateUsedRoutes(dataStoreUsedRoutes);
|
|
1334
1353
|
}
|
|
1335
1354
|
/**
|
|
1336
|
-
*
|
|
1337
|
-
*
|
|
1355
|
+
* This is called to update objects whose routes are unused. The unused objects are either deleted or marked as
|
|
1356
|
+
* tombstones.
|
|
1338
1357
|
* @param unusedRoutes - The routes that are unused in all data stores in this Container.
|
|
1358
|
+
* @param tombstone - if true, the objects corresponding to unused routes are marked tombstones. Otherwise, they
|
|
1359
|
+
* are deleted.
|
|
1339
1360
|
*/
|
|
1340
|
-
|
|
1361
|
+
updateUnusedRoutes(unusedRoutes, tombstone) {
|
|
1341
1362
|
const blobManagerUnusedRoutes = [];
|
|
1342
1363
|
const dataStoreUnusedRoutes = [];
|
|
1343
1364
|
for (const route of unusedRoutes) {
|
|
@@ -1348,8 +1369,11 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1348
1369
|
dataStoreUnusedRoutes.push(route);
|
|
1349
1370
|
}
|
|
1350
1371
|
}
|
|
1351
|
-
|
|
1352
|
-
|
|
1372
|
+
// Todo: Add tombstone for attachment blobs. For now, we ignore attachment blobs that should be tombstoned.
|
|
1373
|
+
if (!tombstone) {
|
|
1374
|
+
this.blobManager.deleteUnusedRoutes(blobManagerUnusedRoutes);
|
|
1375
|
+
}
|
|
1376
|
+
this.dataStores.updateUnusedRoutes(dataStoreUnusedRoutes, tombstone);
|
|
1353
1377
|
}
|
|
1354
1378
|
/**
|
|
1355
1379
|
* Returns a server generated referenced timestamp to be used to track unreferenced nodes by GC.
|
|
@@ -1435,15 +1459,8 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1435
1459
|
const latestSnapshotInfo = await this.refreshLatestSummaryAckFromServer(telemetry_utils_1.ChildLogger.create(summaryNumberLogger, undefined, { all: { safeSummary: true } }));
|
|
1436
1460
|
const latestSnapshotRefSeq = latestSnapshotInfo.latestSnapshotRefSeq;
|
|
1437
1461
|
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
|
-
}
|
|
1462
|
+
// We might need to catch up to the latest summary's reference sequence number before pausing.
|
|
1463
|
+
await this.waitForDeltaManagerToCatchup(latestSnapshotRefSeq, summaryNumberLogger);
|
|
1447
1464
|
}
|
|
1448
1465
|
try {
|
|
1449
1466
|
await this.deltaManager.inbound.pause();
|
|
@@ -1680,6 +1697,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1680
1697
|
return this.blobManager.createBlob(blob);
|
|
1681
1698
|
}
|
|
1682
1699
|
submit(type, contents, localOpMetadata = undefined, metadata = undefined) {
|
|
1700
|
+
var _a, _b, _c, _d;
|
|
1683
1701
|
this.verifyNotClosed();
|
|
1684
1702
|
// There should be no ops in detached container state!
|
|
1685
1703
|
(0, common_utils_1.assert)(this.attachState !== container_definitions_1.AttachState.Detached, 0x132 /* "sending ops in detached container" */);
|
|
@@ -1716,8 +1734,8 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1716
1734
|
// issue than sending.
|
|
1717
1735
|
// Please note that this does not change file format, so it can be disabled in the future if this
|
|
1718
1736
|
// optimization no longer makes sense (for example, batch compression may make it less appealing).
|
|
1719
|
-
if (type === ContainerMessageType.Attach &&
|
|
1720
|
-
this.mc.config.getBoolean("Fluid.ContainerRuntime.
|
|
1737
|
+
if (this.currentlyBatching() && type === ContainerMessageType.Attach &&
|
|
1738
|
+
this.mc.config.getBoolean("Fluid.ContainerRuntime.enableAttachOpReorder") === true) {
|
|
1721
1739
|
if (!this.pendingAttachBatch.push(message)) {
|
|
1722
1740
|
// BatchManager has two limits - soft limit & hard limit. Soft limit is only engaged
|
|
1723
1741
|
// when queue is not empty.
|
|
@@ -1726,9 +1744,9 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1726
1744
|
if (!this.pendingAttachBatch.push(message)) {
|
|
1727
1745
|
throw new container_utils_1.GenericError("BatchTooLarge",
|
|
1728
1746
|
/* error */ undefined, {
|
|
1729
|
-
opSize: message.contents.length,
|
|
1747
|
+
opSize: (_b = ((_a = message.contents) === null || _a === void 0 ? void 0 : _a.length)) !== null && _b !== void 0 ? _b : 0,
|
|
1730
1748
|
count: this.pendingAttachBatch.length,
|
|
1731
|
-
limit: this.pendingAttachBatch.
|
|
1749
|
+
limit: this.pendingAttachBatch.options.hardLimit,
|
|
1732
1750
|
});
|
|
1733
1751
|
}
|
|
1734
1752
|
}
|
|
@@ -1737,23 +1755,23 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1737
1755
|
if (!this.pendingBatch.push(message)) {
|
|
1738
1756
|
throw new container_utils_1.GenericError("BatchTooLarge",
|
|
1739
1757
|
/* error */ undefined, {
|
|
1740
|
-
opSize: message.contents.length,
|
|
1758
|
+
opSize: (_d = ((_c = message.contents) === null || _c === void 0 ? void 0 : _c.length)) !== null && _d !== void 0 ? _d : 0,
|
|
1741
1759
|
count: this.pendingBatch.length,
|
|
1742
|
-
limit: this.pendingBatch.
|
|
1760
|
+
limit: this.pendingBatch.options.hardLimit,
|
|
1743
1761
|
});
|
|
1744
1762
|
}
|
|
1745
|
-
|
|
1746
|
-
if (this._flushMode !== runtime_definitions_1.FlushMode.TurnBased) {
|
|
1747
|
-
this.flush();
|
|
1748
|
-
}
|
|
1749
|
-
else if (!this.flushTrigger) {
|
|
1750
|
-
this.flushTrigger = true;
|
|
1751
|
-
// Queue a microtask to detect the end of the turn and force a flush.
|
|
1752
|
-
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
1753
|
-
Promise.resolve().then(() => {
|
|
1754
|
-
this.flushTrigger = false;
|
|
1763
|
+
if (!this.currentlyBatching()) {
|
|
1755
1764
|
this.flush();
|
|
1756
|
-
}
|
|
1765
|
+
}
|
|
1766
|
+
else if (!this.flushMicroTaskExists) {
|
|
1767
|
+
this.flushMicroTaskExists = true;
|
|
1768
|
+
// Queue a microtask to detect the end of the turn and force a flush.
|
|
1769
|
+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
1770
|
+
Promise.resolve().then(() => {
|
|
1771
|
+
this.flushMicroTaskExists = false;
|
|
1772
|
+
this.flush();
|
|
1773
|
+
});
|
|
1774
|
+
}
|
|
1757
1775
|
}
|
|
1758
1776
|
}
|
|
1759
1777
|
catch (error) {
|
|
@@ -1823,18 +1841,41 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1823
1841
|
throw new Error(`Can't rollback ${type}`);
|
|
1824
1842
|
}
|
|
1825
1843
|
}
|
|
1844
|
+
async waitForDeltaManagerToCatchup(latestSnapshotRefSeq, summaryLogger) {
|
|
1845
|
+
if (latestSnapshotRefSeq > this.deltaManager.lastSequenceNumber) {
|
|
1846
|
+
// We need to catch up to the latest summary's reference sequence number before proceeding.
|
|
1847
|
+
await telemetry_utils_1.PerformanceEvent.timedExecAsync(summaryLogger, {
|
|
1848
|
+
eventName: "WaitingForSeq",
|
|
1849
|
+
lastSequenceNumber: this.deltaManager.lastSequenceNumber,
|
|
1850
|
+
targetSequenceNumber: latestSnapshotRefSeq,
|
|
1851
|
+
lastKnownSeqNumber: this.deltaManager.lastKnownSeqNumber,
|
|
1852
|
+
}, async () => waitForSeq(this.deltaManager, latestSnapshotRefSeq), { start: true, end: true, cancel: "error" });
|
|
1853
|
+
}
|
|
1854
|
+
}
|
|
1826
1855
|
/** Implementation of ISummarizerInternalsProvider.refreshLatestSummaryAck */
|
|
1827
|
-
async refreshLatestSummaryAck(
|
|
1856
|
+
async refreshLatestSummaryAck(options) {
|
|
1857
|
+
const { proposalHandle, ackHandle, summaryRefSeq, summaryLogger } = options;
|
|
1828
1858
|
const readAndParseBlob = async (id) => (0, driver_utils_1.readAndParse)(this.storage, id);
|
|
1829
1859
|
// The call to fetch the snapshot is very expensive and not always needed.
|
|
1830
1860
|
// It should only be done by the summarizerNode, if required.
|
|
1861
|
+
// When fetching from storage we will always get the latest version and do not use the ackHandle.
|
|
1831
1862
|
const snapshotTreeFetcher = async () => {
|
|
1832
|
-
const fetchResult = await this.fetchSnapshotFromStorage(
|
|
1863
|
+
const fetchResult = await this.fetchSnapshotFromStorage(null, summaryLogger, {
|
|
1833
1864
|
eventName: "RefreshLatestSummaryGetSnapshot",
|
|
1834
1865
|
ackHandle,
|
|
1835
1866
|
summaryRefSeq,
|
|
1836
|
-
fetchLatest:
|
|
1867
|
+
fetchLatest: true,
|
|
1868
|
+
});
|
|
1869
|
+
const latestSnapshotRefSeq = await (0, runtime_utils_1.seqFromTree)(fetchResult.snapshotTree, readAndParseBlob);
|
|
1870
|
+
summaryLogger.sendTelemetryEvent({
|
|
1871
|
+
eventName: "LatestSummaryRetrieved",
|
|
1872
|
+
ackHandle,
|
|
1873
|
+
lastSequenceNumber: latestSnapshotRefSeq,
|
|
1874
|
+
targetSequenceNumber: summaryRefSeq,
|
|
1837
1875
|
});
|
|
1876
|
+
// In case we had to retrieve the latest snapshot and it is different than summaryRefSeq,
|
|
1877
|
+
// wait for the delta manager to catch up before refreshing the latest Summary.
|
|
1878
|
+
await this.waitForDeltaManagerToCatchup(latestSnapshotRefSeq, summaryLogger);
|
|
1838
1879
|
return fetchResult.snapshotTree;
|
|
1839
1880
|
};
|
|
1840
1881
|
const result = await this.summarizerNode.refreshLatestSummary(proposalHandle, summaryRefSeq, snapshotTreeFetcher, readAndParseBlob, summaryLogger);
|
|
@@ -1879,7 +1920,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1879
1920
|
this.baseSnapshotBlobs = serializedSnapshotStorage_1.SerializedSnapshotStorage.serializeTreeWithBlobContents(snapshot);
|
|
1880
1921
|
}
|
|
1881
1922
|
}
|
|
1882
|
-
async
|
|
1923
|
+
async initializeBaseSnapshotBlobs() {
|
|
1883
1924
|
var _a;
|
|
1884
1925
|
if (!((_a = this.mc.config.getBoolean("enableOfflineLoad")) !== null && _a !== void 0 ? _a : this.runtimeOptions.enableOfflineLoad) ||
|
|
1885
1926
|
this.attachState !== container_definitions_1.AttachState.Attached || this.context.pendingLocalState) {
|
|
@@ -1897,6 +1938,9 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1897
1938
|
// getPendingLocalState() is only exposed through Container.closeAndGetPendingLocalState(), so it's safe
|
|
1898
1939
|
// to close current batch.
|
|
1899
1940
|
this.flush();
|
|
1941
|
+
if (this._orderSequentiallyCalls !== 0) {
|
|
1942
|
+
throw new container_utils_1.UsageError("can't get state during orderSequentially");
|
|
1943
|
+
}
|
|
1900
1944
|
const previousPendingState = this.context.pendingLocalState;
|
|
1901
1945
|
if (previousPendingState) {
|
|
1902
1946
|
return {
|
|
@@ -1964,6 +2008,9 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1964
2008
|
throw new container_utils_1.UsageError(`Summary heuristic configuration property "${prop}" cannot be less than 0`);
|
|
1965
2009
|
}
|
|
1966
2010
|
}
|
|
2011
|
+
if (configuration.minIdleTime > configuration.maxIdleTime) {
|
|
2012
|
+
throw new container_utils_1.UsageError(`"minIdleTime" [${configuration.minIdleTime}] cannot be greater than "maxIdleTime" [${configuration.maxIdleTime}]`);
|
|
2013
|
+
}
|
|
1967
2014
|
}
|
|
1968
2015
|
}
|
|
1969
2016
|
exports.ContainerRuntime = ContainerRuntime;
|
|
@@ -1974,12 +2021,18 @@ exports.ContainerRuntime = ContainerRuntime;
|
|
|
1974
2021
|
const waitForSeq = async (deltaManager, targetSeq) => new Promise((resolve, reject) => {
|
|
1975
2022
|
// TODO: remove cast to any when actual event is determined
|
|
1976
2023
|
deltaManager.on("closed", reject);
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
2024
|
+
// If we already reached target sequence number, simply resolve the promise.
|
|
2025
|
+
if (deltaManager.lastSequenceNumber >= targetSeq) {
|
|
2026
|
+
resolve();
|
|
2027
|
+
}
|
|
2028
|
+
else {
|
|
2029
|
+
const handleOp = (message) => {
|
|
2030
|
+
if (message.sequenceNumber >= targetSeq) {
|
|
2031
|
+
resolve();
|
|
2032
|
+
deltaManager.off("op", handleOp);
|
|
2033
|
+
}
|
|
2034
|
+
};
|
|
2035
|
+
deltaManager.on("op", handleOp);
|
|
2036
|
+
}
|
|
1984
2037
|
});
|
|
1985
2038
|
//# sourceMappingURL=containerRuntime.js.map
|