Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

gcache with context #61

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 8 additions & 7 deletions arc.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package gcache

import (
"container/list"
"context"
"time"
)

Expand Down Expand Up @@ -163,21 +164,21 @@ func (c *ARC) set(key, value interface{}) (interface{}, error) {
}

// Get a value from cache pool using key if it exists. If not exists and it has LoaderFunc, it will generate the value using you have specified LoaderFunc method returns value.
func (c *ARC) Get(key interface{}) (interface{}, error) {
func (c *ARC) Get(ctx context.Context, key interface{}) (interface{}, error) {
v, err := c.get(key, false)
if err == KeyNotFoundError {
return c.getWithLoader(key, true)
return c.getWithLoader(ctx, key, true)
}
return v, err
}

// GetIFPresent gets a value from cache pool using key if it exists.
// If it dose not exists key, returns KeyNotFoundError.
// And send a request which refresh value for specified key if cache object has LoaderFunc.
func (c *ARC) GetIFPresent(key interface{}) (interface{}, error) {
func (c *ARC) GetIFPresent(ctx context.Context, key interface{}) (interface{}, error) {
v, err := c.get(key, false)
if err == KeyNotFoundError {
return c.getWithLoader(key, false)
return c.getWithLoader(ctx, key, false)
}
return v, err
}
Expand Down Expand Up @@ -237,11 +238,11 @@ func (c *ARC) getValue(key interface{}, onLoad bool) (interface{}, error) {
return nil, KeyNotFoundError
}

func (c *ARC) getWithLoader(key interface{}, isWait bool) (interface{}, error) {
if c.loaderExpireFunc == nil {
func (c *ARC) getWithLoader(ctx context.Context, key interface{}, isWait bool) (interface{}, error) {
if c.loaderExpireFunc == nil || ctx == nil {
return nil, KeyNotFoundError
}
value, _, err := c.load(key, func(v interface{}, expiration *time.Duration, e error) (interface{}, error) {
value, _, err := c.load(ctx, key, func(v interface{}, expiration *time.Duration, e error) (interface{}, error) {
if e != nil {
return nil, e
}
Expand Down
16 changes: 8 additions & 8 deletions arc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,16 +21,16 @@ func TestLoadingARCGet(t *testing.T) {

func TestARCLength(t *testing.T) {
gc := buildTestLoadingCacheWithExpiration(t, TYPE_ARC, 2, time.Millisecond)
gc.Get("test1")
gc.Get("test2")
gc.Get("test3")
gc.Get(ctx, "test1")
gc.Get(ctx, "test2")
gc.Get(ctx, "test3")
length := gc.Len(true)
expectedLength := 2
if length != expectedLength {
t.Errorf("Expected length is %v, not %v", expectedLength, length)
}
time.Sleep(time.Millisecond)
gc.Get("test4")
gc.Get(ctx, "test4")
length = gc.Len(true)
expectedLength = 1
if length != expectedLength {
Expand All @@ -44,7 +44,7 @@ func TestARCEvictItem(t *testing.T) {
gc := buildTestLoadingCache(t, TYPE_ARC, cacheSize, loader)

for i := 0; i < numbers; i++ {
_, err := gc.Get(fmt.Sprintf("Key-%d", i))
_, err := gc.Get(ctx, fmt.Sprintf("Key-%d", i))
if err != nil {
t.Errorf("Unexpected error: %v", err)
}
Expand All @@ -63,7 +63,7 @@ func TestARCPurgeCache(t *testing.T) {
Build()

for i := 0; i < cacheSize; i++ {
_, err := gc.Get(fmt.Sprintf("Key-%d", i))
_, err := gc.Get(ctx, fmt.Sprintf("Key-%d", i))
if err != nil {
t.Errorf("Unexpected error: %v", err)
}
Expand All @@ -85,8 +85,8 @@ func TestARCHas(t *testing.T) {

for i := 0; i < 10; i++ {
t.Run(fmt.Sprint(i), func(t *testing.T) {
gc.Get("test1")
gc.Get("test2")
gc.Get(ctx, "test1")
gc.Get(ctx, "test2")

if gc.Has("test0") {
t.Fatal("should not have test0")
Expand Down
19 changes: 10 additions & 9 deletions cache.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package gcache

import (
"context"
"errors"
"fmt"
"sync"
Expand All @@ -19,8 +20,8 @@ var KeyNotFoundError = errors.New("Key not found.")
type Cache interface {
Set(key, value interface{}) error
SetWithExpire(key, value interface{}, expiration time.Duration) error
Get(key interface{}) (interface{}, error)
GetIFPresent(key interface{}) (interface{}, error)
Get(ctx context.Context, key interface{}) (interface{}, error)
GetIFPresent(ctx context.Context, key interface{}) (interface{}, error)
GetALL(checkExpired bool) map[interface{}]interface{}
get(key interface{}, onLoad bool) (interface{}, error)
Remove(key interface{}) bool
Expand Down Expand Up @@ -48,8 +49,8 @@ type baseCache struct {
}

type (
LoaderFunc func(interface{}) (interface{}, error)
LoaderExpireFunc func(interface{}) (interface{}, *time.Duration, error)
LoaderFunc func(context.Context, interface{}) (interface{}, error)
LoaderExpireFunc func(context.Context, interface{}) (interface{}, *time.Duration, error)
EvictedFunc func(interface{}, interface{})
PurgeVisitorFunc func(interface{}, interface{})
AddedFunc func(interface{}, interface{})
Expand Down Expand Up @@ -86,8 +87,8 @@ func (cb *CacheBuilder) Clock(clock Clock) *CacheBuilder {
// Set a loader function.
// loaderFunc: create a new value with this function if cached value is expired.
func (cb *CacheBuilder) LoaderFunc(loaderFunc LoaderFunc) *CacheBuilder {
cb.loaderExpireFunc = func(k interface{}) (interface{}, *time.Duration, error) {
v, err := loaderFunc(k)
cb.loaderExpireFunc = func(ctx context.Context, k interface{}) (interface{}, *time.Duration, error) {
v, err := loaderFunc(ctx, k)
return v, nil, err
}
return cb
Expand Down Expand Up @@ -189,14 +190,14 @@ func buildCache(c *baseCache, cb *CacheBuilder) {
}

// load a new value using by specified key.
func (c *baseCache) load(key interface{}, cb func(interface{}, *time.Duration, error) (interface{}, error), isWait bool) (interface{}, bool, error) {
v, called, err := c.loadGroup.Do(key, func() (v interface{}, e error) {
func (c *baseCache) load(ctx context.Context, key interface{}, cb func(interface{}, *time.Duration, error) (interface{}, error), isWait bool) (interface{}, bool, error) {
v, called, err := c.loadGroup.Do(ctx, key, func() (v interface{}, e error) {
defer func() {
if r := recover(); r != nil {
e = fmt.Errorf("Loader panics: %v", r)
}
}()
return cb(c.loaderExpireFunc(key))
return cb(c.loaderExpireFunc(ctx, key))
}, isWait)
if err != nil {
return nil, called, err
Expand Down
27 changes: 14 additions & 13 deletions cache_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package gcache

import (
"bytes"
"context"
"encoding/gob"
"sync"
"sync/atomic"
Expand All @@ -21,7 +22,7 @@ func TestLoaderFunc(t *testing.T) {
var testCounter int64
counter := 1000
cache := builder.
LoaderFunc(func(key interface{}) (interface{}, error) {
LoaderFunc(func(_ context.Context, key interface{}) (interface{}, error) {
time.Sleep(10 * time.Millisecond)
return atomic.AddInt64(&testCounter, 1), nil
}).
Expand All @@ -34,7 +35,7 @@ func TestLoaderFunc(t *testing.T) {
wg.Add(1)
go func() {
defer wg.Done()
_, err := cache.Get(0)
_, err := cache.Get(ctx, 0)
if err != nil {
t.Error(err)
}
Expand All @@ -60,7 +61,7 @@ func TestLoaderExpireFuncWithoutExpire(t *testing.T) {
var testCounter int64
counter := 1000
cache := builder.
LoaderExpireFunc(func(key interface{}) (interface{}, *time.Duration, error) {
LoaderExpireFunc(func(_ context.Context, key interface{}) (interface{}, *time.Duration, error) {
return atomic.AddInt64(&testCounter, 1), nil, nil
}).
EvictedFunc(func(key, value interface{}) {
Expand All @@ -72,7 +73,7 @@ func TestLoaderExpireFuncWithoutExpire(t *testing.T) {
wg.Add(1)
go func() {
defer wg.Done()
_, err := cache.Get(0)
_, err := cache.Get(ctx, 0)
if err != nil {
t.Error(err)
}
Expand Down Expand Up @@ -100,7 +101,7 @@ func TestLoaderExpireFuncWithExpire(t *testing.T) {
counter := 1000
expire := 200 * time.Millisecond
cache := builder.
LoaderExpireFunc(func(key interface{}) (interface{}, *time.Duration, error) {
LoaderExpireFunc(func(_ context.Context, key interface{}) (interface{}, *time.Duration, error) {
return atomic.AddInt64(&testCounter, 1), &expire, nil
}).
Build()
Expand All @@ -110,7 +111,7 @@ func TestLoaderExpireFuncWithExpire(t *testing.T) {
wg.Add(1)
go func() {
defer wg.Done()
_, err := cache.Get(0)
_, err := cache.Get(ctx, 0)
if err != nil {
t.Error(err)
}
Expand All @@ -121,7 +122,7 @@ func TestLoaderExpireFuncWithExpire(t *testing.T) {
wg.Add(1)
go func() {
defer wg.Done()
_, err := cache.Get(0)
_, err := cache.Get(ctx, 0)
if err != nil {
t.Error(err)
}
Expand Down Expand Up @@ -164,7 +165,7 @@ func TestLoaderPurgeVisitorFunc(t *testing.T) {
var purgeCounter, evictCounter, loaderCounter int64
counter := 1000
cache := test.cacheBuilder.
LoaderFunc(func(key interface{}) (interface{}, error) {
LoaderFunc(func(_ context.Context, key interface{}) (interface{}, error) {
return atomic.AddInt64(&loaderCounter, 1), nil
}).
EvictedFunc(func(key, value interface{}) {
Expand All @@ -181,7 +182,7 @@ func TestLoaderPurgeVisitorFunc(t *testing.T) {
wg.Add(1)
go func() {
defer wg.Done()
_, err := cache.Get(i)
_, err := cache.Get(ctx, i)
if err != nil {
t.Error(err)
}
Expand Down Expand Up @@ -220,7 +221,7 @@ func TestDeserializeFunc(t *testing.T) {
key2, value2 := "key2", "value2"
cc := New(32).
EvictType(cs.tp).
LoaderFunc(func(k interface{}) (interface{}, error) {
LoaderFunc(func(_ context.Context, k interface{}) (interface{}, error) {
return value1, nil
}).
DeserializeFunc(func(k, v interface{}) (interface{}, error) {
Expand All @@ -239,14 +240,14 @@ func TestDeserializeFunc(t *testing.T) {
return buf.Bytes(), err
}).
Build()
v, err := cc.Get(key1)
v, err := cc.Get(ctx, key1)
if err != nil {
t.Fatal(err)
}
if v != value1 {
t.Errorf("%v != %v", v, value1)
}
v, err = cc.Get(key1)
v, err = cc.Get(ctx, key1)
if err != nil {
t.Fatal(err)
}
Expand All @@ -256,7 +257,7 @@ func TestDeserializeFunc(t *testing.T) {
if err := cc.Set(key2, value2); err != nil {
t.Error(err)
}
v, err = cc.Get(key2)
v, err = cc.Get(ctx, key2)
if err != nil {
t.Error(err)
}
Expand Down
9 changes: 5 additions & 4 deletions examples/autoloading_cache.go
Original file line number Diff line number Diff line change
@@ -1,19 +1,20 @@
package main

import (
"context"
"fmt"
"github.com/bluele/gcache"
)

func main() {
gc := gcache.New(10).
LFU().
LoaderFunc(func(key interface{}) (interface{}, error) {
return fmt.Sprintf("%v-value", key), nil
}).
LoaderFunc(func(_ context.Context, key interface{}) (interface{}, error) {
return fmt.Sprintf("%v-value", key), nil
}).
Build()

v, err := gc.Get("key")
v, err := gc.Get(context.Background(), "key")
if err != nil {
panic(err)
}
Expand Down
5 changes: 3 additions & 2 deletions examples/custom_expiration.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package main

import (
"context"
"fmt"
"github.com/bluele/gcache"
"time"
Expand All @@ -13,7 +14,7 @@ func main() {

gc.SetWithExpire("key", "ok", time.Second*3)

v, err := gc.Get("key")
v, err := gc.Get(context.Background(), "key")
if err != nil {
panic(err)
}
Expand All @@ -22,7 +23,7 @@ func main() {
fmt.Println("waiting 3s for value to expire:")
time.Sleep(time.Second * 3)

v, err = gc.Get("key")
v, err = gc.Get(context.Background(), "key")
if err != nil {
panic(err)
}
Expand Down
3 changes: 2 additions & 1 deletion examples/example.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package main

import (
"context"
"fmt"
"github.com/bluele/gcache"
)
Expand All @@ -11,7 +12,7 @@ func main() {
Build()
gc.Set("key", "ok")

v, err := gc.Get("key")
v, err := gc.Get(context.Background(), "key")
if err != nil {
panic(err)
}
Expand Down
Loading