Files
fm_be/internal/queue/health.go
2026-07-16 22:16:45 +07:00

91 lines
2.2 KiB
Go

package queue
import (
"sync"
"time"
)
type HealthReporter struct {
mu sync.RWMutex
startedAt time.Time
lastPollAt time.Time
lastSuccessAt time.Time
lastFailureAt time.Time
lastError string
consecutivePollFailures int
errorThreshold int
draining bool
}
type HealthSnapshot struct {
Status string `json:"status"`
StartedAt time.Time `json:"started_at"`
LastPollAt time.Time `json:"last_poll_at,omitempty"`
LastSuccessAt time.Time `json:"last_success_at,omitempty"`
LastFailureAt time.Time `json:"last_failure_at,omitempty"`
LastError string `json:"last_error,omitempty"`
ConsecutivePollFailure int `json:"consecutive_poll_failures"`
Draining bool `json:"draining"`
}
func NewHealthReporter(errorThreshold int) *HealthReporter {
if errorThreshold <= 0 {
errorThreshold = 3
}
now := time.Now().UTC()
return &HealthReporter{
startedAt: now,
lastPollAt: now,
errorThreshold: errorThreshold,
}
}
func (r *HealthReporter) MarkPollSuccess() {
r.mu.Lock()
defer r.mu.Unlock()
now := time.Now().UTC()
r.lastPollAt = now
r.lastSuccessAt = now
r.consecutivePollFailures = 0
r.lastError = ""
}
func (r *HealthReporter) MarkPollFailure(err error) {
r.mu.Lock()
defer r.mu.Unlock()
now := time.Now().UTC()
r.lastPollAt = now
r.lastFailureAt = now
r.consecutivePollFailures++
if err != nil {
r.lastError = err.Error()
}
}
func (r *HealthReporter) MarkDraining() {
r.mu.Lock()
defer r.mu.Unlock()
r.draining = true
}
func (r *HealthReporter) Snapshot() HealthSnapshot {
r.mu.RLock()
defer r.mu.RUnlock()
status := "ok"
if r.draining {
status = "draining"
} else if r.consecutivePollFailures >= r.errorThreshold {
status = "degraded"
}
return HealthSnapshot{
Status: status,
StartedAt: r.startedAt,
LastPollAt: r.lastPollAt,
LastSuccessAt: r.lastSuccessAt,
LastFailureAt: r.lastFailureAt,
LastError: r.lastError,
ConsecutivePollFailure: r.consecutivePollFailures,
Draining: r.draining,
}
}