coredns/middleware/cache/cache.go
Michael Grosser ece3cf8ecf Fix various issues with formatting and typos (#424)
* Fix typos

* Simplify code

* Fix error usage
2016-11-13 14:03:12 +00:00

137 lines
3.2 KiB
Go

// Package cache implements a cache.
package cache
import (
"log"
"strconv"
"strings"
"time"
"github.com/miekg/coredns/middleware"
"github.com/miekg/coredns/middleware/pkg/response"
"github.com/hashicorp/golang-lru"
"github.com/miekg/dns"
)
// Cache is middleware that looks up responses in a cache and caches replies.
// It has a success and a denial of existence cache.
type Cache struct {
Next middleware.Handler
Zones []string
ncache *lru.Cache
ncap int
nttl time.Duration
pcache *lru.Cache
pcap int
pttl time.Duration
}
// Return key under which we store the item. The empty string is returned
// when we don't want to cache the message. Currently we do not cache Truncated, errors
// zone transfers or dynamic update messages.
func key(m *dns.Msg, t response.Type, do bool) string {
// We don't store truncated responses.
if m.Truncated {
return ""
}
// Nor errors or Meta or Update
if t == response.OtherError || t == response.Meta || t == response.Update {
return ""
}
qtype := m.Question[0].Qtype
qname := strings.ToLower(m.Question[0].Name)
return rawKey(qname, qtype, do)
}
func rawKey(qname string, qtype uint16, do bool) string {
if do {
return "1" + qname + "." + strconv.Itoa(int(qtype))
}
return "0" + qname + "." + strconv.Itoa(int(qtype))
}
// ResponseWriter is a response writer that caches the reply message.
type ResponseWriter struct {
dns.ResponseWriter
*Cache
}
// WriteMsg implements the dns.ResponseWriter interface.
func (c *ResponseWriter) WriteMsg(res *dns.Msg) error {
do := false
mt, opt := response.Typify(res)
if opt != nil {
do = opt.Do()
}
// key returns empty string for anything we don't want to cache.
key := key(res, mt, do)
duration := c.pttl
if mt == response.NameError || mt == response.NoData {
duration = c.nttl
}
msgTTL := minMsgTTL(res, mt)
if msgTTL < duration {
duration = msgTTL
}
if key != "" {
c.set(res, key, mt, duration)
cacheSize.WithLabelValues(Success).Set(float64(c.pcache.Len()))
cacheSize.WithLabelValues(Denial).Set(float64(c.ncache.Len()))
}
setMsgTTL(res, uint32(duration.Seconds()))
return c.ResponseWriter.WriteMsg(res)
}
func (c *ResponseWriter) set(m *dns.Msg, key string, mt response.Type, duration time.Duration) {
if key == "" {
log.Printf("[ERROR] Caching called with empty cache key")
return
}
switch mt {
case response.NoError, response.Delegation:
i := newItem(m, duration)
c.pcache.Add(key, i)
case response.NameError, response.NoData:
i := newItem(m, duration)
c.ncache.Add(key, i)
case response.OtherError:
// don't cache these
default:
log.Printf("[WARNING] Caching called with unknown classification: %d", mt)
}
}
// Write implements the dns.ResponseWriter interface.
func (c *ResponseWriter) Write(buf []byte) (int, error) {
log.Printf("[WARNING] Caching called with Write: not caching reply")
n, err := c.ResponseWriter.Write(buf)
return n, err
}
const (
maxTTL = 1 * time.Hour
maxNTTL = 30 * time.Minute
minTTL = 5 // seconds
defaultCap = 10000 // default capacity of the cache.
// Success is the class for caching positive caching.
Success = "success"
// Denial is the class defined for negative caching.
Denial = "denial"
)