Context и жизненный цикл Go-сервиса
context.Context переносит сигнал отмены, дедлайн и небольшой request-scoped metadata по дереву вызовов. Он не отменяет работу магически: каждая goroutine и каждый I/O API должны наблюдать Done или принимать ctx. Вместе с явным владением ресурсами это задаёт жизненный цикл запроса, фоновой работы и процесса.
Зачем это на интервью
Тема проверяет не знание четырёх конструкторов, а способность объяснить, почему запрос не продолжает тратить CPU, соединения и деньги после disconnect клиента, как не превратить один общий timeout в каскад ошибок и как безопасно остановить сервис.
Минимум для E4
- Передавать
ctxпервым параметром функции и не хранить его в struct. - Вызывать
cancelу каждого созданного derived context, включая успешный путь. - Различать отмену вызывающей стороны, deadline и собственную ошибку операции через
ctx.Err()/context.Cause. - Останавливать goroutine через
selectсctx.Done()и закрывать принадлежащие ресурсы. - Назвать порядок graceful shutdown: прекратить ingress, дать время активной работе, остановить workers, закрыть зависимости.
Углубление для E5/Senior
E5 проектирует бюджет времени по критическому пути, не увеличивает дедлайн дочернего вызова и отделяет request-scoped работу от независимой durable-задачи. Он определяет ownership каналов, Close и Wait, измеряет число goroutine/очереди/in-flight requests и заранее выбирает политику forced shutdown.
Карта тем
- Основы Context и propagation — контракт, корни и границы API.
- Отмена, дедлайны и причины — constructors, причины и обработка ошибок.
- Бюджеты дедлайнов — latency budget и ретраи.
- Значения Context — допустимый metadata и анти-паттерны.
- Утечки goroutine и ресурсов — ownership, диагностика и backpressure.
- Завершение сервиса — HTTP/gRPC, consumers и фоновые workers.
Типовые вопросы
- Почему
contextне равен механизму принудительной остановки?- Он лишь закрывает
Done; функция должна cooperatively выйти, а API — поддерживать отмену.
- Он лишь закрывает
- Кто вызывает
cancel?- Тот, кто создал derived context; это освобождает timer и связь родитель—ребёнок раньше дедлайна.
- Можно ли передать
context.Background()в библиотечный вызов?- Только на осознанной process-level границе; внутри request path это отрывает работу от отмены и бюджета.
- Что делает shutdown после timeout?
- Прекращает ожидание согласно политике процесса, логирует незавершённую работу и допускает принудительное завершение supervisor’ом.
- Почему нельзя передавать зависимости через values?
- Сигнатура скрывает обязательную зависимость, теряется типовой контракт и lifetime становится неясным.
Практика
- Соберите HTTP endpoint → service → repository с единым
ctx.- Критерии приёмки: disconnect или test cancellation прекращает все уровни; логи различают cancel и deadline;
go test -raceпроходит.
- Критерии приёмки: disconnect или test cancellation прекращает все уровни; логи различают cancel и deadline;
- Реализуйте root context от SIGTERM и worker pool.
- Критерии приёмки: ingress прекращается до закрытия dependency; workers завершаются или фиксируются как timeout; повторный сигнал имеет определённую политику.
Частые ошибки и ловушки
- Использовать
context.TODO()как постоянный способ игнорировать контракт отмены. - Создавать timeout в нижнем слое без бюджета вызывающей операции.
- Закрывать канал со стороны получателя или отменять context вместо ожидания завершения workers.
- Считать
Server.Shutdownостановкой Kafka/RabbitMQ consumer или произвольной goroutine.
Связанные темы
Go: язык, runtime и стандартная библиотека · Unix-сигналы · Наблюдаемость · System Design
Источники
- Go
context— контрактContext, constructors и причины отмены. - Go blog: Go Concurrency Patterns: Context — исходная модель propagation.
- Go blog: Pipelines and cancellation — завершение pipeline без утечек.
- Go
net/http.Serverи gRPC graceful shutdown — shutdown серверов.