Files
tokens-reef/backend/internal/service/health_checker.go

131 lines
3.3 KiB
Go
Raw Normal View History

package service
import (
"context"
"database/sql"
"time"
"github.com/redis/go-redis/v9"
)
// HealthChecker provides health check functionality for dependencies
type HealthChecker struct {
db *sql.DB
redisClient *redis.Client
}
// NewHealthChecker creates a new health checker instance
func NewHealthChecker(db *sql.DB, redisClient *redis.Client) *HealthChecker {
return &HealthChecker{
db: db,
redisClient: redisClient,
}
}
// CheckDatabase checks if the database connection is healthy
func (h *HealthChecker) CheckDatabase() bool {
if h.db == nil {
return false
}
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel()
var result int
err := h.db.QueryRowContext(ctx, "SELECT 1").Scan(&result)
if err != nil {
return false
}
return result == 1
}
// CheckRedis checks if the Redis connection is healthy
func (h *HealthChecker) CheckRedis() bool {
if h.redisClient == nil {
return false
}
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel()
err := h.redisClient.Ping(ctx).Err()
return err == nil
}
// GetDatabaseStats returns database connection statistics
func (h *HealthChecker) GetDatabaseStats() (active, idle int) {
if h.db == nil {
return 0, 0
}
stats := h.db.Stats()
return stats.InUse, stats.Idle
}
// GetRedisStats returns Redis connection statistics
func (h *HealthChecker) GetRedisStats() (total, idle int) {
if h.redisClient == nil {
return 0, 0
}
stats := h.redisClient.PoolStats()
if stats == nil {
return 0, 0
}
return int(stats.TotalConns), int(stats.IdleConns)
}
// HealthStatus represents the health status of all dependencies
type HealthStatus struct {
Database DatabaseHealth `json:"database"`
Redis RedisHealth `json:"redis"`
}
// DatabaseHealth represents database health information
type DatabaseHealth struct {
Healthy bool `json:"healthy"`
ActiveConns int `json:"active_connections"`
IdleConns int `json:"idle_connections"`
OpenConns int `json:"open_connections"`
MaxOpenConns int `json:"max_open_connections"`
WaitCount int64 `json:"wait_count"`
MaxIdleClosed int64 `json:"max_idle_closed"`
MaxLifetimeClosed int64 `json:"max_lifetime_closed"`
}
// RedisHealth represents Redis health information
type RedisHealth struct {
Healthy bool `json:"healthy"`
TotalConns int `json:"total_connections"`
IdleConns int `json:"idle_connections"`
}
// GetHealthStatus returns comprehensive health status
func (h *HealthChecker) GetHealthStatus() HealthStatus {
status := HealthStatus{}
// Database health
status.Database.Healthy = h.CheckDatabase()
if h.db != nil {
stats := h.db.Stats()
status.Database.ActiveConns = stats.InUse
status.Database.IdleConns = stats.Idle
status.Database.OpenConns = stats.OpenConnections
status.Database.MaxOpenConns = stats.MaxOpenConnections
status.Database.WaitCount = stats.WaitCount
status.Database.MaxIdleClosed = stats.MaxIdleClosed
status.Database.MaxLifetimeClosed = stats.MaxLifetimeClosed
}
// Redis health
status.Redis.Healthy = h.CheckRedis()
if h.redisClient != nil {
stats := h.redisClient.PoolStats()
if stats != nil {
status.Redis.TotalConns = int(stats.TotalConns)
status.Redis.IdleConns = int(stats.IdleConns)
}
}
return status
}