This commit is contained in:
2025-02-07 20:33:27 +08:00
parent 76a4b67269
commit e89e1a372a
28 changed files with 276 additions and 1691 deletions

View File

@@ -1,97 +0,0 @@
fork from https://github.com/liyuan1125/gorm-cache
```go
package main
import (
"context"
"fmt"
"github.com/go-redis/redis/v8"
"github.com/liyuan1125/gorm-cache"
redis2 "github.com/liyuan1125/gorm-cache/store/redis"
"gorm.io/driver/mysql"
"gorm.io/gorm"
"os"
"time"
)
var (
db *gorm.DB
redisClient *redis.Client
cachePlugin *cache.Cache
)
func newDb() {
dsn := "root:123456@tcp(127.0.0.1:3306)/gorm?charset=utf8&parseTime=True&loc=Local"
var err error
db, err = gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
fmt.Println(err.Error())
return
}
redisClient = redis.NewClient(&redis.Options{Addr: ":6379"})
cacheConfig := &cache.Config{
Store: redis2.NewWithDb(redisClient), // OR redis2.New(&redis.Options{Addr:"6379"})
Serializer: &cache.DefaultJSONSerializer{},
}
cachePlugin = cache.New(cacheConfig)
if err = db.Use(cachePlugin); err != nil {
fmt.Println(err.Error())
os.Exit(1)
}
}
func basic() {
var username string
ctx := context.Background()
ctx = cache.NewExpiration(ctx, time.Hour)
db.Table("users").WithContext(ctx).Where("id = 1").Limit(1).Pluck("username", &username)
fmt.Println(username)
// output gorm
}
func customKey() {
var nickname string
ctx := context.Background()
ctx = cache.NewExpiration(ctx, time.Hour)
ctx = cache.NewKey(ctx, "nickname")
db.Table("users").WithContext(ctx).Where("id = 1").Limit(1).Pluck("nickname", &nickname)
fmt.Println(nickname)
// output gormwithmysql
}
func useTag() {
var nickname string
ctx := context.Background()
ctx = cache.NewExpiration(ctx, time.Hour)
ctx = cache.NewTag(ctx, "users")
db.Table("users").WithContext(ctx).Where("id = 1").Limit(1).Pluck("nickname", &nickname)
fmt.Println(nickname)
// output gormwithmysql
}
func main() {
newDb()
basic()
customKey()
useTag()
ctx := context.Background()
fmt.Println(redisClient.Keys(ctx, "*").Val())
fmt.Println(cachePlugin.RemoveFromTag(ctx, "users"))
}
```

View File

@@ -1,200 +0,0 @@
package cache
import (
"context"
"hash/fnv"
"os"
"strconv"
"time"
"gorm.io/gorm"
"gorm.io/gorm/callbacks"
)
type Config struct {
Store Store
Prefix string
Serializer Serializer
}
type (
Serializer interface {
Serialize(v any) ([]byte, error)
Deserialize(data []byte, v any) error
}
Store interface {
// Set 写入缓存数据
Set(ctx context.Context, key string, value any, ttl time.Duration) error
// Get 获取缓存数据
Get(ctx context.Context, key string) ([]byte, error)
// SaveTagKey 将缓存key写入tag
SaveTagKey(ctx context.Context, tag, key string) error
// RemoveFromTag 根据缓存tag删除缓存
RemoveFromTag(ctx context.Context, tag string) error
}
)
type Cache struct {
store Store
// Serializer 序列化
Serializer Serializer
// prefix 缓存前缀
prefix string
}
// New
// @param conf
// @date 2022-07-02 08:09:52
func New(conf *Config) *Cache {
if conf.Store == nil {
os.Exit(1)
}
if conf.Serializer == nil {
conf.Serializer = &DefaultJSONSerializer{}
}
return &Cache{
store: conf.Store,
prefix: conf.Prefix,
Serializer: conf.Serializer,
}
}
// Name
// @date 2022-07-02 08:09:48
func (p *Cache) Name() string {
return "gorm:cache"
}
// Initialize
// @param tx
// @date 2022-07-02 08:09:47
func (p *Cache) Initialize(tx *gorm.DB) error {
return tx.Callback().Query().Replace("gorm:query", p.Query)
}
// generateKey
// @param key
// @date 2022-07-02 08:09:46
func generateKey(key string) string {
hash := fnv.New64a()
_, _ = hash.Write([]byte(key))
return strconv.FormatUint(hash.Sum64(), 36)
}
// Query
// @param tx
// @date 2022-07-02 08:09:38
func (p *Cache) Query(tx *gorm.DB) {
ctx := tx.Statement.Context
var ttl time.Duration
var hasTTL bool
if ttl, hasTTL = FromExpiration(ctx); !hasTTL {
callbacks.Query(tx)
return
}
var (
key string
hasKey bool
)
// 调用 Gorm的方法生产SQL
callbacks.BuildQuerySQL(tx)
// 是否有自定义key
if key, hasKey = FromKey(ctx); !hasKey {
key = p.prefix + generateKey(tx.Statement.SQL.String())
}
// 查询缓存数据
if err := p.QueryCache(ctx, key, tx.Statement.Dest); err == nil {
return
}
// 查询数据库
p.QueryDB(tx)
if tx.Error != nil {
return
}
// 写入缓存
if err := p.SaveCache(ctx, key, tx.Statement.Dest, ttl); err != nil {
tx.Logger.Error(ctx, err.Error())
return
}
if tag, hasTag := FromTag(ctx); hasTag {
_ = p.store.SaveTagKey(ctx, tag, key)
}
}
// QueryDB 查询数据库数据
// 这里重写Query方法 是不想执行 callbacks.BuildQuerySQL 两遍
func (p *Cache) QueryDB(tx *gorm.DB) {
if tx.Error != nil || tx.DryRun {
return
}
rows, err := tx.Statement.ConnPool.QueryContext(tx.Statement.Context, tx.Statement.SQL.String(), tx.Statement.Vars...)
if err != nil {
_ = tx.AddError(err)
return
}
defer func() {
_ = tx.AddError(rows.Close())
}()
gorm.Scan(rows, tx, 0)
}
// QueryCache 查询缓存数据
// @param ctx
// @param key
// @param dest
func (p *Cache) QueryCache(ctx context.Context, key string, dest any) error {
values, err := p.store.Get(ctx, key)
if err != nil {
return err
}
switch dest.(type) {
case *int64:
dest = 0
}
return p.Serializer.Deserialize(values, dest)
}
// SaveCache 写入缓存数据
func (p *Cache) SaveCache(ctx context.Context, key string, dest any, ttl time.Duration) error {
values, err := p.Serializer.Serialize(dest)
if err != nil {
return err
}
return p.store.Set(ctx, key, values, ttl)
}
// RemoveFromTag 根据tag删除缓存数据
// @param ctx
// @param tag
// @date 2022-07-02 08:08:59
func (p *Cache) RemoveFromTag(ctx context.Context, tag string) error {
return p.store.RemoveFromTag(ctx, tag)
}

View File

@@ -1,88 +0,0 @@
package cache
import (
"context"
"time"
)
type (
// queryCacheCtx
queryCacheCtx struct{}
// queryCacheKeyCtx
queryCacheKeyCtx struct{}
// queryCacheTagCtx
queryCacheTagCtx struct{}
)
// NewKey
// @param ctx
// @param key
// @date 2022-07-02 08:11:44
func NewKey(ctx context.Context, key string) context.Context {
return context.WithValue(ctx, queryCacheKeyCtx{}, key)
}
// NewTag
// @param ctx
// @param key
// @date 2022-07-02 08:11:43
func NewTag(ctx context.Context, key string) context.Context {
return context.WithValue(ctx, queryCacheTagCtx{}, key)
}
// NewExpiration
// @param ctx
// @param ttl
// @date 2022-07-02 08:11:41
func NewExpiration(ctx context.Context, ttl time.Duration) context.Context {
return context.WithValue(ctx, queryCacheCtx{}, ttl)
}
// FromExpiration
// @param ctx
// @date 2022-07-02 08:11:40
func FromExpiration(ctx context.Context) (time.Duration, bool) {
value := ctx.Value(queryCacheCtx{})
if value != nil {
if t, ok := value.(time.Duration); ok {
return t, true
}
}
return 0, false
}
// FromKey
// @param ctx
// @date 2022-07-02 08:11:39
func FromKey(ctx context.Context) (string, bool) {
value := ctx.Value(queryCacheKeyCtx{})
if value != nil {
if t, ok := value.(string); ok {
return t, true
}
}
return "", false
}
// FromTag
// @param ctx
// @date 2022-07-02 08:11:37
func FromTag(ctx context.Context) (string, bool) {
value := ctx.Value(queryCacheTagCtx{})
if value != nil {
if t, ok := value.(string); ok {
return t, true
}
}
return "", false
}

View File

@@ -1,11 +0,0 @@
github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE=
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
github.com/jinzhu/now v1.1.4 h1:tHnRBy1i5F2Dh8BAFxqFzxKqqvezXrL2OW1TnX+Mlas=
github.com/jinzhu/now v1.1.4/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
gorm.io/driver/mysql v1.3.4 h1:/KoBMgsUHC3bExsekDcmNYaBnfH2WNeFuXqqrqMc98Q=
gorm.io/driver/mysql v1.3.4/go.mod h1:s4Tq0KmD0yhPGHbZEwg1VPlH0vT/GBHJZorPzhcxBUE=
gorm.io/gorm v1.23.4/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk=
gorm.io/gorm v1.23.6 h1:KFLdNgri4ExFFGTRGGFWON2P1ZN28+9SJRN8voOoYe0=
gorm.io/gorm v1.23.6/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk=

View File

@@ -1,92 +0,0 @@
package main
import (
"context"
"fmt"
"github.com/go-redis/redis/v8"
"github.com/liyuan1125/gorm-cache"
redis2 "github.com/liyuan1125/gorm-cache/store/redis"
"gorm.io/driver/mysql"
"gorm.io/gorm"
"os"
"time"
)
var (
db *gorm.DB
redisClient *redis.Client
cachePlugin *cache.Cache
)
func newDb() {
dsn := "root:123456@tcp(127.0.0.1:3306)/gorm?charset=utf8&parseTime=True&loc=Local"
var err error
db, err = gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
fmt.Println(err.Error())
return
}
redisClient = redis.NewClient(&redis.Options{Addr: ":6379"})
cacheConfig := &cache.Config{
Store: redis2.NewWithDb(redisClient), // OR redis2.New(&redis.Options{Addr:"6379"})
Serializer: &cache.DefaultJSONSerializer{},
}
cachePlugin = cache.New(cacheConfig)
if err = db.Use(cachePlugin); err != nil {
fmt.Println(err.Error())
os.Exit(1)
}
}
func basic() {
var username string
ctx := context.Background()
ctx = cache.NewExpiration(ctx, time.Hour)
db.Table("users").WithContext(ctx).Where("id = 1").Limit(1).Pluck("username", &username)
fmt.Println(username)
// output gorm
}
func customKey() {
var nickname string
ctx := context.Background()
ctx = cache.NewExpiration(ctx, time.Hour)
ctx = cache.NewKey(ctx, "nickname")
db.Table("users").WithContext(ctx).Where("id = 1").Limit(1).Pluck("nickname", &nickname)
fmt.Println(nickname)
// output gormwithmysql
}
func useTag() {
var nickname string
ctx := context.Background()
ctx = cache.NewExpiration(ctx, time.Hour)
ctx = cache.NewTag(ctx, "users")
db.Table("users").WithContext(ctx).Where("id = 1").Limit(1).Pluck("nickname", &nickname)
fmt.Println(nickname)
// output gormwithmysql
}
func main() {
newDb()
basic()
customKey()
useTag()
ctx := context.Background()
fmt.Println(redisClient.Keys(ctx, "*").Val())
fmt.Println(cachePlugin.RemoveFromTag(ctx, "users"))
}

View File

@@ -1,23 +0,0 @@
package cache
import (
"encoding/json"
)
type DefaultJSONSerializer struct{}
// Serialize
// @param v
// @date 2022-07-02 08:12:26
func (d *DefaultJSONSerializer) Serialize(v any) ([]byte, error) {
return json.Marshal(v)
}
// Deserialize
// @param data
// @param v
// @date 2022-07-02 08:12:25
func (d *DefaultJSONSerializer) Deserialize(data []byte, v any) error {
return json.Unmarshal(data, v)
}

View File

@@ -1,68 +0,0 @@
package redis
import (
"context"
"time"
"github.com/redis/go-redis/v9"
)
type Store struct {
store *redis.Client
}
// New
// @param conf
// @date 2022-07-02 08:12:14
func New(conf *redis.Options) *Store {
cli := redis.NewClient(conf)
return &Store{store: cli}
}
// NewWithDb
// @param tx
// @date 2022-07-02 08:12:12
func NewWithDb(tx *redis.Client) *Store {
return &Store{store: tx}
}
// Set
// @param ctx
// @param key
// @param value
// @param ttl
// @date 2022-07-02 08:12:11
func (r *Store) Set(ctx context.Context, key string, value any, ttl time.Duration) error {
return r.store.Set(ctx, key, value, ttl).Err()
}
// Get
// @param ctx
// @param key
// @date 2022-07-02 08:12:09
func (r *Store) Get(ctx context.Context, key string) ([]byte, error) {
return r.store.Get(ctx, key).Bytes()
}
// RemoveFromTag
// @param ctx
// @param tag
// @date 2022-07-02 08:12:08
func (r *Store) RemoveFromTag(ctx context.Context, tag string) error {
keys, err := r.store.SMembers(ctx, tag).Result()
if err != nil {
return err
}
return r.store.Del(ctx, keys...).Err()
}
// SaveTagKey
// @param ctx
// @param tag
// @param key
// @date 2022-07-02 08:12:05
func (r *Store) SaveTagKey(ctx context.Context, tag, key string) error {
return r.store.SAdd(ctx, tag, key).Err()
}

View File

@@ -1,66 +0,0 @@
package kv
import (
"github.com/cockroachdb/pebble"
)
var Impl *KvImpl
type KvImpl struct {
PebbleDB *pebble.DB
}
func NewPebble(datadir string) *KvImpl {
db, err := pebble.Open(datadir, &pebble.Options{})
if err != nil {
panic(err)
}
return &KvImpl{
PebbleDB: db,
}
}
func (db *KvImpl) PebbleSet(key, val string) error {
return db.PebbleDB.Set([]byte(key), []byte(val), pebble.Sync)
}
func (db *KvImpl) PebbleGet(key string) ([]byte, error) {
value, _, err := db.PebbleDB.Get([]byte(key))
if err != nil {
return nil, err
}
return value, nil
}
func (db *KvImpl) PebbleFetch(prefixKey string) (result map[string]string, err error) {
keyUpperBound := func(b []byte) []byte {
end := make([]byte, len(b))
copy(end, b)
for i := len(end) - 1; i >= 0; i-- {
end[i] = end[i] + 1
if end[i] != 0 {
return end[:i+1]
}
}
return nil // no upper-bound
}
prefixIterOptions := func(prefix []byte) *pebble.IterOptions {
return &pebble.IterOptions{
LowerBound: prefix,
UpperBound: keyUpperBound(prefix),
}
}
iter, err := db.PebbleDB.NewIter(prefixIterOptions([]byte(prefixKey)))
if err != nil {
return nil, err
}
for iter.First(); iter.Valid(); iter.Next() {
result[string(iter.Key())] = string(iter.Value())
}
return
}