p2p/dnsdisc: re-check tree root when leaf resolution fails (#20682)

This adds additional logic to re-resolve the root name of a tree when a
couple of leaf requests have failed. We need this change to avoid
getting into a failure state where leaf requests keep failing for half
an hour when the tree has been updated.
This commit is contained in:
Felix Lange
2020-02-17 15:23:25 +01:00
committed by GitHub
parent c2117982b8
commit 57d4898e29
2 changed files with 113 additions and 18 deletions

View File

@ -19,6 +19,7 @@ package dnsdisc
import (
"context"
"crypto/ecdsa"
"errors"
"math/rand"
"reflect"
"testing"
@ -176,11 +177,62 @@ func TestIteratorNodeUpdates(t *testing.T) {
t.Fatal(err)
}
// sync the original tree.
// Sync the original tree.
resolver.add(tree1.ToTXT("n"))
checkIterator(t, it, nodes[:25])
// Update some nodes and ensure RandomNode returns the new nodes as well.
// Ensure RandomNode returns the new nodes after the tree is updated.
updateSomeNodes(nodesSeed1, nodes)
tree2, _ := makeTestTree("n", nodes, nil)
resolver.clear()
resolver.add(tree2.ToTXT("n"))
t.Log("tree updated")
clock.Run(c.cfg.RecheckInterval + 1*time.Second)
checkIterator(t, it, nodes)
}
// This test checks that the tree root is rechecked when a couple of leaf
// requests have failed. The test is just like TestIteratorNodeUpdates, but
// without advancing the clock by recheckInterval after the tree update.
func TestIteratorRootRecheckOnFail(t *testing.T) {
var (
clock = new(mclock.Simulated)
nodes = testNodes(nodesSeed1, 30)
resolver = newMapResolver()
c = NewClient(Config{
Resolver: resolver,
Logger: testlog.Logger(t, log.LvlTrace),
RecheckInterval: 20 * time.Minute,
RateLimit: 500,
// Disabling the cache is required for this test because the client doesn't
// notice leaf failures if all records are cached.
CacheLimit: 1,
})
)
c.clock = clock
tree1, url := makeTestTree("n", nodes[:25], nil)
it, err := c.NewIterator(url)
if err != nil {
t.Fatal(err)
}
// Sync the original tree.
resolver.add(tree1.ToTXT("n"))
checkIterator(t, it, nodes[:25])
// Ensure RandomNode returns the new nodes after the tree is updated.
updateSomeNodes(nodesSeed1, nodes)
tree2, _ := makeTestTree("n", nodes, nil)
resolver.clear()
resolver.add(tree2.ToTXT("n"))
t.Log("tree updated")
checkIterator(t, it, nodes)
}
// updateSomeNodes applies ENR updates to some of the given nodes.
func updateSomeNodes(keySeed int64, nodes []*enode.Node) {
keys := testKeys(nodesSeed1, len(nodes))
for i, n := range nodes[:len(nodes)/2] {
r := n.Record()
@ -190,11 +242,6 @@ func TestIteratorNodeUpdates(t *testing.T) {
n2, _ := enode.New(enode.ValidSchemes, r)
nodes[i] = n2
}
tree2, _ := makeTestTree("n", nodes, nil)
clock.Run(c.cfg.RecheckInterval + 1*time.Second)
resolver.clear()
resolver.add(tree2.ToTXT("n"))
checkIterator(t, it, nodes)
}
// This test verifies that randomIterator re-checks the root of the tree to catch
@ -230,9 +277,10 @@ func TestIteratorLinkUpdates(t *testing.T) {
// Add link to tree3, remove link to tree2.
tree1, _ = makeTestTree("t1", nodes[:10], []string{url3})
resolver.add(tree1.ToTXT("t1"))
clock.Run(c.cfg.RecheckInterval + 1*time.Second)
t.Log("tree1 updated")
clock.Run(c.cfg.RecheckInterval + 1*time.Second)
var wantNodes []*enode.Node
wantNodes = append(wantNodes, tree1.Nodes()...)
wantNodes = append(wantNodes, tree3.Nodes()...)
@ -345,5 +393,5 @@ func (mr mapResolver) LookupTXT(ctx context.Context, name string) ([]string, err
if record, ok := mr[name]; ok {
return []string{record}, nil
}
return nil, nil
return nil, errors.New("not found")
}