Skip to content

Commit

Permalink
Add adapter for patrickmn/go-cache
Browse files Browse the repository at this point in the history
  • Loading branch information
emad-elsaid committed Nov 17, 2024
1 parent a963118 commit ac1034e
Show file tree
Hide file tree
Showing 4 changed files with 121 additions and 2 deletions.
45 changes: 44 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ type Cacher[K any, V any] interface {
}
```

## Note on Cacher interface
## Cacher interface

* `memoize` require the cache interface to implement two simple `Load` and `Store` functions
* So you can adapt any other caching library to `memoize`
Expand All @@ -81,6 +81,49 @@ type Cacher[K any, V any] interface {
* `WithFallback`, `WithReadOnly`, and `WithWriteOnly`...etc wraps a cacher or more to provide or supress functionality
* `cache/adapters` subpackage include adapters for popular Go caches to `Cacher` interface. such as [Hashicorp/LRU](https://github.com/hashicorp/golang-lru)

## Cache adapters

`cache/adapters` include adapters for popular go caches. here are examples for using them with `memoize`

### [Patrickmn/go-cache](https://github.com/patrickmn/go-cache)

```go
import (
"github.com/patrickmn/go-cache"
"github.com/emad-elsaid/memoize"
"github.com/emad-elsaid/memoize/cache/adapters/patrickmn"
)

// strlen function memoized for 5 minutes with expired items cleaned every 10 minutes
var strlen = memoize.NewWithCache(
patrickmn.GoCache[int](
cache.New(5*time.Minute, 10*time.Minute)
),

func(s string) int {
return len(s)
},
)
```

### [Hashicorp/golang-lru/v2](https://github.com/hashicorp/golang-lru)

```go
import (
"https://github.com/hashicorp/golang-lru/v2"
"github.com/emad-elsaid/memoize"
"github.com/emad-elsaid/memoize/cache/adapters/hashicorp"
)

// strlen function memoized with max 1000 stored items
var strlen = memoize.NewWithCache(
hashicorp.LRU(
lru.New[string, int](1000),
),
func(s string) int { return len(s) },
)
```

## Brenchmarks

Each struct is tested with two benchmarks:
Expand Down
2 changes: 1 addition & 1 deletion cache/adapters/hashicorp/lru.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import (
"github.com/emad-elsaid/memoize/cache"
)

// Hashicorp LRU cache interface (the part we need)
// Hashicorp LRU cache interface (the part meomize need)
type HashicorpLRU[K comparable, V any] interface {
Get(K) (V, bool)
Add(K, V) bool
Expand Down
44 changes: 44 additions & 0 deletions cache/adapters/patrickmn/go-cache.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// Package patrickmn includes cache adapter for https://github.com/patrickmn/go-cache package
package patrickmn


import (
"github.com/emad-elsaid/memoize/cache"
)

// GoCacher cache interface (the part memoize need)
type GoCacher interface {
Get(k string) (any, bool)
SetDefault(k string, x any)
}

// GoCache creates a new cacher from the patrickmn/go-cache
// For example:
//
// c := cache.New(5*time.Minute, 10*time.Minute)
// memoize.NewWithCache(
// patrickmn.GoCache[int](c),
// func(s string) int { return len(s) },
// )
func GoCache[V any](c GoCacher) cache.Cacher[string, V] {
return &goCache[V]{c}
}

type goCache[V any] struct {
GoCacher
}

func (h *goCache[V]) Load(key string) (value V, loaded bool) {
out, loaded := h.GoCacher.Get(key)
val, ok := out.(V)

if !ok {
return value, false
}

return val, true
}

func (h *goCache[V]) Store(key string, value V) {
h.GoCacher.SetDefault(key, value)
}
32 changes: 32 additions & 0 deletions cache/adapters/patrickmn/go-cache_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package patrickmn

import (
"testing"
)

type Mock map[string]any


func (m Mock) Get(k string) (any, bool) {
v, ok := m[k]
return v, ok
}

func (m Mock) SetDefault(k string, x any) {
m[k] = x
}

func TestGoCache(t *testing.T) {
m := Mock{}
cacher := GoCache[int](m)
cacher.Store("k1", 1)

v, ok := cacher.Load("k1")
if v != 1 {
t.Error("Expected", 1, "got", v)
}

if !ok {
t.Error("Expected", true, "got", ok)
}
}

0 comments on commit ac1034e

Please sign in to comment.