-
Notifications
You must be signed in to change notification settings - Fork 38.7k
p2p: Delay querying DNS seeds #16939
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
p2p: Delay querying DNS seeds #16939
Conversation
|
Behaviour looks like: DNS thread would previously have decided to contact seeds here. DNS thread checks for early exit here, but doesn't need one so keeps waiting. DNS thread checks again for early exit, and since there's two connections now, does: |
|
Concept ACK: seed querying should be kept to a minimum for obvious reasons |
|
Concept ACK. This doesn't make us depend on a single seed, does it? |
c9aa069 to
899d9f4
Compare
|
Fixed the logic to make it query in batches of 3 more sensibly, and bumped the delay for adding fixed seeds to 6 minutes from startup (fixed nodes are only added if addrman is empty, which will only happen if DNS seeds aren't replying -- but this may be due to the first one we try hanging, rather than all of them failing, so 6m is a compromise). Behaviour for empty peers.dat, but no ability to actually connect anywhere is to immediately query 3 random dns seeds, then wait 11 seconds and try the next 3, etc: Same deal if there's some but less than 1k entries in addrman, except it'll wait 11s first: (seed.bitcoinstats.com seems to hang for me, so I've dropped it from my chainparams, but that change isn't in this patch) |
|
The following sections might be updated with supplementary metadata relevant to reviewers and maintainers. ConflictsReviewers, this pull request conflicts with the following ones:
If you consider this pull request important, please also help to review the conflicting pull requests. Ideally, start with the one that should be merged first. |
ariard
left a comment
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.
@ajtowns what's the state of this PR ?
I think that's interesting work but title/description should be updated to inform about the 3 behavior changes:
- delay querying DNS seeds for 5min if addrman is less than 1000 (concept ACK)
- reduce set of queried DNS for fresh nodes to 3 instead of all (need to think more)
- increase fallback to hardcoded seeds for fresh nodes to 6 min instead of 60s (concept ACK)
src/net.cpp
Outdated
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 fresh nodes, addrman can't be bigger than 1000 after querying 3 dns (because nMaxIPs == 256), but if we change DNSSEEDS_TO_QUERY_AT_ONCE, we may assign seeds_wait_time before every time evaluation instead
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.
With the current code:
- if addrman starts off at 0, it'll query all DNS seeds with no delay
- if addrman starts off under 1000, it'll query all DNS seeds in groups of 3 after waiting 11 seconds before each group
- if addrman starts off over 1000, it'll query all DNS seeds in groups of 3 after waiting 5 minutes before each group
That seems mostly fine to me?
(The timeout for connecting to a peer is nConnectTimeout which is 5 seconds; so 11 seconds will pass if you happen to try three peers that timeout before getting two that succeed)
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.
Right, I think I forget we set seeds_right_now to number of seeds if if we don't have any known peers (IIRC my comment motivation)
|
This shouldn't run if the node is not currently making automatic connections (network disable or -noconnect). |
For |
899d9f4 to
5bf1665
Compare
|
Rebased, reverted delay for fixed seeds, and changed to explicitly query all dns seeds if we start up with an empty addrman. |
There is a |
|
Added patch to avoid querying DNS seeds while |
|
Concept ACK. |
src/net.cpp
Outdated
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.
As 25 seconds is a timeout for the early exit from dnsseed thread, we could do not introduce a new heuristic constant at all:
--- a/src/net.cpp
+++ b/src/net.cpp
@@ -41,9 +41,9 @@
static_assert(MINIUPNPC_API_VERSION >= 10, "miniUPnPc API version >= 10 assumed");
#endif
-#include <unordered_map>
-
+#include <chrono>
#include <math.h>
+#include <unordered_map>
// Dump addresses to peers.dat every 15 minutes (900s)
static constexpr int DUMP_PEERS_INTERVAL = 15 * 60;
@@ -1601,12 +1601,12 @@ void CConnman::ThreadDNSAddressSeed()
for (const std::string& seed : seeds) {
if (addrman.size() > 0 && seeds_right_now == 0) {
LogPrintf("Waiting %d seconds before querying DNS seeds.\n", seeds_wait_time.count());
- std::chrono::seconds to_wait = seeds_wait_time;
+ std::chrono::milliseconds to_wait{seeds_wait_time};
while (to_wait.count() > 0) {
- std::chrono::seconds w{25}; // check for early abort every 25 seconds
- if (w > to_wait) w = to_wait;
- if (!interruptNet.sleep_for(w)) return;
- to_wait -= w;
+ std::chrono::milliseconds recheck_period{nConnectTimeout};
+ if (recheck_period > to_wait) recheck_period = to_wait;
+ if (!interruptNet.sleep_for(recheck_period)) return;
+ to_wait -= recheck_period;
int nRelevant = 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.
It'd make sense for DNSSEEDS_DELAY_FEW_PEERS to be calculated from that rather than a constant, but the early exit ought to be based on how long it takes for a connection to succeed rather than the timeout time
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 the other hand, can easily reuse DNSSEEDS_DELAY_FEW_PEERS instead of a new constant, so will just do that.
|
concept ACK When DNS seeding was introduced, the idea was to be a backup, for when P2P peer addr exchange is not working for whatever reason. It seems preferable to make the polling closer to "24 hours" or "never" in the case where AddrMan is healthy and happy. Regardless of that preference, concept ACK as stated above, as this appears to be a strict improvement & resolve an issue. |
src/net.cpp
Outdated
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 seems this branch will never be executed since all of the dns seeds are explicitly queried if we start up with an empty addrman:
https://github.com/bitcoin/bitcoin/blob/5bf16653168cbc3c8bd0814cfa937d0dc8a879ff/src/net.cpp#L1582-L1585
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.
Rearranged to if (s_r_n == 0) { s_r_n += AT_ONCE; if (addrman.size() == 0) { wait(); } }
src/net.cpp
Outdated
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.
While here mind replacing a magic number 2 with a named constant?
fe0c51f to
b1338cc
Compare
|
@ajtowns I think you need to update commit message, it's still mentioning the 25s delays while you now rely on DNSSEED_DELAY_FEW_PEERS? |
sipa
left a comment
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.
utACK b1338cc25c0d66a73aea760117e488b6c91d0d43
If 1000 potential peers are known, wait for 5m before querying DNS seeds for more peers, since eventually the addresses we already know should get us connected. Also check every 11s whether we've got enough active outbounds that DNS seeds aren't worth querying, and exit the dnsseed thread early if so.
b1338cc to
96954d1
Compare
hebasto
left a comment
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.
Tested 96954d1 on Linux Mint 19.3: works as expected.
| if (nRelevant >= 2) { | ||
| LogPrintf("P2P peers available. Skipped DNS seeding.\n"); | ||
| return; | ||
| if (addrman.size() > 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.
Considering
Lines 1582 to 1585 in 96954d1
| } else if (addrman.size() == 0) { | |
| // If we have no known peers, query all. | |
| seeds_right_now = seeds.size(); | |
| } |
Line 1602 in 96954d1
| if (seeds_right_now == 0) { |
this point is not reachable when addrman.size() == 0, and addrman.size() > 0 will always be evaluated to true, so it could be dropped, no?
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 think you are right @hebasto. Although it took 5 minutes for me to confirm. I think if we remove this redundant check, a comment should be added.
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 we did reach it with addrman.size() == 0, waiting would be pointless, so I don't think the logic is wrong; there's no else so there's no unreachable code, just a conditional branch that always happens to go one way. I think double checking is a bit more robust in case we change the code to querying some subset of dns seeds when addrman is empty in future, eg if we had dozens of dns seeds available.
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.
in case we change the code to querying some subset of dns seeds when addrman is empty in future
That's worth explaining in a comment. It may also be more readable to have a bool skip_wait
ariard
left a comment
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.
Tested ACK 96954d1, on Debian 9.1. Both MANY_PEERS/FEW_PEERS cases work.
2020-04-22T22:13:40Z Loaded 3579 addresses from peers.dat 10ms
2020-04-22T22:13:40Z init message: Starting network threads...
2020-04-22T22:13:40Z net thread start
2020-04-22T22:13:40Z addcon thread start
2020-04-22T22:13:40Z Using /16 prefix for IP bucketing.
2020-04-22T22:13:40Z init message: Done loading
2020-04-22T22:13:40Z msghand thread start
2020-04-22T22:13:40Z dnsseed thread start
2020-04-22T22:13:40Z Waiting 300 seconds before querying DNS seeds.
2020-04-22T22:18:40Z Loading addresses from DNS seed dnsseed.bitcoin.dashjr.org
2020-04-22T22:18:40Z Loading addresses from DNS seed seed.bitcoinstats.com
2020-04-22T22:18:41Z Loading addresses from DNS seed seed.bitcoin.sipa.be
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.
utACK 96954d1
Feel free to ignore comment suggestions if you’re not force-pushing any other fixes.
| */ | ||
| static constexpr std::chrono::seconds DNSSEEDS_DELAY_FEW_PEERS{11}; // 11sec | ||
| static constexpr std::chrono::seconds DNSSEEDS_DELAY_MANY_PEERS{300}; // 5min | ||
| static constexpr int DNSSEEDS_DELAY_PEER_THRESHOLD = 1000; // "many" vs "few" peers -- you should only get this many if you've been on the live network |
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 think we should expand this comment into couple sentences. What's "live network" is unclear. What if we have 500 peers, is that not a live network?
Would be also nice to briefly rationalize this distinction (yes, one more time).
Also, long inline comments like this are hard to read :)
| if (gArgs.GetBoolArg("-forcednsseed", DEFAULT_FORCEDNSSEED)) { | ||
| // When -forcednsseed is provided, query all. | ||
| seeds_right_now = seeds.size(); | ||
| } else if (addrman.size() == 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.
Comment here would be nice too. What exactly this tells us?
For example, I spent time thinking "what if a node was connected to 1 node via --connect". But then they'd still probably have a big addrman (all learned from that peer)...
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 tells us we don't have any known peers (there's already a comment to that effect) -- perhaps this is the first time you've started bitcoind or you've just restarted after deleting peers.dat. If you previously queried the dns seeds, or have at some point connected to some other peers that aren't in IBD, this shouldn't be true any more.
| if (nRelevant >= 2) { | ||
| LogPrintf("P2P peers available. Skipped DNS seeding.\n"); | ||
| return; | ||
| if (addrman.size() > 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.
I think you are right @hebasto. Although it took 5 minutes for me to confirm. I think if we remove this redundant check, a comment should be added.
|
@ajtowns Would you prefer to add a new commit with documentation or instead have this merged first or something else? |
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.
tACK 96954d1 (rebased on master) on macOS 10.15.4. It found it useful to run with -debug=addrman and change DNSSEEDS_DELAY_MANY_PEERS to something lower to test the behaviour, as well as renaming peers.dat to test the peer threshold.
The second commit can be tested by calling bitcoin-cli setnetworkactive false within DNSSEEDS_DELAY_MANY_PEERS seconds of the DNS thread starting.
Consider adding the improved documentation as a new commit on top of this PR. That doesn't break any reviews, but it makes it easier to squash if you need to change anything else.
| seeds_right_now = seeds.size(); | ||
| } else if (addrman.size() == 0) { | ||
| // If we have no known peers, query all. | ||
| seeds_right_now = seeds.size(); |
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.
This undoes #15558 for the first run. No strong opinion on the matter, but it should probably be in the PR and commit description. It's also not mentioned in the comments below.
Also, both here and in the if branch it's useful to comment that seeds_right_now > 0 causes the wait to be skipped.
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.
In #15558 the first run will set seeds_right_now = 0 then see that addrman.size() > 0 && seeds_right_now == 0 is false (since addrman.size() == 0) so will not sleep nor bump seeds_right_now by DNSSEEDS_TO_QUERY_AT_ONCE, it will then query a seed, and decrement seeds_right_now so that it's -1, at which point addrman.size() > 0 && seeds_right_now == 0 will continue being false (because seeds_right_now < 0) and will query all seeds without waiting between them.
This retains that behaviour, but is explicit about it. See suhas's comment on 15558
| if (nRelevant >= 2) { | ||
| LogPrintf("P2P peers available. Skipped DNS seeding.\n"); | ||
| return; | ||
| if (addrman.size() > 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.
in case we change the code to querying some subset of dns seeds when addrman is empty in future
That's worth explaining in a comment. It may also be more readable to have a bool skip_wait
| LogPrintf("Waiting %d seconds before querying DNS seeds.\n", seeds_wait_time.count()); | ||
| std::chrono::seconds to_wait = seeds_wait_time; | ||
| while (to_wait.count() > 0) { | ||
| std::chrono::seconds w = std::min(DNSSEEDS_DELAY_FEW_PEERS, to_wait); |
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.
Could use a comment of why you're sleeping in shorter intervals: because we can exit this thread once we have enough nodes.
fanquake
left a comment
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.
ACK 96954d1 - Ran some tests of different scenarios. More documentation is being added in #19084.
# already had a large peers.dat
src/bitcoind | rg dnsseed
2020-05-29T10:00:29.745627Z [dnsseed] dnsseed thread start
2020-05-29T10:00:29.745657Z [dnsseed] Waiting 300 seconds before querying DNS seeds.
2020-05-29T10:00:40.745989Z [dnsseed] P2P peers available. Skipped DNS seeding.
2020-05-29T10:00:40.746071Z [dnsseed] dnsseed thread exit
# deleted peers.dat
src/bitcoind | rg dnsseed
2020-05-29T10:01:38.673439Z [dnsseed] dnsseed thread start
2020-05-29T10:01:38.673508Z [dnsseed] Loading addresses from DNS seed dnsseed.bitcoin.dashjr.org
2020-05-29T10:01:38.674948Z [dnsseed] Loading addresses from DNS seed dnsseed.emzy.de
2020-05-29T10:01:39.681687Z [dnsseed] Loading addresses from DNS seed dnsseed.bluematt.me
2020-05-29T10:01:40.081659Z [dnsseed] Loading addresses from DNS seed seed.bitcoinstats.com
2020-05-29T10:01:40.705362Z [dnsseed] Loading addresses from DNS seed seed.bitcoin.jonasschnelli.ch
2020-05-29T10:01:41.014162Z [dnsseed] Loading addresses from DNS seed seed.bitcoin.sprovoost.nl
2020-05-29T10:01:42.539538Z [dnsseed] Loading addresses from DNS seed seed.btc.petertodd.org
2020-05-29T10:01:45.568217Z [dnsseed] Loading addresses from DNS seed seed.bitcoin.sipa.be
2020-05-29T10:01:45.570067Z [dnsseed] 305 addresses found from DNS seeds
2020-05-29T10:01:45.570103Z [dnsseed] dnsseed thread exit
# peers.dat < 1000 addresses
src/bitcoind | rg dnsseed
2020-05-29T10:04:03.566819Z [dnsseed] dnsseed thread start
2020-05-29T10:04:03.566983Z [dnsseed] Waiting 11 seconds before querying DNS seeds.
2020-05-29T10:04:14.567539Z [dnsseed] P2P peers available. Skipped DNS seeding.
2020-05-29T10:04:14.567643Z [dnsseed] dnsseed thread exit
# -connect=0
src/bitcoind -connect=0 | rg dnsseed
2020-05-29T10:05:19.064542Z [init] InitParameterInteraction: parameter interaction: -connect set -> setting -dnsseed=0
# called setnetworkactive false early
src/bitcoind | rg dnsseed
2020-05-29T10:07:31.399566Z [dnsseed] dnsseed thread start
2020-05-29T10:07:31.399595Z [dnsseed] Waiting 300 seconds before querying DNS seeds.
2020-05-29T10:12:31.466326Z [dnsseed] Waiting for network to be reactivated before querying DNS seeds.
2020-05-29T10:14:20.753947Z [dnsseed] Loading addresses from DNS seed dnsseed.emzy.de
2020-05-29T10:14:20.762381Z [dnsseed] Loading addresses from DNS seed dnsseed.bitcoin.dashjr.org
2020-05-29T10:14:20.763729Z [dnsseed] Loading addresses from DNS seed seed.bitcoin.jonasschnelli.ch
2020-05-29T10:14:20.765102Z [dnsseed] Waiting 300 seconds before querying DNS seeds.
2020-05-29T10:14:42.766527Z [dnsseed] 113 addresses found from DNS seeds
2020-05-29T10:14:42.766629Z [dnsseed] P2P peers available. Finished DNS seeding.
2020-05-29T10:14:42.766673Z [dnsseed] dnsseed thread exit
src/bitcoind | rg dnsseed
2020-05-29T10:15:02.325759Z [dnsseed] dnsseed thread start
2020-05-29T10:15:02.325804Z [dnsseed] Waiting 300 seconds before querying DNS seeds.
2020-05-29T10:15:13.326011Z [dnsseed] P2P peers available. Skipped DNS seeding.
2020-05-29T10:15:13.326123Z [dnsseed] dnsseed thread exit96954d1 DNS seeds: don't query DNS while network is inactive (Anthony Towns) fa5894f DNS seeds: wait for 5m instead of 11s if 1000+ peers are known (Anthony Towns) Pull request description: Changes the logic for querying DNS seeds: after this PR, if there's less than 1000 entries in addrman, it will still usually query DNS seeds after 11s (unless the first few peers tried mostly succeed), but if there's more than 1000 entries it won't try DNS seeds until 5 minutes have passed without getting multiple outbound peers. (If there's 0 entries in addrman, it will still immediately query the DNS seeds). Additionally, delays querying DNS seeds while the p2p network is not active. Fixes bitcoin#15434 ACKs for top commit: fanquake: ACK 96954d1 - Ran some tests of different scenarios. More documentation is being added in bitcoin#19084. ariard: Tested ACK 96954d1, on Debian 9.1. Both MANY_PEERS/FEW_PEERS cases work. Sjors: tACK 96954d1 (rebased on master) on macOS 10.15.4. It found it useful to run with `-debug=addrman` and change `DNSSEEDS_DELAY_MANY_PEERS` to something lower to test the behaviour, as well as renaming `peers.dat` to test the peer threshold. naumenkogs: utACK 96954d1 Tree-SHA512: 73693db3da73bf8e76c3df9e9c82f0a7fb08049187356eac2575c4ffa455f76548dd1c86a11fc6beea8a3baf0ba020e047bebe927883c731383ec72442356005
5cb7ee6 net: improve code documentation for dns seed behaviour (Anthony Towns) Pull request description: Some better internal documentation post #16939 ACKs for top commit: hebasto: ACK 5cb7ee6 naumenkogs: ACK 5cb7ee6 ariard: ACK 5cb7ee6 fanquake: ACK 5cb7ee6 - thanks for following up. Tree-SHA512: 5a12680651cd1e0436aed1cadcbf50047ffeabfdc9f7f2f81fa176c30b10673fc960154c73ec34ed08ace1a000a81cedd44d67587883152654dee46065991b45
…aviour 5cb7ee6 net: improve code documentation for dns seed behaviour (Anthony Towns) Pull request description: Some better internal documentation post bitcoin#16939 ACKs for top commit: hebasto: ACK 5cb7ee6 naumenkogs: ACK 5cb7ee6 ariard: ACK 5cb7ee6 fanquake: ACK 5cb7ee6 - thanks for following up. Tree-SHA512: 5a12680651cd1e0436aed1cadcbf50047ffeabfdc9f7f2f81fa176c30b10673fc960154c73ec34ed08ace1a000a81cedd44d67587883152654dee46065991b45
Summary: ``` If 1000 potential peers are known, wait for 5m before querying DNS seeds for more peers, since eventually the addresses we already know should get us connected. Also check every 11s whether we've got enough active outbounds that DNS seeds aren't worth querying, and exit the dnsseed thread early if so. DNS seeds: don't query DNS while network is inactive ``` Backport of core [[bitcoin/bitcoin#16939 | PR16939]]. Test Plan: ninja all check-all Check IBD starts as expected. Reviewers: #bitcoin_abc, deadalnix Reviewed By: #bitcoin_abc, deadalnix Differential Revision: https://reviews.bitcoinabc.org/D8512
Changes the logic for querying DNS seeds: after this PR, if there's less than 1000 entries in addrman, it will still usually query DNS seeds after 11s (unless the first few peers tried mostly succeed), but if there's more than 1000 entries it won't try DNS seeds until 5 minutes have passed without getting multiple outbound peers. (If there's 0 entries in addrman, it will still immediately query the DNS seeds). Additionally, delays querying DNS seeds while the p2p network is not active.
Fixes #15434