diff --git a/dht_bootstrap.go b/dht_bootstrap.go index 613b7c9b65c..a448e7f2b22 100644 --- a/dht_bootstrap.go +++ b/dht_bootstrap.go @@ -8,6 +8,7 @@ import ( u "github.com/ipfs/go-ipfs-util" goprocess "github.com/jbenet/goprocess" + periodicproc "github.com/jbenet/goprocess/periodic" peer "github.com/libp2p/go-libp2p-peer" routing "github.com/libp2p/go-libp2p-routing" ) @@ -89,6 +90,26 @@ func (dht *IpfsDHT) BootstrapWithConfig(cfg BootstrapConfig) (goprocess.Process, return proc, nil } +// SignalBootstrap ensures the dht routing table remains healthy as peers come and go. +// it builds up a list of peers by requesting random peer IDs. The Bootstrap +// process will run a number of queries each time, and run every time signal fires. +// These parameters are configurable. +// +// SignalBootstrap returns a process, so the user can stop it. +func (dht *IpfsDHT) BootstrapOnSignal(cfg BootstrapConfig, signal <-chan time.Time) (goprocess.Process, error) { + if cfg.Queries <= 0 { + return nil, fmt.Errorf("invalid number of queries: %d", cfg.Queries) + } + + if signal == nil { + return nil, fmt.Errorf("invalid signal: %v", signal) + } + + proc := periodicproc.Ticker(signal, dht.bootstrapWorker(cfg)) + + return proc, nil +} + func (dht *IpfsDHT) bootstrapWorker(cfg BootstrapConfig) func(worker goprocess.Process) { return func(worker goprocess.Process) { // it would be useful to be able to send out signals of when we bootstrap, too... diff --git a/dht_test.go b/dht_test.go index 31b69dcfada..dde2f4eb952 100644 --- a/dht_test.go +++ b/dht_test.go @@ -679,10 +679,23 @@ func TestPeriodicBootstrap(t *testing.T) { } }() + signals := []chan time.Time{} + var cfg BootstrapConfig cfg = DefaultBootstrapConfig cfg.Queries = 5 + // kick off periodic bootstrappers with instrumented signals. + for _, dht := range dhts { + s := make(chan time.Time) + signals = append(signals, s) + proc, err := dht.BootstrapOnSignal(cfg, s) + if err != nil { + t.Fatal(err) + } + defer proc.Close() + } + t.Logf("dhts are not connected. %d", nDHTs) for _, dht := range dhts { rtlen := dht.routingTable.Size() @@ -708,11 +721,9 @@ func TestPeriodicBootstrap(t *testing.T) { } t.Logf("bootstrapping them so they find each other. %d", nDHTs) - for _, dht := range dhts { - _, err := dht.BootstrapWithConfig(cfg) - if err != nil { - t.Fatalf("error bootstrapping a dht: %s", err) - } + now := time.Now() + for _, signal := range signals { + go func(s chan time.Time) { s <- now }(signal) } // this is async, and we dont know when it's finished with one cycle, so keep checking