Move cache Keys to 64bit for a better dispersion and lower collision frequency (#2077)
* - change Key for cache to 64bits. * - change Key for cache to 64bits.
This commit is contained in:
parent
d00e8c3918
commit
4c6c9d4b27
6 changed files with 43 additions and 41 deletions
30
plugin/cache/cache.go
vendored
30
plugin/cache/cache.go
vendored
|
@ -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
|
// Return key under which we store the item, -1 will be returned if we don't store the
|
||||||
// message.
|
// message.
|
||||||
// Currently we do not cache Truncated, errors zone transfers or dynamic update messages.
|
// 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.
|
// We don't store truncated responses.
|
||||||
if m.Truncated {
|
if m.Truncated {
|
||||||
return -1
|
return false, 0
|
||||||
}
|
}
|
||||||
// Nor errors or Meta or Update
|
// Nor errors or Meta or Update
|
||||||
if t == response.OtherError || t == response.Meta || t == response.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 one = []byte("1")
|
||||||
var zero = []byte("0")
|
var zero = []byte("0")
|
||||||
|
|
||||||
func hash(qname string, qtype uint16, do bool) uint32 {
|
func hash(qname string, qtype uint16, do bool) uint64 {
|
||||||
h := fnv.New32()
|
h := fnv.New64()
|
||||||
|
|
||||||
if do {
|
if do {
|
||||||
h.Write(one)
|
h.Write(one)
|
||||||
|
@ -97,7 +97,7 @@ func hash(qname string, qtype uint16, do bool) uint32 {
|
||||||
h.Write([]byte{c})
|
h.Write([]byte{c})
|
||||||
}
|
}
|
||||||
|
|
||||||
return h.Sum32()
|
return h.Sum64()
|
||||||
}
|
}
|
||||||
|
|
||||||
// ResponseWriter is a response writer that caches the reply message.
|
// 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 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
|
duration := w.pttl
|
||||||
if mt == response.NameError || mt == response.NoData {
|
if mt == response.NameError || mt == response.NoData {
|
||||||
|
@ -164,7 +164,7 @@ func (w *ResponseWriter) WriteMsg(res *dns.Msg) error {
|
||||||
duration = msgTTL
|
duration = msgTTL
|
||||||
}
|
}
|
||||||
|
|
||||||
if key != -1 && duration > 0 {
|
if hasKey && duration > 0 {
|
||||||
if w.state.Match(res) {
|
if w.state.Match(res) {
|
||||||
w.set(res, key, mt, duration)
|
w.set(res, key, mt, duration)
|
||||||
cacheSize.WithLabelValues(w.server, Success).Set(float64(w.pcache.Len()))
|
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)
|
return w.ResponseWriter.WriteMsg(res)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *ResponseWriter) set(m *dns.Msg, key int, mt response.Type, duration time.Duration) {
|
func (w *ResponseWriter) set(m *dns.Msg, key uint64, mt response.Type, duration time.Duration) {
|
||||||
if key == -1 || duration == 0 {
|
// duration is expected > 0
|
||||||
return
|
// and key is valid
|
||||||
}
|
|
||||||
|
|
||||||
switch mt {
|
switch mt {
|
||||||
case response.NoError, response.Delegation:
|
case response.NoError, response.Delegation:
|
||||||
i := newItem(m, w.now(), duration)
|
i := newItem(m, w.now(), duration)
|
||||||
w.pcache.Add(uint32(key), i)
|
w.pcache.Add(key, i)
|
||||||
|
|
||||||
case response.NameError, response.NoData:
|
case response.NameError, response.NoData:
|
||||||
i := newItem(m, w.now(), duration)
|
i := newItem(m, w.now(), duration)
|
||||||
w.ncache.Add(uint32(key), i)
|
w.ncache.Add(key, i)
|
||||||
|
|
||||||
case response.OtherError:
|
case response.OtherError:
|
||||||
// don't cache these
|
// don't cache these
|
||||||
|
|
6
plugin/cache/cache_test.go
vendored
6
plugin/cache/cache_test.go
vendored
|
@ -167,9 +167,11 @@ func TestCache(t *testing.T) {
|
||||||
state := request.Request{W: nil, Req: m}
|
state := request.Request{W: nil, Req: m}
|
||||||
|
|
||||||
mt, _ := response.Typify(m, utc)
|
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")
|
i, _ := c.get(time.Now().UTC(), state, "dns://:53")
|
||||||
ok := i != nil
|
ok := i != nil
|
||||||
|
|
|
@ -7,8 +7,8 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// hash serializes the RRset and return a signature cache key.
|
// hash serializes the RRset and return a signature cache key.
|
||||||
func hash(rrs []dns.RR) uint32 {
|
func hash(rrs []dns.RR) uint64 {
|
||||||
h := fnv.New32()
|
h := fnv.New64()
|
||||||
buf := make([]byte, 256)
|
buf := make([]byte, 256)
|
||||||
for _, r := range rrs {
|
for _, r := range rrs {
|
||||||
off, err := dns.PackRR(r, buf, 0, nil, false)
|
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
|
return i
|
||||||
}
|
}
|
||||||
|
|
|
@ -110,9 +110,9 @@ func (d Dnssec) sign(rrs []dns.RR, signerName string, ttl, incep, expir uint32,
|
||||||
return sigs.([]dns.RR), err
|
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 {
|
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
|
// we sign for 8 days, check if a signature in the cache reached 3/4 of that
|
||||||
is75 := time.Now().UTC().Add(sixDays)
|
is75 := time.Now().UTC().Add(sixDays)
|
||||||
|
|
32
plugin/pkg/cache/cache.go
vendored
32
plugin/pkg/cache/cache.go
vendored
|
@ -9,10 +9,10 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// Hash returns the FNV hash of what.
|
// Hash returns the FNV hash of what.
|
||||||
func Hash(what []byte) uint32 {
|
func Hash(what []byte) uint64 {
|
||||||
h := fnv.New32()
|
h := fnv.New64()
|
||||||
h.Write(what)
|
h.Write(what)
|
||||||
return h.Sum32()
|
return h.Sum64()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cache is cache.
|
// Cache is cache.
|
||||||
|
@ -22,7 +22,7 @@ type Cache struct {
|
||||||
|
|
||||||
// shard is a cache with random eviction.
|
// shard is a cache with random eviction.
|
||||||
type shard struct {
|
type shard struct {
|
||||||
items map[uint32]interface{}
|
items map[uint64]interface{}
|
||||||
size int
|
size int
|
||||||
|
|
||||||
sync.RWMutex
|
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.
|
// 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)
|
shard := key & (shardSize - 1)
|
||||||
c.shards[shard].Add(key, el)
|
c.shards[shard].Add(key, el)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get looks up element index under key.
|
// 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)
|
shard := key & (shardSize - 1)
|
||||||
return c.shards[shard].Get(key)
|
return c.shards[shard].Get(key)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove removes the element indexed with key.
|
// Remove removes the element indexed with key.
|
||||||
func (c *Cache) Remove(key uint32) {
|
func (c *Cache) Remove(key uint64) {
|
||||||
shard := key & (shardSize - 1)
|
shard := key & (shardSize - 1)
|
||||||
c.shards[shard].Remove(key)
|
c.shards[shard].Remove(key)
|
||||||
}
|
}
|
||||||
|
@ -72,10 +72,10 @@ func (c *Cache) Len() int {
|
||||||
}
|
}
|
||||||
|
|
||||||
// newShard returns a new shard with size.
|
// 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
|
// 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()
|
l := s.Len()
|
||||||
if l+1 > s.size {
|
if l+1 > s.size {
|
||||||
s.Evict()
|
s.Evict()
|
||||||
|
@ -87,7 +87,7 @@ func (s *shard) Add(key uint32, el interface{}) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove removes the element indexed by key from the cache.
|
// Remove removes the element indexed by key from the cache.
|
||||||
func (s *shard) Remove(key uint32) {
|
func (s *shard) Remove(key uint64) {
|
||||||
s.Lock()
|
s.Lock()
|
||||||
delete(s.items, key)
|
delete(s.items, key)
|
||||||
s.Unlock()
|
s.Unlock()
|
||||||
|
@ -95,26 +95,28 @@ func (s *shard) Remove(key uint32) {
|
||||||
|
|
||||||
// Evict removes a random element from the cache.
|
// Evict removes a random element from the cache.
|
||||||
func (s *shard) Evict() {
|
func (s *shard) Evict() {
|
||||||
key := -1
|
hasKey := false
|
||||||
|
var key uint64
|
||||||
|
|
||||||
s.RLock()
|
s.RLock()
|
||||||
for k := range s.items {
|
for k := range s.items {
|
||||||
key = int(k)
|
key = k
|
||||||
|
hasKey = true
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
s.RUnlock()
|
s.RUnlock()
|
||||||
|
|
||||||
if key == -1 {
|
if !hasKey {
|
||||||
// empty cache
|
// empty cache
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// If this item is gone between the RUnlock and Lock race we don't care.
|
// 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.
|
// 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()
|
s.RLock()
|
||||||
el, found := s.items[key]
|
el, found := s.items[key]
|
||||||
s.RUnlock()
|
s.RUnlock()
|
||||||
|
|
|
@ -31,17 +31,17 @@ type call struct {
|
||||||
// units of work can be executed with duplicate suppression.
|
// units of work can be executed with duplicate suppression.
|
||||||
type Group struct {
|
type Group struct {
|
||||||
mu sync.Mutex // protects m
|
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
|
// 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
|
// 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
|
// time. If a duplicate comes in, the duplicate caller waits for the
|
||||||
// original to complete and receives the same results.
|
// 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()
|
g.mu.Lock()
|
||||||
if g.m == nil {
|
if g.m == nil {
|
||||||
g.m = make(map[uint32]*call)
|
g.m = make(map[uint64]*call)
|
||||||
}
|
}
|
||||||
if c, ok := g.m[key]; ok {
|
if c, ok := g.m[key]; ok {
|
||||||
g.mu.Unlock()
|
g.mu.Unlock()
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue