Rename cache drivers, add memory driver, add tests and drone integration
continuous-integration/drone/push Build was killed
Details
continuous-integration/drone/push Build was killed
Details
This commit is contained in:
parent
43fe2d6511
commit
b396128a23
|
@ -0,0 +1,13 @@
|
|||
kind: pipeline
|
||||
type: docker
|
||||
name: default
|
||||
|
||||
steps:
|
||||
- name: redis
|
||||
image: redis:latest
|
||||
- name: memcache
|
||||
image: memcached:latest
|
||||
- name: test
|
||||
image: golang
|
||||
commands:
|
||||
- go test
|
62
cache.go
62
cache.go
|
@ -2,7 +2,9 @@ package cache
|
|||
|
||||
import (
|
||||
"errors"
|
||||
"github.com/vmihailenco/msgpack/v4"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
@ -10,6 +12,7 @@ import (
|
|||
const (
|
||||
Memcache = "memcache"
|
||||
Redis = "redis"
|
||||
Memory = "memory"
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -18,7 +21,7 @@ var (
|
|||
|
||||
type CacheInterface interface {
|
||||
Has(key string) bool
|
||||
Get(key string) ([]byte, error)
|
||||
Get(key string, dst ...interface{}) ([]byte, error)
|
||||
Set(key string, val interface{}, ttl time.Duration) (err error)
|
||||
Del(key string) error
|
||||
}
|
||||
|
@ -42,7 +45,64 @@ func New(uri string) (CacheInterface, error) {
|
|||
return NewMemcacheCache(MemcacheSettings{
|
||||
Servers: strings.Split(u.Host, ","),
|
||||
})
|
||||
case Memory:
|
||||
cleanupTime := query.Get("cleanupTime")
|
||||
|
||||
if cleanupTime == "" {
|
||||
cleanupTime = "30"
|
||||
}
|
||||
|
||||
i, err := strconv.Atoi(cleanupTime)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return NewMemoryCache(time.Duration(i) * time.Second)
|
||||
}
|
||||
|
||||
return nil, ErrInvalidDriver
|
||||
}
|
||||
|
||||
func encodeValue(val interface{}) ([]byte, error) {
|
||||
var v []byte
|
||||
|
||||
if b, ok := val.([]byte); ok {
|
||||
v = b
|
||||
} else if s, ok := val.(string); ok {
|
||||
b = []byte(s)
|
||||
} else {
|
||||
b, err := msgpack.Marshal(val)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
v = b
|
||||
}
|
||||
|
||||
return v, nil
|
||||
}
|
||||
|
||||
func decodeDst(b []byte, v interface{}) ([]byte, error) {
|
||||
switch v := v.(type) {
|
||||
case *[]byte:
|
||||
if v != nil {
|
||||
*v = b
|
||||
return b, nil
|
||||
}
|
||||
case *string:
|
||||
if v != nil {
|
||||
*v = string(b)
|
||||
return b, nil
|
||||
}
|
||||
}
|
||||
|
||||
err := msgpack.Unmarshal(b, v)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return b, nil
|
||||
}
|
||||
|
|
2
go.mod
2
go.mod
|
@ -5,4 +5,6 @@ go 1.12
|
|||
require (
|
||||
github.com/bradfitz/gomemcache v0.0.0-20190913173617-a41fca850d0b
|
||||
github.com/hoisie/redis v0.0.0-20160730154456-b5c6e81454e0
|
||||
github.com/patrickmn/go-cache v2.1.0+incompatible
|
||||
github.com/vmihailenco/msgpack/v4 v4.2.0
|
||||
)
|
||||
|
|
33
go.sum
33
go.sum
|
@ -1,4 +1,37 @@
|
|||
github.com/bradfitz/gomemcache v0.0.0-20190913173617-a41fca850d0b h1:L/QXpzIa3pOvUGt1D1lA5KjYhPBAN/3iWdP7xeFS9F0=
|
||||
github.com/bradfitz/gomemcache v0.0.0-20190913173617-a41fca850d0b/go.mod h1:H0wQNHz2YrLsuXOZozoeDmnHXkNCRmMW0gwFWDfEZDA=
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
|
||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/hoisie/redis v0.0.0-20160730154456-b5c6e81454e0 h1:mjZV3MTu2A5gwfT5G9IIiLGdwZNciyVq5qqnmJJZ2JI=
|
||||
github.com/hoisie/redis v0.0.0-20160730154456-b5c6e81454e0/go.mod h1:pMYMxVaKJqCDC1JUg/XbPJ4/fSazB25zORpFzqsIGIc=
|
||||
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
|
||||
github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
|
||||
github.com/vmihailenco/msgpack/v4 v4.2.0 h1:c4L4gd938BvSjSsfr9YahJcvasEf5JZ9W7rcEXfgyys=
|
||||
github.com/vmihailenco/msgpack/v4 v4.2.0/go.mod h1:Mu3B7ZwLd5nNOLVOKt9DecVl7IVg0xkDiEjk6CwMrww=
|
||||
github.com/vmihailenco/tagparser v0.1.0 h1:u6yzKTY6gW/KxL/K2NTEQUOSXZipyGiIRarGjJKmQzU=
|
||||
github.com/vmihailenco/tagparser v0.1.0/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80 h1:Ao/3l156eZf2AW5wK8a7/smtodRU+gha3+BeqJ69lRk=
|
||||
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
google.golang.org/appengine v1.6.1 h1:QzqyMA1tlu6CgqCDUtU9V+ZKhLFT2dkJuANu5QaxI3I=
|
||||
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package cache
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/bradfitz/gomemcache/memcache"
|
||||
"time"
|
||||
)
|
||||
|
@ -28,31 +27,25 @@ func (mc *MemcacheCache) Has(key string) bool {
|
|||
return err != nil
|
||||
}
|
||||
|
||||
func (mc *MemcacheCache) Get(key string) ([]byte, error) {
|
||||
func (mc *MemcacheCache) Get(key string, dst ...interface{}) ([]byte, error) {
|
||||
item, err := mc.backend.Get(key)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(dst) > 0 && dst[0] != nil {
|
||||
return decodeDst(item.Value, dst[0])
|
||||
}
|
||||
|
||||
return item.Value, nil
|
||||
}
|
||||
|
||||
func (mc *MemcacheCache) Set(key string, val interface{}, ttl time.Duration) error {
|
||||
var v []byte
|
||||
v, err := encodeValue(val)
|
||||
|
||||
if b, ok := val.([]byte); ok {
|
||||
v = b
|
||||
} else if s, ok := val.(string); ok {
|
||||
b = []byte(s)
|
||||
} else {
|
||||
b, err := json.Marshal(val)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
v = b
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return mc.backend.Set(&memcache.Item{Key: key, Value: v, Expiration: int32(ttl.Seconds())})
|
|
@ -0,0 +1,38 @@
|
|||
package cache
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func Test_MemcacheURI(t *testing.T) {
|
||||
cache, err := New("memcache://memcache")
|
||||
|
||||
if err != nil {
|
||||
t.Fatal("Error creating cache:", err)
|
||||
}
|
||||
|
||||
if _, ok := cache.(*MemcacheCache); !ok {
|
||||
t.Fatal("Cache is not instance of MemcacheCache")
|
||||
}
|
||||
}
|
||||
|
||||
func Test_MemcacheStoreGet(t *testing.T) {
|
||||
cache, err := New("memcache://memcache")
|
||||
|
||||
if err != nil {
|
||||
t.Fatal("Error creating cache:", err)
|
||||
}
|
||||
|
||||
obj := "test"
|
||||
|
||||
cache.Set("test", obj, time.Minute)
|
||||
|
||||
var new string
|
||||
|
||||
cache.Get("test", &new)
|
||||
|
||||
if obj != new {
|
||||
t.Fatal("Expected", obj, "got", new)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,165 @@
|
|||
package cache
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"github.com/patrickmn/go-cache"
|
||||
"reflect"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
MemoryCacheNotExists = errors.New("item does not exist")
|
||||
)
|
||||
|
||||
type MemoryCache struct {
|
||||
c *cache.Cache
|
||||
}
|
||||
|
||||
func NewMemoryCache(cleanupTime time.Duration) (CacheInterface, error) {
|
||||
c := cache.New(1*time.Minute, cleanupTime)
|
||||
|
||||
return &MemoryCache{c: c}, nil
|
||||
}
|
||||
|
||||
func (mc *MemoryCache) Has(key string) bool {
|
||||
_, exists := mc.c.Get(key)
|
||||
|
||||
return exists
|
||||
}
|
||||
|
||||
func (mc *MemoryCache) Get(key string, dst ...interface{}) ([]byte, error) {
|
||||
item, exists := mc.c.Get(key)
|
||||
|
||||
if !exists {
|
||||
return nil, MemoryCacheNotExists
|
||||
}
|
||||
|
||||
v := dst[0]
|
||||
|
||||
switch v := v.(type) {
|
||||
case *string:
|
||||
if v != nil {
|
||||
*v = item.(string)
|
||||
return nil, nil
|
||||
}
|
||||
case *[]byte:
|
||||
if v != nil {
|
||||
*v = item.([]byte)
|
||||
return nil, nil
|
||||
}
|
||||
case *int:
|
||||
if v != nil {
|
||||
*v = item.(int)
|
||||
return nil, nil
|
||||
}
|
||||
case *int8:
|
||||
if v != nil {
|
||||
*v = item.(int8)
|
||||
return nil, nil
|
||||
}
|
||||
case *int16:
|
||||
if v != nil {
|
||||
*v = item.(int16)
|
||||
return nil, nil
|
||||
}
|
||||
case *int32:
|
||||
if v != nil {
|
||||
*v = item.(int32)
|
||||
return nil, nil
|
||||
}
|
||||
case *int64:
|
||||
if v != nil {
|
||||
*v = item.(int64)
|
||||
return nil, nil
|
||||
}
|
||||
case *uint:
|
||||
if v != nil {
|
||||
*v = item.(uint)
|
||||
return nil, nil
|
||||
}
|
||||
case *uint8:
|
||||
if v != nil {
|
||||
*v = item.(uint8)
|
||||
return nil, nil
|
||||
}
|
||||
case *uint16:
|
||||
if v != nil {
|
||||
*v = item.(uint16)
|
||||
return nil, nil
|
||||
}
|
||||
case *uint32:
|
||||
if v != nil {
|
||||
*v = item.(uint32)
|
||||
return nil, nil
|
||||
}
|
||||
case *uint64:
|
||||
if v != nil {
|
||||
*v = item.(uint64)
|
||||
return nil, nil
|
||||
}
|
||||
case *bool:
|
||||
if v != nil {
|
||||
*v = item.(bool)
|
||||
return nil, nil
|
||||
}
|
||||
case *float32:
|
||||
if v != nil {
|
||||
*v = item.(float32)
|
||||
return nil, nil
|
||||
}
|
||||
case *float64:
|
||||
if v != nil {
|
||||
*v = item.(float64)
|
||||
return nil, nil
|
||||
}
|
||||
case *[]string:
|
||||
*v = item.([]string)
|
||||
return nil, nil
|
||||
case *map[string]string:
|
||||
*v = item.(map[string]string)
|
||||
return nil, nil
|
||||
case *map[string]interface{}:
|
||||
*v = item.(map[string]interface{})
|
||||
return nil, nil
|
||||
case *time.Duration:
|
||||
if v != nil {
|
||||
*v = item.(time.Duration)
|
||||
return nil, nil
|
||||
}
|
||||
case *time.Time:
|
||||
if v != nil {
|
||||
*v = item.(time.Time)
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
|
||||
vv := reflect.ValueOf(dst[0])
|
||||
|
||||
if !vv.IsValid() {
|
||||
return nil, errors.New("dst pointer is not valid")
|
||||
}
|
||||
|
||||
if vv.Kind() != reflect.Ptr {
|
||||
return nil, errors.New("dst pointer is not a pointer")
|
||||
}
|
||||
|
||||
vv = vv.Elem()
|
||||
|
||||
if !vv.IsValid() {
|
||||
return nil, errors.New("dst pointer is not a valid element")
|
||||
}
|
||||
|
||||
vv.Set(reflect.ValueOf(item))
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (mc *MemoryCache) Set(key string, val interface{}, ttl time.Duration) error {
|
||||
mc.c.Set(key, val, ttl)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (mc *MemoryCache) Del(key string) error {
|
||||
mc.c.Delete(key)
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
package cache
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func Test_MemoryURI(t *testing.T) {
|
||||
cache, err := New("memory://")
|
||||
|
||||
if err != nil {
|
||||
t.Fatal("Error creating cache:", err)
|
||||
}
|
||||
|
||||
if _, ok := cache.(*MemoryCache); !ok {
|
||||
t.Fatal("Cache is not instance of MemoryCache")
|
||||
}
|
||||
}
|
||||
|
||||
func Test_MemoryStoreGet(t *testing.T) {
|
||||
cache, err := New("memory://")
|
||||
|
||||
if err != nil {
|
||||
t.Fatal("Error creating cache:", err)
|
||||
}
|
||||
|
||||
obj := "test"
|
||||
|
||||
cache.Set("test", obj, time.Minute)
|
||||
|
||||
var new string
|
||||
|
||||
cache.Get("test", &new)
|
||||
|
||||
if obj != new {
|
||||
t.Fatal("Expected", obj, "got", new)
|
||||
}
|
||||
}
|
|
@ -1,7 +1,6 @@
|
|||
package cache
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/hoisie/redis"
|
||||
"time"
|
||||
)
|
||||
|
@ -31,25 +30,25 @@ func (rc *RedisCache) Has(key string) bool {
|
|||
return b
|
||||
}
|
||||
|
||||
func (rc *RedisCache) Get(key string) ([]byte, error) {
|
||||
return rc.c.Get(key)
|
||||
func (rc *RedisCache) Get(key string, dst ...interface{}) ([]byte, error) {
|
||||
b, err := rc.c.Get(key)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(dst) > 0 && dst[0] != nil {
|
||||
return decodeDst(b, dst[0])
|
||||
}
|
||||
|
||||
return b, err
|
||||
}
|
||||
|
||||
func (rc *RedisCache) Set(key string, val interface{}, ttl time.Duration) error {
|
||||
var v []byte
|
||||
v, err := encodeValue(val)
|
||||
|
||||
if b, ok := val.([]byte); ok {
|
||||
v = b
|
||||
} else if s, ok := val.(string); ok {
|
||||
b = []byte(s)
|
||||
} else {
|
||||
b, err := json.Marshal(val)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
v = b
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return rc.c.Setex(key, int64(ttl.Seconds()), v)
|
|
@ -0,0 +1,38 @@
|
|||
package cache
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func Test_RedisURI(t *testing.T) {
|
||||
cache, err := New("redis://redis")
|
||||
|
||||
if err != nil {
|
||||
t.Fatal("Error creating cache:", err)
|
||||
}
|
||||
|
||||
if _, ok := cache.(*RedisCache); !ok {
|
||||
t.Fatal("Cache is not instance of RedisCache")
|
||||
}
|
||||
}
|
||||
|
||||
func Test_RedisStoreGet(t *testing.T) {
|
||||
cache, err := New("redis://redis")
|
||||
|
||||
if err != nil {
|
||||
t.Fatal("Error creating cache:", err)
|
||||
}
|
||||
|
||||
obj := "test"
|
||||
|
||||
cache.Set("test", obj, time.Minute)
|
||||
|
||||
var new string
|
||||
|
||||
cache.Get("test", &new)
|
||||
|
||||
if obj != new {
|
||||
t.Fatal("Expected", obj, "got", new)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue