Отправьте запросы всем репликам и возвращайте первый ответ без ошибки. Остальные отмените общим context; ErrNotFound не должен завершать поиск раньше успеха.
Параллельный запрос к нескольким репликам БД
Отправьте запросы всем репликам и возвращайте первый ответ без ошибки. Остальные отмените общим context; ErrNotFound не должен завершать поиск раньше успеха.
Короткий ответ
Что ответить на собеседовании
Подробный разбор
Ответ с пояснениями
Условие
Имеются три адреса реплик базы данных. Необходимо написать функцию, которая одновременно (в параллельных горутинах) запрашивает значение по заданному ключу у каждой из реплик. Как только одна из реплик возвращает корректный результат (значение, отличное от ErrNotFound и без ошибок), функция должна сразу же вернуть полученное значение и отменить ожидание остальных запросов.
127.0.0.1
127.0.0.2
127.0.0.3
Для работы предоставлен следующий метод (его реализация уже есть):
var ErrNotFound = errors.New("not found")
func Get(ctx context.Context, address, key string) (string, error) {
return "", nil // already implemented
}
Ответ
Отправьте запросы всем репликам и возвращайте первый ответ без ошибки. Остальные отмените общим context; ErrNotFound не должен завершать поиск раньше успеха.
Вариант для Go 1.20+ с существующими Get и ErrNotFound:
func GetFirst(ctx context.Context, addresses []string, key string) (string, error) {
ctx, cancel := context.WithCancel(ctx)
defer cancel()
type reply struct { value string; err error }
results := make(chan reply, len(addresses))
for _, address := range addresses {
go func(address string) {
value, err := Get(ctx, address, key)
results <- reply{value, err}
}(address)
}
failures := make([]error, 0, len(addresses))
onlyMissing := true
for range addresses {
select {
case <-ctx.Done():
return "", ctx.Err()
case r := <-results:
if r.err == nil { return r.value, nil }
failures = append(failures, r.err)
if !errors.Is(r.err, ErrNotFound) { onlyMissing = false }
}
}
if onlyMissing { return "", ErrNotFound }
return "", errors.Join(failures...)
}
Нужны context и errors. Буфер позволяет завершить отправку после раннего возврата; отмена реально прервёт Get только при её поддержке. Быстрейшая реплика может быть отставшей: эта стратегия не обеспечивает чтение самой свежей версии. Пустой адресный список здесь возвращает ErrNotFound.