Go: go-redis/cache升级的坑

这是在做 Golang 项目中的一些实践

在做一个项目, 使用了 go-redis/redisgo-redis/cache

某个节点, 做了所有依赖包的升级.

在做缓存依赖包的升级时, 必须十分注意, 新版本的第三方包做了哪些变更, 阅读CHANGE LOG和相关的源码;

主要风险:

  • breaking changes
  • 对象/方法/参数等协议变更
  • 存储格式(是否向前兼容)

即, 一旦做了升级, 原先正常的代码, 可能会有问题

所以需要确认:

  1. 新老版本存储格式是否一致?
  2. 代码中涉及的地方是否使用同样的操作?

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


golang

644 Words

2021-03-05 00:00 +0000