Refactor: cache use generics
This commit is contained in:
48
common/cache/cache.go
vendored
48
common/cache/cache.go
vendored
@ -7,50 +7,50 @@ import (
|
||||
)
|
||||
|
||||
// Cache store element with a expired time
|
||||
type Cache struct {
|
||||
*cache
|
||||
type Cache[K comparable, V any] struct {
|
||||
*cache[K, V]
|
||||
}
|
||||
|
||||
type cache struct {
|
||||
type cache[K comparable, V any] struct {
|
||||
mapping sync.Map
|
||||
janitor *janitor
|
||||
janitor *janitor[K, V]
|
||||
}
|
||||
|
||||
type element struct {
|
||||
type element[V any] struct {
|
||||
Expired time.Time
|
||||
Payload any
|
||||
Payload V
|
||||
}
|
||||
|
||||
// Put element in Cache with its ttl
|
||||
func (c *cache) Put(key any, payload any, ttl time.Duration) {
|
||||
c.mapping.Store(key, &element{
|
||||
func (c *cache[K, V]) Put(key K, payload V, ttl time.Duration) {
|
||||
c.mapping.Store(key, &element[V]{
|
||||
Payload: payload,
|
||||
Expired: time.Now().Add(ttl),
|
||||
})
|
||||
}
|
||||
|
||||
// Get element in Cache, and drop when it expired
|
||||
func (c *cache) Get(key any) any {
|
||||
func (c *cache[K, V]) Get(key K) V {
|
||||
item, exist := c.mapping.Load(key)
|
||||
if !exist {
|
||||
return nil
|
||||
return getZero[V]()
|
||||
}
|
||||
elm := item.(*element)
|
||||
elm := item.(*element[V])
|
||||
// expired
|
||||
if time.Since(elm.Expired) > 0 {
|
||||
c.mapping.Delete(key)
|
||||
return nil
|
||||
return getZero[V]()
|
||||
}
|
||||
return elm.Payload
|
||||
}
|
||||
|
||||
// GetWithExpire element in Cache with Expire Time
|
||||
func (c *cache) GetWithExpire(key any) (payload any, expired time.Time) {
|
||||
func (c *cache[K, V]) GetWithExpire(key K) (payload V, expired time.Time) {
|
||||
item, exist := c.mapping.Load(key)
|
||||
if !exist {
|
||||
return
|
||||
}
|
||||
elm := item.(*element)
|
||||
elm := item.(*element[V])
|
||||
// expired
|
||||
if time.Since(elm.Expired) > 0 {
|
||||
c.mapping.Delete(key)
|
||||
@ -59,10 +59,10 @@ func (c *cache) GetWithExpire(key any) (payload any, expired time.Time) {
|
||||
return elm.Payload, elm.Expired
|
||||
}
|
||||
|
||||
func (c *cache) cleanup() {
|
||||
func (c *cache[K, V]) cleanup() {
|
||||
c.mapping.Range(func(k, v any) bool {
|
||||
key := k.(string)
|
||||
elm := v.(*element)
|
||||
elm := v.(*element[V])
|
||||
if time.Since(elm.Expired) > 0 {
|
||||
c.mapping.Delete(key)
|
||||
}
|
||||
@ -70,12 +70,12 @@ func (c *cache) cleanup() {
|
||||
})
|
||||
}
|
||||
|
||||
type janitor struct {
|
||||
type janitor[K comparable, V any] struct {
|
||||
interval time.Duration
|
||||
stop chan struct{}
|
||||
}
|
||||
|
||||
func (j *janitor) process(c *cache) {
|
||||
func (j *janitor[K, V]) process(c *cache[K, V]) {
|
||||
ticker := time.NewTicker(j.interval)
|
||||
for {
|
||||
select {
|
||||
@ -88,19 +88,19 @@ func (j *janitor) process(c *cache) {
|
||||
}
|
||||
}
|
||||
|
||||
func stopJanitor(c *Cache) {
|
||||
func stopJanitor[K comparable, V any](c *Cache[K, V]) {
|
||||
c.janitor.stop <- struct{}{}
|
||||
}
|
||||
|
||||
// New return *Cache
|
||||
func New(interval time.Duration) *Cache {
|
||||
j := &janitor{
|
||||
func New[K comparable, V any](interval time.Duration) *Cache[K, V] {
|
||||
j := &janitor[K, V]{
|
||||
interval: interval,
|
||||
stop: make(chan struct{}),
|
||||
}
|
||||
c := &cache{janitor: j}
|
||||
c := &cache[K, V]{janitor: j}
|
||||
go j.process(c)
|
||||
C := &Cache{c}
|
||||
runtime.SetFinalizer(C, stopJanitor)
|
||||
C := &Cache[K, V]{c}
|
||||
runtime.SetFinalizer(C, stopJanitor[K, V])
|
||||
return C
|
||||
}
|
||||
|
28
common/cache/cache_test.go
vendored
28
common/cache/cache_test.go
vendored
@ -11,48 +11,50 @@ import (
|
||||
func TestCache_Basic(t *testing.T) {
|
||||
interval := 200 * time.Millisecond
|
||||
ttl := 20 * time.Millisecond
|
||||
c := New(interval)
|
||||
c := New[string, int](interval)
|
||||
c.Put("int", 1, ttl)
|
||||
c.Put("string", "a", ttl)
|
||||
|
||||
d := New[string, string](interval)
|
||||
d.Put("string", "a", ttl)
|
||||
|
||||
i := c.Get("int")
|
||||
assert.Equal(t, i.(int), 1, "should recv 1")
|
||||
assert.Equal(t, i, 1, "should recv 1")
|
||||
|
||||
s := c.Get("string")
|
||||
assert.Equal(t, s.(string), "a", "should recv 'a'")
|
||||
s := d.Get("string")
|
||||
assert.Equal(t, s, "a", "should recv 'a'")
|
||||
}
|
||||
|
||||
func TestCache_TTL(t *testing.T) {
|
||||
interval := 200 * time.Millisecond
|
||||
ttl := 20 * time.Millisecond
|
||||
now := time.Now()
|
||||
c := New(interval)
|
||||
c := New[string, int](interval)
|
||||
c.Put("int", 1, ttl)
|
||||
c.Put("int2", 2, ttl)
|
||||
|
||||
i := c.Get("int")
|
||||
_, expired := c.GetWithExpire("int2")
|
||||
assert.Equal(t, i.(int), 1, "should recv 1")
|
||||
assert.Equal(t, i, 1, "should recv 1")
|
||||
assert.True(t, now.Before(expired))
|
||||
|
||||
time.Sleep(ttl * 2)
|
||||
i = c.Get("int")
|
||||
j, _ := c.GetWithExpire("int2")
|
||||
assert.Nil(t, i, "should recv nil")
|
||||
assert.Nil(t, j, "should recv nil")
|
||||
assert.True(t, i == 0, "should recv 0")
|
||||
assert.True(t, j == 0, "should recv 0")
|
||||
}
|
||||
|
||||
func TestCache_AutoCleanup(t *testing.T) {
|
||||
interval := 10 * time.Millisecond
|
||||
ttl := 15 * time.Millisecond
|
||||
c := New(interval)
|
||||
c := New[string, int](interval)
|
||||
c.Put("int", 1, ttl)
|
||||
|
||||
time.Sleep(ttl * 2)
|
||||
i := c.Get("int")
|
||||
j, _ := c.GetWithExpire("int")
|
||||
assert.Nil(t, i, "should recv nil")
|
||||
assert.Nil(t, j, "should recv nil")
|
||||
assert.True(t, i == 0, "should recv 0")
|
||||
assert.True(t, j == 0, "should recv 0")
|
||||
}
|
||||
|
||||
func TestCache_AutoGC(t *testing.T) {
|
||||
@ -60,7 +62,7 @@ func TestCache_AutoGC(t *testing.T) {
|
||||
go func() {
|
||||
interval := 10 * time.Millisecond
|
||||
ttl := 15 * time.Millisecond
|
||||
c := New(interval)
|
||||
c := New[string, int](interval)
|
||||
c.Put("int", 1, ttl)
|
||||
sign <- struct{}{}
|
||||
}()
|
||||
|
Reference in New Issue
Block a user