plugin/cache: Add option to adjust SERVFAIL response cache TTL (#5320)
* add servfail cache opt Signed-off-by: Chris O'Haver <cohaver@infoblox.com>
This commit is contained in:
parent
d60ce0c8d4
commit
dded10420b
5 changed files with 78 additions and 2 deletions
4
plugin/cache/README.md
vendored
4
plugin/cache/README.md
vendored
|
@ -38,6 +38,7 @@ cache [TTL] [ZONES...] {
|
|||
denial CAPACITY [TTL] [MINTTL]
|
||||
prefetch AMOUNT [[DURATION] [PERCENTAGE%]]
|
||||
serve_stale [DURATION] [REFRESH_MODE]
|
||||
servfail DURATION
|
||||
}
|
||||
~~~
|
||||
|
||||
|
@ -63,6 +64,9 @@ cache [TTL] [ZONES...] {
|
|||
checking to see if the entry is available from the source. **REFRESH_MODE** defaults to `immediate`. Setting this
|
||||
value to `verify` can lead to increased latency when serving stale responses, but will prevent stale entries
|
||||
from ever being served if an updated response can be retrieved from the source.
|
||||
* `servfail` cache SERVFAIL responses for **DURATION**. Setting **DURATION** to 0 will disable caching of SERVFAIL
|
||||
responses. If this option is not set, SERVFAIL responses will be cached for 5 seconds. **DURATION** may not be
|
||||
greater than 5 minutes.
|
||||
|
||||
## Capacity and Eviction
|
||||
|
||||
|
|
5
plugin/cache/cache.go
vendored
5
plugin/cache/cache.go
vendored
|
@ -32,6 +32,7 @@ type Cache struct {
|
|||
pcap int
|
||||
pttl time.Duration
|
||||
minpttl time.Duration
|
||||
failttl time.Duration // TTL for caching SERVFAIL responses
|
||||
|
||||
// Prefetch.
|
||||
prefetch int
|
||||
|
@ -59,6 +60,7 @@ func New() *Cache {
|
|||
ncache: cache.New(defaultCap),
|
||||
nttl: maxNTTL,
|
||||
minnttl: minNTTL,
|
||||
failttl: minNTTL,
|
||||
prefetch: 0,
|
||||
duration: 1 * time.Minute,
|
||||
percentage: 10,
|
||||
|
@ -158,8 +160,7 @@ func (w *ResponseWriter) WriteMsg(res *dns.Msg) error {
|
|||
if mt == response.NameError || mt == response.NoData {
|
||||
duration = computeTTL(msgTTL, w.minnttl, w.nttl)
|
||||
} else if mt == response.ServerError {
|
||||
// use default ttl which is 5s
|
||||
duration = minTTL
|
||||
duration = w.failttl
|
||||
} else {
|
||||
duration = computeTTL(msgTTL, w.minpttl, w.pttl)
|
||||
}
|
||||
|
|
17
plugin/cache/cache_test.go
vendored
17
plugin/cache/cache_test.go
vendored
|
@ -258,6 +258,23 @@ func TestCacheZeroTTL(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestCacheServfailTTL0(t *testing.T) {
|
||||
c := New()
|
||||
c.minpttl = minTTL
|
||||
c.minnttl = minNTTL
|
||||
c.failttl = 0
|
||||
c.Next = servFailBackend(0)
|
||||
|
||||
req := new(dns.Msg)
|
||||
req.SetQuestion("example.org.", dns.TypeA)
|
||||
ctx := context.TODO()
|
||||
|
||||
c.ServeDNS(ctx, &test.ResponseWriter{}, req)
|
||||
if c.ncache.Len() != 0 {
|
||||
t.Errorf("SERVFAIL response should not have been cached")
|
||||
}
|
||||
}
|
||||
|
||||
func TestServeFromStaleCache(t *testing.T) {
|
||||
c := New()
|
||||
c.Next = ttlBackend(60)
|
||||
|
|
17
plugin/cache/setup.go
vendored
17
plugin/cache/setup.go
vendored
|
@ -188,6 +188,23 @@ func cacheParse(c *caddy.Controller) (*Cache, error) {
|
|||
}
|
||||
ca.verifyStale = mode == "verify"
|
||||
}
|
||||
case "servfail":
|
||||
args := c.RemainingArgs()
|
||||
if len(args) != 1 {
|
||||
return nil, c.ArgErr()
|
||||
}
|
||||
d, err := time.ParseDuration(args[0])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if d < 0 {
|
||||
return nil, errors.New("invalid negative ttl for servfail")
|
||||
}
|
||||
if d > 5*time.Minute {
|
||||
// RFC 2308 prohibits caching SERVFAIL longer than 5 minutes
|
||||
return nil, errors.New("caching SERVFAIL responses over 5 minutes is not permitted")
|
||||
}
|
||||
ca.failttl = d
|
||||
default:
|
||||
return nil, c.ArgErr()
|
||||
}
|
||||
|
|
37
plugin/cache/setup_test.go
vendored
37
plugin/cache/setup_test.go
vendored
|
@ -155,3 +155,40 @@ func TestServeStale(t *testing.T) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestServfail(t *testing.T) {
|
||||
tests := []struct {
|
||||
input string
|
||||
shouldErr bool
|
||||
failttl time.Duration
|
||||
}{
|
||||
{"servfail 1s", false, 1 * time.Second},
|
||||
{"servfail 5m", false, 5 * time.Minute},
|
||||
{"servfail 0s", false, 0},
|
||||
{"servfail 0", false, 0},
|
||||
// fails
|
||||
{"servfail", true, minNTTL},
|
||||
{"servfail 6m", true, minNTTL},
|
||||
{"servfail 20", true, minNTTL},
|
||||
{"servfail -1s", true, minNTTL},
|
||||
{"servfail aa", true, minNTTL},
|
||||
{"servfail 1m invalid", true, minNTTL},
|
||||
}
|
||||
for i, test := range tests {
|
||||
c := caddy.NewTestController("dns", fmt.Sprintf("cache {\n%s\n}", test.input))
|
||||
ca, err := cacheParse(c)
|
||||
if test.shouldErr && err == nil {
|
||||
t.Errorf("Test %v: Expected error but found nil", i)
|
||||
continue
|
||||
} else if !test.shouldErr && err != nil {
|
||||
t.Errorf("Test %v: Expected no error but found error: %v", i, err)
|
||||
continue
|
||||
}
|
||||
if test.shouldErr && err != nil {
|
||||
continue
|
||||
}
|
||||
if ca.failttl != test.failttl {
|
||||
t.Errorf("Test %v: Expected stale %v but found: %v", i, test.failttl, ca.staleUpTo)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue