Skip to content

Commit

Permalink
revert
Browse files Browse the repository at this point in the history
  • Loading branch information
Yiling-J committed Nov 30, 2021
1 parent a2aa672 commit bcd3069
Show file tree
Hide file tree
Showing 6 changed files with 109 additions and 144 deletions.
15 changes: 13 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -227,8 +227,6 @@ err := client.SimpleCacheStore.Invalid(ctx, "foo")
err := client.SimpleCacheStore.Update(ctx, "foo")
```
#### Invalid all keys: `InvalidAll`
**Warning:** This method is implemented using Redis `HyperLogLog` + `List` for memory efficiency, but inaccurate(according to Redis, standard error of 0.81%).
Intend use of this method is: you update store version, then calling this method to clean legacy cache.
```go
// invalid all version 1 simple cache
client.SimpleCacheStore.InvalidAll(ctx, "1")
Expand Down Expand Up @@ -312,3 +310,16 @@ client.SetLogger(logger)
- **HIT**: cache hit to redis, if you enable singleflight, grouped requests only log once.
- **MISS**: cache miss
- **FETCH**: fetch data from fetcher

## Performance
Parallel benchmarks of Cacheme alongside [go-redis/cache](https://github.com/go-redis/cache):
- params: 10000/1000000 hits, 10 keys loop, TTL 10s, `SetParallelism(100)`, singleflight on
- go-redis/cache without local cache
```
cpu: Intel(R) Core(TM) i7-9750H CPU @ 2.60GHz
BenchmarkCachemeGetParallel-12 10000 198082 ns/op
BenchmarkCacheGetParallel-12 10000 189766 ns/op
BenchmarkCachemeGetParallel-12 1000000 9501 ns/op
BenchmarkCacheGetParallel-12 1000000 4323 ns/op
```
At 10000 hits, result almost same. At 1000000 hits, go-redis/cache is about 2 times faster than Cacheme. but keep in mind, go-redis/cache is based on singleflight **only**, not truly distributed. This bench case is single executable, not the real load case.
86 changes: 60 additions & 26 deletions cacheme.go
Original file line number Diff line number Diff line change
Expand Up @@ -269,48 +269,82 @@ func SchemaToStore(pkg string, path string, prefix string, stores []*StoreSchema
}

func InvalidAll(ctx context.Context, group string, client RedisClient) error {
for {
keys, err := client.LRange(ctx, group, 0, 499).Result()
if len(keys) == 0 {
return nil
}
if err != nil {
return err
}
err = client.LTrim(ctx, group, 500, -1).Err()
if err != nil {
return err
iter := client.SScan(ctx, group, 0, "", 200).Iterator()
invalids := []string{}
for iter.Next(ctx) {
invalids = append(invalids, iter.Val())
if len(invalids) == 600 {
err := client.Unlink(ctx, invalids...).Err()
if err != nil {
fmt.Println(err)
}
invalids = []string{}
}
err = client.Unlink(ctx, keys...).Err()
}

if len(invalids) > 0 {
err := client.Unlink(ctx, invalids...).Err()
if err != nil {
return err
}
err = client.Unlink(ctx, group).Err()
return err
}
return nil
}

func InvalidAllCluster(ctx context.Context, group string, client RedisClient) error {

clusterClient := client.(*redis.ClusterClient)
for {
keys, err := client.LRange(ctx, group, 0, 499).Result()
if len(keys) == 0 {
return nil
}

iter := clusterClient.SScan(ctx, group, 0, "", 200).Iterator()
invalids := make(map[string][]string)
counter := 0
for iter.Next(ctx) {

key := iter.Val()
node, err := clusterClient.MasterForKey(ctx, key)
if err != nil {
return err
}
err = client.LTrim(ctx, group, 500, -1).Err()
if err != nil {
return err

addr := node.Options().Addr

if v, ok := invalids[addr]; ok {
v = append(v, key)
invalids[addr] = v

} else {
invalids[addr] = []string{key}
}
pipeline := clusterClient.Pipeline()
for _, key := range keys {
pipeline.Unlink(ctx, key)
counter++

if counter == 600 {

for _, v := range invalids {

err := clusterClient.Unlink(ctx, v...).Err()
if err != nil {
fmt.Println(err)
}
}
invalids = make(map[string][]string)
counter = 0
}
_, err = pipeline.Exec(ctx)
if err != nil {
return err
}

if counter > 0 {
for _, v := range invalids {

err := clusterClient.Unlink(ctx, v...).Err()
if err != nil {
fmt.Println(err)
}
}
err := clusterClient.Unlink(ctx, group).Err()
return err
}
return nil
}

func Marshal(v interface{}) ([]byte, error) {
Expand Down
93 changes: 10 additions & 83 deletions integration/cache_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import (
"context"
"errors"
"fmt"
"strconv"
"sync"
"testing"
"time"
Expand Down Expand Up @@ -503,9 +502,8 @@ func TestCacheKey(t *testing.T) {
keys, err := client.Redis().Keys(ctx, "*").Result()
require.Nil(t, err)
expected := []string{
"cacheme:simple:foo:v1", // cache key
"cacheme:meta:group:Simple:v1", // group key
"cacheme:meta:group:Simple:v1:hll", // hll key
"cacheme:simple:foo:v1", // cache key
"cacheme:group:Simple:v1", // group key
}
require.ElementsMatch(t, keys, expected)
CleanRedis()
Expand All @@ -515,9 +513,8 @@ func TestCacheKey(t *testing.T) {
keys, err = client.Redis().Keys(ctx, "*").Result()
require.Nil(t, err)
expected = []string{
"cacheme:simplem:a:b:c:v1", // cache key
"cacheme:meta:group:SimpleMulti:v1", // group key
"cacheme:meta:group:SimpleMulti:v1:hll", // hll key
"cacheme:simplem:a:b:c:v1", // cache key
"cacheme:group:SimpleMulti:v1", // group key
}
require.ElementsMatch(t, keys, expected)
CleanRedis()
Expand All @@ -527,9 +524,8 @@ func TestCacheKey(t *testing.T) {
keys, err = client.Redis().Keys(ctx, "*").Result()
require.Nil(t, err)
expected = []string{
"cacheme:simplem:b:c:a:v1", // cache key
"cacheme:meta:group:SimpleMulti:v1", // group key
"cacheme:meta:group:SimpleMulti:v1:hll", // hll key
"cacheme:simplem:b:c:a:v1", // cache key
"cacheme:group:SimpleMulti:v1", // group key
}
require.ElementsMatch(t, keys, expected)
}
Expand Down Expand Up @@ -576,9 +572,8 @@ func TestCacheVersion(t *testing.T) {
keys, err := client.Redis().Keys(ctx, "*").Result()
require.Nil(t, err)
expected := []string{
"cacheme:bar:foo:info:v6", // cache key
"cacheme:meta:group:Bar:v6", // group key
"cacheme:meta:group:Bar:v6:hll", // hll key
"cacheme:bar:foo:info:v6", // cache key
"cacheme:group:Bar:v6", // group key
}
require.ElementsMatch(t, keys, expected)
CleanRedis()
Expand All @@ -590,9 +585,8 @@ func TestCacheVersion(t *testing.T) {
keys, err = client.Redis().Keys(ctx, "*").Result()
require.Nil(t, err)
expected = []string{
"cacheme:bar:foo:info:v12", // cache key
"cacheme:meta:group:Bar:v12", // group key
"cacheme:meta:group:Bar:v12:hll", // hll key
"cacheme:bar:foo:info:v12", // cache key
"cacheme:group:Bar:v12", // group key
}
require.ElementsMatch(t, keys, expected)
}
Expand Down Expand Up @@ -695,73 +689,6 @@ func TestGetMCluster(t *testing.T) {
require.Equal(t, 2, fetcher.SimpleMultiCacheStoreCounter)
}

func TestInvalidAllLarge(t *testing.T) {
fetcher.Setup()
client := Cacheme()
defer CleanRedis()
defer ResetCounter()
ctx := context.TODO()
getter := client.FooCacheStore.MGetter()
for i := 0; i < 1275; i++ {
_ = getter.GetM(strconv.Itoa(i))
}
_, err := getter.Do(ctx)
require.Nil(t, err)
keys, err := client.Redis().Keys(ctx, "*").Result()
require.Nil(t, err)
require.Equal(t, 1277, len(keys))
err = client.FooCacheStore.InvalidAll(ctx, "1")
require.Nil(t, err)
keys, err = client.Redis().Keys(ctx, "*").Result()
require.Nil(t, err)
require.True(t, len(keys) < 80)
}

func TestInvalidAllLargeCluster(t *testing.T) {
fetcher.Setup()
client := CachemeCluster()
defer CleanRedisCluster()
defer ResetCounter()
ctx := context.TODO()
getter := client.FooCacheStore.MGetter()
for i := 0; i < 1275; i++ {
_ = getter.GetM(strconv.Itoa(i))
}
_, err := getter.Do(ctx)
require.Nil(t, err)

cc := client.Redis().(*redis.ClusterClient)
var counter int
var mu sync.Mutex
err = cc.ForEachMaster(ctx, func(ctx context.Context, client *redis.Client) error {
keys, err := client.Keys(ctx, "*").Result()
if err != nil {
return err
}
mu.Lock()
counter += len(keys)
mu.Unlock()
return nil
})
require.Nil(t, err)
require.Equal(t, 1277, counter)
err = client.FooCacheStore.InvalidAll(ctx, "1")
require.Nil(t, err)
counter = 0
err = cc.ForEachMaster(ctx, func(ctx context.Context, client *redis.Client) error {
keys, err := client.Keys(ctx, "*").Result()
if err != nil {
return err
}
mu.Lock()
counter += len(keys)
mu.Unlock()
return nil
})
require.Nil(t, err)
require.True(t, counter < 80)
}

func TestMGetSingleFlight(t *testing.T) {
fetcher.Setup()
client := Cacheme()
Expand Down
Loading

0 comments on commit bcd3069

Please sign in to comment.