-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #77 from SiaFoundation/syncer-shutdown
syncer: Add Close and ensure goroutines exit
- Loading branch information
Showing
4 changed files
with
210 additions
and
16 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
package syncer_test | ||
|
||
import ( | ||
"context" | ||
"net" | ||
"testing" | ||
"time" | ||
|
||
"go.sia.tech/core/gateway" | ||
"go.sia.tech/core/types" | ||
"go.sia.tech/coreutils/chain" | ||
"go.sia.tech/coreutils/syncer" | ||
"go.sia.tech/coreutils/testutil" | ||
"go.uber.org/zap/zaptest" | ||
) | ||
|
||
func TestSyncer(t *testing.T) { | ||
log := zaptest.NewLogger(t) | ||
|
||
n, genesis := testutil.Network() | ||
store1, tipState1, err := chain.NewDBStore(chain.NewMemDB(), n, genesis) | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
cm1 := chain.NewManager(store1, tipState1) | ||
|
||
store2, tipState2, err := chain.NewDBStore(chain.NewMemDB(), n, genesis) | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
cm2 := chain.NewManager(store2, tipState2) | ||
|
||
l1, err := net.Listen("tcp", ":0") | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
defer l1.Close() | ||
|
||
l2, err := net.Listen("tcp", ":0") | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
defer l2.Close() | ||
|
||
s1 := syncer.New(l1, cm1, testutil.NewMemPeerStore(), gateway.Header{ | ||
GenesisID: genesis.ID(), | ||
UniqueID: gateway.GenerateUniqueID(), | ||
NetAddress: l1.Addr().String(), | ||
}, syncer.WithLogger(log.Named("syncer1"))) | ||
defer s1.Close() | ||
go s1.Run() | ||
|
||
s2 := syncer.New(l2, cm2, testutil.NewMemPeerStore(), gateway.Header{ | ||
GenesisID: genesis.ID(), | ||
UniqueID: gateway.GenerateUniqueID(), | ||
NetAddress: l2.Addr().String(), | ||
}, syncer.WithLogger(log.Named("syncer2")), syncer.WithSyncInterval(10*time.Millisecond)) | ||
defer s2.Close() | ||
go s2.Run() | ||
|
||
// mine a few blocks on cm1 | ||
testutil.MineBlocks(t, cm1, types.VoidAddress, 10) | ||
// mine less blocks on cm2 | ||
testutil.MineBlocks(t, cm2, types.VoidAddress, 5) | ||
|
||
if cm1.Tip().Height != 10 { | ||
t.Fatalf("expected cm1 tip height to be 10, got %v", cm1.Tip().Height) | ||
} else if cm2.Tip().Height != 5 { | ||
t.Fatalf("expected cm2 tip height to be 5, got %v", cm2.Tip().Height) | ||
} | ||
|
||
// connect the syncers | ||
if _, err := s1.Connect(context.Background(), l2.Addr().String()); err != nil { | ||
t.Fatal(err) | ||
} | ||
// broadcast blocks from s1 | ||
b, ok := cm1.Block(cm1.Tip().ID) | ||
if !ok { | ||
t.Fatal("failed to get block") | ||
} | ||
|
||
// broadcast the tip from s1 to s2 | ||
s1.BroadcastHeader(gateway.BlockHeader{ | ||
ParentID: b.ParentID, | ||
Nonce: b.Nonce, | ||
Timestamp: b.Timestamp, | ||
MerkleRoot: b.MerkleRoot(), | ||
}) | ||
|
||
for i := 0; i < 100; i++ { | ||
if cm1.Tip() == cm2.Tip() { | ||
break | ||
} | ||
time.Sleep(100 * time.Millisecond) | ||
} | ||
|
||
if cm1.Tip() != cm2.Tip() { | ||
t.Fatalf("tips are not equal: %v != %v", cm1.Tip(), cm2.Tip()) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
package testutil | ||
|
||
import ( | ||
"sync" | ||
"time" | ||
|
||
"go.sia.tech/coreutils/syncer" | ||
) | ||
|
||
// A MemPeerStore is an in-memory implementation of a PeerStore. | ||
type MemPeerStore struct { | ||
mu sync.Mutex | ||
peers map[string]syncer.PeerInfo | ||
} | ||
|
||
// AddPeer adds a peer to the store. If the peer already exists, nil should | ||
// be returned. | ||
func (ps *MemPeerStore) AddPeer(addr string) error { | ||
ps.mu.Lock() | ||
defer ps.mu.Unlock() | ||
if _, ok := ps.peers[addr]; ok { | ||
return nil | ||
} | ||
ps.peers[addr] = syncer.PeerInfo{Address: addr} | ||
return nil | ||
} | ||
|
||
// Peers returns the set of known peers. | ||
func (ps *MemPeerStore) Peers() ([]syncer.PeerInfo, error) { | ||
ps.mu.Lock() | ||
defer ps.mu.Unlock() | ||
var peers []syncer.PeerInfo | ||
for _, p := range ps.peers { | ||
peers = append(peers, p) | ||
} | ||
return peers, nil | ||
} | ||
|
||
// PeerInfo returns the metadata for the specified peer or ErrPeerNotFound | ||
// if the peer wasn't found in the store. | ||
func (ps *MemPeerStore) PeerInfo(addr string) (syncer.PeerInfo, error) { | ||
ps.mu.Lock() | ||
defer ps.mu.Unlock() | ||
p, ok := ps.peers[addr] | ||
if !ok { | ||
return syncer.PeerInfo{}, syncer.ErrPeerNotFound | ||
} | ||
return p, nil | ||
} | ||
|
||
// UpdatePeerInfo updates the metadata for the specified peer. If the peer | ||
// is not found, the error should be ErrPeerNotFound. | ||
func (ps *MemPeerStore) UpdatePeerInfo(addr string, fn func(*syncer.PeerInfo)) error { | ||
ps.mu.Lock() | ||
defer ps.mu.Unlock() | ||
p := syncer.PeerInfo{} | ||
fn(&p) | ||
ps.peers[addr] = p | ||
return nil | ||
} | ||
|
||
// Ban temporarily bans one or more IPs. The addr should either be a single | ||
// IP with port (e.g. 1.2.3.4:5678) or a CIDR subnet (e.g. 1.2.3.4/16). | ||
func (ps *MemPeerStore) Ban(addr string, duration time.Duration, reason string) error { | ||
return nil | ||
} | ||
|
||
// Banned returns false | ||
func (ps *MemPeerStore) Banned(addr string) (bool, error) { return false, nil } | ||
|
||
var _ syncer.PeerStore = (*MemPeerStore)(nil) | ||
|
||
// NewMemPeerStore returns a new MemPeerStore. | ||
func NewMemPeerStore() *MemPeerStore { | ||
return &MemPeerStore{ | ||
peers: make(map[string]syncer.PeerInfo), | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters