@fluidframework/container-runtime 2.0.0-internal.1.4.1 → 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/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";
|
|
@@ -47,7 +48,6 @@ export var ContainerMessageType;
|
|
|
47
48
|
})(ContainerMessageType || (ContainerMessageType = {}));
|
|
48
49
|
export const DefaultSummaryConfiguration = {
|
|
49
50
|
state: "enabled",
|
|
50
|
-
idleTime: 15 * 1000,
|
|
51
51
|
minIdleTime: 0,
|
|
52
52
|
maxIdleTime: 30 * 1000,
|
|
53
53
|
maxTime: 60 * 1000,
|
|
@@ -77,6 +77,11 @@ export var RuntimeHeaders;
|
|
|
77
77
|
})(RuntimeHeaders || (RuntimeHeaders = {}));
|
|
78
78
|
const maxConsecutiveReconnectsKey = "Fluid.ContainerRuntime.MaxConsecutiveReconnects";
|
|
79
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;
|
|
80
85
|
/**
|
|
81
86
|
* @deprecated - use ContainerRuntimeMessage instead
|
|
82
87
|
*/
|
|
@@ -94,21 +99,27 @@ export var RuntimeMessage;
|
|
|
94
99
|
* @deprecated - please use version in driver-utils
|
|
95
100
|
*/
|
|
96
101
|
export function isRuntimeMessage(message) {
|
|
97
|
-
|
|
98
|
-
return true;
|
|
99
|
-
}
|
|
100
|
-
return false;
|
|
102
|
+
return Object.values(RuntimeMessage).includes(message.type);
|
|
101
103
|
}
|
|
102
104
|
/**
|
|
103
105
|
* Unpacks runtime messages
|
|
104
106
|
*
|
|
105
|
-
* @remarks This API makes no promises regarding backward-
|
|
107
|
+
* @remarks This API makes no promises regarding backward-compatibility. This is internal API.
|
|
106
108
|
* @param message - message (as it observed in storage / service)
|
|
107
109
|
* @returns unpacked runtime message
|
|
108
110
|
*
|
|
109
111
|
* @internal
|
|
110
112
|
*/
|
|
111
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
|
+
}
|
|
112
123
|
if (message.type === MessageType.Operation) {
|
|
113
124
|
// legacy op format?
|
|
114
125
|
if (message.contents.address !== undefined && message.contents.type === undefined) {
|
|
@@ -157,7 +168,7 @@ export function getDeviceSpec() {
|
|
|
157
168
|
*/
|
|
158
169
|
export class ContainerRuntime extends TypedEventEmitter {
|
|
159
170
|
constructor(context, registry, metadata, electedSummarizerData, chunks, dataStoreAliasMap, runtimeOptions, containerScope, logger, existing, blobManagerSnapshot, _storage, requestHandler, summaryConfiguration) {
|
|
160
|
-
var _a, _b, _c, _d;
|
|
171
|
+
var _a, _b, _c, _d, _e, _f;
|
|
161
172
|
if (summaryConfiguration === void 0) { summaryConfiguration = Object.assign(Object.assign({}, DefaultSummaryConfiguration), (_a = runtimeOptions.summaryOptions) === null || _a === void 0 ? void 0 : _a.summaryConfigOverrides); }
|
|
162
173
|
super();
|
|
163
174
|
this.context = context;
|
|
@@ -170,9 +181,10 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
170
181
|
this.summaryConfiguration = summaryConfiguration;
|
|
171
182
|
this.defaultMaxConsecutiveReconnects = 7;
|
|
172
183
|
this._orderSequentiallyCalls = 0;
|
|
173
|
-
this.
|
|
184
|
+
this.flushMicroTaskExists = false;
|
|
174
185
|
this.savedOps = [];
|
|
175
186
|
this.consecutiveReconnects = 0;
|
|
187
|
+
this.compressedOpCount = 0;
|
|
176
188
|
this._disposed = false;
|
|
177
189
|
this.emitDirtyDocumentEvent = true;
|
|
178
190
|
this.defaultTelemetrySignalSampleCount = 100;
|
|
@@ -182,12 +194,6 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
182
194
|
signalTimestamp: 0,
|
|
183
195
|
trackingSignalSequenceNumber: undefined,
|
|
184
196
|
};
|
|
185
|
-
// Provide lower soft limit - we want to have some number of ops to get efficiency in compression & bandwidth usage,
|
|
186
|
-
// but at the same time we want to send these ops sooner, to reduce overall latency of processing a batch.
|
|
187
|
-
// So there is some ballance here, that depends on compression algorithm and its efficiency working with smaller
|
|
188
|
-
// payloads. That number represents final (compressed) bits (once compression is implemented).
|
|
189
|
-
this.pendingAttachBatch = new BatchManager(64 * 1024);
|
|
190
|
-
this.pendingBatch = new BatchManager();
|
|
191
197
|
this.summarizeOnDemand = (...args) => {
|
|
192
198
|
if (this.clientDetails.type === summarizerClientType) {
|
|
193
199
|
return this.summarizer.summarizeOnDemand(...args);
|
|
@@ -232,8 +238,22 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
232
238
|
this.maxConsecutiveReconnects =
|
|
233
239
|
(_b = this.mc.config.getNumber(maxConsecutiveReconnectsKey)) !== null && _b !== void 0 ? _b : this.defaultMaxConsecutiveReconnects;
|
|
234
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);
|
|
235
248
|
const pendingRuntimeState = context.pendingLocalState;
|
|
236
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
|
+
}
|
|
237
257
|
this.garbageCollector = GarbageCollector.create({
|
|
238
258
|
runtime: this,
|
|
239
259
|
gcOptions: this.runtimeOptions.gcOptions,
|
|
@@ -266,9 +286,9 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
266
286
|
gcDisabled: !this.garbageCollector.shouldRunGC,
|
|
267
287
|
});
|
|
268
288
|
if (baseSnapshot) {
|
|
269
|
-
this.summarizerNode.
|
|
289
|
+
this.summarizerNode.updateBaseSummaryState(baseSnapshot);
|
|
270
290
|
}
|
|
271
|
-
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));
|
|
272
292
|
this.blobManager = new BlobManager(this.handleContext, blobManagerSnapshot, () => this.storage, (blobId, localId) => {
|
|
273
293
|
if (!this.disposed) {
|
|
274
294
|
this.submit(ContainerMessageType.BlobAttach, undefined, undefined, { blobId, localId });
|
|
@@ -282,10 +302,10 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
282
302
|
close: this.closeFn,
|
|
283
303
|
connected: () => this.connected,
|
|
284
304
|
flush: this.flush.bind(this),
|
|
285
|
-
flushMode: () => this.flushMode,
|
|
286
305
|
reSubmit: this.reSubmit.bind(this),
|
|
287
|
-
|
|
288
|
-
|
|
306
|
+
rollback: this.rollback.bind(this),
|
|
307
|
+
orderSequentially: this.orderSequentially.bind(this),
|
|
308
|
+
}, pendingRuntimeState === null || pendingRuntimeState === void 0 ? void 0 : pendingRuntimeState.pending);
|
|
289
309
|
this.context.quorum.on("removeMember", (clientId) => {
|
|
290
310
|
this.clearPartialChunks(clientId);
|
|
291
311
|
});
|
|
@@ -309,7 +329,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
309
329
|
// if summaries are enabled and we are not the summarizer client.
|
|
310
330
|
const defaultAction = () => {
|
|
311
331
|
if (this.summaryCollection.opsSinceLastAck > this.maxOpsSinceLastSummary) {
|
|
312
|
-
this.logger.
|
|
332
|
+
this.logger.sendTelemetryEvent({ eventName: "SummaryStatus:Behind" });
|
|
313
333
|
// unregister default to no log on every op after falling behind
|
|
314
334
|
// and register summary ack handler to re-register this handler
|
|
315
335
|
// after successful summary
|
|
@@ -365,7 +385,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
365
385
|
};
|
|
366
386
|
// summaryNumber was renamed from summaryCount. For older docs that haven't been opened for a long time,
|
|
367
387
|
// the count is reset to 0.
|
|
368
|
-
loadSummaryNumber = (
|
|
388
|
+
loadSummaryNumber = (_f = metadata === null || metadata === void 0 ? void 0 : metadata.summaryNumber) !== null && _f !== void 0 ? _f : 0;
|
|
369
389
|
}
|
|
370
390
|
else {
|
|
371
391
|
this.createContainerMetadata = {
|
|
@@ -400,7 +420,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
400
420
|
runtimeVersion: pkgVersion,
|
|
401
421
|
},
|
|
402
422
|
});
|
|
403
|
-
const { summaryOptions = {}, gcOptions = {}, loadSequenceNumberVerification = "close", flushMode = defaultFlushMode, enableOfflineLoad = false, } = runtimeOptions;
|
|
423
|
+
const { summaryOptions = {}, gcOptions = {}, loadSequenceNumberVerification = "close", flushMode = defaultFlushMode, enableOfflineLoad = false, compressionOptions = {}, maxBatchSizeInBytes = defaultMaxBatchSizeInBytes, } = runtimeOptions;
|
|
404
424
|
const pendingRuntimeState = context.pendingLocalState;
|
|
405
425
|
const baseSnapshot = (_b = pendingRuntimeState === null || pendingRuntimeState === void 0 ? void 0 : pendingRuntimeState.baseSnapshot) !== null && _b !== void 0 ? _b : context.baseSnapshot;
|
|
406
426
|
const storage = !pendingRuntimeState ?
|
|
@@ -455,6 +475,8 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
455
475
|
loadSequenceNumberVerification,
|
|
456
476
|
flushMode,
|
|
457
477
|
enableOfflineLoad,
|
|
478
|
+
compressionOptions,
|
|
479
|
+
maxBatchSizeInBytes,
|
|
458
480
|
}, containerScope, logger, loadExisting, blobManagerSnapshot, storage, requestHandler);
|
|
459
481
|
if (pendingRuntimeState) {
|
|
460
482
|
await runtime.processSavedOps(pendingRuntimeState);
|
|
@@ -728,11 +750,9 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
728
750
|
if (Object.keys(blobManagerSummary.summary.tree).length > 0) {
|
|
729
751
|
addTreeToSummary(summaryTree, blobsTreeName, blobManagerSummary);
|
|
730
752
|
}
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
addSummarizeResultToSummary(summaryTree, gcTreeKey, gcSummary);
|
|
735
|
-
}
|
|
753
|
+
const gcSummary = this.garbageCollector.summarize(fullTree, trackState, telemetryContext);
|
|
754
|
+
if (gcSummary !== undefined) {
|
|
755
|
+
addSummarizeResultToSummary(summaryTree, gcTreeKey, gcSummary);
|
|
736
756
|
}
|
|
737
757
|
}
|
|
738
758
|
// Track how many times the container tries to reconnect with pending messages.
|
|
@@ -1009,23 +1029,10 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1009
1029
|
assert(await context.isRoot(), 0x12b /* "did not get root data store" */);
|
|
1010
1030
|
return context.realize();
|
|
1011
1031
|
}
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
this.mc.logger.sendTelemetryEvent({
|
|
1017
|
-
eventName: "FlushMode Updated",
|
|
1018
|
-
old: this._flushMode,
|
|
1019
|
-
new: mode,
|
|
1020
|
-
});
|
|
1021
|
-
// Flush any pending batches if switching to immediate
|
|
1022
|
-
if (mode === FlushMode.Immediate) {
|
|
1023
|
-
this.flush();
|
|
1024
|
-
}
|
|
1025
|
-
this._flushMode = mode;
|
|
1026
|
-
// Let the PendingStateManager know that FlushMode has been updated.
|
|
1027
|
-
this.pendingStateManager.onFlushModeUpdated(mode);
|
|
1028
|
-
}
|
|
1032
|
+
/**
|
|
1033
|
+
* Flush the pending ops manually.
|
|
1034
|
+
* This method is expected to be called at the end of a batch.
|
|
1035
|
+
*/
|
|
1029
1036
|
flush() {
|
|
1030
1037
|
assert(this._orderSequentiallyCalls === 0, 0x24c /* "Cannot call `flush()` from `orderSequentially`'s callback" */);
|
|
1031
1038
|
this.flushBatch(this.pendingAttachBatch.popBatch());
|
|
@@ -1054,7 +1061,33 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1054
1061
|
if (this.context.submitBatchFn !== undefined) {
|
|
1055
1062
|
const batchToSend = [];
|
|
1056
1063
|
for (const message of batch) {
|
|
1057
|
-
|
|
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 });
|
|
1058
1091
|
}
|
|
1059
1092
|
// returns clientSequenceNumber of last message in a batch
|
|
1060
1093
|
clientSequenceNumber = this.context.submitBatchFn(batchToSend);
|
|
@@ -1081,26 +1114,6 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1081
1114
|
this.pendingStateManager.onFlush();
|
|
1082
1115
|
}
|
|
1083
1116
|
orderSequentially(callback) {
|
|
1084
|
-
// If flush mode is already TurnBased we are either
|
|
1085
|
-
// nested in another orderSequentially, or
|
|
1086
|
-
// the app is flushing manually, in which
|
|
1087
|
-
// case this invocation doesn't own
|
|
1088
|
-
// flushing.
|
|
1089
|
-
if (this.flushMode === FlushMode.TurnBased) {
|
|
1090
|
-
this.trackOrderSequentiallyCalls(callback);
|
|
1091
|
-
return;
|
|
1092
|
-
}
|
|
1093
|
-
const savedFlushMode = this.flushMode;
|
|
1094
|
-
this.setFlushMode(FlushMode.TurnBased);
|
|
1095
|
-
try {
|
|
1096
|
-
this.trackOrderSequentiallyCalls(callback);
|
|
1097
|
-
this.flush();
|
|
1098
|
-
}
|
|
1099
|
-
finally {
|
|
1100
|
-
this.setFlushMode(savedFlushMode);
|
|
1101
|
-
}
|
|
1102
|
-
}
|
|
1103
|
-
trackOrderSequentiallyCalls(callback) {
|
|
1104
1117
|
let checkpoint;
|
|
1105
1118
|
if (this.mc.config.getBoolean("Fluid.ContainerRuntime.EnableRollback")) {
|
|
1106
1119
|
// Note: we are not touching this.pendingAttachBatch here, for two reasons:
|
|
@@ -1135,6 +1148,9 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1135
1148
|
finally {
|
|
1136
1149
|
this._orderSequentiallyCalls--;
|
|
1137
1150
|
}
|
|
1151
|
+
if (this.flushMode === FlushMode.Immediate && this._orderSequentiallyCalls === 0) {
|
|
1152
|
+
this.flush();
|
|
1153
|
+
}
|
|
1138
1154
|
}
|
|
1139
1155
|
async createDataStore(pkg) {
|
|
1140
1156
|
const internalId = uuid();
|
|
@@ -1161,6 +1177,12 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1161
1177
|
canSendOps() {
|
|
1162
1178
|
return this.connected && !this.deltaManager.readOnlyInfo.readonly;
|
|
1163
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
|
+
}
|
|
1164
1186
|
getQuorum() {
|
|
1165
1187
|
return this.context.quorum;
|
|
1166
1188
|
}
|
|
@@ -1311,10 +1333,8 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1311
1333
|
* Implementation of IGarbageCollectionRuntime::updateUsedRoutes.
|
|
1312
1334
|
* After GC has run, called to notify this container's nodes of routes that are used in it.
|
|
1313
1335
|
* @param usedRoutes - The routes that are used in all nodes in this Container.
|
|
1314
|
-
* @param gcTimestamp - The time when GC was run that generated these used routes. If any node node becomes
|
|
1315
|
-
* unreferenced as part of this GC run, this should be used to update the time when it happens.
|
|
1316
1336
|
*/
|
|
1317
|
-
updateUsedRoutes(usedRoutes
|
|
1337
|
+
updateUsedRoutes(usedRoutes) {
|
|
1318
1338
|
// Update our summarizer node's used routes. Updating used routes in summarizer node before
|
|
1319
1339
|
// summarizing is required and asserted by the the summarizer node. We are the root and are
|
|
1320
1340
|
// always referenced, so the used routes is only self-route (empty string).
|
|
@@ -1325,7 +1345,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1325
1345
|
dataStoreUsedRoutes.push(route);
|
|
1326
1346
|
}
|
|
1327
1347
|
}
|
|
1328
|
-
return this.dataStores.updateUsedRoutes(dataStoreUsedRoutes
|
|
1348
|
+
return this.dataStores.updateUsedRoutes(dataStoreUsedRoutes);
|
|
1329
1349
|
}
|
|
1330
1350
|
/**
|
|
1331
1351
|
* When running GC in test mode, this is called to delete objects whose routes are unused. This enables testing
|
|
@@ -1711,8 +1731,8 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1711
1731
|
// issue than sending.
|
|
1712
1732
|
// Please note that this does not change file format, so it can be disabled in the future if this
|
|
1713
1733
|
// optimization no longer makes sense (for example, batch compression may make it less appealing).
|
|
1714
|
-
if (type === ContainerMessageType.Attach &&
|
|
1715
|
-
this.mc.config.getBoolean("Fluid.ContainerRuntime.
|
|
1734
|
+
if (this.currentlyBatching() && type === ContainerMessageType.Attach &&
|
|
1735
|
+
this.mc.config.getBoolean("Fluid.ContainerRuntime.enableAttachOpReorder") === true) {
|
|
1716
1736
|
if (!this.pendingAttachBatch.push(message)) {
|
|
1717
1737
|
// BatchManager has two limits - soft limit & hard limit. Soft limit is only engaged
|
|
1718
1738
|
// when queue is not empty.
|
|
@@ -1737,18 +1757,18 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1737
1757
|
limit: this.pendingBatch.limit,
|
|
1738
1758
|
});
|
|
1739
1759
|
}
|
|
1740
|
-
|
|
1741
|
-
if (this._flushMode !== FlushMode.TurnBased) {
|
|
1742
|
-
this.flush();
|
|
1743
|
-
}
|
|
1744
|
-
else if (!this.flushTrigger) {
|
|
1745
|
-
this.flushTrigger = true;
|
|
1746
|
-
// Queue a microtask to detect the end of the turn and force a flush.
|
|
1747
|
-
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
1748
|
-
Promise.resolve().then(() => {
|
|
1749
|
-
this.flushTrigger = false;
|
|
1760
|
+
if (!this.currentlyBatching()) {
|
|
1750
1761
|
this.flush();
|
|
1751
|
-
}
|
|
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
|
+
}
|
|
1752
1772
|
}
|
|
1753
1773
|
}
|
|
1754
1774
|
catch (error) {
|
|
@@ -1819,7 +1839,8 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1819
1839
|
}
|
|
1820
1840
|
}
|
|
1821
1841
|
/** Implementation of ISummarizerInternalsProvider.refreshLatestSummaryAck */
|
|
1822
|
-
async refreshLatestSummaryAck(
|
|
1842
|
+
async refreshLatestSummaryAck(options) {
|
|
1843
|
+
const { proposalHandle, ackHandle, summaryRefSeq, summaryLogger } = options;
|
|
1823
1844
|
const readAndParseBlob = async (id) => readAndParse(this.storage, id);
|
|
1824
1845
|
// The call to fetch the snapshot is very expensive and not always needed.
|
|
1825
1846
|
// It should only be done by the summarizerNode, if required.
|
|
@@ -1892,6 +1913,9 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1892
1913
|
// getPendingLocalState() is only exposed through Container.closeAndGetPendingLocalState(), so it's safe
|
|
1893
1914
|
// to close current batch.
|
|
1894
1915
|
this.flush();
|
|
1916
|
+
if (this._orderSequentiallyCalls !== 0) {
|
|
1917
|
+
throw new UsageError("can't get state during orderSequentially");
|
|
1918
|
+
}
|
|
1895
1919
|
const previousPendingState = this.context.pendingLocalState;
|
|
1896
1920
|
if (previousPendingState) {
|
|
1897
1921
|
return {
|