Skip to content

Commit 9fab1ed

Browse files
committed
refactor: cache
1 parent 1212edf commit 9fab1ed

10 files changed

+84
-119
lines changed

Gopkg.lock

-33
This file was deleted.

Gopkg.toml

-33
This file was deleted.

bucket.go

+23-37
Original file line numberDiff line numberDiff line change
@@ -1,56 +1,49 @@
11
package roc
22

33
import (
4-
"container/list"
54
"sync"
65
"time"
76
)
87

8+
// Bucket is a piece of cache.
99
type Bucket struct {
10-
guard sync.Mutex
11-
objs *list.List
12-
coll map[string]*list.Element
10+
coll sync.Map
1311
}
1412

13+
// NewBucket returns a bucket.
1514
func NewBucket() (*Bucket, error) {
1615
b := new(Bucket)
1716
return b, nil
1817
}
1918

19+
// Get returns a corresponding value.
2020
func (b *Bucket) Get(key string) (interface{}, error) {
21-
b.guard.Lock()
22-
defer b.guard.Unlock()
23-
element, hit := b.coll[key]
21+
value, hit := b.coll.Load(key)
2422
if !hit {
2523
return nil, ErrMiss
2624
}
27-
u, ok := element.Value.(*Unit)
25+
u, ok := value.(*Unit)
2826
if !ok {
2927
return nil, ErrMiss
3028
}
31-
3229
if u.Expire() {
3330
return nil, ErrMiss
3431
}
35-
b.objs.MoveToFront(element)
3632
return u.Data, nil
3733
}
3834

35+
// Set sets a value for a key.
3936
func (b *Bucket) Set(key string, data interface{}, d time.Duration) error {
40-
b.guard.Lock()
41-
defer b.guard.Unlock()
42-
element, hit := b.coll[key]
37+
value, hit := b.coll.Load(key)
4338
if !hit {
4439
unit := new(Unit)
4540
unit.Key = key
4641
unit.Data = data
4742
unit.ExpirationTime = time.Now().UTC().Add(d)
48-
e := b.objs.PushFront(unit)
49-
b.coll[key] = e
43+
b.coll.Store(key, unit)
5044
return nil
5145
}
52-
b.objs.MoveToFront(element)
53-
unit, ok := element.Value.(*Unit)
46+
unit, ok := value.(*Unit)
5447
if !ok {
5548
return nil
5649
}
@@ -59,29 +52,22 @@ func (b *Bucket) Set(key string, data interface{}, d time.Duration) error {
5952
return nil
6053
}
6154

55+
// Del deletes a key.
6256
func (b *Bucket) Del(key string) {
63-
b.guard.Lock()
64-
defer b.guard.Unlock()
65-
element, hit := b.coll[key]
66-
if !hit {
67-
return
68-
}
69-
b.del(key, element)
70-
}
71-
72-
func (b *Bucket) del(key string, e *list.Element) {
73-
b.objs.Remove(e)
74-
delete(b.coll, key)
57+
b.coll.Delete(key)
7558
}
7659

7760
func (b *Bucket) gc() {
78-
b.guard.Lock()
79-
defer b.guard.Unlock()
80-
81-
for e := b.objs.Front(); e != nil; e = e.Next() {
82-
unit, ok := e.Value.(*Unit)
83-
if ok && unit.Expire() {
84-
b.del(unit.Key, e)
61+
b.coll.Range(func(key, value interface{}) (keep bool) {
62+
keep = true
63+
unit, ok := value.(*Unit)
64+
if !ok {
65+
return
8566
}
86-
}
67+
if !unit.Expire() {
68+
return
69+
}
70+
b.Del(key.(string))
71+
return
72+
})
8773
}

cache.go

+7-8
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
package roc
22

33
import (
4-
"container/list"
54
"context"
65
"time"
76

87
"github.com/xiaojiaoyu100/curlew"
98
)
109

10+
// Cache is a store.
1111
type Cache struct {
1212
GCInterval time.Duration
1313
BucketNum int
@@ -16,28 +16,24 @@ type Cache struct {
1616
close chan struct{}
1717
}
1818

19+
// New returns a new cache.
1920
func New(setters ...Setter) (*Cache, error) {
2021
c := new(Cache)
21-
c.GCInterval = 60 * time.Second
22-
c.BucketNum = 16
22+
c.GCInterval = 120 * time.Second
23+
c.BucketNum = 1024
2324

2425
for _, setter := range setters {
2526
if err := setter(c); err != nil {
2627
return nil, err
2728
}
2829
}
2930

30-
if !isPowerOfTwo(c.BucketNum) {
31-
return nil, ErrorBucketNum
32-
}
3331
c.buckets = make([]*Bucket, 0, c.BucketNum)
3432
for idx := 0; idx < c.BucketNum; idx++ {
3533
bucket, err := NewBucket()
3634
if err != nil {
3735
return nil, err
3836
}
39-
bucket.coll = make(map[string]*list.Element)
40-
bucket.objs = list.New()
4137
c.buckets = append(c.buckets, bucket)
4238
}
4339

@@ -78,6 +74,7 @@ func (c *Cache) gc() {
7874
}()
7975
}
8076

77+
// Get returns a value.
8178
func (c *Cache) Get(key string) (interface{}, error) {
8279
idx, err := c.hashIndex(key)
8380
if err != nil {
@@ -86,6 +83,7 @@ func (c *Cache) Get(key string) (interface{}, error) {
8683
return c.buckets[idx].Get(key)
8784
}
8885

86+
// Set sets a value.
8987
func (c *Cache) Set(key string, value interface{}, duration time.Duration) error {
9088
idx, err := c.hashIndex(key)
9189
if err != nil {
@@ -94,6 +92,7 @@ func (c *Cache) Set(key string, value interface{}, duration time.Duration) error
9492
return c.buckets[idx].Set(key, value, duration)
9593
}
9694

95+
// Del deletes a key.
9796
func (c *Cache) Del(key string) error {
9897
idx, err := c.hashIndex(key)
9998
if err != nil {

cache_test.go

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package roc
2+
3+
import (
4+
"testing"
5+
"time"
6+
)
7+
8+
func TestCache_Set(t *testing.T) {
9+
c, err := New()
10+
if err != nil {
11+
t.Fatalf("new cache failed")
12+
}
13+
err = c.Set("one", "two", time.Second)
14+
if err != nil {
15+
t.Fatalf("set failed")
16+
}
17+
t.Log(c.Get("one"))
18+
}

error.go

+4-2
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
package roc
22

3+
// Error implements an error interface.
34
type Error string
45

6+
// Error returns a error string.
57
func (e Error) Error() string {
68
return string(e)
79
}
810

911
const (
10-
ErrMiss = Error("cache miss")
11-
ErrorBucketNum = Error("bucket num must be the power of two")
12+
// ErrMiss represents a cache miss.
13+
ErrMiss = Error("cache miss")
1214
)

go.mod

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
module github.com/xiaojiaoyu100/roc
22

3-
go 1.12
3+
go 1.14
44

55
require (
66
github.com/xiaojiaoyu100/curlew v0.2.1

go.sum

+29
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,47 @@
1+
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
2+
github.com/aliyun/aliyun-tablestore-go-sdk v1.5.0 h1:TbUP3xdzTTtW2rUaI/qY8wgf4BLsyKy3vtaKa39Tr4Y=
3+
github.com/aliyun/aliyun-tablestore-go-sdk v1.5.0/go.mod h1:jixoiNNRR/4ziq0yub1fTlxmDcQwlpkaujpaWIATQWM=
4+
github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
5+
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
16
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
27
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
8+
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
9+
github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
10+
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
11+
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
12+
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
313
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
414
github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s=
515
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
16+
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
17+
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
18+
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
619
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
720
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
21+
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
822
github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4=
923
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
24+
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
25+
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
26+
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
1027
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
1128
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
1229
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
30+
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
1331
github.com/xiaojiaoyu100/curlew v0.2.1 h1:6fj7fIepQMvjna/SW720ImW8jX+47+nF3F7DicdMpig=
1432
github.com/xiaojiaoyu100/curlew v0.2.1/go.mod h1:T1E4wpTXciXHO0YJPhHz8sQB/MGPLt+M7efYFKK36Hg=
33+
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
34+
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
35+
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
36+
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
1537
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
1638
golang.org/x/sys v0.0.0-20190924062700-2aa67d56cdd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
1739
golang.org/x/sys v0.0.0-20190924092210-98129a5cf4a0 h1:wFKb4oFjFfHcg2hJGd5iawQsCWP5jTIE+5yNFcsMttM=
1840
golang.org/x/sys v0.0.0-20190924092210-98129a5cf4a0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
41+
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
42+
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
43+
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
44+
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
45+
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
46+
gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
47+
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=

hash.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,5 @@ func (c *Cache) hashIndex(key string) (int, error) {
88
if err != nil {
99
return 0, err
1010
}
11-
return int(hash.Sum64() & uint64(c.BucketNum - 1)), nil
11+
return int(hash.Sum64() & uint64(c.BucketNum-1)), nil
1212
}

setter.go

+1-4
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,9 @@ import (
44
"time"
55
)
66

7+
// Setter configures a cache.
78
type Setter func(c *Cache) error
89

9-
func isPowerOfTwo(num int) bool {
10-
return (num != 0) && ((num & (num - 1)) == 0)
11-
}
12-
1310
// WithBucketNum set the number of buckets
1411
func WithBucketNum(num int) Setter {
1512
return func(c *Cache) error {

0 commit comments

Comments
 (0)