Skip to content

Commit 989c556

Browse files
authored
etcdserver: add learner check to readyz
Signed-off-by: GitHub <[email protected]>
1 parent b531071 commit 989c556

File tree

2 files changed

+59
-0
lines changed

2 files changed

+59
-0
lines changed

server/etcdserver/api/etcdhttp/health.go

+12
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ type ServerHealth interface {
5252
Range(context.Context, *pb.RangeRequest) (*pb.RangeResponse, error)
5353
Config() config.ServerConfig
5454
AuthStore() auth.AuthStore
55+
IsLearner() bool
5556
}
5657

5758
// HandleHealth registers metrics and health handlers. it checks health by using v3 range request
@@ -252,6 +253,8 @@ func installReadyzEndpoints(lg *zap.Logger, mux *http.ServeMux, server ServerHea
252253
reg.Register("serializable_read", readCheck(server, true))
253254
// linearizable_read check would be replaced by read_index check in 3.6
254255
reg.Register("linearizable_read", readCheck(server, false))
256+
// check if local is learner
257+
reg.Register("non_learner", learnerCheck(server))
255258
reg.InstallHTTPEndpoints(lg, mux)
256259
}
257260

@@ -431,3 +434,12 @@ func readCheck(srv ServerHealth, serializable bool) func(ctx context.Context) er
431434
return err
432435
}
433436
}
437+
438+
func learnerCheck(srv ServerHealth) func(ctx context.Context) error {
439+
return func(ctx context.Context) error {
440+
if srv.IsLearner() {
441+
return fmt.Errorf("not supported for learner")
442+
}
443+
return nil
444+
}
445+
}

server/etcdserver/api/etcdhttp/health_test.go

+47
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ type fakeHealthServer struct {
4343
linearizableReadError error
4444
missingLeader bool
4545
authStore auth.AuthStore
46+
isLearner bool
4647
}
4748

4849
func (s *fakeHealthServer) Range(_ context.Context, req *pb.RangeRequest) (*pb.RangeResponse, error) {
@@ -52,6 +53,10 @@ func (s *fakeHealthServer) Range(_ context.Context, req *pb.RangeRequest) (*pb.R
5253
return nil, s.linearizableReadError
5354
}
5455

56+
func (s *fakeHealthServer) IsLearner() bool {
57+
return s.isLearner
58+
}
59+
5560
func (s *fakeHealthServer) Config() config.ServerConfig {
5661
return config.ServerConfig{}
5762
}
@@ -77,6 +82,7 @@ type healthTestCase struct {
7782
alarms []*pb.AlarmMember
7883
apiError error
7984
missingLeader bool
85+
isLearner bool
8086
}
8187

8288
func TestHealthHandler(t *testing.T) {
@@ -181,6 +187,11 @@ func TestHTTPSubPath(t *testing.T) {
181187
expectStatusCode: http.StatusServiceUnavailable,
182188
notInResult: []string{"data_corruption"},
183189
},
190+
{
191+
name: "/readyz/learner ok",
192+
healthCheckURL: "/readyz/non_learner",
193+
expectStatusCode: http.StatusOK,
194+
},
184195
{
185196
name: "/readyz/non_exist 404",
186197
healthCheckURL: "/readyz/non_exist",
@@ -344,6 +355,42 @@ func TestLinearizableReadCheck(t *testing.T) {
344355
}
345356
}
346357

358+
func TestLearnerReadyCheck(t *testing.T) {
359+
be, _ := betesting.NewDefaultTmpBackend(t)
360+
defer betesting.Close(t, be)
361+
tests := []healthTestCase{
362+
{
363+
name: "readyz normal",
364+
healthCheckURL: "/readyz",
365+
expectStatusCode: http.StatusOK,
366+
isLearner: false,
367+
},
368+
{
369+
name: "not ready because member is learner",
370+
healthCheckURL: "/readyz",
371+
expectStatusCode: http.StatusServiceUnavailable,
372+
isLearner: true,
373+
},
374+
}
375+
376+
for _, tt := range tests {
377+
t.Run(tt.name, func(t *testing.T) {
378+
mux := http.NewServeMux()
379+
logger := zaptest.NewLogger(t)
380+
s := &fakeHealthServer{
381+
linearizableReadError: tt.apiError,
382+
authStore: auth.NewAuthStore(logger, schema.NewAuthBackend(logger, be), nil, 0),
383+
}
384+
s.isLearner = tt.isLearner
385+
HandleHealth(logger, mux, s)
386+
ts := httptest.NewServer(mux)
387+
defer ts.Close()
388+
checkHTTPResponse(t, ts, tt.healthCheckURL, tt.expectStatusCode, tt.inResult, tt.notInResult)
389+
checkMetrics(t, tt.healthCheckURL, "linearizable_read", tt.expectStatusCode)
390+
})
391+
}
392+
}
393+
347394
func checkHTTPResponse(t *testing.T, ts *httptest.Server, url string, expectStatusCode int, inResult []string, notInResult []string) {
348395
res, err := ts.Client().Do(&http.Request{Method: http.MethodGet, URL: testutil.MustNewURL(t, ts.URL+url)})
349396
if err != nil {

0 commit comments

Comments
 (0)