Refactor: lrucache use generics
This commit is contained in:
parent
14878b37f6
commit
673541e2a8
8 changed files with 106 additions and 97 deletions
97
common/cache/lrucache.go
vendored
97
common/cache/lrucache.go
vendored
|
@ -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
|
||||||
|
}
|
||||||
|
|
41
common/cache/lrucache_test.go
vendored
41
common/cache/lrucache_test.go
vendored
|
@ -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)
|
||||||
|
|
||||||
|
|
|
@ -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)),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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{
|
||||||
|
|
|
@ -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]
|
||||||
|
|
|
@ -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,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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:
|
||||||
|
|
Loading…
Reference in a new issue