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
This commit is contained in:
tidwall 2021-05-25 16:36:49 -07:00
parent 25ba82f41b
commit 30c3e81e0d
2 changed files with 24 additions and 2 deletions

View File

@ -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":

View File

@ -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{})