Pacific73 / gorm-cache

gorm v2的即插即用、无需修改代码的旁路缓存。An easy-to-use look-aside cache solution for gorm v2 users.
Apache License 2.0
111 stars 29 forks source link

严重问题:使用Pluck查询会导致panic #28

Closed athenakia closed 9 months ago

athenakia commented 11 months ago

创建测试数据:

CREATE TABLE `testdb`.`users`  (
  `id` int NOT NULL,
  `name` varchar(255) NOT NULL,
  `value` int NOT NULL,
  PRIMARY KEY (`id`, `id`)
);

INSERT INTO `users` (`id`, `name`, `value`) VALUES (1, 'hello', 100);
INSERT INTO `users` (`id`, `name`, `value`) VALUES (2, 'abc', 123);
INSERT INTO `users` (`id`, `name`, `value`) VALUES (3, 'xyz', 8);

复现代码:

package main

import (
    "fmt"
    "github.com/Pacific73/gorm-cache/cache"
    "github.com/Pacific73/gorm-cache/config"
    "github.com/go-redis/redis/v8"
    "gorm.io/driver/mysql"
    "gorm.io/gorm"
)

type User struct {
    ID    int
    Name  string
    Value int
}

func main() {
    dsn := "user:pass@tcp(127.0.0.1:3306)/testdb?charset=utf8mb4"
    db, _ := gorm.Open(mysql.Open(dsn), &gorm.Config{})

    redisClient := redis.NewClient(&redis.Options{
        Addr: "localhost:6379",
    })

    redisCache, err := cache.NewGorm2Cache(&config.CacheConfig{
        CacheLevel:           config.CacheLevelAll,
        CacheStorage:         config.CacheStorageRedis,
        RedisConfig:          cache.NewRedisConfigWithClient(redisClient),
        InvalidateWhenUpdate: true,  // when you create/update/delete objects, invalidate cache
        CacheTTL:             5000, // 5000 ms
        CacheMaxItemCnt:      5,     // if length of objects retrieved one single time
        // exceeds this number, then don't cache
    })
    if err != nil {
        panic(err)
    }
    // More options in `config.config.go`
    db.Use(redisCache) // use gorm plugin
    // cache.AttachToDB(db)

    var users []User

    db.Where("value > ?", 123).Find(&users) // search cache not hit, objects cached
    db.Where("value > ?", 123).Find(&users) // search cache hit

    db.Where("id IN (?)", []int{1, 2, 3}).Find(&users) // primary key cache not hit, users cached
    db.Where("id IN (?)", []int{1, 3}).Find(&users)    // primary key cache hit

    var values = make([]int, 0)
    db.Model(&User{}).Where("value >= ?", 100).Pluck("value", &values)

    fmt.Println("values:", values)
}

复现代码说明:和README例子几乎一样,只是使用了Pluck,查询结果是int切片。

报错内容:

panic: reflect: call of reflect.Value.Field on int Value

reflect.Value.Field({0x100c1fb40?, 0x14000232000?, 0x147d2e3b0?}, 0x101140a68?)
        .../go/go1.21.0/src/reflect/value.go:1278 +0xec
gorm.io/gorm/schema.(*Field).setupValuerAndSetter.func2({0x14000216198?, 0x14000092528?}, {0x100c1fb40?, 0x14000232000?, 0x100ca1c20?})
        .../go/pkg/mod/gorm.io/gorm@v1.25.4/schema/field.go:454 +0xb8
github.com/Pacific73/gorm-cache/cache.getObjectsAfterLoad(0x1400009ae10)
        .../go/pkg/mod/github.com/!pacific73/gorm-cache@v1.1.0/cache/helpers.go:245 +0x280
github.com/Pacific73/gorm-cache/cache.(*Gorm2Cache).Initialize.AfterQuery.func5(0x1400009ae10)
        .../go/pkg/mod/github.com/!pacific73/gorm-cache@v1.1.0/cache/after_query.go:31 +0x100
gorm.io/gorm.(*processor).Execute(0x140001e41e0, 0x140001f04b0?)
        .../go/pkg/mod/gorm.io/gorm@v1.25.4/callbacks.go:130 +0x300
gorm.io/gorm.(*DB).Pluck(0x140001f0480?, {0x100b504ea, 0x5}, {0x100c18fc0?, 0x14000092528})
        .../go/pkg/mod/gorm.io/gorm@v1.25.4/finisher_api.go:568 +0x260
main.main()
        .../main.go:51 +0x4f0

错误原因说明:

尝试对一个int使用反射来获取字段,但整数是一个基础类型,没有字段。使用Pluck取所有基础类型都会panic。

asjdf commented 11 months ago

这个库的文档有说明不支持Pluck

athenakia commented 11 months ago

这个库的文档有说明不支持Pluck

那直接panic也算是有问题的设计吧,直接不缓存应该更加合理。我看你的代码修复了这个问题,建议提个PR。😅

athenakia commented 11 months ago

文档说的主要是不支持Row操作

asjdf commented 11 months ago

这个库的文档有说明不支持Pluck

那直接panic也算是有问题的设计吧,直接不缓存应该更加合理。我看你的代码修复了这个问题,建议提个PR。😅

我那个库改动了不少 已经不太有能力合并进来了x

Pacific73 commented 11 months ago

感谢二位对本库的关注,我因为日常工作繁忙,可能平时投入的时间不多。针对你们提到的问题,我会尽快修复并release新的版本。如果着急的话,二位可以自行fork做一些改动,当然了,欢迎提交pr,有时间我会看的,感谢二位。

Pacific73 commented 10 months ago

@athenakia @asjdf 此问题已经修复并测试,可以更新到v1.1.1使用,感谢对本库的关注!