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/ > Модуль: Runtime и память · Уровень: Senior+ ## TL;DR `GOMAXPROCS` задаёт число `P` (logical processors) в планировщике Go — максимальное количество горутин, выполняющихся на CPU параллельно. Исторически дефолт равнялся числа CPU хоста (`runtime.NumCPU()`), что в контейнерах с cgroup-лимитом приводило к oversubscription: Go видит, скажем, 64 ядра хоста, а квота — 2 ядра, отсюда лишние context switches, раздутый GC и хвостовая латентность. Решали это библиотекой `automaxprocs` от Uber; в Go 1.25 runtime стал cgroup-aware и читает CPU-квоту автоматически, периодически её обновляя. ## Теория ### P, M, G — где здесь GOMAXPROCS Планировщик Go строится на трёх сущностях: - **G** (goroutine) — горутина. - **M** (machine) — OS-тред. - **P** (processor) — логический процессор, контекст для выполнения; владеет локальной очередью runnable-горутин. `GOMAXPROCS` == число `P`. Чтобы M выполнял Go-код, он должен держать P. Значит **не более `GOMAXPROCS` горутин выполняют Go-код на CPU одновременно**. Это и есть граница реального параллелизма. ``` G G G G (горутины) \ \ / / P P GOMAXPROCS = 2 -> максимум 2 параллельно | | M M (OS-треды, их может быть больше при syscalls) ``` ### Параллелизм vs конкурентность - **Конкурентность** — структура программы: много горутин, логически независимых. Их можно иметь миллионы при `GOMAXPROCS=1`. - **Параллелизм** — физическое одновременное исполнение. Ограничено `GOMAXPROCS` и числом физических ядер. `GOMAXPROCS=1` не мешает писать конкурентный код — он лишь сериализует фактическое исполнение Go-кода (горутины кооперативно вытесняются). Это «знаменитый» тезис Роба Пайка: concurrency is not parallelism. ### Управление значением ```go import "runtime" n := runtime.GOMAXPROCS(0) // 0 = прочитать текущее, не меняя old := runtime.GOMAXPROCS(4) // установить 4, вернёт прежнее значение ``` ```bash GOMAXPROCS=4 ./app # через переменную окружения (имеет приоритет) ``` Приоритет: env `GOMAXPROCS` (если задана и валидна) → программный вызов → дефолт runtime. ### Почему важно для GC и латентности - **GC** использует worker-горутины пропорционально `GOMAXPROCS` (≈25% от P на dedicated mark workers). Завышенный GOMAXPROCS в контейнере => больше GC-воркеров, чем выделено ядер => они конкурируют за квоту, GC-паузы и assist-время растут. - **Хвостовая латентность (p99)**: при oversubscription планировщик ОС вынужден дёргать множество M на малом числе ядер => лишние context switches, потеря L1/L2-кэша, throttling по cgroup-квоте (контейнер «замораживается» до конца периода). Это бьёт именно по хвосту. ### Проблема контейнеров (суть) До Go 1.25 дефолт был `runtime.NumCPU()`, который возвращал число **онлайн-CPU хоста**, игнорируя cgroup CPU-квоту. Типичная картина в Kubernetes: ```yaml resources: limits: cpu: "2" # cgroup quota: 200000 / 100000 = 2 CPU # но узел имеет 64 ядра ``` Go при старте видел 64 => `GOMAXPROCS=64`. Последствия: - 64 потенциально активных P конкурируют за эффективные 2 ядра квоты. - При исчерпании квоты в периоде CFS контейнер **throttling** — все треды замораживаются до следующего периода (100мс), что даёт скачки латентности. - GC-воркеров слишком много, они усиливают throttling. - Лишние переключения контекста, размывание кэша. ### cgroups: где живёт лимит **cgroup v1** (CPU controller): ```bash # Период и квота (в микросекундах): cat /sys/fs/cgroup/cpu/cpu.cfs_period_us # обычно 100000 (100мс) cat /sys/fs/cgroup/cpu/cpu.cfs_quota_us # например 200000 => 2 CPU # Эффективный лимит = quota / period ``` **cgroup v2** (унифицированная иерархия): ```bash cat /sys/fs/cgroup/cpu.max # "200000 100000" => quota period => 2 CPU # "max 100000" => без лимита ``` Расчёт эффективного числа CPU: `ceil(quota / period)` (округление вверх). Квота `200000/100000` = 2.0 CPU. Дробная квота `150000/100000` = 1.5 округляется до 2. Важно: **CPU `requests`** (cgroup `cpu.shares` / `cpu.weight`) — это только вес при контеншене, НЕ жёсткий лимит, его для GOMAXPROCS не используют. Используется именно **limit** (quota). ### Решение до 1.25: uber-go/automaxprocs ```go import _ "go.uber.org/automaxprocs" // init() выставит GOMAXPROCS из cgroup-квоты ``` В `init()` библиотека читает `cpu.cfs_quota_us`/`cpu.max`, вычисляет `floor(quota/period)` (минимум 1) и вызывает `runtime.GOMAXPROCS`. Минусы: статично (читает один раз при старте), не реагирует на vertical scaling/изменение лимита в рантайме, использует floor (а не ceil), и это внешняя зависимость. ```bash # Контроль из лога automaxprocs: # "maxprocs: Updating GOMAXPROCS=2: determined from CPU quota" ``` ### Go 1.25: контейнер-aware runtime Начиная с Go 1.25 рантайм сам учитывает cgroup CPU-лимит при определении дефолтного `GOMAXPROCS`: - При старте дефолт = `max(минимум(round(cgroup CPU limit), NumCPU), 2)`-подобная логика: берётся минимум из числа CPU машины и CPU-лимита cgroup (округление вверх), но не меньше 1 (на практике учитывается дробная квота). - Рантайм **периодически (примерно раз в секунду) перечитывает лимит** и обновляет GOMAXPROCS «на лету» при vertical scaling/изменении квоты — чего automaxprocs не умел. - Явно заданная env `GOMAXPROCS` или вызов `runtime.GOMAXPROCS()` отключают автоматику (ваше значение имеет приоритет и фиксируется). - Поведение можно регулировать через `GODEBUG`: `containermaxprocs=0` отключает учёт cgroup-лимита, `updatemaxprocs=0` отключает периодическое обновление (для возврата к старому поведению/совместимости). Это делает `automaxprocs` ненужным на Go 1.25+ для большинства случаев. | | до Go 1.25 (дефолт) | automaxprocs | Go 1.25+ | |---|---|---|---| | Источник | NumCPU хоста | cgroup quota | cgroup quota | | Учёт лимита | нет | да (floor) | да (round, динамически) | | Обновление в рантайме | нет | нет | да (~1/сек) | | Зависимость | — | внешняя | встроено | ### Как тюнить 1. На Go 1.25+ — обычно ничего, дефолт уже корректен; убедитесь, что не задана лишняя env `GOMAXPROCS`. 2. На Go <1.25 в контейнере — `automaxprocs` или явная установка под лимит. 3. Latency-критичные сервисы: иногда выгодно установить GOMAXPROCS чуть выше CPU-limit (если есть burst-квота) или, наоборот, прибить к requests, чтобы избежать throttling. 4. Замеряйте: cgroup throttling-метрики (`nr_throttled`, `throttled_time` из `cpu.stat`), p99 latency, GC-метрики до/после изменения. ```bash # Сколько раз нас тормозил CFS (cgroup v2): cat /sys/fs/cgroup/cpu.stat # nr_throttled, throttled_usec — рост = надо снижать GOMAXPROCS или поднимать limit ``` ## Подводные камни / gotchas - **CPU requests != limits.** GOMAXPROCS привязывается к limit (quota), а не к requests (shares). Если лимит не задан вовсе — в Go 1.25 поведение как раньше (NumCPU). - **Дробная квота.** Лимит `cpu: "1500m"` (1.5 CPU): automaxprocs возьмёт floor=1, Go 1.25 округлит иначе. Понимать, какое значение реально применилось (логировать `runtime.GOMAXPROCS(0)` при старте). - **Env `GOMAXPROCS` отключает автоматику Go 1.25.** Случайно унаследованная переменная в манифесте сводит на нет container-awareness. - **Throttling маскируется под «медленный код».** p99-скачки кратные 100мс (период CFS) — почти всегда throttling, а не GC и не ваш код. - **CGO/блокирующие syscalls плодят M, не P.** Большое число тредов в `threadcreate`-профиле при малом GOMAXPROCS — это про syscalls, а не про параллелизм Go-кода. - **`GOMAXPROCS=1` не делает код «однопоточным» полностью** — рантайм всё равно создаёт служебные M (sysmon, GC, syscalls). - **Динамическое обновление в 1.25** может удивить, если вы делали capacity-расчёты по фиксированному GOMAXPROCS; при vertical pod autoscaling значение меняется в рантайме. ## Вопросы на собеседовании **В:** Что физически означает GOMAXPROCS и как он связан с моделью P-M-G? **О:** Это число `P` — логических процессоров планировщика. M (OS-тред) обязан владеть P, чтобы исполнять Go-код, поэтому одновременно на CPU выполняется не более GOMAXPROCS горутин. Это верхняя граница реального параллелизма Go-кода; конкурентность (число горутин) при этом не ограничена. **В:** Почему в Kubernetes старый Go-сервис деградировал по p99 при дефолтном GOMAXPROCS? **О:** До 1.25 дефолт = NumCPU хоста, игнорируя cgroup-лимит. На 64-ядерном узле с лимитом 2 CPU рантайм запускал 64 P и пропорционально много GC-воркеров, которые конкурировали за 2 ядра квоты. Это вызывало лишние context switches, размывание кэша и CFS-throttling (заморозка до конца 100мс-периода) — скачки именно в хвосте latency. **В:** Чем cgroup v1 и v2 отличаются в части CPU-лимита? **О:** В v1 лимит задаётся двумя файлами: `cpu.cfs_quota_us` и `cpu.cfs_period_us`, эффективный CPU = quota/period. В v2 это один файл `cpu.max` в формате `" "`, либо `"max"` при отсутствии лимита. Семантика та же — отношение квоты к периоду. **В:** Что делает automaxprocs и какие у неё ограничения? **О:** В `init()` читает cgroup CPU-квоту, вычисляет `floor(quota/period)` (минимум 1) и устанавливает GOMAXPROCS. Ограничения: читает один раз при старте (не реагирует на изменение лимита в рантайме), использует floor, и это внешняя зависимость. На Go 1.25+ обычно избыточна. **В:** Что нового принёс Go 1.25 в этой области? **О:** Рантайм стал cgroup-aware: дефолтный GOMAXPROCS вычисляется с учётом CPU-лимита cgroup (округление вверх, не меньше 1) и периодически (~раз в секунду) перечитывается и обновляется на лету. Управляется через GODEBUG `containermaxprocs` и `updatemaxprocs`. Явная env/вызов отключают автоматику. **В:** Используется ли для GOMAXPROCS CPU requests или limits? **О:** Limits (cgroup quota — жёсткий потолок). Requests маппятся в `cpu.shares`/`cpu.weight` — это лишь относительный вес при контеншене, не потолок, поэтому для определения параллелизма он не годится. Если limit не задан, runtime откатывается к числу CPU. **В:** Как GOMAXPROCS влияет на сборщик мусора? **О:** GC mark-фаза использует dedicated worker-горутины числом ≈25% от GOMAXPROCS, плюс mutator assist. Завышенный GOMAXPROCS относительно реальной квоты => больше GC-воркеров на тех же ядрах => усиление throttling и assist, рост пауз/латентности. Поэтому корректный GOMAXPROCS важен и для GC, а не только для прикладного кода. **В:** Как диагностировать CFS-throttling и подтвердить, что проблема в GOMAXPROCS? **О:** Смотреть `cpu.stat` (`nr_throttled`, `throttled_usec`) — рост означает, что контейнер упирается в квоту. Сопоставить с GOMAXPROCS (логировать `runtime.GOMAXPROCS(0)` при старте) и числом ядер квоты. Скачки latency, кратные периоду CFS (100мс), — характерный признак. Снижение GOMAXPROCS до лимита (или Go 1.25) обычно убирает throttling. **В:** Можно ли поставить GOMAXPROCS больше CPU-лимита и зачем? **О:** Иногда да: при наличии burst-квоты или IO-bound нагрузке небольшое превышение повышает throughput за счёт перекрытия ожиданий. Но риск — рост throttling и хвостовой латентности. Это всегда вопрос замеров под конкретный профиль нагрузки, а не дефолтная рекомендация. ## На что копают на senior+ - Точное понимание P-M-G и того, что GOMAXPROCS лимитирует именно исполнение Go-кода на CPU, а не число тредов (syscalls плодят M сверх GOMAXPROCS). - Глубокое знание cgroup v1/v2, формулы `quota/period`, разницы requests/limits и CFS-throttling как механизма (период, заморозка, метрики `cpu.stat`). - Связь GOMAXPROCS ↔ GC (доля mark-воркеров, assist) и влияние на p99. - Эволюция решения: ручная установка → automaxprocs (floor, статично) → Go 1.25 (round, динамическое обновление, GODEBUG-флаги) и понимание приоритетов (env vs вызов vs дефолт). - Практика тюнинга под latency-SLO: когда привязывать к limit, когда к requests, как доказывать эффект изменения метриками, риски динамического обновления при VPA.