@fluidframework/container-runtime 2.3.0-288113 → 2.3.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/CHANGELOG.md +15 -0
- package/api-report/container-runtime.legacy.alpha.api.md +15 -0
- package/container-runtime.test-files.tar +0 -0
- package/dist/channelCollection.d.ts +1 -1
- package/dist/channelCollection.d.ts.map +1 -1
- package/dist/channelCollection.js +1 -16
- package/dist/channelCollection.js.map +1 -1
- package/dist/connectionTelemetry.d.ts +27 -3
- package/dist/connectionTelemetry.d.ts.map +1 -1
- package/dist/connectionTelemetry.js.map +1 -1
- package/dist/containerRuntime.d.ts +68 -13
- package/dist/containerRuntime.d.ts.map +1 -1
- package/dist/containerRuntime.js +262 -180
- package/dist/containerRuntime.js.map +1 -1
- package/dist/deltaManagerProxies.d.ts.map +1 -1
- package/dist/deltaManagerProxies.js +11 -4
- package/dist/deltaManagerProxies.js.map +1 -1
- package/dist/gc/garbageCollection.d.ts.map +1 -1
- package/dist/gc/garbageCollection.js +0 -2
- package/dist/gc/garbageCollection.js.map +1 -1
- package/dist/gc/gcHelpers.d.ts.map +1 -1
- package/dist/gc/gcHelpers.js +0 -8
- package/dist/gc/gcHelpers.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/legacy.d.ts +3 -1
- package/dist/messageTypes.d.ts +0 -9
- package/dist/messageTypes.d.ts.map +1 -1
- package/dist/messageTypes.js.map +1 -1
- package/dist/opLifecycle/batchManager.d.ts +9 -0
- package/dist/opLifecycle/batchManager.d.ts.map +1 -1
- package/dist/opLifecycle/batchManager.js +19 -6
- package/dist/opLifecycle/batchManager.js.map +1 -1
- package/dist/opLifecycle/duplicateBatchDetector.d.ts +32 -0
- package/dist/opLifecycle/duplicateBatchDetector.d.ts.map +1 -0
- package/dist/opLifecycle/duplicateBatchDetector.js +68 -0
- package/dist/opLifecycle/duplicateBatchDetector.js.map +1 -0
- package/dist/opLifecycle/index.d.ts +3 -2
- package/dist/opLifecycle/index.d.ts.map +1 -1
- package/dist/opLifecycle/index.js +4 -1
- package/dist/opLifecycle/index.js.map +1 -1
- package/dist/opLifecycle/opCompressor.d.ts.map +1 -1
- package/dist/opLifecycle/opCompressor.js +0 -4
- package/dist/opLifecycle/opCompressor.js.map +1 -1
- package/dist/opLifecycle/opGroupingManager.d.ts.map +1 -1
- package/dist/opLifecycle/opGroupingManager.js +0 -4
- package/dist/opLifecycle/opGroupingManager.js.map +1 -1
- package/dist/opLifecycle/opSplitter.d.ts.map +1 -1
- package/dist/opLifecycle/opSplitter.js +1 -6
- package/dist/opLifecycle/opSplitter.js.map +1 -1
- package/dist/opLifecycle/outbox.d.ts.map +1 -1
- package/dist/opLifecycle/outbox.js +1 -4
- package/dist/opLifecycle/outbox.js.map +1 -1
- package/dist/opLifecycle/remoteMessageProcessor.d.ts +37 -17
- package/dist/opLifecycle/remoteMessageProcessor.d.ts.map +1 -1
- package/dist/opLifecycle/remoteMessageProcessor.js +47 -37
- package/dist/opLifecycle/remoteMessageProcessor.js.map +1 -1
- package/dist/packageVersion.d.ts +1 -1
- package/dist/packageVersion.d.ts.map +1 -1
- package/dist/packageVersion.js +1 -1
- package/dist/packageVersion.js.map +1 -1
- package/dist/pendingStateManager.d.ts +27 -17
- package/dist/pendingStateManager.d.ts.map +1 -1
- package/dist/pendingStateManager.js +85 -56
- package/dist/pendingStateManager.js.map +1 -1
- package/dist/scheduleManager.d.ts +2 -4
- package/dist/scheduleManager.d.ts.map +1 -1
- package/dist/scheduleManager.js +6 -37
- package/dist/scheduleManager.js.map +1 -1
- package/dist/summary/summarizerNode/summarizerNodeUtils.d.ts.map +1 -1
- package/dist/summary/summarizerNode/summarizerNodeUtils.js +0 -2
- package/dist/summary/summarizerNode/summarizerNodeUtils.js.map +1 -1
- package/dist/summary/summaryCollection.d.ts.map +1 -1
- package/dist/summary/summaryCollection.js +5 -7
- package/dist/summary/summaryCollection.js.map +1 -1
- package/dist/summary/summaryFormat.d.ts.map +1 -1
- package/dist/summary/summaryFormat.js +1 -4
- package/dist/summary/summaryFormat.js.map +1 -1
- package/lib/channelCollection.d.ts +1 -1
- package/lib/channelCollection.d.ts.map +1 -1
- package/lib/channelCollection.js +1 -16
- package/lib/channelCollection.js.map +1 -1
- package/lib/connectionTelemetry.d.ts +27 -3
- package/lib/connectionTelemetry.d.ts.map +1 -1
- package/lib/connectionTelemetry.js.map +1 -1
- package/lib/containerRuntime.d.ts +68 -13
- package/lib/containerRuntime.d.ts.map +1 -1
- package/lib/containerRuntime.js +262 -181
- package/lib/containerRuntime.js.map +1 -1
- package/lib/deltaManagerProxies.d.ts.map +1 -1
- package/lib/deltaManagerProxies.js +11 -4
- package/lib/deltaManagerProxies.js.map +1 -1
- package/lib/gc/garbageCollection.d.ts.map +1 -1
- package/lib/gc/garbageCollection.js +0 -2
- package/lib/gc/garbageCollection.js.map +1 -1
- package/lib/gc/gcHelpers.d.ts.map +1 -1
- package/lib/gc/gcHelpers.js +0 -8
- package/lib/gc/gcHelpers.js.map +1 -1
- package/lib/index.d.ts +1 -1
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +1 -1
- package/lib/index.js.map +1 -1
- package/lib/legacy.d.ts +3 -1
- package/lib/messageTypes.d.ts +0 -9
- package/lib/messageTypes.d.ts.map +1 -1
- package/lib/messageTypes.js.map +1 -1
- package/lib/opLifecycle/batchManager.d.ts +9 -0
- package/lib/opLifecycle/batchManager.d.ts.map +1 -1
- package/lib/opLifecycle/batchManager.js +17 -5
- package/lib/opLifecycle/batchManager.js.map +1 -1
- package/lib/opLifecycle/duplicateBatchDetector.d.ts +32 -0
- package/lib/opLifecycle/duplicateBatchDetector.d.ts.map +1 -0
- package/lib/opLifecycle/duplicateBatchDetector.js +64 -0
- package/lib/opLifecycle/duplicateBatchDetector.js.map +1 -0
- package/lib/opLifecycle/index.d.ts +3 -2
- package/lib/opLifecycle/index.d.ts.map +1 -1
- package/lib/opLifecycle/index.js +2 -1
- package/lib/opLifecycle/index.js.map +1 -1
- package/lib/opLifecycle/opCompressor.d.ts.map +1 -1
- package/lib/opLifecycle/opCompressor.js +0 -4
- package/lib/opLifecycle/opCompressor.js.map +1 -1
- package/lib/opLifecycle/opGroupingManager.d.ts.map +1 -1
- package/lib/opLifecycle/opGroupingManager.js +0 -4
- package/lib/opLifecycle/opGroupingManager.js.map +1 -1
- package/lib/opLifecycle/opSplitter.d.ts.map +1 -1
- package/lib/opLifecycle/opSplitter.js +1 -6
- package/lib/opLifecycle/opSplitter.js.map +1 -1
- package/lib/opLifecycle/outbox.d.ts.map +1 -1
- package/lib/opLifecycle/outbox.js +1 -4
- package/lib/opLifecycle/outbox.js.map +1 -1
- package/lib/opLifecycle/remoteMessageProcessor.d.ts +37 -17
- package/lib/opLifecycle/remoteMessageProcessor.d.ts.map +1 -1
- package/lib/opLifecycle/remoteMessageProcessor.js +47 -37
- package/lib/opLifecycle/remoteMessageProcessor.js.map +1 -1
- package/lib/packageVersion.d.ts +1 -1
- package/lib/packageVersion.d.ts.map +1 -1
- package/lib/packageVersion.js +1 -1
- package/lib/packageVersion.js.map +1 -1
- package/lib/pendingStateManager.d.ts +27 -17
- package/lib/pendingStateManager.d.ts.map +1 -1
- package/lib/pendingStateManager.js +85 -56
- package/lib/pendingStateManager.js.map +1 -1
- package/lib/scheduleManager.d.ts +2 -4
- package/lib/scheduleManager.d.ts.map +1 -1
- package/lib/scheduleManager.js +6 -37
- package/lib/scheduleManager.js.map +1 -1
- package/lib/summary/summarizerNode/summarizerNodeUtils.d.ts.map +1 -1
- package/lib/summary/summarizerNode/summarizerNodeUtils.js +0 -2
- package/lib/summary/summarizerNode/summarizerNodeUtils.js.map +1 -1
- package/lib/summary/summaryCollection.d.ts.map +1 -1
- package/lib/summary/summaryCollection.js +5 -7
- package/lib/summary/summaryCollection.js.map +1 -1
- package/lib/summary/summaryFormat.d.ts.map +1 -1
- package/lib/summary/summaryFormat.js +1 -4
- package/lib/summary/summaryFormat.js.map +1 -1
- package/lib/tsdoc-metadata.json +1 -1
- package/package.json +50 -24
- package/src/channelCollection.ts +7 -21
- package/src/connectionTelemetry.ts +33 -3
- package/src/containerRuntime.ts +382 -233
- package/src/deltaManagerProxies.ts +11 -4
- package/src/gc/garbageCollection.ts +1 -3
- package/src/gc/gcHelpers.ts +4 -12
- package/src/index.ts +2 -0
- package/src/messageTypes.ts +0 -10
- package/src/opLifecycle/batchManager.ts +29 -7
- package/src/opLifecycle/duplicateBatchDetector.ts +78 -0
- package/src/opLifecycle/index.ts +4 -1
- package/src/opLifecycle/opCompressor.ts +2 -6
- package/src/opLifecycle/opGroupingManager.ts +2 -6
- package/src/opLifecycle/opSplitter.ts +2 -6
- package/src/opLifecycle/outbox.ts +1 -3
- package/src/opLifecycle/remoteMessageProcessor.ts +87 -59
- package/src/packageVersion.ts +1 -1
- package/src/pendingStateManager.ts +114 -66
- package/src/scheduleManager.ts +8 -47
- package/src/summary/summarizerNode/summarizerNodeUtils.ts +1 -3
- package/src/summary/summaryCollection.ts +7 -9
- package/src/summary/summaryFormat.ts +1 -3
- package/src/summary/summaryFormats.md +11 -9
- package/tsconfig.json +1 -0
package/dist/containerRuntime.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Licensed under the MIT License.
|
|
5
5
|
*/
|
|
6
6
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
-
exports.ContainerRuntime = exports.makeLegacySendBatchFn = exports.getDeviceSpec = exports.agentSchedulerId = exports.isRuntimeMessage = exports.defaultPendingOpsRetryDelayMs = exports.defaultPendingOpsWaitTimeoutMs = exports.disabledCompressionConfig = exports.CompressionAlgorithms = exports.defaultRuntimeHeaderData = exports.InactiveResponseHeaderKey = exports.TombstoneResponseHeaderKey = exports.DeletedResponseHeaderKey = exports.DefaultSummaryConfiguration = void 0;
|
|
7
|
+
exports.ContainerRuntime = exports.loadContainerRuntime = exports.makeLegacySendBatchFn = exports.getDeviceSpec = exports.agentSchedulerId = exports.isRuntimeMessage = exports.defaultPendingOpsRetryDelayMs = exports.defaultPendingOpsWaitTimeoutMs = exports.disabledCompressionConfig = exports.CompressionAlgorithms = exports.defaultRuntimeHeaderData = exports.InactiveResponseHeaderKey = exports.TombstoneResponseHeaderKey = exports.DeletedResponseHeaderKey = exports.DefaultSummaryConfiguration = void 0;
|
|
8
8
|
const client_utils_1 = require("@fluid-internal/client-utils");
|
|
9
9
|
const container_definitions_1 = require("@fluidframework/container-definitions");
|
|
10
10
|
const internal_1 = require("@fluidframework/container-definitions/internal");
|
|
@@ -232,15 +232,26 @@ function lastMessageFromMetadata(metadata) {
|
|
|
232
232
|
* We only want to log this once, to avoid spamming telemetry if we are wrong and these cases are hit commonly.
|
|
233
233
|
*/
|
|
234
234
|
let getSingleUseLegacyLogCallback = (logger, type) => {
|
|
235
|
-
// We only want to log this once per ContainerRuntime instance, to avoid spamming telemetry.
|
|
236
|
-
getSingleUseLegacyLogCallback = () => () => { };
|
|
237
235
|
return (codePath) => {
|
|
238
236
|
logger.sendTelemetryEvent({
|
|
239
237
|
eventName: "LegacyMessageFormat",
|
|
240
238
|
details: { codePath, type },
|
|
241
239
|
});
|
|
240
|
+
// Now that we've logged, prevent future logging (globally).
|
|
241
|
+
getSingleUseLegacyLogCallback = () => () => { };
|
|
242
242
|
};
|
|
243
243
|
};
|
|
244
|
+
/**
|
|
245
|
+
* This is meant to be used by a {@link @fluidframework/container-definitions#IRuntimeFactory} to instantiate a container runtime.
|
|
246
|
+
* @param params - An object which specifies all required and optional params necessary to instantiate a runtime.
|
|
247
|
+
* @returns A runtime which provides all the functionality necessary to bind with the loader layer via the {@link @fluidframework/container-definitions#IRuntime} interface and provide a runtime environment via the {@link @fluidframework/container-runtime-definitions#IContainerRuntime} interface.
|
|
248
|
+
* @legacy
|
|
249
|
+
* @alpha
|
|
250
|
+
*/
|
|
251
|
+
async function loadContainerRuntime(params) {
|
|
252
|
+
return ContainerRuntime.loadRuntime(params);
|
|
253
|
+
}
|
|
254
|
+
exports.loadContainerRuntime = loadContainerRuntime;
|
|
244
255
|
/**
|
|
245
256
|
* Represents the runtime of the container. Contains helper functions/state of the container.
|
|
246
257
|
* It will define the store level mappings.
|
|
@@ -592,11 +603,16 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
592
603
|
this._disposed = false;
|
|
593
604
|
this.emitDirtyDocumentEvent = true;
|
|
594
605
|
this.defaultTelemetrySignalSampleCount = 100;
|
|
595
|
-
this.
|
|
606
|
+
this._signalTracking = {
|
|
607
|
+
totalSignalsSentInLatencyWindow: 0,
|
|
596
608
|
signalsLost: 0,
|
|
597
|
-
|
|
609
|
+
signalsOutOfOrder: 0,
|
|
610
|
+
signalsSentSinceLastLatencyMeasurement: 0,
|
|
611
|
+
broadcastSignalSequenceNumber: 0,
|
|
598
612
|
signalTimestamp: 0,
|
|
613
|
+
roundTripSignalSequenceNumber: undefined,
|
|
599
614
|
trackingSignalSequenceNumber: undefined,
|
|
615
|
+
minimumTrackingSignalSequenceNumber: undefined,
|
|
600
616
|
};
|
|
601
617
|
/**
|
|
602
618
|
* It a cache for holding mapping for loading groupIds with its snapshot from the service. Add expiry policy of 1 minute.
|
|
@@ -704,13 +720,14 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
704
720
|
isAttached: () => this.attachState !== container_definitions_1.AttachState.Detached,
|
|
705
721
|
}, pendingRuntimeState?.pending, this.logger);
|
|
706
722
|
let outerDeltaManager;
|
|
707
|
-
|
|
723
|
+
this.useDeltaManagerOpsProxy =
|
|
724
|
+
this.mc.config.getBoolean("Fluid.ContainerRuntime.DeltaManagerOpsProxy") === true;
|
|
708
725
|
// The summarizerDeltaManager Proxy is used to lie to the summarizer to convince it is in the right state as a summarizer client.
|
|
709
726
|
const summarizerDeltaManagerProxy = new deltaManagerProxies_js_1.DeltaManagerSummarizerProxy(this.innerDeltaManager);
|
|
710
727
|
outerDeltaManager = summarizerDeltaManagerProxy;
|
|
711
728
|
// The DeltaManagerPendingOpsProxy is used to control the minimum sequence number
|
|
712
729
|
// It allows us to lie to the layers below so that they can maintain enough local state for rebasing ops.
|
|
713
|
-
if (useDeltaManagerOpsProxy) {
|
|
730
|
+
if (this.useDeltaManagerOpsProxy) {
|
|
714
731
|
const pendingOpsDeltaManagerProxy = new deltaManagerProxies_js_1.DeltaManagerPendingOpsProxy(summarizerDeltaManagerProxy, this.pendingStateManager);
|
|
715
732
|
outerDeltaManager = pendingOpsDeltaManagerProxy;
|
|
716
733
|
}
|
|
@@ -741,6 +758,12 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
741
758
|
this.closeFn(error);
|
|
742
759
|
throw error;
|
|
743
760
|
}
|
|
761
|
+
// DuplicateBatchDetection is only enabled if Offline Load is enabled
|
|
762
|
+
// It maintains a cache of all batchIds/sequenceNumbers within the collab window.
|
|
763
|
+
// Don't waste resources doing so if not needed.
|
|
764
|
+
if (this.offlineEnabled) {
|
|
765
|
+
this.duplicateBatchDetector = new index_js_3.DuplicateBatchDetector();
|
|
766
|
+
}
|
|
744
767
|
if (context.attachState === container_definitions_1.AttachState.Attached) {
|
|
745
768
|
const maxSnapshotCacheDurationMs = this._storage?.policies?.maximumCacheDurationMs;
|
|
746
769
|
if (maxSnapshotCacheDurationMs !== undefined &&
|
|
@@ -1138,13 +1161,9 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
1138
1161
|
let childTree = snapshotTree;
|
|
1139
1162
|
for (const part of pathParts) {
|
|
1140
1163
|
if (hasIsolatedChannels) {
|
|
1141
|
-
|
|
1142
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
1143
|
-
childTree = childTree.trees[internal_5.channelsTreeName];
|
|
1164
|
+
childTree = childTree?.trees[internal_5.channelsTreeName];
|
|
1144
1165
|
}
|
|
1145
|
-
|
|
1146
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
1147
|
-
childTree = childTree.trees[part];
|
|
1166
|
+
childTree = childTree?.trees[part];
|
|
1148
1167
|
}
|
|
1149
1168
|
return childTree;
|
|
1150
1169
|
}
|
|
@@ -1191,8 +1210,6 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
1191
1210
|
return this.resolveHandle(requestParser.createSubRequest(1));
|
|
1192
1211
|
}
|
|
1193
1212
|
if (id === index_js_1.blobManagerBasePath && requestParser.isLeaf(2)) {
|
|
1194
|
-
// TODO why are we non null asserting here?
|
|
1195
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
1196
1213
|
const blob = await this.blobManager.getBlob(requestParser.pathParts[1]);
|
|
1197
1214
|
return blob
|
|
1198
1215
|
? {
|
|
@@ -1465,9 +1482,14 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
1465
1482
|
}
|
|
1466
1483
|
this._connected = connected;
|
|
1467
1484
|
if (!connected) {
|
|
1468
|
-
this.
|
|
1469
|
-
this.
|
|
1470
|
-
this.
|
|
1485
|
+
this._signalTracking.signalsLost = 0;
|
|
1486
|
+
this._signalTracking.signalsOutOfOrder = 0;
|
|
1487
|
+
this._signalTracking.signalTimestamp = 0;
|
|
1488
|
+
this._signalTracking.signalsSentSinceLastLatencyMeasurement = 0;
|
|
1489
|
+
this._signalTracking.totalSignalsSentInLatencyWindow = 0;
|
|
1490
|
+
this._signalTracking.roundTripSignalSequenceNumber = undefined;
|
|
1491
|
+
this._signalTracking.trackingSignalSequenceNumber = undefined;
|
|
1492
|
+
this._signalTracking.minimumTrackingSignalSequenceNumber = undefined;
|
|
1471
1493
|
}
|
|
1472
1494
|
else {
|
|
1473
1495
|
(0, internal_2.assert)(this.attachState === container_definitions_1.AttachState.Attached, 0x3cd /* Connection is possible only if container exists in storage */);
|
|
@@ -1514,158 +1536,159 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
1514
1536
|
if (hasModernRuntimeMessageEnvelope) {
|
|
1515
1537
|
// If the message has the modern message envelope, then process it here.
|
|
1516
1538
|
// Here we unpack the message (decompress, unchunk, and/or ungroup) into a batch of messages with ContainerMessageType
|
|
1517
|
-
const
|
|
1518
|
-
if (
|
|
1539
|
+
const inboundResult = this.remoteMessageProcessor.process(messageCopy, logLegacyCase);
|
|
1540
|
+
if (inboundResult === undefined) {
|
|
1519
1541
|
// This means the incoming message is an incomplete part of a message or batch
|
|
1520
1542
|
// and we need to process more messages before the rest of the system can understand it.
|
|
1521
1543
|
return;
|
|
1522
1544
|
}
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
const
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1545
|
+
if ("batchStart" in inboundResult) {
|
|
1546
|
+
const batchStart = inboundResult.batchStart;
|
|
1547
|
+
const result = this.duplicateBatchDetector?.processInboundBatch(batchStart);
|
|
1548
|
+
if (result?.duplicate) {
|
|
1549
|
+
const error = new internal_7.DataCorruptionError("Duplicate batch - The same batch was sequenced twice", { batchId: batchStart.batchId });
|
|
1550
|
+
this.mc.logger.sendTelemetryEvent({
|
|
1551
|
+
eventName: "DuplicateBatch",
|
|
1552
|
+
details: {
|
|
1553
|
+
batchId: batchStart.batchId,
|
|
1554
|
+
clientId: batchStart.clientId,
|
|
1555
|
+
batchStartCsn: batchStart.batchStartCsn,
|
|
1556
|
+
size: inboundResult.length,
|
|
1557
|
+
duplicateBatchSequenceNumber: result.otherSequenceNumber,
|
|
1558
|
+
...(0, internal_7.extractSafePropertiesFromMessage)(batchStart.keyMessage),
|
|
1559
|
+
},
|
|
1560
|
+
}, error);
|
|
1561
|
+
throw error;
|
|
1562
|
+
}
|
|
1536
1563
|
}
|
|
1537
|
-
|
|
1538
|
-
|
|
1564
|
+
let runtimeBatch = true;
|
|
1565
|
+
// Reach out to PendingStateManager, either to zip localOpMetadata into the *local* message list,
|
|
1566
|
+
// or to check to ensure the *remote* messages don't match the batchId of a pending local batch.
|
|
1567
|
+
// This latter case would indicate that the container has forked - two copies are trying to persist the same local changes.
|
|
1568
|
+
let messagesWithPendingState = this.pendingStateManager.processInboundMessages(inboundResult, local);
|
|
1569
|
+
if (inboundResult.type !== "fullBatch") {
|
|
1570
|
+
(0, internal_2.assert)(messagesWithPendingState.length === 1, 0xa3d /* Partial batch should have exactly one message */);
|
|
1571
|
+
}
|
|
1572
|
+
if (messagesWithPendingState.length === 0) {
|
|
1573
|
+
(0, internal_2.assert)(inboundResult.type === "fullBatch", 0xa3e /* Empty batch is always considered a full batch */);
|
|
1574
|
+
/**
|
|
1575
|
+
* We need to process an empty batch, which will execute expected actions while processing even if there
|
|
1576
|
+
* are no inner runtime messages.
|
|
1577
|
+
*
|
|
1578
|
+
* Empty batches are produced by the outbox on resubmit when the resubmit flow resulted in no runtime
|
|
1579
|
+
* messages.
|
|
1580
|
+
* This can happen if changes from a remote client "cancel out" the pending changes being resubmitted by
|
|
1581
|
+
* this client. We submit an empty batch if "offline load" (aka rehydrating from stashed state) is
|
|
1582
|
+
* enabled, to ensure we account for this batch when comparing batchIds, checking for a forked container.
|
|
1583
|
+
* Otherwise, we would not realize this container has forked in the case where it did fork, and a batch
|
|
1584
|
+
* became empty but wasn't submitted as such.
|
|
1585
|
+
*/
|
|
1586
|
+
messagesWithPendingState = [
|
|
1587
|
+
{
|
|
1588
|
+
message: inboundResult.batchStart.keyMessage,
|
|
1589
|
+
localOpMetadata: undefined,
|
|
1590
|
+
},
|
|
1591
|
+
];
|
|
1592
|
+
// Empty batch message is a non-runtime message as it was generated by the op grouping manager.
|
|
1593
|
+
runtimeBatch = false;
|
|
1539
1594
|
}
|
|
1595
|
+
const locationInBatch = inboundResult.type === "fullBatch"
|
|
1596
|
+
? { batchStart: true, batchEnd: true }
|
|
1597
|
+
: inboundResult.type === "batchStartingMessage"
|
|
1598
|
+
? { batchStart: true, batchEnd: false }
|
|
1599
|
+
: { batchStart: false, batchEnd: inboundResult.batchEnd === true };
|
|
1600
|
+
this.processInboundMessages(messagesWithPendingState, locationInBatch, local, savedOp, runtimeBatch);
|
|
1540
1601
|
}
|
|
1541
1602
|
else {
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
savedOp,
|
|
1551
|
-
}));
|
|
1552
|
-
}
|
|
1553
|
-
else {
|
|
1554
|
-
// A non container runtime message (like other system ops - join, ack, leave, nack etc.)
|
|
1555
|
-
this.ensureNoDataModelChanges(() => this.observeNonRuntimeMessage({
|
|
1556
|
-
message: messageCopy,
|
|
1557
|
-
local,
|
|
1558
|
-
isRuntimeMessage: false,
|
|
1559
|
-
savedOp,
|
|
1560
|
-
}));
|
|
1561
|
-
}
|
|
1603
|
+
this.processInboundMessages([{ message: messageCopy, localOpMetadata: undefined }], { batchStart: true, batchEnd: true }, // Single message
|
|
1604
|
+
local, savedOp, isRuntimeMessage(messageCopy) /* runtimeBatch */);
|
|
1605
|
+
}
|
|
1606
|
+
if (local) {
|
|
1607
|
+
// If we have processed a local op, this means that the container is
|
|
1608
|
+
// making progress and we can reset the counter for how many times
|
|
1609
|
+
// we have consecutively replayed the pending states
|
|
1610
|
+
this.resetReconnectCount();
|
|
1562
1611
|
}
|
|
1563
1612
|
}
|
|
1564
1613
|
/**
|
|
1565
|
-
* Processes
|
|
1566
|
-
*
|
|
1567
|
-
* @param
|
|
1614
|
+
* Processes inbound message(s). It calls schedule manager according to the messages' location in the batch.
|
|
1615
|
+
* @param messages - messages to process.
|
|
1616
|
+
* @param locationInBatch - Are we processing the start and/or end of a batch?
|
|
1617
|
+
* @param local - true if the messages were originally generated by the client receiving it.
|
|
1618
|
+
* @param savedOp - true if the message is a replayed saved op.
|
|
1619
|
+
* @param runtimeBatch - true if these are runtime messages.
|
|
1568
1620
|
*/
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
this.deltaManager.minimumSequenceNumber;
|
|
1577
|
-
}
|
|
1578
|
-
// Surround the actual processing of the operation with messages to the schedule manager indicating
|
|
1579
|
-
// the beginning and end. This allows it to emit appropriate events and/or pause the processing of new
|
|
1580
|
-
// messages once a batch has been fully processed.
|
|
1581
|
-
this.scheduleManager.beforeOpProcessing(message);
|
|
1582
|
-
this._processedClientSequenceNumber = message.clientSequenceNumber;
|
|
1621
|
+
processInboundMessages(messages, locationInBatch, local, savedOp, runtimeBatch) {
|
|
1622
|
+
if (locationInBatch.batchStart) {
|
|
1623
|
+
const firstMessage = messages[0]?.message;
|
|
1624
|
+
(0, internal_2.assert)(firstMessage !== undefined, 0xa31 /* Batch must have at least one message */);
|
|
1625
|
+
this.scheduleManager.batchBegin(firstMessage);
|
|
1626
|
+
}
|
|
1627
|
+
let error;
|
|
1583
1628
|
try {
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
}
|
|
1629
|
+
messages.forEach(({ message, localOpMetadata }) => {
|
|
1630
|
+
this.ensureNoDataModelChanges(() => {
|
|
1631
|
+
if (runtimeBatch) {
|
|
1632
|
+
this.validateAndProcessRuntimeMessage({
|
|
1633
|
+
message: message,
|
|
1634
|
+
local,
|
|
1635
|
+
savedOp,
|
|
1636
|
+
localOpMetadata,
|
|
1637
|
+
});
|
|
1638
|
+
}
|
|
1639
|
+
else {
|
|
1640
|
+
this.observeNonRuntimeMessage(message);
|
|
1641
|
+
}
|
|
1642
|
+
});
|
|
1643
|
+
});
|
|
1600
1644
|
}
|
|
1601
1645
|
catch (e) {
|
|
1602
|
-
|
|
1603
|
-
throw
|
|
1604
|
-
}
|
|
1605
|
-
}
|
|
1606
|
-
/**
|
|
1607
|
-
* Process an empty batch, which will execute expected actions while processing even if there are no messages.
|
|
1608
|
-
* This is a separate function because the processCore function expects at least one message to process.
|
|
1609
|
-
* It is expected to happen only when the outbox produces an empty batch due to a resubmit flow.
|
|
1610
|
-
*/
|
|
1611
|
-
processEmptyBatch(emptyBatch, local) {
|
|
1612
|
-
const { emptyBatchSequenceNumber: sequenceNumber, batchStartCsn } = emptyBatch;
|
|
1613
|
-
(0, internal_2.assert)(sequenceNumber !== undefined, 0x9fa /* emptyBatchSequenceNumber must be defined */);
|
|
1614
|
-
this.emit("batchBegin", { sequenceNumber });
|
|
1615
|
-
this._processedClientSequenceNumber = batchStartCsn;
|
|
1616
|
-
if (!this.hasPendingMessages()) {
|
|
1617
|
-
this.updateDocumentDirtyState(false);
|
|
1646
|
+
error = e;
|
|
1647
|
+
throw error;
|
|
1618
1648
|
}
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1649
|
+
finally {
|
|
1650
|
+
if (locationInBatch.batchEnd) {
|
|
1651
|
+
const lastMessage = messages[messages.length - 1]?.message;
|
|
1652
|
+
(0, internal_2.assert)(lastMessage !== undefined, 0xa32 /* Batch must have at least one message */);
|
|
1653
|
+
this.scheduleManager.batchEnd(error, lastMessage);
|
|
1654
|
+
}
|
|
1622
1655
|
}
|
|
1623
1656
|
}
|
|
1624
1657
|
/**
|
|
1625
1658
|
* Observes messages that are not intended for the runtime layer, updating/notifying Runtime systems as needed.
|
|
1626
|
-
* @param
|
|
1659
|
+
* @param message - non-runtime message to process.
|
|
1627
1660
|
*/
|
|
1628
|
-
observeNonRuntimeMessage(
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
if (this.deltaManager.minimumSequenceNumber <
|
|
1633
|
-
messageWithContext.message.minimumSequenceNumber) {
|
|
1634
|
-
messageWithContext.message.minimumSequenceNumber =
|
|
1635
|
-
this.deltaManager.minimumSequenceNumber;
|
|
1636
|
-
}
|
|
1637
|
-
// Surround the actual processing of the operation with messages to the schedule manager indicating
|
|
1638
|
-
// the beginning and end. This allows it to emit appropriate events and/or pause the processing of new
|
|
1639
|
-
// messages once a batch has been fully processed.
|
|
1640
|
-
this.scheduleManager.beforeOpProcessing(message);
|
|
1641
|
-
this._processedClientSequenceNumber = message.clientSequenceNumber;
|
|
1642
|
-
try {
|
|
1643
|
-
// If there are no more pending messages after processing a local message,
|
|
1644
|
-
// the document is no longer dirty.
|
|
1645
|
-
if (!this.hasPendingMessages()) {
|
|
1646
|
-
this.updateDocumentDirtyState(false);
|
|
1647
|
-
}
|
|
1648
|
-
this.emit("op", message, messageWithContext.isRuntimeMessage);
|
|
1649
|
-
this.scheduleManager.afterOpProcessing(undefined, message);
|
|
1650
|
-
if (local) {
|
|
1651
|
-
// If we have processed a local op, this means that the container is
|
|
1652
|
-
// making progress and we can reset the counter for how many times
|
|
1653
|
-
// we have consecutively replayed the pending states
|
|
1654
|
-
this.resetReconnectCount();
|
|
1655
|
-
}
|
|
1661
|
+
observeNonRuntimeMessage(message) {
|
|
1662
|
+
// Set the minimum sequence number to the containerRuntime's understanding of minimum sequence number.
|
|
1663
|
+
if (this.deltaManager.minimumSequenceNumber < message.minimumSequenceNumber) {
|
|
1664
|
+
message.minimumSequenceNumber = this.deltaManager.minimumSequenceNumber;
|
|
1656
1665
|
}
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1666
|
+
this._processedClientSequenceNumber = message.clientSequenceNumber;
|
|
1667
|
+
// If there are no more pending messages after processing a local message,
|
|
1668
|
+
// the document is no longer dirty.
|
|
1669
|
+
if (!this.hasPendingMessages()) {
|
|
1670
|
+
this.updateDocumentDirtyState(false);
|
|
1660
1671
|
}
|
|
1672
|
+
this.emit("op", message, false /* runtimeMessage */);
|
|
1661
1673
|
}
|
|
1662
1674
|
/**
|
|
1663
1675
|
* Assuming the given message is also a TypedContainerRuntimeMessage,
|
|
1664
1676
|
* checks its type and dispatches the message to the appropriate handler in the runtime.
|
|
1665
1677
|
* Throws a DataProcessingError if the message looks like but doesn't conform to a known TypedContainerRuntimeMessage type.
|
|
1666
1678
|
*/
|
|
1667
|
-
validateAndProcessRuntimeMessage(messageWithContext
|
|
1668
|
-
const { local, message, savedOp } = messageWithContext;
|
|
1679
|
+
validateAndProcessRuntimeMessage(messageWithContext) {
|
|
1680
|
+
const { local, message, savedOp, localOpMetadata } = messageWithContext;
|
|
1681
|
+
// Set the minimum sequence number to the containerRuntime's understanding of minimum sequence number.
|
|
1682
|
+
if (this.useDeltaManagerOpsProxy &&
|
|
1683
|
+
this.deltaManager.minimumSequenceNumber < message.minimumSequenceNumber) {
|
|
1684
|
+
message.minimumSequenceNumber = this.deltaManager.minimumSequenceNumber;
|
|
1685
|
+
}
|
|
1686
|
+
this._processedClientSequenceNumber = message.clientSequenceNumber;
|
|
1687
|
+
// If there are no more pending messages after processing a local message,
|
|
1688
|
+
// the document is no longer dirty.
|
|
1689
|
+
if (!this.hasPendingMessages()) {
|
|
1690
|
+
this.updateDocumentDirtyState(false);
|
|
1691
|
+
}
|
|
1669
1692
|
switch (message.type) {
|
|
1670
1693
|
case messageTypes_js_1.ContainerMessageType.Attach:
|
|
1671
1694
|
case messageTypes_js_1.ContainerMessageType.Alias:
|
|
@@ -1726,20 +1749,25 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
1726
1749
|
}
|
|
1727
1750
|
}
|
|
1728
1751
|
}
|
|
1752
|
+
this.emit("op", message, true /* runtimeMessage */);
|
|
1729
1753
|
}
|
|
1730
1754
|
/**
|
|
1731
1755
|
* Emits the Signal event and update the perf signal data.
|
|
1732
|
-
* @param clientSignalSequenceNumber - is the client signal sequence number to be uploaded.
|
|
1733
1756
|
*/
|
|
1734
|
-
sendSignalTelemetryEvent(
|
|
1735
|
-
const duration = Date.now() - this.
|
|
1757
|
+
sendSignalTelemetryEvent() {
|
|
1758
|
+
const duration = Date.now() - this._signalTracking.signalTimestamp;
|
|
1736
1759
|
this.mc.logger.sendPerformanceEvent({
|
|
1737
1760
|
eventName: "SignalLatency",
|
|
1738
|
-
duration,
|
|
1739
|
-
|
|
1761
|
+
duration, // Roundtrip duration of the tracked signal in milliseconds.
|
|
1762
|
+
signalsSent: this._signalTracking.totalSignalsSentInLatencyWindow, // Signals sent since the last logged SignalLatency event.
|
|
1763
|
+
signalsLost: this._signalTracking.signalsLost, // Signals lost since the last logged SignalLatency event.
|
|
1764
|
+
outOfOrderSignals: this._signalTracking.signalsOutOfOrder, // Out of order signals since the last logged SignalLatency event.
|
|
1765
|
+
reconnectCount: this.consecutiveReconnects, // Container reconnect count.
|
|
1740
1766
|
});
|
|
1741
|
-
this.
|
|
1742
|
-
this.
|
|
1767
|
+
this._signalTracking.signalsLost = 0;
|
|
1768
|
+
this._signalTracking.signalsOutOfOrder = 0;
|
|
1769
|
+
this._signalTracking.signalTimestamp = 0;
|
|
1770
|
+
this._signalTracking.totalSignalsSentInLatencyWindow = 0;
|
|
1743
1771
|
}
|
|
1744
1772
|
processSignal(message, local) {
|
|
1745
1773
|
const envelope = message.content;
|
|
@@ -1747,29 +1775,55 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
1747
1775
|
clientId: message.clientId,
|
|
1748
1776
|
content: envelope.contents.content,
|
|
1749
1777
|
type: envelope.contents.type,
|
|
1778
|
+
targetClientId: message.targetClientId,
|
|
1750
1779
|
};
|
|
1751
|
-
// Only collect signal telemetry for messages sent by the current client.
|
|
1752
|
-
if (message.clientId === this.clientId &&
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
this.
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1780
|
+
// Only collect signal telemetry for broadcast messages sent by the current client.
|
|
1781
|
+
if (message.clientId === this.clientId &&
|
|
1782
|
+
this.connected &&
|
|
1783
|
+
envelope.clientBroadcastSignalSequenceNumber !== undefined) {
|
|
1784
|
+
if (this._signalTracking.trackingSignalSequenceNumber !== undefined &&
|
|
1785
|
+
this._signalTracking.minimumTrackingSignalSequenceNumber !== undefined) {
|
|
1786
|
+
if (envelope.clientBroadcastSignalSequenceNumber >=
|
|
1787
|
+
this._signalTracking.trackingSignalSequenceNumber) {
|
|
1788
|
+
// Calculate the number of signals lost and log the event.
|
|
1789
|
+
const signalsLost = envelope.clientBroadcastSignalSequenceNumber -
|
|
1790
|
+
this._signalTracking.trackingSignalSequenceNumber;
|
|
1791
|
+
if (signalsLost > 0) {
|
|
1792
|
+
this._signalTracking.signalsLost += signalsLost;
|
|
1793
|
+
this.mc.logger.sendErrorEvent({
|
|
1794
|
+
eventName: "SignalLost",
|
|
1795
|
+
signalsLost, // Number of lost signals detected.
|
|
1796
|
+
trackingSequenceNumber: this._signalTracking.trackingSignalSequenceNumber, // The next expected signal sequence number.
|
|
1797
|
+
clientBroadcastSignalSequenceNumber: envelope.clientBroadcastSignalSequenceNumber, // Actual signal sequence number received.
|
|
1798
|
+
});
|
|
1799
|
+
}
|
|
1800
|
+
// Update the tracking signal sequence number to the next expected signal in the sequence.
|
|
1801
|
+
this._signalTracking.trackingSignalSequenceNumber =
|
|
1802
|
+
envelope.clientBroadcastSignalSequenceNumber + 1;
|
|
1803
|
+
}
|
|
1804
|
+
else if (envelope.clientBroadcastSignalSequenceNumber >=
|
|
1805
|
+
this._signalTracking.minimumTrackingSignalSequenceNumber) {
|
|
1806
|
+
this._signalTracking.signalsOutOfOrder++;
|
|
1807
|
+
this.mc.logger.sendTelemetryEvent({
|
|
1808
|
+
eventName: "SignalOutOfOrder",
|
|
1809
|
+
type: envelope.contents.type, // Type of signal that was received out of order.
|
|
1810
|
+
trackingSequenceNumber: this._signalTracking.trackingSignalSequenceNumber, // The next expected signal sequence number.
|
|
1811
|
+
clientBroadcastSignalSequenceNumber: envelope.clientBroadcastSignalSequenceNumber, // Sequence number of the out of order signal.
|
|
1812
|
+
});
|
|
1813
|
+
}
|
|
1814
|
+
if (this._signalTracking.roundTripSignalSequenceNumber !== undefined &&
|
|
1815
|
+
envelope.clientBroadcastSignalSequenceNumber >=
|
|
1816
|
+
this._signalTracking.roundTripSignalSequenceNumber) {
|
|
1817
|
+
if (envelope.clientBroadcastSignalSequenceNumber ===
|
|
1818
|
+
this._signalTracking.roundTripSignalSequenceNumber) {
|
|
1819
|
+
// Latency tracked signal has been received.
|
|
1820
|
+
// We now log the roundtrip duration of the tracked signal.
|
|
1821
|
+
// This telemetry event also logs metrics for signals sent, signals lost, and out of order signals received.
|
|
1822
|
+
// These metrics are reset after logging the telemetry event.
|
|
1823
|
+
this.sendSignalTelemetryEvent();
|
|
1824
|
+
}
|
|
1825
|
+
this._signalTracking.roundTripSignalSequenceNumber = undefined;
|
|
1771
1826
|
}
|
|
1772
|
-
this._perfSignalData.trackingSignalSequenceNumber = undefined;
|
|
1773
1827
|
}
|
|
1774
1828
|
}
|
|
1775
1829
|
if (envelope.address === undefined) {
|
|
@@ -1945,18 +1999,36 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
1945
1999
|
}
|
|
1946
2000
|
return true;
|
|
1947
2001
|
}
|
|
1948
|
-
createNewSignalEnvelope(address, type, content) {
|
|
1949
|
-
const newSequenceNumber = ++this._perfSignalData.signalSequenceNumber;
|
|
2002
|
+
createNewSignalEnvelope(address, type, content, targetClientId) {
|
|
1950
2003
|
const newEnvelope = {
|
|
1951
2004
|
address,
|
|
1952
|
-
clientSignalSequenceNumber: newSequenceNumber,
|
|
1953
2005
|
contents: { type, content },
|
|
1954
2006
|
};
|
|
1955
|
-
|
|
1956
|
-
if (
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
2007
|
+
const isBroadcastSignal = targetClientId === undefined;
|
|
2008
|
+
if (isBroadcastSignal) {
|
|
2009
|
+
const clientBroadcastSignalSequenceNumber = ++this._signalTracking
|
|
2010
|
+
.broadcastSignalSequenceNumber;
|
|
2011
|
+
newEnvelope.clientBroadcastSignalSequenceNumber = clientBroadcastSignalSequenceNumber;
|
|
2012
|
+
this._signalTracking.signalsSentSinceLastLatencyMeasurement++;
|
|
2013
|
+
if (this._signalTracking.minimumTrackingSignalSequenceNumber === undefined ||
|
|
2014
|
+
this._signalTracking.trackingSignalSequenceNumber === undefined) {
|
|
2015
|
+
// Signal monitoring window is undefined
|
|
2016
|
+
// Initialize tracking to expect the next signal sent by the connected client.
|
|
2017
|
+
this._signalTracking.minimumTrackingSignalSequenceNumber =
|
|
2018
|
+
clientBroadcastSignalSequenceNumber;
|
|
2019
|
+
this._signalTracking.trackingSignalSequenceNumber =
|
|
2020
|
+
clientBroadcastSignalSequenceNumber;
|
|
2021
|
+
}
|
|
2022
|
+
// We should not track the round trip of a new signal in the case we are already tracking one.
|
|
2023
|
+
if (clientBroadcastSignalSequenceNumber % this.defaultTelemetrySignalSampleCount === 1 &&
|
|
2024
|
+
this._signalTracking.roundTripSignalSequenceNumber === undefined) {
|
|
2025
|
+
this._signalTracking.signalTimestamp = Date.now();
|
|
2026
|
+
this._signalTracking.roundTripSignalSequenceNumber =
|
|
2027
|
+
clientBroadcastSignalSequenceNumber;
|
|
2028
|
+
this._signalTracking.totalSignalsSentInLatencyWindow +=
|
|
2029
|
+
this._signalTracking.signalsSentSinceLastLatencyMeasurement;
|
|
2030
|
+
this._signalTracking.signalsSentSinceLastLatencyMeasurement = 0;
|
|
2031
|
+
}
|
|
1960
2032
|
}
|
|
1961
2033
|
return newEnvelope;
|
|
1962
2034
|
}
|
|
@@ -1965,10 +2037,16 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
1965
2037
|
* @param type - Type of the signal.
|
|
1966
2038
|
* @param content - Content of the signal. Should be a JSON serializable object or primitive.
|
|
1967
2039
|
* @param targetClientId - When specified, the signal is only sent to the provided client id.
|
|
2040
|
+
*
|
|
2041
|
+
* @remarks
|
|
2042
|
+
*
|
|
2043
|
+
* The `targetClientId` parameter here is currently intended for internal testing purposes only.
|
|
2044
|
+
* Support for this option at container runtime is planned to be deprecated in the future.
|
|
2045
|
+
*
|
|
1968
2046
|
*/
|
|
1969
2047
|
submitSignal(type, content, targetClientId) {
|
|
1970
2048
|
this.verifyNotClosed();
|
|
1971
|
-
const envelope = this.createNewSignalEnvelope(undefined /* address */, type, content);
|
|
2049
|
+
const envelope = this.createNewSignalEnvelope(undefined /* address */, type, content, targetClientId);
|
|
1972
2050
|
return this.submitSignalFn(envelope, targetClientId);
|
|
1973
2051
|
}
|
|
1974
2052
|
setAttachState(attachState) {
|
|
@@ -2371,8 +2449,6 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
2371
2449
|
// Counting dataStores and handles
|
|
2372
2450
|
// Because handles are unchanged dataStores in the current logic,
|
|
2373
2451
|
// summarized dataStore count is total dataStore count minus handle count
|
|
2374
|
-
// TODO why are we non null asserting here
|
|
2375
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
2376
2452
|
const dataStoreTree = summaryTree.tree[internal_5.channelsTreeName];
|
|
2377
2453
|
(0, internal_2.assert)(dataStoreTree.type === driver_definitions_1.SummaryType.Tree, 0x1fc /* "summary is not a tree" */);
|
|
2378
2454
|
const handleCount = Object.values(dataStoreTree.tree).filter((value) => value.type === driver_definitions_1.SummaryType.Handle).length;
|
|
@@ -2695,6 +2771,12 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
2695
2771
|
throw new Error("Runtime is closed");
|
|
2696
2772
|
}
|
|
2697
2773
|
}
|
|
2774
|
+
/**
|
|
2775
|
+
* Resubmits each message in the batch, and then flushes the outbox.
|
|
2776
|
+
*
|
|
2777
|
+
* @remarks - If the "Offline Load" feature is enabled, the batchId is included in the resubmitted messages,
|
|
2778
|
+
* for correlation to detect container forking.
|
|
2779
|
+
*/
|
|
2698
2780
|
reSubmitBatch(batch, batchId) {
|
|
2699
2781
|
this.orderSequentially(() => {
|
|
2700
2782
|
for (const message of batch) {
|