Skip to content

Commit

Permalink
Merge pull request coocood#30 from docmerlin/master
Browse files Browse the repository at this point in the history
Add GetWithExpiration and GetIntWithExpiration
  • Loading branch information
coocood authored Dec 6, 2017
2 parents bb6b41e + 1b5c6cf commit 3e2d56b
Show file tree
Hide file tree
Showing 3 changed files with 90 additions and 4 deletions.
23 changes: 22 additions & 1 deletion cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,22 @@ func (cache *Cache) Get(key []byte) (value []byte, err error) {
hashVal := hashFunc(key)
segId := hashVal & 255
cache.locks[segId].Lock()
value, err = cache.segments[segId].get(key, hashVal)
value, _, err = cache.segments[segId].get(key, hashVal)
cache.locks[segId].Unlock()
if err == nil {
atomic.AddInt64(&cache.hitCount, 1)
} else {
atomic.AddInt64(&cache.missCount, 1)
}
return
}

// Get the value or not found error.
func (cache *Cache) GetWithExpiration(key []byte) (value []byte, expireAt uint32, err error) {
hashVal := hashFunc(key)
segId := hashVal & 255
cache.locks[segId].Lock()
value, expireAt, err = cache.segments[segId].get(key, hashVal)
cache.locks[segId].Unlock()
if err == nil {
atomic.AddInt64(&cache.hitCount, 1)
Expand Down Expand Up @@ -89,6 +104,12 @@ func (cache *Cache) GetInt(key int64) (value []byte, err error) {
return cache.Get(bKey[:])
}

func (cache *Cache) GetIntWithExpiration(key int64) (value []byte, expireAt uint32, err error) {
var bKey [8]byte
binary.LittleEndian.PutUint64(bKey[:], uint64(key))
return cache.GetWithExpiration(bKey[:])
}

func (cache *Cache) DelInt(key int64) (affected bool) {
var bKey [8]byte
binary.LittleEndian.PutUint64(bKey[:], uint64(key))
Expand Down
67 changes: 65 additions & 2 deletions cache_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,41 @@ func TestOverwrite(t *testing.T) {

}

func TestGetWithExpiration(t *testing.T) {
cache := NewCache(1024)
key := []byte("abcd")
val := []byte("efgh")
err := cache.Set(key, val, 2)
if err != nil {
t.Error("err should be nil", err.Error())
}

res, expiry, err := cache.GetWithExpiration(key)
var expireTime time.Time
var startTime = time.Now()
for {
_, _, err := cache.GetWithExpiration(key)
expireTime = time.Now()
if err != nil {
break
}
if time.Now().Unix() > int64(expiry+1) {
break
}
time.Sleep(1 * time.Millisecond)
}
if time.Second > expireTime.Sub(startTime) || 3*time.Second < expireTime.Sub(startTime) {
t.Error("Cache should expire within a second of the expire time")
}

if err != nil {
t.Error("err should be nil", err.Error())
}
if !bytes.Equal(val, res) {
t.Fatalf("%s should be the same as %s but isn't", res, val)
}
}

func TestExpire(t *testing.T) {
cache := NewCache(1024)
key := []byte("abcd")
Expand Down Expand Up @@ -279,11 +314,11 @@ func TestLargeEntry(t *testing.T) {

func TestInt64Key(t *testing.T) {
cache := NewCache(1024)
err := cache.SetInt(1, []byte("abc"), 0)
err := cache.SetInt(1, []byte("abc"), 3)
if err != nil {
t.Error("err should be nil")
}
err = cache.SetInt(2, []byte("cde"), 0)
err = cache.SetInt(2, []byte("cde"), 3)
if err != nil {
t.Error("err should be nil")
}
Expand All @@ -294,6 +329,19 @@ func TestInt64Key(t *testing.T) {
if !bytes.Equal(val, []byte("abc")) {
t.Error("value not equal")
}
time.Sleep(2 * time.Second)
val, expiry, err := cache.GetIntWithExpiration(1)
if err != nil {
t.Error("err should be nil")
}
if !bytes.Equal(val, []byte("abc")) {
t.Error("value not equal")
}
now := time.Now()
if expiry != uint32(now.Unix()+1) {
t.Errorf("Expiry should one second in the future but was %v", now)
}

affected := cache.DelInt(1)
if !affected {
t.Error("del should return affected true")
Expand Down Expand Up @@ -365,6 +413,21 @@ func BenchmarkCacheGet(b *testing.B) {
}
}

func BenchmarkCacheGetWithExpiration(b *testing.B) {
b.StopTimer()
cache := NewCache(256 * 1024 * 1024)
var key [8]byte
for i := 0; i < b.N; i++ {
binary.LittleEndian.PutUint64(key[:], uint64(i))
cache.Set(key[:], make([]byte, 8), 0)
}
b.StartTimer()
for i := 0; i < b.N; i++ {
binary.LittleEndian.PutUint64(key[:], uint64(i))
cache.GetWithExpiration(key[:])
}
}

func BenchmarkMapGet(b *testing.B) {
b.StopTimer()
m := make(map[string][]byte)
Expand Down
4 changes: 3 additions & 1 deletion segment.go
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ func (seg *segment) evacuate(entryLen int64, slotId uint8, now uint32) (slotModi
return
}

func (seg *segment) get(key []byte, hashVal uint64) (value []byte, err error) {
func (seg *segment) get(key []byte, hashVal uint64) (value []byte, expireAt uint32, err error) {
slotId := uint8(hashVal >> 8)
hash16 := uint16(hashVal >> 16)
slotOff := int32(slotId) * seg.slotCap
Expand All @@ -202,13 +202,15 @@ func (seg *segment) get(key []byte, hashVal uint64) (value []byte, err error) {
var hdrBuf [ENTRY_HDR_SIZE]byte
seg.rb.ReadAt(hdrBuf[:], ptr.offset)
hdr := (*entryHdr)(unsafe.Pointer(&hdrBuf[0]))
expireAt = hdr.expireAt

if hdr.expireAt != 0 && hdr.expireAt <= now {
seg.delEntryPtr(slotId, hash16, ptr.offset)
seg.totalExpired++
err = ErrNotFound
return
}

seg.totalTime += int64(now - hdr.accessTime)
hdr.accessTime = now
seg.rb.WriteAt(hdrBuf[:], ptr.offset)
Expand Down

0 comments on commit 3e2d56b

Please sign in to comment.