caches

package module
v2.0.0-...-33315b6 Latest Latest
Warning

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

Go to latest
Published: Oct 17, 2023 License: MIT Imports: 14 Imported by: 0

README

Gorm Caches

Gorm Caches plugin using database request reductions (easer), and response caching mechanism provide you an easy way to optimize database performance.

Features

  • Database request reduction. If three identical requests are running at the same time, only the first one is going to be executed, and its response will be returned for all.
  • Database response caching. By implementing the Cacher interface, you can easily setup a caching mechanism for your database queries.
  • Supports all databases that are supported by gorm itself.

Install

go get -u github.com/go-gorm/caches/v2

Usage

Configure the easer, and the cacher, and then load the plugin to gorm.

package main

import (
	"fmt"
	"sync"

	"github.com/go-gorm/caches"
	"gorm.io/driver/mysql"
	"gorm.io/gorm"
)

func main() {
	db, _ := gorm.Open(
		mysql.Open("DATABASE_DSN"),
		&gorm.Config{},
	)
	cachesPlugin := &caches.Caches{Conf: &caches.Config{
		Easer: true,
		Cacher: &yourCacherImplementation{},
	}}
	_ = db.Use(cachesPlugin)
}

Easer Example

package main

import (
	"fmt"
	"sync"
	"time"

	"github.com/go-gorm/caches"
	"gorm.io/driver/mysql"
	"gorm.io/gorm"
)

type UserRoleModel struct {
	gorm.Model
	Name string `gorm:"unique"`
}

type UserModel struct {
	gorm.Model
	Name   string
	RoleId uint
	Role   *UserRoleModel `gorm:"foreignKey:role_id;references:id"`
}

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

	cachesPlugin := &caches.Caches{Conf: &caches.Config{
		Easer: true,
	}}

	_ = db.Use(cachesPlugin)

	_ = db.AutoMigrate(&UserRoleModel{})

	_ = db.AutoMigrate(&UserModel{})

	adminRole := &UserRoleModel{
		Name: "Admin",
	}
	db.FirstOrCreate(adminRole, "Name = ?", "Admin")

	guestRole := &UserRoleModel{
		Name: "Guest",
	}
	db.FirstOrCreate(guestRole, "Name = ?", "Guest")

	db.Save(&UserModel{
		Name: "ktsivkov",
		Role: adminRole,
	})
	db.Save(&UserModel{
		Name: "anonymous",
		Role: guestRole,
	})

	var (
		q1Users []UserModel
		q2Users []UserModel
	)
	wg := &sync.WaitGroup{}
	wg.Add(2)
	go func() {
		db.Model(&UserModel{}).Joins("Role").Find(&q1Users, "Role.Name = ? AND Sleep(1) = false", "Admin")
		wg.Done()
	}()
	go func() {
		time.Sleep(500 * time.Millisecond)
		db.Model(&UserModel{}).Joins("Role").Find(&q2Users, "Role.Name = ? AND Sleep(1) = false", "Admin")
		wg.Done()
	}()
	wg.Wait()

	fmt.Println(fmt.Sprintf("%+v", q1Users))
	fmt.Println(fmt.Sprintf("%+v", q2Users))
}

Cacher Example

package main

import (
	"context"
	"fmt"
	"sync"
	"time"

	"github.com/truanguyenvan/gorm-caches"
	"gorm.io/driver/mysql"
	"gorm.io/gorm"
)

type UserRoleModel struct {
	gorm.Model
	Name string `gorm:"unique"`
}

type UserModel struct {
	gorm.Model
	Name   string
	RoleId uint
	Role   *UserRoleModel `gorm:"foreignKey:role_id;references:id"`
}

type dummyCacher struct {
	store *sync.Map
}

func (c *dummyCacher) init() {
	if c.store == nil {
		c.store = &sync.Map{}
	}
}

func (c *dummyCacher) Get(ctx context.Context, key string) ([]byte, error) {
	c.init()
	val, ok := c.store.Load(key)
	if !ok {
		return nil, nil
	}
	fmt.Println("READ CACHE KEY: ", key)
	return val.([]byte), nil
}

func (c *dummyCacher) Set(ctx context.Context, key string, val []byte, ttl time.Duration) error {
	fmt.Println("SET CACHE KEY: ", key)
	c.init()
	c.store.Store(key, val)
	return nil
}

func (c *dummyCacher) Delete(ctx context.Context, key string) error {
	fmt.Println("DELETE CACHE KEY: ", key)
	c.init()
	c.store.Delete(key)
	return nil
}

func (c *dummyCacher) DeleteWithPrefix(ctx context.Context, keyPrefix string) error {
	fmt.Println("DELETE CACHE PREFIX: ", keyPrefix)
	c.init()
	return nil
}

func main() {
	db, _ := gorm.Open(
		mysql.Open("DATABASE_DSN"),
		&gorm.Config{},
	)
	db = db.Debug()
	cachesPlugin := &caches.Caches{Conf: &caches.Config{
		Cacher:     &dummyCacher{},
		CacheTTL:   5 * time.Minute,
		InstanceId: "ACD12",
		Serializer: caches.JSONSerializer{},
	}}

	_ = db.Use(cachesPlugin)

	_ = db.AutoMigrate(&UserRoleModel{})

	_ = db.AutoMigrate(&UserModel{})

	adminRole := &UserRoleModel{
		Name: "Admin",
	}
	db.FirstOrCreate(adminRole, "Name = ?", "Admin")

	guestRole := &UserRoleModel{
		Name: "Guest",
	}
	db.FirstOrCreate(guestRole, "Name = ?", "Guest")

	db.Save(&UserModel{
		Name: "ktsivkov",
		Role: adminRole,
	})
	db.Save(&UserModel{
		Name: "anonymous",
		Role: guestRole,
	})

	var (
		q1Users []UserModel
		q2Users []UserModel

		q1User UserModel
		q2User UserModel
	)

	q1 := db.Model(&UserModel{}).First(&q1User, 2)
	fmt.Println(fmt.Sprintf("q1User: %+v", q1Users))
	fmt.Println(fmt.Sprintf("q1User- RowsAffected: %+v", q1.RowsAffected))

	q2 := db.Model(&UserModel{}).First(&q2User, 2)
	fmt.Println(fmt.Sprintf("q2Users: %+v", q2Users))
	fmt.Println(fmt.Sprintf("q1Users- RowsAffected: %+v", q2.RowsAffected))

	//ctxIgnorePlugin := context.WithValue(context.Background(), cachesPlugin.Name(), false)
	q1s := db.Model(&UserModel{}).Joins("Role").Find(&q1Users)
	fmt.Println(fmt.Sprintf("q1Users: %+v", q1Users))
	fmt.Println(fmt.Sprintf("q1Users- RowsAffected: %+v", q1s.RowsAffected))

	db.Model(&UserModel{}).Where("id", 1).Update("name", "123")

	q2s := db.Model(&UserModel{}).Joins("Role").Find(&q2Users)
	fmt.Println(fmt.Sprintf("q2Users: %+v", q2Users))
	fmt.Println(fmt.Sprintf("q1Users- RowsAffected: %+v", q2s.RowsAffected))
}

License

MIT license.

Easer

The easer is an adjusted version of the ServantGo library to fit the needs of this plugin.

Documentation

Index

Constants

View Source
const (
	CACHE_PATTERN = "INSTANCE_%s:TABLE_%s:%s" //instanceId:tableName:keyCache

	LIST_KEY = "LIST"
)

Variables

This section is empty.

Functions

func ContainString

func ContainString(target string, slice []string) bool

func GenCacheKey

func GenCacheKey(instanceId, tableName, key string) string

func GenCachePrefix

func GenCachePrefix(instanceId string, tableName string) string

func SetPointedValue

func SetPointedValue(dest interface{}, src interface{})

Types

type Cacher

type Cacher interface {
	Get(key string) ([]byte, error)
	Set(key string, val []byte, ttl time.Duration) error
	Delete(key string) error
	DeleteWithPrefix(keyPrefix string) error
}

type Caches

type Caches struct {
	Conf *Config
	// contains filtered or unexported fields
}

func (*Caches) AfterCreate

func (c *Caches) AfterCreate(db *gorm.DB)

func (*Caches) AfterUpdate

func (c *Caches) AfterUpdate(db *gorm.DB)

func (*Caches) Initialize

func (c *Caches) Initialize(db *gorm.DB) error

func (*Caches) Name

func (c *Caches) Name() string

func (*Caches) Query

func (c *Caches) Query(db *gorm.DB)

type Config

type Config struct {
	Easer      bool
	InstanceId string
	Cacher     Cacher
	Serializer Serializer
	CacheTTL   time.Duration

	// Tables only cache data within given data tables (cache all if empty)
	Tables []string
}

type JSONSerializer

type JSONSerializer struct{}

func (JSONSerializer) Deserialize

func (JSONSerializer) Deserialize(data []byte, v any) error

func (JSONSerializer) Serialize

func (JSONSerializer) Serialize(v any) ([]byte, error)

type Query

type Query struct {
	Dest         interface{}
	RowsAffected int64
}

type Serializer

type Serializer interface {
	Serialize(v any) ([]byte, error)

	Deserialize(data []byte, v any) error
}

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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