cachelayer

package module
v0.0.7 Latest Latest
Warning

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

Go to latest
Published: Jul 24, 2022 License: MIT Imports: 9 Imported by: 0

README

cachelayer

A cache layer on top of ORM layer

Installation

go get github.com/daqiancode/cachelayer
#for gorm
go get github.com/daqiancode/cachelayer/gormredis
#for mongodb
go get github.com/daqiancode/cachelayer/mongoredis

Idea

2 Types of Cache
  1. Partial cache: load record on demands
  2. Full cache: always load full data from database to cache
Action
  1. Get,List,GetBy,List will use cache(fetch from db if miss)
  2. Create,Delete,Update,Save will clear the cache
2 type Cache content with redis
  1. primary key -> obj, Get,List will use primary redis key, eg. Get: commodity/id/1 -> {id:3,name:"apply",category:1}
  2. index key -> primary keys. eg.ListBy user/category/1 ->[3,4]
Clear cache logic
  1. Get related objects,eg. update(id,v), related objs is old record and new record after updated,[old,new]
  2. Clear cache with id and index rediskey of related objs, clearCache([old,new])

Support

  1. Gorm, including MySQL, PostgreSQL, SQLite, SQL Server
  2. Mongo

Example

import (
	"fmt"
	"log"
	"os"
	"testing"
	"time"

	"github.com/daqiancode/cachelayer"
	"github.com/daqiancode/cachelayer/gormredis"
	"github.com/go-redis/redis/v8"
	"github.com/stretchr/testify/assert"
	"gorm.io/driver/mysql"
	"gorm.io/gorm"
	"gorm.io/gorm/logger"
)

func GetDBClient() *gorm.DB {
	newLogger := logger.New(
		log.New(os.Stdout, "\r\n", log.LstdFlags), // io writer
		logger.Config{
			SlowThreshold:             time.Second, // Slow SQL threshold
			LogLevel:                  logger.Info, // Log level
			IgnoreRecordNotFoundError: true,        // Ignore ErrRecordNotFound error for logger
			Colorful:                  false,       // Disable color
		},
	)
	db, err := gorm.Open(mysql.New(mysql.Config{
		DSN: "root:123456@tcp(localhost:3306)/test?charset=utf8&parseTime=True&loc=Local",
	}), &gorm.Config{Logger: newLogger})
	if err != nil {
		panic(err)
	}
	return db
}

func getRedisClient() *redis.Client {
	return redis.NewClient(&redis.Options{
		Addr: "127.0.0.1:6379",
	})
}

type Commodity struct {
	Id       string
	Name     string
	Category int
}

func (s Commodity) GetID() string {
	return s.Id
}
func (s Commodity) ListIndexes() cachelayer.Indexes {
	return cachelayer.Indexes{}.Add(cachelayer.Index{}.Add("Category", s.Category))
}
func createCache() cachelayer.Cache[Commodity, string] {
	return gormredis.NewGormRedis[Commodity, string]("app", "commodity", "Id", GetDBClient(), getRedisClient(), 10*time.Second)

}
func createCacheFull() *cachelayer.FullRedisCache[Commodity, string] {
	return gormredis.NewGormRedisFull[Commodity, string]("app", "commodity", "Id", GetDBClient(), getRedisClient(), 10*time.Second)

}
func TestGormRedisMain(t *testing.T) {
	id := "1"
	ca := createCache()
	_, err := ca.Delete(id)
	assert.Nil(t, err)
	c, exists, err := ca.Get(id)
	assert.Nil(t, err)
	fmt.Println(exists)
	assert.False(t, exists)
	assert.Equal(t, c.Id, "")
	d := Commodity{Id: id, Name: "tom", Category: 1}
	err = ca.Create(&d)
	assert.Nil(t, err)
	r1, exists, err := ca.GetBy(cachelayer.NewIndex("category", 1))
	assert.Nil(t, err)
	assert.True(t, exists)
	assert.Equal(t, id, r1.Id)
	r2, exists, err := ca.GetBy(cachelayer.NewIndex("category", 100))
	assert.Nil(t, err)
	assert.False(t, exists)
	assert.Equal(t, "", r2.Id)
	r3, err := ca.ListBy(cachelayer.NewIndex("category", 100), nil)
	assert.Nil(t, err)
	assert.Equal(t, 0, len(r3))
	r4, err := ca.List("1", "100")
	assert.Nil(t, err)
	assert.Equal(t, 2, len(r4))
	assert.Equal(t, id, r4[0].Id)
}


Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func IsNullID

func IsNullID[I IDType](id I) bool

func Stringify

func Stringify(value interface{}, null string) string

func StringifyAtom

func StringifyAtom(value interface{}) string

func UniqueStrings

func UniqueStrings(strs []string) []string

Types

type Cache

type Cache[T Table[I], I IDType] interface {
	DBCRUD[T, I]
	//clear cache for objs
	ClearCache(objs ...T) error

	//for extending
	SetCtx(ctx context.Context)
	GetCtx() context.Context
	SetCacheKeyPrefix(prefix string)
	GetCacheKeyPrefix() string
	MakeCacheKey(index Index) string
	SetTableName(table string)
	GetTableName() string
	SetIdField(idField string)
	GetIdField() string
}

Cache 1. Primary key cache: eg. {table}/id/{id} -> record 2.1 Index cache: eg1. {table}/uid/{uid}-> [id1,id2] 2.2 Index cache: eg2. {table}/uid/{uid}/type/{type} -> [id1] 3. Index cache clear cache process, on index change 1. given indexes, 2. find related index cache keys,3. delete

type CacheBase

type CacheBase[T Table[I], I IDType] struct {
	// contains filtered or unexported fields
}

func NewCacheBase

func NewCacheBase[T Table[I], I IDType](prefix, table, idField string, ctx context.Context) *CacheBase[T, I]

func (*CacheBase[T, I]) Close

func (s *CacheBase[T, I]) Close() error

func (*CacheBase[T, I]) GetCacheKeyPrefix

func (s *CacheBase[T, I]) GetCacheKeyPrefix() string

func (*CacheBase[T, I]) GetCtx

func (s *CacheBase[T, I]) GetCtx() context.Context

func (*CacheBase[T, I]) GetIdField

func (s *CacheBase[T, I]) GetIdField() string

func (*CacheBase[T, I]) GetTableName

func (s *CacheBase[T, I]) GetTableName() string

func (*CacheBase[T, I]) MakeCacheKey

func (s *CacheBase[T, I]) MakeCacheKey(index Index) string

func (*CacheBase[T, I]) SetCacheKeyPrefix

func (s *CacheBase[T, I]) SetCacheKeyPrefix(prefix string)
func (s *CacheBase[T, I]) SetSerializer(serializer Serializer) {
	s.serializer = serializer
}
func (s *CacheBase[T, I]) GetSerializer() Serializer {
	return s.serializer
}

func (*CacheBase[T, I]) SetCtx

func (s *CacheBase[T, I]) SetCtx(ctx context.Context)
func (s *CacheBase[T, I]) AddIndexFields(index []string) {
	sort.Strings(index)
	s.indexFields = append(s.indexFields, index)
}
func (s *CacheBase[T, I]) ListIndexFields() [][]string {
	return s.indexFields
}

func (*CacheBase[T, I]) SetIdField

func (s *CacheBase[T, I]) SetIdField(idField string)

func (*CacheBase[T, I]) SetTableName

func (s *CacheBase[T, I]) SetTableName(table string)

type DBCRUD

type DBCRUD[T Table[I], I IDType] interface {
	//Creat create new record into dababase
	Create(obj *T) error
	//Save update if id exists or create new record
	Save(obj *T) error
	//Delete return (effectedrows,error)
	Delete(ids ...I) (int64, error)
	// values can be struct or map[string]interface{}, return (effectedrows,error)
	Update(id I, values interface{}) (int64, error)
	//get obj by id
	Get(id I) (T, bool, error)
	//list objs by ids
	List(ids ...I) ([]T, error)
	//get obj by index
	GetBy(index Index) (T, bool, error)
	//list objs by indexes
	ListBy(index Index, orderBys OrderBys) ([]T, error)
	//close lower clients
	Close() error
}

type FullCache

type FullCache[T Table[I], I IDType] interface {
	ClearCache(objs ...T) error
	//Creat create new record into dababase
	Create(obj *T) error
	//Save update if id exists or create new record
	Save(obj *T) error
	//Delete return (effectedrows,error)
	Delete(ids ...I) (int64, error)
	// values can be struct or map[string]interface{}, return (effectedrows,error)
	Update(id I, values interface{}) (int64, error)

	//get obj by id
	Get(id I) (T, bool, error)
	//list objs by ids
	List(ids ...I) ([]T, error)
	//get obj by index
	GetBy(index Index) (T, bool, error)
	//list objs by indexes
	ListBy(index Index, orderBys OrderBys) ([]T, error)
	//list all objs from db
	ListAll() ([]T, error)

	//close lower clients
	Close() error

	//for extending
	SetCtx(ctx context.Context)
	GetCtx() context.Context
	SetCacheKeyPrefix(prefix string)
	GetCacheKeyPrefix() string
	MakeCacheKey(index Index) string
	SetTableName(table string)
	GetTableName() string
	SetIdField(idField string)
	GetIdField() string
}

type FullDBCache

type FullDBCache[T Table[I], I IDType] interface {
	DBCRUD[T, I]
	// Create(r *T) error
	// Save(r *T) error
	// Update(id I, values interface{}) (int64, error)
	// Delete(ids ...I) (int64, error)
	// Get(id I) (T, bool, error)
	// List(ids ...I) ([]T, error)
	ListAll() ([]T, error)
}

type FullRedisCache

type FullRedisCache[T Table[I], I IDType] struct {
	*CacheBase[T, I]
	// contains filtered or unexported fields
}

func NewFullRedisCache

func NewFullRedisCache[T Table[I], I IDType](prefix, table, idField string, db FullDBCache[T, I], red *redis.Client, ttl time.Duration) *FullRedisCache[T, I]

func (*FullRedisCache[T, I]) CacheKey

func (s *FullRedisCache[T, I]) CacheKey() string

func (*FullRedisCache[T, I]) ClearCache

func (s *FullRedisCache[T, I]) ClearCache(objs ...T) error

func (*FullRedisCache[T, I]) Create

func (s *FullRedisCache[T, I]) Create(r *T) error

func (*FullRedisCache[T, I]) Delete

func (s *FullRedisCache[T, I]) Delete(ids ...I) (int64, error)

func (*FullRedisCache[T, I]) Get

func (s *FullRedisCache[T, I]) Get(id I) (T, bool, error)

func (*FullRedisCache[T, I]) GetBy added in v0.0.5

func (s *FullRedisCache[T, I]) GetBy(index Index) (T, bool, error)

func (*FullRedisCache[T, I]) List

func (s *FullRedisCache[T, I]) List(id ...I) ([]T, error)

func (*FullRedisCache[T, I]) ListAll

func (s *FullRedisCache[T, I]) ListAll() ([]T, error)

func (*FullRedisCache[T, I]) ListBy added in v0.0.5

func (s *FullRedisCache[T, I]) ListBy(index Index, orderBys OrderBys) ([]T, error)

func (*FullRedisCache[T, I]) Load added in v0.0.3

func (s *FullRedisCache[T, I]) Load() error

func (*FullRedisCache[T, I]) Save

func (s *FullRedisCache[T, I]) Save(r *T) error

func (*FullRedisCache[T, I]) Update

func (s *FullRedisCache[T, I]) Update(id I, values interface{}) (int64, error)

type IDInt

type IDInt interface {
	~int | ~int16 | ~int32 | ~int64 | ~uint | ~uint16 | ~uint32 | ~uint64
}

type IDType

type IDType interface {
	IDInt | ~string
}

type Index

type Index map[string]interface{}

func NewIndex

func NewIndex(field string, value interface{}) Index

func (Index) Add

func (s Index) Add(field string, value interface{}) Index

func (Index) Fields

func (s Index) Fields() []string

type Indexes

type Indexes []Index

func (Indexes) Add

func (s Indexes) Add(index Index) Indexes

func (Indexes) Merge

func (s Indexes) Merge(indexes Indexes) Indexes

type JsonSerializer

type JsonSerializer struct {
}

func (*JsonSerializer) Marshal

func (s *JsonSerializer) Marshal(obj interface{}) (string, error)

func (*JsonSerializer) Unmarshal

func (s *JsonSerializer) Unmarshal(data string, objRef interface{}) error

type OrderBy

type OrderBy struct {
	Field string
	Asc   bool
}

func (OrderBy) String

func (s OrderBy) String() string

type OrderBys

type OrderBys []OrderBy

type Indexes []Index

func NewOrderBys

func NewOrderBys(field string, asc bool) OrderBys

func (OrderBys) Add