draft grpc
This commit is contained in:
103
internal/video/runtime/services/health_service.go
Normal file
103
internal/video/runtime/services/health_service.go
Normal file
@@ -0,0 +1,103 @@
|
||||
package services
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/redis/go-redis/v9"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type HealthStatus string
|
||||
|
||||
const (
|
||||
HealthStatusHealthy HealthStatus = "healthy"
|
||||
HealthStatusUnhealthy HealthStatus = "unhealthy"
|
||||
HealthStatusDegraded HealthStatus = "degraded"
|
||||
)
|
||||
|
||||
type ComponentHealth struct {
|
||||
Status HealthStatus `json:"status"`
|
||||
Message string `json:"message,omitempty"`
|
||||
Latency string `json:"latency,omitempty"`
|
||||
CheckedAt time.Time `json:"checked_at"`
|
||||
}
|
||||
|
||||
type HealthReport struct {
|
||||
Status HealthStatus `json:"status"`
|
||||
Version string `json:"version"`
|
||||
CheckedAt time.Time `json:"checked_at"`
|
||||
Components map[string]ComponentHealth `json:"components"`
|
||||
}
|
||||
|
||||
type HealthService struct {
|
||||
db *gorm.DB
|
||||
redis *redis.Client
|
||||
version string
|
||||
}
|
||||
|
||||
func NewHealthService(db *gorm.DB, redisClient *redis.Client, version string) *HealthService {
|
||||
if version == "" {
|
||||
version = "0.0.1"
|
||||
}
|
||||
return &HealthService{db: db, redis: redisClient, version: version}
|
||||
}
|
||||
|
||||
func (s *HealthService) CheckHealth(ctx context.Context) *HealthReport {
|
||||
report := &HealthReport{Status: HealthStatusHealthy, Version: s.version, CheckedAt: time.Now(), Components: map[string]ComponentHealth{}}
|
||||
dbHealth := s.checkDatabase(ctx)
|
||||
report.Components["database"] = dbHealth
|
||||
if dbHealth.Status != HealthStatusHealthy {
|
||||
report.Status = HealthStatusUnhealthy
|
||||
}
|
||||
redisHealth := s.checkRedis(ctx)
|
||||
report.Components["redis"] = redisHealth
|
||||
if redisHealth.Status != HealthStatusHealthy && report.Status == HealthStatusHealthy {
|
||||
report.Status = HealthStatusDegraded
|
||||
}
|
||||
return report
|
||||
}
|
||||
|
||||
func (s *HealthService) SimpleHealthCheck(ctx context.Context) (string, int) {
|
||||
report := s.CheckHealth(ctx)
|
||||
switch report.Status {
|
||||
case HealthStatusHealthy:
|
||||
return "ok", 200
|
||||
case HealthStatusDegraded:
|
||||
return "degraded", 200
|
||||
default:
|
||||
return "unhealthy", 503
|
||||
}
|
||||
}
|
||||
|
||||
func (s *HealthService) checkDatabase(ctx context.Context) ComponentHealth {
|
||||
start := time.Now()
|
||||
sqlDB, err := s.db.DB()
|
||||
if err != nil {
|
||||
return ComponentHealth{Status: HealthStatusUnhealthy, Message: fmt.Sprintf("failed to get database instance: %v", err), CheckedAt: time.Now()}
|
||||
}
|
||||
ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
|
||||
defer cancel()
|
||||
if err := sqlDB.PingContext(ctx); err != nil {
|
||||
return ComponentHealth{Status: HealthStatusUnhealthy, Message: fmt.Sprintf("database ping failed: %v", err), Latency: time.Since(start).String(), CheckedAt: time.Now()}
|
||||
}
|
||||
var result int
|
||||
if err := s.db.WithContext(ctx).Raw("SELECT 1").Scan(&result).Error; err != nil {
|
||||
return ComponentHealth{Status: HealthStatusUnhealthy, Message: fmt.Sprintf("database query failed: %v", err), Latency: time.Since(start).String(), CheckedAt: time.Now()}
|
||||
}
|
||||
return ComponentHealth{Status: HealthStatusHealthy, Latency: time.Since(start).String(), CheckedAt: time.Now()}
|
||||
}
|
||||
|
||||
func (s *HealthService) checkRedis(ctx context.Context) ComponentHealth {
|
||||
start := time.Now()
|
||||
if s.redis == nil {
|
||||
return ComponentHealth{Status: HealthStatusUnhealthy, Message: "redis client not initialized", CheckedAt: time.Now()}
|
||||
}
|
||||
ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
|
||||
defer cancel()
|
||||
if err := s.redis.Ping(ctx).Err(); err != nil {
|
||||
return ComponentHealth{Status: HealthStatusUnhealthy, Message: fmt.Sprintf("redis ping failed: %v", err), Latency: time.Since(start).String(), CheckedAt: time.Now()}
|
||||
}
|
||||
return ComponentHealth{Status: HealthStatusHealthy, Latency: time.Since(start).String(), CheckedAt: time.Now()}
|
||||
}
|
||||
Reference in New Issue
Block a user