entcache

package module
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Dec 20, 2024 License: Apache-2.0 Imports: 20 Imported by: 12

README

EntCache

本项目灵感来源于 ariga.io/entcache. 在数据缓存方面强化, 如果需要使用上下文缓存,请使用源项目.

在使用过程中,发现源项目在缓存方面有一些不足,比如:

  • 无法影响数据变化. 比如在缓存中的数据,在数据库中被发生变更时,缓存中的数据无法获得通知.
  • 缓存时间需要显示控制, 如某些查询需要长时间缓存,某些查询需要短时间缓存.

为了缓存该问题,项目结合了Ent的Hooks机制与模板功能的配合使用,针对中某些特定的查询,如Get方法以及Graphql中Noder查询.在约定的开发模式下, 尽量在发生数据变更后,UI能尽快的获取到最新的数据.

本项目并未实现在自行查询上的同步缓存更新,因此还需要使用者在开发过程中, 显式通过context的使用来合理的使用缓存.

设计

查询类型

在缓存KV中, 统一采用查询语句的Hash值做为键,来避免不同的库表命名冲空.但在查询方式中,我们区分了两种查询类型:

  • Hash: 是开发者自定义的查询.
  • Key: 通过主键获取的数据的查询, 如Client.Get(context.Context, id any)同源的方法(如Only). 能主动影响数据变化.

对于这两种类型的查询,可以设定不同的缓存时间. 一般来说,Hash类型的查询,缓存时间会比较短,Key类型的查询,缓存时间会比较长.

Key查询的淘汰

Key类型的缓存由发现变化到变化结束,其变化结束控制点在于Get触发执行,如果Get未执行时,则变化标记一直存在.

针对同源查询的缓存淘汰处理如下:

  • 在标记期间,如果标记时间超过了上一次查询的缓存时间,则会触发缓存淘汰, 以更新最新的数据.
  • 在标记期间,如果不存在Hash值(变更后的第一次查询),则会触发缓存淘汰,防止变化前的缓存.
  • 未在标记期间的查询,如存在Hash值,则触发缓存淘汰.该Hash值的存储只在标记期间,存在说明存在旧数据.
内置缓存

内置的实现了Cache接口的TinyLFU缓存.

使用

go get github.com/woocoos/entcache

在需要使用的Schema中,引入Hooks:

func (User) Hooks() []ent.Hook {
	return []ent.Hook{
		entcache.DataChangeNotify(),
	}
}

我们可通过配置方式, 初始化Driver.

entcache:
  # 可选, Hash类型查询的缓存时间,如果使用内置缓存,则为内置缓存的缓存时间.
  hashQueryTTL: 10s
  keyQueryTTL: 1h
  # 可选, 指定注册的缓存组件.
  cacheKey: entcache
  # 可选, 缓存前缀, 如果共用缓存组件则会有用.
  cachePrefix: "admin:"
// cnf 为配置组件 
drv := entcache.NewDriver(entcache.Configuration(cnf.sub("entcache")))
client := ent.NewClient(ent.Driver(drv))
// 启用监听
go drv.Start(context.Background())

就可在代码中使用缓存了.

ctx := entcache.WithEntryKey(context.Background(), "User", 1)
client.User.Get(ctx, 1)
利用模板简化

在context的写法有些麻烦是不,并且还会传入无关的Key, 对变更也不友好.

我们内置对Ent Client模板的修改,在生成的代码中,让Get自动引入了缓存的上下文,可在entc使用如:

func main() {
	// ....
	opts := []entc.Option{
		cachegen.QueryCache(),
	}
	if err := entc.Generate("./schema", &gen.Config{}, opts...); err != nil {
		log.Fatalf("running ent codegen: %v", err)
	}
}

Get方法就像平常那样使用了

如果你的项目已经存在模板的修改,那你已经知道怎么修改模板了,可以把调整模板的代码拷贝过来到你的模板中.

entgql也是同理修改,你可参考TODO中的修改.

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func DataChangeNotify

func DataChangeNotify(opts ...HookOption) ent.Hook

DataChangeNotify returns a hook that notifies the cache when a mutation is performed.

Driver in method is a placeholder for the cache driver name, which is lazy loaded by NewDriver. Use IDs method to get the ids of the mutation, that also works for XXXOne.

func Evict

func Evict(ctx context.Context) context.Context

Evict returns a new Context that tells the Driver to refresh the cache entry on Query.

client.T.Query().All(entcache.Evict(ctx))

func Skip

func Skip(ctx context.Context) context.Context

Skip returns a new Context that tells the Driver to skip the cache entry on Query.

client.T.Query().All(entcache.Skip(ctx))

func WithEntryKey

func WithEntryKey(ctx context.Context, typ string, id any) context.Context

WithEntryKey returns a new Context that carries the Key for the cache entry. Note that the key is one shot, otherwise cause error if the ent.Client query involves more than 1 SQL query (e.g. eager loading).

func WithRefEntryKey

func WithRefEntryKey(ctx context.Context, typ string, id any) context.Context

WithRefEntryKey returns a new Context that carries a reference Entry Key for the cache entry. RefEntryKey indicates if the key is a reference an entry key. For example, when Get is called, ref is false, because Get use id query and get all fields. When others are called, such as Only, ref is false.

func WithTTL

func WithTTL(ctx context.Context, ttl time.Duration) context.Context

WithTTL returns a new Context that carries the TTL for the cache entry.

client.T.Query().All(entcache.WithTTL(ctx, time.Second))

Types

type ChangeSet

type ChangeSet struct {
	sync.RWMutex
	// contains filtered or unexported fields
}

ChangeSet is a set of keys that have changed, include update, delete, create.

func NewChangeSet

func NewChangeSet(gcInterval time.Duration) *ChangeSet

func (*ChangeSet) Delete

func (a *ChangeSet) Delete(key Key)

func (*ChangeSet) DeleteRef

func (a *ChangeSet) DeleteRef(key Key)

func (*ChangeSet) Load

func (a *ChangeSet) Load(key Key) (time.Time, bool)

func (*ChangeSet) LoadOrStoreRef

func (a *ChangeSet) LoadOrStoreRef(key Key) (t time.Time, loaded bool)

LoadOrStoreRef returns the time when the key was last updated.

func (*ChangeSet) LoadRef

func (a *ChangeSet) LoadRef(key Key) (time.Time, bool)

func (*ChangeSet) Start

func (a *ChangeSet) Start(ctx context.Context) error

func (*ChangeSet) Stop

func (a *ChangeSet) Stop(ctx context.Context) error

func (*ChangeSet) Store

func (a *ChangeSet) Store(keys ...Key)

type Config

type Config struct {
	// Name of the driver, used for ent cache driver mandger.
	Name string `yaml:"name" json:"name"`
	// Cache defines the cache implementation for holding the cache entries.
	// Default is tinyLFU with size 100000 and HashQueryTTL 1 minute.
	Cache cache.Cache `yaml:"-" json:"-"`
	// HashQueryTTL defines the period of time that an Entry that is hashed through by not Get
	// is valid in the cache.
	HashQueryTTL time.Duration `yaml:"hashQueryTTL" json:"hashQueryTTL"`
	// KeyQueryTTL defines the period of time that an Entry that is not hashed through by Get. This is keep the cached
	// data fresh, can be set to long time, such as 1 hour.
	KeyQueryTTL time.Duration `yaml:"keyQueryTTL" json:"keyQueryTTL"`
	// GCInterval defines the period of time that the cache will be GC.
	GCInterval time.Duration `yaml:"gcInterval" json:"gcInterval"`
	// StoreKey is the driver name of cache driver
	StoreKey string `yaml:"storeKey" json:"storeKey"`
	// CachePrefix is the prefix of cache key, avoid key conflict in redis cache
	CachePrefix string `yaml:"cachePrefix" json:"cachePrefix"`
	// ChangeSet manages data change
	ChangeSet *ChangeSet
}

Config wraps the basic configuration cache options.

type Driver

type Driver struct {
	*Config
	dialect.Driver

	Hash func(query string, args []any) (Key, error)
	// contains filtered or unexported fields
}

A Driver is a SQL cached client. Users should use the constructor below for creating a new driver.

func NewDriver

func NewDriver(drv dialect.Driver, opts ...Option) *Driver

NewDriver wraps the given driver with a caching layer.

func (*Driver) Query

func (d *Driver) Query(ctx context.Context, query string, args, v any) error

Query implements the Querier interface for the driver. It falls back to the underlying wrapped driver in case of caching error.

Note that the driver does not synchronize identical queries that are executed concurrently. Hence, if 2 identical queries are executed at the ~same time, and there is no cache entry for them, the driver will execute both of them and the last successful one will be stored in the cache.

type Entry

type Entry struct {
	Columns []string
	Values  [][]driver.Value
}

type HookOption

type HookOption func(*hookOptions)

func WithDriverName

func WithDriverName(name string) HookOption

WithDriverName sets which named ent cache driver name to use.

type Key

type Key string

func DefaultHash

func DefaultHash(query string, args []any) (Key, error)

DefaultHash provides the default implementation for converting a query and its argument to a cache key.

func NewEntryKey

func NewEntryKey(typ string, id string) Key

type Option

type Option func(*Config)

Option allows configuring the cache driver using functional options.

func WithCache

func WithCache(cc cache.Cache) Option

WithCache provides a cache implementation for holding the cache entries.

func WithChangeSet

func WithChangeSet(cs *ChangeSet) Option

func WithConfiguration

func WithConfiguration(cnf *conf.Configuration) Option

WithConfiguration provides a configuration option for the cache driver.

type Stats

type Stats struct {
	Gets   uint64
	Hits   uint64
	Errors uint64
}

Stats represent the cache statistics of the driver.

Directories

Path Synopsis
integration module

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL