aboutsummaryrefslogtreecommitdiff
path: root/plugin/forward/persistent.go
diff options
context:
space:
mode:
Diffstat (limited to 'plugin/forward/persistent.go')
-rw-r--r--plugin/forward/persistent.go52
1 files changed, 26 insertions, 26 deletions
diff --git a/plugin/forward/persistent.go b/plugin/forward/persistent.go
index d1348f94d..17cc5680a 100644
--- a/plugin/forward/persistent.go
+++ b/plugin/forward/persistent.go
@@ -24,8 +24,8 @@ type Transport struct {
tlsConfig *tls.Config
dial chan string
- yield chan *dns.Conn
- ret chan *dns.Conn
+ yield chan *persistConn
+ ret chan *persistConn
stop chan bool
}
@@ -36,23 +36,13 @@ func newTransport(addr string) *Transport {
expire: defaultExpire,
addr: addr,
dial: make(chan string),
- yield: make(chan *dns.Conn),
- ret: make(chan *dns.Conn),
+ yield: make(chan *persistConn),
+ ret: make(chan *persistConn),
stop: make(chan bool),
}
return t
}
-// len returns the number of connection, used for metrics. Can only be safely
-// used inside connManager() because of data races.
-func (t *Transport) len() int {
- l := 0
- for _, conns := range t.conns {
- l += len(conns)
- }
- return l
-}
-
// connManagers manages the persistent connection cache for UDP and TCP.
func (t *Transport) connManager() {
ticker := time.NewTicker(t.expire)
@@ -66,7 +56,7 @@ Wait:
if time.Since(pc.used) < t.expire {
// Found one, remove from pool and return this conn.
t.conns[proto] = stack[:len(stack)-1]
- t.ret <- pc.c
+ t.ret <- pc
continue Wait
}
// clear entire cache if the last conn is expired
@@ -75,26 +65,21 @@ Wait:
// transport methods anymore. So, it's safe to close them in a separate goroutine
go closeConns(stack)
}
- SocketGauge.WithLabelValues(t.addr).Set(float64(t.len()))
-
t.ret <- nil
- case conn := <-t.yield:
-
- SocketGauge.WithLabelValues(t.addr).Set(float64(t.len() + 1))
-
+ case pc := <-t.yield:
// no proto here, infer from config and conn
- if _, ok := conn.Conn.(*net.UDPConn); ok {
- t.conns["udp"] = append(t.conns["udp"], &persistConn{conn, time.Now()})
+ if _, ok := pc.c.Conn.(*net.UDPConn); ok {
+ t.conns["udp"] = append(t.conns["udp"], pc)
continue Wait
}
if t.tlsConfig == nil {
- t.conns["tcp"] = append(t.conns["tcp"], &persistConn{conn, time.Now()})
+ t.conns["tcp"] = append(t.conns["tcp"], pc)
continue Wait
}
- t.conns["tcp-tls"] = append(t.conns["tcp-tls"], &persistConn{conn, time.Now()})
+ t.conns["tcp-tls"] = append(t.conns["tcp-tls"], pc)
case <-ticker.C:
t.cleanup(false)
@@ -143,8 +128,23 @@ func (t *Transport) cleanup(all bool) {
}
}
+// It is hard to pin a value to this, the import thing is to no block forever, loosing at cached connection is not terrible.
+const yieldTimeout = 25 * time.Millisecond
+
// Yield return the connection to transport for reuse.
-func (t *Transport) Yield(c *dns.Conn) { t.yield <- c }
+func (t *Transport) Yield(pc *persistConn) {
+ pc.used = time.Now() // update used time
+
+ // Make ths non-blocking, because in the case of a very busy forwarder we will *block* on this yield. This
+ // blocks the outer go-routine and stuff will just pile up. We timeout when the send fails to as returning
+ // these connection is an optimization anyway.
+ select {
+ case t.yield <- pc:
+ return
+ case <-time.After(yieldTimeout):
+ return
+ }
+}
// Start starts the transport's connection manager.
func (t *Transport) Start() { go t.connManager() }