Files
beacon-server/internal/guards/errors.go
T
MrAlders0n 03f66d1ae4 Database pools by workload, plan router, observe-only guards, metrics (rest of #237) (#238)
* feat(db): five pgx pools with fixed session settings and Prometheus tracing

* feat(guards): per-family observe/enforce guards with client slots, deadlines and a shadow budget

* fix(guards): attribute only guard deadlines and classify like the handlers

* feat(db): route API statements by sqlc name; class C always unnamed

* feat(admin): statement statistics by sqlc name; create pg_stat_statements where allowed

* fix(db): skip pg_stat_statements when no schema is creatable

* feat(background): index health against post-rebuild baselines, Postgres activity, job and ingest metrics

* feat: separate API, ingest and maintenance stores; guards on the public API; metrics listener

* fix(db): keep ingest writes waiting, size the ingest pool to the host, tidy

* test(db): give the statements probe a query id of its own
2026-10-19 07:52:51 -04:00

46 lines
1.4 KiB
Go

// Copyright 2026 Beacon Contributors
// SPDX-License-Identifier: AGPL-3.0-or-later
package guards
import (
"encoding/json"
"net/http"
"strconv"
)
// Refusal is the response a guard sends instead of the handler's.
type Refusal struct {
Guard string
Status int
RetryAfter int
Code string
Message string
}
func (r *Refusal) Error() string { return "guard " + r.Guard + ": " + r.Message }
func deadlineRefusal() *Refusal {
return &Refusal{Guard: "deadline", Status: http.StatusServiceUnavailable, RetryAfter: 5,
Code: "deadline_exceeded", Message: "The request ran past its database time limit. Retry later."}
}
func admissionRefusal() *Refusal {
return &Refusal{Guard: "admission", Status: http.StatusServiceUnavailable, RetryAfter: 2,
Code: "service_unavailable", Message: "The server is busy. Retry later."}
}
func capRefusal() *Refusal {
return &Refusal{Guard: "cap", Status: http.StatusTooManyRequests, RetryAfter: 1,
Code: "rate_limited", Message: "Too many concurrent requests from this client. Retry later."}
}
// WriteRefusal writes r in the API's standard error envelope.
func WriteRefusal(w http.ResponseWriter, r *Refusal) {
body, _ := json.Marshal(map[string]map[string]string{"error": {"code": r.Code, "message": r.Message}})
w.Header().Set("Retry-After", strconv.Itoa(r.RetryAfter))
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(r.Status)
_, _ = w.Write(body)
}