Go: go-redis/cache升级的坑
这是在做 Golang 项目中的一些实践
在做一个项目, 使用了 go-redis/redis 及 go-redis/cache
某个节点, 做了所有依赖包的升级.
在做缓存依赖包的升级时, 必须十分注意, 新版本的第三方包做了哪些变更, 阅读CHANGE LOG和相关的源码;
主要风险:
- breaking changes
- 对象/方法/参数等协议变更
- 存储格式(是否向前兼容)
即, 一旦做了升级, 原先正常
的代码, 可能会有问题
所以需要确认:
- 新老版本存储格式是否一致?
- 代码中涉及
读
和写
的地方是否使用同样的操作?
1. v7升级v8
issue: Can’t upgrade from v7 to v8 directly?
package main
import (
"context"
"fmt"
cv7 "github.com/go-redis/cache/v7"
cv8 "github.com/go-redis/cache/v8"
rdsv7 "github.com/go-redis/redis/v7"
rdsv8 "github.com/go-redis/redis/v8"
msgpackv4 "github.com/vmihailenco/msgpack/v4"
)
func main() {
// TODO: 测试放入redis用另一个取
d := map[string]interface{}{
"hello": 1,
"world": "ok",
}
// 使用两个版本的cli进行测试, v7/v8
// 1. 使用v7, marshal 放入db
// 2. use v8, 获取
v7 := &cv7.Codec{
Redis: rdsv7.NewClient(&rdsv7.Options{}),
Marshal: msgpackv4.Marshal,
Unmarshal: msgpackv4.Unmarshal,
}
err := v7.Set(&cv7.Item{
Key: "aaa",
Object: d,
})
fmt.Println("set err:", err)
v8 := cv8.New(&cv8.Options{
Redis: rdsv8.NewClient(&rdsv8.Options{}),
})
var result map[string]interface{}
err = v8.Get(context.TODO(), "aaa", &result)
fmt.Println("get err:", err)
fmt.Println(result)
}
执行结果:
set err: <nil>
get err: unknown compression method: 6b
map[]
原因, go-redis/cache v8引入的压缩 v8/cache.go, 没有做向前兼容的逻辑
解决方案: 在基础类中, 更换掉key的namespace, 增加版本号; 例如 project:hello
变成 project:v8:hello
2. 升级v8后的msgpack 与 pipeline的处理
由于 go-redis/cache 主要用于单个对象的 set/get
某些业务场景下, 我们使用pipeline进行批量操作
此时, pipeline拿回回来的是 raw string
, 原先v7由于set算法用的msgpack.Marshal
, 自定义pipeline处理结果直接用msgpack.Unmarshal
没有问题.
但是, 升级到v8后, 由于compession逻辑的存在, 此时使用原生的msgpack.Unmarshal
将会报错(无法识别被压缩的字符串)
解决: 需要切换为 go-redis/cache
对象的Unmarshal