Refactor: lrucache use generics

This commit is contained in:
yaling888 2022-04-05 20:23:16 +08:00 committed by Meta
parent 14878b37f6
commit 673541e2a8
8 changed files with 106 additions and 97 deletions

View file

@ -9,43 +9,43 @@ import (
) )
// Option is part of Functional Options Pattern // Option is part of Functional Options Pattern
type Option func(*LruCache) type Option[K comparable, V any] func(*LruCache[K, V])
// EvictCallback is used to get a callback when a cache entry is evicted // EvictCallback is used to get a callback when a cache entry is evicted
type EvictCallback = func(key any, value any) type EvictCallback = func(key any, value any)
// WithEvict set the evict callback // WithEvict set the evict callback
func WithEvict(cb EvictCallback) Option { func WithEvict[K comparable, V any](cb EvictCallback) Option[K, V] {
return func(l *LruCache) { return func(l *LruCache[K, V]) {
l.onEvict = cb l.onEvict = cb
} }
} }
// WithUpdateAgeOnGet update expires when Get element // WithUpdateAgeOnGet update expires when Get element
func WithUpdateAgeOnGet() Option { func WithUpdateAgeOnGet[K comparable, V any]() Option[K, V] {
return func(l *LruCache) { return func(l *LruCache[K, V]) {
l.updateAgeOnGet = true l.updateAgeOnGet = true
} }
} }
// WithAge defined element max age (second) // WithAge defined element max age (second)
func WithAge(maxAge int64) Option { func WithAge[K comparable, V any](maxAge int64) Option[K, V] {
return func(l *LruCache) { return func(l *LruCache[K, V]) {
l.maxAge = maxAge l.maxAge = maxAge
} }
} }
// WithSize defined max length of LruCache // WithSize defined max length of LruCache
func WithSize(maxSize int) Option { func WithSize[K comparable, V any](maxSize int) Option[K, V] {
return func(l *LruCache) { return func(l *LruCache[K, V]) {
l.maxSize = maxSize l.maxSize = maxSize
} }
} }
// WithStale decide whether Stale return is enabled. // WithStale decide whether Stale return is enabled.
// If this feature is enabled, element will not get Evicted according to `WithAge`. // If this feature is enabled, element will not get Evicted according to `WithAge`.
func WithStale(stale bool) Option { func WithStale[K comparable, V any](stale bool) Option[K, V] {
return func(l *LruCache) { return func(l *LruCache[K, V]) {
l.staleReturn = stale l.staleReturn = stale
} }
} }
@ -53,7 +53,7 @@ func WithStale(stale bool) Option {
// LruCache is a thread-safe, in-memory lru-cache that evicts the // LruCache is a thread-safe, in-memory lru-cache that evicts the
// least recently used entries from memory when (if set) the entries are // least recently used entries from memory when (if set) the entries are
// older than maxAge (in seconds). Use the New constructor to create one. // older than maxAge (in seconds). Use the New constructor to create one.
type LruCache struct { type LruCache[K comparable, V any] struct {
maxAge int64 maxAge int64
maxSize int maxSize int
mu sync.Mutex mu sync.Mutex
@ -65,8 +65,8 @@ type LruCache struct {
} }
// NewLRUCache creates an LruCache // NewLRUCache creates an LruCache
func NewLRUCache(options ...Option) *LruCache { func NewLRUCache[K comparable, V any](options ...Option[K, V]) *LruCache[K, V] {
lc := &LruCache{ lc := &LruCache[K, V]{
lru: list.New(), lru: list.New(),
cache: make(map[any]*list.Element), cache: make(map[any]*list.Element),
} }
@ -80,12 +80,12 @@ func NewLRUCache(options ...Option) *LruCache {
// Get returns the any representation of a cached response and a bool // Get returns the any representation of a cached response and a bool
// set to true if the key was found. // set to true if the key was found.
func (c *LruCache) Get(key any) (any, bool) { func (c *LruCache[K, V]) Get(key K) (V, bool) {
entry := c.get(key) el := c.get(key)
if entry == nil { if el == nil {
return nil, false return getZero[V](), false
} }
value := entry.value value := el.value
return value, true return value, true
} }
@ -94,17 +94,17 @@ func (c *LruCache) Get(key any) (any, bool) {
// a time.Time Give expected expires, // a time.Time Give expected expires,
// and a bool set to true if the key was found. // and a bool set to true if the key was found.
// This method will NOT check the maxAge of element and will NOT update the expires. // This method will NOT check the maxAge of element and will NOT update the expires.
func (c *LruCache) GetWithExpire(key any) (any, time.Time, bool) { func (c *LruCache[K, V]) GetWithExpire(key K) (V, time.Time, bool) {
entry := c.get(key) el := c.get(key)
if entry == nil { if el == nil {
return nil, time.Time{}, false return getZero[V](), time.Time{}, false
} }
return entry.value, time.Unix(entry.expires, 0), true return el.value, time.Unix(el.expires, 0), true
} }
// Exist returns if key exist in cache but not put item to the head of linked list // Exist returns if key exist in cache but not put item to the head of linked list
func (c *LruCache) Exist(key any) bool { func (c *LruCache[K, V]) Exist(key K) bool {
c.mu.Lock() c.mu.Lock()
defer c.mu.Unlock() defer c.mu.Unlock()
@ -113,7 +113,7 @@ func (c *LruCache) Exist(key any) bool {
} }
// Set stores the any representation of a response for a given key. // Set stores the any representation of a response for a given key.
func (c *LruCache) Set(key any, value any) { func (c *LruCache[K, V]) Set(key K, value V) {
expires := int64(0) expires := int64(0)
if c.maxAge > 0 { if c.maxAge > 0 {
expires = time.Now().Unix() + c.maxAge expires = time.Now().Unix() + c.maxAge
@ -123,21 +123,21 @@ func (c *LruCache) Set(key any, value any) {
// SetWithExpire stores the any representation of a response for a given key and given expires. // SetWithExpire stores the any representation of a response for a given key and given expires.
// The expires time will round to second. // The expires time will round to second.
func (c *LruCache) SetWithExpire(key any, value any, expires time.Time) { func (c *LruCache[K, V]) SetWithExpire(key K, value V, expires time.Time) {
c.mu.Lock() c.mu.Lock()
defer c.mu.Unlock() defer c.mu.Unlock()
if le, ok := c.cache[key]; ok { if le, ok := c.cache[key]; ok {
c.lru.MoveToBack(le) c.lru.MoveToBack(le)
e := le.Value.(*entry) e := le.Value.(*entry[K, V])
e.value = value e.value = value
e.expires = expires.Unix() e.expires = expires.Unix()
} else { } else {
e := &entry{key: key, value: value, expires: expires.Unix()} e := &entry[K, V]{key: key, value: value, expires: expires.Unix()}
c.cache[key] = c.lru.PushBack(e) c.cache[key] = c.lru.PushBack(e)
if c.maxSize > 0 { if c.maxSize > 0 {
if len := c.lru.Len(); len > c.maxSize { if elLen := c.lru.Len(); elLen > c.maxSize {
c.deleteElement(c.lru.Front()) c.deleteElement(c.lru.Front())
} }
} }
@ -147,7 +147,7 @@ func (c *LruCache) SetWithExpire(key any, value any, expires time.Time) {
} }
// CloneTo clone and overwrite elements to another LruCache // CloneTo clone and overwrite elements to another LruCache
func (c *LruCache) CloneTo(n *LruCache) { func (c *LruCache[K, V]) CloneTo(n *LruCache[K, V]) {
c.mu.Lock() c.mu.Lock()
defer c.mu.Unlock() defer c.mu.Unlock()
@ -158,12 +158,12 @@ func (c *LruCache) CloneTo(n *LruCache) {
n.cache = make(map[any]*list.Element) n.cache = make(map[any]*list.Element)
for e := c.lru.Front(); e != nil; e = e.Next() { for e := c.lru.Front(); e != nil; e = e.Next() {
elm := e.Value.(*entry) elm := e.Value.(*entry[K, V])
n.cache[elm.key] = n.lru.PushBack(elm) n.cache[elm.key] = n.lru.PushBack(elm)
} }
} }
func (c *LruCache) get(key any) *entry { func (c *LruCache[K, V]) get(key K) *entry[K, V] {
c.mu.Lock() c.mu.Lock()
defer c.mu.Unlock() defer c.mu.Unlock()
@ -172,7 +172,7 @@ func (c *LruCache) get(key any) *entry {
return nil return nil
} }
if !c.staleReturn && c.maxAge > 0 && le.Value.(*entry).expires <= time.Now().Unix() { if !c.staleReturn && c.maxAge > 0 && le.Value.(*entry[K, V]).expires <= time.Now().Unix() {
c.deleteElement(le) c.deleteElement(le)
c.maybeDeleteOldest() c.maybeDeleteOldest()
@ -180,15 +180,15 @@ func (c *LruCache) get(key any) *entry {
} }
c.lru.MoveToBack(le) c.lru.MoveToBack(le)
entry := le.Value.(*entry) el := le.Value.(*entry[K, V])
if c.maxAge > 0 && c.updateAgeOnGet { if c.maxAge > 0 && c.updateAgeOnGet {
entry.expires = time.Now().Unix() + c.maxAge el.expires = time.Now().Unix() + c.maxAge
} }
return entry return el
} }
// Delete removes the value associated with a key. // Delete removes the value associated with a key.
func (c *LruCache) Delete(key any) { func (c *LruCache[K, V]) Delete(key K) {
c.mu.Lock() c.mu.Lock()
if le, ok := c.cache[key]; ok { if le, ok := c.cache[key]; ok {
@ -198,25 +198,25 @@ func (c *LruCache) Delete(key any) {
c.mu.Unlock() c.mu.Unlock()
} }
func (c *LruCache) maybeDeleteOldest() { func (c *LruCache[K, V]) maybeDeleteOldest() {
if !c.staleReturn && c.maxAge > 0 { if !c.staleReturn && c.maxAge > 0 {
now := time.Now().Unix() now := time.Now().Unix()
for le := c.lru.Front(); le != nil && le.Value.(*entry).expires <= now; le = c.lru.Front() { for le := c.lru.Front(); le != nil && le.Value.(*entry[K, V]).expires <= now; le = c.lru.Front() {
c.deleteElement(le) c.deleteElement(le)
} }
} }
} }
func (c *LruCache) deleteElement(le *list.Element) { func (c *LruCache[K, V]) deleteElement(le *list.Element) {
c.lru.Remove(le) c.lru.Remove(le)
e := le.Value.(*entry) e := le.Value.(*entry[K, V])
delete(c.cache, e.key) delete(c.cache, e.key)
if c.onEvict != nil { if c.onEvict != nil {
c.onEvict(e.key, e.value) c.onEvict(e.key, e.value)
} }
} }
func (c *LruCache) Clear() error { func (c *LruCache[K, V]) Clear() error {
c.mu.Lock() c.mu.Lock()
c.cache = make(map[any]*list.Element) c.cache = make(map[any]*list.Element)
@ -225,8 +225,13 @@ func (c *LruCache) Clear() error {
return nil return nil
} }
type entry struct { type entry[K comparable, V any] struct {
key any key K
value any value V
expires int64 expires int64
} }
func getZero[T any]() T {
var result T
return result
}

View file

@ -19,7 +19,7 @@ var entries = []struct {
} }
func TestLRUCache(t *testing.T) { func TestLRUCache(t *testing.T) {
c := NewLRUCache() c := NewLRUCache[string, string]()
for _, e := range entries { for _, e := range entries {
c.Set(e.key, e.value) c.Set(e.key, e.value)
@ -32,7 +32,7 @@ func TestLRUCache(t *testing.T) {
for _, e := range entries { for _, e := range entries {
value, ok := c.Get(e.key) value, ok := c.Get(e.key)
if assert.True(t, ok) { if assert.True(t, ok) {
assert.Equal(t, e.value, value.(string)) assert.Equal(t, e.value, value)
} }
} }
@ -45,25 +45,25 @@ func TestLRUCache(t *testing.T) {
} }
func TestLRUMaxAge(t *testing.T) { func TestLRUMaxAge(t *testing.T) {
c := NewLRUCache(WithAge(86400)) c := NewLRUCache[string, string](WithAge[string, string](86400))
now := time.Now().Unix() now := time.Now().Unix()
expected := now + 86400 expected := now + 86400
// Add one expired entry // Add one expired entry
c.Set("foo", "bar") c.Set("foo", "bar")
c.lru.Back().Value.(*entry).expires = now c.lru.Back().Value.(*entry[string, string]).expires = now
// Reset // Reset
c.Set("foo", "bar") c.Set("foo", "bar")
e := c.lru.Back().Value.(*entry) e := c.lru.Back().Value.(*entry[string, string])
assert.True(t, e.expires >= now) assert.True(t, e.expires >= now)
c.lru.Back().Value.(*entry).expires = now c.lru.Back().Value.(*entry[string, string]).expires = now
// Set a few and verify expiration times // Set a few and verify expiration times
for _, s := range entries { for _, s := range entries {
c.Set(s.key, s.value) c.Set(s.key, s.value)
e := c.lru.Back().Value.(*entry) e := c.lru.Back().Value.(*entry[string, string])
assert.True(t, e.expires >= expected && e.expires <= expected+10) assert.True(t, e.expires >= expected && e.expires <= expected+10)
} }
@ -77,7 +77,7 @@ func TestLRUMaxAge(t *testing.T) {
for _, s := range entries { for _, s := range entries {
le, ok := c.cache[s.key] le, ok := c.cache[s.key]
if assert.True(t, ok) { if assert.True(t, ok) {
le.Value.(*entry).expires = now le.Value.(*entry[string, string]).expires = now
} }
} }
@ -88,22 +88,22 @@ func TestLRUMaxAge(t *testing.T) {
} }
func TestLRUpdateOnGet(t *testing.T) { func TestLRUpdateOnGet(t *testing.T) {
c := NewLRUCache(WithAge(86400), WithUpdateAgeOnGet()) c := NewLRUCache[string, string](WithAge[string, string](86400), WithUpdateAgeOnGet[string, string]())
now := time.Now().Unix() now := time.Now().Unix()
expires := now + 86400/2 expires := now + 86400/2
// Add one expired entry // Add one expired entry
c.Set("foo", "bar") c.Set("foo", "bar")
c.lru.Back().Value.(*entry).expires = expires c.lru.Back().Value.(*entry[string, string]).expires = expires
_, ok := c.Get("foo") _, ok := c.Get("foo")
assert.True(t, ok) assert.True(t, ok)
assert.True(t, c.lru.Back().Value.(*entry).expires > expires) assert.True(t, c.lru.Back().Value.(*entry[string, string]).expires > expires)
} }
func TestMaxSize(t *testing.T) { func TestMaxSize(t *testing.T) {
c := NewLRUCache(WithSize(2)) c := NewLRUCache[string, string](WithSize[string, string](2))
// Add one expired entry // Add one expired entry
c.Set("foo", "bar") c.Set("foo", "bar")
_, ok := c.Get("foo") _, ok := c.Get("foo")
@ -117,7 +117,7 @@ func TestMaxSize(t *testing.T) {
} }
func TestExist(t *testing.T) { func TestExist(t *testing.T) {
c := NewLRUCache(WithSize(1)) c := NewLRUCache[int, int](WithSize[int, int](1))
c.Set(1, 2) c.Set(1, 2)
assert.True(t, c.Exist(1)) assert.True(t, c.Exist(1))
c.Set(2, 3) c.Set(2, 3)
@ -130,7 +130,7 @@ func TestEvict(t *testing.T) {
temp = key.(int) + value.(int) temp = key.(int) + value.(int)
} }
c := NewLRUCache(WithEvict(evict), WithSize(1)) c := NewLRUCache[int, int](WithEvict[int, int](evict), WithSize[int, int](1))
c.Set(1, 2) c.Set(1, 2)
c.Set(2, 3) c.Set(2, 3)
@ -138,21 +138,22 @@ func TestEvict(t *testing.T) {
} }
func TestSetWithExpire(t *testing.T) { func TestSetWithExpire(t *testing.T) {
c := NewLRUCache(WithAge(1)) c := NewLRUCache[int, *struct{}](WithAge[int, *struct{}](1))
now := time.Now().Unix() now := time.Now().Unix()
tenSecBefore := time.Unix(now-10, 0) tenSecBefore := time.Unix(now-10, 0)
c.SetWithExpire(1, 2, tenSecBefore) c.SetWithExpire(1, &struct{}{}, tenSecBefore)
// res is expected not to exist, and expires should be empty time.Time // res is expected not to exist, and expires should be empty time.Time
res, expires, exist := c.GetWithExpire(1) res, expires, exist := c.GetWithExpire(1)
assert.Equal(t, nil, res)
assert.True(t, nil == res)
assert.Equal(t, time.Time{}, expires) assert.Equal(t, time.Time{}, expires)
assert.Equal(t, false, exist) assert.Equal(t, false, exist)
} }
func TestStale(t *testing.T) { func TestStale(t *testing.T) {
c := NewLRUCache(WithAge(1), WithStale(true)) c := NewLRUCache[int, int](WithAge[int, int](1), WithStale[int, int](true))
now := time.Now().Unix() now := time.Now().Unix()
tenSecBefore := time.Unix(now-10, 0) tenSecBefore := time.Unix(now-10, 0)
@ -165,11 +166,11 @@ func TestStale(t *testing.T) {
} }
func TestCloneTo(t *testing.T) { func TestCloneTo(t *testing.T) {
o := NewLRUCache(WithSize(10)) o := NewLRUCache[string, int](WithSize[string, int](10))
o.Set("1", 1) o.Set("1", 1)
o.Set("2", 2) o.Set("2", 2)
n := NewLRUCache(WithSize(2)) n := NewLRUCache[string, int](WithSize[string, int](2))
n.Set("3", 3) n.Set("3", 3)
n.Set("4", 4) n.Set("4", 4)

View file

@ -7,16 +7,15 @@ import (
) )
type memoryStore struct { type memoryStore struct {
cache *cache.LruCache cacheIP *cache.LruCache[string, net.IP]
cacheHost *cache.LruCache[uint32, string]
} }
// GetByHost implements store.GetByHost // GetByHost implements store.GetByHost
func (m *memoryStore) GetByHost(host string) (net.IP, bool) { func (m *memoryStore) GetByHost(host string) (net.IP, bool) {
if elm, exist := m.cache.Get(host); exist { if ip, exist := m.cacheIP.Get(host); exist {
ip := elm.(net.IP)
// ensure ip --> host on head of linked list // ensure ip --> host on head of linked list
m.cache.Get(ipToUint(ip.To4())) m.cacheHost.Get(ipToUint(ip.To4()))
return ip, true return ip, true
} }
@ -25,16 +24,14 @@ func (m *memoryStore) GetByHost(host string) (net.IP, bool) {
// PutByHost implements store.PutByHost // PutByHost implements store.PutByHost
func (m *memoryStore) PutByHost(host string, ip net.IP) { func (m *memoryStore) PutByHost(host string, ip net.IP) {
m.cache.Set(host, ip) m.cacheIP.Set(host, ip)
} }
// GetByIP implements store.GetByIP // GetByIP implements store.GetByIP
func (m *memoryStore) GetByIP(ip net.IP) (string, bool) { func (m *memoryStore) GetByIP(ip net.IP) (string, bool) {
if elm, exist := m.cache.Get(ipToUint(ip.To4())); exist { if host, exist := m.cacheHost.Get(ipToUint(ip.To4())); exist {
host := elm.(string)
// ensure host --> ip on head of linked list // ensure host --> ip on head of linked list
m.cache.Get(host) m.cacheIP.Get(host)
return host, true return host, true
} }
@ -43,32 +40,41 @@ func (m *memoryStore) GetByIP(ip net.IP) (string, bool) {
// PutByIP implements store.PutByIP // PutByIP implements store.PutByIP
func (m *memoryStore) PutByIP(ip net.IP, host string) { func (m *memoryStore) PutByIP(ip net.IP, host string) {
m.cache.Set(ipToUint(ip.To4()), host) m.cacheHost.Set(ipToUint(ip.To4()), host)
} }
// DelByIP implements store.DelByIP // DelByIP implements store.DelByIP
func (m *memoryStore) DelByIP(ip net.IP) { func (m *memoryStore) DelByIP(ip net.IP) {
ipNum := ipToUint(ip.To4()) ipNum := ipToUint(ip.To4())
if elm, exist := m.cache.Get(ipNum); exist { if host, exist := m.cacheHost.Get(ipNum); exist {
m.cache.Delete(elm.(string)) m.cacheIP.Delete(host)
} }
m.cache.Delete(ipNum) m.cacheHost.Delete(ipNum)
} }
// Exist implements store.Exist // Exist implements store.Exist
func (m *memoryStore) Exist(ip net.IP) bool { func (m *memoryStore) Exist(ip net.IP) bool {
return m.cache.Exist(ipToUint(ip.To4())) return m.cacheHost.Exist(ipToUint(ip.To4()))
} }
// CloneTo implements store.CloneTo // CloneTo implements store.CloneTo
// only for memoryStore to memoryStore // only for memoryStore to memoryStore
func (m *memoryStore) CloneTo(store store) { func (m *memoryStore) CloneTo(store store) {
if ms, ok := store.(*memoryStore); ok { if ms, ok := store.(*memoryStore); ok {
m.cache.CloneTo(ms.cache) m.cacheIP.CloneTo(ms.cacheIP)
m.cacheHost.CloneTo(ms.cacheHost)
} }
} }
// FlushFakeIP implements store.FlushFakeIP // FlushFakeIP implements store.FlushFakeIP
func (m *memoryStore) FlushFakeIP() error { func (m *memoryStore) FlushFakeIP() error {
return m.cache.Clear() _ = m.cacheIP.Clear()
return m.cacheHost.Clear()
}
func newMemoryStore(size int) *memoryStore {
return &memoryStore{
cacheIP: cache.NewLRUCache[string, net.IP](cache.WithSize[string, net.IP](size)),
cacheHost: cache.NewLRUCache[uint32, string](cache.WithSize[uint32, string](size)),
}
} }

View file

@ -5,7 +5,6 @@ import (
"net" "net"
"sync" "sync"
"github.com/Dreamacro/clash/common/cache"
"github.com/Dreamacro/clash/component/profile/cachefile" "github.com/Dreamacro/clash/component/profile/cachefile"
"github.com/Dreamacro/clash/component/trie" "github.com/Dreamacro/clash/component/trie"
) )
@ -175,9 +174,7 @@ func New(options Options) (*Pool, error) {
cache: cachefile.Cache(), cache: cachefile.Cache(),
} }
} else { } else {
pool.store = &memoryStore{ pool.store = newMemoryStore(options.Size)
cache: cache.NewLRUCache(cache.WithSize(options.Size * 2)),
}
} }
return pool, nil return pool, nil

View file

@ -11,7 +11,7 @@ import (
type ResolverEnhancer struct { type ResolverEnhancer struct {
mode C.DNSMode mode C.DNSMode
fakePool *fakeip.Pool fakePool *fakeip.Pool
mapping *cache.LruCache mapping *cache.LruCache[string, string]
} }
func (h *ResolverEnhancer) FakeIPEnabled() bool { func (h *ResolverEnhancer) FakeIPEnabled() bool {
@ -67,7 +67,7 @@ func (h *ResolverEnhancer) FindHostByIP(ip net.IP) (string, bool) {
if mapping := h.mapping; mapping != nil { if mapping := h.mapping; mapping != nil {
if host, existed := h.mapping.Get(ip.String()); existed { if host, existed := h.mapping.Get(ip.String()); existed {
return host.(string), true return host, true
} }
} }
@ -99,11 +99,11 @@ func (h *ResolverEnhancer) FlushFakeIP() error {
func NewEnhancer(cfg Config) *ResolverEnhancer { func NewEnhancer(cfg Config) *ResolverEnhancer {
var fakePool *fakeip.Pool var fakePool *fakeip.Pool
var mapping *cache.LruCache var mapping *cache.LruCache[string, string]
if cfg.EnhancedMode != C.DNSNormal { if cfg.EnhancedMode != C.DNSNormal {
fakePool = cfg.Pool fakePool = cfg.Pool
mapping = cache.NewLRUCache(cache.WithSize(4096), cache.WithStale(true)) mapping = cache.NewLRUCache[string, string](cache.WithSize[string, string](4096), cache.WithStale[string, string](true))
} }
return &ResolverEnhancer{ return &ResolverEnhancer{

View file

@ -63,7 +63,7 @@ func withHosts(hosts *trie.DomainTrie) middleware {
} }
} }
func withMapping(mapping *cache.LruCache) middleware { func withMapping(mapping *cache.LruCache[string, string]) middleware {
return func(next handler) handler { return func(next handler) handler {
return func(ctx *context.DNSContext, r *D.Msg) (*D.Msg, error) { return func(ctx *context.DNSContext, r *D.Msg) (*D.Msg, error) {
q := r.Question[0] q := r.Question[0]

View file

@ -39,7 +39,7 @@ type Resolver struct {
fallbackDomainFilters []fallbackDomainFilter fallbackDomainFilters []fallbackDomainFilter
fallbackIPFilters []fallbackIPFilter fallbackIPFilters []fallbackIPFilter
group singleflight.Group group singleflight.Group
lruCache *cache.LruCache lruCache *cache.LruCache[string, *D.Msg]
policy *trie.DomainTrie policy *trie.DomainTrie
proxyServer []dnsClient proxyServer []dnsClient
} }
@ -103,7 +103,7 @@ func (r *Resolver) ExchangeContext(ctx context.Context, m *D.Msg) (msg *D.Msg, e
cache, expireTime, hit := r.lruCache.GetWithExpire(q.String()) cache, expireTime, hit := r.lruCache.GetWithExpire(q.String())
if hit { if hit {
now := time.Now() now := time.Now()
msg = cache.(*D.Msg).Copy() msg = cache.Copy()
if expireTime.Before(now) { if expireTime.Before(now) {
setMsgTTL(msg, uint32(1)) // Continue fetch setMsgTTL(msg, uint32(1)) // Continue fetch
go r.exchangeWithoutCache(ctx, m) go r.exchangeWithoutCache(ctx, m)
@ -336,13 +336,13 @@ type Config struct {
func NewResolver(config Config) *Resolver { func NewResolver(config Config) *Resolver {
defaultResolver := &Resolver{ defaultResolver := &Resolver{
main: transform(config.Default, nil), main: transform(config.Default, nil),
lruCache: cache.NewLRUCache(cache.WithSize(4096), cache.WithStale(true)), lruCache: cache.NewLRUCache[string, *D.Msg](cache.WithSize[string, *D.Msg](4096), cache.WithStale[string, *D.Msg](true)),
} }
r := &Resolver{ r := &Resolver{
ipv6: config.IPv6, ipv6: config.IPv6,
main: transform(config.Main, defaultResolver), main: transform(config.Main, defaultResolver),
lruCache: cache.NewLRUCache(cache.WithSize(4096), cache.WithStale(true)), lruCache: cache.NewLRUCache[string, *D.Msg](cache.WithSize[string, *D.Msg](4096), cache.WithStale[string, *D.Msg](true)),
hosts: config.Hosts, hosts: config.Hosts,
} }

View file

@ -16,7 +16,7 @@ import (
D "github.com/miekg/dns" D "github.com/miekg/dns"
) )
func putMsgToCache(c *cache.LruCache, key string, msg *D.Msg) { func putMsgToCache(c *cache.LruCache[string, *D.Msg], key string, msg *D.Msg) {
var ttl uint32 var ttl uint32
switch { switch {
case len(msg.Answer) != 0: case len(msg.Answer) != 0: