-
Notifications
You must be signed in to change notification settings - Fork 23
/
cache.go
137 lines (124 loc) · 2.59 KB
/
cache.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
package dnsr
import (
"sync"
"time"
)
type cache struct {
capacity int
expire bool
m sync.RWMutex
entries map[string]entry
}
type entry map[RR]struct{}
const MinCacheCapacity = 1000
// newCache initializes and returns a new cache instance.
// Cache capacity defaults to MinCacheCapacity if <= 0.
func newCache(capacity int, expire bool) *cache {
if capacity <= 0 {
capacity = MinCacheCapacity
}
return &cache{
capacity: capacity,
entries: make(map[string]entry),
expire: expire,
}
}
// add adds 0 or more DNS records to the resolver cache for a specific
// domain name and record type. This ensures the cache entry exists, even
// if empty, for NXDOMAIN responses.
func (c *cache) add(qname string, rr RR) {
c.m.Lock()
defer c.m.Unlock()
c._add(qname, rr)
}
// addNX adds an NXDOMAIN to the cache.
// Safe for concurrent usage.
func (c *cache) addNX(qname string) {
c.m.Lock()
defer c.m.Unlock()
c._addEntry(qname)
}
// _add does NOT lock the mutex so unsafe for concurrent usage.
func (c *cache) _add(qname string, rr RR) {
e, ok := c.entries[qname]
if !ok {
c._evict()
}
if e == nil {
c.entries[qname] = make(map[RR]struct{})
e = c.entries[qname]
}
e[rr] = struct{}{}
}
// _addEntry adds an entry for qname to c.
// Not safe for concurrent usage.
func (c *cache) _addEntry(qname string) {
_, ok := c.entries[qname]
if !ok {
c._evict()
// For NXDOMAIN responses,
// the cache entry is present, but nil.
c.entries[qname] = nil
}
}
// FIXME: better random cache eviction than Go’s random key guarantee?
// Not safe for concurrent usage.
func (c *cache) _evict() {
if len(c.entries) < c.capacity {
return
}
// First evict expired entries
if c.expire {
now := time.Now()
for k, e := range c.entries {
for rr := range e {
if !rr.Expiry.IsZero() && rr.Expiry.Before(now) {
delete(e, rr)
}
}
if len(e) == 0 {
delete(c.entries, k)
}
if len(c.entries) < c.capacity {
return
}
}
}
// Then randomly evict entries
for k := range c.entries {
delete(c.entries, k)
if len(c.entries) < c.capacity {
return
}
}
}
// get returns a randomly ordered slice of DNS records.
func (c *cache) get(qname string) RRs {
c.m.RLock()
defer c.m.RUnlock()
e, ok := c.entries[qname]
if !ok {
return nil
}
if len(e) == 0 {
return emptyRRs
}
if c.expire {
now := time.Now()
rrs := make(RRs, 0, len(e))
for rr := range e {
if rr.Expiry.IsZero() || rr.Expiry.After(now) {
rrs = append(rrs, rr)
}
}
return rrs
} else {
i := 0
rrs := make(RRs, len(e))
for rr := range e {
rrs[i] = rr
i++
}
return rrs
}
}