p2p/dnsdisc: fix hot-spin when all trees are empty (#22313)

In the random sync algorithm used by the DNS node iterator, we first pick a random
tree and then perform one sync action on that tree. This happens in a loop until any
node is found. If no trees contain any nodes, the iterator will enter a hot loop spinning
at 100% CPU.

The fix is complicated. The iterator now checks if a meaningful sync action can
be performed on any tree. If there is nothing to do, it waits for the next root record
recheck time to arrive and then tries again.

Fixes #22306
This commit is contained in:
Felix Lange
2021-02-19 09:54:46 +01:00
committed by GitHub
parent 6ec1561044
commit d36276d85e
3 changed files with 140 additions and 21 deletions

View File

@ -231,6 +231,53 @@ func TestIteratorRootRecheckOnFail(t *testing.T) {
checkIterator(t, it, nodes)
}
// This test checks that the iterator works correctly when the tree is initially empty.
func TestIteratorEmptyTree(t *testing.T) {
var (
clock = new(mclock.Simulated)
nodes = testNodes(nodesSeed1, 1)
resolver = newMapResolver()
c = NewClient(Config{
Resolver: resolver,
Logger: testlog.Logger(t, log.LvlTrace),
RecheckInterval: 20 * time.Minute,
RateLimit: 500,
})
)
c.clock = clock
tree1, url := makeTestTree("n", nil, nil)
tree2, _ := makeTestTree("n", nodes, nil)
resolver.add(tree1.ToTXT("n"))
// Start the iterator.
node := make(chan *enode.Node)
it, err := c.NewIterator(url)
if err != nil {
t.Fatal(err)
}
go func() {
it.Next()
node <- it.Node()
}()
// Wait for the client to get stuck in waitForRootUpdates.
clock.WaitForTimers(1)
// Now update the root.
resolver.add(tree2.ToTXT("n"))
// Wait for it to pick up the root change.
clock.Run(c.cfg.RecheckInterval)
select {
case n := <-node:
if n.ID() != nodes[0].ID() {
t.Fatalf("wrong node returned")
}
case <-time.After(5 * time.Second):
t.Fatal("it.Next() did not unblock within 5s of real time")
}
}
// updateSomeNodes applies ENR updates to some of the given nodes.
func updateSomeNodes(keySeed int64, nodes []*enode.Node) {
keys := testKeys(nodesSeed1, len(nodes))