Server: Add standard liveness/readiness check endpoints

Signed-off-by: Michael Mayer <michael@photoprism.app>
This commit is contained in:
Michael Mayer
2025-08-13 13:07:01 +02:00
parent 52204ee246
commit d47b38bc8b
2 changed files with 29 additions and 3 deletions

View File

@@ -33,6 +33,7 @@ import (
"runtime"
"strings"
"sync"
"sync/atomic"
"time"
"github.com/dustin/go-humanize"
@@ -78,6 +79,7 @@ type Config struct {
serial string
env string
start bool
ready atomic.Bool
}
func init() {
@@ -266,10 +268,16 @@ func (c *Config) Init() error {
// Show log message.
log.Debugf("config: successfully initialized [%s]", time.Since(start))
c.ready.Store(true)
return nil
}
// IsReady checks if the application has been successfully initialized.
func (c *Config) IsReady() bool {
return c.ready.Load()
}
// Propagate updates config options in other packages as needed.
func (c *Config) Propagate() {
FlushCache()
@@ -599,6 +607,9 @@ func (c *Config) SetLogLevel(level logrus.Level) {
// Shutdown shuts down the active processes and closes the database connection.
func (c *Config) Shutdown() {
// App is no longer accepting requests.
c.ready.Store(false)
// Send cancel signal to all workers.
mutex.CancelAll()

View File

@@ -111,12 +111,27 @@ func Start(ctx context.Context, conf *config.Config) {
// Register application routes.
registerRoutes(router, conf)
// Register "GET /health" route so clients can perform health checks.
router.GET(conf.BaseUri("/health"), func(c *gin.Context) {
// Register standard health check endpoints to determine whether the server is running.
isLive := func(c *gin.Context) {
c.Header(header.CacheControl, header.CacheControlNoStore)
c.Header(header.AccessControlAllowOrigin, header.Any)
c.String(http.StatusOK, "OK")
})
}
router.Any(conf.BaseUri("/livez"), isLive)
router.Any(conf.BaseUri("/health"), isLive)
router.Any(conf.BaseUri("/healthz"), isLive)
// Register "/readyz" endpoint to check if the server has been successfully initialized.
isReady := func(c *gin.Context) {
c.Header(header.CacheControl, header.CacheControlNoStore)
c.Header(header.AccessControlAllowOrigin, header.Any)
if conf.IsReady() {
c.String(http.StatusOK, "OK")
} else {
c.String(http.StatusServiceUnavailable, "Service Unavailable")
}
}
router.Any(conf.BaseUri("/readyz"), isReady)
// Create a new HTTP server instance with no read or write timeout, except for reading the headers:
// https://pkg.go.dev/net/http#Server