Ответ
В Go 1.21 был добавлен механизм для указания и распространения причины (cause) отмены контекста. Ранее ctx.Err()
мог вернуть только context.Canceled
или context.DeadlineExceeded
, не давая информации о том, почему произошла отмена.
Теперь можно передать конкретную ошибку, которая будет являться причиной отмены. Для этого были добавлены три новые функции:
WithCancelCause(parent Context) (ctx Context, cancel func(cause error))
Создает контекст, который можно отменить, передав причину в функциюcancel
.WithDeadlineCause(parent Context, d time.Time, cause error) (Context, CancelFunc)
АналогWithDeadline
, но позволяет сразу указать причину, которая будет использована при истечении дедлайна.WithTimeoutCause(parent Context, timeout time.Duration, cause error) (Context, CancelFunc)
АналогWithTimeout
, но с возможностью указать причину для таймаута.
Для получения этой причины используется новая функция context.Cause(ctx) error
.
Пример:
package main
import (
"context"
"errors"
"fmt"
"time"
)
func main() {
// Задаем кастомную ошибку как причину
processingErr := errors.New("processing took too long")
// Создаем контекст с таймаутом и причиной
ctx, cancel := context.WithTimeoutCause(
context.Background(),
time.Second,
processingErr,
)
defer cancel()
// Имитируем операцию, которая длится дольше таймаута
select {
case <-time.After(2 * time.Second):
fmt.Println("operation completed successfully")
case <-ctx.Done():
// ctx.Err() вернет стандартную ошибку
fmt.Printf("Context done. Error: %vn", ctx.Err()) // -> context deadline exceeded
// context.Cause() вернет нашу кастомную ошибку
cause := context.Cause(ctx)
fmt.Printf("Cause of cancellation: %vn", cause) // -> processing took too long
// Мы можем проверить, что причина - это именно наша ошибка
if errors.Is(cause, processingErr) {
fmt.Println("The cause is the expected processing error.")
}
}
}
Практическая польза:
- Улучшенная отладка и логирование: Вместо общего сообщения об отмене можно логировать конкретную причину, что упрощает анализ проблем.
- Гранулярная обработка ошибок: Появляется возможность по-разному реагировать на разные причины отмены. Например, отмена по таймауту может требовать повторной попытки, а отмена из-за ошибки валидации — немедленного прекращения.
- Взаимодействие в распределенных системах: При отмене запроса в микросервисной архитектуре можно передать осмысленную ошибку через цепочку вызовов, а не просто сигнал отмены.