aboutsummaryrefslogtreecommitdiffstats
path: root/library/cpp
diff options
context:
space:
mode:
authorarcadia-devtools <arcadia-devtools@yandex-team.ru>2022-06-24 17:01:11 +0300
committerarcadia-devtools <arcadia-devtools@yandex-team.ru>2022-06-24 17:01:11 +0300
commit45ad9f6d8690a224820723ce2fe7cad1110c35f4 (patch)
tree316e3a8422df14c80f09830b0219f475c848bdc1 /library/cpp
parent0796a2f91010abe8890cee7570eb2f61efab27c7 (diff)
downloadydb-45ad9f6d8690a224820723ce2fe7cad1110c35f4.tar.gz
intermediate changes
ref:66616c49c927fd0dbf4d07a83fea6830e84016a3
Diffstat (limited to 'library/cpp')
-rw-r--r--library/cpp/actors/dnsresolver/dnsresolver_caching.cpp376
-rw-r--r--library/cpp/actors/dnsresolver/dnsresolver_caching_ut.cpp134
2 files changed, 246 insertions, 264 deletions
diff --git a/library/cpp/actors/dnsresolver/dnsresolver_caching.cpp b/library/cpp/actors/dnsresolver/dnsresolver_caching.cpp
index 02760f4c275..a7e38806c03 100644
--- a/library/cpp/actors/dnsresolver/dnsresolver_caching.cpp
+++ b/library/cpp/actors/dnsresolver/dnsresolver_caching.cpp
@@ -15,10 +15,13 @@ namespace NDnsResolver {
struct TMonCounters {
NMonitoring::TDynamicCounters::TCounterPtr OutgoingInFlightV4;
NMonitoring::TDynamicCounters::TCounterPtr OutgoingInFlightV6;
+ NMonitoring::TDynamicCounters::TCounterPtr OutgoingInFlightUnspec;
NMonitoring::TDynamicCounters::TCounterPtr OutgoingErrorsV4;
NMonitoring::TDynamicCounters::TCounterPtr OutgoingErrorsV6;
+ NMonitoring::TDynamicCounters::TCounterPtr OutgoingErrorsUnspec;
NMonitoring::TDynamicCounters::TCounterPtr OutgoingTotalV4;
NMonitoring::TDynamicCounters::TCounterPtr OutgoingTotalV6;
+ NMonitoring::TDynamicCounters::TCounterPtr OutgoingTotalUnspec;
NMonitoring::TDynamicCounters::TCounterPtr IncomingInFlight;
NMonitoring::TDynamicCounters::TCounterPtr IncomingErrors;
@@ -31,10 +34,13 @@ namespace NDnsResolver {
TMonCounters(const NMonitoring::TDynamicCounterPtr& counters)
: OutgoingInFlightV4(counters->GetCounter("DnsResolver/Outgoing/InFlight/V4", false))
, OutgoingInFlightV6(counters->GetCounter("DnsResolver/Outgoing/InFlight/V6", false))
+ , OutgoingInFlightUnspec(counters->GetCounter("DnsResolver/Outgoing/InFlight/Unspec", false))
, OutgoingErrorsV4(counters->GetCounter("DnsResolver/Outgoing/Errors/V4", true))
, OutgoingErrorsV6(counters->GetCounter("DnsResolver/Outgoing/Errors/V6", true))
+ , OutgoingErrorsUnspec(counters->GetCounter("DnsResolver/Outgoing/Errors/Unspec", true))
, OutgoingTotalV4(counters->GetCounter("DnsResolver/Outgoing/Total/V4", true))
, OutgoingTotalV6(counters->GetCounter("DnsResolver/Outgoing/Total/V6", true))
+ , OutgoingTotalUnspec(counters->GetCounter("DnsResolver/Outgoing/Total/Unspec", true))
, IncomingInFlight(counters->GetCounter("DnsResolver/Incoming/InFlight", false))
, IncomingErrors(counters->GetCounter("DnsResolver/Incoming/Errors", true))
, IncomingTotal(counters->GetCounter("DnsResolver/Incoming/Total", true))
@@ -42,6 +48,45 @@ namespace NDnsResolver {
, CacheHits(counters->GetCounter("DnsResolver/Cache/Hits", true))
, CacheMisses(counters->GetCounter("DnsResolver/Cache/Misses", true))
{ }
+
+ const NMonitoring::TDynamicCounters::TCounterPtr& OutgoingInFlightByFamily(int family) const {
+ switch (family) {
+ case AF_INET:
+ return OutgoingInFlightV4;
+ case AF_INET6:
+ return OutgoingInFlightV6;
+ case AF_UNSPEC:
+ return OutgoingInFlightUnspec;
+ default:
+ Y_FAIL("Unexpected family %d", family);
+ }
+ }
+
+ const NMonitoring::TDynamicCounters::TCounterPtr& OutgoingErrorsByFamily(int family) const {
+ switch (family) {
+ case AF_INET:
+ return OutgoingErrorsV4;
+ case AF_INET6:
+ return OutgoingErrorsV6;
+ case AF_UNSPEC:
+ return OutgoingErrorsUnspec;
+ default:
+ Y_FAIL("Unexpected family %d", family);
+ }
+ }
+
+ const NMonitoring::TDynamicCounters::TCounterPtr& OutgoingTotalByFamily(int family) const {
+ switch (family) {
+ case AF_INET:
+ return OutgoingTotalV4;
+ case AF_INET6:
+ return OutgoingTotalV6;
+ case AF_UNSPEC:
+ return OutgoingTotalUnspec;
+ default:
+ Y_FAIL("Unexpected family %d", family);
+ }
+ }
};
public:
@@ -97,19 +142,13 @@ namespace NDnsResolver {
WaitingRequests.erase(waitingIt);
switch (waitingInfo.Family) {
+ case AF_UNSPEC:
case AF_INET6:
- if (ev->Get()->Status) {
- ProcessErrorV6(waitingInfo.Position, ev->Get()->Status, std::move(ev->Get()->ErrorText));
- } else {
- ProcessAddrsV6(waitingInfo.Position, std::move(ev->Get()->AddrsV6));
- }
- break;
-
case AF_INET:
if (ev->Get()->Status) {
- ProcessErrorV4(waitingInfo.Position, ev->Get()->Status, std::move(ev->Get()->ErrorText));
+ ProcessError(waitingInfo.Family, waitingInfo.Position, ev->Get()->Status, std::move(ev->Get()->ErrorText));
} else {
- ProcessAddrsV4(waitingInfo.Position, std::move(ev->Get()->AddrsV4));
+ ProcessAddrs(waitingInfo.Family, waitingInfo.Position, std::move(ev->Get()->AddrsV6), std::move(ev->Get()->AddrsV4));
}
break;
@@ -127,12 +166,12 @@ namespace NDnsResolver {
WaitingRequests.erase(waitingIt);
switch (waitingInfo.Family) {
+ case AF_UNSPEC:
case AF_INET6:
- ProcessErrorV6(waitingInfo.Position, ARES_ENOTINITIALIZED, "Caching dns resolver cannot deliver to the underlying resolver");
- break;
case AF_INET:
- ProcessErrorV4(waitingInfo.Position, ARES_ENOTINITIALIZED, "Caching dns resolver cannot deliver to the underlying resolver");
+ ProcessError(waitingInfo.Family, waitingInfo.Position, ARES_ENOTINITIALIZED, "Caching dns resolver cannot deliver to the underlying resolver");
break;
+
default:
Y_FAIL("Unexpected request family %d", waitingInfo.Family);
}
@@ -170,26 +209,30 @@ namespace NDnsResolver {
switch (req->Family) {
case AF_UNSPEC:
+ if (Options.AllowIPv6 && Options.AllowIPv4) {
+ EnqueueRequest(AF_UNSPEC, std::move(req));
+ return;
+ }
if (Options.AllowIPv6) {
- EnqueueRequestIPv6(std::move(req));
+ EnqueueRequest(AF_INET6, std::move(req));
return;
}
if (Options.AllowIPv4) {
- EnqueueRequestIPv4(std::move(req));
+ EnqueueRequest(AF_INET, std::move(req));
return;
}
break;
case AF_INET6:
if (Options.AllowIPv6) {
- EnqueueRequestIPv6(std::move(req));
+ EnqueueRequest(AF_INET6, std::move(req));
return;
}
break;
case AF_INET:
if (Options.AllowIPv4) {
- EnqueueRequestIPv4(std::move(req));
+ EnqueueRequest(AF_INET, std::move(req));
return;
}
break;
@@ -198,7 +241,7 @@ namespace NDnsResolver {
ReplyWithError(std::move(req), ARES_EBADFAMILY);
}
- void EnqueueRequestIPv6(THolder<TIncomingRequest> req) {
+ void EnqueueRequest(int family, THolder<TIncomingRequest> req) {
auto now = TActivationContext::Now();
auto& fullState = NameToState[req->Name];
@@ -206,72 +249,33 @@ namespace NDnsResolver {
*MonCounters->CacheSize = NameToState.size();
}
- auto& state = fullState.StateIPv6;
- EnsureRequest(state, req->Name, AF_INET6, now);
+ auto& state = fullState.StateByFamily(family);
+ EnsureRequest(state, req->Name, family, now);
if (state.IsHardExpired(now)) {
Y_VERIFY(state.Waiting);
if (MonCounters) {
++*MonCounters->CacheMisses;
}
- // We need to wait for ipv6 reply, schedule ipv4 request in parallel if needed
- if (Options.AllowIPv4) {
- EnsureRequest(fullState.StateIPv4, req->Name, AF_INET, now);
- }
state.WaitingRequests.PushBack(req.Release());
return;
}
- // We want to retry AF_UNSPEC with IPv4 in some cases
- if (req->Family == AF_UNSPEC && Options.AllowIPv4 && state.RetryUnspec()) {
- EnqueueRequestIPv4(std::move(req));
- return;
- }
-
- if (MonCounters) {
- ++*MonCounters->CacheHits;
- }
-
- if (state.Status != 0) {
- ReplyWithError(std::move(req), state.Status, state.ErrorText);
- } else {
- ReplyWithAddrs(std::move(req), fullState.AddrsIPv6);
- }
- }
-
- void EnqueueRequestIPv4(THolder<TIncomingRequest> req, bool isCacheMiss = false) {
- auto now = TActivationContext::Now();
-
- auto& fullState = NameToState[req->Name];
if (MonCounters) {
- *MonCounters->CacheSize = NameToState.size();
- }
-
- auto& state = fullState.StateIPv4;
- EnsureRequest(state, req->Name, AF_INET, now);
-
- if (state.IsHardExpired(now)) {
- Y_VERIFY(state.Waiting);
- if (MonCounters && !isCacheMiss) {
- ++*MonCounters->CacheMisses;
- }
- state.WaitingRequests.PushBack(req.Release());
- return;
- }
-
- if (MonCounters && !isCacheMiss) {
++*MonCounters->CacheHits;
}
if (state.Status != 0) {
ReplyWithError(std::move(req), state.Status, state.ErrorText);
} else {
- ReplyWithAddrs(std::move(req), fullState.AddrsIPv4);
+ ReplyWithAddrs(std::move(req), state.AddrsIPv6, state.AddrsIPv4);
}
}
private:
struct TFamilyState {
+ TVector<struct in6_addr> AddrsIPv6;
+ TVector<struct in_addr> AddrsIPv4;
TIncomingRequestList WaitingRequests;
TInstant SoftDeadline;
TInstant HardDeadline;
@@ -287,13 +291,6 @@ namespace NDnsResolver {
return InSoftHeap || InHardHeap || Waiting;
}
- bool RetryUnspec() const {
- return (
- Status == ARES_ENODATA ||
- Status == ARES_EBADRESP ||
- Status == ARES_ETIMEOUT);
- }
-
bool ServerReplied() const {
return ServerReplied(Status);
}
@@ -315,13 +312,38 @@ namespace NDnsResolver {
};
struct TState {
+ TFamilyState StateUnspec;
TFamilyState StateIPv6;
TFamilyState StateIPv4;
- TVector<struct in6_addr> AddrsIPv6;
- TVector<struct in_addr> AddrsIPv4;
bool Needed() const {
- return StateIPv6.Needed() || StateIPv4.Needed();
+ return StateUnspec.Needed() || StateIPv6.Needed() || StateIPv4.Needed();
+ }
+
+ const TFamilyState& StateByFamily(int family) const {
+ switch (family) {
+ case AF_UNSPEC:
+ return StateUnspec;
+ case AF_INET6:
+ return StateIPv6;
+ case AF_INET:
+ return StateIPv4;
+ default:
+ Y_FAIL("Unsupported family %d", family);
+ }
+ }
+
+ TFamilyState& StateByFamily(int family) {
+ switch (family) {
+ case AF_UNSPEC:
+ return StateUnspec;
+ case AF_INET6:
+ return StateIPv6;
+ case AF_INET:
+ return StateIPv4;
+ default:
+ Y_FAIL("Unsupported family %d", family);
+ }
}
};
@@ -366,16 +388,8 @@ namespace NDnsResolver {
}
if (MonCounters) {
- switch (family) {
- case AF_INET6:
- ++*MonCounters->OutgoingInFlightV6;
- ++*MonCounters->OutgoingTotalV6;
- break;
- case AF_INET:
- ++*MonCounters->OutgoingInFlightV4;
- ++*MonCounters->OutgoingTotalV4;
- break;
- }
+ ++*MonCounters->OutgoingInFlightByFamily(family);
+ ++*MonCounters->OutgoingTotalByFamily(family);
}
ui64 reqId = ++LastRequestId;
@@ -406,6 +420,14 @@ namespace NDnsResolver {
}
}
+ void PushSoftUnspec(TNameToState::iterator it, TInstant newDeadline) {
+ PushToHeap<&TState::StateUnspec, &TFamilyState::SoftDeadline, &TFamilyState::NextSoftDeadline, &TFamilyState::InSoftHeap>(SoftHeapUnspec, it, newDeadline);
+ }
+
+ void PushHardUnspec(TNameToState::iterator it, TInstant newDeadline) {
+ PushToHeap<&TState::StateUnspec, &TFamilyState::HardDeadline, &TFamilyState::NextHardDeadline, &TFamilyState::InHardHeap>(HardHeapUnspec, it, newDeadline);
+ }
+
void PushSoftV6(TNameToState::iterator it, TInstant newDeadline) {
PushToHeap<&TState::StateIPv6, &TFamilyState::SoftDeadline, &TFamilyState::NextSoftDeadline, &TFamilyState::InSoftHeap>(SoftHeapIPv6, it, newDeadline);
}
@@ -422,162 +444,111 @@ namespace NDnsResolver {
PushToHeap<&TState::StateIPv4, &TFamilyState::HardDeadline, &TFamilyState::NextHardDeadline, &TFamilyState::InHardHeap>(HardHeapIPv4, it, newDeadline);
}
- void ProcessErrorV6(TNameToState::iterator it, int status, TString errorText) {
- auto now = TActivationContext::Now();
- if (MonCounters) {
- --*MonCounters->OutgoingInFlightV6;
- ++*MonCounters->OutgoingErrorsV6;
- }
-
- auto& state = it->second.StateIPv6;
- Y_VERIFY(state.Waiting, "Got error for a state we are not waiting");
- state.Waiting = false;
-
- // When we have a cached positive reply, don't overwrite it with spurious errors
- const bool serverReplied = TFamilyState::ServerReplied(status);
- if (!serverReplied && state.ServerReplied() && !state.IsHardExpired(now)) {
- PushSoftV6(it, now + Options.SoftNegativeExpireTime);
- if (state.Status == ARES_SUCCESS) {
- SendAddrsV6(it);
- } else {
- SendErrorsV6(it, now);
- }
- return;
- }
-
- state.Status = status;
- state.ErrorText = std::move(errorText);
- PushSoftV6(it, now + Options.SoftNegativeExpireTime);
- if (serverReplied) {
- // Server actually replied, so keep it cached for longer
- PushHardV6(it, now + Options.HardPositiveExpireTime);
- } else {
- PushHardV6(it, now + Options.HardNegativeExpireTime);
+ void PushSoft(int family, TNameToState::iterator it, TInstant newDeadline) {
+ switch (family) {
+ case AF_UNSPEC:
+ PushSoftUnspec(it, newDeadline);
+ break;
+ case AF_INET6:
+ PushSoftV6(it, newDeadline);
+ break;
+ case AF_INET:
+ PushSoftV4(it, newDeadline);
+ break;
+ default:
+ Y_FAIL("Unexpected family %d", family);
}
-
- SendErrorsV6(it, now);
}
- void SendErrorsV6(TNameToState::iterator it, TInstant now) {
- bool cleaned = false;
- auto& state = it->second.StateIPv6;
- while (state.WaitingRequests) {
- THolder<TIncomingRequest> req(state.WaitingRequests.PopFront());
- if (req->Family == AF_UNSPEC && Options.AllowIPv4 && state.RetryUnspec()) {
- if (!cleaned) {
- CleanupExpired(now);
- cleaned = true;
- }
- EnqueueRequestIPv4(std::move(req), /* isCacheMiss */ true);
- } else {
- ReplyWithError(std::move(req), state.Status, state.ErrorText);
- }
+ void PushHard(int family, TNameToState::iterator it, TInstant newDeadline) {
+ switch (family) {
+ case AF_UNSPEC:
+ PushHardUnspec(it, newDeadline);
+ break;
+ case AF_INET6:
+ PushHardV6(it, newDeadline);
+ break;
+ case AF_INET:
+ PushHardV4(it, newDeadline);
+ break;
+ default:
+ Y_FAIL("Unexpected family %d", family);
}
}
- void ProcessErrorV4(TNameToState::iterator it, int status, TString errorText) {
+ void ProcessError(int family, TNameToState::iterator it, int status, TString errorText) {
auto now = TActivationContext::Now();
if (MonCounters) {
- --*MonCounters->OutgoingInFlightV4;
- ++*MonCounters->OutgoingErrorsV4;
+ --*MonCounters->OutgoingInFlightByFamily(family);
+ ++*MonCounters->OutgoingErrorsByFamily(family);
}
- auto& state = it->second.StateIPv4;
+ auto& state = it->second.StateByFamily(family);
Y_VERIFY(state.Waiting, "Got error for a state we are not waiting");
state.Waiting = false;
// When we have a cached positive reply, don't overwrite it with spurious errors
const bool serverReplied = TFamilyState::ServerReplied(status);
if (!serverReplied && state.ServerReplied() && !state.IsHardExpired(now)) {
- PushSoftV4(it, now + Options.SoftNegativeExpireTime);
+ PushSoft(family, it, now + Options.SoftNegativeExpireTime);
if (state.Status == ARES_SUCCESS) {
- SendAddrsV4(it);
+ SendAddrs(family, it);
} else {
- SendErrorsV4(it);
+ SendErrors(family, it);
}
return;
}
state.Status = status;
state.ErrorText = std::move(errorText);
- PushSoftV4(it, now + Options.SoftNegativeExpireTime);
+ PushSoft(family, it, now + Options.SoftNegativeExpireTime);
if (serverReplied) {
// Server actually replied, so keep it cached for longer
- PushHardV4(it, now + Options.HardPositiveExpireTime);
+ PushHard(family, it, now + Options.HardPositiveExpireTime);
} else {
- PushHardV4(it, now + Options.HardNegativeExpireTime);
+ PushHard(family, it, now + Options.HardNegativeExpireTime);
}
- SendErrorsV4(it);
+ SendErrors(family, it);
}
- void SendErrorsV4(TNameToState::iterator it) {
- auto& state = it->second.StateIPv4;
+ void SendErrors(int family, TNameToState::iterator it) {
+ auto& state = it->second.StateByFamily(family);
while (state.WaitingRequests) {
THolder<TIncomingRequest> req(state.WaitingRequests.PopFront());
ReplyWithError(std::move(req), state.Status, state.ErrorText);
}
}
- void ProcessAddrsV6(TNameToState::iterator it, TVector<struct in6_addr> addrs) {
- if (Y_UNLIKELY(addrs.empty())) {
- // Probably unnecessary: we don't want to deal with empty address lists
- return ProcessErrorV6(it, ARES_ENODATA, ares_strerror(ARES_ENODATA));
- }
-
- auto now = TActivationContext::Now();
- if (MonCounters) {
- --*MonCounters->OutgoingInFlightV6;
- }
-
- auto& state = it->second.StateIPv6;
- Y_VERIFY(state.Waiting, "Got reply for a state we are not waiting");
- state.Waiting = false;
-
- state.Status = ARES_SUCCESS;
- it->second.AddrsIPv6 = std::move(addrs);
- PushSoftV6(it, now + Options.SoftPositiveExpireTime);
- PushHardV6(it, now + Options.HardPositiveExpireTime);
-
- SendAddrsV6(it);
- }
-
- void SendAddrsV6(TNameToState::iterator it) {
- auto& state = it->second.StateIPv6;
- while (state.WaitingRequests) {
- THolder<TIncomingRequest> req(state.WaitingRequests.PopFront());
- ReplyWithAddrs(std::move(req), it->second.AddrsIPv6);
- }
- }
-
- void ProcessAddrsV4(TNameToState::iterator it, TVector<struct in_addr> addrs) {
- if (Y_UNLIKELY(addrs.empty())) {
+ void ProcessAddrs(int family, TNameToState::iterator it, TVector<struct in6_addr> addrs6, TVector<struct in_addr> addrs4) {
+ if (Y_UNLIKELY(addrs6.empty() && addrs4.empty())) {
// Probably unnecessary: we don't want to deal with empty address lists
- return ProcessErrorV4(it, ARES_ENODATA, ares_strerror(ARES_ENODATA));
+ return ProcessError(family, it, ARES_ENODATA, ares_strerror(ARES_ENODATA));
}
auto now = TActivationContext::Now();
if (MonCounters) {
- --*MonCounters->OutgoingInFlightV4;
+ --*MonCounters->OutgoingInFlightByFamily(family);
}
- auto& state = it->second.StateIPv4;
+ auto& state = it->second.StateByFamily(family);
Y_VERIFY(state.Waiting, "Got reply for a state we are not waiting");
state.Waiting = false;
state.Status = ARES_SUCCESS;
- it->second.AddrsIPv4 = std::move(addrs);
- PushSoftV4(it, now + Options.SoftPositiveExpireTime);
- PushHardV4(it, now + Options.HardPositiveExpireTime);
+ state.AddrsIPv6 = std::move(addrs6);
+ state.AddrsIPv4 = std::move(addrs4);
+ PushSoft(family, it, now + Options.SoftPositiveExpireTime);
+ PushHard(family, it, now + Options.HardPositiveExpireTime);
- SendAddrsV4(it);
+ SendAddrs(family, it);
}
- void SendAddrsV4(TNameToState::iterator it) {
- auto& state = it->second.StateIPv4;
+ void SendAddrs(int family, TNameToState::iterator it) {
+ auto& state = it->second.StateByFamily(family);
while (state.WaitingRequests) {
THolder<TIncomingRequest> req(state.WaitingRequests.PopFront());
- ReplyWithAddrs(std::move(req), it->second.AddrsIPv4);
+ ReplyWithAddrs(std::move(req), state.AddrsIPv6, state.AddrsIPv4);
}
}
@@ -619,6 +590,8 @@ namespace NDnsResolver {
}
void CleanupExpired(TInstant now) {
+ DoCleanupExpired<&TState::StateUnspec, &TFamilyState::SoftDeadline, &TFamilyState::NextSoftDeadline, &TFamilyState::InSoftHeap>(SoftHeapUnspec, now);
+ DoCleanupExpired<&TState::StateUnspec, &TFamilyState::HardDeadline, &TFamilyState::NextHardDeadline, &TFamilyState::InHardHeap>(HardHeapUnspec, now);
DoCleanupExpired<&TState::StateIPv6, &TFamilyState::SoftDeadline, &TFamilyState::NextSoftDeadline, &TFamilyState::InSoftHeap>(SoftHeapIPv6, now);
DoCleanupExpired<&TState::StateIPv6, &TFamilyState::HardDeadline, &TFamilyState::NextHardDeadline, &TFamilyState::InHardHeap>(HardHeapIPv6, now);
DoCleanupExpired<&TState::StateIPv4, &TFamilyState::SoftDeadline, &TFamilyState::NextSoftDeadline, &TFamilyState::InSoftHeap>(SoftHeapIPv4, now);
@@ -649,36 +622,24 @@ namespace NDnsResolver {
}
}
- void ReplyWithAddrs(THolder<TIncomingRequest> req, const TVector<struct in6_addr>& addrs) {
- switch (req->Type) {
- case EIncomingRequestType::GetHostByName: {
- auto reply = MakeHolder<TEvDns::TEvGetHostByNameResult>();
- reply->AddrsV6 = addrs;
- Send(req->Sender, reply.Release(), 0, req->Cookie);
- break;
- }
- case EIncomingRequestType::GetAddr: {
- Y_VERIFY(!addrs.empty());
- auto reply = MakeHolder<TEvDns::TEvGetAddrResult>();
- reply->Addr = addrs.front();
- Send(req->Sender, reply.Release(), 0, req->Cookie);
- break;
- }
- }
- }
-
- void ReplyWithAddrs(THolder<TIncomingRequest> req, const TVector<struct in_addr>& addrs) {
+ void ReplyWithAddrs(THolder<TIncomingRequest> req, const TVector<struct in6_addr>& addrs6, const TVector<struct in_addr>& addrs4) {
switch (req->Type) {
case EIncomingRequestType::GetHostByName: {
auto reply = MakeHolder<TEvDns::TEvGetHostByNameResult>();
- reply->AddrsV4 = addrs;
+ reply->AddrsV6 = addrs6;
+ reply->AddrsV4 = addrs4;
Send(req->Sender, reply.Release(), 0, req->Cookie);
break;
}
case EIncomingRequestType::GetAddr: {
- Y_VERIFY(!addrs.empty());
auto reply = MakeHolder<TEvDns::TEvGetAddrResult>();
- reply->Addr = addrs.front();
+ if (!addrs6.empty()) {
+ reply->Addr = addrs6.front();
+ } else if (!addrs4.empty()) {
+ reply->Addr = addrs4.front();
+ } else {
+ Y_FAIL("Unexpected reply with empty address list");
+ }
Send(req->Sender, reply.Release(), 0, req->Cookie);
break;
}
@@ -698,6 +659,7 @@ namespace NDnsResolver {
void DropPending(int status, const TString& errorText) {
for (auto& [name, state] : NameToState) {
+ DropPending(state.StateUnspec.WaitingRequests, status, errorText);
DropPending(state.StateIPv6.WaitingRequests, status, errorText);
DropPending(state.StateIPv4.WaitingRequests, status, errorText);
}
@@ -713,6 +675,8 @@ namespace NDnsResolver {
const THolder<TMonCounters> MonCounters;
TNameToState NameToState;
+ TStateHeap<&TState::StateUnspec, &TFamilyState::SoftDeadline> SoftHeapUnspec;
+ TStateHeap<&TState::StateUnspec, &TFamilyState::HardDeadline> HardHeapUnspec;
TStateHeap<&TState::StateIPv6, &TFamilyState::SoftDeadline> SoftHeapIPv6;
TStateHeap<&TState::StateIPv6, &TFamilyState::HardDeadline> HardHeapIPv6;
TStateHeap<&TState::StateIPv4, &TFamilyState::SoftDeadline> SoftHeapIPv4;
diff --git a/library/cpp/actors/dnsresolver/dnsresolver_caching_ut.cpp b/library/cpp/actors/dnsresolver/dnsresolver_caching_ut.cpp
index c3b7cb3c77c..89a7e9ab368 100644
--- a/library/cpp/actors/dnsresolver/dnsresolver_caching_ut.cpp
+++ b/library/cpp/actors/dnsresolver/dnsresolver_caching_ut.cpp
@@ -88,6 +88,18 @@ Y_UNIT_TEST_SUITE(CachingDnsResolver) {
static TMockReply SingleV4(const TString& addr, TDuration delay = DefaultDelay) {
return ManyV4({ addr }, delay);
}
+
+ friend TMockReply operator+(const TMockReply& a, const TMockReply& b) {
+ Y_VERIFY(a.Status == b.Status);
+ TMockReply result;
+ result.Status = a.Status;
+ result.Delay = Max(a.Delay, b.Delay);
+ result.AddrsV6.insert(result.AddrsV6.end(), a.AddrsV6.begin(), a.AddrsV6.end());
+ result.AddrsV6.insert(result.AddrsV6.end(), b.AddrsV6.begin(), b.AddrsV6.end());
+ result.AddrsV4.insert(result.AddrsV4.end(), a.AddrsV4.begin(), a.AddrsV4.end());
+ result.AddrsV4.insert(result.AddrsV4.end(), b.AddrsV4.begin(), b.AddrsV4.end());
+ return result;
+ }
};
using TMockDnsCallback = std::function<TMockReply (const TString&, int)>;
@@ -166,8 +178,10 @@ Y_UNIT_TEST_SUITE(CachingDnsResolver) {
TActorId Sleeper;
TString Section_;
+ NMonitoring::TDynamicCounters::TCounterPtr InFlightUnspec;
NMonitoring::TDynamicCounters::TCounterPtr InFlight6;
NMonitoring::TDynamicCounters::TCounterPtr InFlight4;
+ NMonitoring::TDynamicCounters::TCounterPtr TotalUnspec;
NMonitoring::TDynamicCounters::TCounterPtr Total6;
NMonitoring::TDynamicCounters::TCounterPtr Total4;
NMonitoring::TDynamicCounters::TCounterPtr Misses;
@@ -175,6 +189,7 @@ Y_UNIT_TEST_SUITE(CachingDnsResolver) {
THashMap<TString, TMockReply> ReplyV6;
THashMap<TString, TMockReply> ReplyV4;
+ THashMap<TString, TMockReply> ReplyUnspec;
TCachingDnsRuntime() {
SetScheduledEventFilter([](auto&&, auto&&, auto&&, auto&&) { return false; });
@@ -186,6 +201,9 @@ Y_UNIT_TEST_SUITE(CachingDnsResolver) {
ReplyV4["yandex.ru"] = TMockReply::SingleV4("77.88.55.77", TDuration::MilliSeconds(250));
ReplyV6["router.asus.com"] = TMockReply::Error(ARES_ENODATA);
ReplyV4["router.asus.com"] = TMockReply::SingleV4("192.168.0.1");
+ ReplyUnspec["localhost"] = ReplyV6.at("localhost") + ReplyV4.at("localhost");
+ ReplyUnspec["yandex.ru"] = ReplyV6.at("yandex.ru") + ReplyV4.at("yandex.ru");
+ ReplyUnspec["router.asus.com"] = ReplyV4.at("router.asus.com");
}
void Start(TMockDnsCallback callback) {
@@ -194,8 +212,10 @@ Y_UNIT_TEST_SUITE(CachingDnsResolver) {
Resolver = Register(CreateCachingDnsResolver(MockResolver, ResolverOptions));
Sleeper = AllocateEdgeActor();
+ InFlightUnspec = ResolverOptions.MonCounters->GetCounter("DnsResolver/Outgoing/InFlight/Unspec", false);
InFlight6 = ResolverOptions.MonCounters->GetCounter("DnsResolver/Outgoing/InFlight/V6", false);
InFlight4 = ResolverOptions.MonCounters->GetCounter("DnsResolver/Outgoing/InFlight/V4", false);
+ TotalUnspec = ResolverOptions.MonCounters->GetCounter("DnsResolver/Outgoing/Total/Unspec", true);
Total6 = ResolverOptions.MonCounters->GetCounter("DnsResolver/Outgoing/Total/V6", true);
Total4 = ResolverOptions.MonCounters->GetCounter("DnsResolver/Outgoing/Total/V4", true);
Misses = ResolverOptions.MonCounters->GetCounter("DnsResolver/Cache/Misses", true);
@@ -205,6 +225,13 @@ Y_UNIT_TEST_SUITE(CachingDnsResolver) {
void Start() {
Start([this](const TString& name, int family) {
switch (family) {
+ case AF_UNSPEC: {
+ auto it = ReplyUnspec.find(name);
+ if (it != ReplyUnspec.end()) {
+ return it->second;
+ }
+ break;
+ }
case AF_INET6: {
auto it = ReplyV6.find(name);
if (it != ReplyV6.end()) {
@@ -234,10 +261,10 @@ Y_UNIT_TEST_SUITE(CachingDnsResolver) {
}
void WaitNoInFlight() {
- if (*InFlight6 || *InFlight4) {
+ if (*InFlightUnspec || *InFlight6 || *InFlight4) {
TDispatchOptions options;
options.CustomFinalCondition = [&]() {
- return !*InFlight6 && !*InFlight4;
+ return !*InFlightUnspec && !*InFlight6 && !*InFlight4;
};
DispatchEvents(options);
UNIT_ASSERT_C(!*InFlight6 && !*InFlight4, "Failed to wait for no inflight in " << Section_);
@@ -260,6 +287,10 @@ Y_UNIT_TEST_SUITE(CachingDnsResolver) {
return GrabEdgeEventRethrow<TEvDns::TEvGetAddrResult>(sender);
}
+ void ExpectInFlightUnspec(i64 count) {
+ UNIT_ASSERT_VALUES_EQUAL_C(InFlightUnspec->Val(), count, Section_);
+ }
+
void ExpectInFlight6(i64 count) {
UNIT_ASSERT_VALUES_EQUAL_C(InFlight6->Val(), count, Section_);
}
@@ -268,6 +299,10 @@ Y_UNIT_TEST_SUITE(CachingDnsResolver) {
UNIT_ASSERT_VALUES_EQUAL_C(InFlight4->Val(), count, Section_);
}
+ void ExpectTotalUnspec(i64 count) {
+ UNIT_ASSERT_VALUES_EQUAL_C(TotalUnspec->Val(), count, Section_);
+ }
+
void ExpectTotal6(i64 count) {
UNIT_ASSERT_VALUES_EQUAL_C(Total6->Val(), count, Section_);
}
@@ -276,6 +311,13 @@ Y_UNIT_TEST_SUITE(CachingDnsResolver) {
UNIT_ASSERT_VALUES_EQUAL_C(Total4->Val(), count, Section_);
}
+ void ExpectUnspec(i64 total, i64 inflight) {
+ UNIT_ASSERT_C(
+ TotalUnspec->Val() == total && InFlightUnspec->Val() == inflight,
+ Section_ << ": Expect6(" << total << ", " << inflight << ") "
+ << " but got (" << TotalUnspec->Val() << ", " << InFlightUnspec->Val() << ")");
+ }
+
void Expect6(i64 total, i64 inflight) {
UNIT_ASSERT_C(
Total6->Val() == total && InFlight6->Val() == inflight,
@@ -357,61 +399,41 @@ Y_UNIT_TEST_SUITE(CachingDnsResolver) {
auto sender = runtime.AllocateEdgeActor();
- // First time resolve, ipv4 and ipv6 sent in parallel, we wait for ipv6 result
+ // First time resolve, we expect AF_UNSPEC result to be cached
runtime.Section("First time resolve");
runtime.SendGetAddr(sender, "yandex.ru", AF_UNSPEC);
runtime.ExpectGetAddrSuccess(sender, "2a02:6b8:a::a");
- runtime.Expect6(1, 0);
- runtime.Expect4(1, 0);
+ runtime.ExpectUnspec(1, 0);
runtime.ExpectMisses(1);
runtime.ExpectHits(0);
- // Second resolve, ipv6 and ipv4 queries result in a cache hit
- runtime.Section("Second resolve, ipv6");
- runtime.SendGetAddr(sender, "yandex.ru", AF_INET6);
+ // Second resolve, unspec is a cache hit, ipv6 and ipv4 result in cache misses
+ runtime.Section("Second resolve");
+ runtime.SendGetAddr(sender, "yandex.ru", AF_UNSPEC);
runtime.ExpectGetAddrSuccess(sender, "2a02:6b8:a::a");
- runtime.Expect6(1, 0);
+ runtime.ExpectUnspec(1, 0);
runtime.ExpectHits(1);
- runtime.Section("Second resolve, ipv4");
- runtime.SendGetAddr(sender, "yandex.ru", AF_INET);
- runtime.ExpectGetAddrSuccess(sender, "77.88.55.77");
- runtime.Expect4(1, 0);
- runtime.ExpectHits(2);
- // Wait until soft expiration and try ipv4 again
- // Will cause a cache hit, but will start a new ipv4 request in background
- runtime.Section("Retry ipv4 after soft expiration");
- runtime.Sleep(TDuration::Seconds(15));
- runtime.SendGetAddr(sender, "yandex.ru", AF_INET);
- runtime.ExpectGetAddrSuccess(sender, "77.88.55.77");
- runtime.Expect6(1, 0);
- runtime.Expect4(2, 1);
- runtime.ExpectMisses(1);
- runtime.ExpectHits(3);
- runtime.WaitNoInFlight();
-
- // Wait until soft expiration and try both again
- // Will cause a cache hit, but will start a new ipv6 request in background
+ // Wait until soft expiration and try unspec again
+ // Will cause a cache hit, but will start a new request in background
runtime.Section("Retry both after soft expiration");
runtime.Sleep(TDuration::Seconds(15));
runtime.SendGetAddr(sender, "yandex.ru", AF_UNSPEC);
runtime.ExpectGetAddrSuccess(sender, "2a02:6b8:a::a");
- runtime.Expect6(2, 1);
- runtime.Expect4(2, 0);
+ runtime.ExpectUnspec(2, 1);
runtime.ExpectMisses(1);
- runtime.ExpectHits(4);
+ runtime.ExpectHits(2);
runtime.WaitNoInFlight();
- // Wait until hard expiration and try both again
+ // Wait until hard expiration and try unspec again
// Will cause a cache miss and new resolve requests
runtime.Section("Retry both after hard expiration");
runtime.Sleep(TDuration::Hours(2));
runtime.SendGetAddr(sender, "yandex.ru", AF_UNSPEC);
runtime.ExpectGetAddrSuccess(sender, "2a02:6b8:a::a");
- runtime.Expect6(3, 0);
- runtime.Expect4(3, 0);
+ runtime.ExpectUnspec(3, 0);
runtime.ExpectMisses(2);
- runtime.ExpectHits(4);
+ runtime.ExpectHits(2);
// Wait half the hard expiration time, must always result in a cache hit
runtime.Section("Retry both after half hard expiration");
@@ -419,39 +441,38 @@ Y_UNIT_TEST_SUITE(CachingDnsResolver) {
runtime.Sleep(TDuration::Hours(1));
runtime.SendGetAddr(sender, "yandex.ru", AF_UNSPEC);
runtime.ExpectGetAddrSuccess(sender, "2a02:6b8:a::a");
- runtime.Expect6(3 + i, 1);
- runtime.ExpectHits(4 + i);
+ runtime.ExpectUnspec(3 + i, 1);
+ runtime.ExpectHits(2 + i);
runtime.WaitNoInFlight();
}
- // Change v6 result to a timeout, must keep using cached result until hard expiration
+ // Change unspec result to a timeout, must keep using cached result until hard expiration
runtime.Section("Dns keeps timing out");
- runtime.ReplyV6["yandex.ru"] = TMockReply::Error(ARES_ETIMEOUT);
+ runtime.ReplyUnspec["yandex.ru"] = TMockReply::Error(ARES_ETIMEOUT);
for (ui64 i = 1; i <= 4; ++i) {
runtime.Sleep(TDuration::Seconds(15));
runtime.SendGetAddr(sender, "yandex.ru", AF_UNSPEC);
runtime.ExpectGetAddrSuccess(sender, "2a02:6b8:a::a");
- runtime.Expect6(7 + i, 1);
- runtime.ExpectHits(8 + i);
+ runtime.ExpectUnspec(7 + i, 1);
+ runtime.ExpectHits(6 + i);
runtime.WaitNoInFlight();
}
- // Change v6 result to nodata, must switch to a v4 result eventually
+ // Change unspec result to v4, must switch to a v4 result eventually
runtime.Section("Host changes to being ipv4 only");
- runtime.ReplyV6["yandex.ru"] = TMockReply::Error(ARES_ENODATA);
+ runtime.ReplyUnspec["yandex.ru"] = runtime.ReplyV4.at("yandex.ru");
runtime.Sleep(TDuration::Seconds(2));
runtime.SendGetAddr(sender, "yandex.ru", AF_UNSPEC);
runtime.ExpectGetAddrSuccess(sender, "2a02:6b8:a::a");
runtime.WaitNoInFlight();
runtime.SendGetAddr(sender, "yandex.ru", AF_UNSPEC);
runtime.ExpectGetAddrSuccess(sender, "77.88.55.77");
- runtime.Expect6(12, 0);
- runtime.Expect4(4, 0);
- runtime.ExpectMisses(3);
+ runtime.ExpectUnspec(12, 0);
+ runtime.ExpectMisses(2);
- // Change v6 result to nxdomain, must not fall back to a v4 result
+ // Change unspec result to nxdomain, must start returning it
runtime.Section("Host is removed from dns");
- runtime.ReplyV6["yandex.ru"] = TMockReply::Error(ARES_ENOTFOUND);
+ runtime.ReplyUnspec["yandex.ru"] = TMockReply::Error(ARES_ENOTFOUND);
runtime.Sleep(TDuration::Seconds(15));
runtime.SendGetAddr(sender, "yandex.ru", AF_UNSPEC);
runtime.ExpectGetAddrSuccess(sender, "77.88.55.77");
@@ -478,20 +499,18 @@ Y_UNIT_TEST_SUITE(CachingDnsResolver) {
runtime.ExpectHits(1);
runtime.Section("Dns keeps timing out");
- runtime.ReplyV6["router.asus.com"] = TMockReply::Error(ARES_ETIMEOUT);
- runtime.ReplyV4["router.asus.com"] = TMockReply::Error(ARES_ETIMEOUT);
+ runtime.ReplyUnspec["router.asus.com"] = TMockReply::Error(ARES_ETIMEOUT);
for (ui64 i = 1; i <= 4; ++i) {
runtime.Sleep(TDuration::Seconds(15));
runtime.SendGetAddr(sender, "router.asus.com", AF_UNSPEC);
runtime.ExpectGetAddrSuccess(sender, "192.168.0.1");
- runtime.Expect6(1 + i, 1);
- runtime.Expect4(1 + i, 1);
+ runtime.ExpectUnspec(1 + i, 1);
runtime.ExpectHits(1 + i);
runtime.WaitNoInFlight();
}
- runtime.Section("Host is removed from ipv4 dns");
- runtime.ReplyV4["router.asus.com"] = TMockReply::Error(ARES_ENOTFOUND);
+ runtime.Section("Host is removed from dns");
+ runtime.ReplyUnspec["router.asus.com"] = TMockReply::Error(ARES_ENOTFOUND);
runtime.Sleep(TDuration::Seconds(15));
runtime.SendGetAddr(sender, "router.asus.com", AF_UNSPEC);
runtime.ExpectGetAddrSuccess(sender, "192.168.0.1");
@@ -507,12 +526,11 @@ Y_UNIT_TEST_SUITE(CachingDnsResolver) {
auto sender = runtime.AllocateEdgeActor();
- runtime.ReplyV6["notfound.ru"] = TMockReply::Error(ARES_ENODATA);
- runtime.ReplyV4["notfound.ru"] = TMockReply::Error(ARES_ENOTFOUND);
+ runtime.ReplyUnspec["notfound.ru"] = TMockReply::Error(ARES_ENOTFOUND);
runtime.SendGetAddr(sender, "notfound.ru", AF_UNSPEC);
runtime.ExpectGetAddrError(sender, ARES_ENOTFOUND);
- runtime.ReplyV4["notfound.ru"] = TMockReply::Error(ARES_ETIMEOUT);
+ runtime.ReplyUnspec["notfound.ru"] = TMockReply::Error(ARES_ETIMEOUT);
runtime.SendGetAddr(sender, "notfound.ru", AF_UNSPEC);
runtime.ExpectGetAddrError(sender, ARES_ENOTFOUND);
runtime.WaitNoInFlight();
@@ -546,7 +564,7 @@ Y_UNIT_TEST_SUITE(CachingDnsResolver) {
runtime.SendGetAddr(sender, "yandex.ru", AF_UNSPEC);
runtime.ExpectGetHostByNameSuccess(sender, "192.168.0.1");
runtime.ExpectGetAddrSuccess(sender, "192.168.0.1");
- runtime.ExpectGetHostByNameSuccess(sender, "2a02:6b8:a::a");
+ runtime.ExpectGetHostByNameSuccess(sender, "2a02:6b8:a::a,77.88.55.77");
runtime.ExpectGetAddrSuccess(sender, "2a02:6b8:a::a");
runtime.SendGetHostByName(sender, "notfound.ru", AF_UNSPEC);