示例#1
0
文件: main.go 项目: carriercomm/etcd
func main() {
	var (
		c, n int
		url  string
		size int
	)

	flag.IntVar(&c, "c", 50, "number of connections")
	flag.IntVar(&n, "n", 200, "number of requests")
	flag.IntVar(&size, "s", 128, "size of put request")
	// TODO: config the number of concurrency in each connection
	flag.StringVar(&url, "u", "127.0.0.1:12379", "etcd server endpoint")
	flag.Parse()
	if flag.NArg() < 1 {
		flag.Usage()
		os.Exit(1)
	}

	var act string
	if act = flag.Args()[0]; act != "get" && act != "put" {
		fmt.Printf("unsupported action %v\n", act)
		os.Exit(1)
	}

	conn, err := grpc.Dial(url)
	if err != nil {
		fmt.Errorf("dial error: %v", err)
		os.Exit(1)
	}

	results = make(chan *result, n)
	bar = pb.New(n)
	bar.Format("Bom !")
	bar.Start()

	start := time.Now()

	if act == "get" {
		var rangeEnd []byte
		key := []byte(flag.Args()[1])
		if len(flag.Args()) > 2 {
			rangeEnd = []byte(flag.Args()[2])
		}
		benchGet(conn, key, rangeEnd, n, c)
	} else if act == "put" {
		key := []byte(flag.Args()[1])
		// number of different keys to put into etcd
		kc, err := strconv.ParseInt(flag.Args()[2], 10, 32)
		if err != nil {
			panic(err)
		}
		benchPut(conn, key, int(kc), n, c, size)
	}

	wg.Wait()

	bar.Finish()
	printReport(n, results, time.Now().Sub(start))
}
示例#2
0
文件: range.go 项目: oywc410/etcd
func rangeFunc(cmd *cobra.Command, args []string) {
	if len(args) == 0 || len(args) > 2 {
		fmt.Fprintln(os.Stderr, cmd.Usage())
		os.Exit(1)
	}

	k := args[0]
	end := ""
	if len(args) == 2 {
		end = args[1]
	}

	if rangeConsistency == "l" {
		fmt.Println("bench with linearizable range")
	} else if rangeConsistency == "s" {
		fmt.Println("bench with serializable range")
	} else {
		fmt.Fprintln(os.Stderr, cmd.Usage())
		os.Exit(1)
	}

	results = make(chan result)
	requests := make(chan v3.Op, totalClients)
	bar = pb.New(rangeTotal)

	clients := mustCreateClients(totalClients, totalConns)

	bar.Format("Bom !")
	bar.Start()

	for i := range clients {
		wg.Add(1)
		go doRange(clients[i].KV, requests)
	}

	pdoneC := printReport(results)

	go func() {
		for i := 0; i < rangeTotal; i++ {
			opts := []v3.OpOption{v3.WithRange(end)}
			if rangeConsistency == "s" {
				opts = append(opts, v3.WithSerializable())
			}
			op := v3.OpGet(k, opts...)
			requests <- op
		}
		close(requests)
	}()

	wg.Wait()

	bar.Finish()

	close(results)
	<-pdoneC
}
示例#3
0
文件: range.go 项目: vsayer/etcd
func rangeFunc(cmd *cobra.Command, args []string) {
	if len(args) == 0 || len(args) > 2 {
		fmt.Fprintln(os.Stderr, cmd.Usage())
		os.Exit(1)
	}

	k := []byte(args[0])
	var end []byte
	if len(args) == 2 {
		end = []byte(args[1])
	}

	if rangeConsistency == "l" {
		fmt.Println("bench with linearizable range")
	} else if rangeConsistency == "s" {
		fmt.Println("bench with serializable range")
	} else {
		fmt.Fprintln(os.Stderr, cmd.Usage())
		os.Exit(1)
	}

	results = make(chan result)
	requests := make(chan etcdserverpb.RangeRequest, totalClients)
	bar = pb.New(rangeTotal)

	clients := mustCreateClients(totalClients, totalConns)

	bar.Format("Bom !")
	bar.Start()

	for i := range clients {
		wg.Add(1)
		go doRange(clients[i].KV, requests)
	}

	pdoneC := printReport(results)

	go func() {
		for i := 0; i < rangeTotal; i++ {
			r := etcdserverpb.RangeRequest{Key: k, RangeEnd: end}
			if rangeConsistency == "s" {
				r.Serializable = true
			}
			requests <- r
		}
		close(requests)
	}()

	wg.Wait()

	bar.Finish()

	close(results)
	<-pdoneC
}
示例#4
0
文件: main.go 项目: BlueStalker/etcd
func main() {
	var c, n int
	var url string
	flag.IntVar(&c, "c", 50, "number of connections")
	flag.IntVar(&n, "n", 200, "number of requests")
	// TODO: config the number of concurrency in each connection
	flag.StringVar(&url, "u", "127.0.0.1:12379", "etcd server endpoint")
	flag.Parse()
	if flag.NArg() < 1 {
		flag.Usage()
		os.Exit(1)
	}

	if act := flag.Args()[0]; act != "get" {
		fmt.Errorf("unsupported action %v", act)
		os.Exit(1)
	}
	var rangeEnd []byte
	key := []byte(flag.Args()[1])
	if len(flag.Args()) > 2 {
		rangeEnd = []byte(flag.Args()[2])
	}

	results = make(chan *result, n)
	bar = pb.New(n)
	bar.Format("Bom !")
	bar.Start()

	start := time.Now()

	wg.Add(c)
	requests := make(chan struct{}, n)
	conn, err := grpc.Dial(url)
	if err != nil {
		fmt.Errorf("dial error: %v", err)
		os.Exit(1)
	}

	for i := 0; i < c; i++ {
		go get(etcdserverpb.NewEtcdClient(conn), key, rangeEnd, requests)
	}

	for i := 0; i < n; i++ {
		requests <- struct{}{}
	}
	close(requests)

	wg.Wait()

	bar.Finish()
	printReport(n, results, time.Now().Sub(start))
}
示例#5
0
文件: put.go 项目: lrita/etcd
func putFunc(cmd *cobra.Command, args []string) {
	if keySpaceSize <= 0 {
		fmt.Fprintf(os.Stderr, "expected positive --key-space-size, got (%v)", keySpaceSize)
		os.Exit(1)
	}

	results = make(chan result)
	requests := make(chan v3.Op, totalClients)
	bar = pb.New(putTotal)

	k, v := make([]byte, keySize), string(mustRandBytes(valSize))

	clients := mustCreateClients(totalClients, totalConns)

	bar.Format("Bom !")
	bar.Start()

	for i := range clients {
		wg.Add(1)
		go doPut(context.Background(), clients[i], requests)
	}

	pdoneC := printReport(results)

	go func() {
		for i := 0; i < putTotal; i++ {
			if seqKeys {
				binary.PutVarint(k, int64(i%keySpaceSize))
			} else {
				binary.PutVarint(k, int64(rand.Intn(keySpaceSize)))
			}
			requests <- v3.OpPut(string(k), v)
		}
		close(requests)
	}()

	if compactInterval > 0 {
		go func() {
			for {
				time.Sleep(compactInterval)
				compactKV(clients)
			}
		}()
	}

	wg.Wait()

	bar.Finish()

	close(results)
	<-pdoneC
}
示例#6
0
文件: put.go 项目: ikatson/etcd
func putFunc(cmd *cobra.Command, args []string) {
	if keySpaceSize <= 0 {
		fmt.Fprintf(os.Stderr, "expected positive --key-space-size, got (%v)", keySpaceSize)
		os.Exit(1)
	}

	results = make(chan result)
	requests := make(chan etcdserverpb.PutRequest, totalClients)
	bar = pb.New(putTotal)

	k, v := make([]byte, keySize), mustRandBytes(valSize)

	conns := make([]*grpc.ClientConn, totalConns)
	for i := range conns {
		conns[i] = mustCreateConn()
	}

	clients := make([]etcdserverpb.KVClient, totalClients)
	for i := range clients {
		clients[i] = etcdserverpb.NewKVClient(conns[i%int(totalConns)])
	}

	bar.Format("Bom !")
	bar.Start()

	for i := range clients {
		wg.Add(1)
		go doPut(context.Background(), clients[i], requests)
	}

	pdoneC := printReport(results)

	go func() {
		for i := 0; i < putTotal; i++ {
			if seqKeys {
				binary.PutVarint(k, int64(i%keySpaceSize))
			} else {
				binary.PutVarint(k, int64(rand.Intn(keySpaceSize)))
			}
			requests <- etcdserverpb.PutRequest{Key: k, Value: v}
		}
		close(requests)
	}()

	wg.Wait()

	bar.Finish()

	close(results)
	<-pdoneC
}
示例#7
0
文件: range.go 项目: ngaut/etcd
func rangeFunc(cmd *cobra.Command, args []string) {
	if len(args) == 0 || len(args) > 2 {
		fmt.Fprintln(os.Stderr, cmd.Usage())
		os.Exit(1)
	}

	k := []byte(args[0])
	var end []byte
	if len(args) == 1 {
		end = []byte(args[1])
	}

	results = make(chan *result, rangeTotal)
	requests := make(chan *etcdserverpb.RangeRequest, rangeTotal)
	bar = pb.New(rangeTotal)

	conns := make([]*grpc.ClientConn, totalConns)
	for i := range conns {
		conns[i] = mustCreateConn()
	}

	clients := make([]etcdserverpb.KVClient, totalClients)
	for i := range clients {
		clients[i] = etcdserverpb.NewKVClient(conns[i%int(totalConns)])
	}

	bar.Format("Bom !")
	bar.Start()

	for i := range clients {
		wg.Add(1)
		go doRange(clients[i], requests)
	}

	start := time.Now()
	for i := 0; i < rangeTotal; i++ {
		r := &etcdserverpb.RangeRequest{
			Key:      k,
			RangeEnd: end,
		}
		requests <- r
	}
	close(requests)

	wg.Wait()

	bar.Finish()
	printReport(rangeTotal, results, time.Now().Sub(start))
}
示例#8
0
文件: range.go 项目: jkhelil/etcd
func rangeFunc(cmd *cobra.Command, args []string) {
	if len(args) == 0 || len(args) > 2 {
		fmt.Fprintln(os.Stderr, cmd.Usage())
		os.Exit(1)
	}

	k := []byte(args[0])
	var end []byte
	if len(args) == 1 {
		end = []byte(args[1])
	}

	results = make(chan result)
	requests := make(chan etcdserverpb.RangeRequest, totalClients)
	bar = pb.New(rangeTotal)

	clients := mustCreateClients(totalClients, totalConns)

	bar.Format("Bom !")
	bar.Start()

	for i := range clients {
		wg.Add(1)
		go doRange(clients[i].KV, requests)
	}

	pdoneC := printReport(results)

	go func() {
		for i := 0; i < rangeTotal; i++ {
			requests <- etcdserverpb.RangeRequest{
				Key:      k,
				RangeEnd: end}
		}
		close(requests)
	}()

	wg.Wait()

	bar.Finish()

	close(results)
	<-pdoneC
}
示例#9
0
文件: put.go 项目: ngaut/etcd
func putFunc(cmd *cobra.Command, args []string) {
	results = make(chan *result, putTotal)
	requests := make(chan *etcdserverpb.PutRequest, putTotal)
	bar = pb.New(putTotal)

	k, v := mustRandBytes(keySize), mustRandBytes(valSize)

	conns := make([]*grpc.ClientConn, totalConns)
	for i := range conns {
		conns[i] = mustCreateConn()
	}

	clients := make([]etcdserverpb.KVClient, totalClients)
	for i := range clients {
		clients[i] = etcdserverpb.NewKVClient(conns[i%int(totalConns)])
	}

	bar.Format("Bom !")
	bar.Start()

	for i := range clients {
		wg.Add(1)
		go doPut(clients[i], requests)
	}

	start := time.Now()
	for i := 0; i < putTotal; i++ {
		r := &etcdserverpb.PutRequest{
			Key:   k,
			Value: v,
		}
		requests <- r
	}
	close(requests)

	wg.Wait()

	bar.Finish()
	printReport(putTotal, results, time.Now().Sub(start))
}
示例#10
0
文件: pb.go 项目: ikatson/etcd
func main() {
	count := 5000
	bar := pb.New(count)

	// show percents (by default already true)
	bar.ShowPercent = true

	// show bar (by default already true)
	bar.ShowBar = true

	// no need counters
	bar.ShowCounters = true

	bar.ShowTimeLeft = true

	// and start
	bar.Start()
	for i := 0; i < count; i++ {
		bar.Increment()
		time.Sleep(time.Millisecond)
	}
	bar.FinishPrint("The End!")
}
示例#11
0
文件: watch.go 项目: ikatson/etcd
func watchFunc(cmd *cobra.Command, args []string) {
	watched := make([][]byte, watchedKeyTotal)
	for i := range watched {
		watched[i] = mustRandBytes(32)
	}

	requests := make(chan etcdserverpb.WatchRequest, totalClients)

	conns := make([]*grpc.ClientConn, totalConns)
	for i := range conns {
		conns[i] = mustCreateConn()
	}

	clients := make([]etcdserverpb.WatchClient, totalClients)
	for i := range clients {
		clients[i] = etcdserverpb.NewWatchClient(conns[i%int(totalConns)])
	}

	streams := make([]etcdserverpb.Watch_WatchClient, watchTotalStreams)
	var err error
	for i := range streams {
		streams[i], err = clients[i%int(totalClients)].Watch(context.TODO())
		if err != nil {
			fmt.Fprintln(os.Stderr, "Failed to create watch stream:", err)
			os.Exit(1)
		}
	}

	for i := range streams {
		wg.Add(1)
		go doWatch(streams[i], requests)
	}

	// watching phase
	results = make(chan result)
	bar = pb.New(watchTotal)

	bar.Format("Bom !")
	bar.Start()

	pdoneC := printRate(results)

	go func() {
		for i := 0; i < watchTotal; i++ {
			requests <- etcdserverpb.WatchRequest{
				CreateRequest: &etcdserverpb.WatchCreateRequest{Key: watched[i%(len(watched))]},
			}
		}
		close(requests)
	}()

	wg.Wait()
	bar.Finish()

	fmt.Printf("Watch creation summary:\n")
	close(results)
	<-pdoneC

	// put phase
	kv := etcdserverpb.NewKVClient(conns[0])
	// total number of puts * number of watchers on each key
	eventsTotal := watchPutTotal * (watchTotal / watchedKeyTotal)

	results = make(chan result)
	bar = pb.New(eventsTotal)

	bar.Format("Bom !")
	bar.Start()

	putreqc := make(chan etcdserverpb.PutRequest)

	for i := 0; i < watchPutTotal; i++ {
		wg.Add(1)
		go doPut(context.TODO(), kv, putreqc)
	}

	pdoneC = printRate(results)

	go func() {
		for i := 0; i < eventsTotal; i++ {
			putreqc <- etcdserverpb.PutRequest{
				Key:   watched[i%(len(watched))],
				Value: []byte("data"),
			}
			// TODO: use a real rate-limiter instead of sleep.
			time.Sleep(time.Second / time.Duration(watchPutRate))
		}
		close(putreqc)
	}()

	wg.Wait()
	bar.Finish()
	fmt.Printf("Watch events received summary:\n")
	close(results)
	<-pdoneC
}
示例#12
0
文件: stm.go 项目: siddontang/etcd
func stmFunc(cmd *cobra.Command, args []string) {
	if stmKeyCount <= 0 {
		fmt.Fprintf(os.Stderr, "expected positive --keys, got (%v)", stmKeyCount)
		os.Exit(1)
	}

	if stmWritePercent < 0 || stmWritePercent > 100 {
		fmt.Fprintf(os.Stderr, "expected [0, 100] --txn-wr-percent, got (%v)", stmWritePercent)
		os.Exit(1)
	}

	if stmKeysPerTxn < 0 || stmKeysPerTxn > stmKeyCount {
		fmt.Fprintf(os.Stderr, "expected --keys-per-txn between 0 and %v, got (%v)", stmKeyCount, stmKeysPerTxn)
		os.Exit(1)
	}

	switch stmIsolation {
	case "r":
		mkSTM = v3sync.NewSTMRepeatable
	case "l":
		mkSTM = v3sync.NewSTMSerializable
	default:
		fmt.Fprintln(os.Stderr, cmd.Usage())
		os.Exit(1)
	}

	results = make(chan result)
	requests := make(chan stmApply, totalClients)
	bar = pb.New(stmTotal)

	clients := mustCreateClients(totalClients, totalConns)

	bar.Format("Bom !")
	bar.Start()

	for i := range clients {
		wg.Add(1)
		go doSTM(context.Background(), clients[i], requests)
	}

	pdoneC := printReport(results)

	go func() {
		for i := 0; i < stmTotal; i++ {
			kset := make(map[string]struct{})
			for len(kset) != stmKeysPerTxn {
				k := make([]byte, 16)
				binary.PutVarint(k, int64(rand.Intn(stmKeyCount)))
				s := string(k)
				kset[s] = struct{}{}
			}

			applyf := func(s v3sync.STM) error {
				wrs := int(float32(len(kset)*stmWritePercent) / 100.0)
				for k := range kset {
					s.Get(k)
					if wrs > 0 {
						s.Put(k, string(mustRandBytes(stmValSize)))
						wrs--
					}
				}
				return nil
			}

			requests <- applyf
		}
		close(requests)
	}()

	wg.Wait()

	bar.Finish()

	close(results)
	<-pdoneC
}
示例#13
0
文件: watch.go 项目: oywc410/etcd
func watchFunc(cmd *cobra.Command, args []string) {
	if watchKeySpaceSize <= 0 {
		fmt.Fprintf(os.Stderr, "expected positive --key-space-size, got (%v)", watchKeySpaceSize)
		os.Exit(1)
	}

	watched := make([]string, watchedKeyTotal)
	for i := range watched {
		k := make([]byte, watchKeySize)
		if watchSeqKeys {
			binary.PutVarint(k, int64(i%watchKeySpaceSize))
		} else {
			binary.PutVarint(k, int64(rand.Intn(watchKeySpaceSize)))
		}
		watched[i] = string(k)
	}

	requests := make(chan string, totalClients)

	clients := mustCreateClients(totalClients, totalConns)

	streams := make([]v3.Watcher, watchTotalStreams)
	for i := range streams {
		streams[i] = v3.NewWatcher(clients[i%len(clients)])
	}

	putStartNotifier = make(chan struct{})

	// watching phase
	results = make(chan result)
	bar = pb.New(watchTotal)

	bar.Format("Bom !")
	bar.Start()

	pdoneC := printRate(results)

	atomic.StoreInt32(&nrWatchCompleted, int32(0))
	watchCompletedNotifier = make(chan struct{})
	for i := range streams {
		go doWatch(streams[i], requests)
	}

	go func() {
		for i := 0; i < watchTotal; i++ {
			requests <- watched[i%len(watched)]
		}
		close(requests)
	}()

	<-watchCompletedNotifier
	bar.Finish()

	fmt.Printf("Watch creation summary:\n")
	close(results)
	<-pdoneC

	// put phase
	// total number of puts * number of watchers on each key
	eventsTotal = watchPutTotal * (watchTotal / watchedKeyTotal)
	results = make(chan result)
	bar = pb.New(eventsTotal)

	bar.Format("Bom !")
	bar.Start()

	atomic.StoreInt32(&nrRecvCompleted, 0)
	recvCompletedNotifier = make(chan struct{})
	close(putStartNotifier)

	putreqc := make(chan v3.Op)

	for i := 0; i < watchPutTotal; i++ {
		go doPutForWatch(context.TODO(), clients[i%len(clients)].KV, putreqc)
	}

	pdoneC = printRate(results)

	go func() {
		for i := 0; i < eventsTotal; i++ {
			putreqc <- v3.OpPut(watched[i%(len(watched))], "data")
			// TODO: use a real rate-limiter instead of sleep.
			time.Sleep(time.Second / time.Duration(watchPutRate))
		}
		close(putreqc)
	}()

	<-recvCompletedNotifier
	bar.Finish()
	fmt.Printf("Watch events received summary:\n")
	close(results)
	<-pdoneC
}
示例#14
0
文件: watch.go 项目: xrq1990/etcd
func watchFunc(cmd *cobra.Command, args []string) {
	watched := make([][]byte, watchedKeyTotal)
	for i := range watched {
		watched[i] = mustRandBytes(32)
	}

	requests := make(chan *etcdserverpb.WatchRequest, watchTotal)

	conns := make([]*grpc.ClientConn, totalConns)
	for i := range conns {
		conns[i] = mustCreateConn()
	}

	clients := make([]etcdserverpb.WatchClient, totalClients)
	for i := range clients {
		clients[i] = etcdserverpb.NewWatchClient(conns[i%int(totalConns)])
	}

	streams := make([]etcdserverpb.Watch_WatchClient, watchTotalStreams)
	var err error
	for i := range streams {
		streams[i], err = clients[i%int(totalClients)].Watch(context.TODO())
		if err != nil {
			fmt.Fprintln(os.Stderr, "Failed to create watch stream:", err)
			os.Exit(1)
		}
	}

	for i := range streams {
		wg.Add(1)
		go doWatch(streams[i], requests)
	}

	// watching phase
	results = make(chan *result, watchTotal)
	bar = pb.New(watchTotal)

	bar.Format("Bom !")
	bar.Start()

	start := time.Now()
	for i := 0; i < watchTotal; i++ {
		r := &etcdserverpb.WatchRequest{
			Key: watched[i%(len(watched))],
		}
		requests <- r
	}
	close(requests)

	wg.Wait()
	bar.Finish()
	fmt.Printf("Watch creation summary:\n")
	printRate(watchTotal, results, time.Now().Sub(start))

	// put phase
	kv := etcdserverpb.NewKVClient(conns[0])
	// total number of puts * number of watchers on each key
	eventsTotal := watchPutTotal * (watchTotal / watchedKeyTotal)

	results = make(chan *result, eventsTotal)
	bar = pb.New(eventsTotal)

	bar.Format("Bom !")
	bar.Start()

	start = time.Now()

	// TODO: create multiple clients to do put to increase throughput
	// TODO: use a real rate-limiter instead of sleep.
	for i := 0; i < watchPutTotal; i++ {
		r := &etcdserverpb.PutRequest{
			Key:   watched[i%(len(watched))],
			Value: []byte("data"),
		}
		_, err := kv.Put(context.TODO(), r)
		if err != nil {
			fmt.Fprintln(os.Stderr, "Failed to put:", err)
		}
		time.Sleep(time.Second / time.Duration(watchPutRate))
	}

	for {
		if len(results) == eventsTotal {
			break
		}
		time.Sleep(50 * time.Millisecond)
	}

	bar.Finish()
	fmt.Printf("Watch events received summary:\n")
	printRate(eventsTotal, results, time.Now().Sub(start))
}
示例#15
0
文件: copy.go 项目: ikatson/etcd
func main() {
	// check args
	if len(os.Args) < 3 {
		printUsage()
		return
	}
	sourceName, destName := os.Args[1], os.Args[2]

	// check source
	var source io.Reader
	var sourceSize int64
	if strings.HasPrefix(sourceName, "http://") {
		// open as url
		resp, err := http.Get(sourceName)
		if err != nil {
			fmt.Printf("Can't get %s: %v\n", sourceName, err)
			return
		}
		defer resp.Body.Close()
		if resp.StatusCode != http.StatusOK {
			fmt.Printf("Server return non-200 status: %v\n", resp.Status)
			return
		}
		i, _ := strconv.Atoi(resp.Header.Get("Content-Length"))
		sourceSize = int64(i)
		source = resp.Body
	} else {
		// open as file
		s, err := os.Open(sourceName)
		if err != nil {
			fmt.Printf("Can't open %s: %v\n", sourceName, err)
			return
		}
		defer s.Close()
		// get source size
		sourceStat, err := s.Stat()
		if err != nil {
			fmt.Printf("Can't stat %s: %v\n", sourceName, err)
			return
		}
		sourceSize = sourceStat.Size()
		source = s
	}

	// create dest
	dest, err := os.Create(destName)
	if err != nil {
		fmt.Printf("Can't create %s: %v\n", destName, err)
		return
	}
	defer dest.Close()

	// create bar
	bar := pb.New(int(sourceSize)).SetUnits(pb.U_BYTES).SetRefreshRate(time.Millisecond * 10)
	bar.ShowSpeed = true
	bar.Start()

	// create multi writer
	writer := io.MultiWriter(dest, bar)

	// and copy
	io.Copy(writer, source)
	bar.Finish()
}
示例#16
0
文件: watch.go 项目: vsayer/etcd
func watchFunc(cmd *cobra.Command, args []string) {
	watched := make([][]byte, watchedKeyTotal)
	for i := range watched {
		watched[i] = mustRandBytes(32)
	}

	requests := make(chan etcdserverpb.WatchRequest, totalClients)

	clients := mustCreateClients(totalClients, totalConns)

	streams := make([]etcdserverpb.Watch_WatchClient, watchTotalStreams)
	var err error
	for i := range streams {
		streams[i], err = clients[i%len(clients)].Watch.Watch(context.TODO())
		if err != nil {
			fmt.Fprintln(os.Stderr, "Failed to create watch stream:", err)
			os.Exit(1)
		}
	}

	putStartNotifier = make(chan struct{})

	// watching phase
	results = make(chan result)
	bar = pb.New(watchTotal)

	bar.Format("Bom !")
	bar.Start()

	pdoneC := printRate(results)

	atomic.StoreInt32(&nrWatchCompleted, int32(0))
	watchCompletedNotifier = make(chan struct{})
	for i := range streams {
		go doWatch(streams[i], requests)
	}

	go func() {
		for i := 0; i < watchTotal; i++ {
			requests <- etcdserverpb.WatchRequest{
				RequestUnion: &etcdserverpb.WatchRequest_CreateRequest{
					CreateRequest: &etcdserverpb.WatchCreateRequest{
						Key: watched[i%(len(watched))]}}}
		}
		close(requests)
	}()

	<-watchCompletedNotifier
	bar.Finish()

	fmt.Printf("Watch creation summary:\n")
	close(results)
	<-pdoneC

	// put phase
	// total number of puts * number of watchers on each key
	eventsTotal = watchPutTotal * (watchTotal / watchedKeyTotal)
	results = make(chan result)
	bar = pb.New(eventsTotal)

	bar.Format("Bom !")
	bar.Start()

	atomic.StoreInt32(&nrRecvCompleted, 0)
	recvCompletedNotifier = make(chan struct{})
	close(putStartNotifier)

	putreqc := make(chan etcdserverpb.PutRequest)

	for i := 0; i < watchPutTotal; i++ {
		go doPutForWatch(context.TODO(), clients[i%len(clients)].KV, putreqc)
	}

	pdoneC = printRate(results)

	go func() {
		for i := 0; i < eventsTotal; i++ {
			putreqc <- etcdserverpb.PutRequest{
				Key:   watched[i%(len(watched))],
				Value: []byte("data"),
			}
			// TODO: use a real rate-limiter instead of sleep.
			time.Sleep(time.Second / time.Duration(watchPutRate))
		}
		close(putreqc)
	}()

	<-recvCompletedNotifier
	bar.Finish()
	fmt.Printf("Watch events received summary:\n")
	close(results)
	<-pdoneC
}