func (ivt *IntervalTree) Visit(ivl Interval, ivv IntervalVisitor) {
	ivt.root.visit(&ivl, func(n *intervalNode) bool { return ivv(&n.iv) })
}

func ( ivt * IntervalTree ) Visit ( ivl Interval , ivv IntervalVisitor ) { ivt . root . visit ( & ivl , func ( n * intervalNode ) bool { return ivv ( & n . iv ) } ) }

// Visit calls a visitor function on every tree node intersecting the given interval.
// It will visit each interval [x, y) in ascending order sorted on x.

Visit calls a visitor function on every tree node intersecting the given interval . It will visit each interval [ x y ) in ascending order sorted on x .


func (ivt *IntervalTree) find(ivl Interval) (ret *intervalNode) {
	f := func(n *intervalNode) bool {
		if n.iv.Ivl != ivl {
			return true
		}
		ret = n
		return false
	}
	ivt.root.visit(&ivl, f)
	return ret
}

func ( ivt * IntervalTree ) find ( ivl Interval ) ( ret * intervalNode ) { f := func ( n * intervalNode ) bool { if n . iv . Ivl != ivl { return true } ret = n return false } ivt . root . visit ( & ivl , f ) return ret }

// find the exact node for a given interval


func (ivt *IntervalTree) Find(ivl Interval) (ret *IntervalValue) {
	n := ivt.find(ivl)
	if n == nil {
		return nil
	}
	return &n.iv
}

func ( ivt * IntervalTree ) Find ( ivl Interval ) ( ret * IntervalValue ) { n := ivt . find ( ivl ) if n == nil { return nil } return & n . iv }

// Find gets the IntervalValue for the node matching the given interval


func (ivt *IntervalTree) Intersects(iv Interval) bool {
	x := ivt.root
	for x != nil && iv.Compare(&x.iv.Ivl) != 0 {
		if x.left != nil && x.left.max.Compare(iv.Begin) > 0 {
			x = x.left
		} else {
			x = x.right
		}
	}
	return x != nil
}

func ( ivt * IntervalTree ) Intersects ( iv Interval ) bool { x := ivt . root for x != nil && iv . Compare ( & x . iv . Ivl ) != 0 { if x . left != nil && x . left . max . Compare ( iv . Begin ) > 0 { x = x . left } else { x = x . right } } return x != nil }

// Intersects returns true if there is some tree node intersecting the given interval.

Intersects returns true if there is some tree node intersecting the given interval .


func (ivt *IntervalTree) Contains(ivl Interval) bool {
	var maxEnd, minBegin Comparable

	isContiguous := true
	ivt.Visit(ivl, func(n *IntervalValue) bool {
		if minBegin == nil {
			minBegin = n.Ivl.Begin
			maxEnd = n.Ivl.End
			return true
		}
		if maxEnd.Compare(n.Ivl.Begin) < 0 {
			isContiguous = false
			return false
		}
		if n.Ivl.End.Compare(maxEnd) > 0 {
			maxEnd = n.Ivl.End
		}
		return true
	})

	return isContiguous && minBegin != nil && maxEnd.Compare(ivl.End) >= 0 && minBegin.Compare(ivl.Begin) <= 0
}

func ( ivt * IntervalTree ) Contains ( ivl Interval ) bool { var maxEnd , minBegin Comparable isContiguous := true ivt . Visit ( ivl , func ( n * IntervalValue ) bool { if minBegin == nil { minBegin = n . Ivl . Begin maxEnd = n . Ivl . End return true } if maxEnd . Compare ( n . Ivl . Begin ) < 0 { isContiguous = false return false } if n . Ivl . End . Compare ( maxEnd ) > 0 { maxEnd = n . Ivl . End } return true } ) return isContiguous && minBegin != nil && maxEnd . Compare ( ivl . End ) >= 0 && minBegin . Compare ( ivl . Begin ) <= 0 }

// Contains returns true if the interval tree's keys cover the entire given interval.

Contains returns true if the interval tree s keys cover the entire given interval .


func (ivt *IntervalTree) Stab(iv Interval) (ivs []*IntervalValue) {
	if ivt.count == 0 {
		return nil
	}
	f := func(n *IntervalValue) bool { ivs = append(ivs, n); return true }
	ivt.Visit(iv, f)
	return ivs
}

func ( ivt * IntervalTree ) Stab ( iv Interval ) ( ivs [ ] * IntervalValue ) { if ivt . count == 0 { return nil } f := func ( n * IntervalValue ) bool { ivs = append ( ivs , n ) ; return true } ivt . Visit ( iv , f ) return ivs }

// Stab returns a slice with all elements in the tree intersecting the interval.

Stab returns a slice with all elements in the tree intersecting the interval .


func (ivt *IntervalTree) Union(inIvt IntervalTree, ivl Interval) {
	f := func(n *IntervalValue) bool {
		ivt.Insert(n.Ivl, n.Val)
		return true
	}
	inIvt.Visit(ivl, f)
}

func ( ivt * IntervalTree ) Union ( inIvt IntervalTree , ivl Interval ) { f := func ( n * IntervalValue ) bool { ivt . Insert ( n . Ivl , n . Val ) return true } inIvt . Visit ( ivl , f ) }

// Union merges a given interval tree into the receiver.

Union merges a given interval tree into the receiver .


func NewExactReadCloser(rc io.ReadCloser, totalBytes int64) io.ReadCloser {
	return &exactReadCloser{rc: rc, totalBytes: totalBytes}
}

func NewExactReadCloser ( rc io . ReadCloser , totalBytes int64 ) io . ReadCloser { return & exactReadCloser { rc : rc , totalBytes : totalBytes } }

// NewExactReadCloser returns a ReadCloser that returns errors if the underlying
// reader does not read back exactly the requested number of bytes.

NewExactReadCloser returns a ReadCloser that returns errors if the underlying reader does not read back exactly the requested number of bytes .


func NewElection(s *Session, pfx string) *Election {
	return &Election{session: s, keyPrefix: pfx + "/"}
}

func NewElection ( s * Session , pfx string ) * Election { return & Election { session : s , keyPrefix : pfx + " " } }

// NewElection returns a new election on a given key prefix.

NewElection returns a new election on a given key prefix .


func ResumeElection(s *Session, pfx string, leaderKey string, leaderRev int64) *Election {
	return &Election{
		keyPrefix:     pfx,
		session:       s,
		leaderKey:     leaderKey,
		leaderRev:     leaderRev,
		leaderSession: s,
	}
}

func ResumeElection ( s * Session , pfx string , leaderKey string , leaderRev int64 ) * Election { return & Election { keyPrefix : pfx , session : s , leaderKey : leaderKey , leaderRev : leaderRev , leaderSession : s , } }

// ResumeElection initializes an election with a known leader.

ResumeElection initializes an election with a known leader .


func (e *Election) Proclaim(ctx context.Context, val string) error {
	if e.leaderSession == nil {
		return ErrElectionNotLeader
	}
	client := e.session.Client()
	cmp := v3.Compare(v3.CreateRevision(e.leaderKey), "=", e.leaderRev)
	txn := client.Txn(ctx).If(cmp)
	txn = txn.Then(v3.OpPut(e.leaderKey, val, v3.WithLease(e.leaderSession.Lease())))
	tresp, terr := txn.Commit()
	if terr != nil {
		return terr
	}
	if !tresp.Succeeded {
		e.leaderKey = ""
		return ErrElectionNotLeader
	}

	e.hdr = tresp.Header
	return nil
}

func ( e * Election ) Proclaim ( ctx context . Context , val string ) error { if e . leaderSession == nil { return ErrElectionNotLeader } client := e . session . Client ( ) cmp := v3 . Compare ( v3 . CreateRevision ( e . leaderKey ) , " " , e . leaderRev ) txn := client . Txn ( ctx ) . If ( cmp ) txn = txn . Then ( v3 . OpPut ( e . leaderKey , val , v3 . WithLease ( e . leaderSession . Lease ( ) ) ) ) tresp , terr := txn . Commit ( ) if terr != nil { return terr } if ! tresp . Succeeded { e . leaderKey = " " return ErrElectionNotLeader } e . hdr = tresp . Header return nil }

// Proclaim lets the leader announce a new value without another election.

Proclaim lets the leader announce a new value without another election .


func (e *Election) Resign(ctx context.Context) (err error) {
	if e.leaderSession == nil {
		return nil
	}
	client := e.session.Client()
	cmp := v3.Compare(v3.CreateRevision(e.leaderKey), "=", e.leaderRev)
	resp, err := client.Txn(ctx).If(cmp).Then(v3.OpDelete(e.leaderKey)).Commit()
	if err == nil {
		e.hdr = resp.Header
	}
	e.leaderKey = ""
	e.leaderSession = nil
	return err
}

func ( e * Election ) Resign ( ctx context . Context ) ( err error ) { if e . leaderSession == nil { return nil } client := e . session . Client ( ) cmp := v3 . Compare ( v3 . CreateRevision ( e . leaderKey ) , " " , e . leaderRev ) resp , err := client . Txn ( ctx ) . If ( cmp ) . Then ( v3 . OpDelete ( e . leaderKey ) ) . Commit ( ) if err == nil { e . hdr = resp . Header } e . leaderKey = " " e . leaderSession = nil return err }

// Resign lets a leader start a new election.

Resign lets a leader start a new election .


func (e *Election) Leader(ctx context.Context) (*v3.GetResponse, error) {
	client := e.session.Client()
	resp, err := client.Get(ctx, e.keyPrefix, v3.WithFirstCreate()...)
	if err != nil {
		return nil, err
	} else if len(resp.Kvs) == 0 {
		// no leader currently elected
		return nil, ErrElectionNoLeader
	}
	return resp, nil
}

func
(
e
*
Election
)
Leader
(
ctx
context
.
Context
)
(
*
v3
.
GetResponse
,
error
)
{
client
:=
e
.
session
.
Client
(
)
resp
,
err
:=
client
.
Get
(
ctx
,
e
.
keyPrefix
,
v3
.
WithFirstCreate
(
)
...
)
if
err
!=
{
return
,
err
}
else
if
len
(
resp
.
Kvs
)
==
0
{
// no leader currently elected
return
,
ErrElectionNoLeader
}
return
resp
,
}

// Leader returns the leader value for the current election.

Leader returns the leader value for the current election .


func (e *Election) Observe(ctx context.Context) <-chan v3.GetResponse {
	retc := make(chan v3.GetResponse)
	go e.observe(ctx, retc)
	return retc
}

func ( e * Election ) Observe ( ctx context . Context ) <- chan v3 . GetResponse { retc := make ( chan v3 . GetResponse ) go e . observe ( ctx , retc ) return retc }

// Observe returns a channel that reliably observes ordered leader proposals
// as GetResponse values on every current elected leader key. It will not
// necessarily fetch all historical leader updates, but will always post the
// most recent leader value.
//
// The channel closes when the context is canceled or the underlying watcher
// is otherwise disrupted.

Observe returns a channel that reliably observes ordered leader proposals as GetResponse values on every current elected leader key . It will not necessarily fetch all historical leader updates but will always post the most recent leader value . The channel closes when the context is canceled or the underlying watcher is otherwise disrupted .


func (qa *quotaAlarmer) check(ctx context.Context, r interface{}) error {
	if qa.q.Available(r) {
		return nil
	}
	req := &pb.AlarmRequest{
		MemberID: uint64(qa.id),
		Action:   pb.AlarmRequest_ACTIVATE,
		Alarm:    pb.AlarmType_NOSPACE,
	}
	qa.a.Alarm(ctx, req)
	return rpctypes.ErrGRPCNoSpace
}

func ( qa * quotaAlarmer ) check ( ctx context . Context , r interface { } ) error { if qa . q . Available ( r ) { return nil } req := & pb . AlarmRequest { MemberID : uint64 ( qa . id ) , Action : pb . AlarmRequest_ACTIVATE , Alarm : pb . AlarmType_NOSPACE , } qa . a . Alarm ( ctx , req ) return rpctypes . ErrGRPCNoSpace }

// check whether request satisfies the quota. If there is not enough space,
// ignore request and raise the free space alarm.

check whether request satisfies the quota . If there is not enough space ignore request and raise the free space alarm .


func NewExecWatchCommand() cli.Command {
	return cli.Command{
		Name:      "exec-watch",
		Usage:     "watch a key for changes and exec an executable",
		ArgsUsage: "<key> <command> [args...]",
		Flags: []cli.Flag{
			cli.IntFlag{Name: "after-index", Value: 0, Usage: "watch after the given index"},
			cli.BoolFlag{Name: "recursive, r", Usage: "watch all values for key and child keys"},
		},
		Action: func(c *cli.Context) error {
			execWatchCommandFunc(c, mustNewKeyAPI(c))
			return nil
		},
	}
}

func NewExecWatchCommand ( ) cli . Command { return cli . Command { Name : " " , Usage : " " , ArgsUsage : " " , Flags : [ ] cli . Flag { cli . IntFlag { Name : " " , Value : 0 , Usage : " " } , cli . BoolFlag { Name : " " , Usage : " " } , } , Action : func ( c * cli . Context ) error { execWatchCommandFunc ( c , mustNewKeyAPI ( c ) ) return nil } , } }

// NewExecWatchCommand returns the CLI command for "exec-watch".

NewExecWatchCommand returns the CLI command for exec - watch .


func execWatchCommandFunc(c *cli.Context, ki client.KeysAPI) {
	args := c.Args()
	argslen := len(args)

	if argslen < 2 {
		handleError(c, ExitBadArgs, errors.New("key and command to exec required"))
	}

	var (
		key     string
		cmdArgs []string
	)

	foundSep := false
	for i := range args {
		if args[i] == "--" && i != 0 {
			foundSep = true
			break
		}
	}

	if foundSep {
		key = args[0]
		cmdArgs = args[2:]
	} else {
		// If no flag is parsed, the order of key and cmdArgs will be switched and
		// args will not contain `--`.
		key = args[argslen-1]
		cmdArgs = args[:argslen-1]
	}

	index := 0
	if c.Int("after-index") != 0 {
		index = c.Int("after-index")
	}

	recursive := c.Bool("recursive")

	sigch := make(chan os.Signal, 1)
	signal.Notify(sigch, os.Interrupt)

	go func() {
		<-sigch
		os.Exit(0)
	}()

	w := ki.Watcher(key, &client.WatcherOptions{AfterIndex: uint64(index), Recursive: recursive})

	for {
		resp, err := w.Next(context.TODO())
		if err != nil {
			handleError(c, ExitServerError, err)
		}
		if resp.Node.Dir {
			fmt.Fprintf(os.Stderr, "Ignored dir %s change\n", resp.Node.Key)
			continue
		}

		cmd := exec.Command(cmdArgs[0], cmdArgs[1:]...)
		cmd.Env = environResponse(resp, os.Environ())

		cmd.Stdout = os.Stdout
		cmd.Stderr = os.Stderr

		go func() {
			err := cmd.Start()
			if err != nil {
				fmt.Fprintf(os.Stderr, err.Error())
				os.Exit(1)
			}
			cmd.Wait()
		}()
	}
}

// execWatchCommandFunc executes the "exec-watch" command.

execWatchCommandFunc executes the exec - watch command .


func NewListener(u url.URL, tlsinfo *transport.TLSInfo) (net.Listener, error) {
	return transport.NewTimeoutListener(u.Host, u.Scheme, tlsinfo, ConnReadTimeout, ConnWriteTimeout)
}

func NewListener ( u url . URL , tlsinfo * transport . TLSInfo ) ( net . Listener , error ) { return transport . NewTimeoutListener ( u . Host , u . Scheme , tlsinfo , ConnReadTimeout , ConnWriteTimeout ) }

// NewListener returns a listener for raft message transfer between peers.
// It uses timeout listener to identify broken streams promptly.

NewListener returns a listener for raft message transfer between peers . It uses timeout listener to identify broken streams promptly .


func NewRoundTripper(tlsInfo transport.TLSInfo, dialTimeout time.Duration) (http.RoundTripper, error) {
	// It uses timeout transport to pair with remote timeout listeners.
	// It sets no read/write timeout, because message in requests may
	// take long time to write out before reading out the response.
	return transport.NewTimeoutTransport(tlsInfo, dialTimeout, 0, 0)
}

// NewRoundTripper returns a roundTripper used to send requests
// to rafthttp listener of remote peers.

NewRoundTripper returns a roundTripper used to send requests to rafthttp listener of remote peers .


func createPostRequest(u url.URL, path string, body io.Reader, ct string, urls types.URLs, from, cid types.ID) *http.Request {
	uu := u
	uu.Path = path
	req, err := http.NewRequest("POST", uu.String(), body)
	if err != nil {
		plog.Panicf("unexpected new request error (%v)", err)
	}
	req.Header.Set("Content-Type", ct)
	req.Header.Set("X-Server-From", from.String())
	req.Header.Set("X-Server-Version", version.Version)
	req.Header.Set("X-Min-Cluster-Version", version.MinClusterVersion)
	req.Header.Set("X-Etcd-Cluster-ID", cid.String())
	setPeerURLsHeader(req, urls)

	return req
}

func createPostRequest ( u url . URL , path string , body io . Reader , ct string , urls types . URLs , from , cid types . ID ) * http . Request { uu := u uu . Path = path req , err := http . NewRequest ( " " , uu . String ( ) , body ) if err != nil { plog . Panicf ( " " , err ) } req . Header . Set ( " " , ct ) req . Header . Set ( " " , from . String ( ) ) req . Header . Set ( " " , version . Version ) req . Header . Set ( " " , version . MinClusterVersion ) req . Header . Set ( " " , cid . String ( ) ) setPeerURLsHeader ( req , urls ) return req }

// createPostRequest creates a HTTP POST request that sends raft message.

createPostRequest creates a HTTP POST request that sends raft message .


func checkPostResponse(resp *http.Response, body []byte, req *http.Request, to types.ID) error {
	switch resp.StatusCode {
	case http.StatusPreconditionFailed:
		switch strings.TrimSuffix(string(body), "\n") {
		case errIncompatibleVersion.Error():
			plog.Errorf("request sent was ignored by peer %s (server version incompatible)", to)
			return errIncompatibleVersion
		case errClusterIDMismatch.Error():
			plog.Errorf("request sent was ignored (cluster ID mismatch: remote[%s]=%s, local=%s)",
				to, resp.Header.Get("X-Etcd-Cluster-ID"), req.Header.Get("X-Etcd-Cluster-ID"))
			return errClusterIDMismatch
		default:
			return fmt.Errorf("unhandled error %q when precondition failed", string(body))
		}
	case http.StatusForbidden:
		return errMemberRemoved
	case http.StatusNoContent:
		return nil
	default:
		return fmt.Errorf("unexpected http status %s while posting to %q", http.StatusText(resp.StatusCode), req.URL.String())
	}
}

func checkPostResponse ( resp * http . Response , body [ ] byte , req * http . Request , to types . ID ) error { switch resp . StatusCode { case http . StatusPreconditionFailed : switch strings . TrimSuffix ( string ( body ) , " \n " ) { case errIncompatibleVersion . Error ( ) : plog . Errorf ( " " , to ) return errIncompatibleVersion case errClusterIDMismatch . Error ( ) : plog . Errorf ( " " , to , resp . Header . Get ( " " ) , req . Header . Get ( " " ) ) return errClusterIDMismatch default : return fmt . Errorf ( " " , string ( body ) ) } case http . StatusForbidden : return errMemberRemoved case http . StatusNoContent : return nil default : return fmt . Errorf ( " " , http . StatusText ( resp . StatusCode ) , req . URL . String ( ) ) } }

// checkPostResponse checks the response of the HTTP POST request that sends
// raft message.

checkPostResponse checks the response of the HTTP POST request that sends raft message .


func serverVersion(h http.Header) *semver.Version {
	verStr := h.Get("X-Server-Version")
	// backward compatibility with etcd 2.0
	if verStr == "" {
		verStr = "2.0.0"
	}
	return semver.Must(semver.NewVersion(verStr))
}

func
serverVersion
(
h
http
.
Header
)
*
semver
.
Version
{
verStr
:=
h
.
Get
(
"
"
)
// backward compatibility with etcd 2.0
if
verStr
==
"
"
{
verStr
=
"
"
}
return
semver
.
Must
(
semver
.
NewVersion
(
verStr
)
)
}

// serverVersion returns the server version from the given header.

serverVersion returns the server version from the given header .


func checkVersionCompatibility(name string, server, minCluster *semver.Version) (
	localServer *semver.Version,
	localMinCluster *semver.Version,
	err error) {
	localServer = semver.Must(semver.NewVersion(version.Version))
	localMinCluster = semver.Must(semver.NewVersion(version.MinClusterVersion))
	if compareMajorMinorVersion(server, localMinCluster) == -1 {
		return localServer, localMinCluster, fmt.Errorf("remote version is too low: remote[%s]=%s, local=%s", name, server, localServer)
	}
	if compareMajorMinorVersion(minCluster, localServer) == 1 {
		return localServer, localMinCluster, fmt.Errorf("local version is too low: remote[%s]=%s, local=%s", name, server, localServer)
	}
	return localServer, localMinCluster, nil
}

func checkVersionCompatibility ( name string , server , minCluster * semver . Version ) ( localServer * semver . Version , localMinCluster * semver . Version , err error ) { localServer = semver . Must ( semver . NewVersion ( version . Version ) ) localMinCluster = semver . Must ( semver . NewVersion ( version . MinClusterVersion ) ) if compareMajorMinorVersion ( server , localMinCluster ) == - 1 { return localServer , localMinCluster , fmt . Errorf ( " " , name , server , localServer ) } if compareMajorMinorVersion ( minCluster , localServer ) == 1 { return localServer , localMinCluster , fmt . Errorf ( " " , name , server , localServer ) } return localServer , localMinCluster , nil }

// checkVersionCompatibility checks whether the given version is compatible
// with the local version.

checkVersionCompatibility checks whether the given version is compatible with the local version .


func setPeerURLsHeader(req *http.Request, urls types.URLs) {
	if urls == nil {
		// often not set in unit tests
		return
	}
	peerURLs := make([]string, urls.Len())
	for i := range urls {
		peerURLs[i] = urls[i].String()
	}
	req.Header.Set("X-PeerURLs", strings.Join(peerURLs, ","))
}

func
setPeerURLsHeader
(
req
*
http
.
Request
,
urls
types
.
URLs
)
{
if
urls
==
{
// often not set in unit tests
return
}
peerURLs
:=
make
(
[
]
string
,
urls
.
Len
(
)
)
for
i
:=
range
urls
{
peerURLs
[
i
]
=
urls
[
i
]
.
String
(
)
}
req
.
Header
.
Set
(
"
"
,
strings
.
Join
(
peerURLs
,
"
"
)
)
}

// setPeerURLsHeader reports local urls for peer discovery


func addRemoteFromRequest(tr Transporter, r *http.Request) {
	if from, err := types.IDFromString(r.Header.Get("X-Server-From")); err == nil {
		if urls := r.Header.Get("X-PeerURLs"); urls != "" {
			tr.AddRemote(from, strings.Split(urls, ","))
		}
	}
}

func addRemoteFromRequest ( tr Transporter , r * http . Request ) { if from , err := types . IDFromString ( r . Header . Get ( " " ) ) ; err == nil { if urls := r . Header . Get ( " " ) ; urls != " " { tr . AddRemote ( from , strings . Split ( urls , " " ) ) } } }

// addRemoteFromRequest adds a remote peer according to an http request header


func NewKeysAPIWithPrefix(c Client, p string) KeysAPI {
	return &httpKeysAPI{
		client: c,
		prefix: p,
	}
}

func NewKeysAPIWithPrefix ( c Client , p string ) KeysAPI { return & httpKeysAPI { client : c , prefix : p , } }

// NewKeysAPIWithPrefix acts like NewKeysAPI, but allows the caller
// to provide a custom base URL path. This should only be used in
// very rare cases.

NewKeysAPIWithPrefix acts like NewKeysAPI but allows the caller to provide a custom base URL path . This should only be used in very rare cases .


func (n *Node) TTLDuration() time.Duration {
	return time.Duration(n.TTL) * time.Second
}

func ( n * Node ) TTLDuration ( ) time . Duration { return time . Duration ( n . TTL ) * time . Second }

// TTLDuration returns the Node's TTL as a time.Duration object

TTLDuration returns the Node s TTL as a time . Duration object


func SetPflagsFromEnv(prefix string, fs *pflag.FlagSet) error {
	var err error
	alreadySet := make(map[string]bool)
	usedEnvKey := make(map[string]bool)
	fs.VisitAll(func(f *pflag.Flag) {
		if f.Changed {
			alreadySet[FlagToEnv(prefix, f.Name)] = true
		}
		if serr := setFlagFromEnv(fs, prefix, f.Name, usedEnvKey, alreadySet, false); serr != nil {
			err = serr
		}
	})
	verifyEnv(prefix, usedEnvKey, alreadySet)
	return err
}

func SetPflagsFromEnv ( prefix string , fs * pflag . FlagSet ) error { var err error alreadySet := make ( map [ string ] bool ) usedEnvKey := make ( map [ string ] bool ) fs . VisitAll ( func ( f * pflag . Flag ) { if f . Changed { alreadySet [ FlagToEnv ( prefix , f . Name ) ] = true } if serr := setFlagFromEnv ( fs , prefix , f . Name , usedEnvKey , alreadySet , false ) ; serr != nil { err = serr } } ) verifyEnv ( prefix , usedEnvKey , alreadySet ) return err }

// SetPflagsFromEnv is similar to SetFlagsFromEnv. However, the accepted flagset type is pflag.FlagSet
// and it does not do any logging.

SetPflagsFromEnv is similar to SetFlagsFromEnv . However the accepted flagset type is pflag . FlagSet and it does not do any logging .


func FlagToEnv(prefix, name string) string {
	return prefix + "_" + strings.ToUpper(strings.Replace(name, "-", "_", -1))
}

func FlagToEnv ( prefix , name string ) string { return prefix + " " + strings . ToUpper ( strings . Replace ( name , " " , " " , - 1 ) ) }

// FlagToEnv converts flag string to upper-case environment variable key string.

FlagToEnv converts flag string to upper - case environment variable key string .


func excerpt(str string, pre, suf int) string {
	if pre+suf > len(str) {
		return fmt.Sprintf("%q", str)
	}
	return fmt.Sprintf("%q...%q", str[:pre], str[len(str)-suf:])
}

func excerpt ( str string , pre , suf int ) string { if pre + suf > len ( str ) { return fmt . Sprintf ( " " , str ) } return fmt . Sprintf ( " " , str [ : pre ] , str [ len ( str ) - suf : ] ) }

// excerpt replaces middle part with ellipsis and returns a double-quoted
// string safely escaped with Go syntax.

excerpt replaces middle part with ellipsis and returns a double - quoted string safely escaped with Go syntax .


func passConfChange(entry raftpb.Entry) (bool, string) {
	return entry.Type == raftpb.EntryConfChange, "ConfigChange"
}

func passConfChange ( entry raftpb . Entry ) ( bool , string ) { return entry . Type == raftpb . EntryConfChange , " " }

// The 9 pass functions below takes the raftpb.Entry and return if the entry should be printed and the type of entry,
// the type of the entry will used in the following print function

The 9 pass functions below takes the raftpb . Entry and return if the entry should be printed and the type of entry the type of the entry will used in the following print function


func printInternalRaftRequest(entry raftpb.Entry) {
	var rr etcdserverpb.InternalRaftRequest
	if err := rr.Unmarshal(entry.Data); err == nil {
		fmt.Printf("%4d\t%10d\tnorm\t%s", entry.Term, entry.Index, rr.String())
	}
}

func printInternalRaftRequest ( entry raftpb . Entry ) { var rr etcdserverpb . InternalRaftRequest if err := rr . Unmarshal ( entry . Data ) ; err == nil { fmt . Printf ( " \t \t \t " , entry . Term , entry . Index , rr . String ( ) ) } }

// The 4 print functions below print the entry format based on there types
// printInternalRaftRequest is used to print entry information for IRRRange, IRRPut,
// IRRDeleteRange and IRRTxn entries

The 4 print functions below print the entry format based on there types printInternalRaftRequest is used to print entry information for IRRRange IRRPut IRRDeleteRange and IRRTxn entries


func listEntriesType(entrytype string, streamdecoder string, ents []raftpb.Entry) {
	entryFilters := evaluateEntrytypeFlag(entrytype)
	printerMap := map[string]EntryPrinter{"InternalRaftRequest": printInternalRaftRequest,
		"Request":       printRequest,
		"ConfigChange":  printConfChange,
		"UnknownNormal": printUnknownNormal}
	var stderr bytes.Buffer
	args := strings.Split(streamdecoder, " ")
	cmd := exec.Command(args[0], args[1:]...)
	stdin, err := cmd.StdinPipe()
	if err != nil {
		log.Panic(err)
	}
	stdout, err := cmd.StdoutPipe()
	if err != nil {
		log.Panic(err)
	}
	cmd.Stderr = &stderr
	if streamdecoder != "" {
		err = cmd.Start()
		if err != nil {
			log.Panic(err)
		}
	}

	cnt := 0

	for _, e := range ents {
		passed := false
		currtype := ""
		for _, filter := range entryFilters {
			passed, currtype = filter(e)
			if passed {
				cnt++
				break
			}
		}
		if passed {
			printer := printerMap[currtype]
			printer(e)
			if streamdecoder == "" {
				fmt.Println()
				continue
			}

			// if decoder is set, pass the e.Data to stdin and read the stdout from decoder
			io.WriteString(stdin, hex.EncodeToString(e.Data))
			io.WriteString(stdin, "\n")
			outputReader := bufio.NewReader(stdout)
			decoderoutput, currerr := outputReader.ReadString('\n')
			if currerr != nil {
				fmt.Println(currerr)
				return
			}

			decoder_status, decoded_data := parseDecoderOutput(decoderoutput)

			fmt.Printf("\t%s\t%s", decoder_status, decoded_data)
		}
	}

	stdin.Close()
	err = cmd.Wait()
	if streamdecoder != "" {
		if err != nil {
			log.Panic(err)
		}
		if stderr.String() != "" {
			os.Stderr.WriteString("decoder stderr: " + stderr.String())
		}
	}

	fmt.Printf("\nEntry types (%s) count is : %d", entrytype, cnt)
}

//  listEntriesType filters and prints entries based on the entry-type flag,

listEntriesType filters and prints entries based on the entry - type flag


func newLog(storage Storage, logger Logger) *raftLog {
	return newLogWithSize(storage, logger, noLimit)
}

func newLog ( storage Storage , logger Logger ) * raftLog { return newLogWithSize ( storage , logger , noLimit ) }

// newLog returns log using the given storage and default options. It
// recovers the log to the state that it just commits and applies the
// latest snapshot.

newLog returns log using the given storage and default options . It recovers the log to the state that it just commits and applies the latest snapshot .


func newLogWithSize(storage Storage, logger Logger, maxNextEntsSize uint64) *raftLog {
	if storage == nil {
		log.Panic("storage must not be nil")
	}
	log := &raftLog{
		storage:         storage,
		logger:          logger,
		maxNextEntsSize: maxNextEntsSize,
	}
	firstIndex, err := storage.FirstIndex()
	if err != nil {
		panic(err) // TODO(bdarnell)
	}
	lastIndex, err := storage.LastIndex()
	if err != nil {
		panic(err) // TODO(bdarnell)
	}
	log.unstable.offset = lastIndex + 1
	log.unstable.logger = logger
	// Initialize our committed and applied pointers to the time of the last compaction.
	log.committed = firstIndex - 1
	log.applied = firstIndex - 1

	return log
}

// newLogWithSize returns a log using the given storage and max
// message size.

newLogWithSize returns a log using the given storage and max message size .


func (l *raftLog) findConflict(ents []pb.Entry) uint64 {
	for _, ne := range ents {
		if !l.matchTerm(ne.Index, ne.Term) {
			if ne.Index <= l.lastIndex() {
				l.logger.Infof("found conflict at index %d [existing term: %d, conflicting term: %d]",
					ne.Index, l.zeroTermOnErrCompacted(l.term(ne.Index)), ne.Term)
			}
			return ne.Index
		}
	}
	return 0
}

func ( l * raftLog ) findConflict ( ents [ ] pb . Entry ) uint64 { for _ , ne := range ents { if ! l . matchTerm ( ne . Index , ne . Term ) { if ne . Index <= l . lastIndex ( ) { l . logger . Infof ( " " , ne . Index , l . zeroTermOnErrCompacted ( l . term ( ne . Index ) ) , ne . Term ) } return ne . Index } } return 0 }

// findConflict finds the index of the conflict.
// It returns the first pair of conflicting entries between the existing
// entries and the given entries, if there are any.
// If there is no conflicting entries, and the existing entries contains
// all the given entries, zero will be returned.
// If there is no conflicting entries, but the given entries contains new
// entries, the index of the first new entry will be returned.
// An entry is considered to be conflicting if it has the same index but
// a different term.
// The first entry MUST have an index equal to the argument 'from'.
// The index of the given entries MUST be continuously increasing.

findConflict finds the index of the conflict . It returns the first pair of conflicting entries between the existing entries and the given entries if there are any . If there is no conflicting entries and the existing entries contains all the given entries zero will be returned . If there is no conflicting entries but the given entries contains new entries the index of the first new entry will be returned . An entry is considered to be conflicting if it has the same index but a different term . The first entry MUST have an index equal to the argument from . The index of the given entries MUST be continuously increasing .


func (l *raftLog) nextEnts() (ents []pb.Entry) {
	off := max(l.applied+1, l.firstIndex())
	if l.committed+1 > off {
		ents, err := l.slice(off, l.committed+1, l.maxNextEntsSize)
		if err != nil {
			l.logger.Panicf("unexpected error when getting unapplied entries (%v)", err)
		}
		return ents
	}
	return nil
}

func ( l * raftLog ) nextEnts ( ) ( ents [ ] pb . Entry ) { off := max ( l . applied + 1 , l . firstIndex ( ) ) if l . committed + 1 > off { ents , err := l . slice ( off , l . committed + 1 , l . maxNextEntsSize ) if err != nil { l . logger . Panicf ( " " , err ) } return ents } return nil }

// nextEnts returns all the available entries for execution.
// If applied is smaller than the index of snapshot, it returns all committed
// entries after the index of snapshot.

nextEnts returns all the available entries for execution . If applied is smaller than the index of snapshot it returns all committed entries after the index of snapshot .


func (l *raftLog) allEntries() []pb.Entry {
	ents, err := l.entries(l.firstIndex(), noLimit)
	if err == nil {
		return ents
	}
	if err == ErrCompacted { // try again if there was a racing compaction
		return l.allEntries()
	}
	// TODO (xiangli): handle error?
	panic(err)
}

// allEntries returns all entries in the log.

allEntries returns all entries in the log .


func (l *raftLog) slice(lo, hi, maxSize uint64) ([]pb.Entry, error) {
	err := l.mustCheckOutOfBounds(lo, hi)
	if err != nil {
		return nil, err
	}
	if lo == hi {
		return nil, nil
	}
	var ents []pb.Entry
	if lo < l.unstable.offset {
		storedEnts, err := l.storage.Entries(lo, min(hi, l.unstable.offset), maxSize)
		if err == ErrCompacted {
			return nil, err
		} else if err == ErrUnavailable {
			l.logger.Panicf("entries[%d:%d) is unavailable from storage", lo, min(hi, l.unstable.offset))
		} else if err != nil {
			panic(err) // TODO(bdarnell)
		}

		// check if ents has reached the size limitation
		if uint64(len(storedEnts)) < min(hi, l.unstable.offset)-lo {
			return storedEnts, nil
		}

		ents = storedEnts
	}
	if hi > l.unstable.offset {
		unstable := l.unstable.slice(max(lo, l.unstable.offset), hi)
		if len(ents) > 0 {
			combined := make([]pb.Entry, len(ents)+len(unstable))
			n := copy(combined, ents)
			copy(combined[n:], unstable)
			ents = combined
		} else {
			ents = unstable
		}
	}
	return limitSize(ents, maxSize), nil
}

// slice returns a slice of log entries from lo through hi-1, inclusive.

slice returns a slice of log entries from lo through hi - 1 inclusive .


func NewSession(client *v3.Client, opts ...SessionOption) (*Session, error) {
	ops := &sessionOptions{ttl: defaultSessionTTL, ctx: client.Ctx()}
	for _, opt := range opts {
		opt(ops)
	}

	id := ops.leaseID
	if id == v3.NoLease {
		resp, err := client.Grant(ops.ctx, int64(ops.ttl))
		if err != nil {
			return nil, err
		}
		id = v3.LeaseID(resp.ID)
	}

	ctx, cancel := context.WithCancel(ops.ctx)
	keepAlive, err := client.KeepAlive(ctx, id)
	if err != nil || keepAlive == nil {
		cancel()
		return nil, err
	}

	donec := make(chan struct{})
	s := &Session{client: client, opts: ops, id: id, cancel: cancel, donec: donec}

	// keep the lease alive until client error or cancelled context
	go func() {
		defer close(donec)
		for range keepAlive {
			// eat messages until keep alive channel closes
		}
	}()

	return s, nil
}

// NewSession gets the leased session for a client.

NewSession gets the leased session for a client .


func (s *Session) Close() error {
	s.Orphan()
	// if revoke takes longer than the ttl, lease is expired anyway
	ctx, cancel := context.WithTimeout(s.opts.ctx, time.Duration(s.opts.ttl)*time.Second)
	_, err := s.client.Revoke(ctx, s.id)
	cancel()
	return err
}

// Close orphans the session and revokes the session lease.

Close orphans the session and revokes the session lease .


func WithTTL(ttl int) SessionOption {
	return func(so *sessionOptions) {
		if ttl > 0 {
			so.ttl = ttl
		}
	}
}

func WithTTL ( ttl int ) SessionOption { return func ( so * sessionOptions ) { if ttl > 0 { so . ttl = ttl } } }

// WithTTL configures the session's TTL in seconds.
// If TTL is <= 0, the default 60 seconds TTL will be used.

WithTTL configures the session s TTL in seconds . If TTL is < = 0 the default 60 seconds TTL will be used .


func WithLease(leaseID v3.LeaseID) SessionOption {
	return func(so *sessionOptions) {
		so.leaseID = leaseID
	}
}

func WithLease ( leaseID v3 . LeaseID ) SessionOption { return func ( so * sessionOptions ) { so . leaseID = leaseID } }

// WithLease specifies the existing leaseID to be used for the session.
// This is useful in process restart scenario, for example, to reclaim
// leadership from an election prior to restart.

WithLease specifies the existing leaseID to be used for the session . This is useful in process restart scenario for example to reclaim leadership from an election prior to restart .


func (ro *readOnly) addRequest(index uint64, m pb.Message) {
	ctx := string(m.Entries[0].Data)
	if _, ok := ro.pendingReadIndex[ctx]; ok {
		return
	}
	ro.pendingReadIndex[ctx] = &readIndexStatus{index: index, req: m, acks: make(map[uint64]struct{})}
	ro.readIndexQueue = append(ro.readIndexQueue, ctx)
}

func ( ro * readOnly ) addRequest ( index uint64 , m pb . Message ) { ctx := string ( m . Entries [ 0 ] . Data ) if _ , ok := ro . pendingReadIndex [ ctx ] ; ok { return } ro . pendingReadIndex [ ctx ] = & readIndexStatus { index : index , req : m , acks : make ( map [ uint64 ] struct { } ) } ro . readIndexQueue = append ( ro . readIndexQueue , ctx ) }

// addRequest adds a read only reuqest into readonly struct.
// `index` is the commit index of the raft state machine when it received
// the read only request.
// `m` is the original read only request message from the local or remote node.

addRequest adds a read only reuqest into readonly struct . index is the commit index of the raft state machine when it received the read only request . m is the original read only request message from the local or remote node .


func (ro *readOnly) recvAck(m pb.Message) int {
	rs, ok := ro.pendingReadIndex[string(m.Context)]
	if !ok {
		return 0
	}

	rs.acks[m.From] = struct{}{}
	// add one to include an ack from local node
	return len(rs.acks) + 1
}

// recvAck notifies the readonly struct that the raft state machine received
// an acknowledgment of the heartbeat that attached with the read only request
// context.

recvAck notifies the readonly struct that the raft state machine received an acknowledgment of the heartbeat that attached with the read only request context .


func (ro *readOnly) advance(m pb.Message) []*readIndexStatus {
	var (
		i     int
		found bool
	)

	ctx := string(m.Context)
	rss := []*readIndexStatus{}

	for _, okctx := range ro.readIndexQueue {
		i++
		rs, ok := ro.pendingReadIndex[okctx]
		if !ok {
			panic("cannot find corresponding read state from pending map")
		}
		rss = append(rss, rs)
		if okctx == ctx {
			found = true
			break
		}
	}

	if found {
		ro.readIndexQueue = ro.readIndexQueue[i:]
		for _, rs := range rss {
			delete(ro.pendingReadIndex, string(rs.req.Entries[0].Data))
		}
		return rss
	}

	return nil
}

func ( ro * readOnly ) advance ( m pb . Message ) [ ] * readIndexStatus { var ( i int found bool ) ctx := string ( m . Context ) rss := [ ] * readIndexStatus { } for _ , okctx := range ro . readIndexQueue { i ++ rs , ok := ro . pendingReadIndex [ okctx ] if ! ok { panic ( " " ) } rss = append ( rss , rs ) if okctx == ctx { found = true break } } if found { ro . readIndexQueue = ro . readIndexQueue [ i : ] for _ , rs := range rss { delete ( ro . pendingReadIndex , string ( rs . req . Entries [ 0 ] . Data ) ) } return rss } return nil }

// advance advances the read only request queue kept by the readonly struct.
// It dequeues the requests until it finds the read only request that has
// the same context as the given `m`.

advance advances the read only request queue kept by the readonly struct . It dequeues the requests until it finds the read only request that has the same context as the given m .


func (ro *readOnly) lastPendingRequestCtx() string {
	if len(ro.readIndexQueue) == 0 {
		return ""
	}
	return ro.readIndexQueue[len(ro.readIndexQueue)-1]
}

func ( ro * readOnly ) lastPendingRequestCtx ( ) string { if len ( ro . readIndexQueue ) == 0 { return " " } return ro . readIndexQueue [ len ( ro . readIndexQueue ) - 1 ] }

// lastPendingRequestCtx returns the context of the last pending read only
// request in readonly struct.

lastPendingRequestCtx returns the context of the last pending read only request in readonly struct .


func (s *EtcdServer) Start() {
	s.start()
	s.goAttach(func() { s.adjustTicks() })
	s.goAttach(func() { s.publish(s.Cfg.ReqTimeout()) })
	s.goAttach(s.purgeFile)
	s.goAttach(func() { monitorFileDescriptor(s.getLogger(), s.stopping) })
	s.goAttach(s.monitorVersions)
	s.goAttach(s.linearizableReadLoop)
	s.goAttach(s.monitorKVHash)
}

func ( s * EtcdServer ) Start ( ) { s . start ( ) s . goAttach ( func ( ) { s . adjustTicks ( ) } ) s . goAttach ( func ( ) { s . publish ( s . Cfg . ReqTimeout ( ) ) } ) s . goAttach ( s . purgeFile ) s . goAttach ( func ( ) { monitorFileDescriptor ( s . getLogger ( ) , s . stopping ) } ) s . goAttach ( s . monitorVersions ) s . goAttach ( s . linearizableReadLoop ) s . goAttach ( s . monitorKVHash ) }

// Start performs any initialization of the Server necessary for it to
// begin serving requests. It must be called before Do or Process.
// Start must be non-blocking; any long-running server functionality
// should be implemented in goroutines.

Start performs any initialization of the Server necessary for it to begin serving requests . It must be called before Do or Process . Start must be non - blocking ; any long - running server functionality should be implemented in goroutines .


func (s *EtcdServer) start() {
	lg := s.getLogger()

	if s.Cfg.SnapshotCount == 0 {
		if lg != nil {
			lg.Info(
				"updating snapshot-count to default",
				zap.Uint64("given-snapshot-count", s.Cfg.SnapshotCount),
				zap.Uint64("updated-snapshot-count", DefaultSnapshotCount),
			)
		} else {
			plog.Infof("set snapshot count to default %d", DefaultSnapshotCount)
		}
		s.Cfg.SnapshotCount = DefaultSnapshotCount
	}
	if s.Cfg.SnapshotCatchUpEntries == 0 {
		if lg != nil {
			lg.Info(
				"updating snapshot catch-up entries to default",
				zap.Uint64("given-snapshot-catchup-entries", s.Cfg.SnapshotCatchUpEntries),
				zap.Uint64("updated-snapshot-catchup-entries", DefaultSnapshotCatchUpEntries),
			)
		}
		s.Cfg.SnapshotCatchUpEntries = DefaultSnapshotCatchUpEntries
	}

	s.w = wait.New()
	s.applyWait = wait.NewTimeList()
	s.done = make(chan struct{})
	s.stop = make(chan struct{})
	s.stopping = make(chan struct{})
	s.ctx, s.cancel = context.WithCancel(context.Background())
	s.readwaitc = make(chan struct{}, 1)
	s.readNotifier = newNotifier()
	s.leaderChanged = make(chan struct{})
	if s.ClusterVersion() != nil {
		if lg != nil {
			lg.Info(
				"starting etcd server",
				zap.String("local-member-id", s.ID().String()),
				zap.String("local-server-version", version.Version),
				zap.String("cluster-id", s.Cluster().ID().String()),
				zap.String("cluster-version", version.Cluster(s.ClusterVersion().String())),
			)
		} else {
			plog.Infof("starting server... [version: %v, cluster version: %v]", version.Version, version.Cluster(s.ClusterVersion().String()))
		}
		membership.ClusterVersionMetrics.With(prometheus.Labels{"cluster_version": s.ClusterVersion().String()}).Set(1)
	} else {
		if lg != nil {
			lg.Info(
				"starting etcd server",
				zap.String("local-member-id", s.ID().String()),
				zap.String("local-server-version", version.Version),
				zap.String("cluster-version", "to_be_decided"),
			)
		} else {
			plog.Infof("starting server... [version: %v, cluster version: to_be_decided]", version.Version)
		}
	}

	// TODO: if this is an empty log, writes all peer infos
	// into the first entry
	go s.run()
}

// start prepares and starts server in a new goroutine. It is no longer safe to
// modify a server's fields after it has been sent to Start.
// This function is just used for testing.

start prepares and starts server in a new goroutine . It is no longer safe to modify a server s fields after it has been sent to Start . This function is just used for testing .


func (s *EtcdServer) Process(ctx context.Context, m raftpb.Message) error {
	if s.cluster.IsIDRemoved(types.ID(m.From)) {
		if lg := s.getLogger(); lg != nil {
			lg.Warn(
				"rejected Raft message from removed member",
				zap.String("local-member-id", s.ID().String()),
				zap.String("removed-member-id", types.ID(m.From).String()),
			)
		} else {
			plog.Warningf("reject message from removed member %s", types.ID(m.From).String())
		}
		return httptypes.NewHTTPError(http.StatusForbidden, "cannot process message from removed member")
	}
	if m.Type == raftpb.MsgApp {
		s.stats.RecvAppendReq(types.ID(m.From).String(), m.Size())
	}
	return s.r.Step(ctx, m)
}

func ( s * EtcdServer ) Process ( ctx context . Context , m raftpb . Message ) error { if s . cluster . IsIDRemoved ( types . ID ( m . From ) ) { if lg := s . getLogger ( ) ; lg != nil { lg . Warn ( " " , zap . String ( " " , s . ID ( ) . String ( ) ) , zap . String ( " " , types . ID ( m . From ) . String ( ) ) , ) } else { plog . Warningf ( " " , types . ID ( m . From ) . String ( ) ) } return httptypes . NewHTTPError ( http . StatusForbidden , " " ) } if m . Type == raftpb . MsgApp { s . stats . RecvAppendReq ( types . ID ( m . From ) . String ( ) , m . Size ( ) ) } return s . r . Step ( ctx , m ) }

// Process takes a raft message and applies it to the server's raft state
// machine, respecting any timeout of the given context.

Process takes a raft message and applies it to the server s raft state machine respecting any timeout of the given context .


func (s *EtcdServer) ReportSnapshot(id uint64, status raft.SnapshotStatus) {
	s.r.ReportSnapshot(id, status)
}

func ( s * EtcdServer ) ReportSnapshot ( id uint64 , status raft . SnapshotStatus ) { s . r . ReportSnapshot ( id , status ) }

// ReportSnapshot reports snapshot sent status to the raft state machine,
// and clears the used snapshot from the snapshot store.

ReportSnapshot reports snapshot sent status to the raft state machine and clears the used snapshot from the snapshot store .


func (s *EtcdServer) MoveLeader(ctx context.Context, lead, transferee uint64) error {
	now := time.Now()
	interval := time.Duration(s.Cfg.TickMs) * time.Millisecond

	if lg := s.getLogger(); lg != nil {
		lg.Info(
			"leadership transfer starting",
			zap.String("local-member-id", s.ID().String()),
			zap.String("current-leader-member-id", types.ID(lead).String()),
			zap.String("transferee-member-id", types.ID(transferee).String()),
		)
	} else {
		plog.Infof("%s starts leadership transfer from %s to %s", s.ID(), types.ID(lead), types.ID(transferee))
	}

	s.r.TransferLeadership(ctx, lead, transferee)
	for s.Lead() != transferee {
		select {
		case <-ctx.Done(): // time out
			return ErrTimeoutLeaderTransfer
		case <-time.After(interval):
		}
	}

	// TODO: drain all requests, or drop all messages to the old leader
	if lg := s.getLogger(); lg != nil {
		lg.Info(
			"leadership transfer finished",
			zap.String("local-member-id", s.ID().String()),
			zap.String("old-leader-member-id", types.ID(lead).String()),
			zap.String("new-leader-member-id", types.ID(transferee).String()),
			zap.Duration("took", time.Since(now)),
		)
	} else {
		plog.Infof("%s finished leadership transfer from %s to %s (took %v)", s.ID(), types.ID(lead), types.ID(transferee), time.Since(now))
	}
	return nil
}

// MoveLeader transfers the leader to the given transferee.

MoveLeader transfers the leader to the given transferee .


func (s *EtcdServer) TransferLeadership() error {
	if !s.isLeader() {
		if lg := s.getLogger(); lg != nil {
			lg.Info(
				"skipped leadership transfer; local server is not leader",
				zap.String("local-member-id", s.ID().String()),
				zap.String("current-leader-member-id", types.ID(s.Lead()).String()),
			)
		} else {
			plog.Printf("skipped leadership transfer for stopping non-leader member")
		}
		return nil
	}

	if !s.isMultiNode() {
		if lg := s.getLogger(); lg != nil {
			lg.Info(
				"skipped leadership transfer; it's a single-node cluster",
				zap.String("local-member-id", s.ID().String()),
				zap.String("current-leader-member-id", types.ID(s.Lead()).String()),
			)
		} else {
			plog.Printf("skipped leadership transfer for single member cluster")
		}
		return nil
	}

	transferee, ok := longestConnected(s.r.transport, s.cluster.MemberIDs())
	if !ok {
		return ErrUnhealthy
	}

	tm := s.Cfg.ReqTimeout()
	ctx, cancel := context.WithTimeout(s.ctx, tm)
	err := s.MoveLeader(ctx, s.Lead(), uint64(transferee))
	cancel()
	return err
}

func ( s * EtcdServer ) TransferLeadership ( ) error { if ! s . isLeader ( ) { if lg := s . getLogger ( ) ; lg != nil { lg . Info ( " " , zap . String ( " " , s . ID ( ) . String ( ) ) , zap . String ( " " , types . ID ( s . Lead ( ) ) . String ( ) ) , ) } else { plog . Printf ( " " ) } return nil } if ! s . isMultiNode ( ) { if lg := s . getLogger ( ) ; lg != nil { lg . Info ( " " , zap . String ( " " , s . ID ( ) . String ( ) ) , zap . String ( " " , types . ID ( s . Lead ( ) ) . String ( ) ) , ) } else { plog . Printf ( " " ) } return nil } transferee , ok := longestConnected ( s . r . transport , s . cluster . MemberIDs ( ) ) if ! ok { return ErrUnhealthy } tm := s . Cfg . ReqTimeout ( ) ctx , cancel := context . WithTimeout ( s . ctx , tm ) err := s . MoveLeader ( ctx , s . Lead ( ) , uint64 ( transferee ) ) cancel ( ) return err }

// TransferLeadership transfers the leader to the chosen transferee.

TransferLeadership transfers the leader to the chosen transferee .


func (s *EtcdServer) configure(ctx context.Context, cc raftpb.ConfChange) ([]*membership.Member, error) {
	cc.ID = s.reqIDGen.Next()
	ch := s.w.Register(cc.ID)

	start := time.Now()
	if err := s.r.ProposeConfChange(ctx, cc); err != nil {
		s.w.Trigger(cc.ID, nil)
		return nil, err
	}

	select {
	case x := <-ch:
		if x == nil {
			if lg := s.getLogger(); lg != nil {
				lg.Panic("failed to configure")
			} else {
				plog.Panicf("configure trigger value should never be nil")
			}
		}
		resp := x.(*confChangeResponse)
		if lg := s.getLogger(); lg != nil {
			lg.Info(
				"applied a configuration change through raft",
				zap.String("local-member-id", s.ID().String()),
				zap.String("raft-conf-change", cc.Type.String()),
				zap.String("raft-conf-change-node-id", types.ID(cc.NodeID).String()),
			)
		}
		return resp.membs, resp.err

	case <-ctx.Done():
		s.w.Trigger(cc.ID, nil) // GC wait
		return nil, s.parseProposeCtxErr(ctx.Err(), start)

	case <-s.stopping:
		return nil, ErrStopped
	}
}

func
(
s
*
EtcdServer
)
configure
(
ctx
context
.
Context
,
cc
raftpb
.
ConfChange
)
(
[
]
*
membership
.
Member
,
error
)
{
cc
.
ID
=
s
.
reqIDGen
.
Next
(
)
ch
:=
s
.
w
.
Register
(
cc
.
ID
)
start
:=
time
.
Now
(
)
if
err
:=
s
.
r
.
ProposeConfChange
(
ctx
,
cc
)
;
err
!=
{
s
.
w
.
Trigger
(
cc
.
ID
,
)
return
,
err
}
select
{
case
x
:=
<-
ch
:
if
x
==
{
if
lg
:=
s
.
getLogger
(
)
;
lg
!=
{
lg
.
Panic
(
"
"
)
}
else
{
plog
.
Panicf
(
"
"
)
}
}
resp
:=
x
.
(
*
confChangeResponse
)
if
lg
:=
s
.
getLogger
(
)
;
lg
!=
{
lg
.
Info
(
"
"
,
zap
.
String
(
"
"
,
s
.
ID
(
)
.
String
(
)
)
,
zap
.
String
(
"
"
,
cc
.
Type
.
String
(
)
)
,
zap
.
String
(
"
"
,
types
.
ID
(
cc
.
NodeID
)
.
String
(
)
)
,
)
}
return
resp
.
membs
,
resp
.
err
case
<-
ctx
.
Done
(
)
:
s
.
w
.
Trigger
(
cc
.
ID
,
)
// GC wait
return
,
s
.
parseProposeCtxErr
(
ctx
.
Err
(
)
,
start
)
case
<-
s
.
stopping
:
return
,
ErrStopped
}
}

// configure sends a configuration change through consensus and
// then waits for it to be applied to the server. It
// will block until the change is performed or there is an error.

configure sends a configuration change through consensus and then waits for it to be applied to the server . It will block until the change is performed or there is an error .


func (s *EtcdServer) sync(timeout time.Duration) {
	req := pb.Request{
		Method: "SYNC",
		ID:     s.reqIDGen.Next(),
		Time:   time.Now().UnixNano(),
	}
	data := pbutil.MustMarshal(&req)
	// There is no promise that node has leader when do SYNC request,
	// so it uses goroutine to propose.
	ctx, cancel := context.WithTimeout(s.ctx, timeout)
	s.goAttach(func() {
		s.r.Propose(ctx, data)
		cancel()
	})
}

// sync proposes a SYNC request and is non-blocking.
// This makes no guarantee that the request will be proposed or performed.
// The request will be canceled after the given timeout.

sync proposes a SYNC request and is non - blocking . This makes no guarantee that the request will be proposed or performed . The request will be canceled after the given timeout .


func (s *EtcdServer) publish(timeout time.Duration) {
	b, err := json.Marshal(s.attributes)
	if err != nil {
		if lg := s.getLogger(); lg != nil {
			lg.Panic("failed to marshal JSON", zap.Error(err))
		} else {
			plog.Panicf("json marshal error: %v", err)
		}
		return
	}
	req := pb.Request{
		Method: "PUT",
		Path:   membership.MemberAttributesStorePath(s.id),
		Val:    string(b),
	}

	for {
		ctx, cancel := context.WithTimeout(s.ctx, timeout)
		_, err := s.Do(ctx, req)
		cancel()
		switch err {
		case nil:
			close(s.readych)
			if lg := s.getLogger(); lg != nil {
				lg.Info(
					"published local member to cluster through raft",
					zap.String("local-member-id", s.ID().String()),
					zap.String("local-member-attributes", fmt.Sprintf("%+v", s.attributes)),
					zap.String("request-path", req.Path),
					zap.String("cluster-id", s.cluster.ID().String()),
					zap.Duration("publish-timeout", timeout),
				)
			} else {
				plog.Infof("published %+v to cluster %s", s.attributes, s.cluster.ID())
			}
			return

		case ErrStopped:
			if lg := s.getLogger(); lg != nil {
				lg.Warn(
					"stopped publish because server is stopped",
					zap.String("local-member-id", s.ID().String()),
					zap.String("local-member-attributes", fmt.Sprintf("%+v", s.attributes)),
					zap.Duration("publish-timeout", timeout),
					zap.Error(err),
				)
			} else {
				plog.Infof("aborting publish because server is stopped")
			}
			return

		default:
			if lg := s.getLogger(); lg != nil {
				lg.Warn(
					"failed to publish local member to cluster through raft",
					zap.String("local-member-id", s.ID().String()),
					zap.String("local-member-attributes", fmt.Sprintf("%+v", s.attributes)),
					zap.String("request-path", req.Path),
					zap.Duration("publish-timeout", timeout),
					zap.Error(err),
				)
			} else {
				plog.Errorf("publish error: %v", err)
			}
		}
	}
}

func ( s * EtcdServer ) publish ( timeout time . Duration ) { b , err := json . Marshal ( s . attributes ) if err != nil { if lg := s . getLogger ( ) ; lg != nil { lg . Panic ( " " , zap . Error ( err ) ) } else { plog . Panicf ( " " , err ) } return } req := pb . Request { Method : " " , Path : membership . MemberAttributesStorePath ( s . id ) , Val : string ( b ) , } for { ctx , cancel := context . WithTimeout ( s . ctx , timeout ) _ , err := s . Do ( ctx , req ) cancel ( ) switch err { case nil : close ( s . readych ) if lg := s . getLogger ( ) ; lg != nil { lg . Info ( " " , zap . String ( " " , s . ID ( ) . String ( ) ) , zap . String ( " " , fmt . Sprintf ( " " , s . attributes ) ) , zap . String ( " " , req . Path ) , zap . String ( " " , s . cluster . ID ( ) . String ( ) ) , zap . Duration ( " " , timeout ) , ) } else { plog . Infof ( " " , s . attributes , s . cluster . ID ( ) ) } return case ErrStopped : if lg := s . getLogger ( ) ; lg != nil { lg . Warn ( " " , zap . String ( " " , s . ID ( ) . String ( ) ) , zap . String ( " " , fmt . Sprintf ( " " , s . attributes ) ) , zap . Duration ( " " , timeout ) , zap . Error ( err ) , ) } else { plog . Infof ( " " ) } return default : if lg := s . getLogger ( ) ; lg != nil { lg . Warn ( " " , zap . String ( " " , s . ID ( ) . String ( ) ) , zap . String ( " " , fmt . Sprintf ( " " , s . attributes ) ) , zap . String ( " " , req . Path ) , zap . Duration ( " " , timeout ) , zap . Error ( err ) , ) } else { plog . Errorf ( " " , err ) } } } }

// publish registers server information into the cluster. The information
// is the JSON representation of this server's member struct, updated with the
// static clientURLs of the server.
// The function keeps attempting to register until it succeeds,
// or its server is stopped.

publish registers server information into the cluster . The information is the JSON representation of this server s member struct updated with the static clientURLs of the server . The function keeps attempting to register until it succeeds or its server is stopped .


func (s *EtcdServer) applyEntryNormal(e *raftpb.Entry) {
	shouldApplyV3 := false
	if e.Index > s.consistIndex.ConsistentIndex() {
		// set the consistent index of current executing entry
		s.consistIndex.setConsistentIndex(e.Index)
		shouldApplyV3 = true
	}

	// raft state machine may generate noop entry when leader confirmation.
	// skip it in advance to avoid some potential bug in the future
	if len(e.Data) == 0 {
		select {
		case s.forceVersionC <- struct{}{}:
		default:
		}
		// promote lessor when the local member is leader and finished
		// applying all entries from the last term.
		if s.isLeader() {
			s.lessor.Promote(s.Cfg.electionTimeout())
		}
		return
	}

	var raftReq pb.InternalRaftRequest
	if !pbutil.MaybeUnmarshal(&raftReq, e.Data) { // backward compatible
		var r pb.Request
		rp := &r
		pbutil.MustUnmarshal(rp, e.Data)
		s.w.Trigger(r.ID, s.applyV2Request((*RequestV2)(rp)))
		return
	}
	if raftReq.V2 != nil {
		req := (*RequestV2)(raftReq.V2)
		s.w.Trigger(req.ID, s.applyV2Request(req))
		return
	}

	// do not re-apply applied entries.
	if !shouldApplyV3 {
		return
	}

	id := raftReq.ID
	if id == 0 {
		id = raftReq.Header.ID
	}

	var ar *applyResult
	needResult := s.w.IsRegistered(id)
	if needResult || !noSideEffect(&raftReq) {
		if !needResult && raftReq.Txn != nil {
			removeNeedlessRangeReqs(raftReq.Txn)
		}
		ar = s.applyV3.Apply(&raftReq)
	}

	if ar == nil {
		return
	}

	if ar.err != ErrNoSpace || len(s.alarmStore.Get(pb.AlarmType_NOSPACE)) > 0 {
		s.w.Trigger(id, ar)
		return
	}

	if lg := s.getLogger(); lg != nil {
		lg.Warn(
			"message exceeded backend quota; raising alarm",
			zap.Int64("quota-size-bytes", s.Cfg.QuotaBackendBytes),
			zap.String("quota-size", humanize.Bytes(uint64(s.Cfg.QuotaBackendBytes))),
			zap.Error(ar.err),
		)
	} else {
		plog.Errorf("applying raft message exceeded backend quota")
	}

	s.goAttach(func() {
		a := &pb.AlarmRequest{
			MemberID: uint64(s.ID()),
			Action:   pb.AlarmRequest_ACTIVATE,
			Alarm:    pb.AlarmType_NOSPACE,
		}
		s.raftRequest(s.ctx, pb.InternalRaftRequest{Alarm: a})
		s.w.Trigger(id, ar)
	})
}

// applyEntryNormal apples an EntryNormal type raftpb request to the EtcdServer


func (s *EtcdServer) applyConfChange(cc raftpb.ConfChange, confState *raftpb.ConfState) (bool, error) {
	if err := s.cluster.ValidateConfigurationChange(cc); err != nil {
		cc.NodeID = raft.None
		s.r.ApplyConfChange(cc)
		return false, err
	}

	lg := s.getLogger()
	*confState = *s.r.ApplyConfChange(cc)
	switch cc.Type {
	case raftpb.ConfChangeAddNode:
		m := new(membership.Member)
		if err := json.Unmarshal(cc.Context, m); err != nil {
			if lg != nil {
				lg.Panic("failed to unmarshal member", zap.Error(err))
			} else {
				plog.Panicf("unmarshal member should never fail: %v", err)
			}
		}
		if cc.NodeID != uint64(m.ID) {
			if lg != nil {
				lg.Panic(
					"got different member ID",
					zap.String("member-id-from-config-change-entry", types.ID(cc.NodeID).String()),
					zap.String("member-id-from-message", m.ID.String()),
				)
			} else {
				plog.Panicf("nodeID should always be equal to member ID")
			}
		}
		s.cluster.AddMember(m)
		if m.ID != s.id {
			s.r.transport.AddPeer(m.ID, m.PeerURLs)
		}

	case raftpb.ConfChangeRemoveNode:
		id := types.ID(cc.NodeID)
		s.cluster.RemoveMember(id)
		if id == s.id {
			return true, nil
		}
		s.r.transport.RemovePeer(id)

	case raftpb.ConfChangeUpdateNode:
		m := new(membership.Member)
		if err := json.Unmarshal(cc.Context, m); err != nil {
			if lg != nil {
				lg.Panic("failed to unmarshal member", zap.Error(err))
			} else {
				plog.Panicf("unmarshal member should never fail: %v", err)
			}
		}
		if cc.NodeID != uint64(m.ID) {
			if lg != nil {
				lg.Panic(
					"got different member ID",
					zap.String("member-id-from-config-change-entry", types.ID(cc.NodeID).String()),
					zap.String("member-id-from-message", m.ID.String()),
				)
			} else {
				plog.Panicf("nodeID should always be equal to member ID")
			}
		}
		s.cluster.UpdateRaftAttributes(m.ID, m.RaftAttributes)
		if m.ID != s.id {
			s.r.transport.UpdatePeer(m.ID, m.PeerURLs)
		}
	}
	return false, nil
}

func ( s * EtcdServer ) applyConfChange ( cc raftpb . ConfChange , confState * raftpb . ConfState ) ( bool , error ) { if err := s . cluster . ValidateConfigurationChange ( cc ) ; err != nil { cc . NodeID = raft . None s . r . ApplyConfChange ( cc ) return false , err } lg := s . getLogger ( ) * confState = * s . r . ApplyConfChange ( cc ) switch cc . Type { case raftpb . ConfChangeAddNode : m := new ( membership . Member ) if err := json . Unmarshal ( cc . Context , m ) ; err != nil { if lg != nil { lg . Panic ( " " , zap . Error ( err ) ) } else { plog . Panicf ( " " , err ) } } if cc . NodeID != uint64 ( m . ID ) { if lg != nil { lg . Panic ( " " , zap . String ( " " , types . ID ( cc . NodeID ) . String ( ) ) , zap . String ( " " , m . ID . String ( ) ) , ) } else { plog . Panicf ( " " ) } } s . cluster . AddMember ( m ) if m . ID != s . id { s . r . transport . AddPeer ( m . ID , m . PeerURLs ) } case raftpb . ConfChangeRemoveNode : id := types . ID ( cc . NodeID ) s . cluster . RemoveMember ( id ) if id == s . id { return true , nil } s . r . transport . RemovePeer ( id ) case raftpb . ConfChangeUpdateNode : m := new ( membership . Member ) if err := json . Unmarshal ( cc . Context , m ) ; err != nil { if lg != nil { lg . Panic ( " " , zap . Error ( err ) ) } else { plog . Panicf ( " " , err ) } } if cc . NodeID != uint64 ( m . ID ) { if lg != nil { lg . Panic ( " " , zap . String ( " " , types . ID ( cc . NodeID ) . String ( ) ) , zap . String ( " " , m . ID . String ( ) ) , ) } else { plog . Panicf ( " " ) } } s . cluster . UpdateRaftAttributes ( m . ID , m . RaftAttributes ) if m . ID != s . id { s . r . transport . UpdatePeer ( m . ID , m . PeerURLs ) } } return false , nil }

// applyConfChange applies a ConfChange to the server. It is only
// invoked with a ConfChange that has already passed through Raft

applyConfChange applies a ConfChange to the server . It is only invoked with a ConfChange that has already passed through Raft


func (s *EtcdServer) monitorVersions() {
	for {
		select {
		case <-s.forceVersionC:
		case <-time.After(monitorVersionInterval):
		case <-s.stopping:
			return
		}

		if s.Leader() != s.ID() {
			continue
		}

		v := decideClusterVersion(s.getLogger(), getVersions(s.getLogger(), s.cluster, s.id, s.peerRt))
		if v != nil {
			// only keep major.minor version for comparison
			v = &semver.Version{
				Major: v.Major,
				Minor: v.Minor,
			}
		}

		// if the current version is nil:
		// 1. use the decided version if possible
		// 2. or use the min cluster version
		if s.cluster.Version() == nil {
			verStr := version.MinClusterVersion
			if v != nil {
				verStr = v.String()
			}
			s.goAttach(func() { s.updateClusterVersion(verStr) })
			continue
		}

		// update cluster version only if the decided version is greater than
		// the current cluster version
		if v != nil && s.cluster.Version().LessThan(*v) {
			s.goAttach(func() { s.updateClusterVersion(v.String()) })
		}
	}
}

// monitorVersions checks the member's version every monitorVersionInterval.
// It updates the cluster version if all members agrees on a higher one.
// It prints out log if there is a member with a higher version than the
// local version.

monitorVersions checks the member s version every monitorVersionInterval . It updates the cluster version if all members agrees on a higher one . It prints out log if there is a member with a higher version than the local version .


func (s *EtcdServer) goAttach(f func()) {
	s.wgMu.RLock() // this blocks with ongoing close(s.stopping)
	defer s.wgMu.RUnlock()
	select {
	case <-s.stopping:
		if lg := s.getLogger(); lg != nil {
			lg.Warn("server has stopped; skipping goAttach")
		} else {
			plog.Warning("server has stopped (skipping goAttach)")
		}
		return
	default:
	}

	// now safe to add since waitgroup wait has not started yet
	s.wg.Add(1)
	go func() {
		defer s.wg.Done()
		f()
	}()
}

// goAttach creates a goroutine on a given function and tracks it using
// the etcdserver waitgroup.

goAttach creates a goroutine on a given function and tracks it using the etcdserver waitgroup .


func NewRoundrobinBalanced(
	lg *zap.Logger,
	scs []balancer.SubConn,
	addrToSc map[resolver.Address]balancer.SubConn,
	scToAddr map[balancer.SubConn]resolver.Address,
) Picker {
	return &rrBalanced{
		lg:       lg,
		scs:      scs,
		addrToSc: addrToSc,
		scToAddr: scToAddr,
	}
}

func NewRoundrobinBalanced ( lg * zap . Logger , scs [ ] balancer . SubConn , addrToSc map [ resolver . Address ] balancer . SubConn , scToAddr map [ balancer . SubConn ] resolver . Address , ) Picker { return & rrBalanced { lg : lg , scs : scs , addrToSc : addrToSc , scToAddr : scToAddr , } }

// NewRoundrobinBalanced returns a new roundrobin balanced picker.

NewRoundrobinBalanced returns a new roundrobin balanced picker .


func (rb *rrBalanced) Pick(ctx context.Context, opts balancer.PickOptions) (balancer.SubConn, func(balancer.DoneInfo), error) {
	rb.mu.RLock()
	n := len(rb.scs)
	rb.mu.RUnlock()
	if n == 0 {
		return nil, nil, balancer.ErrNoSubConnAvailable
	}

	rb.mu.Lock()
	cur := rb.next
	sc := rb.scs[cur]
	picked := rb.scToAddr[sc].Addr
	rb.next = (rb.next + 1) % len(rb.scs)
	rb.mu.Unlock()

	rb.lg.Debug(
		"picked",
		zap.String("address", picked),
		zap.Int("subconn-index", cur),
		zap.Int("subconn-size", n),
	)

	doneFunc := func(info balancer.DoneInfo) {
		// TODO: error handling?
		fss := []zapcore.Field{
			zap.Error(info.Err),
			zap.String("address", picked),
			zap.Bool("success", info.Err == nil),
			zap.Bool("bytes-sent", info.BytesSent),
			zap.Bool("bytes-received", info.BytesReceived),
		}
		if info.Err == nil {
			rb.lg.Debug("balancer done", fss...)
		} else {
			rb.lg.Warn("balancer failed", fss...)
		}
	}
	return sc, doneFunc, nil
}

func
(
rb
*
rrBalanced
)
Pick
(
ctx
context
.
Context
,
opts
balancer
.
PickOptions
)
(
balancer
.
SubConn
,
func
(
balancer
.
DoneInfo
)
,
error
)
{
rb
.
mu
.
RLock
(
)
n
:=
len
(
rb
.
scs
)
rb
.
mu
.
RUnlock
(
)
if
n
==
0
{
return
,
,
balancer
.
ErrNoSubConnAvailable
}
rb
.
mu
.
Lock
(
)
cur
:=
rb
.
next
sc
:=
rb
.
scs
[
cur
]
picked
:=
rb
.
scToAddr
[
sc
]
.
Addr
rb
.
next
=
(
rb
.
next
+
1
)
%
len
(
rb
.
scs
)
rb
.
mu
.
Unlock
(
)
rb
.
lg
.
Debug
(
"
"
,
zap
.
String
(
"
"
,
picked
)
,
zap
.
Int
(
"
"
,
cur
)
,
zap
.
Int
(
"
"
,
n
)
,
)
doneFunc
:=
func
(
info
balancer
.
DoneInfo
)
{
// TODO: error handling?
fss
:=
[
]
zapcore
.
Field
{
zap
.
Error
(
info
.
Err
)
,
zap
.
String
(
"
"
,
picked
)
,
zap
.
Bool
(
"
"
,
info
.
Err
==
)
,
zap
.
Bool
(
"
"
,
info
.
BytesSent
)
,
zap
.
Bool
(
"
"
,
info
.
BytesReceived
)
,
}
if
info
.
Err
==
{
rb
.
lg
.
Debug
(
"
"
,
fss
...
)
}
else
{
rb
.
lg
.
Warn
(
"
"
,
fss
...
)
}
}
return
sc
,
doneFunc
,
}

// Pick is called for every client request.

Pick is called for every client request .


func NewTLSListener(l net.Listener, tlsinfo *TLSInfo) (net.Listener, error) {
	check := func(context.Context, *tls.Conn) error { return nil }
	return newTLSListener(l, tlsinfo, check)
}

func NewTLSListener ( l net . Listener , tlsinfo * TLSInfo ) ( net . Listener , error ) { check := func ( context . Context , * tls . Conn ) error { return nil } return newTLSListener ( l , tlsinfo , check ) }

// NewTLSListener handshakes TLS connections and performs optional CRL checking.

NewTLSListener handshakes TLS connections and performs optional CRL checking .


func (l *tlsListener) acceptLoop() {
	var wg sync.WaitGroup
	var pendingMu sync.Mutex

	pending := make(map[net.Conn]struct{})
	ctx, cancel := context.WithCancel(context.Background())
	defer func() {
		cancel()
		pendingMu.Lock()
		for c := range pending {
			c.Close()
		}
		pendingMu.Unlock()
		wg.Wait()
		close(l.donec)
	}()

	for {
		conn, err := l.Listener.Accept()
		if err != nil {
			l.err = err
			return
		}

		pendingMu.Lock()
		pending[conn] = struct{}{}
		pendingMu.Unlock()

		wg.Add(1)
		go func() {
			defer func() {
				if conn != nil {
					conn.Close()
				}
				wg.Done()
			}()

			tlsConn := conn.(*tls.Conn)
			herr := tlsConn.Handshake()
			pendingMu.Lock()
			delete(pending, conn)
			pendingMu.Unlock()

			if herr != nil {
				l.handshakeFailure(tlsConn, herr)
				return
			}
			if err := l.check(ctx, tlsConn); err != nil {
				l.handshakeFailure(tlsConn, err)
				return
			}

			select {
			case l.connc <- tlsConn:
				conn = nil
			case <-ctx.Done():
			}
		}()
	}
}

func ( l * tlsListener ) acceptLoop ( ) { var wg sync . WaitGroup var pendingMu sync . Mutex pending := make ( map [ net . Conn ] struct { } ) ctx , cancel := context . WithCancel ( context . Background ( ) ) defer func ( ) { cancel ( ) pendingMu . Lock ( ) for c := range pending { c . Close ( ) } pendingMu . Unlock ( ) wg . Wait ( ) close ( l . donec ) } ( ) for { conn , err := l . Listener . Accept ( ) if err != nil { l . err = err return } pendingMu . Lock ( ) pending [ conn ] = struct { } { } pendingMu . Unlock ( ) wg . Add ( 1 ) go func ( ) { defer func ( ) { if conn != nil { conn . Close ( ) } wg . Done ( ) } ( ) tlsConn := conn . ( * tls . Conn ) herr := tlsConn . Handshake ( ) pendingMu . Lock ( ) delete ( pending , conn ) pendingMu . Unlock ( ) if herr != nil { l . handshakeFailure ( tlsConn , herr ) return } if err := l . check ( ctx , tlsConn ) ; err != nil { l . handshakeFailure ( tlsConn , err ) return } select { case l . connc <- tlsConn : conn = nil case <- ctx . Done ( ) : } } ( ) } }

// acceptLoop launches each TLS handshake in a separate goroutine
// to prevent a hanging TLS connection from blocking other connections.

acceptLoop launches each TLS handshake in a separate goroutine to prevent a hanging TLS connection from blocking other connections .


func (e *ResolverGroup) SetEndpoints(endpoints []string) {
	addrs := epsToAddrs(endpoints...)
	e.mu.Lock()
	e.endpoints = endpoints
	for _, r := range e.resolvers {
		r.cc.NewAddress(addrs)
	}
	e.mu.Unlock()
}

func ( e * ResolverGroup ) SetEndpoints ( endpoints [ ] string ) { addrs := epsToAddrs ( endpoints ... ) e . mu . Lock ( ) e . endpoints = endpoints for _ , r := range e . resolvers { r . cc . NewAddress ( addrs ) } e . mu . Unlock ( ) }

// SetEndpoints updates the endpoints for ResolverGroup. All registered resolver are updated
// immediately with the new endpoints.

SetEndpoints updates the endpoints for ResolverGroup . All registered resolver are updated immediately with the new endpoints .


func (e *ResolverGroup) Target(endpoint string) string {
	return Target(e.id, endpoint)
}

func ( e * ResolverGroup ) Target ( endpoint string ) string { return Target ( e . id , endpoint ) }

// Target constructs a endpoint target using the endpoint id of the ResolverGroup.

Target constructs a endpoint target using the endpoint id of the ResolverGroup .


func Target(id, endpoint string) string {
	return fmt.Sprintf("%s://%s/%s", scheme, id, endpoint)
}

func Target ( id , endpoint string ) string { return fmt . Sprintf ( " " , scheme , id , endpoint ) }

// Target constructs a endpoint resolver target.

Target constructs a endpoint resolver target .


func (b *builder) Build(target resolver.Target, cc resolver.ClientConn, opts resolver.BuildOption) (resolver.Resolver, error) {
	if len(target.Authority) < 1 {
		return nil, fmt.Errorf("'etcd' target scheme requires non-empty authority identifying etcd cluster being routed to")
	}
	id := target.Authority
	es, err := b.getResolverGroup(id)
	if err != nil {
		return nil, fmt.Errorf("failed to build resolver: %v", err)
	}
	r := &Resolver{
		endpointID: id,
		cc:         cc,
	}
	es.addResolver(r)
	return r, nil
}

func ( b * builder ) Build ( target resolver . Target , cc resolver . ClientConn , opts resolver . BuildOption ) ( resolver . Resolver , error ) { if len ( target . Authority ) < 1 { return nil , fmt . Errorf ( " " ) } id := target . Authority es , err := b . getResolverGroup ( id ) if err != nil { return nil , fmt . Errorf ( " " , err ) } r := & Resolver { endpointID : id , cc : cc , } es . addResolver ( r ) return r , nil }

// Build creates or reuses an etcd resolver for the etcd cluster name identified by the authority part of the target.

Build creates or reuses an etcd resolver for the etcd cluster name identified by the authority part of the target .


func (r *RequestV2) Handle(ctx context.Context, v2api RequestV2Handler) (Response, error) {
	if r.Method == "GET" && r.Quorum {
		r.Method = "QGET"
	}
	switch r.Method {
	case "POST":
		return v2api.Post(ctx, r)
	case "PUT":
		return v2api.Put(ctx, r)
	case "DELETE":
		return v2api.Delete(ctx, r)
	case "QGET":
		return v2api.QGet(ctx, r)
	case "GET":
		return v2api.Get(ctx, r)
	case "HEAD":
		return v2api.Head(ctx, r)
	}
	return Response{}, ErrUnknownMethod
}

func ( r * RequestV2 ) Handle ( ctx context . Context , v2api RequestV2Handler ) ( Response , error ) { if r . Method == " " && r . Quorum { r . Method = " " } switch r . Method { case " " : return v2api . Post ( ctx , r ) case " " : return v2api . Put ( ctx , r ) case " " : return v2api . Delete ( ctx , r ) case " " : return v2api . QGet ( ctx , r ) case " " : return v2api . Get ( ctx , r ) case " " : return v2api . Head ( ctx , r ) } return Response { } , ErrUnknownMethod }

// Handle interprets r and performs an operation on s.store according to r.Method
// and other fields. If r.Method is "POST", "PUT", "DELETE", or a "GET" with
// Quorum == true, r will be sent through consensus before performing its
// respective operation. Do will block until an action is performed or there is
// an error.

Handle interprets r and performs an operation on s . store according to r . Method and other fields . If r . Method is POST PUT DELETE or a GET with Quorum == true r will be sent through consensus before performing its respective operation . Do will block until an action is performed or there is an error .


func NewElectionCommand() *cobra.Command {
	cmd := &cobra.Command{
		Use:   "election [election name (defaults to 'elector')]",
		Short: "Performs election operation",
		Run:   runElectionFunc,
	}
	cmd.Flags().IntVar(&totalClientConnections, "total-client-connections", 10, "total number of client connections")
	return cmd
}

func NewElectionCommand ( ) * cobra . Command { cmd := & cobra . Command { Use : " " , Short : " " , Run : runElectionFunc , } cmd . Flags ( ) . IntVar ( & totalClientConnections , " " , 10 , " " ) return cmd }

// NewElectionCommand returns the cobra command for "election runner".

NewElectionCommand returns the cobra command for election runner .


func nodeToMember(n *v2store.NodeExtern) (*Member, error) {
	m := &Member{ID: MustParseMemberIDFromKey(n.Key)}
	attrs := make(map[string][]byte)
	raftAttrKey := path.Join(n.Key, raftAttributesSuffix)
	attrKey := path.Join(n.Key, attributesSuffix)
	for _, nn := range n.Nodes {
		if nn.Key != raftAttrKey && nn.Key != attrKey {
			return nil, fmt.Errorf("unknown key %q", nn.Key)
		}
		attrs[nn.Key] = []byte(*nn.Value)
	}
	if data := attrs[raftAttrKey]; data != nil {
		if err := json.Unmarshal(data, &m.RaftAttributes); err != nil {
			return nil, fmt.Errorf("unmarshal raftAttributes error: %v", err)
		}
	} else {
		return nil, fmt.Errorf("raftAttributes key doesn't exist")
	}
	if data := attrs[attrKey]; data != nil {
		if err := json.Unmarshal(data, &m.Attributes); err != nil {
			return m, fmt.Errorf("unmarshal attributes error: %v", err)
		}
	}
	return m, nil
}

func nodeToMember ( n * v2store . NodeExtern ) ( * Member , error ) { m := & Member { ID : MustParseMemberIDFromKey ( n . Key ) } attrs := make ( map [ string ] [ ] byte ) raftAttrKey := path . Join ( n . Key , raftAttributesSuffix ) attrKey := path . Join ( n . Key , attributesSuffix ) for _ , nn := range n . Nodes { if nn . Key != raftAttrKey && nn . Key != attrKey { return nil , fmt . Errorf ( " " , nn . Key ) } attrs [ nn . Key ] = [ ] byte ( * nn . Value ) } if data := attrs [ raftAttrKey ] ; data != nil { if err := json . Unmarshal ( data , & m . RaftAttributes ) ; err != nil { return nil , fmt . Errorf ( " " , err ) } } else { return nil , fmt . Errorf ( " " ) } if data := attrs [ attrKey ] ; data != nil { if err := json . Unmarshal ( data , & m . Attributes ) ; err != nil { return m , fmt . Errorf ( " " , err ) } } return m , nil }

// nodeToMember builds member from a key value node.
// the child nodes of the given node MUST be sorted by key.

nodeToMember builds member from a key value node . the child nodes of the given node MUST be sorted by key .


func NewTmpBackend(batchInterval time.Duration, batchLimit int) (*backend, string) {
	dir, err := ioutil.TempDir(os.TempDir(), "etcd_backend_test")
	if err != nil {
		panic(err)
	}
	tmpPath := filepath.Join(dir, "database")
	bcfg := DefaultBackendConfig()
	bcfg.Path, bcfg.BatchInterval, bcfg.BatchLimit = tmpPath, batchInterval, batchLimit
	return newBackend(bcfg), tmpPath
}

func NewTmpBackend ( batchInterval time . Duration , batchLimit int ) ( * backend , string ) { dir , err := ioutil . TempDir ( os . TempDir ( ) , " " ) if err != nil { panic ( err ) } tmpPath := filepath . Join ( dir , " " ) bcfg := DefaultBackendConfig ( ) bcfg . Path , bcfg . BatchInterval , bcfg . BatchLimit = tmpPath , batchInterval , batchLimit return newBackend ( bcfg ) , tmpPath }

// NewTmpBackend creates a backend implementation for testing.

NewTmpBackend creates a backend implementation for testing .


func newRevision(lg *zap.Logger, clock clockwork.Clock, retention int64, rg RevGetter, c Compactable) *Revision {
	rc := &Revision{
		lg:        lg,
		clock:     clock,
		retention: retention,
		rg:        rg,
		c:         c,
	}
	rc.ctx, rc.cancel = context.WithCancel(context.Background())
	return rc
}

func newRevision ( lg * zap . Logger , clock clockwork . Clock , retention int64 , rg RevGetter , c Compactable ) * Revision { rc := & Revision { lg : lg , clock : clock , retention : retention , rg : rg , c : c , } rc . ctx , rc . cancel = context . WithCancel ( context . Background ( ) ) return rc }

// newRevision creates a new instance of Revisonal compactor that purges
// the log older than retention revisions from the current revision.

newRevision creates a new instance of Revisonal compactor that purges the log older than retention revisions from the current revision .


func (rc *Revision) Run() {
	prev := int64(0)
	go func() {
		for {
			select {
			case <-rc.ctx.Done():
				return
			case <-rc.clock.After(revInterval):
				rc.mu.Lock()
				p := rc.paused
				rc.mu.Unlock()
				if p {
					continue
				}
			}

			rev := rc.rg.Rev() - rc.retention
			if rev <= 0 || rev == prev {
				continue
			}

			now := time.Now()
			if rc.lg != nil {
				rc.lg.Info(
					"starting auto revision compaction",
					zap.Int64("revision", rev),
					zap.Int64("revision-compaction-retention", rc.retention),
				)
			} else {
				plog.Noticef("Starting auto-compaction at revision %d (retention: %d revisions)", rev, rc.retention)
			}
			_, err := rc.c.Compact(rc.ctx, &pb.CompactionRequest{Revision: rev})
			if err == nil || err == mvcc.ErrCompacted {
				prev = rev
				if rc.lg != nil {
					rc.lg.Info(
						"completed auto revision compaction",
						zap.Int64("revision", rev),
						zap.Int64("revision-compaction-retention", rc.retention),
						zap.Duration("took", time.Since(now)),
					)
				} else {
					plog.Noticef("Finished auto-compaction at revision %d", rev)
				}
			} else {
				if rc.lg != nil {
					rc.lg.Warn(
						"failed auto revision compaction",
						zap.Int64("revision", rev),
						zap.Int64("revision-compaction-retention", rc.retention),
						zap.Duration("retry-interval", revInterval),
						zap.Error(err),
					)
				} else {
					plog.Noticef("Failed auto-compaction at revision %d (%v)", rev, err)
					plog.Noticef("Retry after %v", revInterval)
				}
			}
		}
	}()
}

func ( rc * Revision ) Run ( ) { prev := int64 ( 0 ) go func ( ) { for { select { case <- rc . ctx . Done ( ) : return case <- rc . clock . After ( revInterval ) : rc . mu . Lock ( ) p := rc . paused rc . mu . Unlock ( ) if p { continue } } rev := rc . rg . Rev ( ) - rc . retention if rev <= 0 || rev == prev { continue } now := time . Now ( ) if rc . lg != nil { rc . lg . Info ( " " , zap . Int64 ( " " , rev ) , zap . Int64 ( " " , rc . retention ) , ) } else { plog . Noticef ( " " , rev , rc . retention ) } _ , err := rc . c . Compact ( rc . ctx , & pb . CompactionRequest { Revision : rev } ) if err == nil || err == mvcc . ErrCompacted { prev = rev if rc . lg != nil { rc . lg . Info ( " " , zap . Int64 ( " " , rev ) , zap . Int64 ( " " , rc . retention ) , zap . Duration ( " " , time . Since ( now ) ) , ) } else { plog . Noticef ( " " , rev ) } } else { if rc . lg != nil { rc . lg . Warn ( " " , zap . Int64 ( " " , rev ) , zap . Int64 ( " " , rc . retention ) , zap . Duration ( " " , revInterval ) , zap . Error ( err ) , ) } else { plog . Noticef ( " " , rev , err ) plog . Noticef ( " " , revInterval ) } } } } ( ) }


func (rc *Revision) Pause() {
	rc.mu.Lock()
	rc.paused = true
	rc.mu.Unlock()
}

func ( rc * Revision ) Pause ( ) { rc . mu . Lock ( ) rc . paused = true rc . mu . Unlock ( ) }

// Pause pauses revision-based compactor.

Pause pauses revision - based compactor .


func (rc *Revision) Resume() {
	rc.mu.Lock()
	rc.paused = false
	rc.mu.Unlock()
}

func ( rc * Revision ) Resume ( ) { rc . mu . Lock ( ) rc . paused = false rc . mu . Unlock ( ) }

// Resume resumes revision-based compactor.

Resume resumes revision - based compactor .


func voteRespMsgType(msgt pb.MessageType) pb.MessageType {
	switch msgt {
	case pb.MsgVote:
		return pb.MsgVoteResp
	case pb.MsgPreVote:
		return pb.MsgPreVoteResp
	default:
		panic(fmt.Sprintf("not a vote message: %s", msgt))
	}
}

func voteRespMsgType ( msgt pb . MessageType ) pb . MessageType { switch msgt { case pb . MsgVote : return pb . MsgVoteResp case pb . MsgPreVote : return pb . MsgPreVoteResp default : panic ( fmt . Sprintf ( " " , msgt ) ) } }

// voteResponseType maps vote and prevote message types to their corresponding responses.

voteResponseType maps vote and prevote message types to their corresponding responses .


func DescribeMessage(m pb.Message, f EntryFormatter) string {
	var buf bytes.Buffer
	fmt.Fprintf(&buf, "%x->%x %v Term:%d Log:%d/%d", m.From, m.To, m.Type, m.Term, m.LogTerm, m.Index)
	if m.Reject {
		fmt.Fprintf(&buf, " Rejected (Hint: %d)", m.RejectHint)
	}
	if m.Commit != 0 {
		fmt.Fprintf(&buf, " Commit:%d", m.Commit)
	}
	if len(m.Entries) > 0 {
		fmt.Fprintf(&buf, " Entries:[")
		for i, e := range m.Entries {
			if i != 0 {
				buf.WriteString(", ")
			}
			buf.WriteString(DescribeEntry(e, f))
		}
		fmt.Fprintf(&buf, "]")
	}
	if !IsEmptySnap(m.Snapshot) {
		fmt.Fprintf(&buf, " Snapshot:%v", m.Snapshot)
	}
	return buf.String()
}

func DescribeMessage ( m pb . Message , f EntryFormatter ) string { var buf bytes . Buffer fmt . Fprintf ( & buf , " " , m . From , m . To , m . Type , m . Term , m . LogTerm , m . Index ) if m . Reject { fmt . Fprintf ( & buf , " " , m . RejectHint ) } if m . Commit != 0 { fmt . Fprintf ( & buf , " " , m . Commit ) } if len ( m . Entries ) > 0 { fmt . Fprintf ( & buf , " " ) for i , e := range m . Entries { if i != 0 { buf . WriteString ( " " ) } buf . WriteString ( DescribeEntry ( e , f ) ) } fmt . Fprintf ( & buf , " " ) } if ! IsEmptySnap ( m . Snapshot ) { fmt . Fprintf ( & buf , " " , m . Snapshot ) } return buf . String ( ) }

// DescribeMessage returns a concise human-readable description of a
// Message for debugging.

DescribeMessage returns a concise human - readable description of a Message for debugging .


func DescribeEntry(e pb.Entry, f EntryFormatter) string {
	var formatted string
	if e.Type == pb.EntryNormal && f != nil {
		formatted = f(e.Data)
	} else {
		formatted = fmt.Sprintf("%q", e.Data)
	}
	return fmt.Sprintf("%d/%d %s %s", e.Term, e.Index, e.Type, formatted)
}

func DescribeEntry ( e pb . Entry , f EntryFormatter ) string { var formatted string if e . Type == pb . EntryNormal && f != nil { formatted = f ( e . Data ) } else { formatted = fmt . Sprintf ( " " , e . Data ) } return fmt . Sprintf ( " " , e . Term , e . Index , e . Type , formatted ) }

// DescribeEntry returns a concise human-readable description of an
// Entry for debugging.

DescribeEntry returns a concise human - readable description of an Entry for debugging .


func DescribeEntries(ents []pb.Entry, f EntryFormatter) string {
	var buf bytes.Buffer
	for _, e := range ents {
		_, _ = buf.WriteString(DescribeEntry(e, f) + "\n")
	}
	return buf.String()
}

func DescribeEntries ( ents [ ] pb . Entry , f EntryFormatter ) string { var buf bytes . Buffer for _ , e := range ents { _ , _ = buf . WriteString ( DescribeEntry ( e , f ) + " \n " ) } return buf . String ( ) }

// DescribeEntries calls DescribeEntry for each Entry, adding a newline to
// each.

DescribeEntries calls DescribeEntry for each Entry adding a newline to each .


func SetLogger(l grpclog.LoggerV2) {
	lgMu.Lock()
	lg = logutil.NewLogger(l)
	// override grpclog so that any changes happen with locking
	grpclog.SetLoggerV2(lg)
	lgMu.Unlock()
}


func GetLogger() logutil.Logger {
	lgMu.RLock()
	l := lg
	lgMu.RUnlock()
	return l
}

func GetLogger ( ) logutil . Logger { lgMu . RLock ( ) l := lg lgMu . RUnlock ( ) return l }

// GetLogger returns the current logutil.Logger.

GetLogger returns the current logutil . Logger .


func (u *unstable) maybeFirstIndex() (uint64, bool) {
	if u.snapshot != nil {
		return u.snapshot.Metadata.Index + 1, true
	}
	return 0, false
}

func ( u * unstable ) maybeFirstIndex ( ) ( uint64 , bool ) { if u . snapshot != nil { return u . snapshot . Metadata . Index + 1 , true } return 0 , false }

// maybeFirstIndex returns the index of the first possible entry in entries
// if it has a snapshot.

maybeFirstIndex returns the index of the first possible entry in entries if it has a snapshot .


func (u *unstable) maybeLastIndex() (uint64, bool) {
	if l := len(u.entries); l != 0 {
		return u.offset + uint64(l) - 1, true
	}
	if u.snapshot != nil {
		return u.snapshot.Metadata.Index, true
	}
	return 0, false
}

func ( u * unstable ) maybeLastIndex ( ) ( uint64 , bool ) { if l := len ( u . entries ) ; l != 0 { return u . offset + uint64 ( l ) - 1 , true } if u . snapshot != nil { return u . snapshot . Metadata . Index , true } return 0 , false }

// maybeLastIndex returns the last index if it has at least one
// unstable entry or snapshot.

maybeLastIndex returns the last index if it has at least one unstable entry or snapshot .


func (u *unstable) maybeTerm(i uint64) (uint64, bool) {
	if i < u.offset {
		if u.snapshot == nil {
			return 0, false
		}
		if u.snapshot.Metadata.Index == i {
			return u.snapshot.Metadata.Term, true
		}
		return 0, false
	}

	last, ok := u.maybeLastIndex()
	if !ok {
		return 0, false
	}
	if i > last {
		return 0, false
	}
	return u.entries[i-u.offset].Term, true
}

func ( u * unstable ) maybeTerm ( i uint64 ) ( uint64 , bool ) { if i < u . offset { if u . snapshot == nil { return 0 , false } if u . snapshot . Metadata . Index == i { return u . snapshot . Metadata . Term , true } return 0 , false } last , ok := u . maybeLastIndex ( ) if ! ok { return 0 , false } if i > last { return 0 , false } return u . entries [ i - u . offset ] . Term , true }

// maybeTerm returns the term of the entry at index i, if there
// is any.

maybeTerm returns the term of the entry at index i if there is any .


func (u *unstable) shrinkEntriesArray() {
	// We replace the array if we're using less than half of the space in
	// it. This number is fairly arbitrary, chosen as an attempt to balance
	// memory usage vs number of allocations. It could probably be improved
	// with some focused tuning.
	const lenMultiple = 2
	if len(u.entries) == 0 {
		u.entries = nil
	} else if len(u.entries)*lenMultiple < cap(u.entries) {
		newEntries := make([]pb.Entry, len(u.entries))
		copy(newEntries, u.entries)
		u.entries = newEntries
	}
}

// shrinkEntriesArray discards the underlying array used by the entries slice
// if most of it isn't being used. This avoids holding references to a bunch of
// potentially large entries that aren't needed anymore. Simply clearing the
// entries wouldn't be safe because clients might still be using them.

shrinkEntriesArray discards the underlying array used by the entries slice if most of it isn t being used . This avoids holding references to a bunch of potentially large entries that aren t needed anymore . Simply clearing the entries wouldn t be safe because clients might still be using them .


func (st *storage) SaveSnap(snap raftpb.Snapshot) error {
	walsnap := walpb.Snapshot{
		Index: snap.Metadata.Index,
		Term:  snap.Metadata.Term,
	}
	err := st.WAL.SaveSnapshot(walsnap)
	if err != nil {
		return err
	}
	err = st.Snapshotter.SaveSnap(snap)
	if err != nil {
		return err
	}
	return st.WAL.ReleaseLockTo(snap.Metadata.Index)
}

func ( st * storage ) SaveSnap ( snap raftpb . Snapshot ) error { walsnap := walpb . Snapshot { Index : snap . Metadata . Index , Term : snap . Metadata . Term , } err := st . WAL . SaveSnapshot ( walsnap ) if err != nil { return err } err = st . Snapshotter . SaveSnap ( snap ) if err != nil { return err } return st . WAL . ReleaseLockTo ( snap . Metadata . Index ) }

// SaveSnap saves the snapshot to disk and release the locked
// wal files since they will not be used.

SaveSnap saves the snapshot to disk and release the locked wal files since they will not be used .


func New(cfg Config) (*Client, error) {
	if len(cfg.Endpoints) == 0 {
		return nil, ErrNoAvailableEndpoints
	}

	return newClient(&cfg)
}

func New ( cfg Config ) ( * Client , error ) { if len ( cfg . Endpoints ) == 0 { return nil , ErrNoAvailableEndpoints } return newClient ( & cfg ) }

// New creates a new etcdv3 client from a given configuration.

New creates a new etcdv3 client from a given configuration .


func NewCtxClient(ctx context.Context) *Client {
	cctx, cancel := context.WithCancel(ctx)
	return &Client{ctx: cctx, cancel: cancel}
}

func NewCtxClient ( ctx context . Context ) * Client { cctx , cancel := context . WithCancel ( ctx ) return & Client { ctx : cctx , cancel : cancel } }

// NewCtxClient creates a client with a context but no underlying grpc
// connection. This is useful for embedded cases that override the
// service interface implementations and do not need connection management.

NewCtxClient creates a client with a context but no underlying grpc connection . This is useful for embedded cases that override the service interface implementations and do not need connection management .


func NewFromURL(url string) (*Client, error) {
	return New(Config{Endpoints: []string{url}})
}

func NewFromURL ( url string ) ( * Client , error ) { return New ( Config { Endpoints : [ ] string { url } } ) }

// NewFromURL creates a new etcdv3 client from a URL.

NewFromURL creates a new etcdv3 client from a URL .


func (c *Client) Close() error {
	c.cancel()
	c.Watcher.Close()
	c.Lease.Close()
	if c.resolverGroup != nil {
		c.resolverGroup.Close()
	}
	if c.conn != nil {
		return toErr(c.ctx, c.conn.Close())
	}
	return c.ctx.Err()
}

func ( c * Client ) Close ( ) error { c . cancel ( ) c . Watcher . Close ( ) c . Lease . Close ( ) if c . resolverGroup != nil { c . resolverGroup . Close ( ) } if c . conn != nil { return toErr ( c . ctx , c . conn . Close ( ) ) } return c . ctx . Err ( ) }

// Close shuts down the client's etcd connections.

Close shuts down the client s etcd connections .


func (c *Client) Endpoints() []string {
	// copy the slice; protect original endpoints from being changed
	c.mu.RLock()
	defer c.mu.RUnlock()
	eps := make([]string, len(c.cfg.Endpoints))
	copy(eps, c.cfg.Endpoints)
	return eps
}

// Endpoints lists the registered endpoints for the client.

Endpoints lists the registered endpoints for the client .


func (c *Client) SetEndpoints(eps ...string) {
	c.mu.Lock()
	defer c.mu.Unlock()
	c.cfg.Endpoints = eps
	c.resolverGroup.SetEndpoints(eps)
}

func ( c * Client ) SetEndpoints ( eps ... string ) { c . mu . Lock ( ) defer c . mu . Unlock ( ) c . cfg . Endpoints = eps c . resolverGroup . SetEndpoints ( eps ) }

// SetEndpoints updates client's endpoints.

SetEndpoints updates client s endpoints .


func (c *Client) Sync(ctx context.Context) error {
	mresp, err := c.MemberList(ctx)
	if err != nil {
		return err
	}
	var eps []string
	for _, m := range mresp.Members {
		eps = append(eps, m.ClientURLs...)
	}
	c.SetEndpoints(eps...)
	return nil
}

func ( c * Client ) Sync ( ctx context . Context ) error { mresp , err := c . MemberList ( ctx ) if err != nil { return err } var eps [ ] string for _ , m := range mresp . Members { eps = append ( eps , m . ClientURLs ... ) } c . SetEndpoints ( eps ... ) return nil }

// Sync synchronizes client's endpoints with the known endpoints from the etcd membership.

Sync synchronizes client s endpoints with the known endpoints from the etcd membership .


func (c *Client) dialSetupOpts(creds *credentials.TransportCredentials, dopts ...grpc.DialOption) (opts []grpc.DialOption, err error) {
	if c.cfg.DialKeepAliveTime > 0 {
		params := keepalive.ClientParameters{
			Time:                c.cfg.DialKeepAliveTime,
			Timeout:             c.cfg.DialKeepAliveTimeout,
			PermitWithoutStream: c.cfg.PermitWithoutStream,
		}
		opts = append(opts, grpc.WithKeepaliveParams(params))
	}
	opts = append(opts, dopts...)

	// Provide a net dialer that supports cancelation and timeout.
	f := func(dialEp string, t time.Duration) (net.Conn, error) {
		proto, host, _ := endpoint.ParseEndpoint(dialEp)
		select {
		case <-c.ctx.Done():
			return nil, c.ctx.Err()
		default:
		}
		dialer := &net.Dialer{Timeout: t}
		return dialer.DialContext(c.ctx, proto, host)
	}
	opts = append(opts, grpc.WithDialer(f))

	if creds != nil {
		opts = append(opts, grpc.WithTransportCredentials(*creds))
	} else {
		opts = append(opts, grpc.WithInsecure())
	}

	// Interceptor retry and backoff.
	// TODO: Replace all of clientv3/retry.go with interceptor based retry, or with
	// https://github.com/grpc/proposal/blob/master/A6-client-retries.md#retry-policy
	// once it is available.
	rrBackoff := withBackoff(c.roundRobinQuorumBackoff(defaultBackoffWaitBetween, defaultBackoffJitterFraction))
	opts = append(opts,
		// Disable stream retry by default since go-grpc-middleware/retry does not support client streams.
		// Streams that are safe to retry are enabled individually.
		grpc.WithStreamInterceptor(c.streamClientInterceptor(c.lg, withMax(0), rrBackoff)),
		grpc.WithUnaryInterceptor(c.unaryClientInterceptor(c.lg, withMax(defaultUnaryMaxRetries), rrBackoff)),
	)

	return opts, nil
}

// dialSetupOpts gives the dial opts prior to any authentication.

dialSetupOpts gives the dial opts prior to any authentication .


func (c *Client) Dial(ep string) (*grpc.ClientConn, error) {
	creds := c.directDialCreds(ep)
	// Use the grpc passthrough resolver to directly dial a single endpoint.
	// This resolver passes through the 'unix' and 'unixs' endpoints schemes used
	// by etcd without modification, allowing us to directly dial endpoints and
	// using the same dial functions that we use for load balancer dialing.
	return c.dial(fmt.Sprintf("passthrough:///%s", ep), creds)
}

// Dial connects to a single endpoint using the client's config.

Dial connects to a single endpoint using the client s config .


func (c *Client) dialWithBalancer(ep string, dopts ...grpc.DialOption) (*grpc.ClientConn, error) {
	_, host, _ := endpoint.ParseEndpoint(ep)
	target := c.resolverGroup.Target(host)
	creds := c.dialWithBalancerCreds(ep)
	return c.dial(target, creds, dopts...)
}

func ( c * Client ) dialWithBalancer ( ep string , dopts ... grpc . DialOption ) ( * grpc . ClientConn , error ) { _ , host , _ := endpoint . ParseEndpoint ( ep ) target := c . resolverGroup . Target ( host ) creds := c . dialWithBalancerCreds ( ep ) return c . dial ( target , creds , dopts ... ) }

// dialWithBalancer dials the client's current load balanced resolver group.  The scheme of the host
// of the provided endpoint determines the scheme used for all endpoints of the client connection.

dialWithBalancer dials the client s current load balanced resolver group . The scheme of the host of the provided endpoint determines the scheme used for all endpoints of the client connection .


func (c *Client) dial(target string, creds *credentials.TransportCredentials, dopts ...grpc.DialOption) (*grpc.ClientConn, error) {
	opts, err := c.dialSetupOpts(creds, dopts...)
	if err != nil {
		return nil, fmt.Errorf("failed to configure dialer: %v", err)
	}

	if c.Username != "" && c.Password != "" {
		c.tokenCred = &authTokenCredential{
			tokenMu: &sync.RWMutex{},
		}

		ctx, cancel := c.ctx, func() {}
		if c.cfg.DialTimeout > 0 {
			ctx, cancel = context.WithTimeout(ctx, c.cfg.DialTimeout)
		}

		err = c.getToken(ctx)
		if err != nil {
			if toErr(ctx, err) != rpctypes.ErrAuthNotEnabled {
				if err == ctx.Err() && ctx.Err() != c.ctx.Err() {
					err = context.DeadlineExceeded
				}
				cancel()
				return nil, err
			}
		} else {
			opts = append(opts, grpc.WithPerRPCCredentials(c.tokenCred))
		}
		cancel()
	}

	opts = append(opts, c.cfg.DialOptions...)

	dctx := c.ctx
	if c.cfg.DialTimeout > 0 {
		var cancel context.CancelFunc
		dctx, cancel = context.WithTimeout(c.ctx, c.cfg.DialTimeout)
		defer cancel() // TODO: Is this right for cases where grpc.WithBlock() is not set on the dial options?
	}

	conn, err := grpc.DialContext(dctx, target, opts...)
	if err != nil {
		return nil, err
	}
	return conn, nil
}

// dial configures and dials any grpc balancer target.

dial configures and dials any grpc balancer target .


func WithRequireLeader(ctx context.Context) context.Context {
	md := metadata.Pairs(rpctypes.MetadataRequireLeaderKey, rpctypes.MetadataHasLeader)
	return metadata.NewOutgoingContext(ctx, md)
}

func WithRequireLeader ( ctx context . Context ) context . Context { md := metadata . Pairs ( rpctypes . MetadataRequireLeaderKey , rpctypes . MetadataHasLeader ) return metadata . NewOutgoingContext ( ctx , md ) }

// WithRequireLeader requires client requests to only succeed
// when the cluster has a leader.

WithRequireLeader requires client requests to only succeed when the cluster has a leader .


func (c *Client) roundRobinQuorumBackoff(waitBetween time.Duration, jitterFraction float64) backoffFunc {
	return func(attempt uint) time.Duration {
		// after each round robin across quorum, backoff for our wait between duration
		n := uint(len(c.Endpoints()))
		quorum := (n/2 + 1)
		if attempt%quorum == 0 {
			c.lg.Debug("backoff", zap.Uint("attempt", attempt), zap.Uint("quorum", quorum), zap.Duration("waitBetween", waitBetween), zap.Float64("jitterFraction", jitterFraction))
			return jitterUp(waitBetween, jitterFraction)
		}
		c.lg.Debug("backoff skipped", zap.Uint("attempt", attempt), zap.Uint("quorum", quorum))
		return 0
	}
}

// roundRobinQuorumBackoff retries against quorum between each backoff.
// This is intended for use with a round robin load balancer.

roundRobinQuorumBackoff retries against quorum between each backoff . This is intended for use with a round robin load balancer .