middleware/cache: split cache in positive and negative and use lru (#298)

Make the cache memory bounded, by using a LRU cache. Also split the
cache in a positive and negative one - each with its own controls.

Extend the cache stanza to allow for this:

    cache {
       positive limit [ttl]
       negative limit [ttl]
    }

is now possible. This also add a cache_test.go in the toplevel test/
directory that exercises the caching path.

Fixes #260
This commit is contained in:
Miek Gieben 2016-10-02 08:31:44 +01:00 committed by GitHub
parent 9b6b8d2762
commit e54c232c8c
16 changed files with 413 additions and 190 deletions

View file

@ -2,10 +2,12 @@ package cache
import (
"strconv"
"time"
"github.com/miekg/coredns/core/dnsserver"
"github.com/miekg/coredns/middleware"
"github.com/hashicorp/golang-lru"
"github.com/mholt/caddy"
)
@ -16,51 +18,103 @@ func init() {
})
}
// Cache sets up the root file path of the server.
func setup(c *caddy.Controller) error {
ttl, zones, err := cacheParse(c)
ca, err := cacheParse(c)
if err != nil {
return middleware.Error("cache", err)
}
dnsserver.GetConfig(c).AddMiddleware(func(next middleware.Handler) middleware.Handler {
return NewCache(ttl, zones, next)
ca.Next = next
return ca
})
return nil
}
func cacheParse(c *caddy.Controller) (int, []string, error) {
var (
err error
ttl int
origins []string
)
func cacheParse(c *caddy.Controller) (*Cache, error) {
ca := &Cache{pcap: defaultCap, ncap: defaultCap, pttl: maxTTL, nttl: maxNTTL}
for c.Next() {
if c.Val() == "cache" {
// cache [ttl] [zones..]
origins = make([]string, len(c.ServerBlockKeys))
copy(origins, c.ServerBlockKeys)
args := c.RemainingArgs()
if len(args) > 0 {
origins = args
// first args may be just a number, then it is the ttl, if not it is a zone
t := origins[0]
ttl, err = strconv.Atoi(t)
if err == nil {
origins = origins[1:]
if len(origins) == 0 {
// There was *only* the ttl, revert back to server block
copy(origins, c.ServerBlockKeys)
}
}
}
// cache [ttl] [zones..]
origins := make([]string, len(c.ServerBlockKeys))
copy(origins, c.ServerBlockKeys)
args := c.RemainingArgs()
for i := range origins {
origins[i] = middleware.Host(origins[i]).Normalize()
if len(args) > 0 {
// first args may be just a number, then it is the ttl, if not it is a zone
ttl, err := strconv.Atoi(args[0])
if err == nil {
ca.pttl = time.Duration(ttl) * time.Second
ca.nttl = time.Duration(ttl) * time.Second
args = args[1:]
}
if len(args) > 0 {
copy(origins, args)
}
return ttl, origins, nil
}
// Refinements? In an extra block.
for c.NextBlock() {
switch c.Val() {
// first number is cap, second is an new ttl
case "positive":
args := c.RemainingArgs()
if len(args) == 0 {
return nil, c.ArgErr()
}
pcap, err := strconv.Atoi(args[0])
if err != nil {
return nil, err
}
ca.pcap = pcap
if len(args) > 1 {
pttl, err := strconv.Atoi(args[1])
if err != nil {
return nil, err
}
ca.pttl = time.Duration(pttl) * time.Second
}
case "negative":
args := c.RemainingArgs()
if len(args) == 0 {
return nil, c.ArgErr()
}
ncap, err := strconv.Atoi(args[0])
if err != nil {
return nil, err
}
ca.ncap = ncap
if len(args) > 1 {
nttl, err := strconv.Atoi(args[1])
if err != nil {
return nil, err
}
ca.nttl = time.Duration(nttl) * time.Second
}
default:
return nil, c.ArgErr()
}
}
for i := range origins {
origins[i] = middleware.Host(origins[i]).Normalize()
}
var err error
ca.Zones = origins
ca.pcache, err = lru.New(ca.pcap)
if err != nil {
return nil, err
}
ca.ncache, err = lru.New(ca.ncap)
if err != nil {
return nil, err
}
return ca, nil
}
return 0, nil, nil
return nil, nil
}