Sobes.tech
Junior — Senior
77

Добавление механизма повторных попыток в DistributiveGet

Условие задачи

Необходимо улучшить функцию DistributiveGet, внедрив повторные запросы при возникновении любой ошибки, кроме ErrNotFound, пока значение не будет получено.

func DistributiveGet(ctx context.Context, addresses []string, key string) (string, error) {
    if ctx.Err() != nil {
        return "", ctx.Err()
    }

    var wg sync.WaitGroup
    wg.Add(len(addresses))

    valueCh := make(chan string, 1)
    notFoundErrCh := make(chan error, 1)

    go func() {
        wg.Wait()
        close(valueCh)
        close(notFoundErrCh)
    }()

    for _, address := range addresses {
        go func(address string) {
            defer wg.Done()

            for i := 0; i < 3; i++ {
                value, err := Get(ctx, address, key)

                if errors.Is(err, ErrNotFound) {
                    select {
                    case notFoundErrCh <- err:
                    default:
                    }
                    return
                }

                if err != nil {
                    select {
                    case <-valueCh:
                        return
                    default:
                        time.Sleep(100 * time.Millisecond)
                        continue
                    }
                }

                select {
                case valueCh <- value:
                default:
                }

                return
            }
        }(address)
    }

    select {
    case v, ok := <-valueCh:
        if !ok {
            return "", ErrDistributedGetFailed
        }
        return v, nil
    case err, ok := <-notFoundErrCh:
        if !ok {
            return "", ErrDistributedGetFailed
        }
        return "", err
    }
}