@fluidframework/container-runtime 2.0.0-internal.1.4.2 → 2.0.0-internal.2.0.0
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/dist/batchManager.d.ts +2 -3
- package/dist/batchManager.d.ts.map +1 -1
- package/dist/batchManager.js +3 -8
- package/dist/batchManager.js.map +1 -1
- package/dist/containerRuntime.d.ts +43 -16
- package/dist/containerRuntime.d.ts.map +1 -1
- package/dist/containerRuntime.js +107 -83
- package/dist/containerRuntime.js.map +1 -1
- package/dist/dataStoreContext.d.ts +4 -20
- package/dist/dataStoreContext.d.ts.map +1 -1
- package/dist/dataStoreContext.js +17 -47
- package/dist/dataStoreContext.js.map +1 -1
- package/dist/dataStores.d.ts +2 -5
- package/dist/dataStores.d.ts.map +1 -1
- package/dist/dataStores.js +3 -11
- package/dist/dataStores.js.map +1 -1
- package/dist/garbageCollection.d.ts +1 -10
- package/dist/garbageCollection.d.ts.map +1 -1
- package/dist/garbageCollection.js +43 -51
- 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 +3 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -5
- package/dist/index.js.map +1 -1
- package/dist/packageVersion.d.ts +1 -1
- package/dist/packageVersion.js +1 -1
- package/dist/packageVersion.js.map +1 -1
- package/dist/pendingStateManager.d.ts +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/scheduleManager.js.map +1 -1
- package/dist/summarizer.js +7 -2
- package/dist/summarizer.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/summaryManager.d.ts.map +1 -1
- package/dist/summaryManager.js +10 -6
- package/dist/summaryManager.js.map +1 -1
- package/lib/batchManager.d.ts +2 -3
- package/lib/batchManager.d.ts.map +1 -1
- package/lib/batchManager.js +3 -8
- package/lib/batchManager.js.map +1 -1
- package/lib/containerRuntime.d.ts +43 -16
- package/lib/containerRuntime.d.ts.map +1 -1
- package/lib/containerRuntime.js +108 -84
- package/lib/containerRuntime.js.map +1 -1
- package/lib/dataStoreContext.d.ts +4 -20
- package/lib/dataStoreContext.d.ts.map +1 -1
- package/lib/dataStoreContext.js +18 -48
- package/lib/dataStoreContext.js.map +1 -1
- package/lib/dataStores.d.ts +2 -5
- package/lib/dataStores.d.ts.map +1 -1
- package/lib/dataStores.js +3 -11
- package/lib/dataStores.js.map +1 -1
- package/lib/garbageCollection.d.ts +1 -10
- package/lib/garbageCollection.d.ts.map +1 -1
- package/lib/garbageCollection.js +42 -50
- 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 +3 -5
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +0 -2
- package/lib/index.js.map +1 -1
- package/lib/packageVersion.d.ts +1 -1
- package/lib/packageVersion.js +1 -1
- package/lib/packageVersion.js.map +1 -1
- package/lib/pendingStateManager.d.ts +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/scheduleManager.js.map +1 -1
- package/lib/summarizer.js +7 -2
- package/lib/summarizer.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/summaryManager.d.ts.map +1 -1
- package/lib/summaryManager.js +10 -6
- package/lib/summaryManager.js.map +1 -1
- package/package.json +40 -38
- package/src/batchManager.ts +7 -11
- package/src/containerRuntime.ts +149 -102
- package/src/dataStoreContext.ts +20 -62
- package/src/dataStores.ts +2 -10
- package/src/garbageCollection.ts +45 -55
- package/src/gcSweepReadyUsageDetection.ts +2 -10
- package/src/index.ts +2 -3
- package/src/packageVersion.ts +1 -1
- package/src/pendingStateManager.ts +57 -96
- package/src/scheduleManager.ts +1 -0
- package/src/summarizer.ts +6 -6
- package/src/summarizerTypes.ts +20 -7
- package/src/summaryFormat.ts +4 -2
- package/src/summaryManager.ts +18 -7
package/dist/containerRuntime.js
CHANGED
|
@@ -12,6 +12,7 @@ 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");
|
|
15
16
|
const containerHandleContext_1 = require("./containerHandleContext");
|
|
16
17
|
const dataStoreRegistry_1 = require("./dataStoreRegistry");
|
|
17
18
|
const summarizer_1 = require("./summarizer");
|
|
@@ -50,7 +51,6 @@ var ContainerMessageType;
|
|
|
50
51
|
})(ContainerMessageType = exports.ContainerMessageType || (exports.ContainerMessageType = {}));
|
|
51
52
|
exports.DefaultSummaryConfiguration = {
|
|
52
53
|
state: "enabled",
|
|
53
|
-
idleTime: 15 * 1000,
|
|
54
54
|
minIdleTime: 0,
|
|
55
55
|
maxIdleTime: 30 * 1000,
|
|
56
56
|
maxTime: 60 * 1000,
|
|
@@ -80,6 +80,11 @@ var RuntimeHeaders;
|
|
|
80
80
|
})(RuntimeHeaders = exports.RuntimeHeaders || (exports.RuntimeHeaders = {}));
|
|
81
81
|
const maxConsecutiveReconnectsKey = "Fluid.ContainerRuntime.MaxConsecutiveReconnects";
|
|
82
82
|
const defaultFlushMode = runtime_definitions_1.FlushMode.TurnBased;
|
|
83
|
+
// The actual limit is 1Mb (socket.io and Kafka limits)
|
|
84
|
+
// We can't estimate it fully, as we
|
|
85
|
+
// - do not know what properties relay service will add
|
|
86
|
+
// - we do not stringify final op, thus we do not know how much escaping will be added.
|
|
87
|
+
const defaultMaxBatchSizeInBytes = 950 * 1024;
|
|
83
88
|
/**
|
|
84
89
|
* @deprecated - use ContainerRuntimeMessage instead
|
|
85
90
|
*/
|
|
@@ -97,22 +102,28 @@ var RuntimeMessage;
|
|
|
97
102
|
* @deprecated - please use version in driver-utils
|
|
98
103
|
*/
|
|
99
104
|
function isRuntimeMessage(message) {
|
|
100
|
-
|
|
101
|
-
return true;
|
|
102
|
-
}
|
|
103
|
-
return false;
|
|
105
|
+
return Object.values(RuntimeMessage).includes(message.type);
|
|
104
106
|
}
|
|
105
107
|
exports.isRuntimeMessage = isRuntimeMessage;
|
|
106
108
|
/**
|
|
107
109
|
* Unpacks runtime messages
|
|
108
110
|
*
|
|
109
|
-
* @remarks This API makes no promises regarding backward-
|
|
111
|
+
* @remarks This API makes no promises regarding backward-compatibility. This is internal API.
|
|
110
112
|
* @param message - message (as it observed in storage / service)
|
|
111
113
|
* @returns unpacked runtime message
|
|
112
114
|
*
|
|
113
115
|
* @internal
|
|
114
116
|
*/
|
|
115
117
|
function unpackRuntimeMessage(message) {
|
|
118
|
+
var _a;
|
|
119
|
+
if ((_a = message.metadata) === null || _a === void 0 ? void 0 : _a.compressed) {
|
|
120
|
+
const contents = common_utils_1.IsoBuffer.from(message.contents.contents, "base64");
|
|
121
|
+
const decompressedMessage = (0, lz4js_1.decompress)(contents);
|
|
122
|
+
const intoString = new TextDecoder().decode(decompressedMessage);
|
|
123
|
+
const asObj = JSON.parse(intoString);
|
|
124
|
+
message.contents.contents = asObj;
|
|
125
|
+
message.metadata.compressed = false;
|
|
126
|
+
}
|
|
116
127
|
if (message.type === protocol_definitions_1.MessageType.Operation) {
|
|
117
128
|
// legacy op format?
|
|
118
129
|
if (message.contents.address !== undefined && message.contents.type === undefined) {
|
|
@@ -163,7 +174,7 @@ exports.getDeviceSpec = getDeviceSpec;
|
|
|
163
174
|
*/
|
|
164
175
|
class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
165
176
|
constructor(context, registry, metadata, electedSummarizerData, chunks, dataStoreAliasMap, runtimeOptions, containerScope, logger, existing, blobManagerSnapshot, _storage, requestHandler, summaryConfiguration) {
|
|
166
|
-
var _a, _b, _c, _d;
|
|
177
|
+
var _a, _b, _c, _d, _e, _f;
|
|
167
178
|
if (summaryConfiguration === void 0) { summaryConfiguration = Object.assign(Object.assign({}, exports.DefaultSummaryConfiguration), (_a = runtimeOptions.summaryOptions) === null || _a === void 0 ? void 0 : _a.summaryConfigOverrides); }
|
|
168
179
|
super();
|
|
169
180
|
this.context = context;
|
|
@@ -176,9 +187,10 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
176
187
|
this.summaryConfiguration = summaryConfiguration;
|
|
177
188
|
this.defaultMaxConsecutiveReconnects = 7;
|
|
178
189
|
this._orderSequentiallyCalls = 0;
|
|
179
|
-
this.
|
|
190
|
+
this.flushMicroTaskExists = false;
|
|
180
191
|
this.savedOps = [];
|
|
181
192
|
this.consecutiveReconnects = 0;
|
|
193
|
+
this.compressedOpCount = 0;
|
|
182
194
|
this._disposed = false;
|
|
183
195
|
this.emitDirtyDocumentEvent = true;
|
|
184
196
|
this.defaultTelemetrySignalSampleCount = 100;
|
|
@@ -188,12 +200,6 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
188
200
|
signalTimestamp: 0,
|
|
189
201
|
trackingSignalSequenceNumber: undefined,
|
|
190
202
|
};
|
|
191
|
-
// Provide lower soft limit - we want to have some number of ops to get efficiency in compression & bandwidth usage,
|
|
192
|
-
// but at the same time we want to send these ops sooner, to reduce overall latency of processing a batch.
|
|
193
|
-
// So there is some ballance here, that depends on compression algorithm and its efficiency working with smaller
|
|
194
|
-
// payloads. That number represents final (compressed) bits (once compression is implemented).
|
|
195
|
-
this.pendingAttachBatch = new batchManager_1.BatchManager(64 * 1024);
|
|
196
|
-
this.pendingBatch = new batchManager_1.BatchManager();
|
|
197
203
|
this.summarizeOnDemand = (...args) => {
|
|
198
204
|
if (this.clientDetails.type === summarizerClientElection_1.summarizerClientType) {
|
|
199
205
|
return this.summarizer.summarizeOnDemand(...args);
|
|
@@ -238,8 +244,22 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
238
244
|
this.maxConsecutiveReconnects =
|
|
239
245
|
(_b = this.mc.config.getNumber(maxConsecutiveReconnectsKey)) !== null && _b !== void 0 ? _b : this.defaultMaxConsecutiveReconnects;
|
|
240
246
|
this._flushMode = runtimeOptions.flushMode;
|
|
247
|
+
// Provide lower soft limit - we want to have some number of ops to get efficiency in compression
|
|
248
|
+
// & bandwidth usage, but at the same time we want to send these ops sooner, to reduce overall
|
|
249
|
+
// latency of processing a batch.
|
|
250
|
+
// So there is some ballance here, that depends on compression algorithm and its efficiency working with smaller
|
|
251
|
+
// payloads. That number represents final (compressed) bits (once compression is implemented).
|
|
252
|
+
this.pendingAttachBatch = new batchManager_1.BatchManager(runtimeOptions.maxBatchSizeInBytes, 64 * 1024);
|
|
253
|
+
this.pendingBatch = new batchManager_1.BatchManager(runtimeOptions.maxBatchSizeInBytes);
|
|
241
254
|
const pendingRuntimeState = context.pendingLocalState;
|
|
242
255
|
const baseSnapshot = (_c = pendingRuntimeState === null || pendingRuntimeState === void 0 ? void 0 : pendingRuntimeState.baseSnapshot) !== null && _c !== void 0 ? _c : context.baseSnapshot;
|
|
256
|
+
const maxSnapshotCacheDurationMs = (_e = (_d = this._storage) === null || _d === void 0 ? void 0 : _d.policies) === null || _e === void 0 ? void 0 : _e.maximumCacheDurationMs;
|
|
257
|
+
if (maxSnapshotCacheDurationMs !== undefined && maxSnapshotCacheDurationMs > 5 * 24 * 60 * 60 * 1000) {
|
|
258
|
+
// This is a runtime enforcement of what's already explicit in the policy's type itself,
|
|
259
|
+
// which dictates the value is either undefined or exactly 5 days in ms.
|
|
260
|
+
// As long as the actual value is less than 5 days, the assumptions GC makes here are valid.
|
|
261
|
+
throw new container_utils_1.UsageError("Driver's maximumCacheDurationMs policy cannot exceed 5 days");
|
|
262
|
+
}
|
|
243
263
|
this.garbageCollector = garbageCollection_1.GarbageCollector.create({
|
|
244
264
|
runtime: this,
|
|
245
265
|
gcOptions: this.runtimeOptions.gcOptions,
|
|
@@ -272,9 +292,9 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
272
292
|
gcDisabled: !this.garbageCollector.shouldRunGC,
|
|
273
293
|
});
|
|
274
294
|
if (baseSnapshot) {
|
|
275
|
-
this.summarizerNode.
|
|
295
|
+
this.summarizerNode.updateBaseSummaryState(baseSnapshot);
|
|
276
296
|
}
|
|
277
|
-
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)
|
|
297
|
+
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));
|
|
278
298
|
this.blobManager = new blobManager_1.BlobManager(this.handleContext, blobManagerSnapshot, () => this.storage, (blobId, localId) => {
|
|
279
299
|
if (!this.disposed) {
|
|
280
300
|
this.submit(ContainerMessageType.BlobAttach, undefined, undefined, { blobId, localId });
|
|
@@ -288,10 +308,10 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
288
308
|
close: this.closeFn,
|
|
289
309
|
connected: () => this.connected,
|
|
290
310
|
flush: this.flush.bind(this),
|
|
291
|
-
flushMode: () => this.flushMode,
|
|
292
311
|
reSubmit: this.reSubmit.bind(this),
|
|
293
|
-
|
|
294
|
-
|
|
312
|
+
rollback: this.rollback.bind(this),
|
|
313
|
+
orderSequentially: this.orderSequentially.bind(this),
|
|
314
|
+
}, pendingRuntimeState === null || pendingRuntimeState === void 0 ? void 0 : pendingRuntimeState.pending);
|
|
295
315
|
this.context.quorum.on("removeMember", (clientId) => {
|
|
296
316
|
this.clearPartialChunks(clientId);
|
|
297
317
|
});
|
|
@@ -315,7 +335,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
315
335
|
// if summaries are enabled and we are not the summarizer client.
|
|
316
336
|
const defaultAction = () => {
|
|
317
337
|
if (this.summaryCollection.opsSinceLastAck > this.maxOpsSinceLastSummary) {
|
|
318
|
-
this.logger.
|
|
338
|
+
this.logger.sendTelemetryEvent({ eventName: "SummaryStatus:Behind" });
|
|
319
339
|
// unregister default to no log on every op after falling behind
|
|
320
340
|
// and register summary ack handler to re-register this handler
|
|
321
341
|
// after successful summary
|
|
@@ -371,7 +391,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
371
391
|
};
|
|
372
392
|
// summaryNumber was renamed from summaryCount. For older docs that haven't been opened for a long time,
|
|
373
393
|
// the count is reset to 0.
|
|
374
|
-
loadSummaryNumber = (
|
|
394
|
+
loadSummaryNumber = (_f = metadata === null || metadata === void 0 ? void 0 : metadata.summaryNumber) !== null && _f !== void 0 ? _f : 0;
|
|
375
395
|
}
|
|
376
396
|
else {
|
|
377
397
|
this.createContainerMetadata = {
|
|
@@ -406,7 +426,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
406
426
|
runtimeVersion: packageVersion_1.pkgVersion,
|
|
407
427
|
},
|
|
408
428
|
});
|
|
409
|
-
const { summaryOptions = {}, gcOptions = {}, loadSequenceNumberVerification = "close", flushMode = defaultFlushMode, enableOfflineLoad = false, } = runtimeOptions;
|
|
429
|
+
const { summaryOptions = {}, gcOptions = {}, loadSequenceNumberVerification = "close", flushMode = defaultFlushMode, enableOfflineLoad = false, compressionOptions = {}, maxBatchSizeInBytes = defaultMaxBatchSizeInBytes, } = runtimeOptions;
|
|
410
430
|
const pendingRuntimeState = context.pendingLocalState;
|
|
411
431
|
const baseSnapshot = (_b = pendingRuntimeState === null || pendingRuntimeState === void 0 ? void 0 : pendingRuntimeState.baseSnapshot) !== null && _b !== void 0 ? _b : context.baseSnapshot;
|
|
412
432
|
const storage = !pendingRuntimeState ?
|
|
@@ -461,6 +481,8 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
461
481
|
loadSequenceNumberVerification,
|
|
462
482
|
flushMode,
|
|
463
483
|
enableOfflineLoad,
|
|
484
|
+
compressionOptions,
|
|
485
|
+
maxBatchSizeInBytes,
|
|
464
486
|
}, containerScope, logger, loadExisting, blobManagerSnapshot, storage, requestHandler);
|
|
465
487
|
if (pendingRuntimeState) {
|
|
466
488
|
await runtime.processSavedOps(pendingRuntimeState);
|
|
@@ -734,11 +756,9 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
734
756
|
if (Object.keys(blobManagerSummary.summary.tree).length > 0) {
|
|
735
757
|
(0, runtime_utils_1.addTreeToSummary)(summaryTree, summaryFormat_1.blobsTreeName, blobManagerSummary);
|
|
736
758
|
}
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
(0, runtime_utils_1.addSummarizeResultToSummary)(summaryTree, garbageCollection_1.gcTreeKey, gcSummary);
|
|
741
|
-
}
|
|
759
|
+
const gcSummary = this.garbageCollector.summarize(fullTree, trackState, telemetryContext);
|
|
760
|
+
if (gcSummary !== undefined) {
|
|
761
|
+
(0, runtime_utils_1.addSummarizeResultToSummary)(summaryTree, garbageCollection_1.gcTreeKey, gcSummary);
|
|
742
762
|
}
|
|
743
763
|
}
|
|
744
764
|
// Track how many times the container tries to reconnect with pending messages.
|
|
@@ -1015,23 +1035,10 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1015
1035
|
(0, common_utils_1.assert)(await context.isRoot(), 0x12b /* "did not get root data store" */);
|
|
1016
1036
|
return context.realize();
|
|
1017
1037
|
}
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
this.mc.logger.sendTelemetryEvent({
|
|
1023
|
-
eventName: "FlushMode Updated",
|
|
1024
|
-
old: this._flushMode,
|
|
1025
|
-
new: mode,
|
|
1026
|
-
});
|
|
1027
|
-
// Flush any pending batches if switching to immediate
|
|
1028
|
-
if (mode === runtime_definitions_1.FlushMode.Immediate) {
|
|
1029
|
-
this.flush();
|
|
1030
|
-
}
|
|
1031
|
-
this._flushMode = mode;
|
|
1032
|
-
// Let the PendingStateManager know that FlushMode has been updated.
|
|
1033
|
-
this.pendingStateManager.onFlushModeUpdated(mode);
|
|
1034
|
-
}
|
|
1038
|
+
/**
|
|
1039
|
+
* Flush the pending ops manually.
|
|
1040
|
+
* This method is expected to be called at the end of a batch.
|
|
1041
|
+
*/
|
|
1035
1042
|
flush() {
|
|
1036
1043
|
(0, common_utils_1.assert)(this._orderSequentiallyCalls === 0, 0x24c /* "Cannot call `flush()` from `orderSequentially`'s callback" */);
|
|
1037
1044
|
this.flushBatch(this.pendingAttachBatch.popBatch());
|
|
@@ -1060,7 +1067,33 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1060
1067
|
if (this.context.submitBatchFn !== undefined) {
|
|
1061
1068
|
const batchToSend = [];
|
|
1062
1069
|
for (const message of batch) {
|
|
1063
|
-
|
|
1070
|
+
let contents = message.contents;
|
|
1071
|
+
let metadata = message.metadata;
|
|
1072
|
+
if (this.runtimeOptions.compressionOptions.minimumSize &&
|
|
1073
|
+
this.runtimeOptions.compressionOptions.minimumSize < message.contents.length) {
|
|
1074
|
+
this.compressedOpCount++;
|
|
1075
|
+
const copiedMessage = Object.assign({}, message.deserializedContent);
|
|
1076
|
+
const compressionStart = Date.now();
|
|
1077
|
+
const contentsAsBuffer = new TextEncoder().encode(JSON.stringify(copiedMessage.contents));
|
|
1078
|
+
const compressedContents = (0, lz4js_1.compress)(contentsAsBuffer);
|
|
1079
|
+
const compressedContent = common_utils_1.IsoBuffer.from(compressedContents).toString("base64");
|
|
1080
|
+
const duration = Date.now() - compressionStart;
|
|
1081
|
+
if (this.compressedOpCount % 100) {
|
|
1082
|
+
this.mc.logger.sendPerformanceEvent({
|
|
1083
|
+
eventName: "compressedOp",
|
|
1084
|
+
duration,
|
|
1085
|
+
sizeBeforeCompression: message.contents.length,
|
|
1086
|
+
sizeAfterCompression: compressedContent.length,
|
|
1087
|
+
});
|
|
1088
|
+
}
|
|
1089
|
+
copiedMessage.contents = compressedContent;
|
|
1090
|
+
const stringifiedContents = JSON.stringify(copiedMessage);
|
|
1091
|
+
if (stringifiedContents.length < message.contents.length) {
|
|
1092
|
+
contents = JSON.stringify(copiedMessage);
|
|
1093
|
+
metadata = Object.assign(Object.assign({}, message.metadata), { compressed: true });
|
|
1094
|
+
}
|
|
1095
|
+
}
|
|
1096
|
+
batchToSend.push({ contents, metadata });
|
|
1064
1097
|
}
|
|
1065
1098
|
// returns clientSequenceNumber of last message in a batch
|
|
1066
1099
|
clientSequenceNumber = this.context.submitBatchFn(batchToSend);
|
|
@@ -1087,26 +1120,6 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1087
1120
|
this.pendingStateManager.onFlush();
|
|
1088
1121
|
}
|
|
1089
1122
|
orderSequentially(callback) {
|
|
1090
|
-
// If flush mode is already TurnBased we are either
|
|
1091
|
-
// nested in another orderSequentially, or
|
|
1092
|
-
// the app is flushing manually, in which
|
|
1093
|
-
// case this invocation doesn't own
|
|
1094
|
-
// flushing.
|
|
1095
|
-
if (this.flushMode === runtime_definitions_1.FlushMode.TurnBased) {
|
|
1096
|
-
this.trackOrderSequentiallyCalls(callback);
|
|
1097
|
-
return;
|
|
1098
|
-
}
|
|
1099
|
-
const savedFlushMode = this.flushMode;
|
|
1100
|
-
this.setFlushMode(runtime_definitions_1.FlushMode.TurnBased);
|
|
1101
|
-
try {
|
|
1102
|
-
this.trackOrderSequentiallyCalls(callback);
|
|
1103
|
-
this.flush();
|
|
1104
|
-
}
|
|
1105
|
-
finally {
|
|
1106
|
-
this.setFlushMode(savedFlushMode);
|
|
1107
|
-
}
|
|
1108
|
-
}
|
|
1109
|
-
trackOrderSequentiallyCalls(callback) {
|
|
1110
1123
|
let checkpoint;
|
|
1111
1124
|
if (this.mc.config.getBoolean("Fluid.ContainerRuntime.EnableRollback")) {
|
|
1112
1125
|
// Note: we are not touching this.pendingAttachBatch here, for two reasons:
|
|
@@ -1141,6 +1154,9 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1141
1154
|
finally {
|
|
1142
1155
|
this._orderSequentiallyCalls--;
|
|
1143
1156
|
}
|
|
1157
|
+
if (this.flushMode === runtime_definitions_1.FlushMode.Immediate && this._orderSequentiallyCalls === 0) {
|
|
1158
|
+
this.flush();
|
|
1159
|
+
}
|
|
1144
1160
|
}
|
|
1145
1161
|
async createDataStore(pkg) {
|
|
1146
1162
|
const internalId = (0, uuid_1.v4)();
|
|
@@ -1167,6 +1183,12 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1167
1183
|
canSendOps() {
|
|
1168
1184
|
return this.connected && !this.deltaManager.readOnlyInfo.readonly;
|
|
1169
1185
|
}
|
|
1186
|
+
/**
|
|
1187
|
+
* Are we in the middle of batching ops together?
|
|
1188
|
+
*/
|
|
1189
|
+
currentlyBatching() {
|
|
1190
|
+
return this.flushMode === runtime_definitions_1.FlushMode.TurnBased || this._orderSequentiallyCalls !== 0;
|
|
1191
|
+
}
|
|
1170
1192
|
getQuorum() {
|
|
1171
1193
|
return this.context.quorum;
|
|
1172
1194
|
}
|
|
@@ -1317,10 +1339,8 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1317
1339
|
* Implementation of IGarbageCollectionRuntime::updateUsedRoutes.
|
|
1318
1340
|
* After GC has run, called to notify this container's nodes of routes that are used in it.
|
|
1319
1341
|
* @param usedRoutes - The routes that are used in all nodes in this Container.
|
|
1320
|
-
* @param gcTimestamp - The time when GC was run that generated these used routes. If any node node becomes
|
|
1321
|
-
* unreferenced as part of this GC run, this should be used to update the time when it happens.
|
|
1322
1342
|
*/
|
|
1323
|
-
updateUsedRoutes(usedRoutes
|
|
1343
|
+
updateUsedRoutes(usedRoutes) {
|
|
1324
1344
|
// Update our summarizer node's used routes. Updating used routes in summarizer node before
|
|
1325
1345
|
// summarizing is required and asserted by the the summarizer node. We are the root and are
|
|
1326
1346
|
// always referenced, so the used routes is only self-route (empty string).
|
|
@@ -1331,7 +1351,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1331
1351
|
dataStoreUsedRoutes.push(route);
|
|
1332
1352
|
}
|
|
1333
1353
|
}
|
|
1334
|
-
return this.dataStores.updateUsedRoutes(dataStoreUsedRoutes
|
|
1354
|
+
return this.dataStores.updateUsedRoutes(dataStoreUsedRoutes);
|
|
1335
1355
|
}
|
|
1336
1356
|
/**
|
|
1337
1357
|
* When running GC in test mode, this is called to delete objects whose routes are unused. This enables testing
|
|
@@ -1717,8 +1737,8 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1717
1737
|
// issue than sending.
|
|
1718
1738
|
// Please note that this does not change file format, so it can be disabled in the future if this
|
|
1719
1739
|
// optimization no longer makes sense (for example, batch compression may make it less appealing).
|
|
1720
|
-
if (type === ContainerMessageType.Attach &&
|
|
1721
|
-
this.mc.config.getBoolean("Fluid.ContainerRuntime.
|
|
1740
|
+
if (this.currentlyBatching() && type === ContainerMessageType.Attach &&
|
|
1741
|
+
this.mc.config.getBoolean("Fluid.ContainerRuntime.enableAttachOpReorder") === true) {
|
|
1722
1742
|
if (!this.pendingAttachBatch.push(message)) {
|
|
1723
1743
|
// BatchManager has two limits - soft limit & hard limit. Soft limit is only engaged
|
|
1724
1744
|
// when queue is not empty.
|
|
@@ -1743,18 +1763,18 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1743
1763
|
limit: this.pendingBatch.limit,
|
|
1744
1764
|
});
|
|
1745
1765
|
}
|
|
1746
|
-
|
|
1747
|
-
if (this._flushMode !== runtime_definitions_1.FlushMode.TurnBased) {
|
|
1748
|
-
this.flush();
|
|
1749
|
-
}
|
|
1750
|
-
else if (!this.flushTrigger) {
|
|
1751
|
-
this.flushTrigger = true;
|
|
1752
|
-
// Queue a microtask to detect the end of the turn and force a flush.
|
|
1753
|
-
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
1754
|
-
Promise.resolve().then(() => {
|
|
1755
|
-
this.flushTrigger = false;
|
|
1766
|
+
if (!this.currentlyBatching()) {
|
|
1756
1767
|
this.flush();
|
|
1757
|
-
}
|
|
1768
|
+
}
|
|
1769
|
+
else if (!this.flushMicroTaskExists) {
|
|
1770
|
+
this.flushMicroTaskExists = true;
|
|
1771
|
+
// Queue a microtask to detect the end of the turn and force a flush.
|
|
1772
|
+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
1773
|
+
Promise.resolve().then(() => {
|
|
1774
|
+
this.flushMicroTaskExists = false;
|
|
1775
|
+
this.flush();
|
|
1776
|
+
});
|
|
1777
|
+
}
|
|
1758
1778
|
}
|
|
1759
1779
|
}
|
|
1760
1780
|
catch (error) {
|
|
@@ -1825,7 +1845,8 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1825
1845
|
}
|
|
1826
1846
|
}
|
|
1827
1847
|
/** Implementation of ISummarizerInternalsProvider.refreshLatestSummaryAck */
|
|
1828
|
-
async refreshLatestSummaryAck(
|
|
1848
|
+
async refreshLatestSummaryAck(options) {
|
|
1849
|
+
const { proposalHandle, ackHandle, summaryRefSeq, summaryLogger } = options;
|
|
1829
1850
|
const readAndParseBlob = async (id) => (0, driver_utils_1.readAndParse)(this.storage, id);
|
|
1830
1851
|
// The call to fetch the snapshot is very expensive and not always needed.
|
|
1831
1852
|
// It should only be done by the summarizerNode, if required.
|
|
@@ -1898,6 +1919,9 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1898
1919
|
// getPendingLocalState() is only exposed through Container.closeAndGetPendingLocalState(), so it's safe
|
|
1899
1920
|
// to close current batch.
|
|
1900
1921
|
this.flush();
|
|
1922
|
+
if (this._orderSequentiallyCalls !== 0) {
|
|
1923
|
+
throw new container_utils_1.UsageError("can't get state during orderSequentially");
|
|
1924
|
+
}
|
|
1901
1925
|
const previousPendingState = this.context.pendingLocalState;
|
|
1902
1926
|
if (previousPendingState) {
|
|
1903
1927
|
return {
|