From 30c3e81e0dde9d0f04062a28af7653670af87b3c Mon Sep 17 00:00:00 2001 From: tidwall Date: Tue, 25 May 2021 16:36:49 -0700 Subject: [PATCH 1/2] Added HEALTHZ command Returns 'ok' if the server is the leader or a follower with a 'caught up' log. This is mainly for HTTP connections that are using an orchestration environment like kubernetes, but will work as a general RESP command. For HTTP a '200 OK' for 'caught up' and '500 Internal Server Error' otherwise. See #608 --- internal/server/server.go | 7 +++++-- internal/server/stats.go | 19 +++++++++++++++++++ 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/internal/server/server.go b/internal/server/server.go index 7a7838a2..a9b72498 100644 --- a/internal/server/server.go +++ b/internal/server/server.go @@ -744,7 +744,8 @@ func (server *Server) handleInputCommand(client *Client, msg *Message) error { return WriteWebSocketMessage(client, []byte(res)) case HTTP: status := "200 OK" - if server.http500Errors && !gjson.Get(res, "ok").Bool() { + if (server.http500Errors || msg._command == "healthz") && + !gjson.Get(res, "ok").Bool() { status = "500 Internal Server Error" } _, err := fmt.Fprintf(client, "HTTP/1.1 %s\r\n"+ @@ -881,7 +882,7 @@ func (server *Server) handleInputCommand(client *Client, msg *Message) error { } case "get", "keys", "scan", "nearby", "within", "intersects", "hooks", "chans", "search", "ttl", "bounds", "server", "info", "type", "jget", - "evalro", "evalrosha": + "evalro", "evalrosha", "healthz": // read operations server.mu.RLock() @@ -1067,6 +1068,8 @@ func (server *Server) command(msg *Message, client *Client) ( res, err = server.cmdStats(msg) case "server": res, err = server.cmdServer(msg) + case "healthz": + res, err = server.cmdHealthz(msg) case "info": res, err = server.cmdInfo(msg) case "scan": diff --git a/internal/server/stats.go b/internal/server/stats.go index 0910b1b5..3706b5f5 100644 --- a/internal/server/stats.go +++ b/internal/server/stats.go @@ -3,6 +3,7 @@ package server import ( "bytes" "encoding/json" + "errors" "fmt" "os" "runtime" @@ -94,6 +95,24 @@ func (s *Server) cmdStats(msg *Message) (res resp.Value, err error) { return res, nil } +func (s *Server) cmdHealthz(msg *Message) (res resp.Value, err error) { + start := time.Now() + // if s.config.followHost() != "" { + m := make(map[string]interface{}) + s.basicStats(m) + if fmt.Sprintf("%v\n", m["caught_up"]) != "true" { + return NOMessage, errors.New("not caught up") + } + // } + switch msg.OutputType { + case JSON: + res = resp.StringValue(`{"ok":true,"elapsed":"` + time.Since(start).String() + "\"}") + case RESP: + res = resp.SimpleStringValue("OK") + } + return res, nil +} + func (s *Server) cmdServer(msg *Message) (res resp.Value, err error) { start := time.Now() m := make(map[string]interface{}) From 27335c37f1b4c732451f6af273fd19ea6ef421bc Mon Sep 17 00:00:00 2001 From: tidwall Date: Tue, 25 May 2021 16:42:26 -0700 Subject: [PATCH 2/2] Always OK for leader --- internal/server/stats.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/internal/server/stats.go b/internal/server/stats.go index 3706b5f5..ec90970f 100644 --- a/internal/server/stats.go +++ b/internal/server/stats.go @@ -97,13 +97,13 @@ func (s *Server) cmdStats(msg *Message) (res resp.Value, err error) { func (s *Server) cmdHealthz(msg *Message) (res resp.Value, err error) { start := time.Now() - // if s.config.followHost() != "" { - m := make(map[string]interface{}) - s.basicStats(m) - if fmt.Sprintf("%v\n", m["caught_up"]) != "true" { - return NOMessage, errors.New("not caught up") + if s.config.followHost() != "" { + m := make(map[string]interface{}) + s.basicStats(m) + if fmt.Sprintf("%v\n", m["caught_up"]) != "true" { + return NOMessage, errors.New("not caught up") + } } - // } switch msg.OutputType { case JSON: res = resp.StringValue(`{"ok":true,"elapsed":"` + time.Since(start).String() + "\"}")