Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[CP][1.4] Fix MRP baseTimeout calculation for LIT (#37696) #38062

Merged
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/app/CommandSender.cpp
Original file line number Diff line number Diff line change
@@ -125,7 +125,8 @@ CHIP_ERROR CommandSender::SendCommandRequestInternal(const SessionHandle & sessi
mExchangeCtx.Grab(exchange);
VerifyOrReturnError(!mExchangeCtx->IsGroupExchangeContext(), CHIP_ERROR_INVALID_MESSAGE_TYPE);

mExchangeCtx->SetResponseTimeout(timeout.ValueOr(session->ComputeRoundTripTimeout(app::kExpectedIMProcessingTime)));
mExchangeCtx->SetResponseTimeout(
timeout.ValueOr(session->ComputeRoundTripTimeout(app::kExpectedIMProcessingTime, true /*isFirstMessageOnExchange*/)));

if (mTimedInvokeTimeoutMs.HasValue())
{
11 changes: 7 additions & 4 deletions src/app/ReadClient.cpp
Original file line number Diff line number Diff line change
@@ -987,12 +987,15 @@ CHIP_ERROR ReadClient::ComputeLivenessCheckTimerTimeout(System::Clock::Timeout *
// So recompute the round-trip timeout directly. Assume MRP, since in practice that is likely what is happening.
auto & peerMRPConfig = mReadPrepareParams.mSessionHolder->GetRemoteMRPConfig();
// Peer will assume we are idle (hence we pass kZero to GetMessageReceiptTimeout()), but will assume we treat it as active
// for the response, so to match the retransmission timeout computation for the message back to the peeer, we should treat
// it as active.
auto roundTripTimeout = mReadPrepareParams.mSessionHolder->GetMessageReceiptTimeout(System::Clock::kZero) +
// for the response, so to match the retransmission timeout computation for the message back to the peer, we should treat
// it as active and handling non-initial message, isFirstMessageOnExchange needs to be set as false for
// GetRetransmissionTimeout.
auto roundTripTimeout =
mReadPrepareParams.mSessionHolder->GetMessageReceiptTimeout(System::Clock::kZero, true /*isFirstMessageOnExchange*/) +
kExpectedIMProcessingTime +
GetRetransmissionTimeout(peerMRPConfig.mActiveRetransTimeout, peerMRPConfig.mIdleRetransTimeout,
System::SystemClock().GetMonotonicTimestamp(), peerMRPConfig.mActiveThresholdTime);
System::SystemClock().GetMonotonicTimestamp(), peerMRPConfig.mActiveThresholdTime,
false /*isFirstMessageOnExchange*/);
*aTimeout = System::Clock::Seconds16(mMaxInterval) + roundTripTimeout;
return CHIP_NO_ERROR;
}
5 changes: 3 additions & 2 deletions src/app/TimedHandler.cpp
Original file line number Diff line number Diff line change
@@ -127,8 +127,9 @@ CHIP_ERROR TimedHandler::HandleTimedRequestAction(Messaging::ExchangeContext * a
// will send nothing and the other side will have to time out to realize
// it's missed its window).
auto delay = System::Clock::Milliseconds32(timeoutMs);
aExchangeContext->SetResponseTimeout(
std::max(delay, aExchangeContext->GetSessionHandle()->ComputeRoundTripTimeout(app::kExpectedIMProcessingTime)));
aExchangeContext->SetResponseTimeout(std::max(delay,
aExchangeContext->GetSessionHandle()->ComputeRoundTripTimeout(
app::kExpectedIMProcessingTime, false /*isFirstMessageOnExchange*/)));
ReturnErrorOnFailure(StatusResponse::Send(Status::Success, aExchangeContext, /* aExpectResponse = */ true));

// Now just wait for the client.
2 changes: 1 addition & 1 deletion src/controller/AutoCommissioner.cpp
Original file line number Diff line number Diff line change
@@ -649,7 +649,7 @@ Optional<System::Clock::Timeout> AutoCommissioner::GetCommandTimeout(DeviceProxy
auto sessionHandle = device->GetSecureSession();
if (sessionHandle.HasValue())
{
timeout = sessionHandle.Value()->ComputeRoundTripTimeout(timeout);
timeout = sessionHandle.Value()->ComputeRoundTripTimeout(timeout, true /*isFirstMessageOnExchange*/);
}

// Enforce the spec minimal timeout. Maybe this enforcement should live in
2 changes: 1 addition & 1 deletion src/controller/python/chip/utils/DeviceProxyUtils.cpp
Original file line number Diff line number Diff line change
@@ -71,7 +71,7 @@ uint32_t pychip_DeviceProxy_ComputeRoundTripTimeout(DeviceProxy * device, uint32

return deviceProxy->GetSecureSession()
.Value()
->ComputeRoundTripTimeout(System::Clock::Milliseconds32(upperLayerProcessingTimeoutMs))
->ComputeRoundTripTimeout(System::Clock::Milliseconds32(upperLayerProcessingTimeoutMs), true /*isFirstMessageOnExchange*/)
.count();
}

7 changes: 4 additions & 3 deletions src/controller/tests/TestWriteChunking.cpp
Original file line number Diff line number Diff line change
@@ -566,9 +566,10 @@ void TestWriteChunking::RunTest(Instructions instructions)
err = writeClient->SendWriteRequest(sessionHandle);
EXPECT_EQ(err, CHIP_NO_ERROR);

GetIOContext().DriveIOUntil(sessionHandle->ComputeRoundTripTimeout(app::kExpectedIMProcessingTime) +
System::Clock::Seconds16(1),
[&]() { return GetExchangeManager().GetNumActiveExchanges() == 0; });
GetIOContext().DriveIOUntil(
sessionHandle->ComputeRoundTripTimeout(app::kExpectedIMProcessingTime, true /*isFirstMessageOnExchange*/) +
System::Clock::Seconds16(1),
[&]() { return GetExchangeManager().GetNumActiveExchanges() == 0; });

EXPECT_EQ(onGoingPath, app::ConcreteAttributePath());
EXPECT_EQ(status.size(), instructions.expectedStatus.size());
8 changes: 4 additions & 4 deletions src/controller/tests/data_model/TestRead.cpp
Original file line number Diff line number Diff line change
@@ -4783,10 +4783,10 @@ System::Clock::Timeout TestRead::ComputeSubscriptionTimeout(System::Clock::Secon
// Add 1000ms of slack to our max interval to make sure we hit the
// subscription liveness timer. 100ms was tried in the past and is not
// sufficient: our process can easily lose the timeslice for 100ms.
const auto & ourMrpConfig = GetDefaultMRPConfig();
auto publisherTransmissionTimeout =
GetRetransmissionTimeout(ourMrpConfig.mActiveRetransTimeout, ourMrpConfig.mIdleRetransTimeout,
System::SystemClock().GetMonotonicTimestamp(), ourMrpConfig.mActiveThresholdTime);
const auto & ourMrpConfig = GetDefaultMRPConfig();
auto publisherTransmissionTimeout = GetRetransmissionTimeout(
ourMrpConfig.mActiveRetransTimeout, ourMrpConfig.mIdleRetransTimeout, System::SystemClock().GetMonotonicTimestamp(),
ourMrpConfig.mActiveThresholdTime, true /* isFirstMessageOnExchange */);

return publisherTransmissionTimeout + aMaxInterval + System::Clock::Milliseconds32(1000);
}
2 changes: 1 addition & 1 deletion src/darwin/Framework/CHIP/MTRBaseDevice.mm
Original file line number Diff line number Diff line change
@@ -1475,7 +1475,7 @@ - (void)_invokeCommandWithEndpointID:(NSNumber *)endpointID
// Clamp to a number of seconds that will not overflow 32-bit
// int when converted to ms.
auto serverTimeoutInSeconds = System::Clock::Seconds16(serverSideProcessingTimeout.unsignedShortValue);
invokeTimeout.SetValue(session->ComputeRoundTripTimeout(serverTimeoutInSeconds));
invokeTimeout.SetValue(session->ComputeRoundTripTimeout(serverTimeoutInSeconds, true /*isFirstMessageOnExchange*/));
}
ReturnErrorOnFailure(commandSender->SendCommandRequest(session, invokeTimeout));

2 changes: 1 addition & 1 deletion src/messaging/ExchangeContext.cpp
Original file line number Diff line number Diff line change
@@ -77,7 +77,7 @@ void ExchangeContext::SetResponseExpected(bool inResponseExpected)

void ExchangeContext::UseSuggestedResponseTimeout(Timeout applicationProcessingTimeout)
{
SetResponseTimeout(mSession->ComputeRoundTripTimeout(applicationProcessingTimeout));
SetResponseTimeout(mSession->ComputeRoundTripTimeout(applicationProcessingTimeout, !HasReceivedAtLeastOneMessage()));
}

void ExchangeContext::SetResponseTimeout(Timeout timeout)
12 changes: 10 additions & 2 deletions src/messaging/ReliableMessageProtocolConfig.cpp
Original file line number Diff line number Diff line change
@@ -126,7 +126,8 @@ Optional<ReliableMessageProtocolConfig> GetLocalMRPConfig()
}

System::Clock::Timeout GetRetransmissionTimeout(System::Clock::Timeout activeInterval, System::Clock::Timeout idleInterval,
System::Clock::Timeout lastActivityTime, System::Clock::Timeout activityThreshold)
System::Clock::Timeout lastActivityTime, System::Clock::Timeout activityThreshold,
bool isFirstMessageOnExchange)
{
auto timeSinceLastActivity = (System::SystemClock().GetMonotonicTimestamp() - lastActivityTime);

@@ -135,7 +136,14 @@ System::Clock::Timeout GetRetransmissionTimeout(System::Clock::Timeout activeInt
System::Clock::Timestamp timeout(0);
for (uint8_t i = 0; i < CHIP_CONFIG_RMP_DEFAULT_MAX_RETRANS + 1; i++)
{
auto baseInterval = ((timeSinceLastActivity + timeout) < activityThreshold) ? activeInterval : idleInterval;
auto baseInterval = activeInterval;
// If we are calculating the timeout for the initial message, we never know whether the peer is active or not, choose
// active/idle interval from PeerActiveMode of session per 4.11.2.1. Retransmissions.
// If we are calculating the timeout for response message, we know the peer is active, always choose active interval.
if (isFirstMessageOnExchange)
{
baseInterval = ((timeSinceLastActivity + timeout) < activityThreshold) ? activeInterval : idleInterval;
}
timeout += Messaging::ReliableMessageMgr::GetBackoff(baseInterval, i, /* computeMaxPossible */ true);
}

5 changes: 3 additions & 2 deletions src/messaging/ReliableMessageProtocolConfig.h
Original file line number Diff line number Diff line change
@@ -257,11 +257,12 @@ Optional<ReliableMessageProtocolConfig> GetLocalMRPConfig();
* @param[in] idleInterval The idle interval to use for the backoff calculation.
* @param[in] lastActivityTime The last time some activity has been recorded.
* @param[in] activityThreshold The activity threshold for a node to be considered active.
*
* @param[in] isFirstMessageOnExchange Indicates whether this is for the initial message on an exchange.
* @return The maximum transmission time
*/
System::Clock::Timeout GetRetransmissionTimeout(System::Clock::Timeout activeInterval, System::Clock::Timeout idleInterval,
System::Clock::Timeout lastActivityTime, System::Clock::Timeout activityThreshold);
System::Clock::Timeout lastActivityTime, System::Clock::Timeout activityThreshold,
bool isFirstMessageOnExchange);

#if CONFIG_BUILD_FOR_HOST_UNIT_TEST

23 changes: 13 additions & 10 deletions src/protocols/secure_channel/CASESession.cpp
Original file line number Diff line number Diff line change
@@ -2418,34 +2418,37 @@ CHIP_ERROR CASESession::OnMessageReceived(ExchangeContext * ec, const PayloadHea

namespace {
System::Clock::Timeout ComputeRoundTripTimeout(ExchangeContext::Timeout serverProcessingTime,
const ReliableMessageProtocolConfig & remoteMrpConfig)
const ReliableMessageProtocolConfig & remoteMrpConfig, bool isFirstMessageOnExchange)
{
// TODO: This is duplicating logic from Session::ComputeRoundTripTimeout. Unfortunately, it's called by
// consumers who do not have a session.
const auto & maybeLocalMRPConfig = GetLocalMRPConfig();
const auto & defaultMRRPConfig = GetDefaultMRPConfig();
const auto & localMRPConfig = maybeLocalMRPConfig.ValueOr(defaultMRRPConfig);
return GetRetransmissionTimeout(remoteMrpConfig.mActiveRetransTimeout, remoteMrpConfig.mIdleRetransTimeout,
// Assume peer is idle, as a worst-case assumption (probably true for
// Sigma1, since that will be our initial message on the session, but less
// so for Sigma2).
System::Clock::kZero, remoteMrpConfig.mActiveThresholdTime) +
// The activity time only matters when isFirstMessageOnExchange is false,
// which only happens for Sigma1. In that case, assume peer is idle,
// as a worst-case assumption, and pass System::Clock::kZero.
System::Clock::kZero, remoteMrpConfig.mActiveThresholdTime, isFirstMessageOnExchange) +
serverProcessingTime +
GetRetransmissionTimeout(localMRPConfig.mActiveRetransTimeout, localMRPConfig.mIdleRetransTimeout,
// Peer will assume we are active, since it's
// responding to our message.
System::SystemClock().GetMonotonicTimestamp(), localMRPConfig.mActiveThresholdTime);
// Peer will be responding to our message, so isFirstMessageOnExchange should be false
// and the timestamp does not matter.
System::SystemClock().GetMonotonicTimestamp(), localMRPConfig.mActiveThresholdTime,
false /*isFirstMessageOnExchange*/);
}
} // anonymous namespace

System::Clock::Timeout CASESession::ComputeSigma1ResponseTimeout(const ReliableMessageProtocolConfig & remoteMrpConfig)
{
return ComputeRoundTripTimeout(kExpectedSigma1ProcessingTime, remoteMrpConfig);
// Sigma1 is the first message on the exchange.
return ComputeRoundTripTimeout(kExpectedSigma1ProcessingTime, remoteMrpConfig, true /*isFirstMessageOnExchange*/);
}

System::Clock::Timeout CASESession::ComputeSigma2ResponseTimeout(const ReliableMessageProtocolConfig & remoteMrpConfig)
{
return ComputeRoundTripTimeout(kExpectedHighProcessingTime, remoteMrpConfig);
// Sigma2 is never the first message on the exchange.
return ComputeRoundTripTimeout(kExpectedHighProcessingTime, remoteMrpConfig, false /*isFirstMessageOnExchange*/);
}

bool CASESession::InvokeBackgroundWorkWatchdog()
10 changes: 6 additions & 4 deletions src/transport/GroupSession.h
Original file line number Diff line number Diff line change
@@ -69,13 +69,14 @@ class IncomingGroupSession : public Session, public ReferenceCounted<IncomingGro

System::Clock::Timestamp GetMRPBaseTimeout() const override { return System::Clock::kZero; }

System::Clock::Milliseconds32 GetAckTimeout() const override
System::Clock::Milliseconds32 GetAckTimeout(bool isFirstMessageOnExchange) const override
{
VerifyOrDie(false);
return System::Clock::Timeout();
}

System::Clock::Milliseconds32 GetMessageReceiptTimeout(System::Clock::Timestamp ourLastActivity) const override
System::Clock::Milliseconds32 GetMessageReceiptTimeout(System::Clock::Timestamp ourLastActivity,
bool isFirstMessageOnExchange) const override
{
// There are no timeouts for group sessions.
VerifyOrDie(false);
@@ -128,13 +129,14 @@ class OutgoingGroupSession : public Session, public ReferenceCounted<OutgoingGro

System::Clock::Timestamp GetMRPBaseTimeout() const override { return System::Clock::kZero; }

System::Clock::Milliseconds32 GetAckTimeout() const override
System::Clock::Milliseconds32 GetAckTimeout(bool isFirstMessageOnExchange) const override
{
VerifyOrDie(false);
return System::Clock::Timeout();
}

System::Clock::Milliseconds32 GetMessageReceiptTimeout(System::Clock::Timestamp ourLastActivity) const override
System::Clock::Milliseconds32 GetMessageReceiptTimeout(System::Clock::Timestamp ourLastActivity,
bool isFirstMessageOnExchange) const override
{
// There are no timeouts for group sessions.
VerifyOrDie(false);
10 changes: 6 additions & 4 deletions src/transport/SecureSession.h
Original file line number Diff line number Diff line change
@@ -162,14 +162,15 @@ class SecureSession : public Session, public ReferenceCounted<SecureSession, Sec

bool AllowsLargePayload() const override { return GetPeerAddress().GetTransportType() == Transport::Type::kTcp; }

System::Clock::Milliseconds32 GetAckTimeout() const override
System::Clock::Milliseconds32 GetAckTimeout(bool isFirstMessageOnExchange) const override
{
switch (mPeerAddress.GetTransportType())
{
case Transport::Type::kUdp: {
const ReliableMessageProtocolConfig & remoteMRPConfig = mRemoteSessionParams.GetMRPConfig();
return GetRetransmissionTimeout(remoteMRPConfig.mActiveRetransTimeout, remoteMRPConfig.mIdleRetransTimeout,
GetLastPeerActivityTime(), remoteMRPConfig.mActiveThresholdTime);
GetLastPeerActivityTime(), remoteMRPConfig.mActiveThresholdTime,
isFirstMessageOnExchange);
}
case Transport::Type::kTcp:
return System::Clock::Seconds16(30);
@@ -181,7 +182,8 @@ class SecureSession : public Session, public ReferenceCounted<SecureSession, Sec
return System::Clock::Timeout();
}

System::Clock::Milliseconds32 GetMessageReceiptTimeout(System::Clock::Timestamp ourLastActivity) const override
System::Clock::Milliseconds32 GetMessageReceiptTimeout(System::Clock::Timestamp ourLastActivity,
bool isFirstMessageOnExchange) const override
{
switch (mPeerAddress.GetTransportType())
{
@@ -190,7 +192,7 @@ class SecureSession : public Session, public ReferenceCounted<SecureSession, Sec
const auto & defaultMRRPConfig = GetDefaultMRPConfig();
const auto & localMRPConfig = maybeLocalMRPConfig.ValueOr(defaultMRRPConfig);
return GetRetransmissionTimeout(localMRPConfig.mActiveRetransTimeout, localMRPConfig.mIdleRetransTimeout,
ourLastActivity, localMRPConfig.mActiveThresholdTime);
ourLastActivity, localMRPConfig.mActiveThresholdTime, isFirstMessageOnExchange);
}
case Transport::Type::kTcp:
return System::Clock::Seconds16(30);
10 changes: 6 additions & 4 deletions src/transport/Session.cpp
Original file line number Diff line number Diff line change
@@ -64,16 +64,18 @@ const OutgoingGroupSession * Session::AsConstOutgoingGroupSession() const
return static_cast<const OutgoingGroupSession *>(this);
}

System::Clock::Timeout Session::ComputeRoundTripTimeout(System::Clock::Timeout upperlayerProcessingTimeout)
System::Clock::Timeout Session::ComputeRoundTripTimeout(System::Clock::Timeout upperlayerProcessingTimeout,
bool isFirstMessageOnExchange)
{
if (IsGroupSession())
{
return System::Clock::kZero;
}

// Treat us as active for purposes of GetMessageReceiptTimeout(), since the
// other side would be responding to our message.
return GetAckTimeout() + upperlayerProcessingTimeout + GetMessageReceiptTimeout(System::SystemClock().GetMonotonicTimestamp());
// Treat us as active for purposes of GetMessageReceiptTimeout(), pass false into GetMessageReceiptTimeout to
// indicate we are processing non-initial message since the other side would be responding to our message.
return GetAckTimeout(isFirstMessageOnExchange) + upperlayerProcessingTimeout +
GetMessageReceiptTimeout(System::SystemClock().GetMonotonicTimestamp(), false /*isFirstMessageOnExchange*/);
}

uint16_t Session::SessionIdForLogging() const
12 changes: 9 additions & 3 deletions src/transport/Session.h
Original file line number Diff line number Diff line change
@@ -254,7 +254,9 @@ class Session
// GetAckTimeout is the estimate for how long it could take for the other
// side to receive our message (accounting for our MRP retransmits if it
// gets lost) and send a response.
virtual System::Clock::Milliseconds32 GetAckTimeout() const = 0;
//
// The MRP retransmit delay computation depends on whether this is the first message on the exchange.
virtual System::Clock::Milliseconds32 GetAckTimeout(bool isFirstMessageOnExchange) const = 0;

// GetReceiptTimeout is the estimate for how long it could take for us to
// receive a message after the other side sends it, accounting for the MRP
@@ -265,15 +267,19 @@ class Session
// System::SystemClock().GetMonotonicTimestamp() (to indicate "peer is
// responding to a message it just received") and System::Clock::kZero (to
// indicate "peer is reaching out to us, not in response to anything").
virtual System::Clock::Milliseconds32 GetMessageReceiptTimeout(System::Clock::Timestamp ourLastActivity) const = 0;
// isFirstMessageOnExchange indicates whether the caller is handling the first message on exchange
virtual System::Clock::Milliseconds32 GetMessageReceiptTimeout(System::Clock::Timestamp ourLastActivity,
bool isFirstMessageOnExchange) const = 0;

const ReliableMessageProtocolConfig & GetRemoteMRPConfig() const { return GetRemoteSessionParameters().GetMRPConfig(); }

// Returns a suggested timeout value based on the round-trip time it takes for the peer at the other end of the session to
// receive a message, process it and send it back. This is computed based on the session type, the type of transport, sleepy
// characteristics of the target and a caller-provided value for the time it takes to process a message at the upper layer on
// the target For group sessions, this function will always return 0.
System::Clock::Timeout ComputeRoundTripTimeout(System::Clock::Timeout upperlayerProcessingTimeout);
// isFirstMessageOnExchange parameter indicates whether this ComputeRoundTripTimeout() call is for an initial message or not.
System::Clock::Timeout ComputeRoundTripTimeout(System::Clock::Timeout upperlayerProcessingTimeout,
bool isFirstMessageOnExchange);

FabricIndex GetFabricIndex() const { return mFabricIndex; }

10 changes: 6 additions & 4 deletions src/transport/UnauthenticatedSessionTable.h
Original file line number Diff line number Diff line change
@@ -88,14 +88,15 @@ class UnauthenticatedSession : public Session, public ReferenceCounted<Unauthent

bool AllowsLargePayload() const override { return GetPeerAddress().GetTransportType() == Transport::Type::kTcp; }

System::Clock::Milliseconds32 GetAckTimeout() const override
System::Clock::Milliseconds32 GetAckTimeout(bool isFirstMessageOnExchange) const override
{
switch (mPeerAddress.GetTransportType())
{
case Transport::Type::kUdp: {
const ReliableMessageProtocolConfig & remoteMRPConfig = mRemoteSessionParams.GetMRPConfig();
return GetRetransmissionTimeout(remoteMRPConfig.mActiveRetransTimeout, remoteMRPConfig.mIdleRetransTimeout,
GetLastPeerActivityTime(), remoteMRPConfig.mActiveThresholdTime);
GetLastPeerActivityTime(), remoteMRPConfig.mActiveThresholdTime,
isFirstMessageOnExchange);
}
case Transport::Type::kTcp:
return System::Clock::Seconds16(30);
@@ -107,7 +108,8 @@ class UnauthenticatedSession : public Session, public ReferenceCounted<Unauthent
return System::Clock::Timeout();
}

System::Clock::Milliseconds32 GetMessageReceiptTimeout(System::Clock::Timestamp ourLastActivity) const override
System::Clock::Milliseconds32 GetMessageReceiptTimeout(System::Clock::Timestamp ourLastActivity,
bool isFirstMessageOnExchange) const override
{
switch (mPeerAddress.GetTransportType())
{
@@ -116,7 +118,7 @@ class UnauthenticatedSession : public Session, public ReferenceCounted<Unauthent
const auto & defaultMRRPConfig = GetDefaultMRPConfig();
const auto & localMRPConfig = maybeLocalMRPConfig.ValueOr(defaultMRRPConfig);
return GetRetransmissionTimeout(localMRPConfig.mActiveRetransTimeout, localMRPConfig.mIdleRetransTimeout,
ourLastActivity, localMRPConfig.mActiveThresholdTime);
ourLastActivity, localMRPConfig.mActiveThresholdTime, isFirstMessageOnExchange);
}
case Transport::Type::kTcp:
return System::Clock::Seconds16(30);