Documentation
¶
Index ¶
- Variables
- func DataChangeNotify(drv *Driver) ent.Hook
- func Evict(ctx context.Context) context.Context
- func NewContext(ctx context.Context, levels ...AddGetDeleter) context.Context
- func Skip(ctx context.Context) context.Context
- func SkipNotFound(ctx context.Context) context.Context
- func WithEntryKey(ctx context.Context, typ string, id any) context.Context
- func WithKey(ctx context.Context, key Key) context.Context
- func WithTTL(ctx context.Context, ttl time.Duration) context.Context
- type AddGetDeleter
- type ChangeSet
- type Driver
- func (d *Driver) ExecContext(ctx context.Context, query string, args ...any) (stdsql.Result, error)
- func (d *Driver) Query(ctx context.Context, query string, args, v any) error
- func (d *Driver) QueryContext(ctx context.Context, query string, args ...any) (*stdsql.Rows, error)
- func (d *Driver) Stats() Stats
- type Entry
- type Key
- type LRU
- type NatsKV
- func (n *NatsKV) Add(ctx context.Context, k Key, e *Entry, ttl time.Duration) error
- func (n *NatsKV) Create(ctx context.Context, k Key, e *Entry, ttl time.Duration) error
- func (n *NatsKV) Del(ctx context.Context, k Key) error
- func (n *NatsKV) Get(ctx context.Context, k Key) (*Entry, error)
- func (n *NatsKV) Watch(ctx context.Context, pattern string, opts ...jetstream.WatchOpt) (jetstream.KeyWatcher, error)
- type Option
- type Options
- type Redis
- type Rueidis
- func (r *Rueidis) Add(ctx context.Context, k Key, e *Entry, ttl time.Duration) error
- func (r *Rueidis) Del(ctx context.Context, k Key) error
- func (r *Rueidis) Get(ctx context.Context, k Key) (*Entry, error)
- func (r *Rueidis) Register(key string) (wait <-chan struct{}, first bool)
- func (r *Rueidis) Unregister(key string)
- type Stats
Constants ¶
This section is empty.
Variables ¶
var ErrNotFound = errors.New("entcache: entry was not found")
ErrNotFound is returned by Get when an Entry does not exist in the cache.
Functions ¶
func DataChangeNotify ¶
DataChangeNotify returns an ent Hook that marks changed entity keys in the Driver's ChangeSet whenever a mutation (create, update, delete) is committed. This enables automatic cache invalidation for key-addressed queries.
Usage:
drv := entcache.NewDriver(sqlDrv, entcache.WithChangeSet(cs)) client := ent.NewClient(ent.Driver(drv)) client.Use(entcache.DataChangeNotify(drv))
func Evict ¶
Evict returns a new Context that tells the Driver to skip and invalidate the cache entry on Query.
client.T.Query().All(entcache.Evict(ctx))
func NewContext ¶
func NewContext(ctx context.Context, levels ...AddGetDeleter) context.Context
NewContext returns a new Context that carries a cache.
func Skip ¶
Skip returns a new Context that tells the Driver to skip the cache entry on Query.
client.T.Query().All(entcache.Skip(ctx))
func SkipNotFound ¶
SkipNotFound returns a new Context that tells the Driver to skip caching when the query result contains zero rows. This prevents caching empty results for entities that may be created shortly after.
client.User.Get(entcache.SkipNotFound(ctx), 42)
func WithEntryKey ¶
WithEntryKey returns a new Context with a structured entity key (e.g. "User:42") and marks the query as key-addressed. Key-addressed queries are eligible for the longer KeyTTL and precise invalidation via ChangeSet.
client.User.Get(entcache.WithEntryKey(ctx, "User", 42), 42)
Types ¶
type AddGetDeleter ¶
type AddGetDeleter interface {
Del(ctx context.Context, k Key) error
Add(ctx context.Context, k Key, e *Entry, ttl time.Duration) error
Get(ctx context.Context, k Key) (*Entry, error)
}
AddGetDeleter defines the interface for getting, adding and deleting entries from the cache.
func FromContext ¶
func FromContext(ctx context.Context) (AddGetDeleter, bool)
FromContext returns the cache value stored in ctx, if any.
type ChangeSet ¶
type ChangeSet struct {
// contains filtered or unexported fields
}
ChangeSet tracks entity keys that have been modified (created, updated, or deleted). It is used by the Driver to detect stale cache entries and force re-queries. A background GC goroutine prunes entries older than the GC interval.
func NewChangeSet ¶
NewChangeSet creates a new ChangeSet with the given GC interval. If gcInterval is <= 0, the default of 5 minutes is used.
func (*ChangeSet) Changed ¶
Changed reports whether the given key has been marked as changed since the given time. This is used by the Driver to decide whether a cache hit should be evicted and re-fetched.
func (*ChangeSet) Clear ¶
Clear removes the change markers for the given keys, acknowledging that the cache has been refreshed.
type Driver ¶
A Driver is an SQL cached client. Users should use the constructor below for creating new driver.
func NewDriver ¶
NewDriver returns a new Driver an existing driver and optional configuration functions. For example:
entcache.NewDriver(
drv,
entcache.TTL(time.Minute),
entcache.Levels(
NewLRU(256),
NewRedis(redis.NewClient(&redis.Options{
Addr: ":6379",
})),
),
)
func (*Driver) ExecContext ¶
ExecContext calls ExecContext of the underlying driver, or fails if it is not supported.
func (*Driver) Query ¶
Query implements the Querier interface for the driver. It falls back to the underlying wrapped driver in case of caching error.
Stampede protection: concurrent identical queries are deduplicated via singleflight. Only the first caller hits the database; others receive the same result.
func (*Driver) QueryContext ¶
QueryContext calls QueryContext of the underlying driver, or fails if it is not supported. Note, this method is not part of the caching layer since Ent does not use it by default.
type Entry ¶
Entry defines an entry to store in a cache.
func (Entry) MarshalBinary ¶
MarshalBinary implements the encoding.BinaryMarshaler interface.
func (*Entry) UnmarshalBinary ¶
UnmarshalBinary implements the encoding.BinaryUnmarshaler interface.
type Key ¶
type Key any
A Key defines a comparable Go value. See http://golang.org/ref/spec#Comparison_operators
func DefaultHash ¶
DefaultHash provides the default implementation for converting a query and its argument to a cache key.
func NewEntryKey ¶
NewEntryKey constructs a structured cache key from an entity type name and ID. This produces keys like "User:42" that enable precise invalidation via ChangeSet.
type LRU ¶
LRU provides an LRU cache that implements the AddGetDeleter interface.
type NatsKV ¶
type NatsKV struct {
// contains filtered or unexported fields
}
NatsKV provides a remote cache backed by NATS JetStream KeyValue and implements the AddGetDeleter interface.
NATS JetStream KV supports:
- Create as a SETNX equivalent (only sets if key doesn't exist), with optional per-key TTL via jetstream.KeyTTL.
- Watch for invalidation notifications.
- Bucket-level TTL (MaxAge) for automatic expiry of all keys.
For per-key TTL, the implementation uses Create (which accepts KVCreateOpt) when TTL > 0. Put is used as a fallback when no TTL is needed since it does not accept TTL options.
func NewNatsKV ¶
NewNatsKV returns a new NATS JetStream KeyValue cache level. The bucket should be created/configured externally. If you need automatic expiry, set MaxAge on the KeyValueConfig when creating the bucket.
js, _ := jetstream.New(nc)
kv, _ := js.CreateKeyValue(ctx, jetstream.KeyValueConfig{
Bucket: "entcache",
MaxAge: 10 * time.Minute, // bucket-level TTL
})
entcache.NewNatsKV(kv)
func (*NatsKV) Add ¶
Add adds the entry to the cache using Put (unconditional overwrite). NATS KV Put does not support per-key TTL — expiry is governed by the bucket's MaxAge configuration. The ttl parameter is used with a Delete-then-Create approach when ttl > 0 to leverage Create's KeyTTL option for per-key expiry.
func (*NatsKV) Create ¶
Create adds the entry to the cache only if the key does not already exist. This is the SETNX (set-if-not-exists) equivalent for NATS KV, useful for stampede protection: only the first caller that wins the Create will populate the cache, others will get an error and should wait or re-check.
func (*NatsKV) Watch ¶
func (n *NatsKV) Watch(ctx context.Context, pattern string, opts ...jetstream.WatchOpt) (jetstream.KeyWatcher, error)
Watch returns a watcher for changes on keys matching the given pattern. This is the invalidation-notification equivalent: callers can watch for key updates and deletions to trigger cache invalidation in local caches when used in a multi-level setup.
The returned KeyWatcher should be stopped by the caller when no longer needed.
type Option ¶
type Option func(*Options)
Option allows configuring the cache driver using functional options.
func ContextLevel ¶
func ContextLevel() Option
ContextLevel configures the driver to work with context/request level cache. Users that use this option, should wraps the *http.Request context with the cache value as follows:
ctx = entcache.NewContext(ctx) ctx = entcache.NewContext(ctx, entcache.NewLRU(128))
func Hash ¶
Hash configures an optional Hash function for converting a query and its arguments to a cache key.
func Levels ¶
func Levels(levels ...AddGetDeleter) Option
Levels configures the Driver to work with the given cache levels. For example, in process LRU cache and a remote Redis cache.
func WithChangeSet ¶
WithChangeSet configures the Driver to use the given ChangeSet for mutation-aware cache invalidation.
func WithKeyTTL ¶
WithKeyTTL configures a separate TTL for key-addressed queries (e.g. Get-by-ID). Key-addressed queries can have a longer TTL because they are precisely invalidated via the ChangeSet. If not set, the regular TTL is used.
type Options ¶
type Options struct {
// TTL defines the period of time that an Entry
// is valid in the cache (used for hash-addressed queries).
TTL time.Duration
// KeyTTL defines the period of time that a key-addressed Entry
// (e.g. Get-by-ID queries) is valid in the cache. Key-addressed
// queries can have a longer TTL because they are precisely
// invalidated via the ChangeSet. If zero, TTL is used.
KeyTTL time.Duration
// Cache defines the GetAddDeleter (cache implementation)
// for holding the cache entries. If no cache implementation
// was provided, an LRU cache with no limit is used.
Cache AddGetDeleter
// Hash defines an optional Hash function for converting
// a query and its arguments to a cache key. If no Hash
// function was provided, the DefaultHash is used.
Hash func(query string, args []any) (Key, error)
// ChangeSet holds the mutation change tracker. When set,
// the Driver checks whether cached entries have been
// invalidated by mutations before returning them.
ChangeSet *ChangeSet
// Logf function. If provided, the Driver will call it with
// errors that can not be handled.
Log func(...any)
}
Options wraps the basic configuration cache options.
type Redis ¶
type Redis struct {
// contains filtered or unexported fields
}
Redis provides a remote cache backed by go-redis and implements the AddGetDeleter interface.
func NewRedis ¶
NewRedis returns a new Redis cache level from the given Redis connection.
entcache.NewRedis(redis.NewClient(&redis.Options{
Addr: ":6379"
}))
entcache.NewRedis(redis.NewClusterClient(&redis.ClusterOptions{
Addrs: []string{":7000", ":7001", ":7002"},
}))
type Rueidis ¶
type Rueidis struct {
// contains filtered or unexported fields
}
Rueidis provides a remote cache backed by rueidis and implements the AddGetDeleter interface with stampede protection.
Stampede protection (inspired by rueidisaside): when a cache miss occurs, the first caller for a given key proceeds to fetch from the database while concurrent callers for the same key block on a channel until the first caller populates the cache. This prevents multiple identical database queries from being executed simultaneously.
func NewRueidis ¶
NewRueidis returns a new Rueidis cache level from the given rueidis client.
c, _ := rueidis.NewClient(rueidis.ClientOption{
InitAddress: []string{"127.0.0.1:6379"},
})
entcache.NewRueidis(c)
func (*Rueidis) Add ¶
Add adds the entry to the cache. If a stampede wait channel exists for this key, it is closed to unblock any goroutines waiting on Get.
func (*Rueidis) Get ¶
Get gets an entry from the cache. If the key is not found, it returns ErrNotFound. Callers can use the stampede protection via the Driver's singleflight integration — this method itself is non-blocking.
func (*Rueidis) Register ¶
Register registers interest in a key for stampede protection. Returns a channel that will be closed when the key is populated via Add, and a boolean indicating whether this caller is the first (i.e. should fetch). If first is true, the caller is responsible for calling Add or Unregister.
func (*Rueidis) Unregister ¶
Unregister removes a stampede wait channel without populating the cache. Use this when the first caller encounters an error fetching from the database.


