func (p *Proxy) exchange(req *dns.Msg) (*dns.Msg, upstream.Upstream, error) {
	p.rttLock.Lock()
	if len(p.upstreamsWithRtt) == 1 {
		u := p.upstreamsWithRtt[0].upstream
		p.rttLock.Unlock()

		reply, _, err := exchangeWithUpstream(u, req)
		return reply, u, err
	}

	// clone upstreamsWithRtt to avoid race condition while upstreams iteration after slice sort
	clones := make([]upstreamWithRtt, len(p.upstreamsWithRtt))
	copy(clones, p.upstreamsWithRtt)
	p.rttLock.Unlock()

	// sort cloned upstreamsWithRtt by rtt "from fast to slow". idxMap is map of indexes in original and sorted slices
	idxMap := sortUpstreamsWithRtt(clones)

	errs := []error{}
	for i, dnsUpstream := range clones {
		reply, elapsed, err := exchangeWithUpstream(dnsUpstream.upstream, req)

		// set elapsed time for each upstream in original slice to sort them before next exchange
		// initial rtt value for each upstream is 0, so unused servers will be at the top of the list after sorting
		if err == nil {
			p.updateUpstreamRtt(idxMap[i], int(elapsed))
			return reply, dnsUpstream.upstream, nil
		}

		errs = append(errs, err)
		// if there was an error, consider upstream RTT equal to the default timeout (this will set upstream to the last place in upstreamsWithRtt array)
		p.updateUpstreamRtt(idxMap[i], int(defaultTimeout/time.Millisecond))
	}

	return nil, nil, errorx.DecorateMany("all upstreams failed to exchange request", errs...)
}
// exchange tries to exchange the request with all upstreams one-by-one
exchange tries to exchange the request with all upstreams one-by-one
// exchange tries to exchange the request with all upstreams one - by - one
func (p *Proxy) exchange(req *dns.Msg) (*dns.Msg, upstream.Upstream, error) {
	p.rttLock.Lock()
	if len(p.upstreamsWithRtt) == 1 {
		u := p.upstreamsWithRtt[0].upstream
		p.rttLock.Unlock()
		reply, _, err := exchangeWithUpstream(u, req)
		return reply, u, err
	}
	clones := make([]upstreamWithRtt, len(p.upstreamsWithRtt))
	copy(clones, p.upstreamsWithRtt)
	p.rttLock.Unlock()
	idxMap := sortUpstreamsWithRtt(clones)
	errs := []error{}
	for i, dnsUpstream := range clones {
		reply, elapsed, err := exchangeWithUpstream(dnsUpstream.upstream, req)
		if err == nil {
			p.updateUpstreamRtt(idxMap[i], int(elapsed))
			return reply, dnsUpstream.upstream, nil
		}
		errs = append(errs, err)
		p.updateUpstreamRtt(idxMap[i], int(defaultTimeout/time.Millisecond))
	}
	return nil, nil, errorx.DecorateMany("all upstreams failed to exchange request", errs...)
}
func ( p * Proxy ) exchange ( req * dns . Msg ) ( * dns . Msg , upstream . Upstream , error ) { p . rttLock . Lock ( ) if len ( p . upstreamsWithRtt ) == 1 { u := p . upstreamsWithRtt [ 0 ] . upstream p . rttLock . Unlock ( ) reply , _ , err := exchangeWithUpstream ( u , req ) return reply , u , err } clones := make ( [ ] upstreamWithRtt , len ( p . upstreamsWithRtt ) ) copy ( clones , p . upstreamsWithRtt ) p . rttLock . Unlock ( ) idxMap := sortUpstreamsWithRtt ( clones ) errs := [ ] error { } for i , dnsUpstream := range clones { reply , elapsed , err := exchangeWithUpstream ( dnsUpstream . upstream , req ) if err == nil { p . updateUpstreamRtt ( idxMap [ i ] , int ( elapsed ) ) return reply , dnsUpstream . upstream , nil } errs = append ( errs , err ) p . updateUpstreamRtt ( idxMap [ i ] , int ( defaultTimeout / time . Millisecond ) ) } return nil , nil , errorx . DecorateMany ( " " , errs ... ) }
exchange tries to exchange the request with all upstreams one-by-one
exchange tries to exchange the request with all upstreams one - by - one
// clone upstreamsWithRtt to avoid race condition while upstreams iteration after slice sort // sort cloned upstreamsWithRtt by rtt "from fast to slow". idxMap is map of indexes in original and sorted slices // set elapsed time for each upstream in original slice to sort them before next exchange // initial rtt value for each upstream is 0, so unused servers will be at the top of the list after sorting // if there was an error, consider upstream RTT equal to the default timeout (this will set upstream to the last place in upstreamsWithRtt array)