gormcache

package module
v0.1.1 Latest Latest
Warning

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

Go to latest
Published: Feb 4, 2026 License: MIT Imports: 14 Imported by: 0

README

GORM Cache Plugin

A flexible and easy-to-use cache plugin for GORM with Redis and in-memory support.

Features

  • 🚀 Easy Integration: Seamless integration with GORM plugin system
  • 💾 Multiple Adapter Support: Redis, In-Memory, and custom adapter support
  • 🎯 Model-Based Caching: Choose which models to cache
  • 🔄 Auto Invalidation: Automatic cache clearing on CREATE, UPDATE, DELETE operations
  • ⏭️ Query-Based Skip: Skip cache for specific queries
  • ⚙️ Customizable: Custom cache key generation and skip condition functions
  • 🔒 Thread-Safe: Safe for concurrent operations

Installation

go get github.com/restayway/gorm-cache

Quick Start

Using In-Memory Cache
package main

import (
    "gorm.io/driver/sqlite"
    "gorm.io/gorm"
    gormcache "github.com/restayway/gorm-cache"
    "time"
)

type User struct {
    ID   uint
    Name string
}

func main() {
    db, _ := gorm.Open(sqlite.Open("test.db"), &gorm.Config{})

    // Install cache plugin
    cachePlugin := gormcache.New(gormcache.Config{
        Adapter: gormcache.NewMemoryAdapter(),
        TTL:     5 * time.Minute,
    })
    db.Use(cachePlugin)

    // Normal GORM queries - automatically cached
    var user User
    db.First(&user, 1) // First query from database
    db.First(&user, 1) // Second query from cache
}
Using Redis Cache
package main

import (
    "gorm.io/driver/postgres"
    "gorm.io/gorm"
    gormcache "github.com/restayway/gorm-cache"
    "time"
)

func main() {
    db, _ := gorm.Open(postgres.Open("dsn"), &gorm.Config{})

    // Install cache plugin with Redis adapter
    cachePlugin := gormcache.New(gormcache.Config{
        Adapter: gormcache.NewRedisAdapter(gormcache.RedisAdapterConfig{
            Addr:     "localhost:6379",
            Password: "",
            DB:       0,
        }),
        TTL:                5 * time.Minute,
        InvalidateOnUpdate: true,
        InvalidateOnCreate: true,
        InvalidateOnDelete: true,
    })
    db.Use(cachePlugin)
}

API Reference

Context-Based API

The plugin provides context-based functions for controlling cache behavior:

// Skip cache using context (recommended)
ctx := gormcache.SkipCacheContext(context.Background())
db.WithContext(ctx).Find(&users)

// Lower-level API - set skip cache value
ctx := gormcache.WithSkipCache(context.Background(), true)
db.WithContext(ctx).Find(&users)

// Enable cache (set skip to false)
ctx := gormcache.WithSkipCache(context.Background(), false)
db.WithContext(ctx).Find(&users)
Scope-Based API

The plugin also provides GORM scope helpers:

// Skip cache using scope
db.Scopes(gormcache.SkipCache()).Find(&users)

// Enable cache using scope
db.Scopes(gormcache.EnableCache()).Find(&users)

Advanced Usage

Cache Specific Models
type User struct {
    ID   uint
    Name string
}

type Order struct {
    ID     uint
    UserID uint
}

cachePlugin := gormcache.New(gormcache.Config{
    Adapter: gormcache.NewMemoryAdapter(),
    TTL:     5 * time.Minute,
    // Only cache User and Order models
    CacheModels: []interface{}{
        User{},
        Order{},
    },
})
db.Use(cachePlugin)
Query-Based Cache Skip
// Method 1: Using SkipCache scope helper
var users []User
db.Scopes(gormcache.SkipCache()).Find(&users)

// Method 2: Using context helper (recommended)
ctx := gormcache.SkipCacheContext(context.Background())
db.WithContext(ctx).Find(&users)

// Method 3: Lower-level context API
ctx := gormcache.WithSkipCache(context.Background(), true)
db.WithContext(ctx).Find(&users)

// Method 4: Custom skip condition
cachePlugin := gormcache.New(gormcache.Config{
    Adapter: gormcache.NewMemoryAdapter(),
    TTL:     5 * time.Minute,
    SkipCacheCondition: func(db *gorm.DB) bool {
        // Skip cache for admin users
        if userRole, ok := db.Statement.Context.Value("user_role").(string); ok {
            return userRole == "admin"
        }
        return false
    },
})
Custom Cache Key Generator
cachePlugin := gormcache.New(gormcache.Config{
    Adapter: gormcache.NewMemoryAdapter(),
    TTL:     5 * time.Minute,
    CacheKeyGenerator: func(db *gorm.DB) string {
        // Custom key generation logic
        return fmt.Sprintf("custom:%s:%v", db.Statement.Table, db.Statement.Vars)
    },
})
Invalidation Settings
cachePlugin := gormcache.New(gormcache.Config{
    Adapter:            gormcache.NewMemoryAdapter(),
    TTL:                5 * time.Minute,
    InvalidateOnUpdate: true,  // Clear cache on UPDATE
    InvalidateOnCreate: false, // Don't clear cache on CREATE
    InvalidateOnDelete: true,  // Clear cache on DELETE
})

Custom Adapter

You can create your own cache adapter:

package main

import (
    "context"
    "time"
    gormcache "github.com/restayway/gorm-cache"
)

type MyCustomAdapter struct {
    // Your custom fields
}

func (a *MyCustomAdapter) Get(ctx context.Context, key string) ([]byte, error) {
    // Get implementation
    return nil, nil
}

func (a *MyCustomAdapter) Set(ctx context.Context, key string, value []byte, ttl time.Duration) error {
    // Set implementation
    return nil
}

func (a *MyCustomAdapter) Delete(ctx context.Context, key string) error {
    // Delete implementation
    return nil
}

func (a *MyCustomAdapter) DeletePattern(ctx context.Context, pattern string) error {
    // DeletePattern implementation
    return nil
}

func (a *MyCustomAdapter) Clear(ctx context.Context) error {
    // Clear implementation
    return nil
}

func (a *MyCustomAdapter) Close() error {
    // Close implementation
    return nil
}

// Usage
func main() {
    cachePlugin := gormcache.New(gormcache.Config{
        Adapter: &MyCustomAdapter{},
        TTL:     5 * time.Minute,
    })
}

Configuration Options

Option Type Default Description
Adapter Adapter MemoryAdapter Cache storage implementation
TTL time.Duration 5 * time.Minute Default TTL for cache
CacheModels []interface{} [] Models to cache (empty = all)
InvalidateOnUpdate bool true Clear cache on UPDATE
InvalidateOnCreate bool true Clear cache on CREATE
InvalidateOnDelete bool true Clear cache on DELETE
KeyPrefix string "gorm:cache:" Cache key prefix
SkipCacheCondition func(*gorm.DB) bool nil Custom condition to skip cache
CacheKeyGenerator func(*gorm.DB) string nil Custom cache key generator

Performance Tips

  1. TTL Settings: Set appropriate TTL based on data update frequency
  2. Model Selection: Only cache frequently read models
  3. Redis vs Memory: Use Redis for production/multi-instance, memory for single instance/dev/test
  4. Invalidation: Disable unnecessary invalidation for better performance

Limitations

  • Currently only SELECT queries are cached
  • Cache serialization uses JSON
  • Memory adapter doesn't persist (data lost on restart)

Contributing

Contributions are welcome! Please feel free to submit PRs.

License

MIT License

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func EnableCache

func EnableCache() func(*gorm.DB) *gorm.DB

EnableCache is a scope helper function to explicitly enable cache for a query Usage: db.Scopes(gormcache.EnableCache()).Find(&users)

func SkipCache

func SkipCache() func(*gorm.DB) *gorm.DB

SkipCache is a scope helper function to skip cache for a specific query Usage: db.Scopes(gormcache.SkipCache()).Find(&users)

func SkipCacheContext

func SkipCacheContext(ctx context.Context) context.Context

SkipCacheContext returns a new context that will skip cache for queries

func WithSkipCache

func WithSkipCache(ctx context.Context, skip bool) context.Context

WithSkipCache is a lower-level function to set skip cache value

Types

type Adapter

type Adapter interface {
	// Get retrieves a value from cache by key
	Get(ctx context.Context, key string) ([]byte, error)

	// Set stores a value in cache with the given key and TTL
	Set(ctx context.Context, key string, value []byte, ttl time.Duration) error

	// Delete removes a value from cache by key
	Delete(ctx context.Context, key string) error

	// DeletePattern removes all keys matching the pattern
	DeletePattern(ctx context.Context, pattern string) error

	// Clear removes all cached data
	Clear(ctx context.Context) error

	// Close closes the adapter connection
	Close() error
}

Adapter defines the interface for cache storage implementations

type CachePlugin

type CachePlugin struct {
	// contains filtered or unexported fields
}

CachePlugin is a GORM plugin that provides caching functionality

func New

func New(config Config) *CachePlugin

New creates a new cache plugin with the given configuration

func (*CachePlugin) Close

func (p *CachePlugin) Close() error

Close closes the cache adapter

func (*CachePlugin) Initialize

func (p *CachePlugin) Initialize(db *gorm.DB) error

Initialize initializes the plugin with GORM

func (*CachePlugin) Name

func (p *CachePlugin) Name() string

Name returns the plugin name

type Config

type Config struct {
	// Adapter is the cache storage implementation
	Adapter Adapter

	// TTL is the default time-to-live for cached data
	TTL time.Duration

	// CacheModels defines which models should be cached
	// If empty, all models will be cached
	CacheModels []interface{}

	// InvalidateOnUpdate determines if cache should be cleared on UPDATE operations
	InvalidateOnUpdate bool

	// InvalidateOnCreate determines if cache should be cleared on CREATE operations
	InvalidateOnCreate bool

	// InvalidateOnDelete determines if cache should be cleared on DELETE operations
	InvalidateOnDelete bool

	// KeyPrefix is the prefix for all cache keys
	KeyPrefix string

	// SkipCacheCondition is a function to determine if cache should be skipped for a query
	// Example: func(db *gorm.DB) bool { return db.Statement.Context.Value("skip_cache") == true }
	SkipCacheCondition func(*gorm.DB) bool

	// CacheKeyGenerator allows custom cache key generation
	// If nil, default key generator will be used
	CacheKeyGenerator func(*gorm.DB) string

	// Serializer is the data serialization implementation
	// If nil, default JSON serializer will be used
	Serializer Serializer
}

Config holds the configuration for the cache plugin

func DefaultConfig

func DefaultConfig() Config

DefaultConfig returns a default configuration

type ErrCacheHit

type ErrCacheHit struct {
	RowsAffected int64
}

ErrCacheHit 是一个内部使用的 Error,用于在缓存命中时跳过数据库查询 这个 Error 会在 afterQueryCallback 中被自动移除,用户不会看到它

func (*ErrCacheHit) Error

func (e *ErrCacheHit) Error() string

type JSONSerializer added in v0.1.0

type JSONSerializer struct{}

JSONSerializer implements JSON serialization

func (*JSONSerializer) Marshal added in v0.1.0

func (j *JSONSerializer) Marshal(v interface{}) ([]byte, error)

Marshal serializes v to JSON bytes

func (*JSONSerializer) Unmarshal added in v0.1.0

func (j *JSONSerializer) Unmarshal(data []byte, v interface{}) error

Unmarshal deserializes JSON bytes to v

type MemoryAdapter

type MemoryAdapter struct {
	// contains filtered or unexported fields
}

MemoryAdapter is an in-memory cache implementation

func NewMemoryAdapter

func NewMemoryAdapter() *MemoryAdapter

NewMemoryAdapter creates a new in-memory cache adapter

func (*MemoryAdapter) Clear

func (m *MemoryAdapter) Clear(ctx context.Context) error

Clear removes all cached data

func (*MemoryAdapter) Close

func (m *MemoryAdapter) Close() error

Close closes the adapter

func (*MemoryAdapter) Delete

func (m *MemoryAdapter) Delete(ctx context.Context, key string) error

Delete removes a value from memory cache

func (*MemoryAdapter) DeletePattern

func (m *MemoryAdapter) DeletePattern(ctx context.Context, pattern string) error

DeletePattern removes all keys matching the pattern

func (*MemoryAdapter) Get

func (m *MemoryAdapter) Get(ctx context.Context, key string) ([]byte, error)

Get retrieves a value from memory cache

func (*MemoryAdapter) Set

func (m *MemoryAdapter) Set(ctx context.Context, key string, value []byte, ttl time.Duration) error

Set stores a value in memory cache

type MsgPackSerializer added in v0.1.0

type MsgPackSerializer struct{}

MsgPackSerializer implements MessagePack serialization

func (*MsgPackSerializer) Marshal added in v0.1.0

func (m *MsgPackSerializer) Marshal(v interface{}) ([]byte, error)

Marshal serializes v to MessagePack bytes

func (*MsgPackSerializer) Unmarshal added in v0.1.0

func (m *MsgPackSerializer) Unmarshal(data []byte, v interface{}) error

Unmarshal deserializes MessagePack bytes to v

type RedisAdapter

type RedisAdapter struct {
	// contains filtered or unexported fields
}

RedisAdapter is a Redis cache implementation

func NewRedisAdapter

func NewRedisAdapter(config RedisAdapterConfig) *RedisAdapter

NewRedisAdapter creates a new Redis cache adapter

func NewRedisAdapterWithClient

func NewRedisAdapterWithClient(client *redis.Client) *RedisAdapter

NewRedisAdapterWithClient creates a new Redis adapter with existing client

func (*RedisAdapter) Clear

func (r *RedisAdapter) Clear(ctx context.Context) error

Clear removes all cached data in the current database

func (*RedisAdapter) Close

func (r *RedisAdapter) Close() error

Close closes the Redis connection

func (*RedisAdapter) Delete

func (r *RedisAdapter) Delete(ctx context.Context, key string) error

Delete removes a value from Redis cache

func (*RedisAdapter) DeletePattern

func (r *RedisAdapter) DeletePattern(ctx context.Context, pattern string) error

DeletePattern removes all keys matching the pattern

func (*RedisAdapter) Get

func (r *RedisAdapter) Get(ctx context.Context, key string) ([]byte, error)

Get retrieves a value from Redis cache

func (*RedisAdapter) Set

func (r *RedisAdapter) Set(ctx context.Context, key string, value []byte, ttl time.Duration) error

Set stores a value in Redis cache

type RedisAdapterConfig

type RedisAdapterConfig struct {
	Addr     string // Redis server address (default: "localhost:6379")
	Password string // Redis password (default: "")
	DB       int    // Redis database (default: 0)
}

RedisAdapterConfig holds configuration for Redis adapter

type Serializer added in v0.1.0

type Serializer interface {
	Marshal(v interface{}) ([]byte, error)
	Unmarshal(data []byte, v interface{}) error
}

Serializer defines the interface for data serialization

Directories

Path Synopsis
examples
basic command
context_usage command
model_selection command
redis command
skip_cache command

Jump to

Keyboard shortcuts

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