-
Notifications
You must be signed in to change notification settings - Fork 0
/
cache.go
106 lines (97 loc) · 1.98 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
package recursive
import (
"net/netip"
"sync"
"sync/atomic"
"time"
"github.com/miekg/dns"
)
type Cache struct {
count uint64 // atomic
hits uint64 // atomic
mu sync.RWMutex
cache map[cacheKey]cacheValue
}
func NewCache() *Cache {
return &Cache{
cache: make(map[cacheKey]cacheValue),
}
}
// HitRatio returns the hit ratio as a percentage.
func (cache *Cache) HitRatio() float64 {
if cache != nil {
count := atomic.LoadUint64(&cache.count)
hits := atomic.LoadUint64(&cache.hits)
if total := count + hits; total > 0 {
return float64(hits*100) / float64(total)
}
}
return 0
}
// Size returns the number of entries in the cache.
func (cache *Cache) Size() (n int) {
if cache != nil {
cache.mu.RLock()
n = len(cache.cache)
cache.mu.RUnlock()
}
return
}
func (cache *Cache) Set(nsaddr netip.Addr, qname string, qtype uint16, msg *dns.Msg) {
if cache != nil && msg != nil {
ttl := min(MinTTL(msg), maxCacheTTL)
if ttl < 0 {
// empty response, cache it for a while
ttl = maxCacheTTL / 10
}
cv := cacheValue{
Msg: msg,
expires: time.Now().Add(time.Duration(ttl) * time.Second),
}
cache.mu.Lock()
cache.cache[cacheKey{
nsaddr: nsaddr,
qname: qname,
qtype: qtype,
}] = cv
cache.mu.Unlock()
}
}
func (cache *Cache) Get(nsaddr netip.Addr, qname string, qtype uint16) *dns.Msg {
if cache != nil {
ck := cacheKey{
nsaddr: nsaddr,
qname: qname,
qtype: qtype,
}
cache.mu.RLock()
cv, ok := cache.cache[ck]
cache.mu.RUnlock()
atomic.AddUint64(&cache.count, 1)
if ok {
if time.Since(cv.expires) < 0 {
atomic.AddUint64(&cache.hits, 1)
return cv.Msg
}
cache.mu.Lock()
delete(cache.cache, ck)
cache.mu.Unlock()
}
}
return nil
}
func (cache *Cache) Clean(now time.Time) {
if cache != nil {
cache.mu.Lock()
defer cache.mu.Unlock()
if now.IsZero() {
clear(cache.cache)
return
}
for ck, cv := range cache.cache {
if now.After(cv.expires) {
delete(cache.cache, ck)
}
}
}
}