blob: fbf1343f58bcbf441df83ac83560e95c7348f548 [file] [log] [blame]
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <memory>
#include <sstream>
#include <utility>
#include "absl/strings/str_cat.h"
#include "absl/types/optional.h"
#include "quic/core/congestion_control/bbr2_misc.h"
#include "quic/core/congestion_control/bbr2_sender.h"
#include "quic/core/congestion_control/bbr_sender.h"
#include "quic/core/congestion_control/tcp_cubic_sender_bytes.h"
#include "quic/core/quic_bandwidth.h"
#include "quic/core/quic_packet_number.h"
#include "quic/core/quic_types.h"
#include "quic/platform/api/quic_flags.h"
#include "quic/platform/api/quic_logging.h"
#include "quic/platform/api/quic_test.h"
#include "quic/test_tools/quic_config_peer.h"
#include "quic/test_tools/quic_connection_peer.h"
#include "quic/test_tools/quic_sent_packet_manager_peer.h"
#include "quic/test_tools/quic_test_utils.h"
#include "quic/test_tools/send_algorithm_test_result.pb.h"
#include "quic/test_tools/send_algorithm_test_utils.h"
#include "quic/test_tools/simulator/link.h"
#include "quic/test_tools/simulator/quic_endpoint.h"
#include "quic/test_tools/simulator/simulator.h"
#include "quic/test_tools/simulator/switch.h"
#include "quic/test_tools/simulator/traffic_policer.h"
using testing::AllOf;
using testing::Ge;
using testing::Le;
DEFINE_QUIC_COMMAND_LINE_FLAG(
std::string,
quic_bbr2_test_regression_mode,
"",
"One of a) 'record' to record test result (one file per test), or "
"b) 'regress' to regress against recorded results, or "
"c) <anything else> for non-regression mode.");
namespace quic {
using CyclePhase = Bbr2ProbeBwMode::CyclePhase;
namespace test {
// Use the initial CWND of 10, as 32 is too much for the test network.
const uint32_t kDefaultInitialCwndPackets = 10;
const uint32_t kDefaultInitialCwndBytes =
kDefaultInitialCwndPackets * kDefaultTCPMSS;
struct LinkParams {
LinkParams(int64_t kilo_bits_per_sec, int64_t delay_us)
: bandwidth(QuicBandwidth::FromKBitsPerSecond(kilo_bits_per_sec)),
delay(QuicTime::Delta::FromMicroseconds(delay_us)) {}
QuicBandwidth bandwidth;
QuicTime::Delta delay;
};
struct TrafficPolicerParams {
std::string name = "policer";
QuicByteCount initial_burst_size;
QuicByteCount max_bucket_size;
QuicBandwidth target_bandwidth = QuicBandwidth::Zero();
};
// All Bbr2DefaultTopologyTests uses the default network topology:
//
// Sender
// |
// | <-- local_link
// |
// Network switch
// * <-- the bottleneck queue in the direction
// | of the receiver
// |
// | <-- test_link
// |
// |
// Receiver
class DefaultTopologyParams {
public:
LinkParams local_link = {10000, 2000};
LinkParams test_link = {4000, 30000};
const simulator::SwitchPortNumber switch_port_count = 2;
// Network switch queue capacity, in number of BDPs.
float switch_queue_capacity_in_bdp = 2;
absl::optional<TrafficPolicerParams> sender_policer_params;
QuicBandwidth BottleneckBandwidth() const {
return std::min(local_link.bandwidth, test_link.bandwidth);
}
// Round trip time of a single full size packet.
QuicTime::Delta RTT() const {
return 2 * (local_link.delay + test_link.delay +
local_link.bandwidth.TransferTime(kMaxOutgoingPacketSize) +
test_link.bandwidth.TransferTime(kMaxOutgoingPacketSize));
}
QuicByteCount BDP() const { return BottleneckBandwidth() * RTT(); }
QuicByteCount SwitchQueueCapacity() const {
return switch_queue_capacity_in_bdp * BDP();
}
std::string ToString() const {
std::ostringstream os;
os << "{ BottleneckBandwidth: " << BottleneckBandwidth()
<< " RTT: " << RTT() << " BDP: " << BDP()
<< " BottleneckQueueSize: " << SwitchQueueCapacity() << "}";
return os.str();
}
};
class Bbr2SimulatorTest : public QuicTest {
protected:
Bbr2SimulatorTest() : simulator_(&random_) {
// Prevent the server(receiver), which only sends acks, from closing
// connection due to too many outstanding packets.
SetQuicFlag(FLAGS_quic_max_tracked_packet_count, 1000000);
}
void SetUp() override {
if (GetQuicFlag(FLAGS_quic_bbr2_test_regression_mode) == "regress") {
SendAlgorithmTestResult expected;
ASSERT_TRUE(LoadSendAlgorithmTestResult(&expected));
random_seed_ = expected.random_seed();
} else {
random_seed_ = QuicRandom::GetInstance()->RandUint64();
}
random_.set_seed(random_seed_);
QUIC_LOG(INFO) << "Using random seed: " << random_seed_;
}
~Bbr2SimulatorTest() override {
const std::string regression_mode =
GetQuicFlag(FLAGS_quic_bbr2_test_regression_mode);
const QuicTime::Delta simulated_duration =
SimulatedNow() - QuicTime::Zero();
if (regression_mode == "record") {
RecordSendAlgorithmTestResult(random_seed_,
simulated_duration.ToMicroseconds());
} else if (regression_mode == "regress") {
CompareSendAlgorithmTestResult(simulated_duration.ToMicroseconds());
}
}
QuicTime SimulatedNow() const { return simulator_.GetClock()->Now(); }
uint64_t random_seed_;
SimpleRandom random_;
simulator::Simulator simulator_;
};
class Bbr2DefaultTopologyTest : public Bbr2SimulatorTest {
protected:
Bbr2DefaultTopologyTest()
: sender_endpoint_(&simulator_,
"Sender",
"Receiver",
Perspective::IS_CLIENT,
TestConnectionId(42)),
receiver_endpoint_(&simulator_,
"Receiver",
"Sender",
Perspective::IS_SERVER,
TestConnectionId(42)) {
sender_ = SetupBbr2Sender(&sender_endpoint_, /*old_sender=*/nullptr);
}
~Bbr2DefaultTopologyTest() {
const auto* test_info =
::testing::UnitTest::GetInstance()->current_test_info();
const Bbr2Sender::DebugState& debug_state = sender_->ExportDebugState();
QUIC_LOG(INFO) << "Bbr2DefaultTopologyTest." << test_info->name()
<< " completed at simulated time: "
<< SimulatedNow().ToDebuggingValue() / 1e6
<< " sec. packet loss:"
<< sender_loss_rate_in_packets() * 100
<< "%, bw_hi:" << debug_state.bandwidth_hi;
}
QuicUnackedPacketMap* GetUnackedMap(QuicConnection* connection) {
return QuicSentPacketManagerPeer::GetUnackedPacketMap(
QuicConnectionPeer::GetSentPacketManager(connection));
}
Bbr2Sender* SetupBbr2Sender(simulator::QuicEndpoint* endpoint,
BbrSender* old_sender) {
// Ownership of the sender will be overtaken by the endpoint.
Bbr2Sender* sender = new Bbr2Sender(
endpoint->connection()->clock()->Now(),
endpoint->connection()->sent_packet_manager().GetRttStats(),
GetUnackedMap(endpoint->connection()), kDefaultInitialCwndPackets,
GetQuicFlag(FLAGS_quic_max_congestion_window), &random_,
QuicConnectionPeer::GetStats(endpoint->connection()), old_sender);
QuicConnectionPeer::SetSendAlgorithm(endpoint->connection(), sender);
const int kTestMaxPacketSize = 1350;
endpoint->connection()->SetMaxPacketLength(kTestMaxPacketSize);
endpoint->RecordTrace();
return sender;
}
void CreateNetwork(const DefaultTopologyParams& params) {
QUIC_LOG(INFO) << "CreateNetwork with parameters: " << params.ToString();
switch_ = std::make_unique<simulator::Switch>(&simulator_, "Switch",
params.switch_port_count,
params.SwitchQueueCapacity());
// WARNING: The order to add links to network_links_ matters, because some
// tests adjusts the link bandwidth on the fly.
// Local link connects sender and port 1.
network_links_.push_back(std::make_unique<simulator::SymmetricLink>(
&sender_endpoint_, switch_->port(1), params.local_link.bandwidth,
params.local_link.delay));
// Test link connects receiver and port 2.
if (params.sender_policer_params.has_value()) {
const TrafficPolicerParams& policer_params =
params.sender_policer_params.value();
sender_policer_ = std::make_unique<simulator::TrafficPolicer>(
&simulator_, policer_params.name, policer_params.initial_burst_size,
policer_params.max_bucket_size, policer_params.target_bandwidth,
switch_->port(2));
network_links_.push_back(std::make_unique<simulator::SymmetricLink>(
&receiver_endpoint_, sender_policer_.get(),
params.test_link.bandwidth, params.test_link.delay));
} else {
network_links_.push_back(std::make_unique<simulator::SymmetricLink>(
&receiver_endpoint_, switch_->port(2), params.test_link.bandwidth,
params.test_link.delay));
}
}
simulator::SymmetricLink* TestLink() { return network_links_[1].get(); }
void DoSimpleTransfer(QuicByteCount transfer_size, QuicTime::Delta timeout) {
sender_endpoint_.AddBytesToTransfer(transfer_size);
// TODO(wub): consider rewriting this to run until the receiver actually
// receives the intended amount of bytes.
bool simulator_result = simulator_.RunUntilOrTimeout(
[this]() { return sender_endpoint_.bytes_to_transfer() == 0; },
timeout);
EXPECT_TRUE(simulator_result)
<< "Simple transfer failed. Bytes remaining: "
<< sender_endpoint_.bytes_to_transfer();
QUIC_LOG(INFO) << "Simple transfer state: " << sender_->ExportDebugState();
}
// Drive the simulator by sending enough data to enter PROBE_BW.
void DriveOutOfStartup(const DefaultTopologyParams& params) {
ASSERT_FALSE(sender_->ExportDebugState().startup.full_bandwidth_reached);
DoSimpleTransfer(1024 * 1024, QuicTime::Delta::FromSeconds(15));
EXPECT_EQ(Bbr2Mode::PROBE_BW, sender_->ExportDebugState().mode);
EXPECT_APPROX_EQ(params.BottleneckBandwidth(),
sender_->ExportDebugState().bandwidth_hi, 0.02f);
}
// Send |bytes|-sized bursts of data |number_of_bursts| times, waiting for
// |wait_time| between each burst.
void SendBursts(const DefaultTopologyParams& params,
size_t number_of_bursts,
QuicByteCount bytes,
QuicTime::Delta wait_time) {
ASSERT_EQ(0u, sender_endpoint_.bytes_to_transfer());
for (size_t i = 0; i < number_of_bursts; i++) {
sender_endpoint_.AddBytesToTransfer(bytes);
// Transfer data and wait for three seconds between each transfer.
simulator_.RunFor(wait_time);
// Ensure the connection did not time out.
ASSERT_TRUE(sender_endpoint_.connection()->connected());
ASSERT_TRUE(receiver_endpoint_.connection()->connected());
}
simulator_.RunFor(wait_time + params.RTT());
ASSERT_EQ(0u, sender_endpoint_.bytes_to_transfer());
}
template <class TerminationPredicate>
bool SendUntilOrTimeout(TerminationPredicate termination_predicate,
QuicTime::Delta timeout) {
EXPECT_EQ(0u, sender_endpoint_.bytes_to_transfer());
const QuicTime deadline = SimulatedNow() + timeout;
do {
sender_endpoint_.AddBytesToTransfer(4 * kDefaultTCPMSS);
if (simulator_.RunUntilOrTimeout(
[this]() { return sender_endpoint_.bytes_to_transfer() == 0; },
deadline - SimulatedNow()) &&
termination_predicate()) {
return true;
}
} while (SimulatedNow() < deadline);
return false;
}
void EnableAggregation(QuicByteCount aggregation_bytes,
QuicTime::Delta aggregation_timeout) {
switch_->port_queue(1)->EnableAggregation(aggregation_bytes,
aggregation_timeout);
}
void SetConnectionOption(QuicTag option) {
SetConnectionOption(std::move(option), sender_);
}
void SetConnectionOption(QuicTag option, Bbr2Sender* sender) {
QuicConfig config;
QuicTagVector options;
options.push_back(option);
QuicConfigPeer::SetReceivedConnectionOptions(&config, options);
sender->SetFromConfig(config, Perspective::IS_SERVER);
}
bool Bbr2ModeIsOneOf(const std::vector<Bbr2Mode>& expected_modes) const {
const Bbr2Mode mode = sender_->ExportDebugState().mode;
for (Bbr2Mode expected_mode : expected_modes) {
if (mode == expected_mode) {
return true;
}
}
return false;
}
const RttStats* rtt_stats() {
return sender_endpoint_.connection()->sent_packet_manager().GetRttStats();
}
QuicConnection* sender_connection() { return sender_endpoint_.connection(); }
Bbr2Sender::DebugState sender_debug_state() const {
return sender_->ExportDebugState();
}
const QuicConnectionStats& sender_connection_stats() {
return sender_connection()->GetStats();
}
QuicUnackedPacketMap* sender_unacked_map() {
return GetUnackedMap(sender_connection());
}
float sender_loss_rate_in_packets() {
return static_cast<float>(sender_connection_stats().packets_lost) /
sender_connection_stats().packets_sent;
}
simulator::QuicEndpoint sender_endpoint_;
simulator::QuicEndpoint receiver_endpoint_;
Bbr2Sender* sender_;
std::unique_ptr<simulator::Switch> switch_;
std::unique_ptr<simulator::TrafficPolicer> sender_policer_;
std::vector<std::unique_ptr<simulator::SymmetricLink>> network_links_;
};
TEST_F(Bbr2DefaultTopologyTest, NormalStartup) {
DefaultTopologyParams params;
CreateNetwork(params);
// Run until the full bandwidth is reached and check how many rounds it was.
sender_endpoint_.AddBytesToTransfer(12 * 1024 * 1024);
QuicRoundTripCount max_bw_round = 0;
QuicBandwidth max_bw(QuicBandwidth::Zero());
bool simulator_result = simulator_.RunUntilOrTimeout(
[this, &max_bw, &max_bw_round]() {
if (max_bw < sender_->ExportDebugState().bandwidth_hi) {
max_bw = sender_->ExportDebugState().bandwidth_hi;
max_bw_round = sender_->ExportDebugState().round_trip_count;
}
return sender_->ExportDebugState().startup.full_bandwidth_reached;
},
QuicTime::Delta::FromSeconds(5));
ASSERT_TRUE(simulator_result);
EXPECT_EQ(Bbr2Mode::DRAIN, sender_->ExportDebugState().mode);
EXPECT_EQ(3u, sender_->ExportDebugState().round_trip_count - max_bw_round);
EXPECT_EQ(
3u,
sender_->ExportDebugState().startup.round_trips_without_bandwidth_growth);
EXPECT_EQ(0u, sender_connection_stats().packets_lost);
EXPECT_APPROX_EQ(params.BottleneckBandwidth(),
sender_->ExportDebugState().bandwidth_hi, 0.01f);
EXPECT_FALSE(sender_->ExportDebugState().last_sample_is_app_limited);
}
TEST_F(Bbr2DefaultTopologyTest, NormalStartupB207) {
SetQuicReloadableFlag(quic_bbr2_exit_startup_on_persistent_queue2, true);
SetConnectionOption(kB207);
DefaultTopologyParams params;
CreateNetwork(params);
// Run until the full bandwidth is reached and check how many rounds it was.
sender_endpoint_.AddBytesToTransfer(12 * 1024 * 1024);
QuicRoundTripCount max_bw_round = 0;
QuicBandwidth max_bw(QuicBandwidth::Zero());
bool simulator_result = simulator_.RunUntilOrTimeout(
[this, &max_bw, &max_bw_round]() {
if (max_bw < sender_->ExportDebugState().bandwidth_hi) {
max_bw = sender_->ExportDebugState().bandwidth_hi;
max_bw_round = sender_->ExportDebugState().round_trip_count;
}
return sender_->ExportDebugState().startup.full_bandwidth_reached;
},
QuicTime::Delta::FromSeconds(5));
ASSERT_TRUE(simulator_result);
EXPECT_EQ(Bbr2Mode::DRAIN, sender_->ExportDebugState().mode);
EXPECT_EQ(1u, sender_->ExportDebugState().round_trip_count - max_bw_round);
EXPECT_EQ(
1u,
sender_->ExportDebugState().startup.round_trips_without_bandwidth_growth);
EXPECT_APPROX_EQ(params.BottleneckBandwidth(),
sender_->ExportDebugState().bandwidth_hi, 0.01f);
EXPECT_EQ(0u, sender_connection_stats().packets_lost);
}
// Add extra_acked to CWND in STARTUP and exit STARTUP on a persistent queue.
TEST_F(Bbr2DefaultTopologyTest, NormalStartupB207andB205) {
SetQuicReloadableFlag(quic_bbr2_startup_extra_acked, true);
SetQuicReloadableFlag(quic_bbr2_exit_startup_on_persistent_queue2, true);
SetConnectionOption(kB205);
SetConnectionOption(kB207);
DefaultTopologyParams params;
CreateNetwork(params);
// Run until the full bandwidth is reached and check how many rounds it was.
sender_endpoint_.AddBytesToTransfer(12 * 1024 * 1024);
QuicRoundTripCount max_bw_round = 0;
QuicBandwidth max_bw(QuicBandwidth::Zero());
bool simulator_result = simulator_.RunUntilOrTimeout(
[this, &max_bw, &max_bw_round]() {
if (max_bw < sender_->ExportDebugState().bandwidth_hi) {
max_bw = sender_->ExportDebugState().bandwidth_hi;
max_bw_round = sender_->ExportDebugState().round_trip_count;
}
return sender_->ExportDebugState().startup.full_bandwidth_reached;
},
QuicTime::Delta::FromSeconds(5));
ASSERT_TRUE(simulator_result);
EXPECT_EQ(Bbr2Mode::DRAIN, sender_->ExportDebugState().mode);
EXPECT_EQ(1u, sender_->ExportDebugState().round_trip_count - max_bw_round);
EXPECT_EQ(
2u,
sender_->ExportDebugState().startup.round_trips_without_bandwidth_growth);
EXPECT_APPROX_EQ(params.BottleneckBandwidth(),
sender_->ExportDebugState().bandwidth_hi, 0.01f);
EXPECT_EQ(0u, sender_connection_stats().packets_lost);
}
// Test a simple long data transfer in the default setup.
TEST_F(Bbr2DefaultTopologyTest, SimpleTransfer) {
DefaultTopologyParams params;
CreateNetwork(params);
// At startup make sure we are at the default.
EXPECT_EQ(kDefaultInitialCwndBytes, sender_->GetCongestionWindow());
// At startup make sure we can send.
EXPECT_TRUE(sender_->CanSend(0));
// And that window is un-affected.
EXPECT_EQ(kDefaultInitialCwndBytes, sender_->GetCongestionWindow());
// Verify that Sender is in slow start.
EXPECT_TRUE(sender_->InSlowStart());
// Verify that pacing rate is based on the initial RTT.
QuicBandwidth expected_pacing_rate = QuicBandwidth::FromBytesAndTimeDelta(
2.885 * kDefaultInitialCwndBytes, rtt_stats()->initial_rtt());
EXPECT_APPROX_EQ(expected_pacing_rate.ToBitsPerSecond(),
sender_->PacingRate(0).ToBitsPerSecond(), 0.01f);
ASSERT_GE(params.BDP(), kDefaultInitialCwndBytes + kDefaultTCPMSS);
DoSimpleTransfer(12 * 1024 * 1024, QuicTime::Delta::FromSeconds(30));
EXPECT_TRUE(Bbr2ModeIsOneOf({Bbr2Mode::PROBE_BW, Bbr2Mode::PROBE_RTT}));
EXPECT_EQ(0u, sender_connection_stats().packets_lost);
EXPECT_FALSE(sender_->ExportDebugState().last_sample_is_app_limited);
// The margin here is quite high, since there exists a possibility that the
// connection just exited high gain cycle.
EXPECT_APPROX_EQ(params.RTT(), rtt_stats()->smoothed_rtt(), 1.0f);
}
TEST_F(Bbr2DefaultTopologyTest, SimpleTransferB2RC) {
SetConnectionOption(kB2RC);
DefaultTopologyParams params;
CreateNetwork(params);
// Transfer 12MB.
DoSimpleTransfer(12 * 1024 * 1024, QuicTime::Delta::FromSeconds(35));
EXPECT_TRUE(Bbr2ModeIsOneOf({Bbr2Mode::PROBE_BW, Bbr2Mode::PROBE_RTT}));
EXPECT_APPROX_EQ(params.BottleneckBandwidth(),
sender_->ExportDebugState().bandwidth_hi, 0.01f);
EXPECT_LE(sender_loss_rate_in_packets(), 0.05);
// The margin here is high, because the aggregation greatly increases
// smoothed rtt.
EXPECT_GE(params.RTT() * 4, rtt_stats()->smoothed_rtt());
EXPECT_APPROX_EQ(params.RTT(), rtt_stats()->min_rtt(), 0.2f);
}
TEST_F(Bbr2DefaultTopologyTest, SimpleTransferB201) {
SetConnectionOption(kB201);
DefaultTopologyParams params;
CreateNetwork(params);
// Transfer 12MB.
DoSimpleTransfer(12 * 1024 * 1024, QuicTime::Delta::FromSeconds(35));
EXPECT_TRUE(Bbr2ModeIsOneOf({Bbr2Mode::PROBE_BW, Bbr2Mode::PROBE_RTT}));
EXPECT_APPROX_EQ(params.BottleneckBandwidth(),
sender_->ExportDebugState().bandwidth_hi, 0.01f);
EXPECT_LE(sender_loss_rate_in_packets(), 0.05);
// The margin here is high, because the aggregation greatly increases
// smoothed rtt.
EXPECT_GE(params.RTT() * 4, rtt_stats()->smoothed_rtt());
EXPECT_APPROX_EQ(params.RTT(), rtt_stats()->min_rtt(), 0.2f);
}
TEST_F(Bbr2DefaultTopologyTest, SimpleTransferB206) {
SetQuicReloadableFlag(quic_bbr2_startup_probe_up_loss_events, true);
SetConnectionOption(kB206);
DefaultTopologyParams params;
CreateNetwork(params);
// Transfer 12MB.
DoSimpleTransfer(12 * 1024 * 1024, QuicTime::Delta::FromSeconds(35));
EXPECT_TRUE(Bbr2ModeIsOneOf({Bbr2Mode::PROBE_BW, Bbr2Mode::PROBE_RTT}));
EXPECT_APPROX_EQ(params.BottleneckBandwidth(),
sender_->ExportDebugState().bandwidth_hi, 0.01f);
EXPECT_LE(sender_loss_rate_in_packets(), 0.05);
// The margin here is high, because the aggregation greatly increases
// smoothed rtt.
EXPECT_GE(params.RTT() * 4, rtt_stats()->smoothed_rtt());
EXPECT_APPROX_EQ(params.RTT(), rtt_stats()->min_rtt(), 0.2f);
}
TEST_F(Bbr2DefaultTopologyTest, SimpleTransferB207) {
SetQuicReloadableFlag(quic_bbr2_exit_startup_on_persistent_queue2, true);
SetConnectionOption(kB207);
DefaultTopologyParams params;
CreateNetwork(params);
// Transfer 12MB.
DoSimpleTransfer(12 * 1024 * 1024, QuicTime::Delta::FromSeconds(35));
EXPECT_TRUE(Bbr2ModeIsOneOf({Bbr2Mode::PROBE_BW, Bbr2Mode::PROBE_RTT}));
EXPECT_APPROX_EQ(params.BottleneckBandwidth(),
sender_->ExportDebugState().bandwidth_hi, 0.01f);
EXPECT_LE(sender_loss_rate_in_packets(), 0.05);
// The margin here is high, because the aggregation greatly increases
// smoothed rtt.
EXPECT_GE(params.RTT() * 4, rtt_stats()->smoothed_rtt());
EXPECT_APPROX_EQ(params.RTT(), rtt_stats()->min_rtt(), 0.2f);
}
TEST_F(Bbr2DefaultTopologyTest, SimpleTransferBBRB) {
SetQuicReloadableFlag(quic_bbr_use_send_rate_in_max_ack_height_tracker, true);
SetConnectionOption(kBBRB);
DefaultTopologyParams params;
CreateNetwork(params);
// Transfer 12MB.
DoSimpleTransfer(12 * 1024 * 1024, QuicTime::Delta::FromSeconds(35));
EXPECT_TRUE(Bbr2ModeIsOneOf({Bbr2Mode::PROBE_BW, Bbr2Mode::PROBE_RTT}));
EXPECT_APPROX_EQ(params.BottleneckBandwidth(),
sender_->ExportDebugState().bandwidth_hi, 0.01f);
EXPECT_LE(sender_loss_rate_in_packets(), 0.05);
// The margin here is high, because the aggregation greatly increases
// smoothed rtt.
EXPECT_GE(params.RTT() * 4, rtt_stats()->smoothed_rtt());
EXPECT_APPROX_EQ(params.RTT(), rtt_stats()->min_rtt(), 0.2f);
}
TEST_F(Bbr2DefaultTopologyTest, SimpleTransferBBR4) {
SetQuicReloadableFlag(quic_bbr2_extra_acked_window, true);
SetConnectionOption(kBBR4);
DefaultTopologyParams params;
CreateNetwork(params);
// Transfer 12MB.
DoSimpleTransfer(12 * 1024 * 1024, QuicTime::Delta::FromSeconds(35));
EXPECT_TRUE(Bbr2ModeIsOneOf({Bbr2Mode::PROBE_BW, Bbr2Mode::PROBE_RTT}));
EXPECT_APPROX_EQ(params.BottleneckBandwidth(),
sender_->ExportDebugState().bandwidth_hi, 0.01f);
EXPECT_LE(sender_loss_rate_in_packets(), 0.05);
// The margin here is high, because the aggregation greatly increases
// smoothed rtt.
EXPECT_GE(params.RTT() * 4, rtt_stats()->smoothed_rtt());
EXPECT_APPROX_EQ(params.RTT(), rtt_stats()->min_rtt(), 0.2f);
}
TEST_F(Bbr2DefaultTopologyTest, SimpleTransferBBR5) {
SetQuicReloadableFlag(quic_bbr2_extra_acked_window, true);
SetConnectionOption(kBBR5);
DefaultTopologyParams params;
CreateNetwork(params);
// Transfer 12MB.
DoSimpleTransfer(12 * 1024 * 1024, QuicTime::Delta::FromSeconds(35));
EXPECT_TRUE(Bbr2ModeIsOneOf({Bbr2Mode::PROBE_BW, Bbr2Mode::PROBE_RTT}));
EXPECT_APPROX_EQ(params.BottleneckBandwidth(),
sender_->ExportDebugState().bandwidth_hi, 0.01f);
EXPECT_LE(sender_loss_rate_in_packets(), 0.05);
// The margin here is high, because the aggregation greatly increases
// smoothed rtt.
EXPECT_GE(params.RTT() * 4, rtt_stats()->smoothed_rtt());
EXPECT_APPROX_EQ(params.RTT(), rtt_stats()->min_rtt(), 0.2f);
}
TEST_F(Bbr2DefaultTopologyTest, SimpleTransferSmallBuffer) {
DefaultTopologyParams params;
params.switch_queue_capacity_in_bdp = 0.5;
CreateNetwork(params);
DoSimpleTransfer(12 * 1024 * 1024, QuicTime::Delta::FromSeconds(30));
EXPECT_TRUE(Bbr2ModeIsOneOf({Bbr2Mode::PROBE_BW, Bbr2Mode::PROBE_RTT}));
EXPECT_APPROX_EQ(params.BottleneckBandwidth(),
sender_->ExportDebugState().bandwidth_hi, 0.02f);
EXPECT_GE(sender_connection_stats().packets_lost, 0u);
EXPECT_FALSE(sender_->ExportDebugState().last_sample_is_app_limited);
}
TEST_F(Bbr2DefaultTopologyTest, SimpleTransferSmallBufferB2H2) {
SetConnectionOption(kB2H2);
DefaultTopologyParams params;
params.switch_queue_capacity_in_bdp = 0.5;
CreateNetwork(params);
DoSimpleTransfer(12 * 1024 * 1024, QuicTime::Delta::FromSeconds(30));
EXPECT_TRUE(Bbr2ModeIsOneOf({Bbr2Mode::PROBE_BW, Bbr2Mode::PROBE_RTT}));
EXPECT_APPROX_EQ(params.BottleneckBandwidth(),
sender_->ExportDebugState().bandwidth_hi, 0.02f);
EXPECT_GE(sender_connection_stats().packets_lost, 0u);
EXPECT_FALSE(sender_->ExportDebugState().last_sample_is_app_limited);
}
TEST_F(Bbr2DefaultTopologyTest, SimpleTransfer2RTTAggregationBytes) {
SetConnectionOption(kBSAO);
DefaultTopologyParams params;
CreateNetwork(params);
// 2 RTTs of aggregation, with a max of 10kb.
EnableAggregation(10 * 1024, 2 * params.RTT());
// Transfer 12MB.
DoSimpleTransfer(12 * 1024 * 1024, QuicTime::Delta::FromSeconds(35));
EXPECT_TRUE(Bbr2ModeIsOneOf({Bbr2Mode::PROBE_BW, Bbr2Mode::PROBE_RTT}));
EXPECT_APPROX_EQ(params.BottleneckBandwidth(),
sender_->ExportDebugState().bandwidth_hi, 0.01f);
if (GetQuicReloadableFlag(quic_fix_pacing_sender_bursts)) {
EXPECT_EQ(sender_loss_rate_in_packets(), 0);
} else {
EXPECT_LE(sender_loss_rate_in_packets(), 0.05);
}
// The margin here is high, because both link level aggregation and ack
// decimation can greatly increase smoothed rtt.
EXPECT_GE(params.RTT() * 5, rtt_stats()->smoothed_rtt());
EXPECT_APPROX_EQ(params.RTT(), rtt_stats()->min_rtt(), 0.2f);
}
TEST_F(Bbr2DefaultTopologyTest, SimpleTransfer2RTTAggregationBytesB201) {
SetConnectionOption(kB201);
DefaultTopologyParams params;
CreateNetwork(params);
// 2 RTTs of aggregation, with a max of 10kb.
EnableAggregation(10 * 1024, 2 * params.RTT());
// Transfer 12MB.
DoSimpleTransfer(12 * 1024 * 1024, QuicTime::Delta::FromSeconds(35));
EXPECT_TRUE(Bbr2ModeIsOneOf({Bbr2Mode::PROBE_BW, Bbr2Mode::PROBE_RTT}));
// TODO(wub): Tighten the error bound once BSAO is default enabled.
EXPECT_APPROX_EQ(params.BottleneckBandwidth(),
sender_->ExportDebugState().bandwidth_hi, 0.5f);
if (GetQuicReloadableFlag(quic_fix_pacing_sender_bursts)) {
EXPECT_LE(sender_loss_rate_in_packets(), 0.01);
} else {
EXPECT_LE(sender_loss_rate_in_packets(), 0.05);
}
// The margin here is high, because both link level aggregation and ack
// decimation can greatly increase smoothed rtt.
EXPECT_GE(params.RTT() * 5, rtt_stats()->smoothed_rtt());
EXPECT_APPROX_EQ(params.RTT(), rtt_stats()->min_rtt(), 0.2f);
}
TEST_F(Bbr2DefaultTopologyTest, SimpleTransferAckDecimation) {
SetConnectionOption(kBSAO);
DefaultTopologyParams params;
CreateNetwork(params);
// Transfer 12MB.
DoSimpleTransfer(12 * 1024 * 1024, QuicTime::Delta::FromSeconds(35));
EXPECT_TRUE(Bbr2ModeIsOneOf({Bbr2Mode::PROBE_BW, Bbr2Mode::PROBE_RTT}));
EXPECT_APPROX_EQ(params.BottleneckBandwidth(),
sender_->ExportDebugState().bandwidth_hi, 0.01f);
EXPECT_LE(sender_loss_rate_in_packets(), 0.001);
EXPECT_FALSE(sender_->ExportDebugState().last_sample_is_app_limited);
// The margin here is high, because the aggregation greatly increases
// smoothed rtt.
EXPECT_GE(params.RTT() * 3, rtt_stats()->smoothed_rtt());
EXPECT_APPROX_EQ(params.RTT(), rtt_stats()->min_rtt(), 0.1f);
}
// Test Bbr2's reaction to a 100x bandwidth decrease during a transfer.
TEST_F(Bbr2DefaultTopologyTest, QUIC_SLOW_TEST(BandwidthDecrease)) {
DefaultTopologyParams params;
params.local_link.bandwidth = QuicBandwidth::FromKBitsPerSecond(15000);
params.test_link.bandwidth = QuicBandwidth::FromKBitsPerSecond(10000);
CreateNetwork(params);
sender_endpoint_.AddBytesToTransfer(20 * 1024 * 1024);
// We can transfer ~12MB in the first 10 seconds. The rest ~8MB needs about
// 640 seconds.
simulator_.RunFor(QuicTime::Delta::FromSeconds(10));
EXPECT_TRUE(Bbr2ModeIsOneOf({Bbr2Mode::PROBE_BW, Bbr2Mode::PROBE_RTT}));
QUIC_LOG(INFO) << "Bandwidth decreasing at time " << SimulatedNow();
EXPECT_APPROX_EQ(params.test_link.bandwidth,
sender_->ExportDebugState().bandwidth_est, 0.1f);
EXPECT_EQ(0u, sender_connection_stats().packets_lost);
// Now decrease the bottleneck bandwidth from 10Mbps to 100Kbps.
params.test_link.bandwidth = QuicBandwidth::FromKBitsPerSecond(100);
TestLink()->set_bandwidth(params.test_link.bandwidth);
bool simulator_result = simulator_.RunUntilOrTimeout(
[this]() { return sender_endpoint_.bytes_to_transfer() == 0; },
QuicTime::Delta::FromSeconds(800));
EXPECT_TRUE(simulator_result);
}
// Test Bbr2's reaction to a 100x bandwidth increase during a transfer.
TEST_F(Bbr2DefaultTopologyTest, QUIC_SLOW_TEST(BandwidthIncrease)) {
DefaultTopologyParams params;
params.local_link.bandwidth = QuicBandwidth::FromKBitsPerSecond(15000);
params.test_link.bandwidth = QuicBandwidth::FromKBitsPerSecond(100);
CreateNetwork(params);
sender_endpoint_.AddBytesToTransfer(20 * 1024 * 1024);
simulator_.RunFor(QuicTime::Delta::FromSeconds(15));
EXPECT_TRUE(Bbr2ModeIsOneOf({Bbr2Mode::PROBE_BW, Bbr2Mode::PROBE_RTT}));
QUIC_LOG(INFO) << "Bandwidth increasing at time " << SimulatedNow();
EXPECT_APPROX_EQ(params.test_link.bandwidth,
sender_->ExportDebugState().bandwidth_est, 0.1f);
EXPECT_LE(sender_loss_rate_in_packets(), 0.30);
// Now increase the bottleneck bandwidth from 100Kbps to 10Mbps.
params.test_link.bandwidth = QuicBandwidth::FromKBitsPerSecond(10000);
TestLink()->set_bandwidth(params.test_link.bandwidth);
bool simulator_result = simulator_.RunUntilOrTimeout(
[this]() { return sender_endpoint_.bytes_to_transfer() == 0; },
QuicTime::Delta::FromSeconds(50));
EXPECT_TRUE(simulator_result);
// Ensure the full bandwidth is discovered.
EXPECT_APPROX_EQ(params.test_link.bandwidth,
sender_->ExportDebugState().bandwidth_hi, 0.02f);
}
// Test Bbr2's reaction to a 100x bandwidth increase during a transfer with BBQ0
TEST_F(Bbr2DefaultTopologyTest, QUIC_SLOW_TEST(BandwidthIncreaseBBQ0)) {
SetQuicReloadableFlag(quic_bbr2_add_bytes_acked_after_inflight_hi_limited,
true);
SetConnectionOption(kBBQ0);
DefaultTopologyParams params;
params.local_link.bandwidth = QuicBandwidth::FromKBitsPerSecond(15000);
params.test_link.bandwidth = QuicBandwidth::FromKBitsPerSecond(100);
CreateNetwork(params);
sender_endpoint_.AddBytesToTransfer(10 * 1024 * 1024);
simulator_.RunFor(QuicTime::Delta::FromSeconds(15));
EXPECT_TRUE(Bbr2ModeIsOneOf({Bbr2Mode::PROBE_BW, Bbr2Mode::PROBE_RTT}));
QUIC_LOG(INFO) << "Bandwidth increasing at time " << SimulatedNow();
EXPECT_APPROX_EQ(params.test_link.bandwidth,
sender_->ExportDebugState().bandwidth_est, 0.1f);
EXPECT_LE(sender_loss_rate_in_packets(), 0.30);
// Now increase the bottleneck bandwidth from 100Kbps to 10Mbps.
params.test_link.bandwidth = QuicBandwidth::FromKBitsPerSecond(10000);
TestLink()->set_bandwidth(params.test_link.bandwidth);
bool simulator_result = simulator_.RunUntilOrTimeout(
[this]() { return sender_endpoint_.bytes_to_transfer() == 0; },
QuicTime::Delta::FromSeconds(50));
EXPECT_TRUE(simulator_result);
// Ensure the full bandwidth is discovered.
EXPECT_APPROX_EQ(params.test_link.bandwidth,
sender_->ExportDebugState().bandwidth_hi, 0.02f);
}
// Test Bbr2's reaction to a 100x bandwidth increase during a transfer with BBQ0
// in the presence of ACK aggregation.
TEST_F(Bbr2DefaultTopologyTest,
QUIC_SLOW_TEST(BandwidthIncreaseBBQ0Aggregation)) {
SetQuicReloadableFlag(quic_bbr2_add_bytes_acked_after_inflight_hi_limited,
true);
SetConnectionOption(kBBQ0);
DefaultTopologyParams params;
params.local_link.bandwidth = QuicBandwidth::FromKBitsPerSecond(15000);
params.test_link.bandwidth = QuicBandwidth::FromKBitsPerSecond(100);
CreateNetwork(params);
// 2 RTTs of aggregation, with a max of 10kb.
EnableAggregation(10 * 1024, 2 * params.RTT());
// Reduce the payload to 2MB because 10MB takes too long.
sender_endpoint_.AddBytesToTransfer(2 * 1024 * 1024);
simulator_.RunFor(QuicTime::Delta::FromSeconds(15));
EXPECT_TRUE(Bbr2ModeIsOneOf({Bbr2Mode::PROBE_BW, Bbr2Mode::PROBE_RTT}));
QUIC_LOG(INFO) << "Bandwidth increasing at time " << SimulatedNow();
// This is much farther off when aggregation is present,
// Ideally BSAO or another option would fix this.
// TODO(ianswett) Make these bound tighter once overestimation is reduced.
EXPECT_APPROX_EQ(params.test_link.bandwidth,
sender_->ExportDebugState().bandwidth_est, 0.6f);
EXPECT_LE(sender_loss_rate_in_packets(), 0.35);
// Now increase the bottleneck bandwidth from 100Kbps to 10Mbps.
params.test_link.bandwidth = QuicBandwidth::FromKBitsPerSecond(10000);
TestLink()->set_bandwidth(params.test_link.bandwidth);
bool simulator_result = simulator_.RunUntilOrTimeout(
[this]() { return sender_endpoint_.bytes_to_transfer() == 0; },
QuicTime::Delta::FromSeconds(50));
EXPECT_TRUE(simulator_result);
// Ensure at least 10% of full bandwidth is discovered.
EXPECT_APPROX_EQ(params.test_link.bandwidth,
sender_->ExportDebugState().bandwidth_hi, 0.90f);
}
// Test Bbr2's reaction to a 100x bandwidth increase during a transfer with B202
TEST_F(Bbr2DefaultTopologyTest, QUIC_SLOW_TEST(BandwidthIncreaseB202)) {
SetQuicReloadableFlag(quic_bbr2_no_probe_up_exit_if_no_queue, true);
SetConnectionOption(kB202);
DefaultTopologyParams params;
params.local_link.bandwidth = QuicBandwidth::FromKBitsPerSecond(15000);
params.test_link.bandwidth = QuicBandwidth::FromKBitsPerSecond(100);
CreateNetwork(params);
sender_endpoint_.AddBytesToTransfer(10 * 1024 * 1024);
simulator_.RunFor(QuicTime::Delta::FromSeconds(15));
EXPECT_TRUE(Bbr2ModeIsOneOf({Bbr2Mode::PROBE_BW, Bbr2Mode::PROBE_RTT}));
QUIC_LOG(INFO) << "Bandwidth increasing at time " << SimulatedNow();
EXPECT_APPROX_EQ(params.test_link.bandwidth,
sender_->ExportDebugState().bandwidth_est, 0.1f);
EXPECT_LE(sender_loss_rate_in_packets(), 0.30);
// Now increase the bottleneck bandwidth from 100Kbps to 10Mbps.
params.test_link.bandwidth = QuicBandwidth::FromKBitsPerSecond(10000);
TestLink()->set_bandwidth(params.test_link.bandwidth);
bool simulator_result = simulator_.RunUntilOrTimeout(
[this]() { return sender_endpoint_.bytes_to_transfer() == 0; },
QuicTime::Delta::FromSeconds(50));
EXPECT_TRUE(simulator_result);
// Ensure the full bandwidth is discovered.
EXPECT_APPROX_EQ(params.test_link.bandwidth,
sender_->ExportDebugState().bandwidth_hi, 0.1f);
}
// Test Bbr2's reaction to a 100x bandwidth increase during a transfer with B202
// in the presence of ACK aggregation.
TEST_F(Bbr2DefaultTopologyTest,
QUIC_SLOW_TEST(BandwidthIncreaseB202Aggregation)) {
SetQuicReloadableFlag(quic_bbr2_no_probe_up_exit_if_no_queue, true);
SetConnectionOption(kB202);
DefaultTopologyParams params;
params.local_link.bandwidth = QuicBandwidth::FromKBitsPerSecond(15000);
params.test_link.bandwidth = QuicBandwidth::FromKBitsPerSecond(100);
CreateNetwork(params);
// 2 RTTs of aggregation, with a max of 10kb.
EnableAggregation(10 * 1024, 2 * params.RTT());
// Reduce the payload to 2MB because 10MB takes too long.
sender_endpoint_.AddBytesToTransfer(2 * 1024 * 1024);
simulator_.RunFor(QuicTime::Delta::FromSeconds(15));
EXPECT_TRUE(Bbr2ModeIsOneOf({Bbr2Mode::PROBE_BW, Bbr2Mode::PROBE_RTT}));
QUIC_LOG(INFO) << "Bandwidth increasing at time " << SimulatedNow();
// This is much farther off when aggregation is present,
// Ideally BSAO or another option would fix this.
EXPECT_APPROX_EQ(params.test_link.bandwidth,
sender_->ExportDebugState().bandwidth_est, 0.6f);
EXPECT_LE(sender_loss_rate_in_packets(), 0.35);
// Now increase the bottleneck bandwidth from 100Kbps to 10Mbps.
params.test_link.bandwidth = QuicBandwidth::FromKBitsPerSecond(10000);
TestLink()->set_bandwidth(params.test_link.bandwidth);
bool simulator_result = simulator_.RunUntilOrTimeout(
[this]() { return sender_endpoint_.bytes_to_transfer() == 0; },
QuicTime::Delta::FromSeconds(50));
EXPECT_TRUE(simulator_result);
// Ensure at least 10% of full bandwidth is discovered.
EXPECT_APPROX_EQ(params.test_link.bandwidth,
sender_->ExportDebugState().bandwidth_hi, 0.92f);
}
// Test Bbr2's reaction to a 100x bandwidth increase during a transfer with B203
TEST_F(Bbr2DefaultTopologyTest, QUIC_SLOW_TEST(BandwidthIncreaseB203)) {
SetQuicReloadableFlag(quic_bbr2_ignore_inflight_hi_in_probe_up, true);
SetConnectionOption(kB203);
DefaultTopologyParams params;
params.local_link.bandwidth = QuicBandwidth::FromKBitsPerSecond(15000);
params.test_link.bandwidth = QuicBandwidth::FromKBitsPerSecond(100);
CreateNetwork(params);
sender_endpoint_.AddBytesToTransfer(10 * 1024 * 1024);
simulator_.RunFor(QuicTime::Delta::FromSeconds(15));
EXPECT_TRUE(Bbr2ModeIsOneOf({Bbr2Mode::PROBE_BW, Bbr2Mode::PROBE_RTT}));
QUIC_LOG(INFO) << "Bandwidth increasing at time " << SimulatedNow();
EXPECT_APPROX_EQ(params.test_link.bandwidth,
sender_->ExportDebugState().bandwidth_est, 0.1f);
EXPECT_LE(sender_loss_rate_in_packets(), 0.30);
// Now increase the bottleneck bandwidth from 100Kbps to 10Mbps.
params.test_link.bandwidth = QuicBandwidth::FromKBitsPerSecond(10000);
TestLink()->set_bandwidth(params.test_link.bandwidth);
bool simulator_result = simulator_.RunUntilOrTimeout(
[this]() { return sender_endpoint_.bytes_to_transfer() == 0; },
QuicTime::Delta::FromSeconds(50));
EXPECT_TRUE(simulator_result);
// Ensure the full bandwidth is discovered.
EXPECT_APPROX_EQ(params.test_link.bandwidth,
sender_->ExportDebugState().bandwidth_hi, 0.02f);
}
// Test Bbr2's reaction to a 100x bandwidth increase during a transfer with B203
// in the presence of ACK aggregation.
TEST_F(Bbr2DefaultTopologyTest,
QUIC_SLOW_TEST(BandwidthIncreaseB203Aggregation)) {
SetQuicReloadableFlag(quic_bbr2_ignore_inflight_hi_in_probe_up, true);
SetConnectionOption(kB203);
DefaultTopologyParams params;
params.local_link.bandwidth = QuicBandwidth::FromKBitsPerSecond(15000);
params.test_link.bandwidth = QuicBandwidth::FromKBitsPerSecond(100);
CreateNetwork(params);
// 2 RTTs of aggregation, with a max of 10kb.
EnableAggregation(10 * 1024, 2 * params.RTT());
// Reduce the payload to 2MB because 10MB takes too long.
sender_endpoint_.AddBytesToTransfer(2 * 1024 * 1024);
simulator_.RunFor(QuicTime::Delta::FromSeconds(15));
EXPECT_TRUE(Bbr2ModeIsOneOf({Bbr2Mode::PROBE_BW, Bbr2Mode::PROBE_RTT}));
QUIC_LOG(INFO) << "Bandwidth increasing at time " << SimulatedNow();
// This is much farther off when aggregation is present,
// Ideally BSAO or another option would fix this.
EXPECT_APPROX_EQ(params.test_link.bandwidth,
sender_->ExportDebugState().bandwidth_est, 0.60f);
EXPECT_LE(sender_loss_rate_in_packets(), 0.35);
// Now increase the bottleneck bandwidth from 100Kbps to 10Mbps.
params.test_link.bandwidth = QuicBandwidth::FromKBitsPerSecond(10000);
TestLink()->set_bandwidth(params.test_link.bandwidth);
bool simulator_result = simulator_.RunUntilOrTimeout(
[this]() { return sender_endpoint_.bytes_to_transfer() == 0; },
QuicTime::Delta::FromSeconds(50));
EXPECT_TRUE(simulator_result);
// Ensure at least 10% of full bandwidth is discovered.
EXPECT_APPROX_EQ(params.test_link.bandwidth,
sender_->ExportDebugState().bandwidth_hi, 0.91f);
}
// Test Bbr2's reaction to a 100x bandwidth increase during a transfer with B204
TEST_F(Bbr2DefaultTopologyTest, QUIC_SLOW_TEST(BandwidthIncreaseB204)) {
SetQuicReloadableFlag(quic_bbr2_startup_extra_acked, true);
SetConnectionOption(kB204);
DefaultTopologyParams params;
params.local_link.bandwidth = QuicBandwidth::FromKBitsPerSecond(15000);
params.test_link.bandwidth = QuicBandwidth::FromKBitsPerSecond(100);
CreateNetwork(params);
sender_endpoint_.AddBytesToTransfer(10 * 1024 * 1024);
simulator_.RunFor(QuicTime::Delta::FromSeconds(15));
EXPECT_TRUE(Bbr2ModeIsOneOf({Bbr2Mode::PROBE_BW, Bbr2Mode::PROBE_RTT}));
QUIC_LOG(INFO) << "Bandwidth increasing at time " << SimulatedNow();
EXPECT_APPROX_EQ(params.test_link.bandwidth,
sender_->ExportDebugState().bandwidth_est, 0.1f);
EXPECT_LE(sender_loss_rate_in_packets(), 0.25);
EXPECT_LE(sender_->ExportDebugState().max_ack_height, 2000u);
// Now increase the bottleneck bandwidth from 100Kbps to 10Mbps.
params.test_link.bandwidth = QuicBandwidth::FromKBitsPerSecond(10000);
TestLink()->set_bandwidth(params.test_link.bandwidth);
bool simulator_result = simulator_.RunUntilOrTimeout(
[this]() { return sender_endpoint_.bytes_to_transfer() == 0; },
QuicTime::Delta::FromSeconds(50));
EXPECT_TRUE(simulator_result);
// Ensure the full bandwidth is discovered.
EXPECT_APPROX_EQ(params.test_link.bandwidth,
sender_->ExportDebugState().bandwidth_hi, 0.02f);
}
// Test Bbr2's reaction to a 100x bandwidth increase during a transfer with B204
// in the presence of ACK aggregation.
TEST_F(Bbr2DefaultTopologyTest,
QUIC_SLOW_TEST(BandwidthIncreaseB204Aggregation)) {
SetQuicReloadableFlag(quic_bbr2_startup_extra_acked, true);
SetConnectionOption(kB204);
DefaultTopologyParams params;
params.local_link.bandwidth = QuicBandwidth::FromKBitsPerSecond(15000);
params.test_link.bandwidth = QuicBandwidth::FromKBitsPerSecond(100);
CreateNetwork(params);
// 2 RTTs of aggregation, with a max of 10kb.
EnableAggregation(10 * 1024, 2 * params.RTT());
// Reduce the payload to 2MB because 10MB takes too long.
sender_endpoint_.AddBytesToTransfer(2 * 1024 * 1024);
simulator_.RunFor(QuicTime::Delta::FromSeconds(15));
EXPECT_TRUE(Bbr2ModeIsOneOf({Bbr2Mode::PROBE_BW, Bbr2Mode::PROBE_RTT}));
QUIC_LOG(INFO) << "Bandwidth increasing at time " << SimulatedNow();
// This is much farther off when aggregation is present, and B204 actually
// is increasing overestimation, which is surprising.
// Ideally BSAO or another option would fix this.
EXPECT_APPROX_EQ(params.test_link.bandwidth,
sender_->ExportDebugState().bandwidth_est, 0.60f);
EXPECT_LE(sender_loss_rate_in_packets(), 0.35);
EXPECT_LE(sender_->ExportDebugState().max_ack_height, 10000u);
// Now increase the bottleneck bandwidth from 100Kbps to 10Mbps.
params.test_link.bandwidth = QuicBandwidth::FromKBitsPerSecond(10000);
TestLink()->set_bandwidth(params.test_link.bandwidth);
bool simulator_result = simulator_.RunUntilOrTimeout(
[this]() { return sender_endpoint_.bytes_to_transfer() == 0; },
QuicTime::Delta::FromSeconds(50));
EXPECT_TRUE(simulator_result);
// Ensure at least 10% of full bandwidth is discovered.
EXPECT_APPROX_EQ(params.test_link.bandwidth,
sender_->ExportDebugState().bandwidth_hi, 0.95f);
}
// Test Bbr2's reaction to a 100x bandwidth increase during a transfer with B205
TEST_F(Bbr2DefaultTopologyTest, QUIC_SLOW_TEST(BandwidthIncreaseB205)) {
SetQuicReloadableFlag(quic_bbr2_startup_extra_acked, true);
SetConnectionOption(kB205);
DefaultTopologyParams params;
params.local_link.bandwidth = QuicBandwidth::FromKBitsPerSecond(15000);
params.test_link.bandwidth = QuicBandwidth::FromKBitsPerSecond(100);
CreateNetwork(params);
sender_endpoint_.AddBytesToTransfer(10 * 1024 * 1024);
simulator_.RunFor(QuicTime::Delta::FromSeconds(15));
EXPECT_TRUE(Bbr2ModeIsOneOf({Bbr2Mode::PROBE_BW, Bbr2Mode::PROBE_RTT}));
QUIC_LOG(INFO) << "Bandwidth increasing at time " << SimulatedNow();
EXPECT_APPROX_EQ(params.test_link.bandwidth,
sender_->ExportDebugState().bandwidth_est, 0.1f);
EXPECT_LE(sender_loss_rate_in_packets(), 0.10);
// Now increase the bottleneck bandwidth from 100Kbps to 10Mbps.
params.test_link.bandwidth = QuicBandwidth::FromKBitsPerSecond(10000);
TestLink()->set_bandwidth(params.test_link.bandwidth);
bool simulator_result = simulator_.RunUntilOrTimeout(
[this]() { return sender_endpoint_.bytes_to_transfer() == 0; },
QuicTime::Delta::FromSeconds(50));
EXPECT_TRUE(simulator_result);
// Ensure the full bandwidth is discovered.
EXPECT_APPROX_EQ(params.test_link.bandwidth,
sender_->ExportDebugState().bandwidth_hi, 0.1f);
}
// Test Bbr2's reaction to a 100x bandwidth increase during a transfer with B205
// in the presence of ACK aggregation.
TEST_F(Bbr2DefaultTopologyTest,
QUIC_SLOW_TEST(BandwidthIncreaseB205Aggregation)) {
SetQuicReloadableFlag(quic_bbr2_startup_extra_acked, true);
SetConnectionOption(kB205);
DefaultTopologyParams params;
params.local_link.bandwidth = QuicBandwidth::FromKBitsPerSecond(15000);
params.test_link.bandwidth = QuicBandwidth::FromKBitsPerSecond(100);
CreateNetwork(params);
// 2 RTTs of aggregation, with a max of 10kb.
EnableAggregation(10 * 1024, 2 * params.RTT());
// Reduce the payload to 2MB because 10MB takes too long.
sender_endpoint_.AddBytesToTransfer(2 * 1024 * 1024);
simulator_.RunFor(QuicTime::Delta::FromSeconds(15));
EXPECT_TRUE(Bbr2ModeIsOneOf({Bbr2Mode::PROBE_BW, Bbr2Mode::PROBE_RTT}));
QUIC_LOG(INFO) << "Bandwidth increasing at time " << SimulatedNow();
// This is much farther off when aggregation is present,
// Ideally BSAO or another option would fix this.
EXPECT_APPROX_EQ(params.test_link.bandwidth,
sender_->ExportDebugState().bandwidth_est, 0.45f);
EXPECT_LE(sender_loss_rate_in_packets(), 0.15);
// Now increase the bottleneck bandwidth from 100Kbps to 10Mbps.
params.test_link.bandwidth = QuicBandwidth::FromKBitsPerSecond(10000);
TestLink()->set_bandwidth(params.test_link.bandwidth);
bool simulator_result = simulator_.RunUntilOrTimeout(
[this]() { return sender_endpoint_.bytes_to_transfer() == 0; },
QuicTime::Delta::FromSeconds(50));
EXPECT_TRUE(simulator_result);
// Ensure at least 5% of full bandwidth is discovered.
EXPECT_APPROX_EQ(params.test_link.bandwidth,
sender_->ExportDebugState().bandwidth_hi, 0.9f);
}
// Test the number of losses incurred by the startup phase in a situation when
// the buffer is less than BDP.
TEST_F(Bbr2DefaultTopologyTest, PacketLossOnSmallBufferStartup) {
DefaultTopologyParams params;
params.switch_queue_capacity_in_bdp = 0.5;
CreateNetwork(params);
DriveOutOfStartup(params);
// Packet loss is smaller with a CWND gain of 2 than 2.889.
EXPECT_LE(sender_loss_rate_in_packets(), 0.05);
}
// Test the number of losses decreases with packet-conservation pacing.
TEST_F(Bbr2DefaultTopologyTest, PacketLossBBQ6SmallBufferStartup) {
SetConnectionOption(kBBQ2); // Increase CWND gain.
SetConnectionOption(kBBQ6);
DefaultTopologyParams params;
params.switch_queue_capacity_in_bdp = 0.5;
CreateNetwork(params);
DriveOutOfStartup(params);
EXPECT_LE(sender_loss_rate_in_packets(), 0.0575);
// bandwidth_lo is cleared exiting STARTUP.
EXPECT_EQ(sender_->ExportDebugState().bandwidth_lo,
QuicBandwidth::Infinite());
}
// Test the number of losses decreases with min_rtt packet-conservation pacing.
TEST_F(Bbr2DefaultTopologyTest, PacketLossBBQ7SmallBufferStartup) {
SetConnectionOption(kBBQ2); // Increase CWND gain.
SetConnectionOption(kBBQ7);
DefaultTopologyParams params;
params.switch_queue_capacity_in_bdp = 0.5;
CreateNetwork(params);
DriveOutOfStartup(params);
EXPECT_LE(sender_loss_rate_in_packets(), 0.06);
// bandwidth_lo is cleared exiting STARTUP.
EXPECT_EQ(sender_->ExportDebugState().bandwidth_lo,
QuicBandwidth::Infinite());
}
// Test the number of losses decreases with Inflight packet-conservation pacing.
TEST_F(Bbr2DefaultTopologyTest, PacketLossBBQ8SmallBufferStartup) {
SetConnectionOption(kBBQ2); // Increase CWND gain.
SetConnectionOption(kBBQ8);
DefaultTopologyParams params;
params.switch_queue_capacity_in_bdp = 0.5;
CreateNetwork(params);
DriveOutOfStartup(params);
EXPECT_LE(sender_loss_rate_in_packets(), 0.065);
// bandwidth_lo is cleared exiting STARTUP.
EXPECT_EQ(sender_->ExportDebugState().bandwidth_lo,
QuicBandwidth::Infinite());
}
// Test the number of losses decreases with CWND packet-conservation pacing.
TEST_F(Bbr2DefaultTopologyTest, PacketLossBBQ9SmallBufferStartup) {
SetConnectionOption(kBBQ2); // Increase CWND gain.
SetConnectionOption(kBBQ9);
DefaultTopologyParams params;
params.switch_queue_capacity_in_bdp = 0.5;
CreateNetwork(params);
DriveOutOfStartup(params);
EXPECT_LE(sender_loss_rate_in_packets(), 0.065);
// bandwidth_lo is cleared exiting STARTUP.
EXPECT_EQ(sender_->ExportDebugState().bandwidth_lo,
QuicBandwidth::Infinite());
}
// Verify the behavior of the algorithm in the case when the connection sends
// small bursts of data after sending continuously for a while.
TEST_F(Bbr2DefaultTopologyTest, ApplicationLimitedBursts) {
DefaultTopologyParams params;
CreateNetwork(params);
DriveOutOfStartup(params);
EXPECT_FALSE(sender_->ExportDebugState().last_sample_is_app_limited);
SendBursts(params, 20, 512, QuicTime::Delta::FromSeconds(3));
EXPECT_TRUE(sender_->ExportDebugState().last_sample_is_app_limited);
EXPECT_APPROX_EQ(params.BottleneckBandwidth(),
sender_->ExportDebugState().bandwidth_hi, 0.01f);
}
// Verify the behavior of the algorithm in the case when the connection sends
// small bursts of data and then starts sending continuously.
TEST_F(Bbr2DefaultTopologyTest, ApplicationLimitedBurstsWithoutPrior) {
DefaultTopologyParams params;
CreateNetwork(params);
SendBursts(params, 40, 512, QuicTime::Delta::FromSeconds(3));
EXPECT_TRUE(sender_->ExportDebugState().last_sample_is_app_limited);
DriveOutOfStartup(params);
EXPECT_APPROX_EQ(params.BottleneckBandwidth(),
sender_->ExportDebugState().bandwidth_hi, 0.01f);
EXPECT_FALSE(sender_->ExportDebugState().last_sample_is_app_limited);
}
// Verify that the DRAIN phase works correctly.
TEST_F(Bbr2DefaultTopologyTest, Drain) {
DefaultTopologyParams params;
CreateNetwork(params);
const QuicTime::Delta timeout = QuicTime::Delta::FromSeconds(10);
// Get the queue at the bottleneck, which is the outgoing queue at the port to
// which the receiver is connected.
const simulator::Queue* queue = switch_->port_queue(2);
bool simulator_result;
// We have no intention of ever finishing this transfer.
sender_endpoint_.AddBytesToTransfer(100 * 1024 * 1024);
// Run the startup, and verify that it fills up the queue.
ASSERT_EQ(Bbr2Mode::STARTUP, sender_->ExportDebugState().mode);
simulator_result = simulator_.RunUntilOrTimeout(
[this]() {
return sender_->ExportDebugState().mode != Bbr2Mode::STARTUP;
},
timeout);
ASSERT_TRUE(simulator_result);
ASSERT_EQ(Bbr2Mode::DRAIN, sender_->ExportDebugState().mode);
EXPECT_APPROX_EQ(sender_->BandwidthEstimate() * (1 / 2.885f),
sender_->PacingRate(0), 0.01f);
// BBR uses CWND gain of 2 during STARTUP, hence it will fill the buffer with
// approximately 1 BDP. Here, we use 0.95 to give some margin for error.
EXPECT_GE(queue->bytes_queued(), 0.95 * params.BDP());
// Observe increased RTT due to bufferbloat.
const QuicTime::Delta queueing_delay =
params.test_link.bandwidth.TransferTime(queue->bytes_queued());
EXPECT_APPROX_EQ(params.RTT() + queueing_delay, rtt_stats()->latest_rtt(),
0.1f);
// Transition to the drain phase and verify that it makes the queue
// have at most a BDP worth of packets.
simulator_result = simulator_.RunUntilOrTimeout(
[this]() { return sender_->ExportDebugState().mode != Bbr2Mode::DRAIN; },
timeout);
ASSERT_TRUE(simulator_result);
ASSERT_EQ(Bbr2Mode::PROBE_BW, sender_->ExportDebugState().mode);
EXPECT_LE(queue->bytes_queued(), params.BDP());
// Wait for a few round trips and ensure we're in appropriate phase of gain
// cycling before taking an RTT measurement.
const QuicRoundTripCount start_round_trip =
sender_->ExportDebugState().round_trip_count;
simulator_result = simulator_.RunUntilOrTimeout(
[this, start_round_trip]() {
const auto& debug_state = sender_->ExportDebugState();
QuicRoundTripCount rounds_passed =
debug_state.round_trip_count - start_round_trip;
return rounds_passed >= 4 && debug_state.mode == Bbr2Mode::PROBE_BW &&
debug_state.probe_bw.phase == CyclePhase::PROBE_REFILL;
},
timeout);
ASSERT_TRUE(simulator_result);
// Observe the bufferbloat go away.
EXPECT_APPROX_EQ(params.RTT(), rtt_stats()->smoothed_rtt(), 0.1f);
}
// Ensure that a connection that is app-limited and is at sufficiently low
// bandwidth will not exit high gain phase, and similarly ensure that the
// connection will exit low gain early if the number of bytes in flight is low.
TEST_F(Bbr2DefaultTopologyTest, InFlightAwareGainCycling) {
DefaultTopologyParams params;
CreateNetwork(params);
DriveOutOfStartup(params);
const QuicTime::Delta timeout = QuicTime::Delta::FromSeconds(5);
bool simulator_result;
// Start a few cycles prior to the high gain one.
simulator_result = SendUntilOrTimeout(
[this]() {
return sender_->ExportDebugState().probe_bw.phase ==
CyclePhase::PROBE_REFILL;
},
timeout);
ASSERT_TRUE(simulator_result);
// Send at 10% of available rate. Run for 3 seconds, checking in the middle
// and at the end. The pacing gain should be high throughout.
QuicBandwidth target_bandwidth = 0.1f * params.BottleneckBandwidth();
QuicTime::Delta burst_interval = QuicTime::Delta::FromMilliseconds(300);
for (int i = 0; i < 2; i++) {
SendBursts(params, 5, target_bandwidth * burst_interval, burst_interval);
EXPECT_EQ(Bbr2Mode::PROBE_BW, sender_->ExportDebugState().mode);
EXPECT_EQ(CyclePhase::PROBE_UP, sender_->ExportDebugState().probe_bw.phase);
EXPECT_APPROX_EQ(params.BottleneckBandwidth(),
sender_->ExportDebugState().bandwidth_hi, 0.02f);
}
// Now that in-flight is almost zero and the pacing gain is still above 1,
// send approximately 1.4 BDPs worth of data. This should cause the PROBE_BW
// mode to enter low gain cycle(PROBE_DOWN), and exit it earlier than one
// min_rtt due to running out of data to send.
sender_endpoint_.AddBytesToTransfer(1.4 * params.BDP());
simulator_result = simulator_.RunUntilOrTimeout(
[this]() {
return sender_->ExportDebugState().probe_bw.phase ==
CyclePhase::PROBE_DOWN;
},
timeout);
ASSERT_TRUE(simulator_result);
simulator_.RunFor(0.75 * sender_->ExportDebugState().min_rtt);
EXPECT_EQ(Bbr2Mode::PROBE_BW, sender_->ExportDebugState().mode);
EXPECT_EQ(CyclePhase::PROBE_CRUISE,
sender_->ExportDebugState().probe_bw.phase);
}
// Test exiting STARTUP earlier upon loss due to loss.
TEST_F(Bbr2DefaultTopologyTest, ExitStartupDueToLoss) {
DefaultTopologyParams params;
params.switch_queue_capacity_in_bdp = 0.5;
CreateNetwork(params);
// Run until the full bandwidth is reached and check how many rounds it was.
sender_endpoint_.AddBytesToTransfer(12 * 1024 * 1024);
QuicRoundTripCount max_bw_round = 0;
QuicBandwidth max_bw(QuicBandwidth::Zero());
bool simulator_result = simulator_.RunUntilOrTimeout(
[this, &max_bw, &max_bw_round]() {
if (max_bw < sender_->ExportDebugState().bandwidth_hi) {
max_bw = sender_->ExportDebugState().bandwidth_hi;
max_bw_round = sender_->ExportDebugState().round_trip_count;
}
return sender_->ExportDebugState().startup.full_bandwidth_reached;
},
QuicTime::Delta::FromSeconds(5));
ASSERT_TRUE(simulator_result);
EXPECT_EQ(Bbr2Mode::DRAIN, sender_->ExportDebugState().mode);
EXPECT_GE(2u, sender_->ExportDebugState().round_trip_count - max_bw_round);
EXPECT_EQ(
1u,
sender_->ExportDebugState().startup.round_trips_without_bandwidth_growth);
EXPECT_NE(0u, sender_connection_stats().packets_lost);
EXPECT_FALSE(sender_->ExportDebugState().last_sample_is_app_limited);
EXPECT_GT(sender_->ExportDebugState().inflight_hi, 1.2f * params.BDP());
}
// Test exiting STARTUP earlier upon loss due to loss when connection option
// B2SL is used.
TEST_F(Bbr2DefaultTopologyTest, ExitStartupDueToLossB2SL) {
SetConnectionOption(kB2SL);
DefaultTopologyParams params;
params.switch_queue_capacity_in_bdp = 0.5;
CreateNetwork(params);
// Run until the full bandwidth is reached and check how many rounds it was.
sender_endpoint_.AddBytesToTransfer(12 * 1024 * 1024);
QuicRoundTripCount max_bw_round = 0;
QuicBandwidth max_bw(QuicBandwidth::Zero());
bool simulator_result = simulator_.RunUntilOrTimeout(
[this, &max_bw, &max_bw_round]() {
if (max_bw < sender_->ExportDebugState().bandwidth_hi) {
max_bw = sender_->ExportDebugState().bandwidth_hi;
max_bw_round = sender_->ExportDebugState().round_trip_count;
}
return sender_->ExportDebugState().startup.full_bandwidth_reached;
},
QuicTime::Delta::FromSeconds(5));
ASSERT_TRUE(simulator_result);
EXPECT_EQ(Bbr2Mode::DRAIN, sender_->ExportDebugState().mode);
EXPECT_GE(2u, sender_->ExportDebugState().round_trip_count - max_bw_round);
EXPECT_EQ(
1u,
sender_->ExportDebugState().startup.round_trips_without_bandwidth_growth);
EXPECT_NE(0u, sender_connection_stats().packets_lost);
EXPECT_FALSE(sender_->ExportDebugState().last_sample_is_app_limited);
EXPECT_APPROX_EQ(sender_->ExportDebugState().inflight_hi, params.BDP(), 0.1f);
}
// Verifies that in STARTUP, if we exceed loss threshold in a round, we exit
// STARTUP at the end of the round even if there's enough bandwidth growth.
TEST_F(Bbr2DefaultTopologyTest, ExitStartupDueToLossB2NE) {
// Set up flags such that any loss will be considered "too high".
SetQuicFlag(FLAGS_quic_bbr2_default_startup_full_loss_count, 0);
SetQuicFlag(FLAGS_quic_bbr2_default_loss_threshold, 0.0);
sender_ = SetupBbr2Sender(&sender_endpoint_, /*old_sender=*/nullptr);
SetConnectionOption(kB2NE);
DefaultTopologyParams params;
params.switch_queue_capacity_in_bdp = 0.5;
CreateNetwork(params);
// Run until the full bandwidth is reached and check how many rounds it was.
sender_endpoint_.AddBytesToTransfer(12 * 1024 * 1024);
QuicRoundTripCount max_bw_round = 0;
QuicBandwidth max_bw(QuicBandwidth::Zero());
bool simulator_result = simulator_.RunUntilOrTimeout(
[this, &max_bw, &max_bw_round]() {
if (max_bw < sender_->ExportDebugState().bandwidth_hi) {
max_bw = sender_->ExportDebugState().bandwidth_hi;
max_bw_round = sender_->ExportDebugState().round_trip_count;
}
return sender_->ExportDebugState().startup.full_bandwidth_reached;
},
QuicTime::Delta::FromSeconds(5));
ASSERT_TRUE(simulator_result);
EXPECT_EQ(Bbr2Mode::DRAIN, sender_->ExportDebugState().mode);
EXPECT_EQ(sender_->ExportDebugState().round_trip_count, max_bw_round);
EXPECT_EQ(
0u,
sender_->ExportDebugState().startup.round_trips_without_bandwidth_growth);
EXPECT_NE(0u, sender_connection_stats().packets_lost);
}
TEST_F(Bbr2DefaultTopologyTest, SenderPoliced) {
DefaultTopologyParams params;
params.sender_policer_params = TrafficPolicerParams();
params.sender_policer_params->initial_burst_size = 1000 * 10;
params.sender_policer_params->max_bucket_size = 1000 * 100;
params.sender_policer_params->target_bandwidth =
params.BottleneckBandwidth() * 0.25;
CreateNetwork(params);
ASSERT_GE(params.BDP(), kDefaultInitialCwndBytes + kDefaultTCPMSS);
DoSimpleTransfer(3 * 1024 * 1024, QuicTime::Delta::FromSeconds(30));
EXPECT_TRUE(Bbr2ModeIsOneOf({Bbr2Mode::PROBE_BW, Bbr2Mode::PROBE_RTT}));
// TODO(wub): Fix (long-term) bandwidth overestimation in policer mode, then
// reduce the loss rate upper bound.
EXPECT_LE(sender_loss_rate_in_packets(), 0.30);
}
// TODO(wub): Add other slowstart stats to BBRv2.
TEST_F(Bbr2DefaultTopologyTest, StartupStats) {
DefaultTopologyParams params;
CreateNetwork(params);
DriveOutOfStartup(params);
ASSERT_FALSE(sender_->InSlowStart());
const QuicConnectionStats& stats = sender_connection_stats();
// The test explicitly replaces the default-created send algorithm with the
// one created by the test. slowstart_count increaments every time a BBR
// sender is created.
EXPECT_GE(stats.slowstart_count, 1u);
EXPECT_FALSE(stats.slowstart_duration.IsRunning());
EXPECT_THAT(stats.slowstart_duration.GetTotalElapsedTime(),
AllOf(Ge(QuicTime::Delta::FromMilliseconds(500)),
Le(QuicTime::Delta::FromMilliseconds(1500))));
EXPECT_EQ(stats.slowstart_duration.GetTotalElapsedTime(),
QuicConnectionPeer::GetSentPacketManager(sender_connection())
->GetSlowStartDuration());
}
TEST_F(Bbr2DefaultTopologyTest, ProbeUpAdaptInflightHiGradually) {
DefaultTopologyParams params;
CreateNetwork(params);
DriveOutOfStartup(params);
AckedPacketVector acked_packets;
QuicPacketNumber acked_packet_number =
sender_unacked_map()->GetLeastUnacked();
for (auto& info : *sender_unacked_map()) {
acked_packets.emplace_back(acked_packet_number++, info.bytes_sent,
SimulatedNow());
}
// Advance time significantly so the OnCongestionEvent enters PROBE_REFILL.
QuicTime now = SimulatedNow() + QuicTime::Delta::FromSeconds(5);
auto next_packet_number = sender_unacked_map()->largest_sent_packet() + 1;
sender_->OnCongestionEvent(
/*rtt_updated=*/true, sender_unacked_map()->bytes_in_flight(), now,
acked_packets, {});
ASSERT_EQ(CyclePhase::PROBE_REFILL,
sender_->ExportDebugState().probe_bw.phase);
// Send and Ack one packet to exit app limited and enter PROBE_UP.
sender_->OnPacketSent(now, /*bytes_in_flight=*/0, next_packet_number++,
kDefaultMaxPacketSize, HAS_RETRANSMITTABLE_DATA);
now = now + params.RTT();
sender_->OnCongestionEvent(
/*rtt_updated=*/true, kDefaultMaxPacketSize, now,
{AckedPacket(next_packet_number - 1, kDefaultMaxPacketSize, now)}, {});
ASSERT_EQ(CyclePhase::PROBE_UP, sender_->ExportDebugState().probe_bw.phase);
// Send 2 packets and lose the first one(50% loss) to exit PROBE_UP.
for (uint64_t i = 0; i < 2; ++i) {
sender_->OnPacketSent(now, /*bytes_in_flight=*/i * kDefaultMaxPacketSize,
next_packet_number++, kDefaultMaxPacketSize,
HAS_RETRANSMITTABLE_DATA);
}
now = now + params.RTT();
sender_->OnCongestionEvent(
/*rtt_updated=*/true, kDefaultMaxPacketSize, now,
{AckedPacket(next_packet_number - 1, kDefaultMaxPacketSize, now)},
{LostPacket(next_packet_number - 2, kDefaultMaxPacketSize)});
QuicByteCount inflight_hi = sender_->ExportDebugState().inflight_hi;
EXPECT_LT(2 * kDefaultMaxPacketSize, inflight_hi);
}
// Ensures bandwidth estimate does not change after a loss only event.
TEST_F(Bbr2DefaultTopologyTest, LossOnlyCongestionEvent) {
DefaultTopologyParams params;
CreateNetwork(params);
DriveOutOfStartup(params);
EXPECT_FALSE(sender_->ExportDebugState().last_sample_is_app_limited);
// Send some bursts, each burst increments round count by 1, since it only
// generates small, app-limited samples, the max_bandwidth_filter_ will not be
// updated.
SendBursts(params, 20, 512, QuicTime::Delta::FromSeconds(3));
// Run until we have something in flight.
sender_endpoint_.AddBytesToTransfer(50 * 1024 * 1024);
bool simulator_result = simulator_.RunUntilOrTimeout(
[&]() { return sender_unacked_map()->bytes_in_flight() > 0; },
QuicTime::Delta::FromSeconds(5));
ASSERT_TRUE(simulator_result);
const QuicBandwidth prior_bandwidth_estimate = sender_->BandwidthEstimate();
EXPECT_APPROX_EQ(params.BottleneckBandwidth(), prior_bandwidth_estimate,
0.01f);
// Lose the least unacked packet.
LostPacketVector lost_packets;
lost_packets.emplace_back(
sender_connection()->sent_packet_manager().GetLeastUnacked(),
kDefaultMaxPacketSize);
QuicTime now = simulator_.GetClock()->Now() + params.RTT() * 0.25;
sender_->OnCongestionEvent(false, sender_unacked_map()->bytes_in_flight(),
now, {}, lost_packets);
// Bandwidth estimate should not change for the loss only event.
EXPECT_EQ(prior_bandwidth_estimate, sender_->BandwidthEstimate());
}
// After quiescence, if the sender is in PROBE_RTT, it should transition to
// PROBE_BW immediately on the first sent packet after quiescence.
TEST_F(Bbr2DefaultTopologyTest, ProbeRttAfterQuiescenceImmediatelyExits) {
DefaultTopologyParams params;
CreateNetwork(params);
DriveOutOfStartup(params);
const QuicTime::Delta timeout = QuicTime::Delta::FromSeconds(15);
bool simulator_result;
// Keep sending until reach PROBE_RTT.
simulator_result = SendUntilOrTimeout(
[this]() {
return sender_->ExportDebugState().mode == Bbr2Mode::PROBE_RTT;
},
timeout);
ASSERT_TRUE(simulator_result);
// Wait for entering a quiescence of 5 seconds.
ASSERT_TRUE(simulator_.RunUntilOrTimeout(
[this]() {
return sender_unacked_map()->bytes_in_flight() == 0 &&
sender_->ExportDebugState().mode == Bbr2Mode::PROBE_RTT;
},
timeout));
simulator_.RunFor(QuicTime::Delta::FromSeconds(5));
// Send one packet to exit quiescence.
EXPECT_EQ(sender_->ExportDebugState().mode, Bbr2Mode::PROBE_RTT);
sender_->OnPacketSent(SimulatedNow(), /*bytes_in_flight=*/0,
sender_unacked_map()->largest_sent_packet() + 1,
kDefaultMaxPacketSize, HAS_RETRANSMITTABLE_DATA);
EXPECT_EQ(sender_->ExportDebugState().mode, Bbr2Mode::PROBE_BW);
}
TEST_F(Bbr2DefaultTopologyTest, ProbeBwAfterQuiescencePostponeMinRttTimestamp) {
DefaultTopologyParams params;
CreateNetwork(params);
DriveOutOfStartup(params);
const QuicTime::Delta timeout = QuicTime::Delta::FromSeconds(5);
bool simulator_result;
// Keep sending until reach PROBE_REFILL.
simulator_result = SendUntilOrTimeout(
[this]() {
return sender_->ExportDebugState().probe_bw.phase ==
CyclePhase::PROBE_REFILL;
},
timeout);
ASSERT_TRUE(simulator_result);
const QuicTime min_rtt_timestamp_before_idle =
sender_->ExportDebugState().min_rtt_timestamp;
// Wait for entering a quiescence of 15 seconds.
ASSERT_TRUE(simulator_.RunUntilOrTimeout(
[this]() { return sender_unacked_map()->bytes_in_flight() == 0; },
params.RTT() + timeout));
simulator_.RunFor(QuicTime::Delta::FromSeconds(15));
// Send some data to exit quiescence.
SendBursts(params, 1, kDefaultTCPMSS, QuicTime::Delta::Zero());
const QuicTime min_rtt_timestamp_after_idle =
sender_->ExportDebugState().min_rtt_timestamp;
EXPECT_LT(min_rtt_timestamp_before_idle + QuicTime::Delta::FromSeconds(14),
min_rtt_timestamp_after_idle);
}
TEST_F(Bbr2DefaultTopologyTest, SwitchToBbr2MidConnection) {
QuicTime now = QuicTime::Zero();
BbrSender old_sender(sender_connection()->clock()->Now(),
sender_connection()->sent_packet_manager().GetRttStats(),
GetUnackedMap(sender_connection()),
kDefaultInitialCwndPackets + 1,
GetQuicFlag(FLAGS_quic_max_congestion_window), &random_,
QuicConnectionPeer::GetStats(sender_connection()));
QuicPacketNumber next_packet_number(1);
// Send packets 1-4.
while (next_packet_number < QuicPacketNumber(5)) {
now = now + QuicTime::Delta::FromMilliseconds(10);
old_sender.OnPacketSent(now, /*bytes_in_flight=*/0, next_packet_number++,
/*bytes=*/1350, HAS_RETRANSMITTABLE_DATA);
}
// Switch from |old_sender| to |sender_|.
const QuicByteCount old_sender_cwnd = old_sender.GetCongestionWindow();
sender_ = SetupBbr2Sender(&sender_endpoint_, &old_sender);
EXPECT_EQ(old_sender_cwnd, sender_->GetCongestionWindow());
// Send packets 5-7.
now = now + QuicTime::Delta::FromMilliseconds(10);
sender_->OnPacketSent(now, /*bytes_in_flight=*/1350, next_packet_number++,
/*bytes=*/23, NO_RETRANSMITTABLE_DATA);
now = now + QuicTime::Delta::FromMilliseconds(10);
sender_->OnPacketSent(now, /*bytes_in_flight=*/1350, next_packet_number++,
/*bytes=*/767, HAS_RETRANSMITTABLE_DATA);
QuicByteCount bytes_in_flight = 767;
while (next_packet_number < QuicPacketNumber(30)) {
now = now + QuicTime::Delta::FromMilliseconds(10);
bytes_in_flight += 1350;
sender_->OnPacketSent(now, bytes_in_flight, next_packet_number++,
/*bytes=*/1350, HAS_RETRANSMITTABLE_DATA);
}
// Ack 1 & 2.
AckedPacketVector acked = {
AckedPacket(QuicPacketNumber(1), /*bytes_acked=*/0, QuicTime::Zero()),
AckedPacket(QuicPacketNumber(2), /*bytes_acked=*/0, QuicTime::Zero()),
};
now = now + QuicTime::Delta::FromMilliseconds(2000);
sender_->OnCongestionEvent(true, bytes_in_flight, now, acked, {});
// Send 30-41.
while (next_packet_number < QuicPacketNumber(42)) {
now = now + QuicTime::Delta::FromMilliseconds(10);
bytes_in_flight += 1350;
sender_->OnPacketSent(now, bytes_in_flight, next_packet_number++,
/*bytes=*/1350, HAS_RETRANSMITTABLE_DATA);
}
// Ack 3.
acked = {
AckedPacket(QuicPacketNumber(3), /*bytes_acked=*/0, QuicTime::Zero()),
};
now = now + QuicTime::Delta::FromMilliseconds(2000);
sender_->OnCongestionEvent(true, bytes_in_flight, now, acked, {});
// Send 42.
now = now + QuicTime::Delta::FromMilliseconds(10);
bytes_in_flight += 1350;
sender_->OnPacketSent(now, bytes_in_flight, next_packet_number++,
/*bytes=*/1350, HAS_RETRANSMITTABLE_DATA);
// Ack 4-7.
acked = {
AckedPacket(QuicPacketNumber(4), /*bytes_acked=*/0, QuicTime::Zero()),
AckedPacket(QuicPacketNumber(5), /*bytes_acked=*/0, QuicTime::Zero()),
AckedPacket(QuicPacketNumber(6), /*bytes_acked=*/767, QuicTime::Zero()),
AckedPacket(QuicPacketNumber(7), /*bytes_acked=*/1350, QuicTime::Zero()),
};
now = now + QuicTime::Delta::FromMilliseconds(2000);
sender_->OnCongestionEvent(true, bytes_in_flight, now, acked, {});
EXPECT_FALSE(sender_->BandwidthEstimate().IsZero());
}
TEST_F(Bbr2DefaultTopologyTest, AdjustNetworkParameters) {
DefaultTopologyParams params;
CreateNetwork(params);
QUIC_LOG(INFO) << "Initial cwnd: " << sender_debug_state().congestion_window
<< "\nInitial pacing rate: " << sender_->PacingRate(0)
<< "\nInitial bandwidth estimate: "
<< sender_->BandwidthEstimate()
<< "\nInitial rtt: " << sender_debug_state().min_rtt;
sender_connection()->AdjustNetworkParameters(
SendAlgorithmInterface::NetworkParams(params.BottleneckBandwidth(),
params.RTT(),
/*allow_cwnd_to_decrease=*/false));
EXPECT_EQ(params.BDP(), sender_->ExportDebugState().congestion_window);
EXPECT_EQ(params.BottleneckBandwidth(),
sender_->PacingRate(/*bytes_in_flight=*/0));
EXPECT_NE(params.BottleneckBandwidth(), sender_->BandwidthEstimate());
EXPECT_APPROX_EQ(params.RTT(), sender_->ExportDebugState().min_rtt, 0.01f);
DriveOutOfStartup(params);
}
TEST_F(Bbr2DefaultTopologyTest,
200InitialCongestionWindowWithNetworkParameterAdjusted) {
DefaultTopologyParams params;
CreateNetwork(params);
sender_endpoint_.AddBytesToTransfer(1 * 1024 * 1024);
// Wait until an ACK comes back.
const QuicTime::Delta timeout = QuicTime::Delta::FromSeconds(5);
bool simulator_result = simulator_.RunUntilOrTimeout(
[this]() { return !sender_->ExportDebugState().min_rtt.IsZero(); },
timeout);
ASSERT_TRUE(simulator_result);
// Bootstrap cwnd by a overly large bandwidth sample.
sender_connection()->AdjustNetworkParameters(
SendAlgorithmInterface::NetworkParams(1024 * params.BottleneckBandwidth(),
QuicTime::Delta::Zero(), false));
// Verify cwnd is capped at 200.
EXPECT_EQ(200 * kDefaultTCPMSS,
sender_->ExportDebugState().congestion_window);
EXPECT_GT(1024 * params.BottleneckBandwidth(), sender_->PacingRate(0));
}
TEST_F(Bbr2DefaultTopologyTest,
100InitialCongestionWindowFromNetworkParameter) {
DefaultTopologyParams params;
CreateNetwork(params);
sender_endpoint_.AddBytesToTransfer(1 * 1024 * 1024);
// Wait until an ACK comes back.
const QuicTime::Delta timeout = QuicTime::Delta::FromSeconds(5);
bool simulator_result = simulator_.RunUntilOrTimeout(
[this]() { return !sender_->ExportDebugState().min_rtt.IsZero(); },
timeout);
ASSERT_TRUE(simulator_result);
// Bootstrap cwnd by a overly large bandwidth sample.
SendAlgorithmInterface::NetworkParams network_params(
1024 * params.BottleneckBandwidth(), QuicTime::Delta::Zero(), false);
network_params.max_initial_congestion_window = 100;
sender_connection()->AdjustNetworkParameters(network_params);
// Verify cwnd is capped at 100.
EXPECT_EQ(100 * kDefaultTCPMSS,
sender_->ExportDebugState().congestion_window);
EXPECT_GT(1024 * params.BottleneckBandwidth(), sender_->PacingRate(0));
}
TEST_F(Bbr2DefaultTopologyTest,
100InitialCongestionWindowWithNetworkParameterAdjusted) {
SetConnectionOption(kICW1);
DefaultTopologyParams params;
CreateNetwork(params);
sender_endpoint_.AddBytesToTransfer(1 * 1024 * 1024);
// Wait until an ACK comes back.
const QuicTime::Delta timeout = QuicTime::Delta::FromSeconds(5);
bool simulator_result = simulator_.RunUntilOrTimeout(
[this]() { return !sender_->ExportDebugState().min_rtt.IsZero(); },
timeout);
ASSERT_TRUE(simulator_result);
// Bootstrap cwnd by a overly large bandwidth sample.
sender_connection()->AdjustNetworkParameters(
SendAlgorithmInterface::NetworkParams(1024 * params.BottleneckBandwidth(),
QuicTime::Delta::Zero(), false));
// Verify cwnd is capped at 100.
EXPECT_EQ(100 * kDefaultTCPMSS,
sender_->ExportDebugState().congestion_window);
EXPECT_GT(1024 * params.BottleneckBandwidth(), sender_->PacingRate(0));
}
// All Bbr2MultiSenderTests uses the following network topology:
//
// Sender 0 (A Bbr2Sender)
// |
// | <-- local_links[0]
// |
// | Sender N (1 <= N < kNumLocalLinks) (May or may not be a Bbr2Sender)
// | |
// | | <-- local_links[N]
// | |
// Network switch
// * <-- the bottleneck queue in the direction
// | of the receiver
// |
// | <-- test_link
// |
// |
// Receiver
class MultiSenderTopologyParams {
public:
static constexpr size_t kNumLocalLinks = 8;
std::array<LinkParams, kNumLocalLinks> local_links = {
LinkParams(10000, 1987), LinkParams(10000, 1993), LinkParams(10000, 1997),
LinkParams(10000, 1999), LinkParams(10000, 2003), LinkParams(10000, 2011),
LinkParams(10000, 2017), LinkParams(10000, 2027),
};
LinkParams test_link = LinkParams(4000, 30000);
const simulator::SwitchPortNumber switch_port_count = kNumLocalLinks + 1;
// Network switch queue capacity, in number of BDPs.
float switch_queue_capacity_in_bdp = 2;
QuicBandwidth BottleneckBandwidth() const {
// Make sure all local links have a higher bandwidth than the test link.
for (size_t i = 0; i < local_links.size(); ++i) {
QUICHE_CHECK_GT(local_links[i].bandwidth, test_link.bandwidth);
}
return test_link.bandwidth;
}
// Sender n's round trip time of a single full size packet.
QuicTime::Delta Rtt(size_t n) const {
return 2 * (local_links[n].delay + test_link.delay +
local_links[n].bandwidth.TransferTime(kMaxOutgoingPacketSize) +
test_link.bandwidth.TransferTime(kMaxOutgoingPacketSize));
}
QuicByteCount Bdp(size_t n) const { return BottleneckBandwidth() * Rtt(n); }
QuicByteCount SwitchQueueCapacity() const {
return switch_queue_capacity_in_bdp * Bdp(1);
}
std::string ToString() const {
std::ostringstream os;
os << "{ BottleneckBandwidth: " << BottleneckBandwidth();
for (size_t i = 0; i < local_links.size(); ++i) {
os << " RTT_" << i << ": " << Rtt(i) << " BDP_" << i << ": " << Bdp(i);
}
os << " BottleneckQueueSize: " << SwitchQueueCapacity() << "}";
return os.str();
}
};
class Bbr2MultiSenderTest : public Bbr2SimulatorTest {
protected:
Bbr2MultiSenderTest() {
uint64_t first_connection_id = 42;
std::vector<simulator::QuicEndpointBase*> receiver_endpoint_pointers;
for (size_t i = 0; i < MultiSenderTopologyParams::kNumLocalLinks; ++i) {
std::string sender_name = absl::StrCat("Sender", i + 1);
std::string receiver_name = absl::StrCat("Receiver", i + 1);
sender_endpoints_.push_back(std::make_unique<simulator::QuicEndpoint>(
&simulator_, sender_name, receiver_name, Perspective::IS_CLIENT,
TestConnectionId(first_connection_id + i)));
receiver_endpoints_.push_back(std::make_unique<simulator::QuicEndpoint>(
&simulator_, receiver_name, sender_name, Perspective::IS_SERVER,
TestConnectionId(first_connection_id + i)));
receiver_endpoint_pointers.push_back(receiver_endpoints_.back().get());
}
receiver_multiplexer_ =
std::make_unique<simulator::QuicEndpointMultiplexer>(
"Receiver multiplexer", receiver_endpoint_pointers);
sender_0_ = SetupBbr2Sender(sender_endpoints_[0].get());
}
~Bbr2MultiSenderTest() {
const auto* test_info =
::testing::UnitTest::GetInstance()->current_test_info();
QUIC_LOG(INFO) << "Bbr2MultiSenderTest." << test_info->name()
<< " completed at simulated time: "
<< SimulatedNow().ToDebuggingValue() / 1e6
<< " sec. Per sender stats:";
for (size_t i = 0; i < sender_endpoints_.size(); ++i) {
QUIC_LOG(INFO) << "sender[" << i << "]: "
<< sender_connection(i)
->sent_packet_manager()
.GetSendAlgorithm()
->GetCongestionControlType()
<< ", packet_loss:"
<< 100.0 * sender_loss_rate_in_packets(i) << "%";
}
}
Bbr2Sender* SetupBbr2Sender(simulator::QuicEndpoint* endpoint) {
// Ownership of the sender will be overtaken by the endpoint.
Bbr2Sender* sender = new Bbr2Sender(
endpoint->connection()->clock()->Now(),
endpoint->connection()->sent_packet_manager().GetRttStats(),
QuicSentPacketManagerPeer::GetUnackedPacketMap(
QuicConnectionPeer::GetSentPacketManager(endpoint->connection())),
kDefaultInitialCwndPackets,
GetQuicFlag(FLAGS_quic_max_congestion_window), &random_,
QuicConnectionPeer::GetStats(endpoint->connection()), nullptr);
// TODO(ianswett): Add dedicated tests for this option until it becomes
// the default behavior.
SetConnectionOption(sender, kBBRA);
QuicConnectionPeer::SetSendAlgorithm(endpoint->connection(), sender);
endpoint->RecordTrace();
return sender;
}
BbrSender* SetupBbrSender(simulator::QuicEndpoint* endpoint) {
// Ownership of the sender will be overtaken by the endpoint.
BbrSender* sender = new BbrSender(
endpoint->connection()->clock()->Now(),
endpoint->connection()->sent_packet_manager().GetRttStats(),
QuicSentPacketManagerPeer::GetUnackedPacketMap(
QuicConnectionPeer::GetSentPacketManager(endpoint->connection())),
kDefaultInitialCwndPackets,
GetQuicFlag(FLAGS_quic_max_congestion_window), &random_,
QuicConnectionPeer::GetStats(endpoint->connection()));
QuicConnectionPeer::SetSendAlgorithm(endpoint->connection(), sender);
endpoint->RecordTrace();
return sender;
}
// reno => Reno. !reno => Cubic.
TcpCubicSenderBytes* SetupTcpSender(simulator::QuicEndpoint* endpoint,
bool reno) {
// Ownership of the sender will be overtaken by the endpoint.
TcpCubicSenderBytes* sender = new TcpCubicSenderBytes(
endpoint->connection()->clock(),
endpoint->connection()->sent_packet_manager().GetRttStats(), reno,
kDefaultInitialCwndPackets,
GetQuicFlag(FLAGS_quic_max_congestion_window),
QuicConnectionPeer::GetStats(endpoint->connection()));
QuicConnectionPeer::SetSendAlgorithm(endpoint->connection(), sender);
endpoint->RecordTrace();
return sender;
}
void SetConnectionOption(SendAlgorithmInterface* sender, QuicTag option) {
QuicConfig config;
QuicTagVector options;
options.push_back(option);
QuicConfigPeer::SetReceivedConnectionOptions(&config, options);
sender->SetFromConfig(config, Perspective::IS_SERVER);
}
void CreateNetwork(const MultiSenderTopologyParams& params) {
QUIC_LOG(INFO) << "CreateNetwork with parameters: " << params.ToString();
switch_ = std::make_unique<simulator::Switch>(&simulator_, "Switch",
params.switch_port_count,
params.SwitchQueueCapacity());
network_links_.push_back(std::make_unique<simulator::SymmetricLink>(
receiver_multiplexer_.get(), switch_->port(1),
params.test_link.bandwidth, params.test_link.delay));
for (size_t i = 0; i < MultiSenderTopologyParams::kNumLocalLinks; ++i) {
simulator::SwitchPortNumber port_number = i + 2;
network_links_.push_back(std::make_unique<simulator::SymmetricLink>(
sender_endpoints_[i].get(), switch_->port(port_number),
params.local_links[i].bandwidth, params.local_links[i].delay));
}
}
QuicConnection* sender_connection(size_t which) {
return sender_endpoints_[which]->connection();
}
const QuicConnectionStats& sender_connection_stats(size_t which) {
return sender_connection(which)->GetStats();
}
float sender_loss_rate_in_packets(size_t which) {
return static_cast<float>(sender_connection_stats(which).packets_lost) /
sender_connection_stats(which).packets_sent;
}
std::vector<std::unique_ptr<simulator::QuicEndpoint>> sender_endpoints_;
std::vector<std::unique_ptr<simulator::QuicEndpoint>> receiver_endpoints_;
std::unique_ptr<simulator::QuicEndpointMultiplexer> receiver_multiplexer_;
Bbr2Sender* sender_0_;
std::unique_ptr<simulator::Switch> switch_;
std::vector<std::unique_ptr<simulator::SymmetricLink>> network_links_;
};
TEST_F(Bbr2MultiSenderTest, Bbr2VsBbr2) {
SetupBbr2Sender(sender_endpoints_[1].get());
MultiSenderTopologyParams params;
CreateNetwork(params);
const QuicByteCount transfer_size = 10 * 1024 * 1024;
const QuicTime::Delta transfer_time =
params.BottleneckBandwidth().TransferTime(transfer_size);
QUIC_LOG(INFO) << "Single flow transfer time: " << transfer_time;
// Transfer 10% of data in first transfer.
sender_endpoints_[0]->AddBytesToTransfer(transfer_size);
bool simulator_result = simulator_.RunUntilOrTimeout(
[this]() {
return receiver_endpoints_[0]->bytes_received() >= 0.1 * transfer_size;
},
transfer_time);
ASSERT_TRUE(simulator_result);
// Start the second transfer and wait until both finish.
sender_endpoints_[1]->AddBytesToTransfer(transfer_size);
simulator_result = simulator_.RunUntilOrTimeout(
[this]() {
return receiver_endpoints_[0]->bytes_received() == transfer_size &&
receiver_endpoints_[1]->bytes_received() == transfer_size;
},
3 * transfer_time);
ASSERT_TRUE(simulator_result);
}
TEST_F(Bbr2MultiSenderTest, QUIC_SLOW_TEST(MultipleBbr2s)) {
const int kTotalNumSenders = 6;
for (int i = 1; i < kTotalNumSenders; ++i) {
SetupBbr2Sender(sender_endpoints_[i].get());
}
MultiSenderTopologyParams params;
CreateNetwork(params);
const QuicByteCount transfer_size = 10 * 1024 * 1024;
const QuicTime::Delta transfer_time =
params.BottleneckBandwidth().TransferTime(transfer_size);
QUIC_LOG(INFO) << "Single flow transfer time: " << transfer_time
<< ". Now: " << SimulatedNow();
// Start all transfers.
for (int i = 0; i < kTotalNumSenders; ++i) {
if (i != 0) {
const QuicTime sender_start_time =
SimulatedNow() + QuicTime::Delta::FromSeconds(2);
bool simulator_result = simulator_.RunUntilOrTimeout(
[&]() { return SimulatedNow() >= sender_start_time; }, transfer_time);
ASSERT_TRUE(simulator_result);
}
sender_endpoints_[i]->AddBytesToTransfer(transfer_size);
}
// Wait for all transfers to finish.
QuicTime::Delta expected_total_transfer_time_upper_bound =
QuicTime::Delta::FromMicroseconds(kTotalNumSenders *
transfer_time.ToMicroseconds() * 1.1);
bool simulator_result = simulator_.RunUntilOrTimeout(
[this]() {
for (int i = 0; i < kTotalNumSenders; ++i) {
if (receiver_endpoints_[i]->bytes_received() < transfer_size) {
return false;
}
}
return true;
},
expected_total_transfer_time_upper_bound);
ASSERT_TRUE(simulator_result)
<< "Expected upper bound: " << expected_total_transfer_time_upper_bound;
}
/* The first 11 packets are sent at the same time, but the duration between the
* acks of the 1st and the 11th packet is 49 milliseconds, causing very low bw
* samples. This happens for both large and small buffers.
*/
/*
TEST_F(Bbr2MultiSenderTest, Bbr2VsBbr2LargeRttTinyBuffer) {
SetupBbr2Sender(sender_endpoints_[1].get());
MultiSenderTopologyParams params;
params.switch_queue_capacity_in_bdp = 0.05;
params.test_link.delay = QuicTime::Delta::FromSeconds(1);
CreateNetwork(params);
const QuicByteCount transfer_size = 10 * 1024 * 1024;
const QuicTime::Delta transfer_time =
params.BottleneckBandwidth().TransferTime(transfer_size);
QUIC_LOG(INFO) << "Single flow transfer time: " << transfer_time;
// Transfer 10% of data in first transfer.
sender_endpoints_[0]->AddBytesToTransfer(transfer_size);
bool simulator_result = simulator_.RunUntilOrTimeout(
[this]() {
return receiver_endpoints_[0]->bytes_received() >= 0.1 * transfer_size;
},
transfer_time);
ASSERT_TRUE(simulator_result);
// Start the second transfer and wait until both finish.
sender_endpoints_[1]->AddBytesToTransfer(transfer_size);
simulator_result = simulator_.RunUntilOrTimeout(
[this]() {
return receiver_endpoints_[0]->bytes_received() == transfer_size &&
receiver_endpoints_[1]->bytes_received() == transfer_size;
},
3 * transfer_time);
ASSERT_TRUE(simulator_result);
}
*/
TEST_F(Bbr2MultiSenderTest, Bbr2VsBbr1) {
SetupBbrSender(sender_endpoints_[1].get());
MultiSenderTopologyParams params;
CreateNetwork(params);
const QuicByteCount transfer_size = 10 * 1024 * 1024;
const QuicTime::Delta transfer_time =
params.BottleneckBandwidth().TransferTime(transfer_size);
QUIC_LOG(INFO) << "Single flow transfer time: " << transfer_time;
// Transfer 10% of data in first transfer.
sender_endpoints_[0]->AddBytesToTransfer(transfer_size);
bool simulator_result = simulator_.RunUntilOrTimeout(
[this]() {
return receiver_endpoints_[0]->bytes_received() >= 0.1 * transfer_size;
},
transfer_time);
ASSERT_TRUE(simulator_result);
// Start the second transfer and wait until both finish.
sender_endpoints_[1]->AddBytesToTransfer(transfer_size);
simulator_result = simulator_.RunUntilOrTimeout(
[this]() {
return receiver_endpoints_[0]->bytes_received() == transfer_size &&
receiver_endpoints_[1]->bytes_received() == transfer_size;
},
3 * transfer_time);
ASSERT_TRUE(simulator_result);
}
TEST_F(Bbr2MultiSenderTest, QUIC_SLOW_TEST(Bbr2VsReno)) {
SetupTcpSender(sender_endpoints_[1].get(), /*reno=*/true);
MultiSenderTopologyParams params;
CreateNetwork(params);
const QuicByteCount transfer_size = 10 * 1024 * 1024;
const QuicTime::Delta transfer_time =
params.BottleneckBandwidth().TransferTime(transfer_size);
QUIC_LOG(INFO) << "Single flow transfer time: " << transfer_time;
// Transfer 10% of data in first transfer.
sender_endpoints_[0]->AddBytesToTransfer(transfer_size);
bool simulator_result = simulator_.RunUntilOrTimeout(
[this]() {
return receiver_endpoints_[0]->bytes_received() >= 0.1 * transfer_size;
},
transfer_time);
ASSERT_TRUE(simulator_result);
// Start the second transfer and wait until both finish.
sender_endpoints_[1]->AddBytesToTransfer(transfer_size);
simulator_result = simulator_.RunUntilOrTimeout(
[this]() {
return receiver_endpoints_[0]->bytes_received() == transfer_size &&
receiver_endpoints_[1]->bytes_received() == transfer_size;
},
3 * transfer_time);
ASSERT_TRUE(simulator_result);
}
TEST_F(Bbr2MultiSenderTest, QUIC_SLOW_TEST(Bbr2VsRenoB2RC)) {
SetConnectionOption(sender_0_, kB2RC);
SetupTcpSender(sender_endpoints_[1].get(), /*reno=*/true);
MultiSenderTopologyParams params;
CreateNetwork(params);
const QuicByteCount transfer_size = 10 * 1024 * 1024;
const QuicTime::Delta transfer_time =
params.BottleneckBandwidth().TransferTime(transfer_size);
QUIC_LOG(INFO) << "Single flow transfer time: " << transfer_time;
// Transfer 10% of data in first transfer.
sender_endpoints_[0]->AddBytesToTransfer(transfer_size);
bool simulator_result = simulator_.RunUntilOrTimeout(
[this]() {
return receiver_endpoints_[0]->bytes_received() >= 0.1 * transfer_size;
},
transfer_time);
ASSERT_TRUE(simulator_result);
// Start the second transfer and wait until both finish.
sender_endpoints_[1]->AddBytesToTransfer(transfer_size);
simulator_result = simulator_.RunUntilOrTimeout(
[this]() {
return receiver_endpoints_[0]->bytes_received() == transfer_size &&
receiver_endpoints_[1]->bytes_received() == transfer_size;
},
3 * transfer_time);
ASSERT_TRUE(simulator_result);
}
TEST_F(Bbr2MultiSenderTest, QUIC_SLOW_TEST(Bbr2VsCubic)) {
SetupTcpSender(sender_endpoints_[1].get(), /*reno=*/false);
MultiSenderTopologyParams params;
CreateNetwork(params);
const QuicByteCount transfer_size = 50 * 1024 * 1024;
const QuicTime::Delta transfer_time =
params.BottleneckBandwidth().TransferTime(transfer_size);
QUIC_LOG(INFO) << "Single flow transfer time: " << transfer_time;
// Transfer 10% of data in first transfer.
sender_endpoints_[0]->AddBytesToTransfer(transfer_size);
bool simulator_result = simulator_.RunUntilOrTimeout(
[this]() {
return receiver_endpoints_[0]->bytes_received() >= 0.1 * transfer_size;
},
transfer_time);
ASSERT_TRUE(simulator_result);
// Start the second transfer and wait until both finish.
sender_endpoints_[1]->AddBytesToTransfer(transfer_size);
simulator_result = simulator_.RunUntilOrTimeout(
[this]() {
return receiver_endpoints_[0]->bytes_received() == transfer_size &&
receiver_endpoints_[1]->bytes_received() == transfer_size;
},
3 * transfer_time);
ASSERT_TRUE(simulator_result);
}
} // namespace test
} // namespace quic