@fluidframework/container-runtime 2.0.0-internal.1.4.4 → 2.0.0-internal.2.0.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/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 -82
- 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/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/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 -83
- 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/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/summaryManager.d.ts.map +1 -1
- package/lib/summaryManager.js +10 -6
- package/lib/summaryManager.js.map +1 -1
- package/package.json +22 -65
- package/src/batchManager.ts +7 -11
- package/src/containerRuntime.ts +149 -100
- 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/summarizerHeuristics.ts +0 -3
- package/src/summarizerTypes.ts +20 -7
- package/src/summaryFormat.ts +4 -2
- package/src/summaryManager.ts +18 -7
package/lib/containerRuntime.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { AttachState, LoaderHeader, } from "@fluidframework/container-definitions";
|
|
2
|
-
import { assert, Trace, TypedEventEmitter, unreachableCase, } from "@fluidframework/common-utils";
|
|
2
|
+
import { assert, Trace, TypedEventEmitter, unreachableCase, IsoBuffer, } from "@fluidframework/common-utils";
|
|
3
3
|
import { ChildLogger, raiseConnectedEvent, PerformanceEvent, TaggedLoggerAdapter, loggerToMonitoringContext, wrapError, } from "@fluidframework/telemetry-utils";
|
|
4
4
|
import { DriverHeader, FetchSource, } from "@fluidframework/driver-definitions";
|
|
5
5
|
import { readAndParse } from "@fluidframework/driver-utils";
|
|
@@ -9,6 +9,7 @@ import { FlushMode, channelsTreeName, } from "@fluidframework/runtime-definition
|
|
|
9
9
|
import { addBlobToSummary, addSummarizeResultToSummary, addTreeToSummary, createRootSummarizerNodeWithGC, RequestParser, create404Response, exceptionToResponse, requestFluidObject, responseToException, seqFromTree, calculateStats, TelemetryContext, } from "@fluidframework/runtime-utils";
|
|
10
10
|
import { GCDataBuilder, trimLeadingAndTrailingSlashes } from "@fluidframework/garbage-collector";
|
|
11
11
|
import { v4 as uuid } from "uuid";
|
|
12
|
+
import { compress, decompress } from "lz4js";
|
|
12
13
|
import { ContainerFluidHandleContext } from "./containerHandleContext";
|
|
13
14
|
import { FluidDataStoreRegistry } from "./dataStoreRegistry";
|
|
14
15
|
import { Summarizer } from "./summarizer";
|
|
@@ -76,6 +77,11 @@ export var RuntimeHeaders;
|
|
|
76
77
|
})(RuntimeHeaders || (RuntimeHeaders = {}));
|
|
77
78
|
const maxConsecutiveReconnectsKey = "Fluid.ContainerRuntime.MaxConsecutiveReconnects";
|
|
78
79
|
const defaultFlushMode = FlushMode.TurnBased;
|
|
80
|
+
// The actual limit is 1Mb (socket.io and Kafka limits)
|
|
81
|
+
// We can't estimate it fully, as we
|
|
82
|
+
// - do not know what properties relay service will add
|
|
83
|
+
// - we do not stringify final op, thus we do not know how much escaping will be added.
|
|
84
|
+
const defaultMaxBatchSizeInBytes = 950 * 1024;
|
|
79
85
|
/**
|
|
80
86
|
* @deprecated - use ContainerRuntimeMessage instead
|
|
81
87
|
*/
|
|
@@ -93,21 +99,27 @@ export var RuntimeMessage;
|
|
|
93
99
|
* @deprecated - please use version in driver-utils
|
|
94
100
|
*/
|
|
95
101
|
export function isRuntimeMessage(message) {
|
|
96
|
-
|
|
97
|
-
return true;
|
|
98
|
-
}
|
|
99
|
-
return false;
|
|
102
|
+
return Object.values(RuntimeMessage).includes(message.type);
|
|
100
103
|
}
|
|
101
104
|
/**
|
|
102
105
|
* Unpacks runtime messages
|
|
103
106
|
*
|
|
104
|
-
* @remarks This API makes no promises regarding backward-
|
|
107
|
+
* @remarks This API makes no promises regarding backward-compatibility. This is internal API.
|
|
105
108
|
* @param message - message (as it observed in storage / service)
|
|
106
109
|
* @returns unpacked runtime message
|
|
107
110
|
*
|
|
108
111
|
* @internal
|
|
109
112
|
*/
|
|
110
113
|
export function unpackRuntimeMessage(message) {
|
|
114
|
+
var _a;
|
|
115
|
+
if ((_a = message.metadata) === null || _a === void 0 ? void 0 : _a.compressed) {
|
|
116
|
+
const contents = IsoBuffer.from(message.contents.contents, "base64");
|
|
117
|
+
const decompressedMessage = decompress(contents);
|
|
118
|
+
const intoString = new TextDecoder().decode(decompressedMessage);
|
|
119
|
+
const asObj = JSON.parse(intoString);
|
|
120
|
+
message.contents.contents = asObj;
|
|
121
|
+
message.metadata.compressed = false;
|
|
122
|
+
}
|
|
111
123
|
if (message.type === MessageType.Operation) {
|
|
112
124
|
// legacy op format?
|
|
113
125
|
if (message.contents.address !== undefined && message.contents.type === undefined) {
|
|
@@ -156,7 +168,7 @@ export function getDeviceSpec() {
|
|
|
156
168
|
*/
|
|
157
169
|
export class ContainerRuntime extends TypedEventEmitter {
|
|
158
170
|
constructor(context, registry, metadata, electedSummarizerData, chunks, dataStoreAliasMap, runtimeOptions, containerScope, logger, existing, blobManagerSnapshot, _storage, requestHandler, summaryConfiguration) {
|
|
159
|
-
var _a, _b, _c, _d;
|
|
171
|
+
var _a, _b, _c, _d, _e, _f;
|
|
160
172
|
if (summaryConfiguration === void 0) { summaryConfiguration = Object.assign(Object.assign({}, DefaultSummaryConfiguration), (_a = runtimeOptions.summaryOptions) === null || _a === void 0 ? void 0 : _a.summaryConfigOverrides); }
|
|
161
173
|
super();
|
|
162
174
|
this.context = context;
|
|
@@ -169,9 +181,10 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
169
181
|
this.summaryConfiguration = summaryConfiguration;
|
|
170
182
|
this.defaultMaxConsecutiveReconnects = 7;
|
|
171
183
|
this._orderSequentiallyCalls = 0;
|
|
172
|
-
this.
|
|
184
|
+
this.flushMicroTaskExists = false;
|
|
173
185
|
this.savedOps = [];
|
|
174
186
|
this.consecutiveReconnects = 0;
|
|
187
|
+
this.compressedOpCount = 0;
|
|
175
188
|
this._disposed = false;
|
|
176
189
|
this.emitDirtyDocumentEvent = true;
|
|
177
190
|
this.defaultTelemetrySignalSampleCount = 100;
|
|
@@ -181,12 +194,6 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
181
194
|
signalTimestamp: 0,
|
|
182
195
|
trackingSignalSequenceNumber: undefined,
|
|
183
196
|
};
|
|
184
|
-
// Provide lower soft limit - we want to have some number of ops to get efficiency in compression & bandwidth usage,
|
|
185
|
-
// but at the same time we want to send these ops sooner, to reduce overall latency of processing a batch.
|
|
186
|
-
// So there is some ballance here, that depends on compression algorithm and its efficiency working with smaller
|
|
187
|
-
// payloads. That number represents final (compressed) bits (once compression is implemented).
|
|
188
|
-
this.pendingAttachBatch = new BatchManager(64 * 1024);
|
|
189
|
-
this.pendingBatch = new BatchManager();
|
|
190
197
|
this.summarizeOnDemand = (...args) => {
|
|
191
198
|
if (this.clientDetails.type === summarizerClientType) {
|
|
192
199
|
return this.summarizer.summarizeOnDemand(...args);
|
|
@@ -231,8 +238,22 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
231
238
|
this.maxConsecutiveReconnects =
|
|
232
239
|
(_b = this.mc.config.getNumber(maxConsecutiveReconnectsKey)) !== null && _b !== void 0 ? _b : this.defaultMaxConsecutiveReconnects;
|
|
233
240
|
this._flushMode = runtimeOptions.flushMode;
|
|
241
|
+
// Provide lower soft limit - we want to have some number of ops to get efficiency in compression
|
|
242
|
+
// & bandwidth usage, but at the same time we want to send these ops sooner, to reduce overall
|
|
243
|
+
// latency of processing a batch.
|
|
244
|
+
// So there is some ballance here, that depends on compression algorithm and its efficiency working with smaller
|
|
245
|
+
// payloads. That number represents final (compressed) bits (once compression is implemented).
|
|
246
|
+
this.pendingAttachBatch = new BatchManager(runtimeOptions.maxBatchSizeInBytes, 64 * 1024);
|
|
247
|
+
this.pendingBatch = new BatchManager(runtimeOptions.maxBatchSizeInBytes);
|
|
234
248
|
const pendingRuntimeState = context.pendingLocalState;
|
|
235
249
|
const baseSnapshot = (_c = pendingRuntimeState === null || pendingRuntimeState === void 0 ? void 0 : pendingRuntimeState.baseSnapshot) !== null && _c !== void 0 ? _c : context.baseSnapshot;
|
|
250
|
+
const maxSnapshotCacheDurationMs = (_e = (_d = this._storage) === null || _d === void 0 ? void 0 : _d.policies) === null || _e === void 0 ? void 0 : _e.maximumCacheDurationMs;
|
|
251
|
+
if (maxSnapshotCacheDurationMs !== undefined && maxSnapshotCacheDurationMs > 5 * 24 * 60 * 60 * 1000) {
|
|
252
|
+
// This is a runtime enforcement of what's already explicit in the policy's type itself,
|
|
253
|
+
// which dictates the value is either undefined or exactly 5 days in ms.
|
|
254
|
+
// As long as the actual value is less than 5 days, the assumptions GC makes here are valid.
|
|
255
|
+
throw new UsageError("Driver's maximumCacheDurationMs policy cannot exceed 5 days");
|
|
256
|
+
}
|
|
236
257
|
this.garbageCollector = GarbageCollector.create({
|
|
237
258
|
runtime: this,
|
|
238
259
|
gcOptions: this.runtimeOptions.gcOptions,
|
|
@@ -265,9 +286,9 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
265
286
|
gcDisabled: !this.garbageCollector.shouldRunGC,
|
|
266
287
|
});
|
|
267
288
|
if (baseSnapshot) {
|
|
268
|
-
this.summarizerNode.
|
|
289
|
+
this.summarizerNode.updateBaseSummaryState(baseSnapshot);
|
|
269
290
|
}
|
|
270
|
-
this.dataStores = new DataStores(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)
|
|
291
|
+
this.dataStores = new DataStores(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));
|
|
271
292
|
this.blobManager = new BlobManager(this.handleContext, blobManagerSnapshot, () => this.storage, (blobId, localId) => {
|
|
272
293
|
if (!this.disposed) {
|
|
273
294
|
this.submit(ContainerMessageType.BlobAttach, undefined, undefined, { blobId, localId });
|
|
@@ -281,10 +302,10 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
281
302
|
close: this.closeFn,
|
|
282
303
|
connected: () => this.connected,
|
|
283
304
|
flush: this.flush.bind(this),
|
|
284
|
-
flushMode: () => this.flushMode,
|
|
285
305
|
reSubmit: this.reSubmit.bind(this),
|
|
286
|
-
|
|
287
|
-
|
|
306
|
+
rollback: this.rollback.bind(this),
|
|
307
|
+
orderSequentially: this.orderSequentially.bind(this),
|
|
308
|
+
}, pendingRuntimeState === null || pendingRuntimeState === void 0 ? void 0 : pendingRuntimeState.pending);
|
|
288
309
|
this.context.quorum.on("removeMember", (clientId) => {
|
|
289
310
|
this.clearPartialChunks(clientId);
|
|
290
311
|
});
|
|
@@ -308,7 +329,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
308
329
|
// if summaries are enabled and we are not the summarizer client.
|
|
309
330
|
const defaultAction = () => {
|
|
310
331
|
if (this.summaryCollection.opsSinceLastAck > this.maxOpsSinceLastSummary) {
|
|
311
|
-
this.logger.
|
|
332
|
+
this.logger.sendTelemetryEvent({ eventName: "SummaryStatus:Behind" });
|
|
312
333
|
// unregister default to no log on every op after falling behind
|
|
313
334
|
// and register summary ack handler to re-register this handler
|
|
314
335
|
// after successful summary
|
|
@@ -364,7 +385,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
364
385
|
};
|
|
365
386
|
// summaryNumber was renamed from summaryCount. For older docs that haven't been opened for a long time,
|
|
366
387
|
// the count is reset to 0.
|
|
367
|
-
loadSummaryNumber = (
|
|
388
|
+
loadSummaryNumber = (_f = metadata === null || metadata === void 0 ? void 0 : metadata.summaryNumber) !== null && _f !== void 0 ? _f : 0;
|
|
368
389
|
}
|
|
369
390
|
else {
|
|
370
391
|
this.createContainerMetadata = {
|
|
@@ -399,7 +420,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
399
420
|
runtimeVersion: pkgVersion,
|
|
400
421
|
},
|
|
401
422
|
});
|
|
402
|
-
const { summaryOptions = {}, gcOptions = {}, loadSequenceNumberVerification = "close", flushMode = defaultFlushMode, enableOfflineLoad = false, } = runtimeOptions;
|
|
423
|
+
const { summaryOptions = {}, gcOptions = {}, loadSequenceNumberVerification = "close", flushMode = defaultFlushMode, enableOfflineLoad = false, compressionOptions = {}, maxBatchSizeInBytes = defaultMaxBatchSizeInBytes, } = runtimeOptions;
|
|
403
424
|
const pendingRuntimeState = context.pendingLocalState;
|
|
404
425
|
const baseSnapshot = (_b = pendingRuntimeState === null || pendingRuntimeState === void 0 ? void 0 : pendingRuntimeState.baseSnapshot) !== null && _b !== void 0 ? _b : context.baseSnapshot;
|
|
405
426
|
const storage = !pendingRuntimeState ?
|
|
@@ -454,6 +475,8 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
454
475
|
loadSequenceNumberVerification,
|
|
455
476
|
flushMode,
|
|
456
477
|
enableOfflineLoad,
|
|
478
|
+
compressionOptions,
|
|
479
|
+
maxBatchSizeInBytes,
|
|
457
480
|
}, containerScope, logger, loadExisting, blobManagerSnapshot, storage, requestHandler);
|
|
458
481
|
if (pendingRuntimeState) {
|
|
459
482
|
await runtime.processSavedOps(pendingRuntimeState);
|
|
@@ -727,11 +750,9 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
727
750
|
if (Object.keys(blobManagerSummary.summary.tree).length > 0) {
|
|
728
751
|
addTreeToSummary(summaryTree, blobsTreeName, blobManagerSummary);
|
|
729
752
|
}
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
addSummarizeResultToSummary(summaryTree, gcTreeKey, gcSummary);
|
|
734
|
-
}
|
|
753
|
+
const gcSummary = this.garbageCollector.summarize(fullTree, trackState, telemetryContext);
|
|
754
|
+
if (gcSummary !== undefined) {
|
|
755
|
+
addSummarizeResultToSummary(summaryTree, gcTreeKey, gcSummary);
|
|
735
756
|
}
|
|
736
757
|
}
|
|
737
758
|
// Track how many times the container tries to reconnect with pending messages.
|
|
@@ -1008,23 +1029,10 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1008
1029
|
assert(await context.isRoot(), 0x12b /* "did not get root data store" */);
|
|
1009
1030
|
return context.realize();
|
|
1010
1031
|
}
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
this.mc.logger.sendTelemetryEvent({
|
|
1016
|
-
eventName: "FlushMode Updated",
|
|
1017
|
-
old: this._flushMode,
|
|
1018
|
-
new: mode,
|
|
1019
|
-
});
|
|
1020
|
-
// Flush any pending batches if switching to immediate
|
|
1021
|
-
if (mode === FlushMode.Immediate) {
|
|
1022
|
-
this.flush();
|
|
1023
|
-
}
|
|
1024
|
-
this._flushMode = mode;
|
|
1025
|
-
// Let the PendingStateManager know that FlushMode has been updated.
|
|
1026
|
-
this.pendingStateManager.onFlushModeUpdated(mode);
|
|
1027
|
-
}
|
|
1032
|
+
/**
|
|
1033
|
+
* Flush the pending ops manually.
|
|
1034
|
+
* This method is expected to be called at the end of a batch.
|
|
1035
|
+
*/
|
|
1028
1036
|
flush() {
|
|
1029
1037
|
assert(this._orderSequentiallyCalls === 0, 0x24c /* "Cannot call `flush()` from `orderSequentially`'s callback" */);
|
|
1030
1038
|
this.flushBatch(this.pendingAttachBatch.popBatch());
|
|
@@ -1053,7 +1061,33 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1053
1061
|
if (this.context.submitBatchFn !== undefined) {
|
|
1054
1062
|
const batchToSend = [];
|
|
1055
1063
|
for (const message of batch) {
|
|
1056
|
-
|
|
1064
|
+
let contents = message.contents;
|
|
1065
|
+
let metadata = message.metadata;
|
|
1066
|
+
if (this.runtimeOptions.compressionOptions.minimumSize &&
|
|
1067
|
+
this.runtimeOptions.compressionOptions.minimumSize < message.contents.length) {
|
|
1068
|
+
this.compressedOpCount++;
|
|
1069
|
+
const copiedMessage = Object.assign({}, message.deserializedContent);
|
|
1070
|
+
const compressionStart = Date.now();
|
|
1071
|
+
const contentsAsBuffer = new TextEncoder().encode(JSON.stringify(copiedMessage.contents));
|
|
1072
|
+
const compressedContents = compress(contentsAsBuffer);
|
|
1073
|
+
const compressedContent = IsoBuffer.from(compressedContents).toString("base64");
|
|
1074
|
+
const duration = Date.now() - compressionStart;
|
|
1075
|
+
if (this.compressedOpCount % 100) {
|
|
1076
|
+
this.mc.logger.sendPerformanceEvent({
|
|
1077
|
+
eventName: "compressedOp",
|
|
1078
|
+
duration,
|
|
1079
|
+
sizeBeforeCompression: message.contents.length,
|
|
1080
|
+
sizeAfterCompression: compressedContent.length,
|
|
1081
|
+
});
|
|
1082
|
+
}
|
|
1083
|
+
copiedMessage.contents = compressedContent;
|
|
1084
|
+
const stringifiedContents = JSON.stringify(copiedMessage);
|
|
1085
|
+
if (stringifiedContents.length < message.contents.length) {
|
|
1086
|
+
contents = JSON.stringify(copiedMessage);
|
|
1087
|
+
metadata = Object.assign(Object.assign({}, message.metadata), { compressed: true });
|
|
1088
|
+
}
|
|
1089
|
+
}
|
|
1090
|
+
batchToSend.push({ contents, metadata });
|
|
1057
1091
|
}
|
|
1058
1092
|
// returns clientSequenceNumber of last message in a batch
|
|
1059
1093
|
clientSequenceNumber = this.context.submitBatchFn(batchToSend);
|
|
@@ -1080,26 +1114,6 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1080
1114
|
this.pendingStateManager.onFlush();
|
|
1081
1115
|
}
|
|
1082
1116
|
orderSequentially(callback) {
|
|
1083
|
-
// If flush mode is already TurnBased we are either
|
|
1084
|
-
// nested in another orderSequentially, or
|
|
1085
|
-
// the app is flushing manually, in which
|
|
1086
|
-
// case this invocation doesn't own
|
|
1087
|
-
// flushing.
|
|
1088
|
-
if (this.flushMode === FlushMode.TurnBased) {
|
|
1089
|
-
this.trackOrderSequentiallyCalls(callback);
|
|
1090
|
-
return;
|
|
1091
|
-
}
|
|
1092
|
-
const savedFlushMode = this.flushMode;
|
|
1093
|
-
this.setFlushMode(FlushMode.TurnBased);
|
|
1094
|
-
try {
|
|
1095
|
-
this.trackOrderSequentiallyCalls(callback);
|
|
1096
|
-
this.flush();
|
|
1097
|
-
}
|
|
1098
|
-
finally {
|
|
1099
|
-
this.setFlushMode(savedFlushMode);
|
|
1100
|
-
}
|
|
1101
|
-
}
|
|
1102
|
-
trackOrderSequentiallyCalls(callback) {
|
|
1103
1117
|
let checkpoint;
|
|
1104
1118
|
if (this.mc.config.getBoolean("Fluid.ContainerRuntime.EnableRollback")) {
|
|
1105
1119
|
// Note: we are not touching this.pendingAttachBatch here, for two reasons:
|
|
@@ -1134,6 +1148,9 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1134
1148
|
finally {
|
|
1135
1149
|
this._orderSequentiallyCalls--;
|
|
1136
1150
|
}
|
|
1151
|
+
if (this.flushMode === FlushMode.Immediate && this._orderSequentiallyCalls === 0) {
|
|
1152
|
+
this.flush();
|
|
1153
|
+
}
|
|
1137
1154
|
}
|
|
1138
1155
|
async createDataStore(pkg) {
|
|
1139
1156
|
const internalId = uuid();
|
|
@@ -1160,6 +1177,12 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1160
1177
|
canSendOps() {
|
|
1161
1178
|
return this.connected && !this.deltaManager.readOnlyInfo.readonly;
|
|
1162
1179
|
}
|
|
1180
|
+
/**
|
|
1181
|
+
* Are we in the middle of batching ops together?
|
|
1182
|
+
*/
|
|
1183
|
+
currentlyBatching() {
|
|
1184
|
+
return this.flushMode === FlushMode.TurnBased || this._orderSequentiallyCalls !== 0;
|
|
1185
|
+
}
|
|
1163
1186
|
getQuorum() {
|
|
1164
1187
|
return this.context.quorum;
|
|
1165
1188
|
}
|
|
@@ -1310,10 +1333,8 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1310
1333
|
* Implementation of IGarbageCollectionRuntime::updateUsedRoutes.
|
|
1311
1334
|
* After GC has run, called to notify this container's nodes of routes that are used in it.
|
|
1312
1335
|
* @param usedRoutes - The routes that are used in all nodes in this Container.
|
|
1313
|
-
* @param gcTimestamp - The time when GC was run that generated these used routes. If any node node becomes
|
|
1314
|
-
* unreferenced as part of this GC run, this should be used to update the time when it happens.
|
|
1315
1336
|
*/
|
|
1316
|
-
updateUsedRoutes(usedRoutes
|
|
1337
|
+
updateUsedRoutes(usedRoutes) {
|
|
1317
1338
|
// Update our summarizer node's used routes. Updating used routes in summarizer node before
|
|
1318
1339
|
// summarizing is required and asserted by the the summarizer node. We are the root and are
|
|
1319
1340
|
// always referenced, so the used routes is only self-route (empty string).
|
|
@@ -1324,7 +1345,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1324
1345
|
dataStoreUsedRoutes.push(route);
|
|
1325
1346
|
}
|
|
1326
1347
|
}
|
|
1327
|
-
return this.dataStores.updateUsedRoutes(dataStoreUsedRoutes
|
|
1348
|
+
return this.dataStores.updateUsedRoutes(dataStoreUsedRoutes);
|
|
1328
1349
|
}
|
|
1329
1350
|
/**
|
|
1330
1351
|
* When running GC in test mode, this is called to delete objects whose routes are unused. This enables testing
|
|
@@ -1710,8 +1731,8 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1710
1731
|
// issue than sending.
|
|
1711
1732
|
// Please note that this does not change file format, so it can be disabled in the future if this
|
|
1712
1733
|
// optimization no longer makes sense (for example, batch compression may make it less appealing).
|
|
1713
|
-
if (type === ContainerMessageType.Attach &&
|
|
1714
|
-
this.mc.config.getBoolean("Fluid.ContainerRuntime.
|
|
1734
|
+
if (this.currentlyBatching() && type === ContainerMessageType.Attach &&
|
|
1735
|
+
this.mc.config.getBoolean("Fluid.ContainerRuntime.enableAttachOpReorder") === true) {
|
|
1715
1736
|
if (!this.pendingAttachBatch.push(message)) {
|
|
1716
1737
|
// BatchManager has two limits - soft limit & hard limit. Soft limit is only engaged
|
|
1717
1738
|
// when queue is not empty.
|
|
@@ -1736,18 +1757,18 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1736
1757
|
limit: this.pendingBatch.limit,
|
|
1737
1758
|
});
|
|
1738
1759
|
}
|
|
1739
|
-
|
|
1740
|
-
if (this._flushMode !== FlushMode.TurnBased) {
|
|
1741
|
-
this.flush();
|
|
1742
|
-
}
|
|
1743
|
-
else if (!this.flushTrigger) {
|
|
1744
|
-
this.flushTrigger = true;
|
|
1745
|
-
// Queue a microtask to detect the end of the turn and force a flush.
|
|
1746
|
-
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
1747
|
-
Promise.resolve().then(() => {
|
|
1748
|
-
this.flushTrigger = false;
|
|
1760
|
+
if (!this.currentlyBatching()) {
|
|
1749
1761
|
this.flush();
|
|
1750
|
-
}
|
|
1762
|
+
}
|
|
1763
|
+
else if (!this.flushMicroTaskExists) {
|
|
1764
|
+
this.flushMicroTaskExists = true;
|
|
1765
|
+
// Queue a microtask to detect the end of the turn and force a flush.
|
|
1766
|
+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
1767
|
+
Promise.resolve().then(() => {
|
|
1768
|
+
this.flushMicroTaskExists = false;
|
|
1769
|
+
this.flush();
|
|
1770
|
+
});
|
|
1771
|
+
}
|
|
1751
1772
|
}
|
|
1752
1773
|
}
|
|
1753
1774
|
catch (error) {
|
|
@@ -1818,7 +1839,8 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1818
1839
|
}
|
|
1819
1840
|
}
|
|
1820
1841
|
/** Implementation of ISummarizerInternalsProvider.refreshLatestSummaryAck */
|
|
1821
|
-
async refreshLatestSummaryAck(
|
|
1842
|
+
async refreshLatestSummaryAck(options) {
|
|
1843
|
+
const { proposalHandle, ackHandle, summaryRefSeq, summaryLogger } = options;
|
|
1822
1844
|
const readAndParseBlob = async (id) => readAndParse(this.storage, id);
|
|
1823
1845
|
// The call to fetch the snapshot is very expensive and not always needed.
|
|
1824
1846
|
// It should only be done by the summarizerNode, if required.
|
|
@@ -1891,6 +1913,9 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1891
1913
|
// getPendingLocalState() is only exposed through Container.closeAndGetPendingLocalState(), so it's safe
|
|
1892
1914
|
// to close current batch.
|
|
1893
1915
|
this.flush();
|
|
1916
|
+
if (this._orderSequentiallyCalls !== 0) {
|
|
1917
|
+
throw new UsageError("can't get state during orderSequentially");
|
|
1918
|
+
}
|
|
1894
1919
|
const previousPendingState = this.context.pendingLocalState;
|
|
1895
1920
|
if (previousPendingState) {
|
|
1896
1921
|
return {
|