go.etcd.io/etcd@v3.3.27+incompatible/functional/runner/election_command.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 "errors" 20 "fmt" 21 22 "github.com/coreos/etcd/clientv3/concurrency" 23 24 "github.com/spf13/cobra" 25 ) 26 27 // NewElectionCommand returns the cobra command for "election runner". 28 func NewElectionCommand() *cobra.Command { 29 cmd := &cobra.Command{ 30 Use: "election [election name (defaults to 'elector')]", 31 Short: "Performs election operation", 32 Run: runElectionFunc, 33 } 34 cmd.Flags().IntVar(&totalClientConnections, "total-client-connections", 10, "total number of client connections") 35 return cmd 36 } 37 38 func runElectionFunc(cmd *cobra.Command, args []string) { 39 election := "elector" 40 if len(args) == 1 { 41 election = args[0] 42 } 43 if len(args) > 1 { 44 ExitWithError(ExitBadArgs, errors.New("election takes at most one argument")) 45 } 46 47 rcs := make([]roundClient, totalClientConnections) 48 validatec := make(chan struct{}, len(rcs)) 49 // nextc closes when election is ready for next round. 50 nextc := make(chan struct{}) 51 eps := endpointsFromFlag(cmd) 52 53 for i := range rcs { 54 v := fmt.Sprintf("%d", i) 55 observedLeader := "" 56 validateWaiters := 0 57 var rcNextc chan struct{} 58 setRcNextc := func() { 59 rcNextc = nextc 60 } 61 62 rcs[i].c = newClient(eps, dialTimeout) 63 var ( 64 s *concurrency.Session 65 err error 66 ) 67 for { 68 s, err = concurrency.NewSession(rcs[i].c) 69 if err == nil { 70 break 71 } 72 } 73 74 e := concurrency.NewElection(s, election) 75 rcs[i].acquire = func() (err error) { 76 ctx, cancel := context.WithCancel(context.Background()) 77 donec := make(chan struct{}) 78 go func() { 79 defer close(donec) 80 for ctx.Err() == nil { 81 if ol, ok := <-e.Observe(ctx); ok { 82 observedLeader = string(ol.Kvs[0].Value) 83 break 84 } 85 } 86 if observedLeader != v { 87 cancel() 88 } 89 }() 90 err = e.Campaign(ctx, v) 91 cancel() 92 <-donec 93 if err == nil { 94 observedLeader = v 95 } 96 if observedLeader == v { 97 validateWaiters = len(rcs) 98 } 99 select { 100 case <-ctx.Done(): 101 return nil 102 default: 103 return err 104 } 105 } 106 rcs[i].validate = func() error { 107 l, err := e.Leader(context.TODO()) 108 if err == nil && string(l.Kvs[0].Value) != observedLeader { 109 return fmt.Errorf("expected leader %q, got %q", observedLeader, l.Kvs[0].Value) 110 } 111 if err != nil { 112 return err 113 } 114 setRcNextc() 115 validatec <- struct{}{} 116 return nil 117 } 118 rcs[i].release = func() error { 119 for validateWaiters > 0 { 120 select { 121 case <-validatec: 122 validateWaiters-- 123 default: 124 return fmt.Errorf("waiting on followers") 125 } 126 } 127 if err := e.Resign(context.TODO()); err != nil { 128 return err 129 } 130 if observedLeader == v { 131 oldNextc := nextc 132 nextc = make(chan struct{}) 133 close(oldNextc) 134 135 } 136 <-rcNextc 137 observedLeader = "" 138 return nil 139 } 140 } 141 // each client creates 1 key from Campaign() and delete it from Resign() 142 // a round involves in 2*len(rcs) requests. 143 doRounds(rcs, rounds, 2*len(rcs)) 144 }