Repository: kubernetes/kubernetes
File: cmd/kubeadm/app/util/etcd/etcd.go (Go)

Code before:
			return false, nil
		})
	if err != nil {
		return lastError
	}
	klog.V(1).Infof("etcd endpoints read from etcd: %s", strings.Join(cli.Endpoints(), ","))

	c.Endpoints = cli.Endpoints()
	return nil
}

// Member struct defines an etcd member; it is used for avoiding to spread github.com/coreos/etcd dependency
// across kubeadm codebase
type Member struct {
	Name    string
	PeerURL string
}

func (c *Client) listMembers(timeout time.Duration) (*clientv3.MemberListResponse, error) {
	// Gets the member list
	var lastError error
	var resp *clientv3.MemberListResponse
	if timeout == 0 {
		timeout = kubeadmapi.GetActiveTimeouts().EtcdAPICall.Duration
	}
	err := wait.PollUntilContextTimeout(context.Background(), constants.EtcdAPICallRetryInterval, timeout,
		true, func(_ context.Context) (bool, error) {
			cli, err := c.newEtcdClient(c.Endpoints)
			if err != nil {
				lastError = err
				return false, nil
			}
			defer func() { _ = cli.Close() }()

			ctx, cancel := context.WithTimeout(context.Background(), etcdTimeout)
			resp, err = cli.MemberList(ctx)
			cancel()
			if err == nil {
				return true, nil
			}
			klog.V(5).Infof("Failed to get etcd member list: %v", err)
			lastError = err
			return false, nil
		})
	if err != nil {
		return nil, lastError
	}
	return resp, nil
}

// GetMemberID returns the member ID of the given peer URL

Review comment (suggestion):
Apologies, I just got off work....
There is still an issue here: we added a 2-second timeout for the request `cli.MemberList(ctx)` in the `listMembersOnce` method. However, we are still calling `c.getMemberStatus(learnerID)` -> `c.listMembersOnce()` on a 500ms cycle. ~This may cause requests to etcd to be blocked under certain network anomalies, and all responses could be returned together once the network recovers... I think this does not align with our expectations.~

Code after:
			return false, nil
		})
	if err != nil {
		return lastError
	}
	klog.V(1).Infof("etcd endpoints read from etcd: %s", strings.Join(cli.Endpoints(), ","))

	c.Endpoints = cli.Endpoints()
	return nil
}

// Member struct defines an etcd member; it is used for avoiding to spread github.com/coreos/etcd dependency
// across kubeadm codebase
type Member struct {
	Name    string
	PeerURL string
}

func (c *Client) listMembersOnce() (*clientv3.MemberListResponse, error) {
	cli, err := c.newEtcdClient(c.Endpoints)
	if err != nil {
		return nil, err
	}
	defer func() { _ = cli.Close() }()

	ctx, cancel := context.WithTimeout(context.Background(), etcdTimeout)
	resp, err := cli.MemberList(ctx)
	cancel()
	if err == nil {
		return resp, nil
	}
	klog.V(5).Infof("Failed to get etcd member list: %v", err)
	return nil, err
}

func (c *Client) listMembers(timeout time.Duration) (*clientv3.MemberListResponse, error) {
	// Gets the member list
	var (
		err       error
		lastError error
		resp      *clientv3.MemberListResponse
	)

	if timeout == 0 {
		timeout = kubeadmapi.GetActiveTimeouts().EtcdAPICall.Duration
	}
	err = wait.PollUntilContextTimeout(context.Background(), constants.EtcdAPICallRetryInterval, timeout,
		true, func(_ context.Context) (bool, error) {
			resp, err = c.listMembersOnce()
			if err != nil {
				lastError = err