@fluidframework/container-runtime 2.1.0-276985 → 2.1.0-281041
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/README.md +71 -18
- package/api-extractor/api-extractor.current.json +5 -0
- package/api-extractor/api-extractor.legacy.json +1 -1
- package/api-extractor.json +1 -1
- package/api-report/container-runtime.legacy.public.api.md +9 -0
- package/container-runtime.test-files.tar +0 -0
- package/dist/blobManager/blobManager.d.ts +10 -0
- package/dist/blobManager/blobManager.d.ts.map +1 -1
- package/dist/blobManager/blobManager.js +19 -0
- package/dist/blobManager/blobManager.js.map +1 -1
- package/dist/channelCollection.d.ts +1 -1
- package/dist/channelCollection.d.ts.map +1 -1
- package/dist/channelCollection.js +40 -8
- package/dist/channelCollection.js.map +1 -1
- package/dist/containerRuntime.d.ts +14 -5
- package/dist/containerRuntime.d.ts.map +1 -1
- package/dist/containerRuntime.js +142 -98
- package/dist/containerRuntime.js.map +1 -1
- package/dist/dataStoreContext.d.ts +4 -0
- package/dist/dataStoreContext.d.ts.map +1 -1
- package/dist/dataStoreContext.js +9 -3
- package/dist/dataStoreContext.js.map +1 -1
- package/dist/gc/garbageCollection.d.ts +1 -1
- package/dist/gc/garbageCollection.d.ts.map +1 -1
- package/dist/gc/garbageCollection.js +14 -8
- package/dist/gc/garbageCollection.js.map +1 -1
- package/dist/gc/gcDefinitions.d.ts +4 -2
- package/dist/gc/gcDefinitions.d.ts.map +1 -1
- package/dist/gc/gcDefinitions.js.map +1 -1
- package/dist/gc/gcHelpers.d.ts.map +1 -1
- package/dist/gc/gcHelpers.js +12 -0
- package/dist/gc/gcHelpers.js.map +1 -1
- package/dist/gc/gcTelemetry.d.ts +3 -2
- package/dist/gc/gcTelemetry.d.ts.map +1 -1
- package/dist/gc/gcTelemetry.js +6 -6
- package/dist/gc/gcTelemetry.js.map +1 -1
- package/dist/legacy.d.ts +1 -1
- package/dist/metadata.d.ts +7 -1
- package/dist/metadata.d.ts.map +1 -1
- package/dist/metadata.js +6 -0
- package/dist/metadata.js.map +1 -1
- package/dist/opLifecycle/batchManager.d.ts +8 -1
- package/dist/opLifecycle/batchManager.d.ts.map +1 -1
- package/dist/opLifecycle/batchManager.js +37 -16
- package/dist/opLifecycle/batchManager.js.map +1 -1
- package/dist/opLifecycle/definitions.d.ts +1 -1
- package/dist/opLifecycle/definitions.d.ts.map +1 -1
- package/dist/opLifecycle/definitions.js.map +1 -1
- package/dist/opLifecycle/index.d.ts +1 -1
- 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.map +1 -1
- package/dist/opLifecycle/opCompressor.js +12 -8
- package/dist/opLifecycle/opCompressor.js.map +1 -1
- package/dist/opLifecycle/opGroupingManager.d.ts.map +1 -1
- package/dist/opLifecycle/opGroupingManager.js +14 -11
- package/dist/opLifecycle/opGroupingManager.js.map +1 -1
- package/dist/opLifecycle/opSplitter.d.ts.map +1 -1
- package/dist/opLifecycle/opSplitter.js +11 -6
- package/dist/opLifecycle/opSplitter.js.map +1 -1
- package/dist/opLifecycle/outbox.d.ts +22 -6
- package/dist/opLifecycle/outbox.d.ts.map +1 -1
- package/dist/opLifecycle/outbox.js +43 -21
- package/dist/opLifecycle/outbox.js.map +1 -1
- package/dist/opLifecycle/remoteMessageProcessor.d.ts +6 -6
- package/dist/opLifecycle/remoteMessageProcessor.d.ts.map +1 -1
- package/dist/opLifecycle/remoteMessageProcessor.js +18 -6
- 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/pendingStateManager.d.ts +37 -13
- package/dist/pendingStateManager.d.ts.map +1 -1
- package/dist/pendingStateManager.js +95 -45
- package/dist/pendingStateManager.js.map +1 -1
- package/dist/public.d.ts +1 -1
- package/dist/scheduleManager.js +4 -0
- package/dist/scheduleManager.js.map +1 -1
- package/dist/summary/summarizerNode/summarizerNodeUtils.d.ts.map +1 -1
- package/dist/summary/summarizerNode/summarizerNodeUtils.js +2 -0
- package/dist/summary/summarizerNode/summarizerNodeUtils.js.map +1 -1
- package/dist/summary/summaryFormat.d.ts.map +1 -1
- package/dist/summary/summaryFormat.js +4 -1
- package/dist/summary/summaryFormat.js.map +1 -1
- package/internal.d.ts +1 -1
- package/legacy.d.ts +1 -1
- package/lib/blobManager/blobManager.d.ts +10 -0
- package/lib/blobManager/blobManager.d.ts.map +1 -1
- package/lib/blobManager/blobManager.js +19 -0
- package/lib/blobManager/blobManager.js.map +1 -1
- package/lib/channelCollection.d.ts +1 -1
- package/lib/channelCollection.d.ts.map +1 -1
- package/lib/channelCollection.js +40 -8
- package/lib/channelCollection.js.map +1 -1
- package/lib/containerRuntime.d.ts +14 -5
- package/lib/containerRuntime.d.ts.map +1 -1
- package/lib/containerRuntime.js +142 -98
- package/lib/containerRuntime.js.map +1 -1
- package/lib/dataStoreContext.d.ts +4 -0
- package/lib/dataStoreContext.d.ts.map +1 -1
- package/lib/dataStoreContext.js +10 -4
- package/lib/dataStoreContext.js.map +1 -1
- package/lib/gc/garbageCollection.d.ts +1 -1
- package/lib/gc/garbageCollection.d.ts.map +1 -1
- package/lib/gc/garbageCollection.js +14 -8
- package/lib/gc/garbageCollection.js.map +1 -1
- package/lib/gc/gcDefinitions.d.ts +4 -2
- package/lib/gc/gcDefinitions.d.ts.map +1 -1
- package/lib/gc/gcDefinitions.js.map +1 -1
- package/lib/gc/gcHelpers.d.ts.map +1 -1
- package/lib/gc/gcHelpers.js +12 -0
- package/lib/gc/gcHelpers.js.map +1 -1
- package/lib/gc/gcTelemetry.d.ts +3 -2
- package/lib/gc/gcTelemetry.d.ts.map +1 -1
- package/lib/gc/gcTelemetry.js +6 -6
- package/lib/gc/gcTelemetry.js.map +1 -1
- package/lib/legacy.d.ts +1 -1
- package/lib/metadata.d.ts +7 -1
- package/lib/metadata.d.ts.map +1 -1
- package/lib/metadata.js +4 -1
- package/lib/metadata.js.map +1 -1
- package/lib/opLifecycle/batchManager.d.ts +8 -1
- package/lib/opLifecycle/batchManager.d.ts.map +1 -1
- package/lib/opLifecycle/batchManager.js +35 -15
- package/lib/opLifecycle/batchManager.js.map +1 -1
- package/lib/opLifecycle/definitions.d.ts +1 -1
- package/lib/opLifecycle/definitions.d.ts.map +1 -1
- package/lib/opLifecycle/definitions.js.map +1 -1
- package/lib/opLifecycle/index.d.ts +1 -1
- 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.map +1 -1
- package/lib/opLifecycle/opCompressor.js +12 -8
- package/lib/opLifecycle/opCompressor.js.map +1 -1
- package/lib/opLifecycle/opGroupingManager.d.ts.map +1 -1
- package/lib/opLifecycle/opGroupingManager.js +14 -11
- package/lib/opLifecycle/opGroupingManager.js.map +1 -1
- package/lib/opLifecycle/opSplitter.d.ts.map +1 -1
- package/lib/opLifecycle/opSplitter.js +11 -6
- package/lib/opLifecycle/opSplitter.js.map +1 -1
- package/lib/opLifecycle/outbox.d.ts +22 -6
- package/lib/opLifecycle/outbox.d.ts.map +1 -1
- package/lib/opLifecycle/outbox.js +44 -22
- package/lib/opLifecycle/outbox.js.map +1 -1
- package/lib/opLifecycle/remoteMessageProcessor.d.ts +6 -6
- package/lib/opLifecycle/remoteMessageProcessor.d.ts.map +1 -1
- package/lib/opLifecycle/remoteMessageProcessor.js +18 -6
- 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/pendingStateManager.d.ts +37 -13
- package/lib/pendingStateManager.d.ts.map +1 -1
- package/lib/pendingStateManager.js +95 -45
- package/lib/pendingStateManager.js.map +1 -1
- package/lib/public.d.ts +1 -1
- package/lib/scheduleManager.js +4 -0
- package/lib/scheduleManager.js.map +1 -1
- package/lib/summary/summarizerNode/summarizerNodeUtils.d.ts.map +1 -1
- package/lib/summary/summarizerNode/summarizerNodeUtils.js +2 -0
- package/lib/summary/summarizerNode/summarizerNodeUtils.js.map +1 -1
- package/lib/summary/summaryFormat.d.ts.map +1 -1
- package/lib/summary/summaryFormat.js +4 -1
- package/lib/summary/summaryFormat.js.map +1 -1
- package/package.json +45 -30
- package/src/blobManager/blobManager.ts +19 -0
- package/src/channelCollection.ts +48 -11
- package/src/containerRuntime.ts +190 -132
- package/src/dataStoreContext.ts +22 -4
- package/src/gc/garbageCollection.ts +15 -10
- package/src/gc/gcDefinitions.ts +7 -2
- package/src/gc/gcHelpers.ts +18 -6
- package/src/gc/gcTelemetry.ts +20 -8
- package/src/metadata.ts +11 -1
- package/src/opLifecycle/README.md +0 -8
- package/src/opLifecycle/batchManager.ts +46 -16
- package/src/opLifecycle/definitions.ts +1 -1
- package/src/opLifecycle/index.ts +8 -1
- package/src/opLifecycle/opCompressor.ts +12 -8
- package/src/opLifecycle/opGroupingManager.ts +14 -11
- package/src/opLifecycle/opSplitter.ts +10 -6
- package/src/opLifecycle/outbox.ts +64 -26
- package/src/opLifecycle/remoteMessageProcessor.ts +24 -8
- package/src/packageVersion.ts +1 -1
- package/src/pendingStateManager.ts +173 -74
- package/src/scheduleManager.ts +6 -2
- package/src/summary/README.md +81 -0
- package/src/summary/summarizerNode/summarizerNodeUtils.ts +3 -1
- package/src/summary/summaryFormat.ts +3 -1
- package/src/summary/summaryFormats.md +69 -8
- package/tsconfig.json +0 -1
- package/src/summary/images/appTree.png +0 -0
- package/src/summary/images/protocolAndAppTree.png +0 -0
- package/src/summary/images/summaryTree.png +0 -0
package/src/containerRuntime.ts
CHANGED
|
@@ -61,6 +61,7 @@ import {
|
|
|
61
61
|
MessageType,
|
|
62
62
|
ISequencedDocumentMessage,
|
|
63
63
|
ISignalMessage,
|
|
64
|
+
type ISummaryContext,
|
|
64
65
|
} from "@fluidframework/driver-definitions/internal";
|
|
65
66
|
import { readAndParse } from "@fluidframework/driver-utils/internal";
|
|
66
67
|
import type { IIdCompressor } from "@fluidframework/id-compressor";
|
|
@@ -91,7 +92,6 @@ import {
|
|
|
91
92
|
} from "@fluidframework/runtime-definitions/internal";
|
|
92
93
|
import {
|
|
93
94
|
GCDataBuilder,
|
|
94
|
-
ReadAndParseBlob,
|
|
95
95
|
RequestParser,
|
|
96
96
|
TelemetryContext,
|
|
97
97
|
addBlobToSummary,
|
|
@@ -169,6 +169,7 @@ import {
|
|
|
169
169
|
} from "./messageTypes.js";
|
|
170
170
|
import { IBatchMetadata, ISavedOpMetadata } from "./metadata.js";
|
|
171
171
|
import {
|
|
172
|
+
BatchId,
|
|
172
173
|
BatchMessage,
|
|
173
174
|
IBatch,
|
|
174
175
|
IBatchCheckpoint,
|
|
@@ -181,7 +182,7 @@ import {
|
|
|
181
182
|
} from "./opLifecycle/index.js";
|
|
182
183
|
import { pkgVersion } from "./packageVersion.js";
|
|
183
184
|
import {
|
|
184
|
-
|
|
185
|
+
PendingMessageResubmitData,
|
|
185
186
|
IPendingLocalState,
|
|
186
187
|
PendingStateManager,
|
|
187
188
|
} from "./pendingStateManager.js";
|
|
@@ -668,7 +669,7 @@ export const makeLegacySendBatchFn =
|
|
|
668
669
|
(batch: IBatch) => {
|
|
669
670
|
// Default to negative one to match Container.submitBatch behavior
|
|
670
671
|
let clientSequenceNumber: number = -1;
|
|
671
|
-
for (const message of batch.
|
|
672
|
+
for (const message of batch.messages) {
|
|
672
673
|
clientSequenceNumber = submitFn(
|
|
673
674
|
MessageType.Operation,
|
|
674
675
|
// For back-compat (submitFn only works on deserialized content)
|
|
@@ -693,7 +694,7 @@ export const makeLegacySendBatchFn =
|
|
|
693
694
|
type MessageWithContext = {
|
|
694
695
|
local: boolean;
|
|
695
696
|
savedOp?: boolean;
|
|
696
|
-
|
|
697
|
+
localOpMetadata?: unknown;
|
|
697
698
|
} & (
|
|
698
699
|
| {
|
|
699
700
|
message: InboundSequencedContainerRuntimeMessage;
|
|
@@ -988,11 +989,7 @@ export class ContainerRuntime
|
|
|
988
989
|
}
|
|
989
990
|
};
|
|
990
991
|
|
|
991
|
-
const disableCompression = mc.config.getBoolean(
|
|
992
|
-
"Fluid.ContainerRuntime.CompressionDisabled",
|
|
993
|
-
);
|
|
994
992
|
const compressionLz4 =
|
|
995
|
-
disableCompression !== true &&
|
|
996
993
|
compressionOptions.minimumBatchSizeInBytes !== Infinity &&
|
|
997
994
|
compressionOptions.compressionAlgorithm === "lz4";
|
|
998
995
|
|
|
@@ -1012,9 +1009,7 @@ export class ContainerRuntime
|
|
|
1012
1009
|
},
|
|
1013
1010
|
);
|
|
1014
1011
|
|
|
1015
|
-
const featureGatesForTelemetry: Record<string, boolean | number | undefined> = {
|
|
1016
|
-
disableCompression,
|
|
1017
|
-
};
|
|
1012
|
+
const featureGatesForTelemetry: Record<string, boolean | number | undefined> = {};
|
|
1018
1013
|
|
|
1019
1014
|
const runtime = new containerRuntimeCtor(
|
|
1020
1015
|
context,
|
|
@@ -1360,6 +1355,13 @@ export class ContainerRuntime
|
|
|
1360
1355
|
*/
|
|
1361
1356
|
private readonly loadedFromVersionId: string | undefined;
|
|
1362
1357
|
|
|
1358
|
+
private readonly isSnapshotInstanceOfISnapshot: boolean | undefined;
|
|
1359
|
+
|
|
1360
|
+
/**
|
|
1361
|
+
* The summary context of the last acked summary. The properties from this as used when uploading a summary.
|
|
1362
|
+
*/
|
|
1363
|
+
private lastAckedSummaryContext: ISummaryContext | undefined;
|
|
1364
|
+
|
|
1363
1365
|
/**
|
|
1364
1366
|
* It a cache for holding mapping for loading groupIds with its snapshot from the service. Add expiry policy of 1 minute.
|
|
1365
1367
|
* Starting with 1 min and based on recorded usage we can tweak it later on.
|
|
@@ -1512,9 +1514,6 @@ export class ContainerRuntime
|
|
|
1512
1514
|
this.disableAttachReorder = this.mc.config.getBoolean(
|
|
1513
1515
|
"Fluid.ContainerRuntime.disableAttachOpReorder",
|
|
1514
1516
|
);
|
|
1515
|
-
const disableChunking = this.mc.config.getBoolean(
|
|
1516
|
-
"Fluid.ContainerRuntime.CompressionChunkingDisabled",
|
|
1517
|
-
);
|
|
1518
1517
|
|
|
1519
1518
|
const opGroupingManager = new OpGroupingManager(
|
|
1520
1519
|
{
|
|
@@ -1531,7 +1530,7 @@ export class ContainerRuntime
|
|
|
1531
1530
|
const opSplitter = new OpSplitter(
|
|
1532
1531
|
chunks,
|
|
1533
1532
|
this.submitBatchFn,
|
|
1534
|
-
|
|
1533
|
+
runtimeOptions.chunkSizeInBytes,
|
|
1535
1534
|
runtimeOptions.maxBatchSizeInBytes,
|
|
1536
1535
|
this.mc.logger,
|
|
1537
1536
|
);
|
|
@@ -1549,10 +1548,6 @@ export class ContainerRuntime
|
|
|
1549
1548
|
clientId: () => this.clientId,
|
|
1550
1549
|
close: this.closeFn,
|
|
1551
1550
|
connected: () => this.connected,
|
|
1552
|
-
reSubmit: (message: IPendingBatchMessage) => {
|
|
1553
|
-
this.reSubmit(message);
|
|
1554
|
-
this.flush();
|
|
1555
|
-
},
|
|
1556
1551
|
reSubmitBatch: this.reSubmitBatch.bind(this),
|
|
1557
1552
|
isActiveConnection: () => this.innerDeltaManager.active,
|
|
1558
1553
|
isAttached: () => this.attachState !== AttachState.Detached,
|
|
@@ -1665,6 +1660,10 @@ export class ContainerRuntime
|
|
|
1665
1660
|
|
|
1666
1661
|
const parentContext = wrapContext(this);
|
|
1667
1662
|
|
|
1663
|
+
if (snapshotWithContents !== undefined) {
|
|
1664
|
+
this.isSnapshotInstanceOfISnapshot = true;
|
|
1665
|
+
}
|
|
1666
|
+
|
|
1668
1667
|
// Due to a mismatch between different layers in terms of
|
|
1669
1668
|
// what is the interface of passing signals, we need the
|
|
1670
1669
|
// downstream stores to wrap the signal.
|
|
@@ -1921,7 +1920,6 @@ export class ContainerRuntime
|
|
|
1921
1920
|
sessionRuntimeSchema: JSON.stringify(this.sessionSchema),
|
|
1922
1921
|
featureGates: JSON.stringify({
|
|
1923
1922
|
...featureGatesForTelemetry,
|
|
1924
|
-
disableChunking,
|
|
1925
1923
|
disableAttachReorder: this.disableAttachReorder,
|
|
1926
1924
|
disablePartialFlush,
|
|
1927
1925
|
closeSummarizerDelayOverride,
|
|
@@ -2167,9 +2165,13 @@ export class ContainerRuntime
|
|
|
2167
2165
|
let childTree = snapshotTree;
|
|
2168
2166
|
for (const part of pathParts) {
|
|
2169
2167
|
if (hasIsolatedChannels) {
|
|
2170
|
-
|
|
2168
|
+
// TODO Why are we non null asserting here
|
|
2169
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
2170
|
+
childTree = childTree.trees[channelsTreeName]!;
|
|
2171
2171
|
}
|
|
2172
|
-
|
|
2172
|
+
// TODO Why are we non null asserting here
|
|
2173
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
2174
|
+
childTree = childTree.trees[part]!;
|
|
2173
2175
|
}
|
|
2174
2176
|
return childTree;
|
|
2175
2177
|
}
|
|
@@ -2221,7 +2223,9 @@ export class ContainerRuntime
|
|
|
2221
2223
|
}
|
|
2222
2224
|
|
|
2223
2225
|
if (id === blobManagerBasePath && requestParser.isLeaf(2)) {
|
|
2224
|
-
|
|
2226
|
+
// TODO why are we non null asserting here?
|
|
2227
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
2228
|
+
const blob = await this.blobManager.getBlob(requestParser.pathParts[1]!);
|
|
2225
2229
|
return blob
|
|
2226
2230
|
? {
|
|
2227
2231
|
status: 200,
|
|
@@ -2618,34 +2622,38 @@ export class ContainerRuntime
|
|
|
2618
2622
|
// but will not modify the contents object (likely it will replace it on the message).
|
|
2619
2623
|
const messageCopy = { ...messageArg };
|
|
2620
2624
|
const savedOp = (messageCopy.metadata as ISavedOpMetadata)?.savedOp;
|
|
2621
|
-
|
|
2622
|
-
|
|
2623
|
-
|
|
2624
|
-
|
|
2625
|
-
|
|
2626
|
-
|
|
2627
|
-
|
|
2628
|
-
const
|
|
2629
|
-
|
|
2630
|
-
|
|
2631
|
-
|
|
2632
|
-
|
|
2633
|
-
|
|
2634
|
-
|
|
2635
|
-
|
|
2636
|
-
|
|
2637
|
-
|
|
2638
|
-
|
|
2639
|
-
|
|
2640
|
-
|
|
2641
|
-
|
|
2642
|
-
|
|
2643
|
-
|
|
2644
|
-
|
|
2645
|
-
|
|
2646
|
-
|
|
2647
|
-
|
|
2648
|
-
|
|
2625
|
+
if (modernRuntimeMessage) {
|
|
2626
|
+
const processResult = this.remoteMessageProcessor.process(messageCopy);
|
|
2627
|
+
if (processResult === undefined) {
|
|
2628
|
+
// This means the incoming message is an incomplete part of a message or batch
|
|
2629
|
+
// and we need to process more messages before the rest of the system can understand it.
|
|
2630
|
+
return;
|
|
2631
|
+
}
|
|
2632
|
+
const batchStartCsn = processResult.batchStartCsn;
|
|
2633
|
+
const batch = processResult.messages;
|
|
2634
|
+
const messages: {
|
|
2635
|
+
message: InboundSequencedContainerRuntimeMessage;
|
|
2636
|
+
localOpMetadata: unknown;
|
|
2637
|
+
}[] = local
|
|
2638
|
+
? this.pendingStateManager.processPendingLocalBatch(batch, batchStartCsn)
|
|
2639
|
+
: batch.map((message) => ({ message, localOpMetadata: undefined }));
|
|
2640
|
+
messages.forEach(({ message, localOpMetadata }) => {
|
|
2641
|
+
const msg: MessageWithContext = {
|
|
2642
|
+
message,
|
|
2643
|
+
local,
|
|
2644
|
+
modernRuntimeMessage,
|
|
2645
|
+
savedOp,
|
|
2646
|
+
localOpMetadata,
|
|
2647
|
+
};
|
|
2648
|
+
this.ensureNoDataModelChanges(() => this.processCore(msg));
|
|
2649
|
+
});
|
|
2650
|
+
} else {
|
|
2651
|
+
const msg: MessageWithContext = {
|
|
2652
|
+
message: messageCopy as InboundSequencedContainerRuntimeMessageOrSystemMessage,
|
|
2653
|
+
local,
|
|
2654
|
+
modernRuntimeMessage,
|
|
2655
|
+
savedOp,
|
|
2656
|
+
};
|
|
2649
2657
|
this.ensureNoDataModelChanges(() => this.processCore(msg));
|
|
2650
2658
|
}
|
|
2651
2659
|
}
|
|
@@ -2656,7 +2664,7 @@ export class ContainerRuntime
|
|
|
2656
2664
|
* Direct the message to the correct subsystem for processing, and implement other side effects
|
|
2657
2665
|
*/
|
|
2658
2666
|
private processCore(messageWithContext: MessageWithContext) {
|
|
2659
|
-
const { message, local } = messageWithContext;
|
|
2667
|
+
const { message, local, localOpMetadata } = messageWithContext;
|
|
2660
2668
|
|
|
2661
2669
|
// Intercept to reduce minimum sequence number to the delta manager's minimum sequence number.
|
|
2662
2670
|
// Sequence numbers are not guaranteed to follow any sort of order. Re-entrancy is one of those situations
|
|
@@ -2676,23 +2684,12 @@ export class ContainerRuntime
|
|
|
2676
2684
|
this._processedClientSequenceNumber = message.clientSequenceNumber;
|
|
2677
2685
|
|
|
2678
2686
|
try {
|
|
2679
|
-
//
|
|
2680
|
-
// These calls should be made for all but chunked ops:
|
|
2681
|
-
// 1) this.pendingStateManager.processPendingLocalMessage() below
|
|
2682
|
-
// 2) this.resetReconnectCount() below
|
|
2687
|
+
// RemoteMessageProcessor would have already reconstituted Chunked Ops into the original op type
|
|
2683
2688
|
assert(
|
|
2684
2689
|
message.type !== ContainerMessageType.ChunkedOp,
|
|
2685
2690
|
0x93b /* we should never get here with chunked ops */,
|
|
2686
2691
|
);
|
|
2687
2692
|
|
|
2688
|
-
let localOpMetadata: unknown;
|
|
2689
|
-
if (local && messageWithContext.modernRuntimeMessage) {
|
|
2690
|
-
localOpMetadata = this.pendingStateManager.processPendingLocalMessage(
|
|
2691
|
-
messageWithContext.message,
|
|
2692
|
-
messageWithContext.batchStartCsn,
|
|
2693
|
-
);
|
|
2694
|
-
}
|
|
2695
|
-
|
|
2696
2693
|
// If there are no more pending messages after processing a local message,
|
|
2697
2694
|
// the document is no longer dirty.
|
|
2698
2695
|
if (!this.hasPendingMessages()) {
|
|
@@ -2888,14 +2885,16 @@ export class ContainerRuntime
|
|
|
2888
2885
|
/**
|
|
2889
2886
|
* Flush the pending ops manually.
|
|
2890
2887
|
* This method is expected to be called at the end of a batch.
|
|
2888
|
+
* @param resubmittingBatchId - If defined, indicates this is a resubmission of a batch
|
|
2889
|
+
* with the given Batch ID, which must be preserved
|
|
2891
2890
|
*/
|
|
2892
|
-
private flush(): void {
|
|
2891
|
+
private flush(resubmittingBatchId?: BatchId): void {
|
|
2893
2892
|
assert(
|
|
2894
2893
|
this._orderSequentiallyCalls === 0,
|
|
2895
2894
|
0x24c /* "Cannot call `flush()` from `orderSequentially`'s callback" */,
|
|
2896
2895
|
);
|
|
2897
2896
|
|
|
2898
|
-
this.outbox.flush();
|
|
2897
|
+
this.outbox.flush(resubmittingBatchId);
|
|
2899
2898
|
assert(this.outbox.isEmpty, 0x3cf /* reentrancy */);
|
|
2900
2899
|
}
|
|
2901
2900
|
|
|
@@ -2909,7 +2908,7 @@ export class ContainerRuntime
|
|
|
2909
2908
|
// Note: we are not touching any batches other than mainBatch here, for two reasons:
|
|
2910
2909
|
// 1. It would not help, as other batches are flushed independently from main batch.
|
|
2911
2910
|
// 2. There is no way to undo process of data store creation, blob creation, ID compressor ops, or other things tracked by other batches.
|
|
2912
|
-
checkpoint = this.outbox.
|
|
2911
|
+
checkpoint = this.outbox.getBatchCheckpoints().mainBatch;
|
|
2913
2912
|
}
|
|
2914
2913
|
try {
|
|
2915
2914
|
this._orderSequentiallyCalls++;
|
|
@@ -3052,7 +3051,7 @@ export class ContainerRuntime
|
|
|
3052
3051
|
}
|
|
3053
3052
|
|
|
3054
3053
|
/**
|
|
3055
|
-
*
|
|
3054
|
+
* Typically ops are batched and later flushed together, but in some cases we want to flush immediately.
|
|
3056
3055
|
*/
|
|
3057
3056
|
private currentlyBatching() {
|
|
3058
3057
|
return this.flushMode !== FlushMode.Immediate || this._orderSequentiallyCalls !== 0;
|
|
@@ -3552,7 +3551,7 @@ export class ContainerRuntime
|
|
|
3552
3551
|
summaryRefSeqNum = this.deltaManager.lastSequenceNumber;
|
|
3553
3552
|
const minimumSequenceNumber = this.deltaManager.minimumSequenceNumber;
|
|
3554
3553
|
const message = `Summary @${summaryRefSeqNum}:${this.deltaManager.minimumSequenceNumber}`;
|
|
3555
|
-
const
|
|
3554
|
+
const lastAckedContext = this.lastAckedSummaryContext;
|
|
3556
3555
|
|
|
3557
3556
|
const startSummaryResult = this.summarizerNode.startSummary(
|
|
3558
3557
|
summaryRefSeqNum,
|
|
@@ -3619,10 +3618,10 @@ export class ContainerRuntime
|
|
|
3619
3618
|
0x395 /* it's one and the same thing */,
|
|
3620
3619
|
);
|
|
3621
3620
|
|
|
3622
|
-
if (
|
|
3621
|
+
if (lastAckedContext !== this.lastAckedSummaryContext) {
|
|
3623
3622
|
return {
|
|
3624
3623
|
continue: false,
|
|
3625
|
-
error: `Last summary changed while summarizing. ${this.
|
|
3624
|
+
error: `Last summary changed while summarizing. ${this.lastAckedSummaryContext} !== ${lastAckedContext}`,
|
|
3626
3625
|
};
|
|
3627
3626
|
}
|
|
3628
3627
|
return { continue: true };
|
|
@@ -3694,7 +3693,9 @@ export class ContainerRuntime
|
|
|
3694
3693
|
// Counting dataStores and handles
|
|
3695
3694
|
// Because handles are unchanged dataStores in the current logic,
|
|
3696
3695
|
// summarized dataStore count is total dataStore count minus handle count
|
|
3697
|
-
|
|
3696
|
+
// TODO why are we non null asserting here
|
|
3697
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
3698
|
+
const dataStoreTree = summaryTree.tree[channelsTreeName]!;
|
|
3698
3699
|
|
|
3699
3700
|
assert(dataStoreTree.type === SummaryType.Tree, 0x1fc /* "summary is not a tree" */);
|
|
3700
3701
|
const handleCount = Object.values(dataStoreTree.tree).filter(
|
|
@@ -3730,18 +3731,11 @@ export class ContainerRuntime
|
|
|
3730
3731
|
};
|
|
3731
3732
|
}
|
|
3732
3733
|
|
|
3733
|
-
const summaryContext =
|
|
3734
|
-
|
|
3735
|
-
|
|
3736
|
-
|
|
3737
|
-
|
|
3738
|
-
referenceSequenceNumber: summaryRefSeqNum,
|
|
3739
|
-
}
|
|
3740
|
-
: {
|
|
3741
|
-
proposalHandle: lastAck.summaryOp.contents.handle,
|
|
3742
|
-
ackHandle: lastAck.summaryAck.contents.handle,
|
|
3743
|
-
referenceSequenceNumber: summaryRefSeqNum,
|
|
3744
|
-
};
|
|
3734
|
+
const summaryContext: ISummaryContext = {
|
|
3735
|
+
proposalHandle: this.lastAckedSummaryContext?.proposalHandle ?? undefined,
|
|
3736
|
+
ackHandle: this.lastAckedSummaryContext?.ackHandle ?? this.loadedFromVersionId,
|
|
3737
|
+
referenceSequenceNumber: summaryRefSeqNum,
|
|
3738
|
+
};
|
|
3745
3739
|
|
|
3746
3740
|
let handle: string;
|
|
3747
3741
|
try {
|
|
@@ -4032,7 +4026,9 @@ export class ContainerRuntime
|
|
|
4032
4026
|
this.outbox.submit(message);
|
|
4033
4027
|
}
|
|
4034
4028
|
|
|
4035
|
-
if
|
|
4029
|
+
// Note: Technically, the system "always" batches - if this case is true we'll just have a single-message batch.
|
|
4030
|
+
const flushImmediatelyOnSubmit = !this.currentlyBatching();
|
|
4031
|
+
if (flushImmediatelyOnSubmit) {
|
|
4036
4032
|
this.flush();
|
|
4037
4033
|
} else {
|
|
4038
4034
|
this.scheduleFlush();
|
|
@@ -4113,16 +4109,22 @@ export class ContainerRuntime
|
|
|
4113
4109
|
}
|
|
4114
4110
|
}
|
|
4115
4111
|
|
|
4116
|
-
private reSubmitBatch(batch:
|
|
4112
|
+
private reSubmitBatch(batch: PendingMessageResubmitData[], batchId: BatchId) {
|
|
4117
4113
|
this.orderSequentially(() => {
|
|
4118
4114
|
for (const message of batch) {
|
|
4119
4115
|
this.reSubmit(message);
|
|
4120
4116
|
}
|
|
4121
4117
|
});
|
|
4122
|
-
|
|
4118
|
+
|
|
4119
|
+
// Only include Batch ID if "Offline Load" feature is enabled
|
|
4120
|
+
// It's only needed to identify batches across container forks arising from misuse of offline load.
|
|
4121
|
+
const includeBatchId =
|
|
4122
|
+
this.mc.config.getBoolean("Fluid.Container.enableOfflineLoad") ?? false;
|
|
4123
|
+
|
|
4124
|
+
this.flush(includeBatchId ? batchId : undefined);
|
|
4123
4125
|
}
|
|
4124
4126
|
|
|
4125
|
-
private reSubmit(message:
|
|
4127
|
+
private reSubmit(message: PendingMessageResubmitData) {
|
|
4126
4128
|
// Need to parse from string for back-compat
|
|
4127
4129
|
const containerRuntimeMessage = this.parseLocalOpContent(message.content);
|
|
4128
4130
|
this.reSubmitCore(containerRuntimeMessage, message.localOpMetadata, message.opMetadata);
|
|
@@ -4225,84 +4227,140 @@ export class ContainerRuntime
|
|
|
4225
4227
|
const { proposalHandle, ackHandle, summaryRefSeq, summaryLogger } = options;
|
|
4226
4228
|
// proposalHandle is always passed from RunningSummarizer.
|
|
4227
4229
|
assert(proposalHandle !== undefined, 0x766 /* proposalHandle should be available */);
|
|
4228
|
-
const readAndParseBlob = async <T>(id: string) => readAndParse<T>(this.storage, id);
|
|
4229
4230
|
const result = await this.summarizerNode.refreshLatestSummary(
|
|
4230
4231
|
proposalHandle,
|
|
4231
4232
|
summaryRefSeq,
|
|
4232
4233
|
);
|
|
4233
4234
|
|
|
4235
|
+
/* eslint-disable jsdoc/check-indentation */
|
|
4234
4236
|
/**
|
|
4235
|
-
*
|
|
4236
|
-
*
|
|
4237
|
-
*
|
|
4238
|
-
*
|
|
4237
|
+
* If the snapshot corresponding to the ack is not tracked by this client, it was submitted by another client.
|
|
4238
|
+
* Take action as per the following scenarios:
|
|
4239
|
+
* 1. If that snapshot is older than the one tracked by this client, ignore the ack because only the latest
|
|
4240
|
+
* snapshot is tracked.
|
|
4241
|
+
* 2. If that snapshot is newer, attempt to fetch the latest snapshot and do one of the following:
|
|
4242
|
+
* 2.1. If the fetched snapshot is same or newer than the one for which ack was received, close this client.
|
|
4243
|
+
* The next summarizer client will likely start from this snapshot and get out of this state. Fetching
|
|
4244
|
+
* the snapshot updates the cache for this client so if it's re-elected as summarizer, this will prevent
|
|
4245
|
+
* any thrashing.
|
|
4246
|
+
* 2.2. If the fetched snapshot is older than the one for which ack was received, ignore the ack. This can
|
|
4247
|
+
* happen in scenarios where the snapshot for the ack was lost in storage (in scenarios like DB rollback,
|
|
4248
|
+
* etc.) but the summary ack is still there because it's tracked a different service. In such cases,
|
|
4249
|
+
* ignoring the ack is the correct thing to do because the latest snapshot in storage is not the one for
|
|
4250
|
+
* the ack but is still the one tracked by this client. If we were to close the summarizer like in the
|
|
4251
|
+
* previous scenario, it will result in this document stuck in this state in a loop.
|
|
4239
4252
|
*/
|
|
4240
|
-
|
|
4241
|
-
|
|
4242
|
-
|
|
4243
|
-
|
|
4244
|
-
|
|
4245
|
-
ackHandle,
|
|
4246
|
-
targetSequenceNumber: summaryRefSeq,
|
|
4247
|
-
},
|
|
4248
|
-
readAndParseBlob,
|
|
4249
|
-
);
|
|
4253
|
+
/* eslint-enable jsdoc/check-indentation */
|
|
4254
|
+
if (!result.isSummaryTracked) {
|
|
4255
|
+
if (result.isSummaryNewer) {
|
|
4256
|
+
await this.fetchLatestSnapshotAndMaybeClose(summaryRefSeq, ackHandle, summaryLogger);
|
|
4257
|
+
}
|
|
4250
4258
|
return;
|
|
4251
4259
|
}
|
|
4252
4260
|
|
|
4253
4261
|
// Notify the garbage collector so it can update its latest summary state.
|
|
4254
4262
|
await this.garbageCollector.refreshLatestSummary(result);
|
|
4263
|
+
|
|
4264
|
+
// If we here, the ack was tracked by this client. Update the summary context of the last ack.
|
|
4265
|
+
this.lastAckedSummaryContext = {
|
|
4266
|
+
proposalHandle,
|
|
4267
|
+
ackHandle,
|
|
4268
|
+
referenceSequenceNumber: summaryRefSeq,
|
|
4269
|
+
};
|
|
4255
4270
|
}
|
|
4256
4271
|
|
|
4257
4272
|
/**
|
|
4258
|
-
* Fetches the latest snapshot from storage
|
|
4259
|
-
*
|
|
4260
|
-
*
|
|
4273
|
+
* Fetches the latest snapshot from storage. If the fetched snapshot is same or newer than the one for which ack
|
|
4274
|
+
* was received, close this client. Fetching the snapshot will update the cache for this client so if it's
|
|
4275
|
+
* re-elected as summarizer, this will prevent any thrashing.
|
|
4276
|
+
* If the fetched snapshot is older than the one for which ack was received, ignore the ack and return. This can
|
|
4277
|
+
* happen in scenarios where the snapshot for the ack was lost in storage in scenarios like DB rollback, etc.
|
|
4261
4278
|
*/
|
|
4262
|
-
private async
|
|
4279
|
+
private async fetchLatestSnapshotAndMaybeClose(
|
|
4280
|
+
targetRefSeq: number,
|
|
4281
|
+
targetAckHandle: string,
|
|
4263
4282
|
logger: ITelemetryLoggerExt,
|
|
4264
|
-
event: ITelemetryGenericEventExt,
|
|
4265
|
-
readAndParseBlob: ReadAndParseBlob,
|
|
4266
4283
|
) {
|
|
4267
|
-
await PerformanceEvent.timedExecAsync(
|
|
4284
|
+
const fetchedSnapshotRefSeq = await PerformanceEvent.timedExecAsync(
|
|
4268
4285
|
logger,
|
|
4269
|
-
|
|
4286
|
+
{ eventName: "RefreshLatestSummaryAckFetch" },
|
|
4270
4287
|
async (perfEvent: {
|
|
4271
4288
|
end: (arg0: {
|
|
4272
|
-
|
|
4273
|
-
|
|
4274
|
-
|
|
4275
|
-
|
|
4289
|
+
details: {
|
|
4290
|
+
getVersionDuration?: number | undefined;
|
|
4291
|
+
getSnapshotDuration?: number | undefined;
|
|
4292
|
+
snapshotRefSeq?: number | undefined;
|
|
4293
|
+
snapshotVersion?: string | undefined;
|
|
4294
|
+
newerSnapshotPresent?: boolean | undefined;
|
|
4295
|
+
targetRefSeq?: number | undefined;
|
|
4296
|
+
targetAckHandle?: string | undefined;
|
|
4297
|
+
};
|
|
4276
4298
|
}) => void;
|
|
4277
4299
|
}) => {
|
|
4278
|
-
const
|
|
4300
|
+
const props: {
|
|
4279
4301
|
getVersionDuration?: number;
|
|
4280
4302
|
getSnapshotDuration?: number;
|
|
4281
4303
|
snapshotRefSeq?: number;
|
|
4282
4304
|
snapshotVersion?: string;
|
|
4283
|
-
|
|
4305
|
+
newerSnapshotPresent?: boolean | undefined;
|
|
4306
|
+
targetRefSeq?: number | undefined;
|
|
4307
|
+
targetAckHandle?: string | undefined;
|
|
4308
|
+
} = { targetRefSeq, targetAckHandle };
|
|
4284
4309
|
const trace = Trace.start();
|
|
4285
4310
|
|
|
4286
|
-
|
|
4287
|
-
|
|
4288
|
-
|
|
4289
|
-
|
|
4290
|
-
|
|
4291
|
-
|
|
4292
|
-
|
|
4293
|
-
|
|
4311
|
+
let snapshotTree: ISnapshotTree | null;
|
|
4312
|
+
const scenarioName = "RefreshLatestSummaryAckFetch";
|
|
4313
|
+
// If loader supplied us the ISnapshot when loading, the new getSnapshotApi is supported and feature gate is ON, then use the
|
|
4314
|
+
// new API, otherwise it will reduce the service performance because the service will need to recalculate the full snapshot
|
|
4315
|
+
// in case previously getSnapshotApi was used and now we use the getVersions API.
|
|
4316
|
+
if (
|
|
4317
|
+
this.isSnapshotInstanceOfISnapshot &&
|
|
4318
|
+
this.storage.getSnapshot !== undefined &&
|
|
4319
|
+
this.mc.config.getBoolean("Fluid.Container.UseLoadingGroupIdForSnapshotFetch2") ===
|
|
4320
|
+
true
|
|
4321
|
+
) {
|
|
4322
|
+
const snapshot = await this.storage.getSnapshot({
|
|
4323
|
+
scenarioName,
|
|
4324
|
+
fetchSource: FetchSource.noCache,
|
|
4325
|
+
});
|
|
4326
|
+
const id = snapshot.snapshotTree.id;
|
|
4327
|
+
assert(id !== undefined, "id of the fetched snapshot should be defined");
|
|
4328
|
+
props.snapshotVersion = id;
|
|
4329
|
+
snapshotTree = snapshot.snapshotTree;
|
|
4330
|
+
} else {
|
|
4331
|
+
const versions = await this.storage.getVersions(
|
|
4332
|
+
null,
|
|
4333
|
+
1,
|
|
4334
|
+
scenarioName,
|
|
4335
|
+
FetchSource.noCache,
|
|
4336
|
+
);
|
|
4337
|
+
assert(
|
|
4338
|
+
!!versions && !!versions[0],
|
|
4339
|
+
0x137 /* "Failed to get version from storage" */,
|
|
4340
|
+
);
|
|
4341
|
+
snapshotTree = await this.storage.getSnapshotTree(versions[0]);
|
|
4342
|
+
assert(!!snapshotTree, 0x138 /* "Failed to get snapshot from storage" */);
|
|
4343
|
+
props.snapshotVersion = versions[0].id;
|
|
4344
|
+
}
|
|
4294
4345
|
|
|
4295
|
-
|
|
4296
|
-
|
|
4297
|
-
|
|
4298
|
-
|
|
4299
|
-
|
|
4300
|
-
stats.snapshotVersion = versions[0].id;
|
|
4346
|
+
props.getSnapshotDuration = trace.trace().duration;
|
|
4347
|
+
const readAndParseBlob = async <T>(id: string) => readAndParse<T>(this.storage, id);
|
|
4348
|
+
const snapshotRefSeq = await seqFromTree(snapshotTree, readAndParseBlob);
|
|
4349
|
+
props.snapshotRefSeq = snapshotRefSeq;
|
|
4350
|
+
props.newerSnapshotPresent = snapshotRefSeq >= targetRefSeq;
|
|
4301
4351
|
|
|
4302
|
-
perfEvent.end(
|
|
4352
|
+
perfEvent.end({ details: props });
|
|
4353
|
+
return snapshotRefSeq;
|
|
4303
4354
|
},
|
|
4304
4355
|
);
|
|
4305
4356
|
|
|
4357
|
+
// If the snapshot that was fetched is older than the target snapshot, return. The summarizer will not be closed
|
|
4358
|
+
// because the snapshot is likely deleted from storage and it so, closing the summarizer will result in the
|
|
4359
|
+
// document being stuck in this state.
|
|
4360
|
+
if (fetchedSnapshotRefSeq < targetRefSeq) {
|
|
4361
|
+
return;
|
|
4362
|
+
}
|
|
4363
|
+
|
|
4306
4364
|
await delay(this.closeSummarizerDelayMs);
|
|
4307
4365
|
this._summarizer?.stop("latestSummaryStateStale");
|
|
4308
4366
|
this.disposeFn();
|
package/src/dataStoreContext.ts
CHANGED
|
@@ -59,7 +59,6 @@ import {
|
|
|
59
59
|
isSnapshotFetchRequiredForLoadingGroupId,
|
|
60
60
|
} from "@fluidframework/runtime-utils/internal";
|
|
61
61
|
import {
|
|
62
|
-
DataCorruptionError,
|
|
63
62
|
DataProcessingError,
|
|
64
63
|
LoggingError,
|
|
65
64
|
MonitoringContext,
|
|
@@ -305,6 +304,13 @@ export abstract class FluidDataStoreContext
|
|
|
305
304
|
return this._isInMemoryRoot;
|
|
306
305
|
}
|
|
307
306
|
|
|
307
|
+
/**
|
|
308
|
+
* Returns the count of pending messages that are stored until the data store is realized.
|
|
309
|
+
*/
|
|
310
|
+
public get pendingCount(): number {
|
|
311
|
+
return this.pending?.length ?? 0;
|
|
312
|
+
}
|
|
313
|
+
|
|
308
314
|
protected registry: IFluidDataStoreRegistry | undefined;
|
|
309
315
|
|
|
310
316
|
protected detachedRuntimeCreation = false;
|
|
@@ -567,8 +573,10 @@ export abstract class FluidDataStoreContext
|
|
|
567
573
|
// "verifyNotClosed" which logs tombstone errors. Throw error if tombstoned and throwing on load is configured.
|
|
568
574
|
this.verifyNotClosed("process", false /* checkTombstone */, safeTelemetryProps);
|
|
569
575
|
if (this.tombstoned && this.gcThrowOnTombstoneUsage) {
|
|
570
|
-
throw
|
|
576
|
+
throw DataProcessingError.create(
|
|
571
577
|
"Context is tombstoned! Call site [process]",
|
|
578
|
+
"process",
|
|
579
|
+
undefined /* sequencedMessage */,
|
|
572
580
|
safeTelemetryProps,
|
|
573
581
|
);
|
|
574
582
|
}
|
|
@@ -961,7 +969,12 @@ export abstract class FluidDataStoreContext
|
|
|
961
969
|
) {
|
|
962
970
|
if (this.deleted) {
|
|
963
971
|
const messageString = `Context is deleted! Call site [${callSite}]`;
|
|
964
|
-
const error =
|
|
972
|
+
const error = DataProcessingError.create(
|
|
973
|
+
messageString,
|
|
974
|
+
callSite,
|
|
975
|
+
undefined /* sequencedMessage */,
|
|
976
|
+
safeTelemetryProps,
|
|
977
|
+
);
|
|
965
978
|
this.mc.logger.sendErrorEvent(
|
|
966
979
|
{
|
|
967
980
|
eventName: "GC_Deleted_DataStore_Changed",
|
|
@@ -979,7 +992,12 @@ export abstract class FluidDataStoreContext
|
|
|
979
992
|
|
|
980
993
|
if (checkTombstone && this.tombstoned) {
|
|
981
994
|
const messageString = `Context is tombstoned! Call site [${callSite}]`;
|
|
982
|
-
const error =
|
|
995
|
+
const error = DataProcessingError.create(
|
|
996
|
+
messageString,
|
|
997
|
+
callSite,
|
|
998
|
+
undefined /* sequencedMessage */,
|
|
999
|
+
safeTelemetryProps,
|
|
1000
|
+
);
|
|
983
1001
|
|
|
984
1002
|
sendGCUnexpectedUsageEvent(
|
|
985
1003
|
this.mc,
|