Что происходит с горутиной в Go если функция выполняется долго и возвращает ошибку
Что проверяют
Понимаете ли вы, что горутина ничего не знает про того, кто её запустил: возвращённая ошибка никуда не попадает сама, а долгая работа не прерывается снаружи. По ответу видно, умеете ли вы доводить результат и отмену до вызывающей стороны.
Как отвечать
go f()не возвращает ничего. Значение и ошибка функции просто отбрасываются, компилятор об этом даже не предупредит.- Ошибку нужно явно отдать наружу: канал результата,
sync.WaitGroupплюс общее поле илиerrgroup.Group, гдеg.Wait()вернёт первую ошибку и отменит общий контекст. - Долгая работа снаружи не останавливается: способа убить горутину в языке нет. Отмена кооперативная, то есть в функцию передают
ctx, внутри проверяютctx.Done()либо отдают контекст в библиотеку, которая умеет его слушать (HTTP-клиент, драйвер базы). context.WithTimeoutсам ничего не обрывает: если контекст не доехал до места, где реально ждут, функция доработает до конца.- Паника внутри горутины не ловится снаружи,
recoverработает только в той же горутине и только в отложенной функции. - Когда
mainвозвращается, процесс завершается целиком: недоработавшие горутины умирают вместе с ним, отложенные функции в них не выполняются.
g, ctx := errgroup.WithContext(ctx)
g.Go(func() error { return slowCall(ctx) })
if err := g.Wait(); err != nil {
return err
}Где спотыкаются
Ждут, что контекст с таймаутом «убьёт» горутину. Отмена это только сигнал: вызывающий уйдёт по таймауту, а горутина продолжит занимать соединение и память, пока не доделает работу.
Второе: пишут результат в небуферизованный канал, из которого уже никто не читает. Отправка блокируется навсегда, горутина и всё, что она держит, не собирается сборщиком мусора. Лечится буфером на одно значение или отправкой через select с ctx.Done().
Третье: логируют ошибку внутри горутины и считают, что обработали её. Вызывающий об ошибке не узнает и продолжит работать с пустым результатом, а тест на такой код зелёный, потому что паники нет.