diff --git a/Gopkg.lock b/Gopkg.lock deleted file mode 100644 index 3a57be8..0000000 --- a/Gopkg.lock +++ /dev/null @@ -1,33 +0,0 @@ -# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'. - - -[[projects]] - name = "github.com/konsorten/go-windows-terminal-sequences" - packages = ["."] - revision = "f55edac94c9bbba5d6182a4be46d86a2c9b5b50e" - version = "v1.0.2" - -[[projects]] - name = "github.com/sirupsen/logrus" - packages = ["."] - revision = "839c75faf7f98a33d445d181f3018b5c3409a45e" - version = "v1.4.2" - -[[projects]] - name = "github.com/xiaojiaoyu100/curlew" - packages = ["."] - revision = "b9ac0f9fcfabb049db3adab6cf7b68e40b243252" - version = "v0.2.1" - -[[projects]] - branch = "master" - name = "golang.org/x/sys" - packages = ["unix"] - revision = "98129a5cf4a0207f7e2202f458737deeaca0514a" - -[solve-meta] - analyzer-name = "dep" - analyzer-version = 1 - inputs-digest = "1ceaa2efbacae919f2f72becc62444f882b389b0c87c0be638cee777d2acf933" - solver-name = "gps-cdcl" - solver-version = 1 diff --git a/Gopkg.toml b/Gopkg.toml deleted file mode 100644 index adcc52f..0000000 --- a/Gopkg.toml +++ /dev/null @@ -1,33 +0,0 @@ -# Gopkg.toml example -# -# Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md -# for detailed Gopkg.toml documentation. -# -# required = ["github.com/user/thing/cmd/thing"] -# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"] -# -# [[constraint]] -# name = "github.com/user/project" -# version = "1.0.0" -# -# [[constraint]] -# name = "github.com/user/project2" -# branch = "dev" -# source = "github.com/myfork/project2" -# -# [[override]] -# name = "github.com/x/y" -# version = "2.4.0" -# -# [prune] -# non-go = false -# go-tests = true -# unused-packages = true - - -[[constraint]] - name = "github.com/xiaojiaoyu100/curlew" - version = "0.2.1" - -[prune] - unused-packages = true diff --git a/bucket.go b/bucket.go index 0ae6ba5..75af1b8 100755 --- a/bucket.go +++ b/bucket.go @@ -1,56 +1,49 @@ package roc import ( - "container/list" "sync" "time" ) +// Bucket is a piece of cache. type Bucket struct { - guard sync.Mutex - objs *list.List - coll map[string]*list.Element + coll sync.Map } +// NewBucket returns a bucket. func NewBucket() (*Bucket, error) { b := new(Bucket) return b, nil } +// Get returns a corresponding value. func (b *Bucket) Get(key string) (interface{}, error) { - b.guard.Lock() - defer b.guard.Unlock() - element, hit := b.coll[key] + value, hit := b.coll.Load(key) if !hit { return nil, ErrMiss } - u, ok := element.Value.(*Unit) + u, ok := value.(*Unit) if !ok { return nil, ErrMiss } - if u.Expire() { return nil, ErrMiss } - b.objs.MoveToFront(element) return u.Data, nil } +// Set sets a value for a key. func (b *Bucket) Set(key string, data interface{}, d time.Duration) error { - b.guard.Lock() - defer b.guard.Unlock() - element, hit := b.coll[key] + value, hit := b.coll.Load(key) if !hit { unit := new(Unit) unit.Key = key unit.Data = data unit.ExpirationTime = time.Now().UTC().Add(d) - e := b.objs.PushFront(unit) - b.coll[key] = e + b.coll.Store(key, unit) return nil } - b.objs.MoveToFront(element) - unit, ok := element.Value.(*Unit) + unit, ok := value.(*Unit) if !ok { return nil } @@ -59,29 +52,22 @@ func (b *Bucket) Set(key string, data interface{}, d time.Duration) error { return nil } +// Del deletes a key. func (b *Bucket) Del(key string) { - b.guard.Lock() - defer b.guard.Unlock() - element, hit := b.coll[key] - if !hit { - return - } - b.del(key, element) -} - -func (b *Bucket) del(key string, e *list.Element) { - b.objs.Remove(e) - delete(b.coll, key) + b.coll.Delete(key) } func (b *Bucket) gc() { - b.guard.Lock() - defer b.guard.Unlock() - - for e := b.objs.Front(); e != nil; e = e.Next() { - unit, ok := e.Value.(*Unit) - if ok && unit.Expire() { - b.del(unit.Key, e) + b.coll.Range(func(key, value interface{}) (keep bool) { + keep = true + unit, ok := value.(*Unit) + if !ok { + return } - } + if !unit.Expire() { + return + } + b.Del(key.(string)) + return + }) } diff --git a/cache.go b/cache.go index ace46b3..59f4491 100755 --- a/cache.go +++ b/cache.go @@ -1,13 +1,13 @@ package roc import ( - "container/list" "context" "time" "github.com/xiaojiaoyu100/curlew" ) +// Cache is a store. type Cache struct { GCInterval time.Duration BucketNum int @@ -16,10 +16,11 @@ type Cache struct { close chan struct{} } +// New returns a new cache. func New(setters ...Setter) (*Cache, error) { c := new(Cache) - c.GCInterval = 60 * time.Second - c.BucketNum = 16 + c.GCInterval = 120 * time.Second + c.BucketNum = 1024 for _, setter := range setters { if err := setter(c); err != nil { @@ -27,17 +28,12 @@ func New(setters ...Setter) (*Cache, error) { } } - if !isPowerOfTwo(c.BucketNum) { - return nil, ErrorBucketNum - } c.buckets = make([]*Bucket, 0, c.BucketNum) for idx := 0; idx < c.BucketNum; idx++ { bucket, err := NewBucket() if err != nil { return nil, err } - bucket.coll = make(map[string]*list.Element) - bucket.objs = list.New() c.buckets = append(c.buckets, bucket) } @@ -78,6 +74,7 @@ func (c *Cache) gc() { }() } +// Get returns a value. func (c *Cache) Get(key string) (interface{}, error) { idx, err := c.hashIndex(key) if err != nil { @@ -86,6 +83,7 @@ func (c *Cache) Get(key string) (interface{}, error) { return c.buckets[idx].Get(key) } +// Set sets a value. func (c *Cache) Set(key string, value interface{}, duration time.Duration) error { idx, err := c.hashIndex(key) if err != nil { @@ -94,6 +92,7 @@ func (c *Cache) Set(key string, value interface{}, duration time.Duration) error return c.buckets[idx].Set(key, value, duration) } +// Del deletes a key. func (c *Cache) Del(key string) error { idx, err := c.hashIndex(key) if err != nil { diff --git a/cache_test.go b/cache_test.go new file mode 100644 index 0000000..5f647c9 --- /dev/null +++ b/cache_test.go @@ -0,0 +1,18 @@ +package roc + +import ( + "testing" + "time" +) + +func TestCache_Set(t *testing.T) { + c, err := New() + if err != nil { + t.Fatalf("new cache failed") + } + err = c.Set("one", "two", time.Second) + if err != nil { + t.Fatalf("set failed") + } + t.Log(c.Get("one")) +} diff --git a/error.go b/error.go index b9e2cd4..d90c217 100755 --- a/error.go +++ b/error.go @@ -1,12 +1,14 @@ package roc +// Error implements an error interface. type Error string +// Error returns a error string. func (e Error) Error() string { return string(e) } const ( - ErrMiss = Error("cache miss") - ErrorBucketNum = Error("bucket num must be the power of two") + // ErrMiss represents a cache miss. + ErrMiss = Error("cache miss") ) diff --git a/go.mod b/go.mod index 32bfa6c..4dc0099 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/xiaojiaoyu100/roc -go 1.12 +go 1.14 require ( github.com/xiaojiaoyu100/curlew v0.2.1 diff --git a/go.sum b/go.sum index df502af..dea1531 100644 --- a/go.sum +++ b/go.sum @@ -1,18 +1,47 @@ +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/aliyun/aliyun-tablestore-go-sdk v1.5.0 h1:TbUP3xdzTTtW2rUaI/qY8wgf4BLsyKy3vtaKa39Tr4Y= +github.com/aliyun/aliyun-tablestore-go-sdk v1.5.0/go.mod h1:jixoiNNRR/4ziq0yub1fTlxmDcQwlpkaujpaWIATQWM= +github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= +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/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s= github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +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/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/xiaojiaoyu100/curlew v0.2.1 h1:6fj7fIepQMvjna/SW720ImW8jX+47+nF3F7DicdMpig= github.com/xiaojiaoyu100/curlew v0.2.1/go.mod h1:T1E4wpTXciXHO0YJPhHz8sQB/MGPLt+M7efYFKK36Hg= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +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-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190924062700-2aa67d56cdd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190924092210-98129a5cf4a0 h1:wFKb4oFjFfHcg2hJGd5iawQsCWP5jTIE+5yNFcsMttM= golang.org/x/sys v0.0.0-20190924092210-98129a5cf4a0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/hash.go b/hash.go index d28c021..376a11a 100755 --- a/hash.go +++ b/hash.go @@ -8,5 +8,5 @@ func (c *Cache) hashIndex(key string) (int, error) { if err != nil { return 0, err } - return int(hash.Sum64() & uint64(c.BucketNum - 1)), nil + return int(hash.Sum64() & uint64(c.BucketNum-1)), nil } diff --git a/setter.go b/setter.go index b6b8ac9..9b953c4 100755 --- a/setter.go +++ b/setter.go @@ -4,12 +4,9 @@ import ( "time" ) +// Setter configures a cache. type Setter func(c *Cache) error -func isPowerOfTwo(num int) bool { - return (num != 0) && ((num & (num - 1)) == 0) -} - // WithBucketNum set the number of buckets func WithBucketNum(num int) Setter { return func(c *Cache) error {