Skip to content
Draft
Show file tree
Hide file tree
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
1 change: 1 addition & 0 deletions test/extensions/config_subscription/grpc/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,7 @@ envoy_cc_test(
"//test/test_common:network_utility_lib",
"//test/test_common:resources_lib",
"//test/test_common:utility_lib",
"@abseil-cpp//absl/time",
"@envoy_api//envoy/config/bootstrap/v3:pkg_cc_proto",
"@envoy_api//envoy/extensions/transport_sockets/tls/v3:pkg_cc_proto",
],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
#include "test/test_common/environment.h"
#include "test/test_common/simulated_time_system.h"

#include "absl/time/clock.h"
#include "absl/time/time.h"
#include "gtest/gtest.h"

using testing::Eq;
Expand Down Expand Up @@ -354,8 +356,37 @@ TEST_P(XdsFailoverAdsIntegrationTest, StartupPrimaryNotResponding) {

// Expect another connection attempt to the primary. Reject the stream (gRPC failure) immediately.
// As this is a 2nd consecutive failure, it will trigger failover.
primaryConnectionFailure();
ASSERT_TRUE(xds_connection_->waitForDisconnect());
//
// For GoogleGrpc: after a TCP close, the gRPC channel can enter TRANSIENT_FAILURE state.
// When Envoy's simulated-time retry timer fires, the gRPC library may immediately reject
// the stream attempt (without establishing a new TCP connection) because the channel's
// real-time backoff has not yet expired. This also counts as a 2nd consecutive failure
// and triggers failover. Detect this case by polling (without advancing simulated time)
// before attempting to accept a second connection.
bool second_failure_via_transient_failure = false;
if (clientType() == Grpc::ClientType::GoogleGrpc) {
// Poll for up to 200*TIMEOUT_FACTOR ms (real time, no simulated clock advancement).
// The gRPC completion queue typically propagates the UNAVAILABLE failure to Envoy's
// main thread within a few milliseconds; 200ms (×TIMEOUT_FACTOR for slow builds)
// provides ample margin while still being much shorter than the 30s default timeout
// that would otherwise trigger the "don't use DefaultTimeout" test guard.
const auto deadline = absl::Now() + absl::Milliseconds(200 * TIMEOUT_FACTOR);
// 5ms between polls: short enough to detect the failure promptly without busy-looping.
constexpr absl::Duration kPollInterval = absl::Milliseconds(5);
while (absl::Now() < deadline) {
const auto counter =
TestUtility::findCounter(test_server_->statStore(), "cluster_manager.cds.update_failure");
if (counter != nullptr && counter->value() >= 2) {
second_failure_via_transient_failure = true;
break;
}
absl::SleepFor(kPollInterval);
}
}
if (!second_failure_via_transient_failure) {
primaryConnectionFailure();
ASSERT_TRUE(xds_connection_->waitForDisconnect());
}

// The CDS request fails when the primary disconnects.
test_server_->waitForCounter("cluster_manager.cds.update_failure", Ge(2));
Expand Down