Skip to content

Commit 49fc89b

Browse files
VineyATviney-shih
andauthored
fix: add compress mechanism + fix the trace length + support New Relic APM (#17)
* support New Relic APM * add compress mechanism for Marshal() and Unmarshal() function * fix the trace length is ~10x times of the cache size Co-authored-by: viney-shih <taili.shih@gmail.com>
1 parent 8d7a414 commit 49fc89b

9 files changed

Lines changed: 241 additions & 9 deletions

File tree

.travis.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
language: go
22

33
go:
4-
- "1.16"
54
- "1.17"
65
- "1.18"
76
- "1.19"

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,8 @@ func ExampleCache_GetByFunc() {
174174
CacheAttributes: map[cache.Type]cache.Attribute{
175175
cache.LocalCacheType: {TTL: 10 * time.Minute},
176176
},
177+
MarshalFunc: msgpack.Marshal, // msgpack is from "github.com/vmihailenco/msgpack/v5"
178+
UnmarshalFunc: msgpack.Unmarshal,
177179
},
178180
})
179181

@@ -215,6 +217,8 @@ func ExampleService_Create_mGetter() {
215217
// HINT: remember to return as a slice, and the item order needs to consist with the keys in the parameters.
216218
return []Object{{Str: "value3", Num: 3}}, nil
217219
},
220+
MarshalFunc: cache.Marshal,
221+
UnmarshalFunc: cache.Unmarshal,
218222
},
219223
})
220224

example_advanced_test.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"time"
77

88
"github.com/go-redis/redis/v8"
9+
"github.com/vmihailenco/msgpack/v5"
910

1011
"github.com/viney-shih/go-cache"
1112
)
@@ -35,6 +36,8 @@ func Example_cacheAsidePattern() {
3536
cache.SharedCacheType: {TTL: time.Hour},
3637
cache.LocalCacheType: {TTL: 10 * time.Minute},
3738
},
39+
MarshalFunc: msgpack.Marshal,
40+
UnmarshalFunc: msgpack.Unmarshal,
3841
},
3942
{
4043
Prefix: "student",
@@ -54,6 +57,8 @@ func Example_cacheAsidePattern() {
5457

5558
return nil, fmt.Errorf("XD")
5659
},
60+
MarshalFunc: cache.Marshal,
61+
UnmarshalFunc: cache.Unmarshal,
5762
},
5863
})
5964

go.mod

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,10 @@ go 1.18
55
require (
66
github.com/go-redis/redis/v8 v8.11.4
77
github.com/google/uuid v1.3.0
8+
github.com/klauspost/compress v1.15.14
89
github.com/stretchr/testify v1.7.0
910
github.com/vmihailenco/go-tinylfu v0.2.2
11+
github.com/vmihailenco/msgpack/v5 v5.3.5
1012
golang.org/x/exp v0.0.0-20210526181343-b47a03e3048a
1113
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
1214
)
@@ -16,5 +18,6 @@ require (
1618
github.com/davecgh/go-spew v1.1.1 // indirect
1719
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
1820
github.com/pmezard/go-difflib v1.0.0 // indirect
21+
github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
1922
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect
2023
)

go.sum

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
2929
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
3030
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
3131
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
32+
github.com/klauspost/compress v1.15.14 h1:i7WCKDToww0wA+9qrUZ1xOjp218vfFo3nTU6UHp+gOc=
33+
github.com/klauspost/compress v1.15.14/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM=
3234
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
3335
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
3436
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
@@ -44,10 +46,15 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
4446
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
4547
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
4648
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
49+
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
4750
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
4851
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
4952
github.com/vmihailenco/go-tinylfu v0.2.2 h1:H1eiG6HM36iniK6+21n9LLpzx1G9R3DJa2UjUjbynsI=
5053
github.com/vmihailenco/go-tinylfu v0.2.2/go.mod h1:CutYi2Q9puTxfcolkliPq4npPuofg9N9t8JVrjzwa3Q=
54+
github.com/vmihailenco/msgpack/v5 v5.3.5 h1:5gO0H1iULLWGhs2H5tbAHIZTV8/cYafcFOr9znI5mJU=
55+
github.com/vmihailenco/msgpack/v5 v5.3.5/go.mod h1:7xyJ9e+0+9SaZT0Wt1RGleJXzli6Q/V5KbhBonMG9jc=
56+
github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g=
57+
github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds=
5158
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
5259
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
5360
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=

marshaler.go

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
package cache
2+
3+
import (
4+
"fmt"
5+
6+
"github.com/klauspost/compress/s2"
7+
"github.com/vmihailenco/msgpack/v5"
8+
)
9+
10+
// ref: https://github.com/go-redis/cache/blob/v8/cache.go
11+
12+
const (
13+
compressionThreshold = 64
14+
timeLen = 4
15+
)
16+
17+
const (
18+
noCompression = 0x0
19+
s2Compression = 0x1
20+
)
21+
22+
// Marshal marshals value by msgpack + compress
23+
func Marshal(value interface{}) ([]byte, error) {
24+
switch value := value.(type) {
25+
case nil:
26+
return nil, nil
27+
case []byte:
28+
return value, nil
29+
case string:
30+
return []byte(value), nil
31+
}
32+
33+
b, err := msgpack.Marshal(value)
34+
if err != nil {
35+
return nil, err
36+
}
37+
38+
return compress(b), nil
39+
}
40+
41+
func compress(data []byte) []byte {
42+
if len(data) < compressionThreshold {
43+
n := len(data) + 1
44+
b := make([]byte, n, n+timeLen)
45+
copy(b, data)
46+
b[len(b)-1] = noCompression
47+
return b
48+
}
49+
50+
n := s2.MaxEncodedLen(len(data)) + 1
51+
b := make([]byte, n, n+timeLen)
52+
b = s2.Encode(b, data)
53+
b = append(b, s2Compression)
54+
return b
55+
}
56+
57+
// Unmarshal unmarshals binary with the compress + msgpack
58+
func Unmarshal(b []byte, value interface{}) error {
59+
if len(b) == 0 {
60+
return nil
61+
}
62+
63+
switch value := value.(type) {
64+
case nil:
65+
return nil
66+
case *[]byte:
67+
clone := make([]byte, len(b))
68+
copy(clone, b)
69+
*value = clone
70+
return nil
71+
case *string:
72+
*value = string(b)
73+
return nil
74+
}
75+
76+
switch c := b[len(b)-1]; c {
77+
case noCompression:
78+
b = b[:len(b)-1]
79+
case s2Compression:
80+
b = b[:len(b)-1]
81+
82+
var err error
83+
b, err = s2.Decode(nil, b)
84+
if err != nil {
85+
return err
86+
}
87+
default:
88+
return fmt.Errorf("unknown compression method: %x", c)
89+
}
90+
91+
return msgpack.Unmarshal(b, value)
92+
}

marshaler_test.go

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
package cache
2+
3+
import (
4+
"testing"
5+
"time"
6+
7+
"github.com/stretchr/testify/suite"
8+
)
9+
10+
var (
11+
mockTimeNow = time.Date(2022, 11, 23, 0, 0, 0, 0, time.Local)
12+
)
13+
14+
type marshalerSuite struct {
15+
suite.Suite
16+
}
17+
18+
func (s *marshalerSuite) SetupSuite() {}
19+
20+
func (s *marshalerSuite) TearDownSuite() {}
21+
22+
func (s *marshalerSuite) SetupTest() {}
23+
24+
func (s *marshalerSuite) TearDownTest() {}
25+
26+
func TestMarshalerSuite(t *testing.T) {
27+
suite.Run(t, new(marshalerSuite))
28+
}
29+
30+
type mockStruct struct {
31+
ID int64
32+
Key string
33+
CreatedAt time.Time
34+
child *mockStruct
35+
}
36+
37+
func (s *marshalerSuite) TestMarshaler() {
38+
var bs []byte
39+
var err error
40+
marshal := Marshal
41+
unmarshal := Unmarshal
42+
43+
// nil
44+
var null error
45+
bs, err = marshal(null)
46+
s.Require().NoError(err)
47+
48+
var retNull error
49+
s.Require().NoError(unmarshal(bs, &retNull))
50+
s.Require().Equal(null, retNull)
51+
52+
// bytes
53+
bytes := []byte("strings to bytes")
54+
bs, err = marshal(bytes)
55+
s.Require().NoError(err)
56+
57+
var retBytes []byte
58+
s.Require().NoError(unmarshal(bs, &retBytes))
59+
s.Require().Equal(bytes, retBytes)
60+
61+
// string
62+
str := "this is a string"
63+
bs, err = marshal(str)
64+
s.Require().NoError(err)
65+
66+
var retStr string
67+
s.Require().NoError(unmarshal(bs, &retStr))
68+
s.Require().Equal(str, retStr)
69+
70+
// pointer
71+
num := 100
72+
intPtr := &num
73+
bs, err = marshal(intPtr)
74+
s.Require().NoError(err)
75+
76+
var retIntPtr *int
77+
s.Require().NoError(unmarshal(bs, &retIntPtr))
78+
s.Require().Equal(intPtr, retIntPtr)
79+
80+
// struct
81+
st := mockStruct{
82+
ID: 28825252,
83+
Key: "I am rich",
84+
CreatedAt: mockTimeNow,
85+
}
86+
bs, err = marshal(st)
87+
s.Require().NoError(err)
88+
89+
retSt := mockStruct{}
90+
s.Require().NoError(unmarshal(bs, &retSt))
91+
s.Require().Equal(st, retSt)
92+
93+
// struct without nil pointer
94+
st2 := mockStruct{
95+
ID: 28825252,
96+
Key: "I am rich",
97+
CreatedAt: mockTimeNow,
98+
child: &mockStruct{
99+
ID: 2266,
100+
},
101+
}
102+
bs, err = marshal(st2)
103+
s.Require().NoError(err)
104+
105+
var retSt2 mockStruct
106+
s.Require().NoError(unmarshal(bs, &retSt2))
107+
s.Require().Equal(st, retSt2)
108+
109+
// compress
110+
st3 := mockStruct{
111+
ID: 1234567890,
112+
Key: `1234567890123456789012345678901234567890123456789012345678901234567890`, // 70 chars
113+
CreatedAt: mockTimeNow,
114+
}
115+
bs, err = marshal(st3)
116+
s.Require().NoError(err)
117+
118+
var retSt3 mockStruct
119+
s.Require().NoError(unmarshal(bs, &retSt3))
120+
s.Require().Equal(st3, retSt3)
121+
}

redis.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ func (r *rds) MSet(
3939
return nil
4040
}
4141

42-
_, err := r.ring.Pipelined(ctx, func(pipe redis.Pipeliner) error {
42+
_, err := r.ring.WithContext(ctx).Pipelined(ctx, func(pipe redis.Pipeliner) error {
4343
// set multiple pairs
4444
pairSlice := make([]interface{}, len(keyVals)*2)
4545
i := 0
@@ -63,7 +63,7 @@ func (r *rds) MSet(
6363
}
6464

6565
func (r *rds) MGet(ctx context.Context, keys []string) ([]Value, error) {
66-
vals, err := r.ring.MGet(ctx, keys...).Result()
66+
vals, err := r.ring.WithContext(ctx).MGet(ctx, keys...).Result()
6767
if err != nil {
6868
return nil, err
6969
}
@@ -88,7 +88,7 @@ func (r *rds) MGet(ctx context.Context, keys []string) ([]Value, error) {
8888
}
8989

9090
func (r *rds) Del(ctx context.Context, keys ...string) error {
91-
_, err := r.ring.Del(ctx, keys...).Result()
91+
_, err := r.ring.WithContext(ctx).Del(ctx, keys...).Result()
9292

9393
return err
9494
}

tinylfu.go

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,8 @@ import (
1111
)
1212

1313
const (
14-
maxOffset = 10 * time.Second
15-
defaultSamples = 100000
16-
defaultOffset = -1
14+
maxOffset = 10 * time.Second
15+
defaultOffset = -1
1716
)
1817

1918
type tinyLFU struct {
@@ -26,10 +25,12 @@ type tinyLFU struct {
2625

2726
// NewTinyLFU generates Adapter with tinylfu
2827
func NewTinyLFU(size int, options ...TinyLFUOptions) Adapter {
29-
// number of keys to track frequency
28+
// samples are the number of keys to track frequency
3029
// TinyLFU works best for small number of keys (~ 100k)
3130
// Ref: https://github.com/vmihailenco/go-cache-benchmark
32-
samples := defaultSamples
31+
// consider the discussing in (https://github.com/ben-manes/caffeine/issues/106),
32+
// choose ~10x the cache size as the default value.
33+
samples := size * 10
3334

3435
o := loadtinyLFUOptions(options...)
3536
if o.offset != defaultOffset && o.offset < 0 {

0 commit comments

Comments
 (0)