Git Product home page Git Product logo

gorm-cache's Issues

Why there are two tiers for cache?

Hi @Pacific73
I am studying gorm-cache, I noticed there are two tier caches: one is remote Redis, another is local memory.
Why design a two-tier cache? This makes the design more complex, will it improve performance?

Can I use the remote Redis only ? or use the local memory only?

Thank you.

并发情况下处理耗时慢慢累积,最终超时

版本v1.1.0

使用内存缓存,如下配置

       cache.NewGorm2Cache(&config.CacheConfig{
		CacheLevel:           config.CacheLevelAll,
		CacheStorage:         config.CacheStorageMemory,
		InvalidateWhenUpdate: true,  // when you create/update/delete objects, invalidate cache
		CacheTTL:             10000, // 10s
		CacheSize:            5000,
		// exceeds this number, then don't cache
	}

AfterUpdate方法耗时会慢慢累积,最终会导致数据读写操作均超时

以下是go trace看到的结果

首先可以看到AfterUpdate是耗时大户
image

大量的goroutine在AfterUpdate
image

scheduler wait
image

可以看到阻塞在写channel
image

对应的代码
image

这里为什么采用deletables chan *Item这样的设计?

获取缓存的疑问

keyExists, err := cache.SearchKeyExists(ctx, tableName, sql, db.Statement.Vars...)

现在的代码实现里,在 before_query 查 key 是否存在,在 after_query 里查 value,感觉会出现一种情况,查 key 的时候查到了,然后缓存过期了,再查 value 的时候失败了。

为什么不在 before_query 的时候直接查,放到 context 里传下去呢,还可以少一次查询操作,是我哪里没考虑到吗

请问怎么主动删除某个缓存数据

我们这边系统是分布式的,存在一个,其他服务更改了缓存数据,广播通知其他服务更新缓存

请问怎么主动删除某个表的缓存数据呢

How to support Tenant?

Hi @Pacific73
Can gorm-chche support Tenant?

For example: there are many tenant in my system, they can access the same Table:task with different product Id. When Tenant A list task list A with product A Id, if Tenant B update his task B, this operation will not remove task list A in Cache.

In another word, A tenant Create/Update/Delete operations should only affect cached data associated with that tenant, can't affect other tenant cache data.

Thank you.

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

创建测试数据:

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/[email protected]/schema/field.go:454 +0xb8
github.com/Pacific73/gorm-cache/cache.getObjectsAfterLoad(0x1400009ae10)
        .../go/pkg/mod/github.com/!pacific73/[email protected]/cache/helpers.go:245 +0x280
github.com/Pacific73/gorm-cache/cache.(*Gorm2Cache).Initialize.AfterQuery.func5(0x1400009ae10)
        .../go/pkg/mod/github.com/!pacific73/[email protected]/cache/after_query.go:31 +0x100
gorm.io/gorm.(*processor).Execute(0x140001e41e0, 0x140001f04b0?)
        .../go/pkg/mod/gorm.io/[email protected]/callbacks.go:130 +0x300
gorm.io/gorm.(*DB).Pluck(0x140001f0480?, {0x100b504ea, 0x5}, {0x100c18fc0?, 0x14000092528})
        .../go/pkg/mod/gorm.io/[email protected]/finisher_api.go:568 +0x260
main.main()
        .../main.go:51 +0x4f0

错误原因说明:

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

如果直接传 cache key 是不是也行

sqlObj, _ := db.InstanceGet("gorm:cache:sql")
sql := sqlObj.(string)
varObj, _ := db.InstanceGet("gorm:cache:vars")
vars := varObj.([]interface{})

看了下 sql 和 vars 好像只用在打印日志和生成 key 上,如果直接在 before_query 拼接好 key 然后传过去呢,个人想法哈

Json tag冲突的问题

I believe that you can read Chinese without barriers, if you have any difficulty in understanding the following, please let me know

通常golang的习惯,json tag和gorm tag都会写在同一个struct里

我希望给用户交付的Json需要忽略某些字段的时候就会在struct里设置json:"-"的tag来忽略这个字段,但是同时这个字段又是要参与逻辑的,比如password,salt之类的

同时这个struct也是gorm要返回的,这时由于cache的序列化是基于encoding/json的,就会同时忽略json:"-"的字段,也就是逻辑判断字段会少内容

解决方案:我目前的做法是引入jsoniter来替换掉encoding/json,因为jsoniter允许指定tagkey,并且jsoniter号称性能也比encoding/json要快,询问一下这个变更是否适合合并入主线

关于CacheMaxItemCnt

// CacheMaxItemCnt for given query, if objects retrieved are more than this cnt,
// then we choose not to cache for this query. 0 represents caching all queries.
CacheMaxItemCnt int64

注释里写着0表示缓存所有,但是判断的时候好像没特殊判断0。
// cache search data
if int64(len(objects)) > cache.Config.CacheMaxItemCnt {
	return
}

如果分库的情况下如何删除某个具体的库的具体的表的缓存呢?

实际使用情况是分库使用的
库1 : db_1.test表
库2 : db_2.test表
...
这个会不断扩展
但是都是从同一个gorm的对象去操作的,使用之前,根据业务,db.Table(tableName),去设置哪个库和表名
问题1:当我查询db_1和db_2的表数据,缓存的,是分开2份缓存吗?不会串数据吧
问题2:当我要InvalidateSearchCache去删除缓存的时候,我怎么精准的删除db_2的test表缓存?而不影响db_1.test表?

使用内存缓存,分页查询中search key exists总是返回false

问题描述

使用内存缓存,分页查询缓存不生效,追进去发现SearchKeyExists总视返回false,查询结果也相同

缓存配置

cache, _ := cache.NewGorm2Cache(&cacheConfig.CacheConfig{
	CacheLevel:           cacheConfig.CacheLevelAll,
	CacheStorage:         cacheConfig.CacheStorageMemory,
	CacheSize:            5000,
	InvalidateWhenUpdate: true,  // when you create/update/delete objects, invalidate cache
	CacheTTL:             60000, // 60000 ms
	CacheMaxItemCnt:      60000, // if length of objects retrieved one single time
	DebugMode:            true,
	// exceeds this number, then don't cache
	})
日志
2022-09-09 05:44:32.635 [INFO] [BeforeQuery] search key exists ? false
2022-09-09 05:44:32.635 [INFO] [BeforeQuery] parse primary keys = []
2022-09-09 05:44:32.907 [INFO] [AfterQuery] start to set primary cache for kvs: []
2022-09-09 05:44:32.907 [INFO] [AfterQuery] start to set search cache for sql: SELECT count(*) FROM "peiz"."p_order" WHERE "p_order"."mc_id" = $1 AND "status" NOT IN ($2,$3,$4) AND "p_order"."deleted_at" IS NULL
2022-09-09 05:44:32.907 [INFO] [AfterQuery] set cache: 37
2022-09-09 05:44:32.907 [INFO] [AfterQuery] sql SELECT count(*) FROM "peiz"."p_order" WHERE "p_order"."mc_id" = $1 AND "status" NOT IN ($2,$3,$4) AND "p_order"."deleted_at" IS NULL cached
22-09-09 05:44:32 [DEBU] SELECT count(*) FROM "peiz"."p_order" WHERE "p_order"."mc_id" = 337644723584954368 AND "status" NOT IN ('start','deleted','panic') AND "p_order"."deleted_at" IS NULL [272.238116ms]
2022-09-09 05:44:32.907 [INFO] [BeforeQuery] search key exists ? false
2022-09-09 05:44:32.907 [INFO] [BeforeQuery] parse primary keys = []
2022-09-09 05:44:33.318 [INFO] [AfterQuery] start to set search cache for sql: SELECT * FROM "peiz"."p_order" WHERE "p_order"."mc_id" = $1 AND "status" NOT IN ($2,$3,$4) AND "p_order"."deleted_at" IS NULL ORDER BY create_time desc
2022-09-09 05:44:33.319 [INFO] [AfterQuery] start to set primary cache for kvs: [{Key:0x40000b7258 Value:{响应内容}]
2022-09-09 05:44:33.319 [INFO] [AfterQuery] sql SELECT * FROM "peiz"."p_order" WHERE "p_order"."mc_id" = $1 AND "status" NOT IN ($2,$3,$4) AND "p_order"."deleted_at" IS NULL ORDER BY create_time desc  cached
22-09-09 05:44:33 [DEBU] SELECT * FROM "peiz"."p_order" WHERE "p_order"."mc_id" = 337644723584954368 AND "status" NOT IN ('start','deleted','panic') AND "p_order"."deleted_at" IS NULL ORDER BY create_time desc  [411.897497ms]
22-09-09 05:44:33 [INFO] 223.104.103.141 "POST /api/p/order/page" 200 (691ms)
2022-09-09 05:44:40.891 [INFO] [BeforeQuery] search key exists ? false
2022-09-09 05:44:40.891 [INFO] [BeforeQuery] parse primary keys = []
2022-09-09 05:44:40.95 [INFO] [AfterQuery] start to set primary cache for kvs: []
2022-09-09 05:44:40.95 [INFO] [AfterQuery] start to set search cache for sql: SELECT count(*) FROM "peiz"."p_order" WHERE "p_order"."mc_id" = $1 AND "status" NOT IN ($2,$3,$4) AND "p_order"."deleted_at" IS NULL
2022-09-09 05:44:40.95 [INFO] [AfterQuery] set cache: 37
2022-09-09 05:44:40.95 [INFO] [AfterQuery] sql SELECT count(*) FROM "peiz"."p_order" WHERE "p_order"."mc_id" = $1 AND "status" NOT IN ($2,$3,$4) AND "p_order"."deleted_at" IS NULL cached
22-09-09 05:44:40 [DEBU] SELECT count(*) FROM "peiz"."p_order" WHERE "p_order"."mc_id" = 337644723584954368 AND "status" NOT IN ('start','deleted','panic') AND "p_order"."deleted_at" IS NULL [59.785883ms]
2022-09-09 05:44:40.95 [INFO] [BeforeQuery] search key exists ? false
2022-09-09 05:44:40.95 [INFO] [BeforeQuery] parse primary keys = []
2022-09-09 05:44:41.091 [INFO] [AfterQuery] start to set search cache for sql: SELECT * FROM "peiz"."p_order" WHERE "p_order"."mc_id" = $1 AND "status" NOT IN ($2,$3,$4) AND "p_order"."deleted_at" IS NULL ORDER BY create_time desc
2022-09-09 05:44:41.092 [INFO] [AfterQuery] start to set primary cache for kvs: [{Key:0x400050b2a8 Value:{响应内容}}]
2022-09-09 05:44:41.092 [INFO] [AfterQuery] sql SELECT * FROM "peiz"."p_order" WHERE "p_order"."mc_id" = $1 AND "status" NOT IN ($2,$3,$4) AND "p_order"."deleted_at" IS NULL ORDER BY create_time desc  cached
22-09-09 05:44:41 [DEBU] SELECT * FROM "peiz"."p_order" WHERE "p_order"."mc_id" = 337644723584954368 AND "status" NOT IN ('start','deleted','panic') AND "p_order"."deleted_at" IS NULL ORDER BY create_time desc  [141.459062ms]
22-09-09 05:44:41 [INFO] 223.104.103.141 "POST /api/p/order/page" 200 (203ms)

基本信息

gorm.io/gorm v1.23.8
github.com/Pacific73/gorm-cache v1.0.1-0.20220620032759-a6797d3696a5

v1.0.0 没有实现Plugin接口,用hash直接加载的合并了Plugin接口的pr

使用缓存后Count操作会异常

Count操作会异常,原因是gorm的Count的返回类型是int64,会提示反射类型是uint, 直接异常

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

goroutine 29 [running]:
reflect.Value.Field({0xdd5ce0, 0xc0004b4600, 0x187}, 0x0)
C:/Program Files/Go/src/reflect/value.go:1278 +0x269
gorm.io/gorm/schema.(*Field).setupValuerAndSetter.func2({0xf95ba8, 0x157aca0}, {0xdd5ce0, 0xc0004b4600, 0x187})
C:/Users/Administrator/go/pkg/mod/gorm.io/[email protected]/schema/field.go:454 +0x117
github.com/Pacific73/gorm-cache/cache.getObjectsAfterLoad(0xc000212690)
C:/Users/Administrator/go/pkg/mod/github.com/!pacific73/[email protected]/cache/helpers.go:249 +0x3d6
github.com/Pacific73/gorm-cache/cache.AfterQuery.func1(0xc000212690)
C:/Users/Administrator/go/pkg/mod/github.com/!pacific73/[email protected]/cache/after_query.go:31 +0x314

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.