Add healthcheck endpoint

This commit is contained in:
Rustem Kamalov
2026-03-25 04:09:37 +03:00
parent a4d459761e
commit 0fcfc06baa
2 changed files with 73 additions and 0 deletions

View File

@@ -19,5 +19,8 @@ FROM zenika/alpine-chrome:with-chromedriver
COPY --from=builder /app/openserp /usr/local/bin/openserp
ADD config.yaml /usr/src/app
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
CMD wget --no-verbose --tries=1 --spider http://localhost:7000/health || exit 1
ENTRYPOINT ["openserp"]

View File

@@ -3,9 +3,11 @@ package core
import (
"context"
"fmt"
"runtime"
"sort"
"strings"
"sync"
"time"
"github.com/gofiber/fiber/v2"
"github.com/sirupsen/logrus"
@@ -24,6 +26,7 @@ type Server struct {
app *fiber.App
addr string
searchEngines []SearchEngine
startTime time.Time
}
func NewServer(host string, port int, searchEngines ...SearchEngine) *Server {
@@ -32,8 +35,12 @@ func NewServer(host string, port int, searchEngines ...SearchEngine) *Server {
app: fiber.New(),
addr: addr,
searchEngines: searchEngines,
startTime: time.Now(),
}
// Health endpoint used by orchestration probes.
serv.app.Get("/health", serv.handleHealthCheck)
for _, engine := range searchEngines {
locEngine := engine
limiter := engine.GetRateLimiter()
@@ -370,6 +377,69 @@ func (s *Server) deduplicateMegaResults(results []MegaSearchResult) []MegaSearch
return deduped
}
type HealthStatus struct {
Status string `json:"status"`
Uptime string `json:"uptime"`
Engines []EngineHealth `json:"engines"`
System map[string]interface{} `json:"system"`
}
type EngineHealth struct {
Name string `json:"name"`
Initialized bool `json:"initialized"`
Status string `json:"status"`
}
// handleHealthCheck returns current service and engine status.
// Degraded state stays HTTP 200 to avoid unnecessary restarts in orchestrators.
func (s *Server) handleHealthCheck(c *fiber.Ctx) error {
engines := make([]EngineHealth, 0, len(s.searchEngines))
availableEngines := 0
for _, engine := range s.searchEngines {
status := "ready"
if !engine.IsInitialized() {
status = "not_initialized"
} else {
availableEngines++
}
engines = append(engines, EngineHealth{
Name: engine.Name(),
Initialized: engine.IsInitialized(),
Status: status,
})
}
overallStatus := "healthy"
totalEngines := len(s.searchEngines)
switch {
case totalEngines == 0 || availableEngines == 0:
overallStatus = "unhealthy"
case availableEngines < totalEngines:
overallStatus = "degraded"
}
var memStats runtime.MemStats
runtime.ReadMemStats(&memStats)
health := HealthStatus{
Status: overallStatus,
Uptime: time.Since(s.startTime).Round(time.Second).String(),
Engines: engines,
System: map[string]interface{}{
"goroutines": runtime.NumGoroutine(),
"memory_mb": memStats.Alloc / 1024 / 1024,
"go_version": runtime.Version(),
},
}
if overallStatus == "unhealthy" {
c.Status(fiber.StatusServiceUnavailable)
}
return c.JSON(health)
}
func (s *Server) Listen() error {
return s.app.Listen(s.addr)
}