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 
This commit is contained in:
tidwall 2021-05-25 16:36:49 -07:00
parent d307d93c89
commit 5a37198602
2 changed files with 24 additions and 2 deletions
internal/server

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