Documentation
¶
Overview ¶
Package health — ЕДИНСТВЕННЫЙ в дереве носитель разведённых живости и готовности.
Готовность (`/readyz`) строится из ИМЕНОВАННЫХ проверок критичных зависимостей (база / канал к владельцу прав / дренер регистраций / исполнитель операций). Отказ зависимости снимает под из ротации (503), а живость (`/healthz`) при этом остаётся 200 — она зависит ТОЛЬКО от процесса, чтобы блип зависимости не вызвал шторм перезапусков. Каждый чекер ограничен собственным сроком: зависшая зависимость считается недоступной, но не вешает обработчик.
На гашении SetShuttingDown переводит `/readyz` в 503 (kubelet перестаёт слать трафик) ДО остановки gRPC-серверов.
Почему носитель ОДИН, а не по копии на сервис ¶
Копий было три, и они были кодом идентичны — то есть один механизм, о котором дерево говорило трижды. Разойтись им было нечем: копии не собираются вместе и друг друга не читают, поэтому расхождение пришло бы молча. Замер, из которого это выведено (задача продукта #1655): трём сервисам из семи готовность требовалось завести впервые, и заведение по месту дало бы ШЕСТЬ копий вместо трёх (`architecture.md` §«Переиспользование через общий фундамент pkg/»).
Чего этот пакет НЕ решает ¶
ЧТО именно проверяет каждая зависимость — вопрос композиционного корня: он один знает, какая база у сервиса своя и к кому он ходит. Пакет отвечает лишь за то, чтобы ответ был РАЗВЕДЁН на два и чтобы ни один чекер не мог подвесить обработчик.
Index ¶
Constants ¶
This section is empty.
Variables ¶
var ErrDependencyNotWired = errors.New("dependency not wired yet")
ErrDependencyNotWired — зависимость объявлена корнем, но её носитель ещё не установлен. Причина «не готов» без раскрытия внутренних деталей наружу.
Functions ¶
This section is empty.
Types ¶
type Aggregator ¶
type Aggregator struct {
// contains filtered or unexported fields
}
Aggregator агрегирует readiness по набору чекеров и разводит liveness/readiness. Потокобезопасен (shutting — под mutex'ом; чекеры read-only).
func New ¶
func New(checkers []Checker, opts ...Option) *Aggregator
New конструирует Aggregator поверх набора чекеров.
func (*Aggregator) Evaluate ¶
func (a *Aggregator) Evaluate(ctx context.Context) (bool, []string)
Evaluate прогоняет все чекеры конкурентно с per-checker bounded-timeout и возвращает готовность + имена упавших зависимостей (в порядке объявления чекеров). Результат зеркалится в observer.
func (*Aggregator) LiveHandler ¶
func (a *Aggregator) LiveHandler() http.HandlerFunc
LiveHandler — liveness probe. 200 `ok`, пока процесс жив и не в shutdown. НЕ зависит от внешних зависимостей (защита от restart-storm).
func (*Aggregator) ReadyHandler ¶
func (a *Aggregator) ReadyHandler() http.HandlerFunc
ReadyHandler — readiness probe. 200 `{"status":"ready"}`, когда все критичные зависимости здоровы; 503 со списком упавших — иначе; 503 `{"status":"shutting_down"}` на graceful-shutdown.
func (*Aggregator) SetShuttingDown ¶
func (a *Aggregator) SetShuttingDown()
SetShuttingDown помечает процесс в состоянии graceful-shutdown — readiness сразу флипает в 503, liveness тоже отдаёт 503 (процесс гасится).
type Checker ¶
Checker — именованная проверка одной зависимости. Check возвращает nil, если зависимость здорова; любая ошибка (или таймаут) трактуется как down.
type Option ¶
type Option func(*Aggregator)
Option — функциональная опция Aggregator.
func WithResultObserver ¶
WithResultObserver подключает зеркало результата readiness (напр. dependency_up Prometheus-gauge): вызывается на каждый чекер при каждой оценке.
func WithTimeout ¶
WithTimeout задаёт per-checker бюджет (дефолт 1s).
type Slot ¶
type Slot struct {
// contains filtered or unexported fields
}
Slot — зависимость, чей носитель появляется ПОЗЖЕ поверхности.
Зачем ¶
Диагностическая поверхность поднимается РАНО и намеренно: пока её нет, проба живости получает отказ соединения, и медленный старт читается kubelet'ом как мёртвый процесс — то есть шторм перезапусков ровно там, где процесс исправно поднимается. Часть зависимостей при этом ещё не создана: соединение с соседом набирается ниже по тексту корня.
Прямое замыкание на переменную корня здесь негодно ДВАЖДЫ: обработчик читает её из другой горутины (гонка, и `-race` это покажет), а до присваивания он увидит нулевое значение и объявит зависимость исправной. Slot закрывает оба: доступ синхронизирован, а до установки ответ — НЕ ГОТОВ.
Умолчание выбрано fail-closed осознанно: неустановленная зависимость есть «ответа нет», а неполученный ответ не является «да».
func (*Slot) Check ¶
Check — тело чекера. До Install отвечает ErrDependencyNotWired: «носителя ещё нет» и «носитель отвечает» обязаны быть различимы, иначе окно старта молча зачитывается в готовность.