Senior Go Interview Prep - Core Go: https://go.vbloher.org/docs/01-core-go/ - Механика defer в Go: https://go.vbloher.org/docs/01-core-go/defer/ - Встраивание структур и интерфейсов (Embedding): https://go.vbloher.org/docs/01-core-go/embedding/ - Ошибки в Go: error, wrapping, errors.Is/As/Join: https://go.vbloher.org/docs/01-core-go/errors/ - Дженерики в Go (1.18+): https://go.vbloher.org/docs/01-core-go/generics/ - Интерфейсы в Go: https://go.vbloher.org/docs/01-core-go/interfaces/ - Устройство map в Go: https://go.vbloher.org/docs/01-core-go/maps/ - panic / recover: механика, раскрутка стека и runtime-паники: https://go.vbloher.org/docs/01-core-go/panic-recover/ - Указатели в Go: https://go.vbloher.org/docs/01-core-go/pointers/ - Рефлексия в Go (reflect): https://go.vbloher.org/docs/01-core-go/reflection/ - Внутреннее устройство слайсов в Go: https://go.vbloher.org/docs/01-core-go/slices/ - Строки, руны и байты в Go: https://go.vbloher.org/docs/01-core-go/strings-runes-bytes/ - Система типов Go: defined types, alignment, memory layout: https://go.vbloher.org/docs/01-core-go/type-system/ - Concurrency: https://go.vbloher.org/docs/02-concurrency/ - sync/atomic: https://go.vbloher.org/docs/02-concurrency/atomic/ - Буферизованные vs небуферизованные каналы: https://go.vbloher.org/docs/02-concurrency/buffered-unbuffered/ - Канал vs Mutex: когда что выбрать: https://go.vbloher.org/docs/02-concurrency/channel-vs-mutex/ - Каналы: устройство hchan: https://go.vbloher.org/docs/02-concurrency/channels/ - Утечки горутин, дедлоки, livelock, starvation: https://go.vbloher.org/docs/02-concurrency/common-leaks-deadlocks/ - sync.Cond: https://go.vbloher.org/docs/02-concurrency/cond/ - context: https://go.vbloher.org/docs/02-concurrency/context/ - Горутины: жизненный цикл, стоимость, стек: https://go.vbloher.org/docs/02-concurrency/goroutines-lifecycle/ - sync.Mutex и sync.RWMutex: https://go.vbloher.org/docs/02-concurrency/mutex-rwmutex/ - sync.Once: https://go.vbloher.org/docs/02-concurrency/once/ - Паттерны конкурентности: https://go.vbloher.org/docs/02-concurrency/patterns/ - Race Detector (гонки данных и -race): https://go.vbloher.org/docs/02-concurrency/race-detector/ - Планировщик GMP: https://go.vbloher.org/docs/02-concurrency/scheduler-gmp/ - select: https://go.vbloher.org/docs/02-concurrency/select/ - sync.WaitGroup: https://go.vbloher.org/docs/02-concurrency/waitgroup/ - Runtime и память: https://go.vbloher.org/docs/03-runtime-memory/ - Паттерны аллокаций и снижение давления на GC: https://go.vbloher.org/docs/03-runtime-memory/allocation-patterns/ - Escape Analysis: когда переменная убегает в кучу: https://go.vbloher.org/docs/03-runtime-memory/escape-analysis/ - Сборщик мусора Go: concurrent tri-color mark-sweep: https://go.vbloher.org/docs/03-runtime-memory/gc/ - Тюнинг GC: GOGC и GOMEMLIMIT: https://go.vbloher.org/docs/03-runtime-memory/gogc-gomemlimit/ - GOMAXPROCS: параллелизм планировщика и проблема контейнеров: https://go.vbloher.org/docs/03-runtime-memory/gomaxprocs/ - Утечки горутин (goroutine leaks): https://go.vbloher.org/docs/03-runtime-memory/goroutine-leaks/ - Утечки памяти в Go (несмотря на GC): https://go.vbloher.org/docs/03-runtime-memory/memory-leaks/ - Модель памяти Go (Go Memory Model): happens-before и синхронизация: https://go.vbloher.org/docs/03-runtime-memory/memory-model/ - pprof: профилирование CPU, памяти и блокировок в Go: https://go.vbloher.org/docs/03-runtime-memory/pprof/ - Execution Tracer и runtime/trace: тайминги вместо агрегатов: https://go.vbloher.org/docs/03-runtime-memory/runtime-tracing/ - Стек vs Куча: где живут данные в Go: https://go.vbloher.org/docs/03-runtime-memory/stack-vs-heap/ - Тестирование: https://go.vbloher.org/docs/04-testing/ - testify, assert/require и golden files: https://go.vbloher.org/docs/04-testing/assertions-testify/ - Бенчмарки в Go: https://go.vbloher.org/docs/04-testing/benchmarks/ - Покрытие, -race и флаки-тесты: https://go.vbloher.org/docs/04-testing/coverage-race/ - Нативный fuzzing в Go (1.18+): https://go.vbloher.org/docs/04-testing/fuzzing/ - Интеграционные тесты, testcontainers-go, TestMain: https://go.vbloher.org/docs/04-testing/integration-testcontainers/ - Моки, стабы и тестируемость: https://go.vbloher.org/docs/04-testing/mocks/ - Table-driven тесты, subtests и параллельность: https://go.vbloher.org/docs/04-testing/table-driven/ - Backend: https://go.vbloher.org/docs/05-backend/ - Аутентификация и авторизация: AuthN/AuthZ, сессии vs токены, RBAC/ABAC, API keys, mTLS, секреты: https://go.vbloher.org/docs/05-backend/auth-authz/ - Graceful Shutdown HTTP/gRPC сервера в Go: https://go.vbloher.org/docs/05-backend/graceful-shutdown/ - gRPC: типы RPC, интерсепторы, контекст, метаданные, error model: https://go.vbloher.org/docs/05-backend/grpc/ - JWT (JSON Web Token): https://go.vbloher.org/docs/05-backend/jwt/ - Middleware-паттерн в Go: https://go.vbloher.org/docs/05-backend/middleware/ - net/http: Server, Handler, ServeMux, таймауты, Client и контекст: https://go.vbloher.org/docs/05-backend/net-http/ - OAuth2: роли, grant types, OIDC, токены и типовые ошибки: https://go.vbloher.org/docs/05-backend/oauth2/ - OpenAPI/Swagger, code generation, contract-first vs code-first, валидация: https://go.vbloher.org/docs/05-backend/openapi/ - Protocol Buffers: схемы, wire format, эволюция и совместимость: https://go.vbloher.org/docs/05-backend/protobuf/ - REST: принципы, версионирование, идемпотентность, статусы, пагинация, ошибки: https://go.vbloher.org/docs/05-backend/rest/ - Сети и протоколы: https://go.vbloher.org/docs/06-networking/ - Пулы соединений: http.Transport, БД, утечки: https://go.vbloher.org/docs/06-networking/connection-pooling/ - DNS: записи, резолвинг, кэширование, DNS в Go: https://go.vbloher.org/docs/06-networking/dns/ - Версии HTTP: 1.1, 2, 3: https://go.vbloher.org/docs/06-networking/http-versions/ - TCP/IP: модель, транспорт и что важно бэкендеру: https://go.vbloher.org/docs/06-networking/tcp-ip/ - TLS: handshake, сертификаты, mTLS, производительность: https://go.vbloher.org/docs/06-networking/tls/ - UDP и надёжность поверх UDP: https://go.vbloher.org/docs/06-networking/udp/ - WebSocket: upgrade, фреймы, масштабирование: https://go.vbloher.org/docs/06-networking/websocket/ - Базы данных: https://go.vbloher.org/docs/07-databases/ - Пул соединений к PostgreSQL в Go: database/sql, pgx, pgxpool, PgBouncer: https://go.vbloher.org/docs/07-databases/connection-pooling-pgx/ - Взаимоблокировки (Deadlocks) в PostgreSQL: https://go.vbloher.org/docs/07-databases/deadlocks/ - Индексы в PostgreSQL: https://go.vbloher.org/docs/07-databases/indexes/ - Уровни изоляции транзакций в PostgreSQL: https://go.vbloher.org/docs/07-databases/isolation-levels/ - MVCC в PostgreSQL: версии строк, видимость, VACUUM и bloat: https://go.vbloher.org/docs/07-databases/mvcc/ - Обзор NoSQL и Redis: https://go.vbloher.org/docs/07-databases/nosql-redis/ - Партиционирование таблиц в PostgreSQL: https://go.vbloher.org/docs/07-databases/partitioning/ - Архитектура PostgreSQL: https://go.vbloher.org/docs/07-databases/postgresql-architecture/ - Планирование и оптимизация запросов в PostgreSQL: https://go.vbloher.org/docs/07-databases/query-planning/ - Репликация в PostgreSQL: https://go.vbloher.org/docs/07-databases/replication/ - Шардирование (горизонтальное масштабирование): https://go.vbloher.org/docs/07-databases/sharding/ - Транзакции в PostgreSQL и Go (database/sql, pgx): https://go.vbloher.org/docs/07-databases/transactions/ - Распределённые системы: https://go.vbloher.org/docs/08-distributed-systems/ - CAP теорема: https://go.vbloher.org/docs/08-distributed-systems/cap-theorem/ - Circuit Breaker: https://go.vbloher.org/docs/08-distributed-systems/circuit-breaker/ - Консенсус и Raft: репликация состояния в присутствии отказов: https://go.vbloher.org/docs/08-distributed-systems/consensus-raft/ - Модели согласованности: https://go.vbloher.org/docs/08-distributed-systems/consistency/ - Гарантии доставки сообщений: at-most-once / at-least-once / exactly-once: https://go.vbloher.org/docs/08-distributed-systems/delivery-guarantees/ - Eventual Consistency: https://go.vbloher.org/docs/08-distributed-systems/eventual-consistency/ - Идемпотентность в распределённых системах: https://go.vbloher.org/docs/08-distributed-systems/idempotency/ - Apache Kafka: https://go.vbloher.org/docs/08-distributed-systems/kafka/ - Transactional Outbox: https://go.vbloher.org/docs/08-distributed-systems/outbox/ - RabbitMQ: AMQP 0-9-1, маршрутизация, надёжность доставки и сравнение с Kafka: https://go.vbloher.org/docs/08-distributed-systems/rabbitmq/ - Ретраи: backoff, jitter, budgets и идемпотентность: https://go.vbloher.org/docs/08-distributed-systems/retries/ - Saga Pattern: https://go.vbloher.org/docs/08-distributed-systems/saga/ - Observability: https://go.vbloher.org/docs/09-observability/ - Grafana: https://go.vbloher.org/docs/09-observability/grafana/ - Метрики: RED, USE, Golden Signals: https://go.vbloher.org/docs/09-observability/metrics/ - OpenTelemetry: https://go.vbloher.org/docs/09-observability/opentelemetry/ - Prometheus: https://go.vbloher.org/docs/09-observability/prometheus/ - SLI / SLO / SLA: https://go.vbloher.org/docs/09-observability/slo-sli/ - Структурированное логирование (slog): https://go.vbloher.org/docs/09-observability/structured-logging/ - Distributed Tracing: https://go.vbloher.org/docs/09-observability/tracing/ - System Design: https://go.vbloher.org/docs/10-system-design/ - Analytics Pipeline: https://go.vbloher.org/docs/10-system-design/analytics-pipeline/ - Chat System: https://go.vbloher.org/docs/10-system-design/chat/ - Фреймворк System Design интервью: https://go.vbloher.org/docs/10-system-design/framework/ - Notification Service: https://go.vbloher.org/docs/10-system-design/notification-service/ - Order Service: https://go.vbloher.org/docs/10-system-design/order-service/ - Payment Service: https://go.vbloher.org/docs/10-system-design/payment-service/ - Rate Limiter: https://go.vbloher.org/docs/10-system-design/rate-limiter/ - URL Shortener: https://go.vbloher.org/docs/10-system-design/url-shortener/ - DevOps: https://go.vbloher.org/docs/11-devops/ - CI/CD: пайплайны, стадии, стратегии деплоя: https://go.vbloher.org/docs/11-devops/cicd/ - Облака (AWS / GCP) для бэкендера: https://go.vbloher.org/docs/11-devops/cloud-aws-gcp/ - Docker для Go-разработчика: https://go.vbloher.org/docs/11-devops/docker/ - GitHub Actions и GitLab CI: https://go.vbloher.org/docs/11-devops/github-gitlab-ci/ - Kubernetes для Go-разработчика: https://go.vbloher.org/docs/11-devops/kubernetes/ - Terraform / Infrastructure as Code: https://go.vbloher.org/docs/11-devops/terraform/ - Алгоритмы: https://go.vbloher.org/docs/12-algorithms/ - Типовые алгоритмические задачи и паттерны: https://go.vbloher.org/docs/12-algorithms/common-problems/ - Асимптотическая сложность (Big-O): https://go.vbloher.org/docs/12-algorithms/complexity/ - Структуры данных в Go: https://go.vbloher.org/docs/12-algorithms/data-structures/ - Специфика live-coding на Go: https://go.vbloher.org/docs/12-algorithms/go-specifics/ - Behavioral: https://go.vbloher.org/docs/13-behavioral/ - Конфликты, разногласия и работа со стейкхолдерами: https://go.vbloher.org/docs/13-behavioral/conflicts/ - Как проходит senior-интервью: этапы, оценка, оффер: https://go.vbloher.org/docs/13-behavioral/interview-flow/ - Лидерство и менторство: https://go.vbloher.org/docs/13-behavioral/leadership-mentoring/ - Типовые поведенческие вопросы для Senior: https://go.vbloher.org/docs/13-behavioral/senior-questions/ > Модуль: Observability · Уровень: Senior ## TL;DR - **SLI (Service Level Indicator)** — измеримая метрика поведения сервиса с точки зрения пользователя: доля успешных запросов, доля быстрых запросов, свежесть данных. Формула почти всегда `good events / valid events`. - **SLO (Service Level Objective)** — целевое значение SLI на окне времени: «99.9% запросов за 30 дней успешны». Внутренняя цель команды. - **SLA (Service Level Agreement)** — юридический/контрактный обязательство перед клиентом с компенсациями (refund, credits). SLA обычно слабее SLO (если SLO=99.9%, SLA=99.5%), чтобы у команды был запас. - **Error budget** = `1 − SLO`. При SLO 99.9% бюджет ошибок = 0.1% запросов за окно. Бюджет — это разрешённое количество «плохого», которое можно тратить на риск (релизы, эксперименты). - **Burn rate** — скорость расходования бюджета. Алертить надо НЕ на «упал один запрос», а на скорость прожигания бюджета через **multi-window multi-burn-rate** алерты (быстрый + медленный burn). Это даёт высокий precision и recall и убирает шум. - SLI выбирают по принципу: метрика должна отражать опыт пользователя, быть измеримой на границе, где пользователь «видит» сервис, и иметь чёткий критерий good/bad. ## Теория ### Зачем вообще SLO, а не «аптайм 100%» 100% надёжности — недостижимая и вредная цель: каждый «девятка» удорожает систему экспоненциально, а пользователь всё равно ограничен надёжностью своего Wi-Fi, DNS, браузера. SLO — это инженерный компромисс: формальное согласие команды и бизнеса о том, какой уровень ненадёжности приемлем. Из этого согласия рождается **error budget** — единый язык для конфликта «фичи vs стабильность». Пока бюджет есть — катим релизы; бюджет исчерпан — фриз фич и работа над надёжностью. ### SLI: что именно измеряем SLI — отношение «хороших» событий к «валидным» за окно: ``` SLI = good_events / valid_events × 100% ``` «Valid» — это события, которые в принципе должны учитываться (исключаем, например, запросы от ботов или health-чеки). «Good» — соответствующие критерию качества. #### Типы SLI | Тип SLI | Что измеряет | Пример good/valid | |---|---|---| | Availability | доля успешных запросов | `2xx,3xx,4xx-кроме-429 / все запросы` | | Latency | доля быстрых запросов | `запросы < 300ms / все запросы` | | Quality / Correctness | корректность ответа | `ответы без деградации / все` | | Freshness | свежесть данных (батчи, репликация) | `записи моложе N мин / все` | | Coverage | доля обработанных данных | `обработанные записи / все поступившие` | | Throughput / Durability | для очередей, хранилищ | `недропнутые сообщения / все` | Ключевой момент для senior: **latency SLI — это НЕ среднее и НЕ p99 само по себе, а доля запросов в пределах порога**. То есть «доля запросов быстрее 300ms ≥ 99%», а не «p99 ≤ 300ms». Так SLI остаётся в формате `good/valid` и складывается в error budget. Часто используют два порога (например, 99% быстрее 300ms И 99.9% быстрее 1s), чтобы ловить и типичный опыт, и хвост. #### Где измерять SLI измеряют как можно ближе к пользователю, но в точке, которую вы контролируете: - **Load balancer / ingress / API gateway** — лучший компромисс: видит реальный трафик, не зависит от того, дошёл ли запрос до приложения. Минус — не видит проблем DNS/CDN до балансировщика. - **На сервере (внутри приложения)** — проще инструментировать, но не учитывает запросы, которые умерли до входа (перегруженный LB, отказ соединения). - **На клиенте (RUM)** — самый честный опыт, но шумно (сеть пользователя) и трудно отделить вашу вину. Senior-ответ: для большинства request/response SLI берут метрики с балансировщика; для критичного UX добавляют клиентский RUM как отдельный SLI. ### SLO: формулировка и окна SLO = SLI + целевое значение + окно измерения: > «99.9% HTTP-запросов к API за rolling 28 дней возвращают не-5xx за < 500ms.» #### Rolling window vs calendar window - **Rolling (скользящее, напр. последние 28/30 дней)** — отражает текущее здоровье, не «прощает» в начале нового месяца. Лучше для внутренних SLO и алертинга. - **Calendar (месяц/квартал)** — совпадает с биллинг-циклом, удобно для SLA-отчётности; но в начале периода бюджет «сбрасывается» и команда может расслабиться. 28 дней (а не 30) часто выбирают, чтобы окно всегда содержало одинаковое число выходных — убирает недельную сезонность из графика. #### Сколько «девяток» = сколько бюджета | SLO | Допустимый downtime/мес (~30 дней) | Бюджет ошибок (доля) | |---|---|---| | 99% (two nines) | ~7.2 часа | 1% | | 99.9% (three nines) | ~43 мин | 0.1% | | 99.95% | ~21.6 мин | 0.05% | | 99.99% (four nines) | ~4.3 мин | 0.01% | | 99.999% (five nines) | ~26 сек | 0.001% | Важно: «downtime» — это эквивалент в непрерывном простое; реально бюджет тратится размазанно (фоновые ошибки + всплески). ### Error budget ``` error_budget (доля) = 1 − SLO error_budget (события)= valid_events × (1 − SLO) budget_consumed = bad_events / (valid_events × (1 − SLO)) budget_remaining = 1 − budget_consumed ``` **Error budget policy** — заранее согласованный документ: что происходит при разных уровнях бюджета. Типичный пример: - Бюджет > 0 → релизы идут как обычно. - Бюджет потрачен (< 0) → freeze новых фич, все силы на надёжность, обязательный postmortem. - Бюджет тратится подозрительно быстро → ревью рисковых изменений. Политика — самая важная часть, потому что без согласованных последствий SLO превращается в график, на который никто не смотрит. ### Alerting на burn rate Главная идея: алертить не на нарушение SLI в моменте, а на **скорость прожигания error budget**. **Burn rate** = во сколько раз быстрее, чем «равномерно», тратится бюджет. ``` burn_rate = (error_rate за окно) / (1 − SLO) ``` - Burn rate = 1 → при такой скорости бюджет на весь период истратится ровно к концу окна SLO. - Burn rate = 10 → бюджет 30-дневного SLO сгорит за 3 дня. - Burn rate = 14.4 → за окно 1 час сгорит ~2% месячного бюджета. #### Сколько бюджета сгорает Доля бюджета, израсходованная за окно длиной `alert_window`: ``` budget_burned = burn_rate × (alert_window / SLO_window) ``` Например, burn_rate=14.4 за 1ч при 30-дневном SLO: `14.4 × (1h / 720h) = 2%` бюджета. #### Почему простые алерты плохи | Подход | Проблема | |---|---| | Алерт «error rate > X% за 5 мин» | Низкий precision: шумит на любом всплеске, не связан с бюджетом | | Алерт «потрачено N% бюджета» (только) | Низкий recall к скорости: медленная утечка не триггерит, а быстрая катастрофа триггерит слишком поздно | | Один длинный window | Долгий time-to-detect для острых инцидентов | | Один короткий window | Шум, флапы, ложные срабатывания | #### Multi-window, multi-burn-rate (рекомендация SRE Workbook) Комбинируем несколько алертов с разными парами (burn rate, окно). Канонический набор для 30-дневного SLO: | Severity | Long window | Short window | Burn rate | % бюджета за long window | |---|---|---|---|---| | Page (срочно) | 1 час | 5 мин | 14.4 | 2% | | Page | 6 часов | 30 мин | 6 | 5% | | Ticket (не срочно) | 1 день | 2 часа | 3 | 10% | | Ticket | 3 дня | 6 часов | 1 | 10% | Зачем **два** окна (long + short) на алерт: - **Long window** даёт высокий precision: проблема действительно значима (потрачено заметно бюджета). - **Short window** даёт быстрый **reset / restore**: алерт быстро гаснет, когда инцидент закончился, а не висит ещё час. Short также подтверждает, что проблема всё ещё происходит сейчас, а не «выгорела» в начале long-окна. Алерт срабатывает, когда **оба** окна превышают порог burn rate: ``` burn_rate(long_window) > threshold AND burn_rate(short_window) > threshold ``` Short-окно обычно = 1/12 от long-окна. #### Пример на PromQL SLI на recording rules, считаем долю ошибок по multi-window: ```promql # Записываем error ratio за разные окна (recording rule) job:slo_errors_per_request:ratio_rate5m = sum(rate(http_requests_total{code=~"5.."}[5m])) / sum(rate(http_requests_total[5m])) job:slo_errors_per_request:ratio_rate1h = sum(rate(http_requests_total{code=~"5.."}[1h])) / sum(rate(http_requests_total[1h])) # Page-алерт: быстрый burn (14.4x), окна 1h + 5m, SLO 99.9% → бюджет 0.001 - alert: ErrorBudgetBurnFast expr: | job:slo_errors_per_request:ratio_rate1h > (14.4 * 0.001) and job:slo_errors_per_request:ratio_rate5m > (14.4 * 0.001) labels: { severity: page } # Ticket-алерт: медленный burn (3x), окна 1d + 2h - alert: ErrorBudgetBurnSlow expr: | job:slo_errors_per_request:ratio_rate1d > (3 * 0.001) and job:slo_errors_per_request:ratio_rate2h > (3 * 0.001) labels: { severity: ticket } ``` Latency-SLI через гистограмму (доля медленных запросов): ```promql # good = быстрее 300ms; считаем долю МЕДЛЕННЫХ как (1 - доля быстрых) 1 - ( sum(rate(http_request_duration_seconds_bucket{le="0.3"}[1h])) / sum(rate(http_request_duration_seconds_count[1h])) ) > (14.4 * 0.001) ``` > Важно: `le="0.3"` должен реально присутствовать как граница бакета в гистограмме, иначе придётся интерполировать. Выбирайте бакеты под пороги SLO заранее. #### Go: инструментирование SLI ```go var ( httpRequests = prometheus.NewCounterVec( prometheus.CounterOpts{Name: "http_requests_total"}, []string{"code", "method", "route"}, ) httpDuration = prometheus.NewHistogramVec( prometheus.HistogramOpts{ Name: "http_request_duration_seconds", // бакеты подобраны под пороги SLO: 300ms и 1s Buckets: []float64{0.05, 0.1, 0.2, 0.3, 0.5, 1, 2, 5}, }, []string{"route"}, ) ) func sloMiddleware(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { start := time.Now() rec := &statusRecorder{ResponseWriter: w, status: 200} next.ServeHTTP(rec, r) route := routePattern(r) // нормализованный шаблон, НЕ raw path (кардинальность!) httpRequests.WithLabelValues( strconv.Itoa(rec.status), r.Method, route, ).Inc() httpDuration.WithLabelValues(route).Observe(time.Since(start).Seconds()) }) } ``` Ключевое для senior: метки нормализуются по **route pattern** (`/users/{id}`), иначе высокая кардинальность убьёт Prometheus. И 4xx, как правило, исключают из «bad» для availability-SLI (это вина клиента), кроме 429/некоторых 408. ### Как выбирать SLI: практический процесс 1. Определите **user journeys** (критичные сценарии): «открыть ленту», «оформить заказ», «получить ответ API». 2. Для каждого journey выберите 1–3 SLI (обычно availability + latency; для данных — freshness/correctness). 3. Сформулируйте good/valid в формате событий, привязанных к точке измерения. 4. Поставьте достижимое SLO: начните с измерения текущего поведения за 4 недели, поставьте чуть ниже фактического p, ужесточайте итеративно. 5. Согласуйте error budget policy с продуктом и руководством. 6. Настройте multi-burn-rate алерты и дашборд с остатком бюджета. Меньше SLI — лучше: 1–3 на сервис. Каждый лишний SLI размывает фокус и создаёт шум. ## Подводные камни / gotchas - **Latency SLI как p99, а не как доля** — p99 нельзя усреднять и складывать в бюджет; используйте «долю запросов под порогом». - **Усреднение перцентилей между инстансами** — `avg(p99)` математически бессмысленно. Считайте перцентили из гистограмм через `histogram_quantile` по агрегированным бакетам, а не усредняя готовые квантили. - **Бакеты гистограммы не совпадают с порогом SLO** — если SLO про 300ms, а ближайший бакет 250ms/500ms, доля считается неточно. Закладывайте границы бакетов под пороги заранее. - **Высокая кардинальность меток** (raw URL, user_id, request_id в метках) — взрывает TSDB. Только нормализованные route/method/code. - **Включение 4xx в bad для availability** — раздувает мнимые отказы; обычно 4xx (кроме 429/408) — это валидный «good» с точки зрения доступности сервиса. - **Алерт на «потрачено N% бюджета» без burn rate** — острый инцидент задетектится слишком поздно (бюджет уже сгорит), медленная утечка — вообще не задетектится вовремя. - **Один single-window алерт** — либо шумит (короткое окно), либо медленно детектит (длинное). Нужен multi-window. - **SLO выше реального опыта пользователя** — измерять availability на сервере при том, что половина запросов умирает на перегруженном LB; SLI зелёный, пользователи злые. - **SLA жёстче или равен SLO** — нет запаса прочности; любой внутренний инцидент сразу превращается в выплату компенсаций. SLA должен быть слабее SLO. - **Calendar reset расхолаживает** — в начале месяца бюджет «полный», команда катит рискованные релизы; rolling window честнее. - **SLO без error budget policy** — это просто график, который игнорируют. Без согласованных последствий цель не работает. - **Двойной учёт ретраев** — если клиент ретраит, один пользовательский запрос = N серверных; SLI на серверной стороне исказит реальный опыт. - **Зависимые сервисы и композиция SLO** — последовательная цепочка из 3 сервисов по 99.9% даёт ~99.7% сквозного; SLO нельзя «наследовать» наивно. ## Вопросы на собеседовании **В:** В чём разница между SLI, SLO и SLA и как они связаны? **О:** SLI — измеримая метрика опыта пользователя (`good/valid`). SLO — внутренняя цель по SLI на окне (99.9% за 28 дней). SLA — внешнее контрактное обязательство с компенсациями. Связь: SLI измеряем, по нему ставим SLO, SLA делаем строго слабее SLO (запас прочности). Из SLO выводим error budget = 1−SLO. **В:** Почему latency-SLI правильно формулировать как «долю быстрых запросов», а не как p99? **О:** Чтобы SLI оставался в формате `good/valid` и складывался в единый error budget. Перцентили нельзя усреднять между инстансами/окнами и нельзя напрямую конвертировать в бюджет ошибок. «Доля запросов < 300ms ≥ 99%» — это счётчик хороших событий, которым можно оперировать как и availability. **В:** Что такое error budget и зачем он нужен? **О:** `1 − SLO` — допустимая доля «плохого» за окно. Это объективный, согласованный бизнесом ресурс на риск: пока бюджет есть — катим фичи, эксперименты, рискованные релизы; кончился — freeze и работа над надёжностью. Превращает спор «скорость vs стабильность» в арифметику вместо политики. **В:** Что такое burn rate и как считать порог для алерта? **О:** Burn rate = `error_rate / (1−SLO)` — во сколько раз быстрее равномерного тратится бюджет. BR=1 истратит весь бюджет ровно к концу окна SLO. Для алерта выбираем, какой процент бюджета готовы потерять за время детекта: `budget_burned = burn_rate × (alert_window / SLO_window)`. Напр., BR=14.4 за 1ч = 2% месячного бюджета. **В:** Почему рекомендуют multi-window multi-burn-rate алерты? **О:** Длинное окно даёт precision (проблема значима, потрачено реально много), короткое окно — быстрый reset (алерт гаснет сразу после конца инцидента) и подтверждение, что проблема происходит сейчас. Несколько пар burn rate/окно покрывают и острые инциденты (быстрый page), и медленные утечки (ticket). Это даёт хорошие precision, recall и time-to-detect одновременно. **В:** Где измерять SLI и какие тут компромиссы? **О:** Ближе к пользователю, но в контролируемой точке. LB/ingress — золотая середина: видит реальный трафик, не зависит от того, дошёл ли запрос до приложения. На сервере — проще, но не видит запросов, умерших до входа. На клиенте (RUM) — честнее всего, но шумно из-за сети пользователя. Часто комбинируют LB-SLI + клиентский RUM как отдельный индикатор. **В:** Как выбрать значение SLO для нового сервиса? **О:** Измерить фактическое поведение за ~4 недели, поставить SLO чуть ниже наблюдаемого p (чтобы он был достижим, но не тривиален), согласовать error budget policy, затем итеративно ужесточать. Нельзя ставить 100% и нельзя ставить «с потолка» выше, чем сервис реально способен держать — иначе бюджет постоянно в минусе и алерты обесцениваются. **В:** Чем rolling window отличается от calendar window и что выбрать? **О:** Rolling (последние N дней) отражает текущее здоровье и не «сбрасывает» бюджет на стыке месяцев — лучше для алертинга. Calendar совпадает с биллинг-циклом — удобно для SLA-отчётности, но расхолаживает в начале периода. На практике: rolling для внутренних SLO/алертов, calendar для контрактной отчётности. ## На что копают на senior+ - **Математика композиции SLO**: как сквозной SLO зависит от цепочки зависимостей (последовательное умножение надёжностей, влияние параллелизма/ретраев/фолбэков), как ставить SLO компонентам, чтобы выдержать целевой пользовательский SLO. - **Статистическая корректность перцентилей**: почему `avg(p99)` неверно, как `histogram_quantile` работает на агрегированных бакетах, ошибка интерполяции при неудачных границах бакетов, ограничения классических vs native/exponential histograms в Prometheus. - **Дизайн error budget policy** как организационного инструмента: кто владелец, что именно происходит при исчерпании, как избежать gaming (подкрутка SLO под факт), связь с release-процессом и postmortem-культурой. - **Тонкая настройка multi-burn-rate**: выбор конкретных пар (rate, window), trade-off precision/recall/time-to-detect/reset-time, влияние low-traffic сервисов (мало событий → шумный SLI, нужны min-traffic guards или агрегация). - **SLI для не-request/response систем**: батчи (freshness, coverage), стримы/очереди (lag, throughput, durability), асинхронные пайплайны — где «valid event» неочевиден. - **Учёт ретраев и идемпотентности** в SLI: как не считать один пользовательский запрос за N, разница user-perceived vs server-side reliability. - **Связь SLO с capacity/autoscaling и нагрузочным тестированием**: как latency-SLO задаёт точку насыщения и триггеры масштабирования. - **Инструменты SLO-as-code**: Sloth, OpenSLO, Pyrra — генерация recording/alerting rules из декларативного SLO-спека, версионирование SLO в Git.