func Benchmark5DoozerConClientSet(b *testing.B) { b.StopTimer() l := mustListen() defer l.Close() a := l.Addr().String() u := mustListenPacket(a) defer u.Close() l1 := mustListen() defer l1.Close() u1 := mustListenPacket(l1.Addr().String()) defer u1.Close() l2 := mustListen() defer l2.Close() u2 := mustListenPacket(l2.Addr().String()) defer u2.Close() l3 := mustListen() defer l3.Close() u3 := mustListenPacket(l3.Addr().String()) defer u3.Close() l4 := mustListen() defer l4.Close() u4 := mustListenPacket(l4.Addr().String()) defer u4.Close() go Main("a", "X", "", nil, u, l, nil, 1e9, 1e8, 3e9) go Main("a", "Y", "", doozer.New("a", a), u1, l1, nil, 1e9, 1e8, 3e9) go Main("a", "Z", "", doozer.New("a", a), u2, l2, nil, 1e9, 1e8, 3e9) go Main("a", "V", "", doozer.New("a", a), u3, l3, nil, 1e9, 1e8, 3e9) go Main("a", "W", "", doozer.New("a", a), u4, l4, nil, 1e9, 1e8, 3e9) cl := doozer.New("foo", l.Addr().String()) cl.Set("/ctl/cal/1", store.Missing, nil) cl.Set("/ctl/cal/2", store.Missing, nil) cl.Set("/ctl/cal/3", store.Missing, nil) cl.Set("/ctl/cal/4", store.Missing, nil) cls := []*doozer.Client{ cl, doozer.New("foo", l1.Addr().String()), doozer.New("foo", l2.Addr().String()), doozer.New("foo", l3.Addr().String()), doozer.New("foo", l4.Addr().String()), } c := make(chan bool, b.N) b.StartTimer() for i := 0; i < b.N; i++ { i := i go func() { cls[i%len(cls)].Set("/test", store.Clobber, nil) c <- true }() } for i := 0; i < b.N; i++ { <-c } }
func TestDoozerWaitSimple(t *testing.T) { l := mustListen() defer l.Close() u := mustListenPacket(l.Addr().String()) defer u.Close() go Main("a", "X", "", nil, u, l, nil, 1e9, 2e9, 3e9) cl := doozer.New("foo", l.Addr().String()) var rev int64 = 1 cl.Set("/test/foo", store.Clobber, []byte("bar")) ev, err := cl.Wait("/test/**", rev) assert.Equal(t, nil, err) assert.Equal(t, "/test/foo", ev.Path) assert.Equal(t, []byte("bar"), ev.Body) assert.T(t, ev.IsSet()) rev = ev.Rev + 1 cl.Set("/test/fun", store.Clobber, []byte("house")) ev, err = cl.Wait("/test/**", rev) assert.Equal(t, nil, err) assert.Equal(t, "/test/fun", ev.Path) assert.Equal(t, []byte("house"), ev.Body) assert.T(t, ev.IsSet()) rev = ev.Rev + 1 cl.Del("/test/foo", store.Clobber) ev, err = cl.Wait("/test/**", rev) assert.Equal(t, nil, err) assert.Equal(t, "/test/foo", ev.Path) assert.T(t, ev.IsDel()) }
func TestDoozerWalk(t *testing.T) { l := mustListen() defer l.Close() u := mustListenPacket(l.Addr().String()) defer u.Close() go Main("a", "X", "", nil, u, l, nil, 1e9, 2e9, 3e9) cl := doozer.New("foo", l.Addr().String()) cl.Set("/test/foo", store.Clobber, []byte("bar")) cl.Set("/test/fun", store.Clobber, []byte("house")) info, err := cl.Walk("/test/**", nil, 0, -1) assert.Equal(t, nil, err) assert.Equal(t, 2, len(info)) assert.Equal(t, "/test/foo", info[0].Path) assert.Equal(t, "bar", string(info[0].Body)) assert.T(t, info[0].IsSet()) assert.Equal(t, "/test/fun", info[1].Path) assert.Equal(t, "house", string(info[1].Body)) assert.T(t, info[1].IsSet()) }
func TestDoozerWalkWithRev(t *testing.T) { l := mustListen() defer l.Close() u := mustListenPacket(l.Addr().String()) defer u.Close() go Main("a", "X", "", nil, u, l, nil, 1e9, 2e9, 3e9) cl := doozer.New("foo", l.Addr().String()) rev, _ := cl.Set("/test/foo", store.Clobber, []byte("bar")) cl.Set("/test/fun", store.Clobber, []byte("house")) cl.Set("/test/fab", store.Clobber, []byte("ulous")) w, err := cl.Walk("/test/**", &rev, nil, nil) assert.Equal(t, nil, err, err) ls := []string{} for e := range w.C { ls = append(ls, e.Path) } sort.SortStrings(ls) assert.Equal(t, []string{"/test/foo"}, ls) }
// IsCal checks if addr is a CAL in the cluster named name. // Returns a client if so, nil if not. func isCal(name, addr string) *doozer.Client { c := doozer.New(name, addr) v, _, _ := c.Get("/ctl/name", nil) if string(v) != name { return nil } var cals []string names, err := c.Getdir("/ctl/cal", nil, 0, -1) if err != nil { panic(err) } for _, name := range names { cals = append(cals, name) } for _, cal := range cals { body, _, err := c.Get("/ctl/cal/"+cal, nil) if err != nil || len(body) == 0 { continue } id := string(body) v, _, err := c.Get("/ctl/node/"+id+"/addr", nil) if err != nil { panic(err) } if string(v) == addr { return c } } return nil }
func TestDoozerGetdirOffsetLimit(t *testing.T) { l := mustListen() defer l.Close() u := mustListenPacket(l.Addr().String()) defer u.Close() go Main("a", "X", "", nil, u, l, nil, 1e9, 2e9, 3e9) cl := doozer.New("foo", l.Addr().String()) cl.Set("/test/a", store.Clobber, []byte("1")) cl.Set("/test/b", store.Clobber, []byte("2")) cl.Set("/test/c", store.Clobber, []byte("3")) cl.Set("/test/d", store.Clobber, []byte("4")) // The order is arbitrary. We need to collect them // because it's not safe to assume the order. w, _ := cl.Getdir("/test", 0, 0, nil) ents := make([]string, 0) for e := range w.C { ents = append(ents, e.Path) } w, _ = cl.Getdir("/test", 1, 2, nil) assert.Equal(t, ents[1], (<-w.C).Path) assert.Equal(t, ents[2], (<-w.C).Path) assert.Equal(t, (*doozer.Event)(nil), <-w.C) assert.T(t, closed(w.C)) }
func TestDoozerWalkWithOffsetAndLimit(t *testing.T) { l := mustListen() defer l.Close() u := mustListenPacket(l.Addr().String()) defer u.Close() go Main("a", "X", "", nil, u, l, nil, 1e9, 2e9, 3e9) cl := doozer.New("foo", l.Addr().String()) cl.Set("/test/a", store.Clobber, []byte("abc")) cl.Set("/test/b", store.Clobber, []byte("def")) cl.Set("/test/c", store.Clobber, []byte("ghi")) cl.Set("/test/d", store.Clobber, []byte("jkl")) info, err := cl.Walk("/test/**", nil, 1, 2) assert.Equal(t, nil, err) assert.Equal(t, 2, len(info)) assert.Equal(t, "/test/b", info[0].Path) assert.Equal(t, "def", string(info[0].Body)) assert.T(t, info[0].IsSet()) assert.Equal(t, "/test/c", info[1].Path) assert.Equal(t, "ghi", string(info[1].Body)) assert.T(t, info[1].IsSet()) }
func TestDoozerWalk(t *testing.T) { l := mustListen() defer l.Close() u := mustListenPacket(l.Addr().String()) defer u.Close() go Main("a", "X", "", nil, u, l, nil, 1e9, 2e9, 3e9) cl := doozer.New("foo", l.Addr().String()) cl.Set("/test/foo", store.Clobber, []byte("bar")) cl.Set("/test/fun", store.Clobber, []byte("house")) w, err := cl.Walk("/test/**", nil, nil, nil) assert.Equal(t, nil, err, err) ev := <-w.C assert.NotEqual(t, (*doozer.Event)(nil), ev) assert.Equal(t, "/test/foo", ev.Path) assert.Equal(t, "bar", string(ev.Body)) assert.T(t, ev.IsSet()) ev = <-w.C assert.NotEqual(t, (*doozer.Event)(nil), ev) assert.Equal(t, "/test/fun", ev.Path) assert.Equal(t, "house", string(ev.Body)) assert.T(t, ev.IsSet()) ev = <-w.C assert.Tf(t, closed(w.C), "got %v", ev) }
func TestDoozerGetWithRev(t *testing.T) { l := mustListen() defer l.Close() u := mustListenPacket(l.Addr().String()) defer u.Close() go Main("a", "X", "", nil, u, l, nil, 1e9, 2e9, 3e9) cl := doozer.New("foo", l.Addr().String()) rev1, err := cl.Set("/x", store.Missing, []byte{'a'}) assert.Equal(t, nil, err) v, rev, err := cl.Get("/x", &rev1) // Use the snapshot. assert.Equal(t, nil, err) assert.Equal(t, rev1, rev) assert.Equal(t, []byte{'a'}, v) rev2, err := cl.Set("/x", rev, []byte{'b'}) assert.Equal(t, nil, err) v, rev, err = cl.Get("/x", nil) // Read the new value. assert.Equal(t, nil, err) assert.Equal(t, rev2, rev) assert.Equal(t, []byte{'b'}, v) v, rev, err = cl.Get("/x", &rev1) // Read the saved value again. assert.Equal(t, nil, err) assert.Equal(t, rev1, rev) assert.Equal(t, []byte{'a'}, v) }
func TestDoozerWatchSimple(t *testing.T) { l := mustListen() defer l.Close() u := mustListenPacket(l.Addr().String()) defer u.Close() go Main("a", "X", "", nil, u, l, nil, 1e9, 2e9, 3e9) cl := doozer.New("foo", l.Addr().String()) w, err := cl.Watch("/test/**", 0) assert.Equal(t, nil, err, err) defer w.Cancel() cl.Set("/test/foo", store.Clobber, []byte("bar")) ev := <-w.C assert.Equal(t, "/test/foo", ev.Path) assert.Equal(t, []byte("bar"), ev.Body) assert.T(t, ev.IsSet()) cl.Set("/test/fun", store.Clobber, []byte("house")) ev = <-w.C assert.Equal(t, "/test/fun", ev.Path) assert.Equal(t, []byte("house"), ev.Body) assert.T(t, ev.IsSet()) cl.Del("/test/foo", store.Clobber) ev = <-w.C assert.Equal(t, "/test/foo", ev.Path) assert.T(t, ev.IsDel()) w.Cancel() ev = <-w.C assert.Tf(t, closed(w.C), "got %v", ev) }
func TestDoozerWatchWithRev(t *testing.T) { l := mustListen() defer l.Close() u := mustListenPacket(l.Addr().String()) defer u.Close() go Main("a", "X", "", nil, u, l, nil, 1e9, 2e9, 3e9) cl := doozer.New("foo", l.Addr().String()) // Create some history cl.Set("/test/foo", store.Clobber, []byte("bar")) cl.Set("/test/fun", store.Clobber, []byte("house")) // Ask doozer for the history w, err := cl.Watch("/test/**", 1) assert.Equal(t, nil, err, err) defer w.Cancel() ev := <-w.C assert.Equal(t, "/test/foo", ev.Path) assert.Equal(t, []byte("bar"), ev.Body) assert.T(t, ev.IsSet()) ev = <-w.C assert.Equal(t, "/test/fun", ev.Path) assert.Equal(t, []byte("house"), ev.Body) assert.T(t, ev.IsSet()) }
func TestDoozerGet(t *testing.T) { l := mustListen() defer l.Close() u := mustListenPacket(l.Addr().String()) defer u.Close() go Main("a", "X", "", nil, u, l, nil, 1e9, 2e9, 3e9) cl := doozer.New("foo", l.Addr().String()) _, err := cl.Set("/x", store.Missing, []byte{'a'}) assert.Equal(t, nil, err) ents, rev, err := cl.Get("/x", nil) assert.Equal(t, nil, err) assert.NotEqual(t, store.Dir, rev) assert.Equal(t, []byte{'a'}, ents) //cl.Set("/test/a", store.Missing, []byte{'1'}) //cl.Set("/test/b", store.Missing, []byte{'2'}) //cl.Set("/test/c", store.Missing, []byte{'3'}) //ents, rev, err = cl.Get("/test", 0) //sort.SortStrings(ents) //assert.Equal(t, store.Dir, rev) //assert.Equal(t, nil, err) //assert.Equal(t, []string{"a", "b", "c"}, ents) }
func TestDoozerGetdirOnDir(t *testing.T) { l := mustListen() defer l.Close() u := mustListenPacket(l.Addr().String()) defer u.Close() go Main("a", "X", "", nil, u, l, nil, 1e9, 2e9, 3e9) cl := doozer.New("foo", l.Addr().String()) cl.Set("/test/a", store.Clobber, []byte("1")) cl.Set("/test/b", store.Clobber, []byte("2")) cl.Set("/test/c", store.Clobber, []byte("3")) w, err := cl.Getdir("/test", 0, 0, nil) assert.Equal(t, nil, err) got := make([]string, 0) for e := range w.C { got = append(got, e.Path) } sort.SortStrings(got) assert.Equal(t, []string{"a", "b", "c"}, got) }
func TestDoozerReconnect(t *testing.T) { l := mustListen() defer l.Close() a := l.Addr().String() u := mustListenPacket(a) defer u.Close() go Main("a", "X", "", nil, u, l, nil, 1e9, 2e9, 3e9) l1 := mustListen() go Main("a", "Y", "", doozer.New("a", a), mustListenPacket(l1.Addr().String()), l1, nil, 1e9, 2e9, 3e9) l2 := mustListen() go Main("a", "Z", "", doozer.New("a", a), mustListenPacket(l2.Addr().String()), l2, nil, 1e9, 2e9, 3e9) c0 := doozer.New("foo", a) _, err := c0.Set("/ctl/cal/2", 0, []byte{}) assert.Equal(t, nil, err) _, err = c0.Set("/ctl/cal/3", 0, []byte{}) assert.Equal(t, nil, err) // Wait for the other nodes to become CALs. for <-c0.Len < 3 { time.Sleep(5e8) } rev, err := c0.Set("/x", -1, []byte{'a'}) assert.Equal(t, nil, err, err) rev, err = c0.Set("/x", -1, []byte{'b'}) assert.Equal(t, nil, err) l1.Close() ents, rev, err := c0.Get("/x", nil) assert.Equal(t, nil, err, err) assert.NotEqual(t, store.Dir, rev) assert.Equal(t, []byte{'b'}, ents) rev, err = c0.Set("/x", -1, []byte{'c'}) assert.Equal(t, nil, err, err) rev, err = c0.Set("/x", -1, []byte{'d'}) assert.Equal(t, nil, err) }
func boot(name, id, laddr, baddr string) *doozer.Client { b := doozer.New("<boot>", baddr) cl := lookupAndAttach(b, name) if cl == nil { return elect(name, id, laddr, b) } return cl }
func TestDoozerNop(t *testing.T) { l := mustListen() defer l.Close() u := mustListenPacket(l.Addr().String()) defer u.Close() go Main("a", "X", "", nil, u, l, nil, 1e9, 2e9, 3e9) cl := doozer.New("foo", l.Addr().String()) err := cl.Nop() assert.Equal(t, nil, err) }
func TestDoozerGetdirMissing(t *testing.T) { l := mustListen() defer l.Close() u := mustListenPacket(l.Addr().String()) defer u.Close() go Main("a", "X", "", nil, u, l, nil, 1e9, 2e9, 3e9) cl := doozer.New("foo", l.Addr().String()) names, err := cl.Getdir("/not/here", nil, 0, -1) assert.Equal(t, os.ENOENT, err) assert.Equal(t, []string{}, names) }
func TestDoozerSet(t *testing.T) { l := mustListen() defer l.Close() u := mustListenPacket(l.Addr().String()) defer u.Close() go Main("a", "X", "", nil, u, l, nil, 1e9, 2e9, 3e9) cl := doozer.New("foo", l.Addr().String()) for i := byte(0); i < 10; i++ { _, err := cl.Set("/x", store.Clobber, []byte{'0' + i}) assert.Equal(t, nil, err) } }
func TestDoozerGetdirOnFile(t *testing.T) { l := mustListen() defer l.Close() u := mustListenPacket(l.Addr().String()) defer u.Close() go Main("a", "X", "", nil, u, l, nil, 1e9, 2e9, 3e9) cl := doozer.New("foo", l.Addr().String()) cl.Set("/test/a", store.Clobber, []byte("1")) names, err := cl.Getdir("/test/a", nil, 0, -1) assert.Equal(t, os.ENOTDIR, err) assert.Equal(t, []string{}, names) }
func Benchmark1DoozerClientSet(b *testing.B) { b.StopTimer() l := mustListen() defer l.Close() a := l.Addr().String() u := mustListenPacket(a) defer u.Close() go Main("a", "X", "", nil, u, l, nil, 1e9, 2e9, 3e9) cl := doozer.New("foo", l.Addr().String()) b.StartTimer() for i := 0; i < b.N; i++ { cl.Set("/test", store.Clobber, nil) } }
func TestDoozerGetdirOffsetLimit(t *testing.T) { l := mustListen() defer l.Close() u := mustListenPacket(l.Addr().String()) defer u.Close() go Main("a", "X", "", nil, u, l, nil, 1e9, 2e9, 3e9) cl := doozer.New("foo", l.Addr().String()) cl.Set("/test/a", store.Clobber, []byte("1")) cl.Set("/test/b", store.Clobber, []byte("2")) cl.Set("/test/c", store.Clobber, []byte("3")) cl.Set("/test/d", store.Clobber, []byte("4")) names, err := cl.Getdir("/test", nil, 1, 2) assert.Equal(t, nil, err) assert.Equal(t, []string{"b", "c"}, names) }
func TestDoozerWalkWithRev(t *testing.T) { l := mustListen() defer l.Close() u := mustListenPacket(l.Addr().String()) defer u.Close() go Main("a", "X", "", nil, u, l, nil, 1e9, 2e9, 3e9) cl := doozer.New("foo", l.Addr().String()) rev, _ := cl.Set("/test/foo", store.Clobber, []byte("bar")) cl.Set("/test/fun", store.Clobber, []byte("house")) cl.Set("/test/fab", store.Clobber, []byte("ulous")) info, err := cl.Walk("/test/**", &rev, 0, -1) assert.Equal(t, nil, err) assert.Equal(t, 1, len(info)) assert.Equal(t, "/test/foo", info[0].Path) }
func TestDoozerGetdirOffsetLimitBounds(t *testing.T) { l := mustListen() defer l.Close() u := mustListenPacket(l.Addr().String()) defer u.Close() go Main("a", "X", "", nil, u, l, nil, 1e9, 2e9, 3e9) cl := doozer.New("foo", l.Addr().String()) cl.Set("/test/a", store.Clobber, []byte("1")) cl.Set("/test/b", store.Clobber, []byte("2")) cl.Set("/test/c", store.Clobber, []byte("3")) cl.Set("/test/d", store.Clobber, []byte("4")) w, _ := cl.Getdir("/test", 1, 5, nil) assert.NotEqual(t, (*doozer.Event)(nil), <-w.C) assert.NotEqual(t, (*doozer.Event)(nil), <-w.C) assert.NotEqual(t, (*doozer.Event)(nil), <-w.C) assert.Equal(t, (*doozer.Event)(nil), <-w.C) assert.T(t, closed(w.C)) }
func TestDoozerStat(t *testing.T) { l := mustListen() defer l.Close() u := mustListenPacket(l.Addr().String()) defer u.Close() go Main("a", "X", "", nil, u, l, nil, 1e9, 2e9, 3e9) cl := doozer.New("foo", l.Addr().String()) cl.Set("/test/foo", store.Clobber, []byte("bar")) setRev, _ := cl.Set("/test/fun", store.Clobber, []byte("house")) ln, rev, err := cl.Stat("/test", nil) assert.Equal(t, nil, err) assert.Equal(t, store.Dir, rev) assert.Equal(t, int32(2), ln) ln, rev, err = cl.Stat("/test/fun", nil) assert.Equal(t, nil, err) assert.Equal(t, setRev, rev) assert.Equal(t, int32(5), ln) }
func Benchmark1DoozerConClientSet(b *testing.B) { b.StopTimer() l := mustListen() defer l.Close() a := l.Addr().String() u := mustListenPacket(a) defer u.Close() go Main("a", "X", "", nil, u, l, nil, 1e9, 2e9, 3e9) cl := doozer.New("foo", l.Addr().String()) c := make(chan bool, b.N) b.StartTimer() for i := 0; i < b.N; i++ { go func() { cl.Set("/test", store.Clobber, nil) c <- true }() } for i := 0; i < b.N; i++ { <-c } }
func TestDoozerWalkWithOffsetAndLimit(t *testing.T) { l := mustListen() defer l.Close() u := mustListenPacket(l.Addr().String()) defer u.Close() go Main("a", "X", "", nil, u, l, nil, 1e9, 2e9, 3e9) cl := doozer.New("foo", l.Addr().String()) cl.Set("/test/a", store.Clobber, []byte("abc")) cl.Set("/test/b", store.Clobber, []byte("def")) cl.Set("/test/c", store.Clobber, []byte("ghi")) cl.Set("/test/d", store.Clobber, []byte("jkl")) offset := int32(1) limit := int32(2) w, err := cl.Walk("/test/**", nil, &offset, &limit) assert.Equal(t, nil, err, err) ev := <-w.C assert.NotEqual(t, (*doozer.Event)(nil), ev) assert.Equal(t, "/test/b", ev.Path) assert.Equal(t, "def", string(ev.Body)) assert.T(t, ev.IsSet()) ev = <-w.C assert.NotEqual(t, (*doozer.Event)(nil), ev) assert.Equal(t, "/test/c", ev.Path) assert.Equal(t, "ghi", string(ev.Body)) assert.T(t, ev.IsSet()) ev = <-w.C assert.Tf(t, closed(w.C), "got %v", ev) }
func Main(clusterName, self, baddr string, cl *doozer.Client, udpConn net.PacketConn, listener, webListener net.Listener, pulseInterval, fillDelay, kickTimeout int64) { listenAddr := listener.Addr().String() var activateSeqn int64 useSelf := make(chan bool, 1) st := store.New() pr := &proposer{ seqns: make(chan int64, alpha), props: make(chan *consensus.Prop), st: st, } calSrv := func() { go gc.Pulse(self, st.Seqns, pr, pulseInterval) go gc.Clean(st, 360000, time.Tick(1e9)) } if cl == nil { // we are the only node in a new cluster set(st, "/ctl/name", clusterName, store.Missing) set(st, "/ctl/node/"+self+"/addr", listenAddr, store.Missing) set(st, "/ctl/node/"+self+"/hostname", os.Getenv("HOSTNAME"), store.Missing) set(st, "/ctl/node/"+self+"/version", Version, store.Missing) set(st, "/ctl/cal/0", self, store.Missing) calSrv() close(useSelf) } else { setC(cl, "/ctl/node/"+self+"/addr", listenAddr, store.Clobber) setC(cl, "/ctl/node/"+self+"/hostname", os.Getenv("HOSTNAME"), store.Clobber) setC(cl, "/ctl/node/"+self+"/version", Version, store.Clobber) rev, err := cl.Rev() if err != nil { panic(err) } walk, err := cl.Walk("/**", &rev, nil, nil) if err != nil { panic(err) } watch, err := cl.Watch("/**", rev+1) if err != nil { panic(err) } go follow(st.Ops, watch.C) follow(st.Ops, walk.C) st.Flush() ch, err := st.Wait(rev + 1) if err == nil { <-ch } go func() { activateSeqn = activate(st, self, cl) calSrv() advanceUntil(cl, st.Seqns, activateSeqn+alpha) err := watch.Cancel() if err != nil { panic(err) } close(useSelf) if baddr != "" { b := doozer.New("<boot>", baddr) setC( b, "/ctl/ns/"+clusterName+"/"+self, listenAddr, store.Missing, ) } }() } start := <-st.Seqns cmw := st.Watch(store.Any) in := make(chan consensus.Packet, 50) out := make(chan consensus.Packet, 50) consensus.NewManager(self, start, alpha, in, out, st.Ops, pr.seqns, pr.props, cmw, fillDelay, st) if cl == nil { // Skip ahead alpha steps so that the registrar can provide a // meaningful cluster. for i := start + 1; i < start+alpha+1; i++ { st.Ops <- store.Op{i, store.Nop} } } shun := make(chan string, 3) // sufficient for a cluster of 7 go member.Clean(shun, st, pr) sv := &server.Server{listenAddr, st, pr, self, alpha} go sv.Serve(listener, useSelf) if webListener != nil { web.Store = st web.ClusterName = clusterName go web.Serve(webListener) } go func() { for p := range out { addr, err := net.ResolveUDPAddr(p.Addr) if err != nil { log.Println(err) continue } n, err := udpConn.WriteTo(p.Data, addr) if err != nil { log.Println(err) continue } if n != len(p.Data) { log.Println("packet len too long:", len(p.Data)) continue } } }() lv := liveness{ timeout: kickTimeout, ival: kickTimeout / 2, times: make(map[string]int64), self: self, shun: shun, } for { t := time.Nanoseconds() buf := make([]byte, maxUDPLen) n, addr, err := udpConn.ReadFrom(buf) if err == os.EINVAL { return } if err != nil { log.Println(err) continue } buf = buf[:n] // Update liveness time stamp for this addr lv.times[addr.String()] = t lv.check(t) in <- consensus.Packet{addr.String(), buf} } }