Skip to content

Commit

Permalink
refactor: cache
Browse files Browse the repository at this point in the history
  • Loading branch information
zenghur committed Apr 17, 2020
1 parent 1212edf commit 9fab1ed
Show file tree
Hide file tree
Showing 10 changed files with 84 additions and 119 deletions.
33 changes: 0 additions & 33 deletions Gopkg.lock

This file was deleted.

33 changes: 0 additions & 33 deletions Gopkg.toml

This file was deleted.

60 changes: 23 additions & 37 deletions bucket.go
Original file line number Diff line number Diff line change
@@ -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
}
Expand All @@ -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
})
}
15 changes: 7 additions & 8 deletions cache.go
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -16,28 +16,24 @@ 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 {
return nil, err
}
}

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)
}

Expand Down Expand Up @@ -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 {
Expand All @@ -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 {
Expand All @@ -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 {
Expand Down
18 changes: 18 additions & 0 deletions cache_test.go
Original file line number Diff line number Diff line change
@@ -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"))
}
6 changes: 4 additions & 2 deletions error.go
Original file line number Diff line number Diff line change
@@ -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")
)
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module github.com/xiaojiaoyu100/roc

go 1.12
go 1.14

require (
github.com/xiaojiaoyu100/curlew v0.2.1
Expand Down
29 changes: 29 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -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=
2 changes: 1 addition & 1 deletion hash.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
5 changes: 1 addition & 4 deletions setter.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down

0 comments on commit 9fab1ed

Please sign in to comment.