Healthcheck/endpoints.go
2024-09-01 22:05:49 -03:00

142 lines
3.0 KiB
Go

package main
import (
"encoding/json"
"net/http"
)
type HealthcheckShortResult struct {
ResponseTime float64 `json:"response_time"`
SuccessPercentage float64 `json:"success_percentage"`
}
func healthCheckHandler(w http.ResponseWriter, r *http.Request) {
rows, err := db.Query(`
SELECT h.public_name, r.success_percentage, r.response_time
FROM hosts h
JOIN results r ON h.id = r.host_id
ORDER BY r.timestamp DESC, r.id DESC
LIMIT 1000;
`)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
defer rows.Close()
results := make(map[string][]HealthcheckShortResult)
for rows.Next() {
var publicName string
var successPercentage float64
var responseTime float64
err := rows.Scan(&publicName, &successPercentage, &responseTime)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
if len(results[publicName]) >= 50 {
continue
}
result := HealthcheckShortResult{
ResponseTime: responseTime,
SuccessPercentage: successPercentage,
}
results[publicName] = append(results[publicName], result)
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(results)
}
func statusHandler(w http.ResponseWriter, r *http.Request) {
statuses, err := getHostStatus()
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(statuses)
}
type HostStatus map[string]string
func getHostStatus() (map[string]string, error) {
resultByHost := make(map[string][]float64)
statuses := make(HostStatus)
hosts, err := db.Query(`select id, public_name from hosts`)
if err != nil {
return nil, err
}
for hosts.Next() {
var id int64
var publicName string
err := hosts.Scan(&id, &publicName)
if err != nil {
return nil, err
}
results, err := db.Query(`select success_percentage from results where host_id = $1 order by timestamp desc, id desc limit 10`, id)
if err != nil {
return nil, err
}
for results.Next() {
var successPercentage float64
results.Scan(&successPercentage)
resultByHost[publicName] = append(resultByHost[publicName], successPercentage)
}
}
for publicName, result := range resultByHost {
total := 10
if len(result) < total {
continue
}
var sum float64 = 0.0
var halfSum float64 = 0.0
for _, value := range result[len(result)-total:] {
sum += value
}
for _, value := range result[len(result)-total/2:] {
halfSum += value
}
average := sum / float64(total)
recent := halfSum / float64(total/2)
var status string
if average > 0.95 {
status = "healthy"
} else if average > 0.6 {
if recent < 0.5 {
status = "unstable failing"
} else if recent > 0.8 {
status = "unstable recovering"
} else {
status = "unstable"
}
} else {
if recent > 0.6 {
status = "unhealthy recovering"
} else {
status = "unhealthy"
}
}
statuses[publicName] = status
}
return statuses, nil
}