Skip to content

Commit

Permalink
Merge pull request #27 from jingyih/clientv3_balancer_support_learner
Browse files Browse the repository at this point in the history
etcdserver: learner return code Unavailable for unsupported request types
  • Loading branch information
jingyih committed Apr 26, 2019
2 parents ebed520 + c489dee commit 636bf1c
Show file tree
Hide file tree
Showing 4 changed files with 52 additions and 2 deletions.
46 changes: 46 additions & 0 deletions clientv3/integration/kv_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1050,3 +1050,49 @@ func TestKVForLearner(t *testing.T) {
}
}
}

// TestBalancerSupportLearner verifies that balancer's retry and failover mechanism supports cluster with learner member
func TestBalancerSupportLearner(t *testing.T) {
defer testutil.AfterTest(t)

clus := integration.NewClusterV3(t, &integration.ClusterConfig{Size: 3})
defer clus.Terminate(t)

// we have to add and launch learner member after initial cluster was created, because
// bootstrapping a cluster with learner member is not supported.
clus.AddAndLaunchLearnerMember(t)

learners, err := clus.GetLearnerMembers()
if err != nil {
t.Fatalf("failed to get the learner members in cluster: %v", err)
}
if len(learners) != 1 {
t.Fatalf("added 1 learner to cluster, got %d", len(learners))
}

// clus.Members[3] is the newly added learner member, which was appended to clus.Members
learnerEp := clus.Members[3].GRPCAddr()
cfg := clientv3.Config{
Endpoints: []string{learnerEp},
DialTimeout: 5 * time.Second,
DialOptions: []grpc.DialOption{grpc.WithBlock()},
}
cli, err := clientv3.New(cfg)
if err != nil {
t.Fatalf("failed to create clientv3: %v", err)
}
defer cli.Close()

// wait until learner member is ready
<-clus.Members[3].ReadyNotify()

if _, err := cli.Get(context.Background(), "foo"); err == nil {
t.Fatalf("expect Get request to learner to fail, got no error")
}

eps := []string{learnerEp, clus.Members[0].GRPCAddr()}
cli.SetEndpoints(eps...)
if _, err := cli.Get(context.Background(), "foo"); err != nil {
t.Errorf("expect no error (balancer should retry when request to learner fails), got error: %v", err)
}
}
1 change: 0 additions & 1 deletion etcdserver/api/v3rpc/interceptor.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,6 @@ func newUnaryInterceptor(s *etcdserver.EtcdServer) grpc.UnaryServerInterceptor {
return nil, rpctypes.ErrGRPCNotCapable
}

// TODO: add test in clientv3/integration to verify behavior
if s.IsLearner() && !isRPCEnabledForLearner(req) {
return nil, rpctypes.ErrGPRCNotSupportedForLearner
}
Expand Down
3 changes: 2 additions & 1 deletion etcdserver/api/v3rpc/rpctypes/error.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ var (
ErrGRPCTimeoutDueToConnectionLost = status.New(codes.Unavailable, "etcdserver: request timed out, possibly due to connection lost").Err()
ErrGRPCUnhealthy = status.New(codes.Unavailable, "etcdserver: unhealthy cluster").Err()
ErrGRPCCorrupt = status.New(codes.DataLoss, "etcdserver: corrupt cluster").Err()
ErrGPRCNotSupportedForLearner = status.New(codes.FailedPrecondition, "etcdserver: rpc not supported for learner").Err()
ErrGPRCNotSupportedForLearner = status.New(codes.Unavailable, "etcdserver: rpc not supported for learner").Err()
ErrGRPCBadLeaderTransferee = status.New(codes.FailedPrecondition, "etcdserver: bad leader transferee").Err()

errStringToError = map[string]error{
Expand Down Expand Up @@ -126,6 +126,7 @@ var (
ErrorDesc(ErrGRPCTimeoutDueToConnectionLost): ErrGRPCTimeoutDueToConnectionLost,
ErrorDesc(ErrGRPCUnhealthy): ErrGRPCUnhealthy,
ErrorDesc(ErrGRPCCorrupt): ErrGRPCCorrupt,
ErrorDesc(ErrGPRCNotSupportedForLearner): ErrGPRCNotSupportedForLearner,
ErrorDesc(ErrGRPCBadLeaderTransferee): ErrGRPCBadLeaderTransferee,
}
)
Expand Down
4 changes: 4 additions & 0 deletions integration/cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -1166,6 +1166,10 @@ func (m *member) RecoverPartition(t testing.TB, others ...*member) {
}
}

func (m *member) ReadyNotify() <-chan struct{} {
return m.s.ReadyNotify()
}

func MustNewHTTPClient(t testing.TB, eps []string, tls *transport.TLSInfo) client.Client {
cfgtls := transport.TLSInfo{}
if tls != nil {
Expand Down

0 comments on commit 636bf1c

Please sign in to comment.