go-redis
go-redis copied to clipboard
HGetAll doesn't seem to support key / Nil checking
err := rdb.HGetAll(ctx, "nonsense").Scan(&resultstruct1)
// Verify key exists
if err == redis.Nil {
log.Printf("Empty record")
}
Gives no logs
err := rdb.Get(ctx, "nonsense")
// Verify key exists
if err == redis.Nil {
log.Printf("Empty record")
}
Gives the correct error.
Splitting the result and scan methods into 2 seperate objects, and then checking for errors:
cmd := rdb.HGetAll(ctx, "nonsense")
// Verify key exists
err := cmd.Err()
if err == redis.Nil {
log.Printf("Empty record")
}
if err != nil {
log.Printf("Error reading from Redis")
return nil, err
}
scanErr := cmd.Scan(&resultstruct1)
if scanErr != nil {
log.Printf("Error Scanning Object")
return nil, err
}
Yields no "empty record" line, nor a scanning error, it just results in an empty struct.
It seems that the HGetAll function works differently regarding the Nil detection.
Duplicate of #1668
I have ended up to wrap Scan
like this:
// ScanCmder is the interface used by MustScan to scan result into destinations.
//
// In go-redis v8, the following commands have implemented this interface:
// - MapStringStringCmd
// - SliceCmd
// - StringCmd
type ScanCmder interface {
Scan(dst interface{}) error
}
// MustScan enhances the Scan method of a ScanCmder with these features:
// - it returns the error redis.Nil when the key does not exist. See https://github.com/go-redis/redis/issues/1668
// - it supports embedded struct better. See https://github.com/go-redis/redis/issues/2005#issuecomment-1019667052
func MustScan(s ScanCmder, dest ...interface{}) error {
if cmd, ok := s.(*redis.StringStringMapCmd); ok {
if len(cmd.Val()) == 0 {
return redis.Nil
}
}
for _, d := range dest {
if err := s.Scan(d); err != nil {
return err
}
}
return nil
}
Update
MustScan
should be modified to handle *redis.SliceCmd
:
// MustScan enhances the Scan method of a ScanCmder with these features:
// - it returns the error redis.Nil when the key does not exist. See https://github.com/go-redis/redis/issues/1668
// - it supports embedded struct better. See https://github.com/go-redis/redis/issues/2005#issuecomment-1019667052
func MustScan(s ScanCmder, dest ...interface{}) error {
switch cmd := s.(type) {
case *redis.StringStringMapCmd:
if len(cmd.Val()) == 0 {
return redis.Nil
}
case *redis.SliceCmd:
keyExists := false
for _, v := range cmd.Val() {
if v != nil {
keyExists = true
break
}
}
if !keyExists {
return redis.Nil
}
}
for _, d := range dest {
if err := s.Scan(d); err != nil {
return err
}
}
return nil
}
@ZekeLu thank you! I was having this same issue.
Hi has anyone tried storing redis hash as proto marshalled and unmarshall and retrieve in struct? I am facing issue in that Doing below but not able to get desired result
var res interface{} res, err = redis.GoRedisInstance().Do(context.Background(), "HGETALL", key).Result() if err != nil { return nil, false, err } if response, ok := res.(string); ok { value = []byte(response) } styleEntry, err := parseStyle(ctx, value)
func parseStyle(ctx context.Context, b []byte) (*style_fetch.CMSStyleRedisEntry, error) { // convert to product redis object productRedis := style_fetch.CMSStyleRedisEntry{} err := proto.Unmarshal(b, &productRedis) if err != nil { return nil, err } return &productRedis, nil }
While debugging redis.GoRedisInstance().Do(context.Background(), "HGETALL", key) and .Result post that gives below.
Resolved proto unmarshall required at invidual field level and not on whole struct as here we do hset/hmset individual fields
unboxed, ok := res.(map[string]string)
if !ok {
fmt.Println("Output should be a pointer of a map")
}
//If error is nil but value is also empty/nil. Data not found in redis, reindex
if unboxed == nil || len(unboxed) <= 0 {
//async reindexing call
isCacheMiss = true
statsd.Instance().Incr("redis.keyNotPresentError", 1)
return nil, isCacheMiss, nil
}
redisEntry := &style_fetch.CMSStyleRedisEntry{}
for key, value := range unboxed {
if key == "style" {
style := &style_fetch.Style{}
proto.Unmarshal([]byte(value), style)
redisEntry.Style = style
}
//...other keys
}
Will any of the maintainers fix this? Silly bug.
Closing as duplicate of https://github.com/redis/go-redis/issues/1668