@fluidframework/container-runtime 2.4.0 → 2.5.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 +18 -0
- package/api-report/container-runtime.legacy.alpha.api.md +3 -1
- package/container-runtime.test-files.tar +0 -0
- package/dist/blobManager/blobManager.d.ts +3 -3
- package/dist/blobManager/blobManager.d.ts.map +1 -1
- package/dist/blobManager/blobManager.js +1 -1
- package/dist/blobManager/blobManager.js.map +1 -1
- package/dist/channelCollection.d.ts +20 -5
- package/dist/channelCollection.d.ts.map +1 -1
- package/dist/channelCollection.js +183 -119
- package/dist/channelCollection.js.map +1 -1
- package/dist/containerRuntime.d.ts +12 -4
- package/dist/containerRuntime.d.ts.map +1 -1
- package/dist/containerRuntime.js +155 -66
- package/dist/containerRuntime.js.map +1 -1
- package/dist/dataStoreContext.d.ts +15 -3
- package/dist/dataStoreContext.d.ts.map +1 -1
- package/dist/dataStoreContext.js +48 -19
- package/dist/dataStoreContext.js.map +1 -1
- package/dist/gc/garbageCollection.d.ts +5 -6
- package/dist/gc/garbageCollection.d.ts.map +1 -1
- package/dist/gc/garbageCollection.js +23 -22
- package/dist/gc/garbageCollection.js.map +1 -1
- package/dist/gc/gcDefinitions.d.ts +2 -2
- package/dist/gc/gcDefinitions.d.ts.map +1 -1
- package/dist/gc/gcDefinitions.js.map +1 -1
- package/dist/opLifecycle/outbox.d.ts +3 -0
- package/dist/opLifecycle/outbox.d.ts.map +1 -1
- package/dist/opLifecycle/outbox.js +9 -0
- package/dist/opLifecycle/outbox.js.map +1 -1
- package/dist/opLifecycle/remoteMessageProcessor.d.ts +1 -0
- package/dist/opLifecycle/remoteMessageProcessor.d.ts.map +1 -1
- package/dist/opLifecycle/remoteMessageProcessor.js +2 -0
- package/dist/opLifecycle/remoteMessageProcessor.js.map +1 -1
- package/dist/opProperties.js +1 -1
- package/dist/opProperties.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/summary/documentSchema.d.ts +11 -0
- package/dist/summary/documentSchema.d.ts.map +1 -1
- package/dist/summary/documentSchema.js +45 -30
- package/dist/summary/documentSchema.js.map +1 -1
- package/lib/blobManager/blobManager.d.ts +3 -3
- package/lib/blobManager/blobManager.d.ts.map +1 -1
- package/lib/blobManager/blobManager.js +1 -1
- package/lib/blobManager/blobManager.js.map +1 -1
- package/lib/channelCollection.d.ts +20 -5
- package/lib/channelCollection.d.ts.map +1 -1
- package/lib/channelCollection.js +183 -119
- package/lib/channelCollection.js.map +1 -1
- package/lib/containerRuntime.d.ts +12 -4
- package/lib/containerRuntime.d.ts.map +1 -1
- package/lib/containerRuntime.js +155 -66
- package/lib/containerRuntime.js.map +1 -1
- package/lib/dataStoreContext.d.ts +15 -3
- package/lib/dataStoreContext.d.ts.map +1 -1
- package/lib/dataStoreContext.js +48 -19
- package/lib/dataStoreContext.js.map +1 -1
- package/lib/gc/garbageCollection.d.ts +5 -6
- package/lib/gc/garbageCollection.d.ts.map +1 -1
- package/lib/gc/garbageCollection.js +23 -22
- package/lib/gc/garbageCollection.js.map +1 -1
- package/lib/gc/gcDefinitions.d.ts +2 -2
- package/lib/gc/gcDefinitions.d.ts.map +1 -1
- package/lib/gc/gcDefinitions.js.map +1 -1
- package/lib/opLifecycle/outbox.d.ts +3 -0
- package/lib/opLifecycle/outbox.d.ts.map +1 -1
- package/lib/opLifecycle/outbox.js +9 -0
- package/lib/opLifecycle/outbox.js.map +1 -1
- package/lib/opLifecycle/remoteMessageProcessor.d.ts +1 -0
- package/lib/opLifecycle/remoteMessageProcessor.d.ts.map +1 -1
- package/lib/opLifecycle/remoteMessageProcessor.js +2 -0
- package/lib/opLifecycle/remoteMessageProcessor.js.map +1 -1
- package/lib/opProperties.js +1 -1
- package/lib/opProperties.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/summary/documentSchema.d.ts +11 -0
- package/lib/summary/documentSchema.d.ts.map +1 -1
- package/lib/summary/documentSchema.js +45 -30
- package/lib/summary/documentSchema.js.map +1 -1
- package/package.json +24 -24
- package/src/blobManager/blobManager.ts +2 -2
- package/src/channelCollection.ts +227 -160
- package/src/containerRuntime.ts +197 -80
- package/src/dataStoreContext.ts +66 -23
- package/src/gc/garbageCollection.ts +32 -32
- package/src/gc/gcDefinitions.ts +3 -3
- package/src/opLifecycle/outbox.ts +12 -0
- package/src/opLifecycle/remoteMessageProcessor.ts +3 -0
- package/src/opProperties.ts +1 -1
- package/src/packageVersion.ts +1 -1
- package/src/summary/documentSchema.ts +58 -39
package/lib/containerRuntime.js
CHANGED
|
@@ -1587,7 +1587,9 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1587
1587
|
: inboundResult.type === "batchStartingMessage"
|
|
1588
1588
|
? { batchStart: true, batchEnd: false }
|
|
1589
1589
|
: { batchStart: false, batchEnd: inboundResult.batchEnd === true };
|
|
1590
|
-
this.processInboundMessages(messagesWithPendingState, locationInBatch, local, savedOp, runtimeBatch
|
|
1590
|
+
this.processInboundMessages(messagesWithPendingState, locationInBatch, local, savedOp, runtimeBatch, inboundResult.type === "fullBatch"
|
|
1591
|
+
? inboundResult.groupedBatch
|
|
1592
|
+
: false /* groupedBatch */);
|
|
1591
1593
|
}
|
|
1592
1594
|
else {
|
|
1593
1595
|
if (!runtimeBatch) {
|
|
@@ -1600,7 +1602,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1600
1602
|
}
|
|
1601
1603
|
}
|
|
1602
1604
|
this.processInboundMessages([{ message: messageCopy, localOpMetadata: undefined }], { batchStart: true, batchEnd: true }, // Single message
|
|
1603
|
-
local, savedOp, runtimeBatch);
|
|
1605
|
+
local, savedOp, runtimeBatch, false /* groupedBatch */);
|
|
1604
1606
|
}
|
|
1605
1607
|
if (local) {
|
|
1606
1608
|
// If we have processed a local op, this means that the container is
|
|
@@ -1611,35 +1613,93 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1611
1613
|
}
|
|
1612
1614
|
/**
|
|
1613
1615
|
* Processes inbound message(s). It calls schedule manager according to the messages' location in the batch.
|
|
1614
|
-
* @param
|
|
1616
|
+
* @param messagesWithMetadata - messages to process along with their metadata.
|
|
1615
1617
|
* @param locationInBatch - Are we processing the start and/or end of a batch?
|
|
1616
1618
|
* @param local - true if the messages were originally generated by the client receiving it.
|
|
1617
1619
|
* @param savedOp - true if the message is a replayed saved op.
|
|
1618
1620
|
* @param runtimeBatch - true if these are runtime messages.
|
|
1621
|
+
* @param groupedBatch - true if these messages are part of a grouped op batch.
|
|
1619
1622
|
*/
|
|
1620
|
-
processInboundMessages(
|
|
1623
|
+
processInboundMessages(messagesWithMetadata, locationInBatch, local, savedOp, runtimeBatch, groupedBatch) {
|
|
1621
1624
|
if (locationInBatch.batchStart) {
|
|
1622
|
-
const firstMessage =
|
|
1625
|
+
const firstMessage = messagesWithMetadata[0]?.message;
|
|
1623
1626
|
assert(firstMessage !== undefined, 0xa31 /* Batch must have at least one message */);
|
|
1624
1627
|
this.scheduleManager.batchBegin(firstMessage);
|
|
1625
1628
|
}
|
|
1626
1629
|
let error;
|
|
1627
1630
|
try {
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
this.validateAndProcessRuntimeMessage({
|
|
1632
|
-
message: message,
|
|
1633
|
-
local,
|
|
1634
|
-
savedOp,
|
|
1635
|
-
localOpMetadata,
|
|
1636
|
-
});
|
|
1637
|
-
}
|
|
1638
|
-
else {
|
|
1631
|
+
if (!runtimeBatch) {
|
|
1632
|
+
messagesWithMetadata.forEach(({ message }) => {
|
|
1633
|
+
this.ensureNoDataModelChanges(() => {
|
|
1639
1634
|
this.observeNonRuntimeMessage(message);
|
|
1640
|
-
}
|
|
1635
|
+
});
|
|
1641
1636
|
});
|
|
1642
|
-
|
|
1637
|
+
return;
|
|
1638
|
+
}
|
|
1639
|
+
// Helper that updates a message's minimum sequence number to the minimum sequence number that container
|
|
1640
|
+
// runtime is tracking and sets _processedClientSequenceNumber. It returns the updated message.
|
|
1641
|
+
const updateSequenceNumbers = (message) => {
|
|
1642
|
+
// Set the minimum sequence number to the containerRuntime's understanding of minimum sequence number.
|
|
1643
|
+
message.minimumSequenceNumber =
|
|
1644
|
+
this.useDeltaManagerOpsProxy &&
|
|
1645
|
+
this.deltaManager.minimumSequenceNumber < message.minimumSequenceNumber
|
|
1646
|
+
? this.deltaManager.minimumSequenceNumber
|
|
1647
|
+
: message.minimumSequenceNumber;
|
|
1648
|
+
this._processedClientSequenceNumber = message.clientSequenceNumber;
|
|
1649
|
+
return message;
|
|
1650
|
+
};
|
|
1651
|
+
// Non-grouped batch messages are processed one at a time.
|
|
1652
|
+
if (!groupedBatch) {
|
|
1653
|
+
for (const { message, localOpMetadata } of messagesWithMetadata) {
|
|
1654
|
+
updateSequenceNumbers(message);
|
|
1655
|
+
this.ensureNoDataModelChanges(() => {
|
|
1656
|
+
this.validateAndProcessRuntimeMessages(message, [
|
|
1657
|
+
{
|
|
1658
|
+
contents: message.contents,
|
|
1659
|
+
localOpMetadata,
|
|
1660
|
+
clientSequenceNumber: message.clientSequenceNumber,
|
|
1661
|
+
},
|
|
1662
|
+
], local, savedOp);
|
|
1663
|
+
this.emit("op", message, true /* runtimeMessage */);
|
|
1664
|
+
});
|
|
1665
|
+
}
|
|
1666
|
+
return;
|
|
1667
|
+
}
|
|
1668
|
+
let bunchedMessagesContent = [];
|
|
1669
|
+
let previousMessage;
|
|
1670
|
+
// Helper that processes the previous bunch of messages.
|
|
1671
|
+
const sendBunchedMessages = () => {
|
|
1672
|
+
assert(previousMessage !== undefined, 0xa67 /* previous message must exist */);
|
|
1673
|
+
this.ensureNoDataModelChanges(() => {
|
|
1674
|
+
this.validateAndProcessRuntimeMessages(
|
|
1675
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
1676
|
+
previousMessage, bunchedMessagesContent, local, savedOp);
|
|
1677
|
+
});
|
|
1678
|
+
bunchedMessagesContent = [];
|
|
1679
|
+
};
|
|
1680
|
+
/**
|
|
1681
|
+
* For grouped batch messages, bunch contiguous messages of the same type and process them together.
|
|
1682
|
+
* This is an optimization mainly for DDSes, where it can process a bunch of ops together. DDSes
|
|
1683
|
+
* like merge tree or shared tree can process ops more efficiently when they are bunched together.
|
|
1684
|
+
*/
|
|
1685
|
+
for (const { message, localOpMetadata } of messagesWithMetadata) {
|
|
1686
|
+
const currentMessage = updateSequenceNumbers(message);
|
|
1687
|
+
if (previousMessage && previousMessage.type !== currentMessage.type) {
|
|
1688
|
+
sendBunchedMessages();
|
|
1689
|
+
}
|
|
1690
|
+
previousMessage = currentMessage;
|
|
1691
|
+
bunchedMessagesContent.push({
|
|
1692
|
+
contents: message.contents,
|
|
1693
|
+
localOpMetadata,
|
|
1694
|
+
clientSequenceNumber: message.clientSequenceNumber,
|
|
1695
|
+
});
|
|
1696
|
+
}
|
|
1697
|
+
// Process the last bunch of messages.
|
|
1698
|
+
sendBunchedMessages();
|
|
1699
|
+
// Send the "op" events for the messages now that the ops have been processed.
|
|
1700
|
+
for (const { message } of messagesWithMetadata) {
|
|
1701
|
+
this.emit("op", message, true /* runtimeMessage */);
|
|
1702
|
+
}
|
|
1643
1703
|
}
|
|
1644
1704
|
catch (e) {
|
|
1645
1705
|
error = e;
|
|
@@ -1647,7 +1707,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1647
1707
|
}
|
|
1648
1708
|
finally {
|
|
1649
1709
|
if (locationInBatch.batchEnd) {
|
|
1650
|
-
const lastMessage =
|
|
1710
|
+
const lastMessage = messagesWithMetadata[messagesWithMetadata.length - 1]?.message;
|
|
1651
1711
|
assert(lastMessage !== undefined, 0xa32 /* Batch must have at least one message */);
|
|
1652
1712
|
this.scheduleManager.batchEnd(error, lastMessage);
|
|
1653
1713
|
}
|
|
@@ -1671,62 +1731,50 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1671
1731
|
this.emit("op", message, false /* runtimeMessage */);
|
|
1672
1732
|
}
|
|
1673
1733
|
/**
|
|
1674
|
-
*
|
|
1675
|
-
*
|
|
1734
|
+
* Process runtime messages. The messages here are contiguous messages in a batch.
|
|
1735
|
+
* Assuming the messages in the given bunch are also a TypedContainerRuntimeMessage, checks its type and dispatch
|
|
1736
|
+
* the messages to the appropriate handler in the runtime.
|
|
1676
1737
|
* Throws a DataProcessingError if the message looks like but doesn't conform to a known TypedContainerRuntimeMessage type.
|
|
1738
|
+
* @param message - The core message with common properties for all the messages.
|
|
1739
|
+
* @param messageContents - The contents, local metadata and clientSequenceNumbers of the messages.
|
|
1740
|
+
* @param local - true if the messages were originally generated by the client receiving it.
|
|
1741
|
+
* @param savedOp - true if the message is a replayed saved op.
|
|
1742
|
+
*
|
|
1677
1743
|
*/
|
|
1678
|
-
|
|
1679
|
-
const { local, message, savedOp, localOpMetadata } = messageWithContext;
|
|
1680
|
-
// Set the minimum sequence number to the containerRuntime's understanding of minimum sequence number.
|
|
1681
|
-
if (this.useDeltaManagerOpsProxy &&
|
|
1682
|
-
this.deltaManager.minimumSequenceNumber < message.minimumSequenceNumber) {
|
|
1683
|
-
message.minimumSequenceNumber = this.deltaManager.minimumSequenceNumber;
|
|
1684
|
-
}
|
|
1685
|
-
this._processedClientSequenceNumber = message.clientSequenceNumber;
|
|
1744
|
+
validateAndProcessRuntimeMessages(message, messagesContent, local, savedOp) {
|
|
1686
1745
|
// If there are no more pending messages after processing a local message,
|
|
1687
1746
|
// the document is no longer dirty.
|
|
1688
1747
|
if (!this.hasPendingMessages()) {
|
|
1689
1748
|
this.updateDocumentDirtyState(false);
|
|
1690
1749
|
}
|
|
1750
|
+
// Get the contents without the localOpMetadata because not all message types know about localOpMetadata.
|
|
1751
|
+
const contents = messagesContent.map((c) => c.contents);
|
|
1691
1752
|
switch (message.type) {
|
|
1753
|
+
case ContainerMessageType.FluidDataStoreOp:
|
|
1692
1754
|
case ContainerMessageType.Attach:
|
|
1693
1755
|
case ContainerMessageType.Alias:
|
|
1694
|
-
|
|
1695
|
-
|
|
1756
|
+
// Remove the metadata from the message before sending it to the channel collection. The metadata
|
|
1757
|
+
// is added by the container runtime and is not part of the message that the channel collection and
|
|
1758
|
+
// layers below it expect.
|
|
1759
|
+
this.channelCollection.processMessages({ envelope: message, messagesContent, local });
|
|
1696
1760
|
break;
|
|
1697
1761
|
case ContainerMessageType.BlobAttach:
|
|
1698
|
-
this.blobManager.
|
|
1762
|
+
this.blobManager.processBlobAttachMessage(message, local);
|
|
1699
1763
|
break;
|
|
1700
1764
|
case ContainerMessageType.IdAllocation:
|
|
1701
|
-
|
|
1702
|
-
// stashed ops flow. The compressor is stashed with these ops already processed.
|
|
1703
|
-
// That said, in idCompressorMode === "delayed", we might not serialize ID compressor, and
|
|
1704
|
-
// thus we need to process all the ops.
|
|
1705
|
-
if (!(this.skipSavedCompressorOps && savedOp === true)) {
|
|
1706
|
-
const range = message.contents;
|
|
1707
|
-
// Some other client turned on the id compressor. If we have not turned it on,
|
|
1708
|
-
// put it in a pending queue and delay finalization.
|
|
1709
|
-
if (this._idCompressor === undefined) {
|
|
1710
|
-
assert(this.idCompressorMode !== undefined, 0x93c /* id compressor should be enabled */);
|
|
1711
|
-
this.pendingIdCompressorOps.push(range);
|
|
1712
|
-
}
|
|
1713
|
-
else {
|
|
1714
|
-
assert(this.pendingIdCompressorOps.length === 0, 0x979 /* there should be no pending ops! */);
|
|
1715
|
-
this._idCompressor.finalizeCreationRange(range);
|
|
1716
|
-
}
|
|
1717
|
-
}
|
|
1765
|
+
this.processIdCompressorMessages(contents, savedOp);
|
|
1718
1766
|
break;
|
|
1719
1767
|
case ContainerMessageType.GC:
|
|
1720
|
-
this.garbageCollector.
|
|
1768
|
+
this.garbageCollector.processMessages(contents, message.timestamp, local);
|
|
1721
1769
|
break;
|
|
1722
1770
|
case ContainerMessageType.ChunkedOp:
|
|
1723
|
-
// From observability POV, we should not
|
|
1771
|
+
// From observability POV, we should not expose the rest of the system (including "op" events on object) to these messages.
|
|
1724
1772
|
// Also resetReconnectCount() would be wrong - see comment that was there before this change was made.
|
|
1725
1773
|
assert(false, 0x93d /* should not even get here */);
|
|
1726
1774
|
case ContainerMessageType.Rejoin:
|
|
1727
1775
|
break;
|
|
1728
1776
|
case ContainerMessageType.DocumentSchemaChange:
|
|
1729
|
-
this.documentsSchemaController.
|
|
1777
|
+
this.documentsSchemaController.processDocumentSchemaMessages(contents, local, message.sequenceNumber);
|
|
1730
1778
|
break;
|
|
1731
1779
|
default: {
|
|
1732
1780
|
const error = getUnknownMessageTypeError(message.type, "validateAndProcessRuntimeMessage" /* codePath */, message);
|
|
@@ -1734,7 +1782,26 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1734
1782
|
throw error;
|
|
1735
1783
|
}
|
|
1736
1784
|
}
|
|
1737
|
-
|
|
1785
|
+
}
|
|
1786
|
+
processIdCompressorMessages(messageContents, savedOp) {
|
|
1787
|
+
for (const range of messageContents) {
|
|
1788
|
+
// Don't re-finalize the range if we're processing a "savedOp" in
|
|
1789
|
+
// stashed ops flow. The compressor is stashed with these ops already processed.
|
|
1790
|
+
// That said, in idCompressorMode === "delayed", we might not serialize ID compressor, and
|
|
1791
|
+
// thus we need to process all the ops.
|
|
1792
|
+
if (!(this.skipSavedCompressorOps && savedOp === true)) {
|
|
1793
|
+
// Some other client turned on the id compressor. If we have not turned it on,
|
|
1794
|
+
// put it in a pending queue and delay finalization.
|
|
1795
|
+
if (this._idCompressor === undefined) {
|
|
1796
|
+
assert(this.idCompressorMode !== undefined, 0x93c /* id compressor should be enabled */);
|
|
1797
|
+
this.pendingIdCompressorOps.push(range);
|
|
1798
|
+
}
|
|
1799
|
+
else {
|
|
1800
|
+
assert(this.pendingIdCompressorOps.length === 0, 0x979 /* there should be no pending ops! */);
|
|
1801
|
+
this._idCompressor.finalizeCreationRange(range);
|
|
1802
|
+
}
|
|
1803
|
+
}
|
|
1804
|
+
}
|
|
1738
1805
|
}
|
|
1739
1806
|
/**
|
|
1740
1807
|
* Emits the Signal event and update the perf signal data.
|
|
@@ -1743,11 +1810,13 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1743
1810
|
const duration = Date.now() - this._signalTracking.signalTimestamp;
|
|
1744
1811
|
this.mc.logger.sendPerformanceEvent({
|
|
1745
1812
|
eventName: "SignalLatency",
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1813
|
+
details: {
|
|
1814
|
+
duration, // Roundtrip duration of the tracked signal in milliseconds.
|
|
1815
|
+
sent: this._signalTracking.totalSignalsSentInLatencyWindow, // Signals sent since the last logged SignalLatency event.
|
|
1816
|
+
lost: this._signalTracking.signalsLost, // Signals lost since the last logged SignalLatency event.
|
|
1817
|
+
outOfOrder: this._signalTracking.signalsOutOfOrder, // Out of order signals since the last logged SignalLatency event.
|
|
1818
|
+
reconnectCount: this.consecutiveReconnects, // Container reconnect count.
|
|
1819
|
+
},
|
|
1751
1820
|
});
|
|
1752
1821
|
this._signalTracking.signalsLost = 0;
|
|
1753
1822
|
this._signalTracking.signalsOutOfOrder = 0;
|
|
@@ -1774,9 +1843,11 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1774
1843
|
this._signalTracking.signalsLost += signalsLost;
|
|
1775
1844
|
this.mc.logger.sendErrorEvent({
|
|
1776
1845
|
eventName: "SignalLost",
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1846
|
+
details: {
|
|
1847
|
+
signalsLost, // Number of lost signals detected.
|
|
1848
|
+
expectedSequenceNumber: this._signalTracking.trackingSignalSequenceNumber, // The next expected signal sequence number.
|
|
1849
|
+
clientBroadcastSignalSequenceNumber, // Actual signal sequence number received.
|
|
1850
|
+
},
|
|
1780
1851
|
});
|
|
1781
1852
|
}
|
|
1782
1853
|
// Update the tracking signal sequence number to the next expected signal in the sequence.
|
|
@@ -1788,11 +1859,18 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1788
1859
|
clientBroadcastSignalSequenceNumber >=
|
|
1789
1860
|
this._signalTracking.minimumTrackingSignalSequenceNumber) {
|
|
1790
1861
|
this._signalTracking.signalsOutOfOrder++;
|
|
1862
|
+
const details = {
|
|
1863
|
+
expectedSequenceNumber: this._signalTracking.trackingSignalSequenceNumber, // The next expected signal sequence number.
|
|
1864
|
+
clientBroadcastSignalSequenceNumber, // Sequence number of the out of order signal.
|
|
1865
|
+
};
|
|
1866
|
+
// Only log `contents.type` when address is for container to avoid
|
|
1867
|
+
// chance that contents type is customer data.
|
|
1868
|
+
if (envelope.address === undefined) {
|
|
1869
|
+
details.contentsType = envelope.contents.type; // Type of signal that was received out of order.
|
|
1870
|
+
}
|
|
1791
1871
|
this.mc.logger.sendTelemetryEvent({
|
|
1792
1872
|
eventName: "SignalOutOfOrder",
|
|
1793
|
-
|
|
1794
|
-
trackingSequenceNumber: this._signalTracking.trackingSignalSequenceNumber, // The next expected signal sequence number.
|
|
1795
|
-
clientBroadcastSignalSequenceNumber, // Sequence number of the out of order signal.
|
|
1873
|
+
details,
|
|
1796
1874
|
});
|
|
1797
1875
|
}
|
|
1798
1876
|
if (this._signalTracking.roundTripSignalSequenceNumber !== undefined &&
|
|
@@ -1801,7 +1879,8 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1801
1879
|
this._signalTracking.roundTripSignalSequenceNumber) {
|
|
1802
1880
|
// Latency tracked signal has been received.
|
|
1803
1881
|
// We now log the roundtrip duration of the tracked signal.
|
|
1804
|
-
// This telemetry event also logs metrics for
|
|
1882
|
+
// This telemetry event also logs metrics for broadcast signals
|
|
1883
|
+
// sent, lost, and out of order.
|
|
1805
1884
|
// These metrics are reset after logging the telemetry event.
|
|
1806
1885
|
this.sendSignalTelemetryEvent();
|
|
1807
1886
|
}
|
|
@@ -2298,7 +2377,17 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
2298
2377
|
},
|
|
2299
2378
|
},
|
|
2300
2379
|
});
|
|
2301
|
-
assert
|
|
2380
|
+
// legacy: assert 0x3d1
|
|
2381
|
+
if (!this.outbox.isEmpty) {
|
|
2382
|
+
throw DataProcessingError.create("Can't trigger summary in the middle of a batch", "submitSummary", undefined, {
|
|
2383
|
+
summaryNumber,
|
|
2384
|
+
pendingMessages: this.pendingMessagesCount,
|
|
2385
|
+
outboxLength: this.outbox.messageCount,
|
|
2386
|
+
mainBatchLength: this.outbox.mainBatchMessageCount,
|
|
2387
|
+
blobAttachBatchLength: this.outbox.blobAttachBatchMessageCount,
|
|
2388
|
+
idAllocationBatchLength: this.outbox.idAllocationBatchMessageCount,
|
|
2389
|
+
});
|
|
2390
|
+
}
|
|
2302
2391
|
// If the container is dirty, i.e., there are pending unacked ops, the summary will not be eventual consistent
|
|
2303
2392
|
// and it may even be incorrect. So, wait for the container to be saved with a timeout. If the container is not
|
|
2304
2393
|
// saved within the timeout, check if it should be failed or can continue.
|