Merge pull request #1118 from unihorn/133
etcdserver: add TestRecvSnapshot
This commit is contained in:
@ -13,6 +13,7 @@ import (
|
||||
"github.com/coreos/etcd/raft"
|
||||
"github.com/coreos/etcd/raft/raftpb"
|
||||
"github.com/coreos/etcd/store"
|
||||
"github.com/coreos/etcd/testutil"
|
||||
"github.com/coreos/etcd/third_party/code.google.com/p/go.net/context"
|
||||
)
|
||||
|
||||
@ -508,9 +509,6 @@ func TestSnapshot(t *testing.T) {
|
||||
}
|
||||
|
||||
// Applied > SnapCount should trigger a SaveSnap event
|
||||
// TODO: receive a snapshot from raft leader should also be able
|
||||
// to trigger snapSave and also trigger a store.Recover.
|
||||
// We need fake node!
|
||||
func TestTriggerSnap(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
n := raft.StartNode(0xBAD0, []int64{0xBAD0}, 10, 1)
|
||||
@ -543,6 +541,63 @@ func TestTriggerSnap(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestRecvSnapshot tests when it receives a snapshot from raft leader,
|
||||
// it should trigger storage.SaveSnap and also store.Recover.
|
||||
func TestRecvSnapshot(t *testing.T) {
|
||||
n := newReadyNode()
|
||||
st := &storeRecorder{}
|
||||
p := &storageRecorder{}
|
||||
s := &EtcdServer{
|
||||
Store: st,
|
||||
Send: func(_ []raftpb.Message) {},
|
||||
Storage: p,
|
||||
Node: n,
|
||||
}
|
||||
|
||||
s.Start()
|
||||
n.readyc <- raft.Ready{Snapshot: raftpb.Snapshot{Index: 1}}
|
||||
// make goroutines move forward to receive snapshot
|
||||
testutil.ForceGosched()
|
||||
s.Stop()
|
||||
|
||||
waction := []string{"Recovery"}
|
||||
if g := st.Action(); !reflect.DeepEqual(g, waction) {
|
||||
t.Errorf("store action = %v, want %v", g, waction)
|
||||
}
|
||||
waction = []string{"Save", "SaveSnap"}
|
||||
if g := p.Action(); !reflect.DeepEqual(g, waction) {
|
||||
t.Errorf("storage action = %v, want %v", g, waction)
|
||||
}
|
||||
}
|
||||
|
||||
// TestRecvSlowSnapshot tests that slow snapshot will not be applied
|
||||
// to store.
|
||||
func TestRecvSlowSnapshot(t *testing.T) {
|
||||
n := newReadyNode()
|
||||
st := &storeRecorder{}
|
||||
s := &EtcdServer{
|
||||
Store: st,
|
||||
Send: func(_ []raftpb.Message) {},
|
||||
Storage: &storageRecorder{},
|
||||
Node: n,
|
||||
}
|
||||
|
||||
s.Start()
|
||||
n.readyc <- raft.Ready{Snapshot: raftpb.Snapshot{Index: 1}}
|
||||
// make goroutines move forward to receive snapshot
|
||||
testutil.ForceGosched()
|
||||
action := st.Action()
|
||||
|
||||
n.readyc <- raft.Ready{Snapshot: raftpb.Snapshot{Index: 1}}
|
||||
// make goroutines move forward to receive snapshot
|
||||
testutil.ForceGosched()
|
||||
s.Stop()
|
||||
|
||||
if g := st.Action(); !reflect.DeepEqual(g, action) {
|
||||
t.Errorf("store action = %v, want %v", g, action)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: test wait trigger correctness in multi-server case
|
||||
|
||||
func TestGetBool(t *testing.T) {
|
||||
@ -626,7 +681,10 @@ func (s *storeRecorder) Save() ([]byte, error) {
|
||||
s.record("Save")
|
||||
return nil, nil
|
||||
}
|
||||
func (s *storeRecorder) Recovery(b []byte) error { return nil }
|
||||
func (s *storeRecorder) Recovery(b []byte) error {
|
||||
s.record("Recovery")
|
||||
return nil
|
||||
}
|
||||
func (s *storeRecorder) TotalTransactions() uint64 { return 0 }
|
||||
func (s *storeRecorder) JsonStats() []byte { return nil }
|
||||
func (s *storeRecorder) DeleteExpiredKeys(cutoff time.Time) {
|
||||
@ -687,6 +745,25 @@ func (p *storageRecorder) SaveSnap(st raftpb.Snapshot) {
|
||||
p.record("SaveSnap")
|
||||
}
|
||||
|
||||
type readyNode struct {
|
||||
readyc chan raft.Ready
|
||||
}
|
||||
|
||||
func newReadyNode() *readyNode {
|
||||
readyc := make(chan raft.Ready, 1)
|
||||
return &readyNode{readyc: readyc}
|
||||
}
|
||||
func (n *readyNode) Tick() {}
|
||||
func (n *readyNode) Campaign(ctx context.Context) error { return nil }
|
||||
func (n *readyNode) Propose(ctx context.Context, data []byte) error { return nil }
|
||||
func (n *readyNode) Configure(ctx context.Context, data []byte) error { return nil }
|
||||
func (n *readyNode) Step(ctx context.Context, msg raftpb.Message) error { return nil }
|
||||
func (n *readyNode) Ready() <-chan raft.Ready { return n.readyc }
|
||||
func (n *readyNode) Stop() {}
|
||||
func (n *readyNode) Compact(d []byte) {}
|
||||
func (n *readyNode) AddNode(id int64) {}
|
||||
func (n *readyNode) RemoveNode(id int64) {}
|
||||
|
||||
func TestGenID(t *testing.T) {
|
||||
// Sanity check that the GenID function has been seeded appropriately
|
||||
// (math/rand is seeded with 1 by default)
|
||||
|
@ -2,11 +2,11 @@ package raft
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"runtime"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/coreos/etcd/raft/raftpb"
|
||||
"github.com/coreos/etcd/testutil"
|
||||
"github.com/coreos/etcd/third_party/code.google.com/p/go.net/context"
|
||||
)
|
||||
|
||||
@ -96,7 +96,7 @@ func TestBlockProposal(t *testing.T) {
|
||||
errc <- n.Propose(context.TODO(), []byte("somedata"))
|
||||
}()
|
||||
|
||||
forceGosched()
|
||||
testutil.ForceGosched()
|
||||
select {
|
||||
case err := <-errc:
|
||||
t.Errorf("err = %v, want blocking", err)
|
||||
@ -104,7 +104,7 @@ func TestBlockProposal(t *testing.T) {
|
||||
}
|
||||
|
||||
n.Campaign(context.TODO())
|
||||
forceGosched()
|
||||
testutil.ForceGosched()
|
||||
select {
|
||||
case err := <-errc:
|
||||
if err != nil {
|
||||
@ -216,7 +216,7 @@ func TestCompact(t *testing.T) {
|
||||
Nodes: []int64{1},
|
||||
}
|
||||
|
||||
forceGosched()
|
||||
testutil.ForceGosched()
|
||||
select {
|
||||
case <-n.Ready():
|
||||
default:
|
||||
@ -224,7 +224,7 @@ func TestCompact(t *testing.T) {
|
||||
}
|
||||
|
||||
n.Compact(w.Data)
|
||||
forceGosched()
|
||||
testutil.ForceGosched()
|
||||
select {
|
||||
case rd := <-n.Ready():
|
||||
if !reflect.DeepEqual(rd.Snapshot, w) {
|
||||
@ -233,7 +233,7 @@ func TestCompact(t *testing.T) {
|
||||
default:
|
||||
t.Fatalf("unexpected compact failure: unable to create a snapshot")
|
||||
}
|
||||
forceGosched()
|
||||
testutil.ForceGosched()
|
||||
// TODO: this test the run updates the snapi correctly... should be tested
|
||||
// separately with other kinds of updates
|
||||
select {
|
||||
@ -265,12 +265,3 @@ func TestIsStateEqual(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// WARNING: This is a hack.
|
||||
// Remove this when we are able to block/check the status of the go-routines.
|
||||
func forceGosched() {
|
||||
// possibility enough to sched upto 10 go routines.
|
||||
for i := 0; i < 10000; i++ {
|
||||
runtime.Gosched()
|
||||
}
|
||||
}
|
||||
|
14
testutil/testutil.go
Normal file
14
testutil/testutil.go
Normal file
@ -0,0 +1,14 @@
|
||||
package testutil
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
)
|
||||
|
||||
// WARNING: This is a hack.
|
||||
// Remove this when we are able to block/check the status of the go-routines.
|
||||
func ForceGosched() {
|
||||
// possibility enough to sched upto 10 go routines.
|
||||
for i := 0; i < 10000; i++ {
|
||||
runtime.Gosched()
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user