From d1393164a526699755c79f52a2a22e4cf761363b Mon Sep 17 00:00:00 2001 From: Tyler Date: Sat, 5 Oct 2019 16:56:24 -0400 Subject: [PATCH] Add LRU cache --- .gitignore | 2 ++ cache.go | 15 +++++++++ go.mod | 1 + go.sum | 2 ++ lru.go | 46 +++++++++++++++++++++++++ lru_test.go | 96 +++++++++++++++++++++++++++++++++++++++++++++++++++++ memory.go | 24 ++++++++------ 7 files changed, 176 insertions(+), 10 deletions(-) create mode 100644 .gitignore create mode 100644 lru.go create mode 100644 lru_test.go diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..caa32e6 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.idea/ +*.iml \ No newline at end of file diff --git a/cache.go b/cache.go index f8ab7fb..6daae6c 100644 --- a/cache.go +++ b/cache.go @@ -14,6 +14,7 @@ const ( Memcache = "memcache" Redis = "redis" Memory = "memory" + Lru = "lru" ) var ( @@ -66,6 +67,20 @@ func New(uri string) (CacheInterface, error) { } return NewMemoryCache(time.Duration(i) * time.Second) + case Lru: + size := query.Get("size") + + if size == "" { + size = "128" + } + + i, err := strconv.Atoi(size) + + if err != nil { + return nil, err + } + + return NewLruCache(i) } return nil, ErrInvalidDriver diff --git a/go.mod b/go.mod index fb1350c..f779336 100644 --- a/go.mod +++ b/go.mod @@ -4,6 +4,7 @@ go 1.12 require ( github.com/bradfitz/gomemcache v0.0.0-20190913173617-a41fca850d0b + github.com/hashicorp/golang-lru v0.5.3 github.com/hoisie/redis v0.0.0-20160730154456-b5c6e81454e0 github.com/patrickmn/go-cache v2.1.0+incompatible github.com/vmihailenco/msgpack/v4 v4.2.0 diff --git a/go.sum b/go.sum index ff4f405..192a3c7 100644 --- a/go.sum +++ b/go.sum @@ -3,6 +3,8 @@ github.com/bradfitz/gomemcache v0.0.0-20190913173617-a41fca850d0b/go.mod h1:H0wQ github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/hashicorp/golang-lru v0.5.3 h1:YPkqC67at8FYaadspW/6uE0COsBxS2656RLEr8Bppgk= +github.com/hashicorp/golang-lru v0.5.3/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hoisie/redis v0.0.0-20160730154456-b5c6e81454e0 h1:mjZV3MTu2A5gwfT5G9IIiLGdwZNciyVq5qqnmJJZ2JI= github.com/hoisie/redis v0.0.0-20160730154456-b5c6e81454e0/go.mod h1:pMYMxVaKJqCDC1JUg/XbPJ4/fSazB25zORpFzqsIGIc= github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= diff --git a/lru.go b/lru.go new file mode 100644 index 0000000..4bf0bbc --- /dev/null +++ b/lru.go @@ -0,0 +1,46 @@ +package cache + +import ( + "github.com/hashicorp/golang-lru" + "time" +) + +type LruCache struct { + c *lru.Cache +} + +func NewLruCache(size int) (CacheInterface, error) { + c, err := lru.New(size) + + if err != nil { + return nil, err + } + + return &LruCache{c: c}, nil +} + +func (mc *LruCache) Has(key string) bool { + _, exists := mc.c.Get(key) + + return exists +} + +func (mc *LruCache) Get(key string, dst ...interface{}) ([]byte, error) { + item, exists := mc.c.Get(key) + + if !exists { + return nil, ErrMemoryCacheNotExists + } + + return memoryCacheGet(item, dst...) +} + +func (mc *LruCache) Set(key string, val interface{}, ttl time.Duration) error { + mc.c.Add(key, val) + return nil +} + +func (mc *LruCache) Del(key string) error { + mc.c.Remove(key) + return nil +} diff --git a/lru_test.go b/lru_test.go new file mode 100644 index 0000000..3ebbf63 --- /dev/null +++ b/lru_test.go @@ -0,0 +1,96 @@ +package cache + +import ( + "testing" + "time" +) + +func TestNew_LruURI(t *testing.T) { + cache, err := New("lru://") + + if err != nil { + t.Fatal("Error creating cache:", err) + } + + if _, ok := cache.(*LruCache); !ok { + t.Fatal("Cache is not instance of MemoryCache") + } +} + +func TestLruCache_Get(t *testing.T) { + cache, err := New("lru://") + + if err != nil { + t.Fatal("Error creating cache:", err) + } + + obj := "test" + + cache.Set("test", obj, time.Minute) + + var new string + + cache.Get("test", &new) + + if obj != new { + t.Fatal("Expected", obj, "got", new) + } +} + +func TestLruCache_GetRaw(t *testing.T) { + cache, err := New("lru://") + + if err != nil { + t.Fatal("Error creating cache:", err) + } + + obj := "test" + + cache.Set("test", obj, time.Minute) + + v, err := cache.Get("test") + + if err != nil { + t.Fatal("Unable to get value:", err) + } + + new := string(v) + + if obj != new { + t.Fatal("Expected", obj, "got", new) + } +} + +func TestLruCache_Has(t *testing.T) { + cache, err := New("lru://") + + if err != nil { + t.Fatal("Error creating cache:", err) + } + + cache.Set("test", "test", time.Minute) + + if !cache.Has("test") { + t.Fatal("Expected cache to have object 'test'") + } +} + +func TestLruCache_Del(t *testing.T) { + cache, err := New("lru://") + + if err != nil { + t.Fatal("Error creating cache:", err) + } + + cache.Set("test", "test", time.Minute) + + if !cache.Has("test") { + t.Fatal("Expected cache to have object 'test'") + } + + cache.Del("test") + + if cache.Has("test") { + t.Fatal("Cache did not properly delete item") + } +} diff --git a/memory.go b/memory.go index 0883be4..195c1fb 100644 --- a/memory.go +++ b/memory.go @@ -35,6 +35,20 @@ func (mc *MemoryCache) Get(key string, dst ...interface{}) ([]byte, error) { return nil, ErrMemoryCacheNotExists } + return memoryCacheGet(item, dst...) +} + +func (mc *MemoryCache) Set(key string, val interface{}, ttl time.Duration) error { + mc.c.Set(key, val, ttl) + return nil +} + +func (mc *MemoryCache) Del(key string) error { + mc.c.Delete(key) + return nil +} + +func memoryCacheGet(item interface{}, dst ...interface{}) ([]byte, error) { if len(dst) == 0 { switch item.(type) { case string: @@ -165,13 +179,3 @@ func (mc *MemoryCache) Get(key string, dst ...interface{}) ([]byte, error) { return nil, nil } - -func (mc *MemoryCache) Set(key string, val interface{}, ttl time.Duration) error { - mc.c.Set(key, val, ttl) - return nil -} - -func (mc *MemoryCache) Del(key string) error { - mc.c.Delete(key) - return nil -}