mirror of
				https://github.com/coredns/coredns.git
				synced 2025-10-31 02:03:20 -04:00 
			
		
		
		
	Singleinflight interferes with the health checking of upstream. If an upstream would fail, singleinflight would mirror that error to to other proxy *iff* multple identical queries would be inflight. This would lead to marking *all* upstreams as bad, essentially collapsing multiple upstreams into a SPOF. Clearly not what we want. Singleinflight does have some nice properties, but I've opted to rip it out entirely. Caching should almost (but not quite) as good. Added a test case in test that uses 3 CoreDNS instances to reflect the setup from #715. Found another bug as well, where (when the policy would be nil), we would always Spray even though we've found a healthy host.
		
			
				
	
	
		
			98 lines
		
	
	
		
			2.0 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			98 lines
		
	
	
		
			2.0 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package proxy
 | |
| 
 | |
| import (
 | |
| 	"context"
 | |
| 	"net"
 | |
| 	"time"
 | |
| 
 | |
| 	"github.com/coredns/coredns/request"
 | |
| 
 | |
| 	"github.com/miekg/dns"
 | |
| )
 | |
| 
 | |
| type dnsEx struct {
 | |
| 	Timeout time.Duration
 | |
| 	Options
 | |
| }
 | |
| 
 | |
| type Options struct {
 | |
| 	ForceTCP bool // If true use TCP for upstream no matter what
 | |
| }
 | |
| 
 | |
| func newDNSEx() *dnsEx {
 | |
| 	return newDNSExWithOption(Options{})
 | |
| }
 | |
| 
 | |
| func newDNSExWithOption(opt Options) *dnsEx {
 | |
| 	return &dnsEx{Timeout: defaultTimeout * time.Second, Options: opt}
 | |
| }
 | |
| 
 | |
| func (d *dnsEx) Protocol() string          { return "dns" }
 | |
| func (d *dnsEx) OnShutdown(p *Proxy) error { return nil }
 | |
| func (d *dnsEx) OnStartup(p *Proxy) error  { return nil }
 | |
| 
 | |
| // Exchange implements the Exchanger interface.
 | |
| func (d *dnsEx) Exchange(ctx context.Context, addr string, state request.Request) (*dns.Msg, error) {
 | |
| 	proto := state.Proto()
 | |
| 	if d.Options.ForceTCP {
 | |
| 		proto = "tcp"
 | |
| 	}
 | |
| 	co, err := net.DialTimeout(proto, addr, d.Timeout)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	reply, _, err := d.ExchangeConn(state.Req, co)
 | |
| 
 | |
| 	co.Close()
 | |
| 
 | |
| 	if reply != nil && reply.Truncated {
 | |
| 		// Suppress proxy error for truncated responses
 | |
| 		err = nil
 | |
| 	}
 | |
| 
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	// Make sure it fits in the DNS response.
 | |
| 	reply, _ = state.Scrub(reply)
 | |
| 	reply.Compress = true
 | |
| 	reply.Id = state.Req.Id
 | |
| 
 | |
| 	return reply, nil
 | |
| }
 | |
| 
 | |
| func (d *dnsEx) ExchangeConn(m *dns.Msg, co net.Conn) (*dns.Msg, time.Duration, error) {
 | |
| 	start := time.Now()
 | |
| 	r, err := exchange(m, co)
 | |
| 	rtt := time.Since(start)
 | |
| 
 | |
| 	return r, rtt, err
 | |
| }
 | |
| 
 | |
| func exchange(m *dns.Msg, co net.Conn) (*dns.Msg, error) {
 | |
| 	opt := m.IsEdns0()
 | |
| 
 | |
| 	udpsize := uint16(dns.MinMsgSize)
 | |
| 	// If EDNS0 is used use that for size.
 | |
| 	if opt != nil && opt.UDPSize() >= dns.MinMsgSize {
 | |
| 		udpsize = opt.UDPSize()
 | |
| 	}
 | |
| 
 | |
| 	dnsco := &dns.Conn{Conn: co, UDPSize: udpsize}
 | |
| 
 | |
| 	writeDeadline := time.Now().Add(defaultTimeout)
 | |
| 	dnsco.SetWriteDeadline(writeDeadline)
 | |
| 	dnsco.WriteMsg(m)
 | |
| 
 | |
| 	readDeadline := time.Now().Add(defaultTimeout)
 | |
| 	co.SetReadDeadline(readDeadline)
 | |
| 	r, err := dnsco.ReadMsg()
 | |
| 
 | |
| 	dnsco.Close()
 | |
| 	if r == nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	return r, err
 | |
| }
 |