@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/src/containerRuntime.ts
CHANGED
|
@@ -34,6 +34,7 @@ import {
|
|
|
34
34
|
Trace,
|
|
35
35
|
TypedEventEmitter,
|
|
36
36
|
unreachableCase,
|
|
37
|
+
IsoBuffer,
|
|
37
38
|
} from "@fluidframework/common-utils";
|
|
38
39
|
import {
|
|
39
40
|
ChildLogger,
|
|
@@ -107,6 +108,7 @@ import {
|
|
|
107
108
|
} from "@fluidframework/runtime-utils";
|
|
108
109
|
import { GCDataBuilder, trimLeadingAndTrailingSlashes } from "@fluidframework/garbage-collector";
|
|
109
110
|
import { v4 as uuid } from "uuid";
|
|
111
|
+
import { compress, decompress } from "lz4js";
|
|
110
112
|
import { ContainerFluidHandleContext } from "./containerHandleContext";
|
|
111
113
|
import { FluidDataStoreRegistry } from "./dataStoreRegistry";
|
|
112
114
|
import { Summarizer } from "./summarizer";
|
|
@@ -147,6 +149,7 @@ import {
|
|
|
147
149
|
ISummarizerInternalsProvider,
|
|
148
150
|
ISummarizerOptions,
|
|
149
151
|
ISummarizerRuntime,
|
|
152
|
+
IRefreshSummaryAckOptions,
|
|
150
153
|
} from "./summarizerTypes";
|
|
151
154
|
import { formExponentialFn, Throttler } from "./throttler";
|
|
152
155
|
import { RunWhileConnectedCoordinator } from "./runWhileConnectedCoordinator";
|
|
@@ -229,11 +232,6 @@ export interface ISummaryBaseConfiguration {
|
|
|
229
232
|
|
|
230
233
|
export interface ISummaryConfigurationHeuristics extends ISummaryBaseConfiguration {
|
|
231
234
|
state: "enabled";
|
|
232
|
-
/**
|
|
233
|
-
* @deprecated Please move all implementations to {@link ISummaryConfigurationHeuristics.minIdleTime} and
|
|
234
|
-
* {@link ISummaryConfigurationHeuristics.maxIdleTime} instead.
|
|
235
|
-
*/
|
|
236
|
-
idleTime: number;
|
|
237
235
|
/**
|
|
238
236
|
* Defines the maximum allowed time, since the last received Ack, before running the summary
|
|
239
237
|
* with reason maxTime.
|
|
@@ -297,8 +295,6 @@ export type ISummaryConfiguration =
|
|
|
297
295
|
export const DefaultSummaryConfiguration: ISummaryConfiguration = {
|
|
298
296
|
state: "enabled",
|
|
299
297
|
|
|
300
|
-
idleTime: 15 * 1000, // 15 secs.
|
|
301
|
-
|
|
302
298
|
minIdleTime: 0,
|
|
303
299
|
|
|
304
300
|
maxIdleTime: 30 * 1000, // 30 secs.
|
|
@@ -418,6 +414,18 @@ export interface ISummaryRuntimeOptions {
|
|
|
418
414
|
summarizerOptions?: Readonly<Partial<ISummarizerOptions>>;
|
|
419
415
|
}
|
|
420
416
|
|
|
417
|
+
/**
|
|
418
|
+
* Options for op compression.
|
|
419
|
+
* @experimental - Not ready for use
|
|
420
|
+
*/
|
|
421
|
+
export interface ICompressionRuntimeOptions {
|
|
422
|
+
/**
|
|
423
|
+
* The minimum size the content payload must exceed before it is compressed.
|
|
424
|
+
* Compression is disabled if undefined.
|
|
425
|
+
*/
|
|
426
|
+
readonly minimumSize?: number;
|
|
427
|
+
}
|
|
428
|
+
|
|
421
429
|
/**
|
|
422
430
|
* Options for container runtime.
|
|
423
431
|
*/
|
|
@@ -444,6 +452,22 @@ export interface IContainerRuntimeOptions {
|
|
|
444
452
|
* Save enough runtime state to be able to serialize upon request and load to the same state in a new container.
|
|
445
453
|
*/
|
|
446
454
|
readonly enableOfflineLoad?: boolean;
|
|
455
|
+
/**
|
|
456
|
+
* Enables the runtime to compress ops.
|
|
457
|
+
* @experimental Not ready for use.
|
|
458
|
+
*/
|
|
459
|
+
readonly compressionOptions?: ICompressionRuntimeOptions;
|
|
460
|
+
/**
|
|
461
|
+
* If specified, when in FlushMode.TurnBased, if the size of the ops between JS turns exceeds this value,
|
|
462
|
+
* an error will be thrown and the container will close.
|
|
463
|
+
*
|
|
464
|
+
* If unspecified, the limit is 950 * 1024.
|
|
465
|
+
*
|
|
466
|
+
* 'Infinity' will disable any limit.
|
|
467
|
+
*
|
|
468
|
+
* @experimental This config should be driven by the connection with the service and will be moved in the future.
|
|
469
|
+
*/
|
|
470
|
+
readonly maxBatchSizeInBytes?: number;
|
|
447
471
|
}
|
|
448
472
|
|
|
449
473
|
/**
|
|
@@ -515,6 +539,12 @@ const maxConsecutiveReconnectsKey = "Fluid.ContainerRuntime.MaxConsecutiveReconn
|
|
|
515
539
|
|
|
516
540
|
const defaultFlushMode = FlushMode.TurnBased;
|
|
517
541
|
|
|
542
|
+
// The actual limit is 1Mb (socket.io and Kafka limits)
|
|
543
|
+
// We can't estimate it fully, as we
|
|
544
|
+
// - do not know what properties relay service will add
|
|
545
|
+
// - we do not stringify final op, thus we do not know how much escaping will be added.
|
|
546
|
+
const defaultMaxBatchSizeInBytes = 950 * 1024;
|
|
547
|
+
|
|
518
548
|
/**
|
|
519
549
|
* @deprecated - use ContainerRuntimeMessage instead
|
|
520
550
|
*/
|
|
@@ -532,22 +562,28 @@ export enum RuntimeMessage {
|
|
|
532
562
|
* @deprecated - please use version in driver-utils
|
|
533
563
|
*/
|
|
534
564
|
export function isRuntimeMessage(message: ISequencedDocumentMessage): boolean {
|
|
535
|
-
|
|
536
|
-
return true;
|
|
537
|
-
}
|
|
538
|
-
return false;
|
|
565
|
+
return (Object.values(RuntimeMessage) as string[]).includes(message.type);
|
|
539
566
|
}
|
|
540
567
|
|
|
541
568
|
/**
|
|
542
569
|
* Unpacks runtime messages
|
|
543
570
|
*
|
|
544
|
-
* @remarks This API makes no promises regarding backward-
|
|
571
|
+
* @remarks This API makes no promises regarding backward-compatibility. This is internal API.
|
|
545
572
|
* @param message - message (as it observed in storage / service)
|
|
546
573
|
* @returns unpacked runtime message
|
|
547
574
|
*
|
|
548
575
|
* @internal
|
|
549
576
|
*/
|
|
550
577
|
export function unpackRuntimeMessage(message: ISequencedDocumentMessage) {
|
|
578
|
+
if (message.metadata?.compressed) {
|
|
579
|
+
const contents = IsoBuffer.from(message.contents.contents, "base64");
|
|
580
|
+
const decompressedMessage = decompress(contents);
|
|
581
|
+
const intoString = new TextDecoder().decode(decompressedMessage);
|
|
582
|
+
const asObj = JSON.parse(intoString);
|
|
583
|
+
message.contents.contents = asObj;
|
|
584
|
+
message.metadata.compressed = false;
|
|
585
|
+
}
|
|
586
|
+
|
|
551
587
|
if (message.type === MessageType.Operation) {
|
|
552
588
|
// legacy op format?
|
|
553
589
|
if (message.contents.address !== undefined && message.contents.type === undefined) {
|
|
@@ -637,6 +673,8 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
637
673
|
loadSequenceNumberVerification = "close",
|
|
638
674
|
flushMode = defaultFlushMode,
|
|
639
675
|
enableOfflineLoad = false,
|
|
676
|
+
compressionOptions = {},
|
|
677
|
+
maxBatchSizeInBytes = defaultMaxBatchSizeInBytes,
|
|
640
678
|
} = runtimeOptions;
|
|
641
679
|
|
|
642
680
|
const pendingRuntimeState = context.pendingLocalState as IPendingRuntimeState | undefined;
|
|
@@ -712,6 +750,8 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
712
750
|
loadSequenceNumberVerification,
|
|
713
751
|
flushMode,
|
|
714
752
|
enableOfflineLoad,
|
|
753
|
+
compressionOptions,
|
|
754
|
+
maxBatchSizeInBytes,
|
|
715
755
|
},
|
|
716
756
|
containerScope,
|
|
717
757
|
logger,
|
|
@@ -804,8 +844,8 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
804
844
|
private readonly defaultMaxConsecutiveReconnects = 7;
|
|
805
845
|
|
|
806
846
|
private _orderSequentiallyCalls: number = 0;
|
|
807
|
-
private _flushMode: FlushMode;
|
|
808
|
-
private
|
|
847
|
+
private readonly _flushMode: FlushMode;
|
|
848
|
+
private flushMicroTaskExists = false;
|
|
809
849
|
|
|
810
850
|
private _connected: boolean;
|
|
811
851
|
|
|
@@ -813,6 +853,7 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
813
853
|
private baseSnapshotBlobs?: ISerializedBaseSnapshotBlobs;
|
|
814
854
|
|
|
815
855
|
private consecutiveReconnects = 0;
|
|
856
|
+
private compressedOpCount = 0;
|
|
816
857
|
|
|
817
858
|
/**
|
|
818
859
|
* Used to delay transition to "connected" state while we upload
|
|
@@ -853,13 +894,8 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
853
894
|
private readonly scheduleManager: ScheduleManager;
|
|
854
895
|
private readonly blobManager: BlobManager;
|
|
855
896
|
private readonly pendingStateManager: PendingStateManager;
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
// but at the same time we want to send these ops sooner, to reduce overall latency of processing a batch.
|
|
859
|
-
// So there is some ballance here, that depends on compression algorithm and its efficiency working with smaller
|
|
860
|
-
// payloads. That number represents final (compressed) bits (once compression is implemented).
|
|
861
|
-
private readonly pendingAttachBatch = new BatchManager(64 * 1024);
|
|
862
|
-
private readonly pendingBatch = new BatchManager();
|
|
897
|
+
private readonly pendingAttachBatch: BatchManager;
|
|
898
|
+
private readonly pendingBatch: BatchManager;
|
|
863
899
|
|
|
864
900
|
private readonly garbageCollector: IGarbageCollector;
|
|
865
901
|
|
|
@@ -992,9 +1028,25 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
992
1028
|
|
|
993
1029
|
this._flushMode = runtimeOptions.flushMode;
|
|
994
1030
|
|
|
1031
|
+
// Provide lower soft limit - we want to have some number of ops to get efficiency in compression
|
|
1032
|
+
// & bandwidth usage, but at the same time we want to send these ops sooner, to reduce overall
|
|
1033
|
+
// latency of processing a batch.
|
|
1034
|
+
// So there is some ballance here, that depends on compression algorithm and its efficiency working with smaller
|
|
1035
|
+
// payloads. That number represents final (compressed) bits (once compression is implemented).
|
|
1036
|
+
this.pendingAttachBatch = new BatchManager(runtimeOptions.maxBatchSizeInBytes, 64 * 1024);
|
|
1037
|
+
this.pendingBatch = new BatchManager(runtimeOptions.maxBatchSizeInBytes);
|
|
1038
|
+
|
|
995
1039
|
const pendingRuntimeState = context.pendingLocalState as IPendingRuntimeState | undefined;
|
|
996
1040
|
const baseSnapshot: ISnapshotTree | undefined = pendingRuntimeState?.baseSnapshot ?? context.baseSnapshot;
|
|
997
1041
|
|
|
1042
|
+
const maxSnapshotCacheDurationMs = this._storage?.policies?.maximumCacheDurationMs;
|
|
1043
|
+
if (maxSnapshotCacheDurationMs !== undefined && maxSnapshotCacheDurationMs > 5 * 24 * 60 * 60 * 1000) {
|
|
1044
|
+
// This is a runtime enforcement of what's already explicit in the policy's type itself,
|
|
1045
|
+
// which dictates the value is either undefined or exactly 5 days in ms.
|
|
1046
|
+
// As long as the actual value is less than 5 days, the assumptions GC makes here are valid.
|
|
1047
|
+
throw new UsageError("Driver's maximumCacheDurationMs policy cannot exceed 5 days");
|
|
1048
|
+
}
|
|
1049
|
+
|
|
998
1050
|
this.garbageCollector = GarbageCollector.create({
|
|
999
1051
|
runtime: this,
|
|
1000
1052
|
gcOptions: this.runtimeOptions.gcOptions,
|
|
@@ -1033,7 +1085,7 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1033
1085
|
);
|
|
1034
1086
|
|
|
1035
1087
|
if (baseSnapshot) {
|
|
1036
|
-
this.summarizerNode.
|
|
1088
|
+
this.summarizerNode.updateBaseSummaryState(baseSnapshot);
|
|
1037
1089
|
}
|
|
1038
1090
|
|
|
1039
1091
|
this.dataStores = new DataStores(
|
|
@@ -1062,7 +1114,6 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1062
1114
|
packagePath,
|
|
1063
1115
|
),
|
|
1064
1116
|
new Map<string, string>(dataStoreAliasMap),
|
|
1065
|
-
this.garbageCollector.writeDataAtRoot,
|
|
1066
1117
|
);
|
|
1067
1118
|
|
|
1068
1119
|
this.blobManager = new BlobManager(
|
|
@@ -1095,11 +1146,10 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1095
1146
|
close: this.closeFn,
|
|
1096
1147
|
connected: () => this.connected,
|
|
1097
1148
|
flush: this.flush.bind(this),
|
|
1098
|
-
flushMode: () => this.flushMode,
|
|
1099
1149
|
reSubmit: this.reSubmit.bind(this),
|
|
1100
|
-
|
|
1150
|
+
rollback: this.rollback.bind(this),
|
|
1151
|
+
orderSequentially: this.orderSequentially.bind(this),
|
|
1101
1152
|
},
|
|
1102
|
-
this._flushMode,
|
|
1103
1153
|
pendingRuntimeState?.pending);
|
|
1104
1154
|
|
|
1105
1155
|
this.context.quorum.on("removeMember", (clientId: string) => {
|
|
@@ -1152,7 +1202,7 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1152
1202
|
// if summaries are enabled and we are not the summarizer client.
|
|
1153
1203
|
const defaultAction = () => {
|
|
1154
1204
|
if (this.summaryCollection.opsSinceLastAck > this.maxOpsSinceLastSummary) {
|
|
1155
|
-
this.logger.
|
|
1205
|
+
this.logger.sendTelemetryEvent({ eventName: "SummaryStatus:Behind" });
|
|
1156
1206
|
// unregister default to no log on every op after falling behind
|
|
1157
1207
|
// and register summary ack handler to re-register this handler
|
|
1158
1208
|
// after successful summary
|
|
@@ -1443,11 +1493,9 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1443
1493
|
addTreeToSummary(summaryTree, blobsTreeName, blobManagerSummary);
|
|
1444
1494
|
}
|
|
1445
1495
|
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
addSummarizeResultToSummary(summaryTree, gcTreeKey, gcSummary);
|
|
1450
|
-
}
|
|
1496
|
+
const gcSummary = this.garbageCollector.summarize(fullTree, trackState, telemetryContext);
|
|
1497
|
+
if (gcSummary !== undefined) {
|
|
1498
|
+
addSummarizeResultToSummary(summaryTree, gcTreeKey, gcSummary);
|
|
1451
1499
|
}
|
|
1452
1500
|
}
|
|
1453
1501
|
|
|
@@ -1774,29 +1822,11 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1774
1822
|
return context.realize();
|
|
1775
1823
|
}
|
|
1776
1824
|
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
this.mc.logger.sendTelemetryEvent({
|
|
1783
|
-
eventName: "FlushMode Updated",
|
|
1784
|
-
old: this._flushMode,
|
|
1785
|
-
new: mode,
|
|
1786
|
-
});
|
|
1787
|
-
|
|
1788
|
-
// Flush any pending batches if switching to immediate
|
|
1789
|
-
if (mode === FlushMode.Immediate) {
|
|
1790
|
-
this.flush();
|
|
1791
|
-
}
|
|
1792
|
-
|
|
1793
|
-
this._flushMode = mode;
|
|
1794
|
-
|
|
1795
|
-
// Let the PendingStateManager know that FlushMode has been updated.
|
|
1796
|
-
this.pendingStateManager.onFlushModeUpdated(mode);
|
|
1797
|
-
}
|
|
1798
|
-
|
|
1799
|
-
public flush(): void {
|
|
1825
|
+
/**
|
|
1826
|
+
* Flush the pending ops manually.
|
|
1827
|
+
* This method is expected to be called at the end of a batch.
|
|
1828
|
+
*/
|
|
1829
|
+
private flush(): void {
|
|
1800
1830
|
assert(this._orderSequentiallyCalls === 0,
|
|
1801
1831
|
0x24c /* "Cannot call `flush()` from `orderSequentially`'s callback" */);
|
|
1802
1832
|
|
|
@@ -1832,7 +1862,38 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1832
1862
|
if (this.context.submitBatchFn !== undefined) {
|
|
1833
1863
|
const batchToSend: IBatchMessage[] = [];
|
|
1834
1864
|
for (const message of batch) {
|
|
1835
|
-
|
|
1865
|
+
let contents = message.contents;
|
|
1866
|
+
let metadata = message.metadata;
|
|
1867
|
+
if (this.runtimeOptions.compressionOptions.minimumSize &&
|
|
1868
|
+
this.runtimeOptions.compressionOptions.minimumSize < message.contents.length) {
|
|
1869
|
+
this.compressedOpCount++;
|
|
1870
|
+
const copiedMessage = { ...message.deserializedContent };
|
|
1871
|
+
|
|
1872
|
+
const compressionStart = Date.now();
|
|
1873
|
+
const contentsAsBuffer = new TextEncoder().encode(JSON.stringify(copiedMessage.contents));
|
|
1874
|
+
const compressedContents = compress(contentsAsBuffer);
|
|
1875
|
+
const compressedContent = IsoBuffer.from(compressedContents).toString("base64");
|
|
1876
|
+
const duration = Date.now() - compressionStart;
|
|
1877
|
+
|
|
1878
|
+
if (this.compressedOpCount % 100) {
|
|
1879
|
+
this.mc.logger.sendPerformanceEvent({
|
|
1880
|
+
eventName: "compressedOp",
|
|
1881
|
+
duration,
|
|
1882
|
+
sizeBeforeCompression: message.contents.length,
|
|
1883
|
+
sizeAfterCompression: compressedContent.length,
|
|
1884
|
+
});
|
|
1885
|
+
}
|
|
1886
|
+
|
|
1887
|
+
copiedMessage.contents = compressedContent;
|
|
1888
|
+
const stringifiedContents = JSON.stringify(copiedMessage);
|
|
1889
|
+
|
|
1890
|
+
if (stringifiedContents.length < message.contents.length) {
|
|
1891
|
+
contents = JSON.stringify(copiedMessage);
|
|
1892
|
+
metadata = { ...message.metadata, compressed: true };
|
|
1893
|
+
}
|
|
1894
|
+
}
|
|
1895
|
+
|
|
1896
|
+
batchToSend.push({ contents, metadata });
|
|
1836
1897
|
}
|
|
1837
1898
|
// returns clientSequenceNumber of last message in a batch
|
|
1838
1899
|
clientSequenceNumber = this.context.submitBatchFn(batchToSend);
|
|
@@ -1873,36 +1934,14 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1873
1934
|
}
|
|
1874
1935
|
|
|
1875
1936
|
public orderSequentially(callback: () => void): void {
|
|
1876
|
-
// If flush mode is already TurnBased we are either
|
|
1877
|
-
// nested in another orderSequentially, or
|
|
1878
|
-
// the app is flushing manually, in which
|
|
1879
|
-
// case this invocation doesn't own
|
|
1880
|
-
// flushing.
|
|
1881
|
-
if (this.flushMode === FlushMode.TurnBased) {
|
|
1882
|
-
this.trackOrderSequentiallyCalls(callback);
|
|
1883
|
-
return;
|
|
1884
|
-
}
|
|
1885
|
-
|
|
1886
|
-
const savedFlushMode = this.flushMode;
|
|
1887
|
-
this.setFlushMode(FlushMode.TurnBased);
|
|
1888
|
-
|
|
1889
|
-
try {
|
|
1890
|
-
this.trackOrderSequentiallyCalls(callback);
|
|
1891
|
-
this.flush();
|
|
1892
|
-
} finally {
|
|
1893
|
-
this.setFlushMode(savedFlushMode);
|
|
1894
|
-
}
|
|
1895
|
-
}
|
|
1896
|
-
|
|
1897
|
-
private trackOrderSequentiallyCalls(callback: () => void): void {
|
|
1898
1937
|
let checkpoint: { rollback: (action: (message: BatchMessage) => void) => void; } | undefined;
|
|
1938
|
+
|
|
1899
1939
|
if (this.mc.config.getBoolean("Fluid.ContainerRuntime.EnableRollback")) {
|
|
1900
1940
|
// Note: we are not touching this.pendingAttachBatch here, for two reasons:
|
|
1901
1941
|
// 1. It would not help, as we flush attach ops as they become available.
|
|
1902
1942
|
// 2. There is no way to undo process of data store creation.
|
|
1903
1943
|
checkpoint = this.pendingBatch.checkpoint();
|
|
1904
1944
|
}
|
|
1905
|
-
|
|
1906
1945
|
try {
|
|
1907
1946
|
this._orderSequentiallyCalls++;
|
|
1908
1947
|
callback();
|
|
@@ -1933,6 +1972,10 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1933
1972
|
} finally {
|
|
1934
1973
|
this._orderSequentiallyCalls--;
|
|
1935
1974
|
}
|
|
1975
|
+
|
|
1976
|
+
if (this.flushMode === FlushMode.Immediate && this._orderSequentiallyCalls === 0) {
|
|
1977
|
+
this.flush();
|
|
1978
|
+
}
|
|
1936
1979
|
}
|
|
1937
1980
|
|
|
1938
1981
|
public async createDataStore(pkg: string | string[]): Promise<IDataStore> {
|
|
@@ -1982,6 +2025,13 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1982
2025
|
return this.connected && !this.deltaManager.readOnlyInfo.readonly;
|
|
1983
2026
|
}
|
|
1984
2027
|
|
|
2028
|
+
/**
|
|
2029
|
+
* Are we in the middle of batching ops together?
|
|
2030
|
+
*/
|
|
2031
|
+
private currentlyBatching() {
|
|
2032
|
+
return this.flushMode === FlushMode.TurnBased || this._orderSequentiallyCalls !== 0;
|
|
2033
|
+
}
|
|
2034
|
+
|
|
1985
2035
|
public getQuorum(): IQuorumClients {
|
|
1986
2036
|
return this.context.quorum;
|
|
1987
2037
|
}
|
|
@@ -2194,10 +2244,8 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
2194
2244
|
* Implementation of IGarbageCollectionRuntime::updateUsedRoutes.
|
|
2195
2245
|
* After GC has run, called to notify this container's nodes of routes that are used in it.
|
|
2196
2246
|
* @param usedRoutes - The routes that are used in all nodes in this Container.
|
|
2197
|
-
* @param gcTimestamp - The time when GC was run that generated these used routes. If any node node becomes
|
|
2198
|
-
* unreferenced as part of this GC run, this should be used to update the time when it happens.
|
|
2199
2247
|
*/
|
|
2200
|
-
public updateUsedRoutes(usedRoutes: string[]
|
|
2248
|
+
public updateUsedRoutes(usedRoutes: string[]) {
|
|
2201
2249
|
// Update our summarizer node's used routes. Updating used routes in summarizer node before
|
|
2202
2250
|
// summarizing is required and asserted by the the summarizer node. We are the root and are
|
|
2203
2251
|
// always referenced, so the used routes is only self-route (empty string).
|
|
@@ -2210,7 +2258,7 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
2210
2258
|
}
|
|
2211
2259
|
}
|
|
2212
2260
|
|
|
2213
|
-
return this.dataStores.updateUsedRoutes(dataStoreUsedRoutes
|
|
2261
|
+
return this.dataStores.updateUsedRoutes(dataStoreUsedRoutes);
|
|
2214
2262
|
}
|
|
2215
2263
|
|
|
2216
2264
|
/**
|
|
@@ -2682,8 +2730,8 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
2682
2730
|
// issue than sending.
|
|
2683
2731
|
// Please note that this does not change file format, so it can be disabled in the future if this
|
|
2684
2732
|
// optimization no longer makes sense (for example, batch compression may make it less appealing).
|
|
2685
|
-
if (type === ContainerMessageType.Attach &&
|
|
2686
|
-
this.mc.config.getBoolean("Fluid.ContainerRuntime.
|
|
2733
|
+
if (this.currentlyBatching() && type === ContainerMessageType.Attach &&
|
|
2734
|
+
this.mc.config.getBoolean("Fluid.ContainerRuntime.enableAttachOpReorder") === true) {
|
|
2687
2735
|
if (!this.pendingAttachBatch.push(message)) {
|
|
2688
2736
|
// BatchManager has two limits - soft limit & hard limit. Soft limit is only engaged
|
|
2689
2737
|
// when queue is not empty.
|
|
@@ -2711,18 +2759,17 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
2711
2759
|
limit: this.pendingBatch.limit,
|
|
2712
2760
|
});
|
|
2713
2761
|
}
|
|
2714
|
-
|
|
2715
|
-
|
|
2716
|
-
if (this._flushMode !== FlushMode.TurnBased) {
|
|
2717
|
-
this.flush();
|
|
2718
|
-
} else if (!this.flushTrigger) {
|
|
2719
|
-
this.flushTrigger = true;
|
|
2720
|
-
// Queue a microtask to detect the end of the turn and force a flush.
|
|
2721
|
-
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
2722
|
-
Promise.resolve().then(() => {
|
|
2723
|
-
this.flushTrigger = false;
|
|
2762
|
+
if (!this.currentlyBatching()) {
|
|
2724
2763
|
this.flush();
|
|
2725
|
-
})
|
|
2764
|
+
} else if (!this.flushMicroTaskExists) {
|
|
2765
|
+
this.flushMicroTaskExists = true;
|
|
2766
|
+
// Queue a microtask to detect the end of the turn and force a flush.
|
|
2767
|
+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
2768
|
+
Promise.resolve().then(() => {
|
|
2769
|
+
this.flushMicroTaskExists = false;
|
|
2770
|
+
this.flush();
|
|
2771
|
+
});
|
|
2772
|
+
}
|
|
2726
2773
|
}
|
|
2727
2774
|
} catch (error) {
|
|
2728
2775
|
this.closeFn(error as GenericError);
|
|
@@ -2812,12 +2859,8 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
2812
2859
|
}
|
|
2813
2860
|
|
|
2814
2861
|
/** Implementation of ISummarizerInternalsProvider.refreshLatestSummaryAck */
|
|
2815
|
-
public async refreshLatestSummaryAck(
|
|
2816
|
-
proposalHandle
|
|
2817
|
-
ackHandle: string,
|
|
2818
|
-
summaryRefSeq: number,
|
|
2819
|
-
summaryLogger: ITelemetryLogger,
|
|
2820
|
-
) {
|
|
2862
|
+
public async refreshLatestSummaryAck(options: IRefreshSummaryAckOptions) {
|
|
2863
|
+
const { proposalHandle, ackHandle, summaryRefSeq, summaryLogger } = options;
|
|
2821
2864
|
const readAndParseBlob = async <T>(id: string) => readAndParse<T>(this.storage, id);
|
|
2822
2865
|
// The call to fetch the snapshot is very expensive and not always needed.
|
|
2823
2866
|
// It should only be done by the summarizerNode, if required.
|
|
@@ -2933,6 +2976,10 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
2933
2976
|
// to close current batch.
|
|
2934
2977
|
this.flush();
|
|
2935
2978
|
|
|
2979
|
+
if (this._orderSequentiallyCalls !== 0) {
|
|
2980
|
+
throw new UsageError("can't get state during orderSequentially");
|
|
2981
|
+
}
|
|
2982
|
+
|
|
2936
2983
|
const previousPendingState = this.context.pendingLocalState as IPendingRuntimeState | undefined;
|
|
2937
2984
|
if (previousPendingState) {
|
|
2938
2985
|
return {
|