Skip to content

Commit

Permalink
Add clock interface, real and fake clocks (#33)
Browse files Browse the repository at this point in the history
  • Loading branch information
pcman312 authored and bluele committed Jun 10, 2017
1 parent d84b26d commit bbe6d2a
Show file tree
Hide file tree
Showing 12 changed files with 157 additions and 100 deletions.
10 changes: 6 additions & 4 deletions arc.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ func (c *ARC) SetWithExpire(key, value interface{}, expiration time.Duration) er
return err
}

t := time.Now().Add(expiration)
t := c.clock.Now().Add(expiration)
item.(*arcItem).expiration = &t
return nil
}
Expand All @@ -89,14 +89,15 @@ func (c *ARC) set(key, value interface{}) (interface{}, error) {
item.value = value
} else {
item = &arcItem{
clock: c.clock,
key: key,
value: value,
}
c.items[key] = item
}

if c.expiration != nil {
t := time.Now().Add(*c.expiration)
t := c.clock.Now().Add(*c.expiration)
item.expiration = &t
}

Expand Down Expand Up @@ -243,7 +244,7 @@ func (c *ARC) getWithLoader(key interface{}, isWait bool) (interface{}, error) {
return nil, err
}
if expiration != nil {
t := time.Now().Add(*expiration)
t := c.clock.Now().Add(*expiration)
item.(*arcItem).expiration = &t
}
return v, nil
Expand Down Expand Up @@ -343,7 +344,7 @@ func (it *arcItem) IsExpired(now *time.Time) bool {
return false
}
if now == nil {
t := time.Now()
t := it.clock.Now()
now = &t
}
return it.expiration.Before(*now)
Expand All @@ -355,6 +356,7 @@ type arcList struct {
}

type arcItem struct {
clock Clock
key interface{}
value interface{}
expiration *time.Time
Expand Down
20 changes: 9 additions & 11 deletions arc_test.go
Original file line number Diff line number Diff line change
@@ -1,30 +1,28 @@
package gcache_test
package gcache

import (
"fmt"
"testing"
"time"

"github.com/bluele/gcache"
)

func buildARCache(size int) gcache.Cache {
return gcache.New(size).
func buildARCache(size int) Cache {
return New(size).
ARC().
EvictedFunc(evictedFuncForARC).
Build()
}

func buildLoadingARCache(size int) gcache.Cache {
return gcache.New(size).
func buildLoadingARCache(size int) Cache {
return New(size).
ARC().
LoaderFunc(loader).
EvictedFunc(evictedFuncForARC).
Build()
}

func buildLoadingARCacheWithExpiration(size int, ep time.Duration) gcache.Cache {
return gcache.New(size).
func buildLoadingARCacheWithExpiration(size int, ep time.Duration) Cache {
return New(size).
ARC().
Expiration(ep).
LoaderFunc(loader).
Expand Down Expand Up @@ -82,9 +80,9 @@ func TestARCEvictItem(t *testing.T) {
}

func TestARCGetIFPresent(t *testing.T) {
testGetIFPresent(t, gcache.TYPE_ARC)
testGetIFPresent(t, TYPE_ARC)
}

func TestARCGetALL(t *testing.T) {
testGetALL(t, gcache.TYPE_ARC)
testGetALL(t, TYPE_ARC)
}
13 changes: 11 additions & 2 deletions cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ type Cache interface {
}

type baseCache struct {
clock Clock
size int
loaderExpireFunc LoaderExpireFunc
evictedFunc EvictedFunc
Expand All @@ -53,6 +54,7 @@ type (
)

type CacheBuilder struct {
clock Clock
tp string
size int
loaderExpireFunc LoaderExpireFunc
Expand All @@ -68,11 +70,17 @@ func New(size int) *CacheBuilder {
panic("gcache: size <= 0")
}
return &CacheBuilder{
tp: TYPE_SIMPLE,
size: size,
clock: NewRealClock(),
tp: TYPE_SIMPLE,
size: size,
}
}

func (cb *CacheBuilder) Clock(clock Clock) *CacheBuilder {
cb.clock = clock
return cb
}

// Set a loader function.
// loaderFunc: create a new value with this function if cached value is expired.
func (cb *CacheBuilder) LoaderFunc(loaderFunc LoaderFunc) *CacheBuilder {
Expand Down Expand Up @@ -157,6 +165,7 @@ func (cb *CacheBuilder) build() Cache {
}

func buildCache(c *baseCache, cb *CacheBuilder) {
c.clock = cb.clock
c.size = cb.size
c.loaderExpireFunc = cb.loaderExpireFunc
c.expiration = cb.expiration
Expand Down
49 changes: 23 additions & 26 deletions cache_test.go
Original file line number Diff line number Diff line change
@@ -1,24 +1,21 @@
package gcache_test
package gcache

import (
"bytes"
"encoding/gob"
"testing"
"time"

"sync"
"sync/atomic"

"github.com/bluele/gcache"
"testing"
"time"
)

func TestLoaderFunc(t *testing.T) {
size := 2
var testCaches = []*gcache.CacheBuilder{
gcache.New(size).Simple(),
gcache.New(size).LRU(),
gcache.New(size).LFU(),
gcache.New(size).ARC(),
var testCaches = []*CacheBuilder{
New(size).Simple(),
New(size).LRU(),
New(size).LFU(),
New(size).ARC(),
}
for _, builder := range testCaches {
var testCounter int64
Expand Down Expand Up @@ -53,11 +50,11 @@ func TestLoaderFunc(t *testing.T) {

func TestLoaderExpireFuncWithoutExpire(t *testing.T) {
size := 2
var testCaches = []*gcache.CacheBuilder{
gcache.New(size).Simple(),
gcache.New(size).LRU(),
gcache.New(size).LFU(),
gcache.New(size).ARC(),
var testCaches = []*CacheBuilder{
New(size).Simple(),
New(size).LRU(),
New(size).LFU(),
New(size).ARC(),
}
for _, builder := range testCaches {
var testCounter int64
Expand Down Expand Up @@ -92,11 +89,11 @@ func TestLoaderExpireFuncWithoutExpire(t *testing.T) {

func TestLoaderExpireFuncWithExpire(t *testing.T) {
size := 2
var testCaches = []*gcache.CacheBuilder{
gcache.New(size).Simple(),
gcache.New(size).LRU(),
gcache.New(size).LFU(),
gcache.New(size).ARC(),
var testCaches = []*CacheBuilder{
New(size).Simple(),
New(size).LRU(),
New(size).LFU(),
New(size).ARC(),
}
for _, builder := range testCaches {
var testCounter int64
Expand Down Expand Up @@ -143,16 +140,16 @@ func TestDeserializeFunc(t *testing.T) {
var cases = []struct {
tp string
}{
{gcache.TYPE_SIMPLE},
{gcache.TYPE_LRU},
{gcache.TYPE_LFU},
{gcache.TYPE_ARC},
{TYPE_SIMPLE},
{TYPE_LRU},
{TYPE_LFU},
{TYPE_ARC},
}

for _, cs := range cases {
key1, value1 := "key1", "value1"
key2, value2 := "key2", "value2"
cc := gcache.New(32).
cc := New(32).
EvictType(cs.tp).
LoaderFunc(func(k interface{}) (interface{}, error) {
return value1, nil
Expand Down
53 changes: 53 additions & 0 deletions clock.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package gcache

import (
"sync"
"time"
)

type Clock interface {
Now() time.Time
}

type RealClock struct{}

func NewRealClock() Clock {
return RealClock{}
}

func (rc RealClock) Now() time.Time {
t := time.Now()
return t
}

type FakeClock interface {
Clock

Advance(d time.Duration)
}

func NewFakeClock() FakeClock {
return &fakeclock{
// Taken from github.com/jonboulle/clockwork: use a fixture that does not fulfill Time.IsZero()
now: time.Date(1984, time.April, 4, 0, 0, 0, 0, time.UTC),
}
}

type fakeclock struct {
now time.Time

mutex sync.RWMutex
}

func (fc *fakeclock) Now() time.Time {
fc.mutex.RLock()
defer fc.mutex.RUnlock()
t := fc.now
return t
}

func (fc *fakeclock) Advance(d time.Duration) {
fc.mutex.Lock()
defer fc.mutex.Unlock()
fc.now = fc.now.Add(d)
}
32 changes: 15 additions & 17 deletions helpers_test.go
Original file line number Diff line number Diff line change
@@ -1,18 +1,16 @@
package gcache_test
package gcache

import (
"fmt"
"testing"
"time"

"github.com/bluele/gcache"
)

func loader(key interface{}) (interface{}, error) {
return fmt.Sprintf("valueFor%s", key), nil
}

func testSetCache(t *testing.T, gc gcache.Cache, numbers int) {
func testSetCache(t *testing.T, gc Cache, numbers int) {
for i := 0; i < numbers; i++ {
key := fmt.Sprintf("Key-%d", i)
value, err := loader(key)
Expand All @@ -24,7 +22,7 @@ func testSetCache(t *testing.T, gc gcache.Cache, numbers int) {
}
}

func testGetCache(t *testing.T, gc gcache.Cache, numbers int) {
func testGetCache(t *testing.T, gc Cache, numbers int) {
for i := 0; i < numbers; i++ {
key := fmt.Sprintf("Key-%d", i)
v, err := gc.Get(key)
Expand All @@ -39,17 +37,17 @@ func testGetCache(t *testing.T, gc gcache.Cache, numbers int) {
}

func testGetIFPresent(t *testing.T, evT string) {
cache := gcache.
cache :=
New(8).
EvictType(evT).
LoaderFunc(
func(key interface{}) (interface{}, error) {
return "value", nil
}).
Build()
EvictType(evT).
LoaderFunc(
func(key interface{}) (interface{}, error) {
return "value", nil
}).
Build()

v, err := cache.GetIFPresent("key")
if err != gcache.KeyNotFoundError {
if err != KeyNotFoundError {
t.Errorf("err should not be %v", err)
}

Expand All @@ -66,11 +64,11 @@ func testGetIFPresent(t *testing.T, evT string) {

func testGetALL(t *testing.T, evT string) {
size := 8
cache := gcache.
cache :=
New(size).
Expiration(time.Millisecond).
EvictType(evT).
Build()
Expiration(time.Millisecond).
EvictType(evT).
Build()
for i := 0; i < size; i++ {
cache.Set(i, i*i)
}
Expand Down
Loading

0 comments on commit bbe6d2a

Please sign in to comment.