123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276 |
- package chord_test
- import (
- "runtime"
- "testing"
- "time"
- "trial/chord"
- )
- type MultiLocalTrans struct {
- remote chord.Transport
- hosts map[string]*chord.LocalTransport
- }
- func InitMLTransport() *MultiLocalTrans {
- hosts := make(map[string]*chord.LocalTransport)
- remote := &chord.BlackholeTransport{}
- ml := &MultiLocalTrans{hosts: hosts}
- ml.remote = remote
- return ml
- }
- func (ml *MultiLocalTrans) ListVnodes(host string) ([]*chord.Vnode, error) {
- if local, ok := ml.hosts[host]; ok {
- return local.ListVnodes(host)
- }
- return ml.remote.ListVnodes(host)
- }
- // Ping a Vnode, check for liveness
- func (ml *MultiLocalTrans) Ping(v *chord.Vnode) (bool, error) {
- if local, ok := ml.hosts[v.Host]; ok {
- return local.Ping(v)
- }
- return ml.remote.Ping(v)
- }
- // Request a nodes predecessor
- func (ml *MultiLocalTrans) GetPredecessor(v *chord.Vnode) (*chord.Vnode, error) {
- if local, ok := ml.hosts[v.Host]; ok {
- return local.GetPredecessor(v)
- }
- return ml.remote.GetPredecessor(v)
- }
- // Notify our successor of ourselves
- func (ml *MultiLocalTrans) Notify(target, self *chord.Vnode) ([]*chord.Vnode, error) {
- if local, ok := ml.hosts[target.Host]; ok {
- return local.Notify(target, self)
- }
- return ml.remote.Notify(target, self)
- }
- // Find a successor
- func (ml *MultiLocalTrans) FindSuccessors(v *chord.Vnode, n int, k []byte) ([]*chord.Vnode, error) {
- if local, ok := ml.hosts[v.Host]; ok {
- return local.FindSuccessors(v, n, k)
- }
- return ml.remote.FindSuccessors(v, n, k)
- }
- // Clears a predecessor if it matches a given vnode. Used to leave.
- func (ml *MultiLocalTrans) ClearPredecessor(target, self *chord.Vnode) error {
- if local, ok := ml.hosts[target.Host]; ok {
- return local.ClearPredecessor(target, self)
- }
- return ml.remote.ClearPredecessor(target, self)
- }
- // Instructs a node to skip a given successor. Used to leave.
- func (ml *MultiLocalTrans) SkipSuccessor(target, self *chord.Vnode) error {
- if local, ok := ml.hosts[target.Host]; ok {
- return local.SkipSuccessor(target, self)
- }
- return ml.remote.SkipSuccessor(target, self)
- }
- func (ml *MultiLocalTrans) Register(v *chord.Vnode, o chord.VnodeRPC) {
- local, ok := ml.hosts[v.Host]
- if !ok {
- local = chord.InitLocalTransport(nil).(*chord.LocalTransport)
- ml.hosts[v.Host] = local
- }
- local.Register(v, o)
- }
- func (ml *MultiLocalTrans) Deregister(host string) {
- delete(ml.hosts, host)
- }
- func TestDefaultConfig(t *testing.T) {
- conf := chord.DefaultConfig("test")
- if conf.Hostname != "test" {
- t.Fatalf("bad hostname")
- }
- if conf.NumVnodes != 8 {
- t.Fatalf("bad num Vnodes")
- }
- if conf.NumSuccessors != 8 {
- t.Fatalf("bad num succ")
- }
- if conf.HashFunc == nil {
- t.Fatalf("bad hash")
- }
- if conf.HashBits != 160 {
- t.Fatalf("bad hash bits")
- }
- if conf.StabilizeMin != time.Duration(15*time.Second) {
- t.Fatalf("bad min stable")
- }
- if conf.StabilizeMax != time.Duration(45*time.Second) {
- t.Fatalf("bad max stable")
- }
- if conf.Delegate != nil {
- t.Fatalf("bad delegate")
- }
- }
- func fastConf() *chord.Config {
- conf := chord.DefaultConfig("test")
- conf.StabilizeMin = time.Duration(15 * time.Millisecond)
- conf.StabilizeMax = time.Duration(45 * time.Millisecond)
- return conf
- }
- func TestCreateShutdown(t *testing.T) {
- // Start the timer thread
- time.After(15)
- conf := fastConf()
- numGo := runtime.NumGoroutine()
- r, err := chord.Create(conf, nil)
- if err != nil {
- t.Fatalf("unexpected err. %s", err)
- }
- r.Shutdown()
- after := runtime.NumGoroutine()
- if after != numGo {
- t.Fatalf("unexpected routines! A:%d B:%d", after, numGo)
- }
- }
- func TestJoin(t *testing.T) {
- // Create a multi transport
- ml := InitMLTransport()
- // Create the initial ring
- conf := fastConf()
- r, err := chord.Create(conf, ml)
- if err != nil {
- t.Fatalf("unexpected err. %s", err)
- }
- // Create a second ring
- conf2 := fastConf()
- conf2.Hostname = "test2"
- r2, err := chord.Join(conf2, ml, "test")
- if err != nil {
- t.Fatalf("failed to join local node! Got %s", err)
- }
- // Shutdown
- r.Shutdown()
- r2.Shutdown()
- }
- func TestJoinDeadHost(t *testing.T) {
- // Create a multi transport
- ml := InitMLTransport()
- // Create the initial ring
- conf := fastConf()
- _, err := chord.Join(conf, ml, "noop")
- if err == nil {
- t.Fatalf("expected err!")
- }
- }
- func TestLeave(t *testing.T) {
- // Create a multi transport
- ml := InitMLTransport()
- // Create the initial ring
- conf := fastConf()
- r, err := chord.Create(conf, ml)
- if err != nil {
- t.Fatalf("unexpected err. %s", err)
- }
- // Create a second ring
- conf2 := fastConf()
- conf2.Hostname = "test2"
- r2, err := chord.Join(conf2, ml, "test")
- if err != nil {
- t.Fatalf("failed to join local node! Got %s", err)
- }
- // Wait for some stabilization
- <-time.After(100 * time.Millisecond)
- // Node 1 should leave
- r.Leave()
- ml.Deregister("test")
- // Wait for stabilization
- <-time.After(100 * time.Millisecond)
- // Verify r2 ring is still in tact
- num := len(r2.Vnodes)
- for idx, vn := range r2.Vnodes {
- if vn.Successors[0] != &r2.Vnodes[(idx+1)%num].Vnode {
- t.Fatalf("bad successor! Got:%s:%s", vn.Successors[0].Host,
- vn.Successors[0])
- }
- }
- }
- func TestLookupBadN(t *testing.T) {
- // Create a multi transport
- ml := InitMLTransport()
- // Create the initial ring
- conf := fastConf()
- r, err := chord.Create(conf, ml)
- if err != nil {
- t.Fatalf("unexpected err. %s", err)
- }
- _, err = r.Lookup(10, []byte("test"))
- if err == nil {
- t.Fatalf("expected err!")
- }
- }
- func TestLookup(t *testing.T) {
- // Create a multi transport
- ml := InitMLTransport()
- // Create the initial ring
- conf := fastConf()
- r, err := chord.Create(conf, ml)
- if err != nil {
- t.Fatalf("unexpected err. %s", err)
- }
- // Create a second ring
- conf2 := fastConf()
- conf2.Hostname = "test2"
- r2, err := chord.Join(conf2, ml, "test")
- if err != nil {
- t.Fatalf("failed to join local node! Got %s", err)
- }
- // Wait for some stabilization
- <-time.After(100 * time.Millisecond)
- // Try key lookup
- keys := [][]byte{[]byte("test"), []byte("foo"), []byte("bar")}
- for _, k := range keys {
- vn1, err := r.Lookup(3, k)
- if err != nil {
- t.Fatalf("unexpected err %s", err)
- }
- vn2, err := r2.Lookup(3, k)
- if err != nil {
- t.Fatalf("unexpected err %s", err)
- }
- if len(vn1) != len(vn2) {
- t.Fatalf("result len differs!")
- }
- for idx := range vn1 {
- if vn1[idx].String() != vn2[idx].String() {
- t.Fatalf("results differ!")
- }
- }
- }
- }
|