Sobes.tech
Junior — Senior

Adding retry mechanism in DistributiveGet

livecode

Task condition

It is necessary to improve the DistributiveGet function by implementing retries upon any error except ErrNotFound, until the value is obtained.

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
    }
}