From 342972a8867a46c5a64c2be10acaf1db060e7475 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 23 Apr 2026 09:32:36 +0000 Subject: [PATCH 1/2] Initial plan From 1e4b0adf14e0f57e0606235f022c170173399bba Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 23 Apr 2026 09:54:41 +0000 Subject: [PATCH 2/2] dns_cache: test past-TTL failure branch is floored at dns_min_refresh_rate Agent-Logs-Url: https://github.com/phlax/envoy/sessions/798a1ee4-459e-4f76-8dda-8bbeef41e639 Co-authored-by: phlax <454682+phlax@users.noreply.github.com> --- .../dns_cache_impl_test.cc | 72 +++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/test/extensions/common/dynamic_forward_proxy/dns_cache_impl_test.cc b/test/extensions/common/dynamic_forward_proxy/dns_cache_impl_test.cc index 54c62af5568dc..6abb35fa2de35 100644 --- a/test/extensions/common/dynamic_forward_proxy/dns_cache_impl_test.cc +++ b/test/extensions/common/dynamic_forward_proxy/dns_cache_impl_test.cc @@ -987,6 +987,78 @@ TEST_F(DnsCacheImplTest, ResolveFailureBackoffZeroFlooredByMinRefresh) { 1 /* added */, 0 /* removed */, 1 /* num hosts */); } +TEST_F(DnsCacheImplTest, ResolveFailurePastHostTtlFlooredByMinRefresh) { + *config_.mutable_host_ttl() = Protobuf::util::TimeUtil::SecondsToDuration(1); + *config_.mutable_dns_refresh_rate() = Protobuf::util::TimeUtil::SecondsToDuration(60); + *config_.mutable_dns_min_refresh_rate() = Protobuf::util::TimeUtil::SecondsToDuration(1); + initialize(); + InSequence s; + + MockLoadDnsCacheEntryCallbacks callbacks; + Network::DnsResolver::ResolveCb resolve_cb; + Event::MockTimer* resolve_timer = new Event::MockTimer(&context_.server_context_.dispatcher_); + Event::MockTimer* timeout_timer = new Event::MockTimer(&context_.server_context_.dispatcher_); + EXPECT_CALL(*timeout_timer, enableTimer(std::chrono::milliseconds(5000), nullptr)); + EXPECT_CALL(*resolver_, resolve("foo.com", _, _)) + .WillOnce(DoAll(SaveArg<2>(&resolve_cb), Return(&resolver_->active_query_))); + auto result = dns_cache_->loadDnsCacheEntry("foo.com", 80, false, callbacks); + EXPECT_EQ(DnsCache::LoadDnsCacheEntryStatus::Loading, result.status_); + + EXPECT_CALL(*timeout_timer, disableTimer()); + EXPECT_CALL( + update_callbacks_, + onDnsHostAddOrUpdate("foo.com:80", DnsHostInfoEquals("10.0.0.1:80", "foo.com", false))); + EXPECT_CALL(callbacks, + onLoadDnsCacheComplete(DnsHostInfoEquals("10.0.0.1:80", "foo.com", false))); + EXPECT_CALL(update_callbacks_, + onDnsResolutionComplete("foo.com:80", + DnsHostInfoEquals("10.0.0.1:80", "foo.com", false), + Network::DnsResolver::ResolutionStatus::Completed)); + EXPECT_CALL(*resolve_timer, enableTimer(std::chrono::milliseconds(dns_ttl_), _)); + resolve_cb(Network::DnsResolver::ResolutionStatus::Completed, "", + TestUtility::makeDnsResponse({"10.0.0.1"})); + + checkStats(1 /* attempt */, 1 /* success */, 0 /* failure */, 1 /* address changed */, + 1 /* added */, 0 /* removed */, 1 /* num hosts */); + + // Advance a small amount so the alarm fires before TTL (now - last_used = 100ms < 1000ms = + // host_ttl), which causes onReResolveAlarm to startResolve rather than evict. + simTime().advanceTimeWait(std::chrono::milliseconds(100)); + + EXPECT_CALL(*timeout_timer, enableTimer(std::chrono::milliseconds(5000), nullptr)); + EXPECT_CALL(*resolver_, resolve("foo.com", _, _)) + .WillOnce(DoAll(SaveArg<2>(&resolve_cb), Return(&resolver_->active_query_))); + resolve_timer->invokeCallback(); + + // Advance past the TTL boundary so that elapsed = 1000ms >= host_ttl (1000ms) when + // finishResolve runs. + simTime().advanceTimeWait(std::chrono::milliseconds(900)); + + // DNS fails. elapsed >= host_ttl so the past-TTL branch sets refresh_interval to 0. + // The unconditional floor then lifts it to dns_min_refresh_rate (1000ms). + EXPECT_CALL(*timeout_timer, disableTimer()); + EXPECT_CALL(update_callbacks_, onDnsHostAddOrUpdate(_, _)).Times(0); + EXPECT_CALL(callbacks, onLoadDnsCacheComplete(_)).Times(0); + EXPECT_CALL(update_callbacks_, + onDnsResolutionComplete("foo.com:80", + DnsHostInfoEquals("10.0.0.1:80", "foo.com", false), + Network::DnsResolver::ResolutionStatus::Failure)); + // Past-TTL branch: raw backoff is discarded and refresh_interval becomes 0; + // the unconditional floor lifts it to dns_min_refresh_rate (1000ms). + EXPECT_CALL(*resolve_timer, enableTimer(std::chrono::milliseconds(1000), _)); + resolve_cb(Network::DnsResolver::ResolutionStatus::Failure, "", TestUtility::makeDnsResponse({})); + + checkStats(2 /* attempt */, 1 /* success */, 1 /* failure */, 1 /* address changed */, + 1 /* added */, 0 /* removed */, 1 /* num hosts */); + + // After the floored interval now-last_used = 2000ms >= host_ttl (1000ms): the host is evicted. + simTime().advanceTimeWait(std::chrono::milliseconds(1000)); + EXPECT_CALL(update_callbacks_, onDnsHostRemove("foo.com:80")); + resolve_timer->invokeCallback(); + checkStats(2 /* attempt */, 1 /* success */, 1 /* failure */, 1 /* address changed */, + 1 /* added */, 1 /* removed */, 0 /* num hosts */); +} + TEST_F(DnsCacheImplTest, DisableRefreshOnFailureContainsFailedHost) { config_.set_disable_dns_refresh_on_failure(true);