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)) return WriteWebSocketMessage(client, []byte(res))
case HTTP: case HTTP:
status := "200 OK" 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" status = "500 Internal Server Error"
} }
_, err := fmt.Fprintf(client, "HTTP/1.1 %s\r\n"+ _, 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", case "get", "keys", "scan", "nearby", "within", "intersects", "hooks",
"chans", "search", "ttl", "bounds", "server", "info", "type", "jget", "chans", "search", "ttl", "bounds", "server", "info", "type", "jget",
"evalro", "evalrosha": "evalro", "evalrosha", "healthz":
// read operations // read operations
server.mu.RLock() server.mu.RLock()
@ -1067,6 +1068,8 @@ func (server *Server) command(msg *Message, client *Client) (
res, err = server.cmdStats(msg) res, err = server.cmdStats(msg)
case "server": case "server":
res, err = server.cmdServer(msg) res, err = server.cmdServer(msg)
case "healthz":
res, err = server.cmdHealthz(msg)
case "info": case "info":
res, err = server.cmdInfo(msg) res, err = server.cmdInfo(msg)
case "scan": case "scan":

View File

@ -3,6 +3,7 @@ package server
import ( import (
"bytes" "bytes"
"encoding/json" "encoding/json"
"errors"
"fmt" "fmt"
"os" "os"
"runtime" "runtime"
@ -94,6 +95,24 @@ func (s *Server) cmdStats(msg *Message) (res resp.Value, err error) {
return res, nil 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) { func (s *Server) cmdServer(msg *Message) (res resp.Value, err error) {
start := time.Now() start := time.Now()
m := make(map[string]interface{}) m := make(map[string]interface{})