package channel import ( "time" "go.uber.org/zap" "openteam/server/internal/store" ) // StartHealthCheck runs the periodic health-check loop in a goroutine. // Only channels that are enabled and have a bound test model are checked. func (s *Service) StartHealthCheck() { go func() { ticker := time.NewTicker(s.cfg.HealthCheck.Interval) defer ticker.Stop() s.runHealthCheck() for range ticker.C { s.runHealthCheck() } }() } func (s *Service) runHealthCheck() { var channels []store.Channel if err := s.db.Where("enabled = ?", true).Find(&channels).Error; err != nil { s.log.Warn("health check query failed", zap.Error(err)) return } for i := range channels { ch := &channels[i] // Skip channels already in cooldown until the cooldown elapses. if ch.HealthStatus == "cooldown" { continue } ok, _, _, err := s.Test(ch) if err != nil || !ok { s.MarkFailure(ch.ID) continue } s.MarkHealthy(ch.ID, "healthy") } } // RecoverCooldown moves channels back from cooldown to degraded after the // cooldown window, giving them another chance to pass the health check. // Called periodically; keeps cooldown bounded. func (s *Service) RecoverCooldown() { cutoff := time.Now().Add(-s.cfg.HealthCheck.Cooldown) s.db.Model(&store.Channel{}). Where("health_status = ? AND updated_at < ?", "cooldown", cutoff). Updates(map[string]any{"health_status": "degraded", "health_failures": 0}) }