diff --git a/plugin/cache/cache.go b/plugin/cache/cache.go index c46267658..654841b23 100644 --- a/plugin/cache/cache.go +++ b/plugin/cache/cache.go @@ -60,24 +60,24 @@ func New() *Cache { // Return key under which we store the item, -1 will be returned if we don't store 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) int { +func key(m *dns.Msg, t response.Type, do bool) (bool, uint64) { // We don't store truncated responses. if m.Truncated { - return -1 + return false, 0 } // Nor errors or Meta or Update if t == response.OtherError || t == response.Meta || t == response.Update { - return -1 + return false, 0 } - return int(hash(m.Question[0].Name, m.Question[0].Qtype, do)) + return true, hash(m.Question[0].Name, m.Question[0].Qtype, do) } var one = []byte("1") var zero = []byte("0") -func hash(qname string, qtype uint16, do bool) uint32 { - h := fnv.New32() +func hash(qname string, qtype uint16, do bool) uint64 { + h := fnv.New64() if do { h.Write(one) @@ -97,7 +97,7 @@ func hash(qname string, qtype uint16, do bool) uint32 { h.Write([]byte{c}) } - return h.Sum32() + return h.Sum64() } // ResponseWriter is a response writer that caches the reply message. @@ -152,7 +152,7 @@ func (w *ResponseWriter) WriteMsg(res *dns.Msg) error { } // key returns empty string for anything we don't want to cache. - key := key(res, mt, do) + hasKey, key := key(res, mt, do) duration := w.pttl if mt == response.NameError || mt == response.NoData { @@ -164,7 +164,7 @@ func (w *ResponseWriter) WriteMsg(res *dns.Msg) error { duration = msgTTL } - if key != -1 && duration > 0 { + if hasKey && duration > 0 { if w.state.Match(res) { w.set(res, key, mt, duration) cacheSize.WithLabelValues(w.server, Success).Set(float64(w.pcache.Len())) @@ -195,19 +195,17 @@ func (w *ResponseWriter) WriteMsg(res *dns.Msg) error { return w.ResponseWriter.WriteMsg(res) } -func (w *ResponseWriter) set(m *dns.Msg, key int, mt response.Type, duration time.Duration) { - if key == -1 || duration == 0 { - return - } - +func (w *ResponseWriter) set(m *dns.Msg, key uint64, mt response.Type, duration time.Duration) { + // duration is expected > 0 + // and key is valid switch mt { case response.NoError, response.Delegation: i := newItem(m, w.now(), duration) - w.pcache.Add(uint32(key), i) + w.pcache.Add(key, i) case response.NameError, response.NoData: i := newItem(m, w.now(), duration) - w.ncache.Add(uint32(key), i) + w.ncache.Add(key, i) case response.OtherError: // don't cache these diff --git a/plugin/cache/cache_test.go b/plugin/cache/cache_test.go index c2ab74920..08ff667a7 100644 --- a/plugin/cache/cache_test.go +++ b/plugin/cache/cache_test.go @@ -167,9 +167,11 @@ func TestCache(t *testing.T) { state := request.Request{W: nil, Req: m} mt, _ := response.Typify(m, utc) - k := key(m, mt, state.Do()) + valid, k := key(m, mt, state.Do()) - crr.set(m, k, mt, c.pttl) + if valid { + crr.set(m, k, mt, c.pttl) + } i, _ := c.get(time.Now().UTC(), state, "dns://:53") ok := i != nil diff --git a/plugin/dnssec/cache.go b/plugin/dnssec/cache.go index ea95b73b4..e1f503703 100644 --- a/plugin/dnssec/cache.go +++ b/plugin/dnssec/cache.go @@ -7,8 +7,8 @@ import ( ) // hash serializes the RRset and return a signature cache key. -func hash(rrs []dns.RR) uint32 { - h := fnv.New32() +func hash(rrs []dns.RR) uint64 { + h := fnv.New64() buf := make([]byte, 256) for _, r := range rrs { off, err := dns.PackRR(r, buf, 0, nil, false) @@ -17,6 +17,6 @@ func hash(rrs []dns.RR) uint32 { } } - i := h.Sum32() + i := h.Sum64() return i } diff --git a/plugin/dnssec/dnssec.go b/plugin/dnssec/dnssec.go index 1ebcb13af..68b9eb52d 100644 --- a/plugin/dnssec/dnssec.go +++ b/plugin/dnssec/dnssec.go @@ -110,9 +110,9 @@ func (d Dnssec) sign(rrs []dns.RR, signerName string, ttl, incep, expir uint32, return sigs.([]dns.RR), err } -func (d Dnssec) set(key uint32, sigs []dns.RR) { d.cache.Add(key, sigs) } +func (d Dnssec) set(key uint64, sigs []dns.RR) { d.cache.Add(key, sigs) } -func (d Dnssec) get(key uint32, server string) ([]dns.RR, bool) { +func (d Dnssec) get(key uint64, server string) ([]dns.RR, bool) { if s, ok := d.cache.Get(key); ok { // we sign for 8 days, check if a signature in the cache reached 3/4 of that is75 := time.Now().UTC().Add(sixDays) diff --git a/plugin/pkg/cache/cache.go b/plugin/pkg/cache/cache.go index 954befa76..8a5ad783e 100644 --- a/plugin/pkg/cache/cache.go +++ b/plugin/pkg/cache/cache.go @@ -9,10 +9,10 @@ import ( ) // Hash returns the FNV hash of what. -func Hash(what []byte) uint32 { - h := fnv.New32() +func Hash(what []byte) uint64 { + h := fnv.New64() h.Write(what) - return h.Sum32() + return h.Sum64() } // Cache is cache. @@ -22,7 +22,7 @@ type Cache struct { // shard is a cache with random eviction. type shard struct { - items map[uint32]interface{} + items map[uint64]interface{} size int sync.RWMutex @@ -45,19 +45,19 @@ func New(size int) *Cache { } // Add adds a new element to the cache. If the element already exists it is overwritten. -func (c *Cache) Add(key uint32, el interface{}) { +func (c *Cache) Add(key uint64, el interface{}) { shard := key & (shardSize - 1) c.shards[shard].Add(key, el) } // Get looks up element index under key. -func (c *Cache) Get(key uint32) (interface{}, bool) { +func (c *Cache) Get(key uint64) (interface{}, bool) { shard := key & (shardSize - 1) return c.shards[shard].Get(key) } // Remove removes the element indexed with key. -func (c *Cache) Remove(key uint32) { +func (c *Cache) Remove(key uint64) { shard := key & (shardSize - 1) c.shards[shard].Remove(key) } @@ -72,10 +72,10 @@ func (c *Cache) Len() int { } // newShard returns a new shard with size. -func newShard(size int) *shard { return &shard{items: make(map[uint32]interface{}), size: size} } +func newShard(size int) *shard { return &shard{items: make(map[uint64]interface{}), size: size} } // Add adds element indexed by key into the cache. Any existing element is overwritten -func (s *shard) Add(key uint32, el interface{}) { +func (s *shard) Add(key uint64, el interface{}) { l := s.Len() if l+1 > s.size { s.Evict() @@ -87,7 +87,7 @@ func (s *shard) Add(key uint32, el interface{}) { } // Remove removes the element indexed by key from the cache. -func (s *shard) Remove(key uint32) { +func (s *shard) Remove(key uint64) { s.Lock() delete(s.items, key) s.Unlock() @@ -95,26 +95,28 @@ func (s *shard) Remove(key uint32) { // Evict removes a random element from the cache. func (s *shard) Evict() { - key := -1 + hasKey := false + var key uint64 s.RLock() for k := range s.items { - key = int(k) + key = k + hasKey = true break } s.RUnlock() - if key == -1 { + if !hasKey { // empty cache return } // If this item is gone between the RUnlock and Lock race we don't care. - s.Remove(uint32(key)) + s.Remove(key) } // Get looks up the element indexed under key. -func (s *shard) Get(key uint32) (interface{}, bool) { +func (s *shard) Get(key uint64) (interface{}, bool) { s.RLock() el, found := s.items[key] s.RUnlock() diff --git a/plugin/pkg/singleflight/singleflight.go b/plugin/pkg/singleflight/singleflight.go index 365e3ef58..e70646cd1 100644 --- a/plugin/pkg/singleflight/singleflight.go +++ b/plugin/pkg/singleflight/singleflight.go @@ -31,17 +31,17 @@ type call struct { // units of work can be executed with duplicate suppression. type Group struct { mu sync.Mutex // protects m - m map[uint32]*call // lazily initialized + m map[uint64]*call // lazily initialized } // Do executes and returns the results of the given function, making // sure that only one execution is in-flight for a given key at a // time. If a duplicate comes in, the duplicate caller waits for the // original to complete and receives the same results. -func (g *Group) Do(key uint32, fn func() (interface{}, error)) (interface{}, error) { +func (g *Group) Do(key uint64, fn func() (interface{}, error)) (interface{}, error) { g.mu.Lock() if g.m == nil { - g.m = make(map[uint32]*call) + g.m = make(map[uint64]*call) } if c, ok := g.m[key]; ok { g.mu.Unlock()