-
Notifications
You must be signed in to change notification settings - Fork 6.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix HostResolver behavior on fail #62652
Conversation
This is an automated comment for commit 8a24515 with description of existing statuses. It's updated for the latest CI running ❌ Click here to open a full report in a separate page
Successful checks
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, can you please add tests emulating the situation that will raise the new behaviour?
src/Common/HostResolvePool.cpp
Outdated
while (it != records.end() && it->address == address) | ||
{ | ||
it->failed = true; | ||
it->fail_time = now; | ||
if (it->fail_count < RECORD_FAIL_COUNT_LIMIT) | ||
++it->fail_count; | ||
++it; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what's the point of creating while
cycle here? We assume that we may have the same addresses there?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, you a right, with single record for address this cycle useless, removed.
Did not managed to write good test. Result of fix can be viewed after long work - reduced count of timeouts. With short test it's too random - in selectBest random IP choosed, so it can be alive IP. |
src/Common/HostResolvePool.cpp
Outdated
if (merged.empty() || merged.back().address != *it_next) | ||
merged.push_back(Record(*it_next, now)); | ||
else | ||
merged.back().resolve_time = now; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I do not understand that lines.
It is a case when new address discovered. Why do you do additional check here if (merged.empty() || merged.back().address != *it_next)
? This condition is always true here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
On my host getaddrinfo returns 3 record per each address, for example
(gdb) frame
#0 Poco::Net::DNS::hostByName (hostname=..., hintFlags=<optimized out>) at ./build-clang-17/./base/poco/Net/src/DNS.cpp:80
80 if (rc == 0)
(gdb) l
75 struct addrinfo* pAI;
76 struct addrinfo hints;
77 std::memset(&hints, 0, sizeof(hints));
78 hints.ai_flags = hintFlags;
79 int rc = getaddrinfo(hostname.c_str(), NULL, &hints, &pAI);
80 if (rc == 0)
81 {
82 HostEntry result(pAI);
83 freeaddrinfo(pAI);
84 return result;
(gdb) p *pAI
$14 = {ai_flags = 32, ai_family = 2, ai_socktype = 1, ai_protocol = 6, ai_addrlen = 16, ai_addr = 0x76bbf2316170, ai_canonname = 0x0, ai_next = 0x76bbf2316180}
(gdb) p *pAI->ai_next
$15 = {ai_flags = 32, ai_family = 2, ai_socktype = 2, ai_protocol = 17, ai_addrlen = 16, ai_addr = 0x76bbf23161b0, ai_canonname = 0x0, ai_next = 0x76bbf23161c0}
(gdb) p *pAI->ai_next->ai_next
$16 = {ai_flags = 32, ai_family = 2, ai_socktype = 3, ai_protocol = 0, ai_addrlen = 16, ai_addr = 0x76bbf23161f0, ai_canonname = 0x0, ai_next = 0x0}
(gdb) p *(sockaddr_in*)pAI->ai_addr
$19 = {sin_family = 2, sin_port = 0, sin_addr = {s_addr = 16777343}, sin_zero = "\000\000\000\000\000\000\000"}
(gdb) p *(sockaddr_in*)pAI->ai_next->ai_addr
$20 = {sin_family = 2, sin_port = 0, sin_addr = {s_addr = 16777343}, sin_zero = "\000\000\000\000\000\000\000"}
(gdb) p *(sockaddr_in*)pAI->ai_next->ai_next->ai_addr
$21 = {sin_family = 2, sin_port = 0, sin_addr = {s_addr = 16777343}, sin_zero = "\000\000\000\000\000\000\000"}
(16777343 ==> '127.0.0.1')
so each IP has three copies in next_gen
parameter in HostResolver::updateImpl
method.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
By the way, randomizing in HostResolver does not respect address priority from /etc/gai.conf
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I did not saw any code in Clickhouse which cares about ip order, so I did not supported it here either. if you wish you could try to add it!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
so each IP has three copies in next_gen parameter in HostResolver::updateImpl method.
I thought that DNSResolver eliminating duplicates. But if it does not, than some one should do it. Lets do it in HostResolver because it expects unique records right now. Thanks for that info.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
BTW you reminded me that it should be like that here https://github.com/ClickHouse/ClickHouse/blob/f75447bf11e76e09a291b3a8793f3e953bf9e279/src/Common/HostResolvePool.cpp#L50
- , resolve_function([](const String & host_to_resolve) { return DNSResolver::instance().resolveHostAll(host_to_resolve); })
+ , resolve_function([](const String & host_to_resolve) { return DNSResolver::instance().resolveHostAllInOriginOrder(host_to_resolve); })
Could you fix that as well?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Second random in choosing IPs by weights, so ordering here does not fix it.
src/Common/HostResolvePool.cpp
Outdated
@@ -237,7 +244,7 @@ void HostResolver::updateImpl(Poco::Timestamp now, std::vector<Poco::Net::IPAddr | |||
} | |||
|
|||
for (auto & rec : merged) | |||
if (rec.failed && rec.fail_time < last_effective_resolve) | |||
if (rec.failed && rec.fail_time < now - Poco::Timespan(history.totalSeconds() * (1ull << (rec.fail_count - 1)), 0)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What that expression means? now - Poco::Timespan(history.totalSeconds() * (1ull << (rec.fail_count - 1))
Make a variable or function, the name will give us a hint what it is.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Moved in separate method HostResolver::Record::cleanTimeoutedFailedFlag
src/Common/HostResolvePool.h
Outdated
@@ -141,6 +142,7 @@ class HostResolver : public std::enable_shared_from_this<HostResolver> | |||
size_t usage = 0; | |||
bool failed = false; | |||
Poco::Timestamp fail_time = 0; | |||
size_t fail_count = 0; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if you want to count failtures that you do not need this at all bool failed = false;
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
uint_8 fail_count = 0;
+
static_assert that (1 << sizeof (fail_count)) <= RECORD_FAIL_COUNT_LIMIT, or something like that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It;s a different. failed
flag cleaned from time to time (HostResolverPool.cpp, line 248), and without fail_count
it is cleaned every 2 minutes (DEFAULT_RESOLVE_TIME_HISTORY_SECONDS
). It is possible that in some time IP can be accessible again. failed
set to true
to check address again, but fail_count
is not set to zero to make pauses between checks in 2, 4, 8, ..., 62 minutes. fail_count
set to zero only after success check.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I understood that too after some time ) Tnx.
May be it is better name for fail_count
as consecutive_fail_count
. You make bigger penalty according consecutive failures.
Actually idea what I see in the code is good. I like it. It is possible to write good tests here. See |
Without tests it wont be merged. Please try to do them. It is better to check your good ideas with some tests than on real clusters. Usually IP is not bad completely. It just overloaded and connections are failed to it. So there should not be big penalty just for occasional fails. If IP is really expired that it has to be removed from dns and clickhouse forgets it as well. |
history time could be set as a small value in tests. |
It's a |
You do not have to make a test for |
Tried to add test for this. |
@CheSema Is any chance to review tests on next week? |
a17cf27
to
21d0aeb
Compare
/// there are could be duplicates in next_gen vector | ||
if (merged.empty() || merged.back().address != *it_next) | ||
{ | ||
CurrentMetrics::add(metrics.active_count, 1); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here we do not update metrics for duplicates.
@@ -237,10 +254,22 @@ void HostResolver::updateImpl(Poco::Timestamp now, std::vector<Poco::Net::IPAddr | |||
} | |||
|
|||
for (auto & rec : merged) | |||
if (rec.failed && rec.fail_time < last_effective_resolve) | |||
rec.failed = false; | |||
{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here I adjust new counter banned_count
. class Rec
is unaware about metrics, as a result that code does not belong Rec's method.
@@ -149,6 +152,11 @@ class HostResolver : public std::enable_shared_from_this<HostResolver> | |||
return address < r.address; | |||
} | |||
|
|||
bool operator ==(const Record & r) const |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
needs for is_unuque check under chassert
@@ -166,6 +174,28 @@ class HostResolver : public std::enable_shared_from_this<HostResolver> | |||
return 8; | |||
return 10; | |||
} | |||
|
|||
bool setFail(const Poco::Timestamp & now) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
return true if status has chenged. Needs for adjusting metrics.
@@ -188,7 +219,7 @@ class HostResolver : public std::enable_shared_from_this<HostResolver> | |||
|
|||
std::mutex mutex; | |||
|
|||
Poco::Timestamp last_resolve_time TSA_GUARDED_BY(mutex); | |||
Poco::Timestamp last_resolve_time TSA_GUARDED_BY(mutex) = Poco::Timestamp::TIMEVAL_MIN; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
just to be sure that HostResolver::update is called in c-tor even if history is 0
.
|
||
addresses = std::set<String>{"127.0.0.1", "127.0.0.2", "127.0.0.3"}; | ||
addresses = std::multiset<String>{"127.0.0.1", "127.0.0.2", "127.0.0.3"}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
for testing duplicates in resolve result
@Felixoid ci in pull request skipped black check for a new .py file |
38787c4
@CheSema Is any chance to backport to 24.3 LTS? |
This is new feature. Normally we do not back port such changes. I will check if there are conflicts. |
Please don't backport features. This would introduce unnecessary risk on minor upgrades for stable releases, where we should only backport important bug fixes. |
HostResolver was changed only in 24.3, so backports to 24.2 and 23.8 are not required. |
Changelog category (leave one):
Changelog entry (a user-readable short description of the changes that goes to CHANGELOG.md):
HostResolver has each IP address several times.
If remote host has several IPs and by some reason (firewall rules for example) access on some IPs allowed and on others forbidden, than only first record of forbidden IPs marked as failed, and in each try these IPs have a chance to be chosen (and failed again).
Even if fix this, every 120 seconds DNS cache dropped, and IPs can be chosen again.
This fix
Include tests (required builds will be added automatically):