@fluidframework/container-runtime 2.0.0-internal.2.2.1 → 2.0.0-internal.2.3.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/.eslintrc.js +19 -8
- package/dist/batchTracker.d.ts +1 -2
- package/dist/batchTracker.d.ts.map +1 -1
- package/dist/batchTracker.js.map +1 -1
- package/dist/blobManager.d.ts +45 -34
- package/dist/blobManager.d.ts.map +1 -1
- package/dist/blobManager.js +135 -102
- package/dist/blobManager.js.map +1 -1
- package/dist/containerRuntime.d.ts +54 -8
- package/dist/containerRuntime.d.ts.map +1 -1
- package/dist/containerRuntime.js +143 -72
- package/dist/containerRuntime.js.map +1 -1
- package/dist/dataStoreContext.d.ts +1 -1
- package/dist/dataStoreContext.d.ts.map +1 -1
- package/dist/dataStoreContext.js +6 -8
- package/dist/dataStoreContext.js.map +1 -1
- package/dist/dataStores.d.ts +12 -9
- package/dist/dataStores.d.ts.map +1 -1
- package/dist/dataStores.js +41 -35
- package/dist/dataStores.js.map +1 -1
- package/dist/garbageCollection.d.ts +41 -20
- package/dist/garbageCollection.d.ts.map +1 -1
- package/dist/garbageCollection.js +205 -150
- package/dist/garbageCollection.js.map +1 -1
- package/dist/garbageCollectionConstants.d.ts +7 -3
- package/dist/garbageCollectionConstants.d.ts.map +1 -1
- package/dist/garbageCollectionConstants.js +10 -8
- package/dist/garbageCollectionConstants.js.map +1 -1
- package/dist/garbageCollectionTombstoneUtils.d.ts +14 -0
- package/dist/garbageCollectionTombstoneUtils.d.ts.map +1 -0
- package/dist/garbageCollectionTombstoneUtils.js +23 -0
- package/dist/garbageCollectionTombstoneUtils.js.map +1 -0
- package/dist/index.d.ts +1 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -5
- package/dist/index.js.map +1 -1
- package/dist/opLifecycle/batchManager.d.ts +13 -1
- package/dist/opLifecycle/batchManager.d.ts.map +1 -1
- package/dist/opLifecycle/batchManager.js +35 -1
- package/dist/opLifecycle/batchManager.js.map +1 -1
- package/dist/opLifecycle/definitions.d.ts +25 -1
- package/dist/opLifecycle/definitions.d.ts.map +1 -1
- package/dist/opLifecycle/definitions.js.map +1 -1
- package/dist/opLifecycle/index.d.ts +2 -2
- package/dist/opLifecycle/index.d.ts.map +1 -1
- package/dist/opLifecycle/index.js +2 -1
- package/dist/opLifecycle/index.js.map +1 -1
- package/dist/opLifecycle/opCompressor.d.ts +1 -1
- package/dist/opLifecycle/opCompressor.d.ts.map +1 -1
- package/dist/opLifecycle/opCompressor.js +24 -10
- package/dist/opLifecycle/opCompressor.js.map +1 -1
- package/dist/opLifecycle/opDecompressor.d.ts +2 -1
- package/dist/opLifecycle/opDecompressor.d.ts.map +1 -1
- package/dist/opLifecycle/opDecompressor.js +30 -17
- package/dist/opLifecycle/opDecompressor.js.map +1 -1
- package/dist/opLifecycle/opSplitter.d.ts +34 -2
- package/dist/opLifecycle/opSplitter.d.ts.map +1 -1
- package/dist/opLifecycle/opSplitter.js +114 -5
- package/dist/opLifecycle/opSplitter.js.map +1 -1
- package/dist/opLifecycle/outbox.d.ts +5 -0
- package/dist/opLifecycle/outbox.d.ts.map +1 -1
- package/dist/opLifecycle/outbox.js +24 -14
- package/dist/opLifecycle/outbox.js.map +1 -1
- package/dist/opLifecycle/remoteMessageProcessor.d.ts.map +1 -1
- package/dist/opLifecycle/remoteMessageProcessor.js +17 -2
- package/dist/opLifecycle/remoteMessageProcessor.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/runningSummarizer.d.ts.map +1 -1
- package/dist/runningSummarizer.js +0 -1
- package/dist/runningSummarizer.js.map +1 -1
- package/dist/scheduleManager.d.ts +0 -1
- package/dist/scheduleManager.d.ts.map +1 -1
- package/dist/scheduleManager.js +9 -20
- package/dist/scheduleManager.js.map +1 -1
- package/dist/summarizer.d.ts +0 -1
- package/dist/summarizer.d.ts.map +1 -1
- package/dist/summarizer.js +2 -1
- package/dist/summarizer.js.map +1 -1
- package/dist/summarizerTypes.d.ts +1 -0
- package/dist/summarizerTypes.d.ts.map +1 -1
- package/dist/summarizerTypes.js.map +1 -1
- package/dist/summaryFormat.d.ts.map +1 -1
- package/dist/summaryFormat.js +1 -2
- package/dist/summaryFormat.js.map +1 -1
- package/lib/batchTracker.d.ts +1 -2
- package/lib/batchTracker.d.ts.map +1 -1
- package/lib/batchTracker.js.map +1 -1
- package/lib/blobManager.d.ts +45 -34
- package/lib/blobManager.d.ts.map +1 -1
- package/lib/blobManager.js +137 -104
- package/lib/blobManager.js.map +1 -1
- package/lib/containerRuntime.d.ts +54 -8
- package/lib/containerRuntime.d.ts.map +1 -1
- package/lib/containerRuntime.js +140 -69
- package/lib/containerRuntime.js.map +1 -1
- package/lib/dataStoreContext.d.ts +1 -1
- package/lib/dataStoreContext.d.ts.map +1 -1
- package/lib/dataStoreContext.js +7 -9
- package/lib/dataStoreContext.js.map +1 -1
- package/lib/dataStores.d.ts +12 -9
- package/lib/dataStores.d.ts.map +1 -1
- package/lib/dataStores.js +44 -38
- package/lib/dataStores.js.map +1 -1
- package/lib/garbageCollection.d.ts +41 -20
- package/lib/garbageCollection.d.ts.map +1 -1
- package/lib/garbageCollection.js +201 -146
- package/lib/garbageCollection.js.map +1 -1
- package/lib/garbageCollectionConstants.d.ts +7 -3
- package/lib/garbageCollectionConstants.d.ts.map +1 -1
- package/lib/garbageCollectionConstants.js +9 -7
- package/lib/garbageCollectionConstants.js.map +1 -1
- package/lib/garbageCollectionTombstoneUtils.d.ts +14 -0
- package/lib/garbageCollectionTombstoneUtils.d.ts.map +1 -0
- package/lib/garbageCollectionTombstoneUtils.js +19 -0
- package/lib/garbageCollectionTombstoneUtils.js.map +1 -0
- package/lib/index.d.ts +1 -2
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +1 -2
- package/lib/index.js.map +1 -1
- package/lib/opLifecycle/batchManager.d.ts +13 -1
- package/lib/opLifecycle/batchManager.d.ts.map +1 -1
- package/lib/opLifecycle/batchManager.js +35 -1
- package/lib/opLifecycle/batchManager.js.map +1 -1
- package/lib/opLifecycle/definitions.d.ts +25 -1
- package/lib/opLifecycle/definitions.d.ts.map +1 -1
- package/lib/opLifecycle/definitions.js.map +1 -1
- package/lib/opLifecycle/index.d.ts +2 -2
- package/lib/opLifecycle/index.d.ts.map +1 -1
- package/lib/opLifecycle/index.js +1 -1
- package/lib/opLifecycle/index.js.map +1 -1
- package/lib/opLifecycle/opCompressor.d.ts +1 -1
- package/lib/opLifecycle/opCompressor.d.ts.map +1 -1
- package/lib/opLifecycle/opCompressor.js +24 -10
- package/lib/opLifecycle/opCompressor.js.map +1 -1
- package/lib/opLifecycle/opDecompressor.d.ts +2 -1
- package/lib/opLifecycle/opDecompressor.d.ts.map +1 -1
- package/lib/opLifecycle/opDecompressor.js +30 -17
- package/lib/opLifecycle/opDecompressor.js.map +1 -1
- package/lib/opLifecycle/opSplitter.d.ts +34 -2
- package/lib/opLifecycle/opSplitter.d.ts.map +1 -1
- package/lib/opLifecycle/opSplitter.js +112 -4
- package/lib/opLifecycle/opSplitter.js.map +1 -1
- package/lib/opLifecycle/outbox.d.ts +5 -0
- package/lib/opLifecycle/outbox.d.ts.map +1 -1
- package/lib/opLifecycle/outbox.js +24 -14
- package/lib/opLifecycle/outbox.js.map +1 -1
- package/lib/opLifecycle/remoteMessageProcessor.d.ts.map +1 -1
- package/lib/opLifecycle/remoteMessageProcessor.js +17 -2
- package/lib/opLifecycle/remoteMessageProcessor.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/runningSummarizer.d.ts.map +1 -1
- package/lib/runningSummarizer.js +0 -1
- package/lib/runningSummarizer.js.map +1 -1
- package/lib/scheduleManager.d.ts +0 -1
- package/lib/scheduleManager.d.ts.map +1 -1
- package/lib/scheduleManager.js +9 -20
- package/lib/scheduleManager.js.map +1 -1
- package/lib/summarizer.d.ts +0 -1
- package/lib/summarizer.d.ts.map +1 -1
- package/lib/summarizer.js +2 -1
- package/lib/summarizer.js.map +1 -1
- package/lib/summarizerTypes.d.ts +1 -0
- package/lib/summarizerTypes.d.ts.map +1 -1
- package/lib/summarizerTypes.js.map +1 -1
- package/lib/summaryFormat.d.ts.map +1 -1
- package/lib/summaryFormat.js +1 -2
- package/lib/summaryFormat.js.map +1 -1
- package/package.json +20 -19
- package/src/batchTracker.ts +1 -1
- package/src/blobManager.ts +159 -111
- package/src/containerRuntime.ts +202 -73
- package/src/dataStoreContext.ts +15 -16
- package/src/dataStores.ts +61 -45
- package/src/garbageCollection.ts +258 -183
- package/src/garbageCollectionConstants.ts +10 -7
- package/src/garbageCollectionTombstoneUtils.ts +28 -0
- package/src/index.ts +2 -5
- package/src/opLifecycle/batchManager.ts +59 -1
- package/src/opLifecycle/definitions.ts +27 -1
- package/src/opLifecycle/index.ts +2 -1
- package/src/opLifecycle/opCompressor.ts +29 -12
- package/src/opLifecycle/opDecompressor.ts +39 -18
- package/src/opLifecycle/opSplitter.ts +141 -7
- package/src/opLifecycle/outbox.ts +32 -16
- package/src/opLifecycle/remoteMessageProcessor.ts +19 -3
- package/src/packageVersion.ts +1 -1
- package/src/runningSummarizer.ts +0 -1
- package/src/scheduleManager.ts +19 -30
- package/src/summarizer.ts +1 -1
- package/src/summarizerTypes.ts +1 -0
- package/src/summaryFormat.ts +1 -2
package/src/containerRuntime.ts
CHANGED
|
@@ -69,6 +69,7 @@ import {
|
|
|
69
69
|
} from "@fluidframework/protocol-definitions";
|
|
70
70
|
import {
|
|
71
71
|
FlushMode,
|
|
72
|
+
gcTreeKey,
|
|
72
73
|
InboundAttachMessage,
|
|
73
74
|
IFluidDataStoreContextDetached,
|
|
74
75
|
IFluidDataStoreRegistry,
|
|
@@ -155,9 +156,6 @@ import {
|
|
|
155
156
|
IGarbageCollector,
|
|
156
157
|
IGCStats,
|
|
157
158
|
} from "./garbageCollection";
|
|
158
|
-
import {
|
|
159
|
-
gcTreeKey,
|
|
160
|
-
} from "./garbageCollectionConstants";
|
|
161
159
|
import {
|
|
162
160
|
channelToDataStore,
|
|
163
161
|
IDataStoreAliasMessage,
|
|
@@ -482,6 +480,25 @@ export interface IContainerRuntimeOptions {
|
|
|
482
480
|
* @experimental This config should be driven by the connection with the service and will be moved in the future.
|
|
483
481
|
*/
|
|
484
482
|
readonly maxBatchSizeInBytes?: number;
|
|
483
|
+
/**
|
|
484
|
+
* If the op payload needs to be chunked in order to work around the maximum size of the batch, this value represents
|
|
485
|
+
* how large the individual chunks will be. This is only supported when compression is enabled.
|
|
486
|
+
*
|
|
487
|
+
* If unspecified, if a batch exceeds `maxBatchSizeInBytes` after compression, the container will close with an instance
|
|
488
|
+
* of `GenericError` with the `BatchTooLarge` message.
|
|
489
|
+
*
|
|
490
|
+
* @experimental Not ready for use.
|
|
491
|
+
*/
|
|
492
|
+
readonly chunkSizeInBytes?: number;
|
|
493
|
+
/**
|
|
494
|
+
* If enabled, the runtime will block all attempts to send an op with a different reference sequence number
|
|
495
|
+
* from the previous ops submitted in the same JS turn. This happens when ops are reentrant (an op is created as a
|
|
496
|
+
* response to another op, likely from an event handler).
|
|
497
|
+
*
|
|
498
|
+
* By default, the feature is disabled. If enabled from options, the `Fluid.ContainerRuntime.DisableOpReentryCheck`
|
|
499
|
+
* can be used to disable it at runtime.
|
|
500
|
+
*/
|
|
501
|
+
readonly enableOpReentryCheck?: boolean;
|
|
485
502
|
}
|
|
486
503
|
|
|
487
504
|
/**
|
|
@@ -507,6 +524,30 @@ export enum RuntimeHeaders {
|
|
|
507
524
|
viaHandle = "viaHandle",
|
|
508
525
|
}
|
|
509
526
|
|
|
527
|
+
/** True if a tombstoned object should be returned without erroring */
|
|
528
|
+
export const AllowTombstoneRequestHeaderKey = "allowTombstone"; // Belongs in the enum above, but avoiding the breaking change
|
|
529
|
+
|
|
530
|
+
/** Tombstone error responses will have this header set to true */
|
|
531
|
+
export const TombstoneResponseHeaderKey = "isTombstoned"
|
|
532
|
+
|
|
533
|
+
/**
|
|
534
|
+
* The full set of parsed header data that may be found on Runtime requests
|
|
535
|
+
*/
|
|
536
|
+
export interface RuntimeHeaderData {
|
|
537
|
+
wait?: boolean;
|
|
538
|
+
externalRequest?: boolean;
|
|
539
|
+
viaHandle?: boolean;
|
|
540
|
+
allowTombstone?: boolean;
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
/** Default values for Runtime Headers */
|
|
544
|
+
export const defaultRuntimeHeaderData: Required<RuntimeHeaderData> = {
|
|
545
|
+
wait: true,
|
|
546
|
+
externalRequest: false,
|
|
547
|
+
viaHandle: false,
|
|
548
|
+
allowTombstone: false,
|
|
549
|
+
}
|
|
550
|
+
|
|
510
551
|
/**
|
|
511
552
|
* Available compression algorithms for op compression.
|
|
512
553
|
*/
|
|
@@ -662,6 +703,8 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
662
703
|
compressionAlgorithm: CompressionAlgorithms.lz4
|
|
663
704
|
},
|
|
664
705
|
maxBatchSizeInBytes = defaultMaxBatchSizeInBytes,
|
|
706
|
+
chunkSizeInBytes = Number.POSITIVE_INFINITY,
|
|
707
|
+
enableOpReentryCheck = false,
|
|
665
708
|
} = runtimeOptions;
|
|
666
709
|
|
|
667
710
|
const pendingRuntimeState = context.pendingLocalState as IPendingRuntimeState | undefined;
|
|
@@ -719,7 +762,9 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
719
762
|
if (loadSequenceNumberVerification === "log") {
|
|
720
763
|
logger.sendErrorEvent({ eventName: "SequenceNumberMismatch" }, error);
|
|
721
764
|
} else {
|
|
765
|
+
// Call both close and dispose as close implementation will no longer dispose runtime in future (2.0.0-internal.3.0.0)
|
|
722
766
|
context.closeFn(error);
|
|
767
|
+
context.disposeFn?.(error);
|
|
723
768
|
}
|
|
724
769
|
}
|
|
725
770
|
}
|
|
@@ -739,6 +784,8 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
739
784
|
enableOfflineLoad,
|
|
740
785
|
compressionOptions,
|
|
741
786
|
maxBatchSizeInBytes,
|
|
787
|
+
chunkSizeInBytes,
|
|
788
|
+
enableOpReentryCheck,
|
|
742
789
|
},
|
|
743
790
|
containerScope,
|
|
744
791
|
logger,
|
|
@@ -790,8 +837,17 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
790
837
|
return this.reSubmit;
|
|
791
838
|
}
|
|
792
839
|
|
|
840
|
+
public get disposeFn(): (error?: ICriticalContainerError) => void {
|
|
841
|
+
// In old loaders without dispose functionality, closeFn is equivalent but will also switch container to readonly mode
|
|
842
|
+
return this.context.disposeFn ?? this.context.closeFn;
|
|
843
|
+
}
|
|
844
|
+
|
|
793
845
|
public get closeFn(): (error?: ICriticalContainerError) => void {
|
|
794
|
-
|
|
846
|
+
// Also call disposeFn to retain functionality of runtime being disposed on close
|
|
847
|
+
return (error?: ICriticalContainerError) => {
|
|
848
|
+
this.context.closeFn(error);
|
|
849
|
+
this.context.disposeFn?.(error);
|
|
850
|
+
};
|
|
795
851
|
}
|
|
796
852
|
|
|
797
853
|
public get flushMode(): FlushMode {
|
|
@@ -863,6 +919,7 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
863
919
|
|
|
864
920
|
private dirtyContainer: boolean;
|
|
865
921
|
private emitDirtyDocumentEvent = true;
|
|
922
|
+
private readonly enableOpReentryCheck: boolean;
|
|
866
923
|
|
|
867
924
|
private readonly defaultTelemetrySignalSampleCount = 100;
|
|
868
925
|
private _perfSignalData: IPerfSignalReport = {
|
|
@@ -1010,17 +1067,28 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1010
1067
|
this.messageAtLastSummary = metadata?.message;
|
|
1011
1068
|
|
|
1012
1069
|
this._connected = this.context.connected;
|
|
1013
|
-
this.remoteMessageProcessor = new RemoteMessageProcessor(new OpSplitter(chunks), new OpDecompressor());
|
|
1014
1070
|
|
|
1015
|
-
this.
|
|
1071
|
+
this.mc = loggerToMonitoringContext(ChildLogger.create(this.logger, "ContainerRuntime"));
|
|
1072
|
+
|
|
1073
|
+
const opSplitter = new OpSplitter(
|
|
1074
|
+
chunks,
|
|
1075
|
+
this.context.submitBatchFn,
|
|
1076
|
+
this.mc.config.getBoolean("Fluid.ContainerRuntime.DisableCompressionChunking") === true ?
|
|
1077
|
+
Number.POSITIVE_INFINITY : runtimeOptions.chunkSizeInBytes,
|
|
1078
|
+
runtimeOptions.maxBatchSizeInBytes,
|
|
1079
|
+
this.mc.logger);
|
|
1080
|
+
this.remoteMessageProcessor = new RemoteMessageProcessor(opSplitter, new OpDecompressor());
|
|
1016
1081
|
|
|
1017
|
-
this.
|
|
1018
|
-
ChildLogger.create(this.logger, "ContainerRuntime"));
|
|
1082
|
+
this.handleContext = new ContainerFluidHandleContext("", this);
|
|
1019
1083
|
|
|
1020
1084
|
if (this.summaryConfiguration.state === "enabled") {
|
|
1021
1085
|
this.validateSummaryHeuristicConfiguration(this.summaryConfiguration);
|
|
1022
1086
|
}
|
|
1023
1087
|
|
|
1088
|
+
this.enableOpReentryCheck = runtimeOptions.enableOpReentryCheck === true
|
|
1089
|
+
// Allow for a break-glass config to override the options
|
|
1090
|
+
&& this.mc.config.getBoolean("Fluid.ContainerRuntime.DisableOpReentryCheck") !== true;
|
|
1091
|
+
|
|
1024
1092
|
this.summariesDisabled = this.isSummariesDisabled();
|
|
1025
1093
|
this.heuristicsDisabled = this.isHeuristicsDisabled();
|
|
1026
1094
|
this.summarizerClientElectionEnabled = this.isSummarizerClientElectionEnabled();
|
|
@@ -1079,6 +1147,10 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1079
1147
|
// If GC should not run, let the summarizer node know so that it does not track GC state.
|
|
1080
1148
|
gcDisabled: !this.garbageCollector.shouldRunGC,
|
|
1081
1149
|
},
|
|
1150
|
+
// Function to get GC data if needed. This will always be called by the root summarizer node to get GC data.
|
|
1151
|
+
async (fullGC?: boolean) => this.getGCDataInternal(fullGC),
|
|
1152
|
+
// Function to get the GC details from the base snapshot we loaded from.
|
|
1153
|
+
async () => this.garbageCollector.getBaseGCDetails(),
|
|
1082
1154
|
);
|
|
1083
1155
|
|
|
1084
1156
|
if (baseSnapshot) {
|
|
@@ -1092,7 +1164,7 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1092
1164
|
(id: string, createParam: CreateChildSummarizerNodeParam) => (
|
|
1093
1165
|
summarizeInternal: SummarizeInternalFn,
|
|
1094
1166
|
getGCDataFn: (fullGC?: boolean) => Promise<IGarbageCollectionData>,
|
|
1095
|
-
getBaseGCDetailsFn
|
|
1167
|
+
getBaseGCDetailsFn?: () => Promise<IGarbageCollectionDetailsBase>,
|
|
1096
1168
|
) => this.summarizerNode.createChild(
|
|
1097
1169
|
summarizeInternal,
|
|
1098
1170
|
id,
|
|
@@ -1117,12 +1189,13 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1117
1189
|
this.handleContext,
|
|
1118
1190
|
blobManagerSnapshot,
|
|
1119
1191
|
() => this.storage,
|
|
1120
|
-
(
|
|
1192
|
+
(localId: string, blobId?: string) => {
|
|
1121
1193
|
if (!this.disposed) {
|
|
1122
|
-
this.submit(ContainerMessageType.BlobAttach, undefined, undefined, {
|
|
1194
|
+
this.submit(ContainerMessageType.BlobAttach, undefined, undefined, { localId, blobId });
|
|
1123
1195
|
}
|
|
1124
1196
|
},
|
|
1125
1197
|
(blobPath: string) => this.garbageCollector.nodeUpdated(blobPath, "Loaded"),
|
|
1198
|
+
(fromPath: string, toPath: string) => this.garbageCollector.addedOutboundReference(fromPath, toPath),
|
|
1126
1199
|
this,
|
|
1127
1200
|
pendingRuntimeState?.pendingAttachmentBlobs,
|
|
1128
1201
|
);
|
|
@@ -1147,15 +1220,24 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1147
1220
|
},
|
|
1148
1221
|
pendingRuntimeState?.pending);
|
|
1149
1222
|
|
|
1223
|
+
const compressionOptions = this.mc.config.getBoolean("Fluid.ContainerRuntime.DisableCompression") === true ?
|
|
1224
|
+
{
|
|
1225
|
+
minimumBatchSizeInBytes: Number.POSITIVE_INFINITY,
|
|
1226
|
+
compressionAlgorithm: CompressionAlgorithms.lz4
|
|
1227
|
+
} : runtimeOptions.compressionOptions;
|
|
1228
|
+
|
|
1150
1229
|
this.outbox = new Outbox({
|
|
1151
1230
|
shouldSend: () => this.canSendOps(),
|
|
1152
1231
|
pendingStateManager: this.pendingStateManager,
|
|
1153
1232
|
containerContext: this.context,
|
|
1154
1233
|
compressor: new OpCompressor(this.mc.logger),
|
|
1234
|
+
splitter: opSplitter,
|
|
1155
1235
|
config: {
|
|
1156
|
-
compressionOptions
|
|
1236
|
+
compressionOptions,
|
|
1157
1237
|
maxBatchSizeInBytes: runtimeOptions.maxBatchSizeInBytes,
|
|
1238
|
+
enableOpReentryCheck: this.enableOpReentryCheck,
|
|
1158
1239
|
},
|
|
1240
|
+
logger: this.mc.logger,
|
|
1159
1241
|
});
|
|
1160
1242
|
|
|
1161
1243
|
this.context.quorum.on("removeMember", (clientId: string) => {
|
|
@@ -1402,16 +1484,20 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1402
1484
|
}
|
|
1403
1485
|
|
|
1404
1486
|
private async getDataStoreFromRequest(id: string, request: IRequest): Promise<IFluidRouter> {
|
|
1405
|
-
const
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1487
|
+
const headerData: RuntimeHeaderData = {};
|
|
1488
|
+
if (typeof request.headers?.[RuntimeHeaders.wait] === "boolean") {
|
|
1489
|
+
headerData.wait = request.headers[RuntimeHeaders.wait];
|
|
1490
|
+
}
|
|
1491
|
+
if (typeof request.headers?.[RuntimeHeaders.viaHandle] === "boolean") {
|
|
1492
|
+
headerData.viaHandle = request.headers[RuntimeHeaders.viaHandle];
|
|
1493
|
+
}
|
|
1494
|
+
if (typeof request.headers?.[AllowTombstoneRequestHeaderKey] === "boolean") {
|
|
1495
|
+
headerData.allowTombstone = request.headers[AllowTombstoneRequestHeaderKey];
|
|
1496
|
+
}
|
|
1411
1497
|
|
|
1412
1498
|
await this.dataStores.waitIfPendingAlias(id);
|
|
1413
1499
|
const internalId = this.internalId(id);
|
|
1414
|
-
const dataStoreContext = await this.dataStores.getDataStore(internalId,
|
|
1500
|
+
const dataStoreContext = await this.dataStores.getDataStore(internalId, headerData);
|
|
1415
1501
|
|
|
1416
1502
|
/**
|
|
1417
1503
|
* If GC should run and this an external app request with "externalRequest" header, we need to return
|
|
@@ -1638,7 +1724,6 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1638
1724
|
if (!this.shouldContinueReconnecting()) {
|
|
1639
1725
|
this.closeFn(
|
|
1640
1726
|
DataProcessingError.create(
|
|
1641
|
-
// eslint-disable-next-line max-len
|
|
1642
1727
|
"Runtime detected too many reconnects with no progress syncing local ops. Batch of ops is likely too large (over 1Mb)",
|
|
1643
1728
|
"setConnectionState",
|
|
1644
1729
|
undefined,
|
|
@@ -1685,7 +1770,7 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1685
1770
|
|
|
1686
1771
|
try {
|
|
1687
1772
|
let localOpMetadata: unknown;
|
|
1688
|
-
if (local && runtimeMessage) {
|
|
1773
|
+
if (local && runtimeMessage && message.type !== ContainerMessageType.ChunkedOp) {
|
|
1689
1774
|
localOpMetadata = this.pendingStateManager.processPendingLocalMessage(message);
|
|
1690
1775
|
}
|
|
1691
1776
|
|
|
@@ -1803,7 +1888,7 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1803
1888
|
private async getRootDataStoreChannel(id: string, wait = true): Promise<IFluidDataStoreChannel> {
|
|
1804
1889
|
await this.dataStores.waitIfPendingAlias(id);
|
|
1805
1890
|
const internalId = this.internalId(id);
|
|
1806
|
-
const context = await this.dataStores.getDataStore(internalId, wait
|
|
1891
|
+
const context = await this.dataStores.getDataStore(internalId, { wait });
|
|
1807
1892
|
assert(await context.isRoot(), 0x12b /* "did not get root data store" */);
|
|
1808
1893
|
return context.realize();
|
|
1809
1894
|
}
|
|
@@ -1820,9 +1905,9 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1820
1905
|
assert(this.outbox.isEmpty, 0x3cf /* reentrancy */);
|
|
1821
1906
|
}
|
|
1822
1907
|
|
|
1823
|
-
public orderSequentially(callback: () =>
|
|
1908
|
+
public orderSequentially<T>(callback: () => T): T {
|
|
1824
1909
|
let checkpoint: IBatchCheckpoint | undefined;
|
|
1825
|
-
|
|
1910
|
+
let result: T;
|
|
1826
1911
|
if (this.mc.config.getBoolean("Fluid.ContainerRuntime.EnableRollback")) {
|
|
1827
1912
|
// Note: we are not touching this.pendingAttachBatch here, for two reasons:
|
|
1828
1913
|
// 1. It would not help, as we flush attach ops as they become available.
|
|
@@ -1831,7 +1916,7 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1831
1916
|
}
|
|
1832
1917
|
try {
|
|
1833
1918
|
this._orderSequentiallyCalls++;
|
|
1834
|
-
callback();
|
|
1919
|
+
result = callback();
|
|
1835
1920
|
} catch (error) {
|
|
1836
1921
|
if (checkpoint) {
|
|
1837
1922
|
// This will throw and close the container if rollback fails
|
|
@@ -1863,6 +1948,7 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1863
1948
|
if (this.flushMode === FlushMode.Immediate && this._orderSequentiallyCalls === 0) {
|
|
1864
1949
|
this.flush();
|
|
1865
1950
|
}
|
|
1951
|
+
return result;
|
|
1866
1952
|
}
|
|
1867
1953
|
|
|
1868
1954
|
public async createDataStore(pkg: string | string[]): Promise<IDataStore> {
|
|
@@ -2112,6 +2198,10 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
2112
2198
|
return this.dataStores.updateStateBeforeGC();
|
|
2113
2199
|
}
|
|
2114
2200
|
|
|
2201
|
+
private async getGCDataInternal(fullGC?: boolean): Promise<IGarbageCollectionData> {
|
|
2202
|
+
return this.dataStores.getGCData(fullGC);
|
|
2203
|
+
}
|
|
2204
|
+
|
|
2115
2205
|
/**
|
|
2116
2206
|
* Implementation of IGarbageCollectionRuntime::getGCData.
|
|
2117
2207
|
* Generates and returns the GC data for this container.
|
|
@@ -2119,7 +2209,7 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
2119
2209
|
*/
|
|
2120
2210
|
public async getGCData(fullGC?: boolean): Promise<IGarbageCollectionData> {
|
|
2121
2211
|
const builder = new GCDataBuilder();
|
|
2122
|
-
const dsGCData = await this.
|
|
2212
|
+
const dsGCData = await this.summarizerNode.getGCData(fullGC);
|
|
2123
2213
|
builder.addNodes(dsGCData.gcNodes);
|
|
2124
2214
|
|
|
2125
2215
|
const blobsGCData = this.blobManager.getGCData(fullGC);
|
|
@@ -2138,40 +2228,28 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
2138
2228
|
// always referenced, so the used routes is only self-route (empty string).
|
|
2139
2229
|
this.summarizerNode.updateUsedRoutes([""]);
|
|
2140
2230
|
|
|
2141
|
-
const
|
|
2142
|
-
|
|
2143
|
-
for (const route of usedRoutes) {
|
|
2144
|
-
if (this.isBlobPath(route)) {
|
|
2145
|
-
blobManagerUsedRoutes.push(route);
|
|
2146
|
-
} else {
|
|
2147
|
-
dataStoreUsedRoutes.push(route);
|
|
2148
|
-
}
|
|
2149
|
-
}
|
|
2150
|
-
|
|
2151
|
-
this.blobManager.updateUsedRoutes(blobManagerUsedRoutes);
|
|
2152
|
-
this.dataStores.updateUsedRoutes(dataStoreUsedRoutes);
|
|
2231
|
+
const { dataStoreRoutes } = this.getDataStoreAndBlobManagerRoutes(usedRoutes);
|
|
2232
|
+
this.dataStores.updateUsedRoutes(dataStoreRoutes);
|
|
2153
2233
|
}
|
|
2154
2234
|
|
|
2155
2235
|
/**
|
|
2156
|
-
* This is called to update objects whose routes are unused.
|
|
2157
|
-
*
|
|
2158
|
-
* @param unusedRoutes - The routes that are unused in all data stores and attachment blobs in this Container.
|
|
2159
|
-
* @param tombstone - if true, the objects corresponding to unused routes are marked tombstones. Otherwise, they
|
|
2160
|
-
* are deleted.
|
|
2236
|
+
* This is called to update objects whose routes are unused.
|
|
2237
|
+
* @param unusedRoutes - Data store and attachment blob routes that are unused in this Container.
|
|
2161
2238
|
*/
|
|
2162
|
-
public updateUnusedRoutes(unusedRoutes: string[]
|
|
2163
|
-
const
|
|
2164
|
-
|
|
2165
|
-
|
|
2166
|
-
|
|
2167
|
-
blobManagerUnusedRoutes.push(route);
|
|
2168
|
-
} else {
|
|
2169
|
-
dataStoreUnusedRoutes.push(route);
|
|
2170
|
-
}
|
|
2171
|
-
}
|
|
2239
|
+
public updateUnusedRoutes(unusedRoutes: string[]) {
|
|
2240
|
+
const { blobManagerRoutes, dataStoreRoutes } = this.getDataStoreAndBlobManagerRoutes(unusedRoutes);
|
|
2241
|
+
this.blobManager.updateUnusedRoutes(blobManagerRoutes);
|
|
2242
|
+
this.dataStores.updateUnusedRoutes(dataStoreRoutes);
|
|
2243
|
+
}
|
|
2172
2244
|
|
|
2173
|
-
|
|
2174
|
-
|
|
2245
|
+
/**
|
|
2246
|
+
* This is called to update objects that are tombstones.
|
|
2247
|
+
* @param tombstonedRoutes - Data store and attachment blob routes that are tombstones in this Container.
|
|
2248
|
+
*/
|
|
2249
|
+
public updateTombstonedRoutes(tombstonedRoutes: string[]) {
|
|
2250
|
+
const { blobManagerRoutes, dataStoreRoutes } = this.getDataStoreAndBlobManagerRoutes(tombstonedRoutes);
|
|
2251
|
+
this.blobManager.updateTombstonedRoutes(blobManagerRoutes);
|
|
2252
|
+
this.dataStores.updateTombstonedRoutes(dataStoreRoutes);
|
|
2175
2253
|
}
|
|
2176
2254
|
|
|
2177
2255
|
/**
|
|
@@ -2201,7 +2279,7 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
2201
2279
|
public async getGCNodePackagePath(nodePath: string): Promise<readonly string[] | undefined> {
|
|
2202
2280
|
switch (this.getNodeType(nodePath)) {
|
|
2203
2281
|
case GCNodeType.Blob:
|
|
2204
|
-
return [
|
|
2282
|
+
return [BlobManager.basePath];
|
|
2205
2283
|
case GCNodeType.DataStore:
|
|
2206
2284
|
case GCNodeType.SubDataStore:
|
|
2207
2285
|
return this.dataStores.getDataStorePackagePath(nodePath);
|
|
@@ -2221,6 +2299,25 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
2221
2299
|
return true;
|
|
2222
2300
|
}
|
|
2223
2301
|
|
|
2302
|
+
/**
|
|
2303
|
+
* From a given list of routes, separate and return routes that belong to blob manager and data stores.
|
|
2304
|
+
* @param routes - A list of routes that can belong to data stores or blob manager.
|
|
2305
|
+
* @returns - Two route lists - One that contains routes for blob manager and another one that contains routes
|
|
2306
|
+
* for data stores.
|
|
2307
|
+
*/
|
|
2308
|
+
private getDataStoreAndBlobManagerRoutes(routes: string[]) {
|
|
2309
|
+
const blobManagerRoutes: string[] = [];
|
|
2310
|
+
const dataStoreRoutes: string[] = [];
|
|
2311
|
+
for (const route of routes) {
|
|
2312
|
+
if (this.isBlobPath(route)) {
|
|
2313
|
+
blobManagerRoutes.push(route);
|
|
2314
|
+
} else {
|
|
2315
|
+
dataStoreRoutes.push(route);
|
|
2316
|
+
}
|
|
2317
|
+
}
|
|
2318
|
+
return { blobManagerRoutes, dataStoreRoutes };
|
|
2319
|
+
}
|
|
2320
|
+
|
|
2224
2321
|
/**
|
|
2225
2322
|
* Runs garbage collection and updates the reference / used state of the nodes in the container.
|
|
2226
2323
|
* @returns the statistics of the garbage collection run; undefined if GC did not run.
|
|
@@ -2315,7 +2412,6 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
2315
2412
|
if (this.deltaManager.lastSequenceNumber !== summaryRefSeqNum) {
|
|
2316
2413
|
return {
|
|
2317
2414
|
continue: false,
|
|
2318
|
-
// eslint-disable-next-line max-len
|
|
2319
2415
|
error: `lastSequenceNumber changed before uploading to storage. ${this.deltaManager.lastSequenceNumber} !== ${summaryRefSeqNum}`,
|
|
2320
2416
|
};
|
|
2321
2417
|
}
|
|
@@ -2325,7 +2421,6 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
2325
2421
|
if (lastAck !== this.summaryCollection.latestAck) {
|
|
2326
2422
|
return {
|
|
2327
2423
|
continue: false,
|
|
2328
|
-
// eslint-disable-next-line max-len
|
|
2329
2424
|
error: `Last summary changed while summarizing. ${this.summaryCollection.latestAck} !== ${lastAck}`,
|
|
2330
2425
|
};
|
|
2331
2426
|
}
|
|
@@ -2587,7 +2682,6 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
2587
2682
|
} else if (!this.flushMicroTaskExists) {
|
|
2588
2683
|
this.flushMicroTaskExists = true;
|
|
2589
2684
|
// Queue a microtask to detect the end of the turn and force a flush.
|
|
2590
|
-
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
2591
2685
|
Promise.resolve().then(() => {
|
|
2592
2686
|
this.flushMicroTaskExists = false;
|
|
2593
2687
|
this.flush();
|
|
@@ -2708,17 +2802,42 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
2708
2802
|
// It should only be done by the summarizerNode, if required.
|
|
2709
2803
|
// When fetching from storage we will always get the latest version and do not use the ackHandle.
|
|
2710
2804
|
const snapshotTreeFetcher = async () => {
|
|
2711
|
-
const fetchResult = await this.
|
|
2712
|
-
null,
|
|
2805
|
+
const fetchResult = await this.fetchLatestSnapshotFromStorage(
|
|
2713
2806
|
summaryLogger,
|
|
2714
2807
|
{
|
|
2715
2808
|
eventName: "RefreshLatestSummaryGetSnapshot",
|
|
2716
2809
|
ackHandle,
|
|
2717
2810
|
summaryRefSeq,
|
|
2718
2811
|
fetchLatest: true,
|
|
2719
|
-
}
|
|
2812
|
+
},
|
|
2813
|
+
);
|
|
2720
2814
|
|
|
2721
2815
|
const latestSnapshotRefSeq = await seqFromTree(fetchResult.snapshotTree, readAndParseBlob);
|
|
2816
|
+
/**
|
|
2817
|
+
* If the fetched snapshot is older than the one for which the ack was received, close the container.
|
|
2818
|
+
* This should never happen because an ack should be sent after the latest summary is updated in the server.
|
|
2819
|
+
* However, there are couple of scenarios where it's possible:
|
|
2820
|
+
* 1. A file was modified externally resulting in modifying the snapshot's sequence number. This can lead to
|
|
2821
|
+
* the document being unusable and we should not proceed.
|
|
2822
|
+
* 2. The server DB failed after the ack was sent which may delete the corresponding snapshot. Ideally, in
|
|
2823
|
+
* such cases, the file will be rolled back along with the ack and we will eventually reach a consistent
|
|
2824
|
+
* state.
|
|
2825
|
+
*/
|
|
2826
|
+
if (latestSnapshotRefSeq < summaryRefSeq) {
|
|
2827
|
+
const error = DataProcessingError.create(
|
|
2828
|
+
"Fetched snapshot is older than the received ack",
|
|
2829
|
+
"RefreshLatestSummaryAck",
|
|
2830
|
+
undefined /* sequencedMessage */,
|
|
2831
|
+
{
|
|
2832
|
+
ackHandle,
|
|
2833
|
+
summaryRefSeq,
|
|
2834
|
+
latestSnapshotRefSeq,
|
|
2835
|
+
},
|
|
2836
|
+
);
|
|
2837
|
+
this.closeFn(error);
|
|
2838
|
+
throw error;
|
|
2839
|
+
}
|
|
2840
|
+
|
|
2722
2841
|
summaryLogger.sendTelemetryEvent(
|
|
2723
2842
|
{
|
|
2724
2843
|
eventName: "LatestSummaryRetrieved",
|
|
@@ -2744,7 +2863,12 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
2744
2863
|
);
|
|
2745
2864
|
|
|
2746
2865
|
// Notify the garbage collector so it can update its latest summary state.
|
|
2747
|
-
await this.garbageCollector.
|
|
2866
|
+
await this.garbageCollector.refreshLatestSummary(
|
|
2867
|
+
result,
|
|
2868
|
+
proposalHandle,
|
|
2869
|
+
summaryRefSeq,
|
|
2870
|
+
readAndParseBlob,
|
|
2871
|
+
);
|
|
2748
2872
|
}
|
|
2749
2873
|
|
|
2750
2874
|
/**
|
|
@@ -2756,11 +2880,12 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
2756
2880
|
private async refreshLatestSummaryAckFromServer(
|
|
2757
2881
|
summaryLogger: ITelemetryLogger,
|
|
2758
2882
|
): Promise<{ latestSnapshotRefSeq: number; latestSnapshotVersionId: string | undefined; }> {
|
|
2759
|
-
const { snapshotTree, versionId } = await this.
|
|
2760
|
-
|
|
2761
|
-
|
|
2762
|
-
|
|
2763
|
-
|
|
2883
|
+
const { snapshotTree, versionId } = await this.fetchLatestSnapshotFromStorage(
|
|
2884
|
+
summaryLogger,
|
|
2885
|
+
{
|
|
2886
|
+
eventName: "RefreshLatestSummaryGetSnapshot",
|
|
2887
|
+
fetchLatest: true,
|
|
2888
|
+
},
|
|
2764
2889
|
);
|
|
2765
2890
|
|
|
2766
2891
|
const readAndParseBlob = async <T>(id: string) => readAndParse<T>(this.storage, id);
|
|
@@ -2775,16 +2900,19 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
2775
2900
|
);
|
|
2776
2901
|
|
|
2777
2902
|
// Notify the garbage collector so it can update its latest summary state.
|
|
2778
|
-
await this.garbageCollector.
|
|
2903
|
+
await this.garbageCollector.refreshLatestSummary(
|
|
2904
|
+
result,
|
|
2905
|
+
undefined,
|
|
2906
|
+
latestSnapshotRefSeq,
|
|
2907
|
+
readAndParseBlob,
|
|
2908
|
+
)
|
|
2779
2909
|
|
|
2780
2910
|
return { latestSnapshotRefSeq, latestSnapshotVersionId: versionId };
|
|
2781
2911
|
}
|
|
2782
2912
|
|
|
2783
|
-
private async
|
|
2784
|
-
versionId: string | null,
|
|
2913
|
+
private async fetchLatestSnapshotFromStorage(
|
|
2785
2914
|
logger: ITelemetryLogger,
|
|
2786
2915
|
event: ITelemetryGenericEvent,
|
|
2787
|
-
fetchSource?: FetchSource,
|
|
2788
2916
|
): Promise<{ snapshotTree: ISnapshotTree; versionId: string; }> {
|
|
2789
2917
|
return PerformanceEvent.timedExecAsync(
|
|
2790
2918
|
logger, event, async (perfEvent: {
|
|
@@ -2797,7 +2925,7 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
2797
2925
|
const trace = Trace.start();
|
|
2798
2926
|
|
|
2799
2927
|
const versions = await this.storage.getVersions(
|
|
2800
|
-
|
|
2928
|
+
null, 1, "refreshLatestSummaryAckFromServer", FetchSource.noCache);
|
|
2801
2929
|
assert(!!versions && !!versions[0], 0x137 /* "Failed to get version from storage" */);
|
|
2802
2930
|
stats.getVersionDuration = trace.trace().duration;
|
|
2803
2931
|
|
|
@@ -2960,6 +3088,7 @@ const waitForSeq = async (
|
|
|
2960
3088
|
): Promise<void> => new Promise<void>((resolve, reject) => {
|
|
2961
3089
|
// TODO: remove cast to any when actual event is determined
|
|
2962
3090
|
deltaManager.on("closed" as any, reject);
|
|
3091
|
+
deltaManager.on("disposed" as any, reject);
|
|
2963
3092
|
|
|
2964
3093
|
// If we already reached target sequence number, simply resolve the promise.
|
|
2965
3094
|
if (deltaManager.lastSequenceNumber >= targetSeq) {
|
package/src/dataStoreContext.ts
CHANGED
|
@@ -61,7 +61,6 @@ import {
|
|
|
61
61
|
import {
|
|
62
62
|
addBlobToSummary,
|
|
63
63
|
convertSummaryTreeToITree,
|
|
64
|
-
packagePathToTelemetryProperty,
|
|
65
64
|
} from "@fluidframework/runtime-utils";
|
|
66
65
|
import {
|
|
67
66
|
ChildLogger,
|
|
@@ -88,6 +87,7 @@ import {
|
|
|
88
87
|
getFluidDataStoreAttributes,
|
|
89
88
|
} from "./summaryFormat";
|
|
90
89
|
import { throwOnTombstoneUsageKey } from "./garbageCollectionConstants";
|
|
90
|
+
import { sendGCTombstoneEvent } from "./garbageCollectionTombstoneUtils";
|
|
91
91
|
import { summarizerClientType } from "./summarizerClientElection";
|
|
92
92
|
|
|
93
93
|
function createAttributes(
|
|
@@ -775,20 +775,19 @@ export abstract class FluidDataStoreContext extends TypedEventEmitter<IFluidData
|
|
|
775
775
|
|
|
776
776
|
if (checkTombstone && this.tombstoned) {
|
|
777
777
|
const messageString = `Context is tombstoned! Call site [${callSite}]`;
|
|
778
|
-
const error = new DataCorruptionError(messageString,
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
// throwOnTombstoneUsage is set and the client is not a summarizer.
|
|
778
|
+
const error = new DataCorruptionError(messageString, safeTelemetryProps);
|
|
779
|
+
|
|
780
|
+
sendGCTombstoneEvent(
|
|
781
|
+
this.mc,
|
|
782
|
+
{
|
|
783
|
+
eventName: "GC_Tombstone_DataStore_Changed",
|
|
784
|
+
category: this.throwOnTombstoneUsage ? "error" : "generic",
|
|
785
|
+
isSummarizerClient: this.clientDetails.type === summarizerClientType,
|
|
786
|
+
callSite,
|
|
787
|
+
},
|
|
788
|
+
this.pkg,
|
|
789
|
+
error,
|
|
790
|
+
);
|
|
792
791
|
if (this.throwOnTombstoneUsage) {
|
|
793
792
|
throw error;
|
|
794
793
|
}
|
|
@@ -799,7 +798,7 @@ export abstract class FluidDataStoreContext extends TypedEventEmitter<IFluidData
|
|
|
799
798
|
return (
|
|
800
799
|
summarizeInternal: SummarizeInternalFn,
|
|
801
800
|
getGCDataFn: (fullGC?: boolean) => Promise<IGarbageCollectionData>,
|
|
802
|
-
getBaseGCDetailsFn
|
|
801
|
+
getBaseGCDetailsFn?: () => Promise<IGarbageCollectionDetailsBase>,
|
|
803
802
|
) => this.summarizerNode.createChild(
|
|
804
803
|
summarizeInternal,
|
|
805
804
|
id,
|