go.etcd.io/etcd@v3.3.27+incompatible/functional/runner/global.go (about) 1 // Copyright 2016 The etcd Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package runner 16 17 import ( 18 "context" 19 "fmt" 20 "log" 21 "sync" 22 "time" 23 24 "github.com/coreos/etcd/clientv3" 25 26 "github.com/spf13/cobra" 27 "golang.org/x/time/rate" 28 ) 29 30 // shared flags 31 var ( 32 totalClientConnections int // total number of client connections to be made with server 33 endpoints []string 34 dialTimeout time.Duration 35 rounds int // total number of rounds to run; set to <= 0 to run forever. 36 reqRate int // maximum number of requests per second. 37 ) 38 39 type roundClient struct { 40 c *clientv3.Client 41 progress int 42 acquire func() error 43 validate func() error 44 release func() error 45 } 46 47 func newClient(eps []string, timeout time.Duration) *clientv3.Client { 48 c, err := clientv3.New(clientv3.Config{ 49 Endpoints: eps, 50 DialTimeout: time.Duration(timeout) * time.Second, 51 }) 52 if err != nil { 53 log.Fatal(err) 54 } 55 return c 56 } 57 58 func doRounds(rcs []roundClient, rounds int, requests int) { 59 var wg sync.WaitGroup 60 61 wg.Add(len(rcs)) 62 finished := make(chan struct{}) 63 limiter := rate.NewLimiter(rate.Limit(reqRate), reqRate) 64 for i := range rcs { 65 go func(rc *roundClient) { 66 defer wg.Done() 67 for rc.progress < rounds || rounds <= 0 { 68 if err := limiter.WaitN(context.Background(), requests/len(rcs)); err != nil { 69 log.Panicf("rate limiter error %v", err) 70 } 71 72 for rc.acquire() != nil { /* spin */ 73 } 74 75 if err := rc.validate(); err != nil { 76 log.Fatal(err) 77 } 78 79 time.Sleep(10 * time.Millisecond) 80 rc.progress++ 81 finished <- struct{}{} 82 83 for rc.release() != nil { /* spin */ 84 } 85 } 86 }(&rcs[i]) 87 } 88 89 start := time.Now() 90 for i := 1; i < len(rcs)*rounds+1 || rounds <= 0; i++ { 91 select { 92 case <-finished: 93 if i%100 == 0 { 94 fmt.Printf("finished %d, took %v\n", i, time.Since(start)) 95 start = time.Now() 96 } 97 case <-time.After(time.Minute): 98 log.Panic("no progress after 1 minute!") 99 } 100 } 101 wg.Wait() 102 103 for _, rc := range rcs { 104 rc.c.Close() 105 } 106 } 107 108 func endpointsFromFlag(cmd *cobra.Command) []string { 109 eps, err := cmd.Flags().GetStringSlice("endpoints") 110 if err != nil { 111 ExitWithError(ExitError, err) 112 } 113 return eps 114 }