etcd-runner: add barrier, observe !ok handling, and election name arg to election-runner.
parent
fa85445ef8
commit
d57ad8ec8d
|
@ -20,32 +20,34 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/coreos/etcd/clientv3/concurrency"
|
"github.com/coreos/etcd/clientv3/concurrency"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
// NewElectionCommand returns the cobra command for "election runner".
|
// NewElectionCommand returns the cobra command for "election runner".
|
||||||
func NewElectionCommand() *cobra.Command {
|
func NewElectionCommand() *cobra.Command {
|
||||||
cmd := &cobra.Command{
|
cmd := &cobra.Command{
|
||||||
Use: "election",
|
Use: "election [election name (defaults to 'elector')]",
|
||||||
Short: "Performs election operation",
|
Short: "Performs election operation",
|
||||||
Run: runElectionFunc,
|
Run: runElectionFunc,
|
||||||
}
|
}
|
||||||
cmd.Flags().IntVar(&rounds, "rounds", 100, "number of rounds to run")
|
|
||||||
cmd.Flags().IntVar(&totalClientConnections, "total-client-connections", 10, "total number of client connections")
|
cmd.Flags().IntVar(&totalClientConnections, "total-client-connections", 10, "total number of client connections")
|
||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
func runElectionFunc(cmd *cobra.Command, args []string) {
|
func runElectionFunc(cmd *cobra.Command, args []string) {
|
||||||
if len(args) > 0 {
|
election := "elector"
|
||||||
ExitWithError(ExitBadArgs, errors.New("election does not take any argument"))
|
if len(args) == 1 {
|
||||||
|
election = args[0]
|
||||||
|
}
|
||||||
|
if len(args) > 1 {
|
||||||
|
ExitWithError(ExitBadArgs, errors.New("election takes at most one argument"))
|
||||||
}
|
}
|
||||||
|
|
||||||
rcs := make([]roundClient, totalClientConnections)
|
rcs := make([]roundClient, totalClientConnections)
|
||||||
validatec, releasec := make(chan struct{}, len(rcs)), make(chan struct{}, len(rcs))
|
validatec := make(chan struct{}, len(rcs))
|
||||||
for range rcs {
|
// nextc closes when election is ready for next round.
|
||||||
releasec <- struct{}{}
|
nextc := make(chan struct{})
|
||||||
}
|
|
||||||
|
|
||||||
eps := endpointsFromFlag(cmd)
|
eps := endpointsFromFlag(cmd)
|
||||||
dialTimeout := dialTimeoutFromCmd(cmd)
|
dialTimeout := dialTimeoutFromCmd(cmd)
|
||||||
|
|
||||||
|
@ -53,6 +55,10 @@ func runElectionFunc(cmd *cobra.Command, args []string) {
|
||||||
v := fmt.Sprintf("%d", i)
|
v := fmt.Sprintf("%d", i)
|
||||||
observedLeader := ""
|
observedLeader := ""
|
||||||
validateWaiters := 0
|
validateWaiters := 0
|
||||||
|
var rcNextc chan struct{}
|
||||||
|
setRcNextc := func() {
|
||||||
|
rcNextc = nextc
|
||||||
|
}
|
||||||
|
|
||||||
rcs[i].c = newClient(eps, dialTimeout)
|
rcs[i].c = newClient(eps, dialTimeout)
|
||||||
var (
|
var (
|
||||||
|
@ -65,18 +71,22 @@ func runElectionFunc(cmd *cobra.Command, args []string) {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
e := concurrency.NewElection(s, "electors")
|
|
||||||
|
|
||||||
rcs[i].acquire = func() error {
|
e := concurrency.NewElection(s, election)
|
||||||
<-releasec
|
rcs[i].acquire = func() (err error) {
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
donec := make(chan struct{})
|
||||||
go func() {
|
go func() {
|
||||||
|
defer close(donec)
|
||||||
|
for ctx.Err() == nil {
|
||||||
if ol, ok := <-e.Observe(ctx); ok {
|
if ol, ok := <-e.Observe(ctx); ok {
|
||||||
observedLeader = string(ol.Kvs[0].Value)
|
observedLeader = string(ol.Kvs[0].Value)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
if observedLeader != v {
|
if observedLeader != v {
|
||||||
cancel()
|
cancel()
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}()
|
}()
|
||||||
err = e.Campaign(ctx, v)
|
err = e.Campaign(ctx, v)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
|
@ -85,18 +95,24 @@ func runElectionFunc(cmd *cobra.Command, args []string) {
|
||||||
if observedLeader == v {
|
if observedLeader == v {
|
||||||
validateWaiters = len(rcs)
|
validateWaiters = len(rcs)
|
||||||
}
|
}
|
||||||
|
cancel()
|
||||||
|
<-donec
|
||||||
select {
|
select {
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
return nil
|
return nil
|
||||||
default:
|
default:
|
||||||
cancel()
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
rcs[i].validate = func() error {
|
rcs[i].validate = func() error {
|
||||||
if l, err := e.Leader(context.TODO()); err == nil && string(l.Kvs[0].Value) != observedLeader {
|
l, err := e.Leader(context.TODO())
|
||||||
return fmt.Errorf("expected leader %q, got %q", observedLeader, l)
|
if err == nil && string(l.Kvs[0].Value) != observedLeader {
|
||||||
|
return fmt.Errorf("expected leader %q, got %q", observedLeader, l.Kvs[0].Value)
|
||||||
}
|
}
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
setRcNextc()
|
||||||
validatec <- struct{}{}
|
validatec <- struct{}{}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -113,14 +129,15 @@ func runElectionFunc(cmd *cobra.Command, args []string) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if observedLeader == v {
|
if observedLeader == v {
|
||||||
for range rcs {
|
close(nextc)
|
||||||
releasec <- struct{}{}
|
nextc = make(chan struct{})
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
<-rcNextc
|
||||||
observedLeader = ""
|
observedLeader = ""
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// each client creates 1 key from Campaign() and delete it from Resign()
|
||||||
doRounds(rcs, rounds)
|
// a round involves in 2*len(rcs) requests.
|
||||||
|
doRounds(rcs, rounds, 2*len(rcs))
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue