Skip to content

Commit

Permalink
added new store (freecache)
Browse files Browse the repository at this point in the history
  • Loading branch information
thejasn committed Jul 28, 2020
1 parent 5a5061c commit f31406a
Show file tree
Hide file tree
Showing 3 changed files with 430 additions and 0 deletions.
111 changes: 111 additions & 0 deletions store/freecache.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
package store

import (
"errors"
"fmt"
)

const (
// FreecacheType represents the storage type as a string value
FreecacheType = "freecache"
// FreecacheTagPattern represents the tag pattern to be used as a key in specified storage
FreecacheTagPattern = "freecache_tag_%s"
)

// FreecacheClientInterface represents a coocood/freecache client
type FreecacheClientInterface interface {
Get(key []byte) (value []byte, err error)
GetInt(key int64) (value []byte, err error)
Set(key, value []byte, expireSeconds int) (err error)
SetInt(key int64, value []byte, expireSeconds int) (err error)
Del(key []byte) (affected bool)
DelInt(key int64) (affected bool)
Clear()
}

type FreecacheStore struct {
client FreecacheClientInterface
options *Options
}

func NewFreecache(client FreecacheClientInterface, options *Options) *FreecacheStore {
if options == nil {
options = &Options{}
}

return &FreecacheStore{
client: client,
options: options,
}
}
func (f *FreecacheStore) Get(key interface{}) (interface{}, error) {
var err error
var result interface{}
if k, ok := key.(string); ok {
result, err = f.client.Get([]byte(k))
if err != nil {
return nil, errors.New("value not found in Freecache store")
}
return result, err
}

return nil, errors.New("key type not supported by Freecache store")

}

// Set sets a key, value and expiration for a cache entry and stores it in the cache.
// If the key is larger than 65535 or value is larger than 1/1024 of the cache size,
// the entry will not be written to the cache. expireSeconds <= 0 means no expire,
// but it can be evicted when cache is full.
func (f *FreecacheStore) Set(key interface{}, value interface{}, options *Options) error {
var err error
var val []byte

//type check for value, as freecache only supports value of type []byte
switch v := value.(type) {
case []byte:
val = v
default:
return errors.New("value type not supported by Freecache store")
}

if k, ok := key.(string); ok {
err = f.client.Set([]byte(k), val, int(options.Expiration.Seconds()))
if err != nil {
return fmt.Errorf("size of key: %v, value: %v, err: %v", k, len(val), err)
}
return err
}
return errors.New("key type not supported by Freecache store")
}

func (f *FreecacheStore) Delete(key interface{}) error {
if v, ok := key.(string); ok {
if f.client.Del([]byte(v)) {
return nil
}
return fmt.Errorf("failed to delete key %v", key)
}
return errors.New("key type not supported by Freecache store")

}

func (f *FreecacheStore) Invalidate(options InvalidateOptions) error {
if tags := options.TagsValue(); len(tags) > 0 {
for _, tag := range tags {
var tagKey = fmt.Sprintf(FreecacheTagPattern, tag)
return f.Delete([]byte(tagKey))
}
}

return nil
}

func (f *FreecacheStore) Clear() error {
f.client.Clear()
return nil
}

func (f *FreecacheStore) GetType() string {
return FreecacheType
}
188 changes: 188 additions & 0 deletions store/freecache_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
package store

import (
"errors"
"fmt"
"testing"
"time"

mocksStore "github.com/eko/gocache/test/mocks/store/clients"
"github.com/golang/mock/gomock"
"github.com/stretchr/testify/assert"
)

func TestNewFreecache(t *testing.T) {
// Given
ctrl := gomock.NewController(t)
defer ctrl.Finish()

client := mocksStore.NewMockFreecacheClientInterface(ctrl)
options := &Options{
Expiration: 6 * time.Second,
}

// When
store := NewFreecache(client, options)

// Then
assert.IsType(t, new(FreecacheStore), store)
assert.Equal(t, client, store.client)
assert.Equal(t, options, store.options)
}

func TestFreecacheGet(t *testing.T) {
// Given
ctrl := gomock.NewController(t)
defer ctrl.Finish()

client := mocksStore.NewMockFreecacheClientInterface(ctrl)
client.EXPECT().Get([]byte("key1")).Return([]byte("val1"), nil)
client.EXPECT().Get([]byte("key2")).Return([]byte("val2"), nil)

s := NewFreecache(client, nil)

value, err := s.Get("key1")
assert.Nil(t, err)
assert.Equal(t, []byte("val1"), value)

value, err = s.Get("key2")
assert.Nil(t, err)
assert.Equal(t, []byte("val2"), value)
}

func TestFreecacheGetNotFound(t *testing.T) {
// Given
ctrl := gomock.NewController(t)
defer ctrl.Finish()

client := mocksStore.NewMockFreecacheClientInterface(ctrl)
client.EXPECT().Get([]byte("key1")).Return(nil, errors.New("value not found in Freecache store"))

s := NewFreecache(client, nil)

value, err := s.Get("key1")
assert.EqualError(t, err, "value not found in Freecache store")
assert.Nil(t, value)
}

func TestFreecacheSet(t *testing.T) {
// Given
ctrl := gomock.NewController(t)
defer ctrl.Finish()

cacheKey := "my-key"
cacheValue := []byte("my-cache-value")
options := &Options{
Expiration: 6 * time.Second,
}

client := mocksStore.NewMockFreecacheClientInterface(ctrl)
client.EXPECT().Set([]byte(cacheKey), cacheValue, 6).Return(nil)

s := NewFreecache(client, options)
err := s.Set(cacheKey, cacheValue, options)
assert.Nil(t, err)
}

func TestFreecacheSetInvalidValue(t *testing.T) {
// Given
ctrl := gomock.NewController(t)
defer ctrl.Finish()

cacheKey := "my-key"
cacheValue := "my-cache-value"
options := &Options{
Expiration: 6 * time.Second,
}
expectedErr := errors.New("value type not supported by Freecache store")

client := mocksStore.NewMockFreecacheClientInterface(ctrl)

s := NewFreecache(client, options)
err := s.Set(cacheKey, cacheValue, options)
assert.Equal(t, expectedErr, err)
}

func TestFreecacheSetInvalidSize(t *testing.T) {
// Given
ctrl := gomock.NewController(t)
defer ctrl.Finish()

cacheKey := "my-key"
cacheValue := []byte("my-cache-value")
options := &Options{
Expiration: 6 * time.Second,
}
expectedErr := fmt.Errorf("size of key: %v, value: %v, err: %v", cacheKey, cacheValue, errors.New(""))
client := mocksStore.NewMockFreecacheClientInterface(ctrl)
client.EXPECT().Set([]byte(cacheKey), cacheValue, 6).Return(expectedErr)

s := NewFreecache(client, options)
err := s.Set(cacheKey, cacheValue, options)
assert.NotNil(t, err)

}

func TestFreecacheSetInvalidKey(t *testing.T) {
// Given
ctrl := gomock.NewController(t)
defer ctrl.Finish()

cacheKey := 1
cacheValue := []byte("my-cache-value")
options := &Options{
Expiration: 6 * time.Second,
}

expectedErr := errors.New("key type not supported by Freecache store")

client := mocksStore.NewMockFreecacheClientInterface(ctrl)

s := NewFreecache(client, options)
err := s.Set(cacheKey, cacheValue, options)
assert.Equal(t, expectedErr, err)
}

func TestFreecacheDelete(t *testing.T) {
// Given
ctrl := gomock.NewController(t)
defer ctrl.Finish()

cacheKey := "key"

client := mocksStore.NewMockFreecacheClientInterface(ctrl)
client.EXPECT().Del(gomock.Any()).Return(true)

s := NewFreecache(client, nil)
err := s.Delete(cacheKey)
assert.Nil(t, err)
}

func TestFreecacheDeleteFailed(t *testing.T) {
// Given
ctrl := gomock.NewController(t)
defer ctrl.Finish()

cacheKey := "key"
expectedErr := fmt.Errorf("failed to delete key %v", cacheKey)
client := mocksStore.NewMockFreecacheClientInterface(ctrl)
client.EXPECT().Del(gomock.Any()).Return(false)

s := NewFreecache(client, nil)
err := s.Delete(cacheKey)
assert.Equal(t, expectedErr, err)
}

func TestFreecacheDeleteInvalidKey(t *testing.T) {
// Given
ctrl := gomock.NewController(t)
defer ctrl.Finish()

cacheKey := 1
expectedErr := errors.New("key type not supported by Freecache store")
client := mocksStore.NewMockFreecacheClientInterface(ctrl)

s := NewFreecache(client, nil)
err := s.Delete(cacheKey)
assert.Equal(t, expectedErr, err)
}
Loading

0 comments on commit f31406a

Please sign in to comment.