Есть функция readOne, которая загружает данные одного клиента (~15 мс). Её
трогать нельзя. Нужно реализовать Fetch: загрузить данные всех клиентов так,
чтобы всё укладывалось примерно в 15 мс, а при первой же ошибке сразу
вернуть её, не дожидаясь остальных.
type Client struct {
ID string
}
// уже объявлена, не трогаем
var readOne = func(ctx context.Context, c Client) (string, error) {
time.Sleep(15 * time.Millisecond)
return c.ID, nil
}
func Fetch(ctx context.Context, clients []Client) (map[string]string, error)
Требования
- Все
readOne запускаются одновременно. Результат — map[ID]данные.
(В исходнике функция возвращала map[string]int64, а readOne — строку.
Типы не сходились, поэтому map здесь строковая.)
- Первая ошибка
readOne → Fetch сразу возвращает nil и эту ошибку
(errors.Is её находит), даже если остальные readOne контекст игнорируют и
работают ещё долго.
- После ошибки контекст, переданный остальным
readOne, отменён. Их
ошибки, пришедшие позже (в том числе context.Canceled), первую ошибку не
подменяют.
- Отмена внешнего
ctx → сразу nil, ctx.Err().
- Пустой список → пустая (не
nil) map и nil.
- Утечек нет: когда все
readOne вернутся, от Fetch не остаётся горутин.
Код проходит go test -race.
Пример
res, err := Fetch(ctx, []Client{{"one"}, {"two"}, {"three"}, {"four"}})
// ≈ 15 мс; res = map[four:four one:one three:three two:two], err = nil
Пакет golang.org/x/sync недоступен. Напиши его минимальный аналог:
type Group struct{ /* ... */ }
func WithContext(ctx context.Context) (*Group, context.Context)
func (g *Group) Go(f func() error)
func (g *Group) Wait() error
Go запускает f в новой горутине.
Wait ждёт все запущенные f и возвращает первую по времени ненулевую
ошибку (или nil).
- Если группа создана через
WithContext, возвращённый контекст отменяется
при первой ошибке и, в любом случае, после возврата Wait.
- Нулевое значение
var g Group тоже работает, просто без контекста.
Устно: почему Fetch на errgroup не выполняет требование «сразу вернуть
ошибку», если readOne игнорирует контекст?