-
Notifications
You must be signed in to change notification settings - Fork 823
Fixed Cache fetch error on Redis Cluster #4056
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 4 commits
65e2a81
66507d3
132d092
afddc3f
f3defda
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -110,17 +110,38 @@ func (c *RedisClient) MGet(ctx context.Context, keys []string) ([][]byte, error) | |
defer cancel() | ||
} | ||
|
||
cmd := c.rdb.MGet(ctx, keys...) | ||
if err := cmd.Err(); err != nil { | ||
return nil, err | ||
} | ||
|
||
ret := make([][]byte, len(keys)) | ||
for i, val := range cmd.Val() { | ||
if val != nil { | ||
ret[i] = StringToBytes(val.(string)) | ||
|
||
// redis.UniversalClient can take redis.Client and redis.ClusterClient. | ||
// if redis.Client is set, then Single node or sentinel configuration. mget is always supported. | ||
// if redis.ClusterClient is set, then Redis Cluster configuration. mget may not be supported. | ||
_, isCluster := c.rdb.(*redis.ClusterClient) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just a question to better understand the fix (I'm not an expect on Redis or the Go bindings). This is working around an issue/limitation in the bindings, correct? Or a limitation in a particular server version? I only ask because my first reaction to this is that checking the type of an interface feels wrong. If it's the only option, then we'll have to live with it of course. Either way, it might be nice to put a comment here to explain why we have to check the type. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thank you for the review! I thought the same thing, but I think this method is the simplest. The Redis client is initialized with the This is the implementation. https://github.com/go-redis/redis/blob/v8.8.0/universal.go#L199-L206
The only way to check if the Universal Client is Redis Cluster is by type assertion. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Added a comment. |
||
|
||
if isCluster { | ||
for i, key := range keys { | ||
cmd := c.rdb.Get(ctx, key) | ||
err := cmd.Err() | ||
if err == redis.Nil { | ||
// if key not found, response nil | ||
continue | ||
} else if err != nil { | ||
return nil, err | ||
} | ||
ret[i] = StringToBytes(cmd.Val()) | ||
} | ||
} else { | ||
cmd := c.rdb.MGet(ctx, keys...) | ||
if err := cmd.Err(); err != nil { | ||
return nil, err | ||
} | ||
|
||
for i, val := range cmd.Val() { | ||
if val != nil { | ||
ret[i] = StringToBytes(val.(string)) | ||
} | ||
} | ||
} | ||
|
||
return ret, nil | ||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
package cache | ||
|
||
import ( | ||
"context" | ||
"testing" | ||
"time" | ||
|
||
"github.com/alicebob/miniredis/v2" | ||
"github.com/go-redis/redis/v8" | ||
"github.com/stretchr/testify/require" | ||
) | ||
|
||
func TestRedisClient(t *testing.T) { | ||
single, err := mockRedisClientSingle() | ||
require.Nil(t, err) | ||
defer single.Close() | ||
|
||
cluster, err := mockRedisClientCluster() | ||
require.Nil(t, err) | ||
defer cluster.Close() | ||
|
||
ctx := context.Background() | ||
|
||
tests := []struct { | ||
name string | ||
client *RedisClient | ||
}{ | ||
{ | ||
name: "single redis client", | ||
client: single, | ||
}, | ||
{ | ||
name: "cluster redis client", | ||
client: cluster, | ||
}, | ||
} | ||
|
||
for _, tt := range tests { | ||
t.Run(tt.name, func(t *testing.T) { | ||
keys := []string{"key1", "key2", "key3"} | ||
bufs := [][]byte{[]byte("data1"), []byte("data2"), []byte("data3")} | ||
miss := []string{"miss1", "miss2"} | ||
|
||
// set values | ||
err := tt.client.MSet(ctx, keys, bufs) | ||
require.Nil(t, err) | ||
|
||
// get keys | ||
values, err := tt.client.MGet(ctx, keys) | ||
require.Nil(t, err) | ||
require.Len(t, values, len(keys)) | ||
for i, value := range values { | ||
require.Equal(t, values[i], value) | ||
} | ||
|
||
// get missing keys | ||
values, err = tt.client.MGet(ctx, miss) | ||
require.Nil(t, err) | ||
require.Len(t, values, len(miss)) | ||
for _, value := range values { | ||
require.Nil(t, value) | ||
} | ||
}) | ||
} | ||
} | ||
|
||
func mockRedisClientSingle() (*RedisClient, error) { | ||
redisServer, err := miniredis.Run() | ||
if err != nil { | ||
return nil, err | ||
} | ||
return &RedisClient{ | ||
expiration: time.Minute, | ||
timeout: 100 * time.Millisecond, | ||
rdb: redis.NewClient(&redis.Options{ | ||
Addr: redisServer.Addr(), | ||
}), | ||
}, nil | ||
} | ||
|
||
func mockRedisClientCluster() (*RedisClient, error) { | ||
redisServer1, err := miniredis.Run() | ||
if err != nil { | ||
return nil, err | ||
} | ||
redisServer2, err := miniredis.Run() | ||
if err != nil { | ||
return nil, err | ||
} | ||
return &RedisClient{ | ||
expiration: time.Minute, | ||
timeout: 100 * time.Millisecond, | ||
rdb: redis.NewClusterClient(&redis.ClusterOptions{ | ||
Addrs: []string{ | ||
redisServer1.Addr(), | ||
redisServer2.Addr(), | ||
}, | ||
}), | ||
}, nil | ||
} |
This file was deleted.
This file was deleted.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please rebase
master
and move this CHANGELOG entry to the top. We've cut 1.9 release in the meanwhile. Thanks!There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@kamijin-fanta This commit is still valid. Could you address it, please?