Skip to content

Commit

Permalink
Add negative cache
Browse files Browse the repository at this point in the history
Cache negative results, too, but only with half TTL, as configured.

Plus get rid of "mesg.Id = req.Id" data race (build with -race flag)
at once without locking.

Found another data race: delete must be protected with Lock,
RLock is not enough.
  • Loading branch information
tgulacsi committed Feb 13, 2015
1 parent 44b4cfa commit 6bbefe3
Show file tree
Hide file tree
Showing 2 changed files with 33 additions and 17 deletions.
6 changes: 3 additions & 3 deletions cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ type MemoryCache struct {
Backend map[string]Mesg
Expire time.Duration
Maxcount int
mu *sync.RWMutex
mu sync.RWMutex
}

func (c *MemoryCache) Get(key string) (*dns.Msg, error) {
Expand Down Expand Up @@ -92,9 +92,9 @@ func (c *MemoryCache) Set(key string, msg *dns.Msg) error {
}

func (c *MemoryCache) Remove(key string) {
c.mu.RLock()
c.mu.Lock()
delete(c.Backend, key)
c.mu.RUnlock()
c.mu.Unlock()
}

func (c *MemoryCache) Exists(key string) bool {
Expand Down
44 changes: 30 additions & 14 deletions handler.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package main

import (
"sync"
"time"

"github.com/miekg/dns"
Expand All @@ -24,18 +23,18 @@ func (q *Question) String() string {
}

type GODNSHandler struct {
resolver *Resolver
cache Cache
hosts Hosts
resolver *Resolver
cache, negCache Cache
hosts Hosts
}

func NewHandler() *GODNSHandler {

var (
clientConfig *dns.ClientConfig
cacheConfig CacheSettings
resolver *Resolver
cache Cache
clientConfig *dns.ClientConfig
cacheConfig CacheSettings
resolver *Resolver
cache, negCache Cache
)

resolvConfig := settings.ResolvConfig
Expand All @@ -52,10 +51,14 @@ func NewHandler() *GODNSHandler {
switch cacheConfig.Backend {
case "memory":
cache = &MemoryCache{
Backend: make(map[string]Mesg),
Backend: make(map[string]Mesg, cacheConfig.Maxcount),
Expire: time.Duration(cacheConfig.Expire) * time.Second,
Maxcount: cacheConfig.Maxcount,
mu: new(sync.RWMutex),
}
negCache = &MemoryCache{
Backend: make(map[string]Mesg),
Expire: time.Duration(cacheConfig.Expire) * time.Second / 2,
Maxcount: cacheConfig.Maxcount,
}
case "redis":
// cache = &MemoryCache{
Expand All @@ -72,7 +75,7 @@ func NewHandler() *GODNSHandler {

hosts := NewHosts(settings.Hosts, settings.Redis)

return &GODNSHandler{resolver, cache, hosts}
return &GODNSHandler{resolver, cache, negCache, hosts}
}

func (h *GODNSHandler) do(Net string, w dns.ResponseWriter, req *dns.Msg) {
Expand Down Expand Up @@ -123,11 +126,19 @@ func (h *GODNSHandler) do(Net string, w dns.ResponseWriter, req *dns.Msg) {
if IPQuery > 0 {
mesg, err := h.cache.Get(key)
if err != nil {
Debug("%s didn't hit cache: %s", Q.String(), err)
if mesg, err = h.negCache.Get(key); err != nil {
Debug("%s didn't hit cache: %s", Q.String(), err)
} else {
Debug("%s hit negative cache", Q.String())
dns.HandleFailed(w, req)
return
}
} else {
Debug("%s hit cache", Q.String())
mesg.Id = req.Id
w.WriteMsg(mesg)
// we need this copy against concurrent modification of Id
msg := *mesg
msg.Id = req.Id
w.WriteMsg(&msg)
return
}
}
Expand All @@ -137,6 +148,11 @@ func (h *GODNSHandler) do(Net string, w dns.ResponseWriter, req *dns.Msg) {
if err != nil {
Debug("%s", err)
dns.HandleFailed(w, req)

// cache the failure, too!
if err = h.negCache.Set(key, nil); err != nil {
Debug("Set %s negative cache failed: %v", Q.String(), err)
}
return
}

Expand Down

0 comments on commit 6bbefe3

Please sign in to comment.