mirror of
https://github.com/openimsdk/open-im-server.git
synced 2025-05-06 10:30:59 +08:00
feat: local cache
This commit is contained in:
parent
749eb11830
commit
3e30e50a09
@ -14,18 +14,18 @@ type Cache[V any] interface {
|
||||
func NewCache[V any](slotNum, slotSize int, successTTL, failedTTL time.Duration, target Target, onEvict EvictCallback[string, V]) Cache[V] {
|
||||
c := &slot[V]{
|
||||
n: uint64(slotNum),
|
||||
slots: make([]*LRU[string, V], slotNum),
|
||||
slots: make([]*InertiaLRU[string, V], slotNum),
|
||||
target: target,
|
||||
}
|
||||
for i := 0; i < slotNum; i++ {
|
||||
c.slots[i] = NewLRU[string, V](slotSize, successTTL, failedTTL, c.target, onEvict)
|
||||
c.slots[i] = NewInertiaLRU[string, V](slotSize, successTTL, failedTTL, c.target, onEvict)
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
type slot[V any] struct {
|
||||
n uint64
|
||||
slots []*LRU[string, V]
|
||||
slots []*InertiaLRU[string, V]
|
||||
target Target
|
||||
}
|
||||
|
||||
|
@ -1,5 +1 @@
|
||||
package local
|
||||
|
||||
import "github.com/hashicorp/golang-lru/v2/simplelru"
|
||||
|
||||
type EvictCallback[K comparable, V any] simplelru.EvictCallback[K, V]
|
||||
|
@ -1,108 +1,20 @@
|
||||
package local
|
||||
|
||||
import (
|
||||
"github.com/hashicorp/golang-lru/v2/simplelru"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
import "github.com/hashicorp/golang-lru/v2/simplelru"
|
||||
|
||||
type LRU1[K comparable, V any] interface {
|
||||
type EvictCallback[K comparable, V any] simplelru.EvictCallback[K, V]
|
||||
|
||||
type LRU[K comparable, V any] interface {
|
||||
Get(key K, fetch func() (V, error)) (V, error)
|
||||
Del(key K) bool
|
||||
Stop()
|
||||
}
|
||||
|
||||
//type expirableLRU[K comparable, V any] struct {
|
||||
// core expirable.LRU[K, V]
|
||||
//}
|
||||
//
|
||||
//func (x *expirableLRU[K, V]) Get(key K, fetch func() (V, error)) (V, error) {
|
||||
//
|
||||
// return x.core.Get(key, fetch)
|
||||
//}
|
||||
//
|
||||
//func (x *expirableLRU[K, V]) Del(key K) bool {
|
||||
// return x.core.Remove(key)
|
||||
//}
|
||||
//
|
||||
//func (x *expirableLRU[K, V]) Stop() {
|
||||
//
|
||||
//}
|
||||
|
||||
type waitItem[V any] struct {
|
||||
lock sync.Mutex
|
||||
expires int64
|
||||
err error
|
||||
value V
|
||||
}
|
||||
|
||||
func NewLRU[K comparable, V any](size int, successTTL, failedTTL time.Duration, target Target, onEvict EvictCallback[K, V]) *LRU[K, V] {
|
||||
var cb simplelru.EvictCallback[K, *waitItem[V]]
|
||||
if onEvict != nil {
|
||||
cb = func(key K, value *waitItem[V]) {
|
||||
onEvict(key, value.value)
|
||||
}
|
||||
}
|
||||
core, err := simplelru.NewLRU[K, *waitItem[V]](size, cb)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return &LRU[K, V]{
|
||||
core: core,
|
||||
successTTL: successTTL,
|
||||
failedTTL: failedTTL,
|
||||
target: target,
|
||||
}
|
||||
}
|
||||
|
||||
type LRU[K comparable, V any] struct {
|
||||
lock sync.Mutex
|
||||
core *simplelru.LRU[K, *waitItem[V]]
|
||||
successTTL time.Duration
|
||||
failedTTL time.Duration
|
||||
target Target
|
||||
}
|
||||
|
||||
func (x *LRU[K, V]) Get(key K, fetch func() (V, error)) (V, error) {
|
||||
x.lock.Lock()
|
||||
v, ok := x.core.Get(key)
|
||||
if ok {
|
||||
x.lock.Unlock()
|
||||
v.lock.Lock()
|
||||
expires, value, err := v.expires, v.value, v.err
|
||||
if expires != 0 && expires > time.Now().UnixMilli() {
|
||||
v.lock.Unlock()
|
||||
x.target.IncrGetHit()
|
||||
return value, err
|
||||
}
|
||||
} else {
|
||||
v = &waitItem[V]{}
|
||||
x.core.Add(key, v)
|
||||
v.lock.Lock()
|
||||
x.lock.Unlock()
|
||||
}
|
||||
defer v.lock.Unlock()
|
||||
if v.expires > time.Now().UnixMilli() {
|
||||
return v.value, v.err
|
||||
}
|
||||
v.value, v.err = fetch()
|
||||
if v.err == nil {
|
||||
v.expires = time.Now().Add(x.successTTL).UnixMilli()
|
||||
x.target.IncrGetSuccess()
|
||||
} else {
|
||||
v.expires = time.Now().Add(x.failedTTL).UnixMilli()
|
||||
x.target.IncrGetFailed()
|
||||
}
|
||||
return v.value, v.err
|
||||
}
|
||||
|
||||
func (x *LRU[K, V]) Del(key K) bool {
|
||||
x.lock.Lock()
|
||||
ok := x.core.Remove(key)
|
||||
x.lock.Unlock()
|
||||
return ok
|
||||
}
|
||||
|
||||
func (x *LRU[K, V]) Stop() {
|
||||
type Target interface {
|
||||
IncrGetHit()
|
||||
IncrGetSuccess()
|
||||
IncrGetFailed()
|
||||
|
||||
IncrDelHit()
|
||||
IncrDelNotFound()
|
||||
}
|
||||
|
73
pkg/localcache/local/lru_expirable.go
Normal file
73
pkg/localcache/local/lru_expirable.go
Normal file
@ -0,0 +1,73 @@
|
||||
package local
|
||||
|
||||
import (
|
||||
"github.com/hashicorp/golang-lru/v2/expirable"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
func NewExpirableLRU[K comparable, V any](size int, successTTL, failedTTL time.Duration, target Target, onEvict EvictCallback[K, V]) LRU[K, V] {
|
||||
var cb expirable.EvictCallback[K, *expirableLruItem[V]]
|
||||
if onEvict != nil {
|
||||
cb = func(key K, value *expirableLruItem[V]) {
|
||||
onEvict(key, value.value)
|
||||
}
|
||||
}
|
||||
core := expirable.NewLRU[K, *expirableLruItem[V]](size, cb, successTTL)
|
||||
return &expirableLRU[K, V]{
|
||||
core: core,
|
||||
successTTL: successTTL,
|
||||
failedTTL: failedTTL,
|
||||
target: target,
|
||||
}
|
||||
}
|
||||
|
||||
type expirableLruItem[V any] struct {
|
||||
lock sync.RWMutex
|
||||
err error
|
||||
value V
|
||||
}
|
||||
|
||||
type expirableLRU[K comparable, V any] struct {
|
||||
lock sync.Mutex
|
||||
core *expirable.LRU[K, *expirableLruItem[V]]
|
||||
successTTL time.Duration
|
||||
failedTTL time.Duration
|
||||
target Target
|
||||
}
|
||||
|
||||
func (x *expirableLRU[K, V]) Get(key K, fetch func() (V, error)) (V, error) {
|
||||
x.lock.Lock()
|
||||
v, ok := x.core.Get(key)
|
||||
if ok {
|
||||
x.lock.Unlock()
|
||||
x.target.IncrGetSuccess()
|
||||
v.lock.RLock()
|
||||
defer v.lock.RUnlock()
|
||||
return v.value, v.err
|
||||
} else {
|
||||
v = &expirableLruItem[V]{}
|
||||
x.core.Add(key, v)
|
||||
v.lock.Lock()
|
||||
x.lock.Unlock()
|
||||
defer v.lock.Unlock()
|
||||
v.value, v.err = fetch()
|
||||
if v.err == nil {
|
||||
x.target.IncrGetSuccess()
|
||||
} else {
|
||||
x.target.IncrGetFailed()
|
||||
x.core.Remove(key)
|
||||
}
|
||||
return v.value, v.err
|
||||
}
|
||||
}
|
||||
|
||||
func (x *expirableLRU[K, V]) Del(key K) bool {
|
||||
x.lock.Lock()
|
||||
ok := x.core.Remove(key)
|
||||
x.lock.Unlock()
|
||||
return ok
|
||||
}
|
||||
|
||||
func (x *expirableLRU[K, V]) Stop() {
|
||||
}
|
85
pkg/localcache/local/lru_inertia.go
Normal file
85
pkg/localcache/local/lru_inertia.go
Normal file
@ -0,0 +1,85 @@
|
||||
package local
|
||||
|
||||
import (
|
||||
"github.com/hashicorp/golang-lru/v2/simplelru"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
type inertiaLruItem[V any] struct {
|
||||
lock sync.Mutex
|
||||
expires int64
|
||||
err error
|
||||
value V
|
||||
}
|
||||
|
||||
func NewInertiaLRU[K comparable, V any](size int, successTTL, failedTTL time.Duration, target Target, onEvict EvictCallback[K, V]) *InertiaLRU[K, V] {
|
||||
var cb simplelru.EvictCallback[K, *inertiaLruItem[V]]
|
||||
if onEvict != nil {
|
||||
cb = func(key K, value *inertiaLruItem[V]) {
|
||||
onEvict(key, value.value)
|
||||
}
|
||||
}
|
||||
core, err := simplelru.NewLRU[K, *inertiaLruItem[V]](size, cb)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return &InertiaLRU[K, V]{
|
||||
core: core,
|
||||
successTTL: successTTL,
|
||||
failedTTL: failedTTL,
|
||||
target: target,
|
||||
}
|
||||
}
|
||||
|
||||
type InertiaLRU[K comparable, V any] struct {
|
||||
lock sync.Mutex
|
||||
core *simplelru.LRU[K, *inertiaLruItem[V]]
|
||||
successTTL time.Duration
|
||||
failedTTL time.Duration
|
||||
target Target
|
||||
}
|
||||
|
||||
func (x *InertiaLRU[K, V]) Get(key K, fetch func() (V, error)) (V, error) {
|
||||
x.lock.Lock()
|
||||
v, ok := x.core.Get(key)
|
||||
if ok {
|
||||
x.lock.Unlock()
|
||||
v.lock.Lock()
|
||||
expires, value, err := v.expires, v.value, v.err
|
||||
if expires != 0 && expires > time.Now().UnixMilli() {
|
||||
v.lock.Unlock()
|
||||
x.target.IncrGetHit()
|
||||
return value, err
|
||||
}
|
||||
} else {
|
||||
v = &inertiaLruItem[V]{}
|
||||
x.core.Add(key, v)
|
||||
v.lock.Lock()
|
||||
x.lock.Unlock()
|
||||
}
|
||||
defer v.lock.Unlock()
|
||||
if v.expires > time.Now().UnixMilli() {
|
||||
return v.value, v.err
|
||||
}
|
||||
v.value, v.err = fetch()
|
||||
if v.err == nil {
|
||||
v.expires = time.Now().Add(x.successTTL).UnixMilli()
|
||||
x.target.IncrGetSuccess()
|
||||
} else {
|
||||
v.expires = time.Now().Add(x.failedTTL).UnixMilli()
|
||||
x.target.IncrGetFailed()
|
||||
}
|
||||
return v.value, v.err
|
||||
}
|
||||
|
||||
func (x *InertiaLRU[K, V]) Del(key K) bool {
|
||||
x.lock.Lock()
|
||||
ok := x.core.Remove(key)
|
||||
x.lock.Unlock()
|
||||
return ok
|
||||
}
|
||||
|
||||
func (x *InertiaLRU[K, V]) Stop() {
|
||||
|
||||
}
|
@ -43,7 +43,7 @@ func (r *cacheTarget) String() string {
|
||||
func TestName(t *testing.T) {
|
||||
target := &cacheTarget{}
|
||||
l := NewCache[string](100, 1000, time.Second*20, time.Second*5, target, nil)
|
||||
//l := NewLRU[string, string](1000, time.Second*20, time.Second*5, target)
|
||||
//l := NewInertiaLRU[string, string](1000, time.Second*20, time.Second*5, target)
|
||||
|
||||
fn := func(key string, n int, fetch func() (string, error)) {
|
||||
for i := 0; i < n; i++ {
|
@ -1,10 +0,0 @@
|
||||
package local
|
||||
|
||||
type Target interface {
|
||||
IncrGetHit()
|
||||
IncrGetSuccess()
|
||||
IncrGetFailed()
|
||||
|
||||
IncrDelHit()
|
||||
IncrDelNotFound()
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user