Caching initial

This commit is contained in:
Daniel Sullivan 2019-10-19 17:34:23 +09:00
parent 7fe81acec6
commit dffbfc9fe9
4 changed files with 153 additions and 19 deletions

80
cache_store.go Normal file
View File

@ -0,0 +1,80 @@
package gorm
import (
"fmt"
"os"
"strconv"
"sync"
"time"
)
type cacheItem struct {
dataMutex sync.RWMutex
data interface{}
created int64
accessMutex sync.RWMutex
accessCount int64
}
type cache struct {
size int
highWaterMark int
enabled bool
database map[string]*cacheItem
}
func (c *cache) Enable() {
// Kick off the maintenance loop
size := os.Getenv("QUERY_CACHE_SIZE")
if size == "" {
size = "8192"
}
highWaterMark := os.Getenv("QUERY_CACHE_HIGH_WATER")
if highWaterMark == "" {
highWaterMark = "6192"
}
c.size, _ = strconv.Atoi(size)
c.highWaterMark, _ = strconv.Atoi(highWaterMark)
c.database = make(map[string]*cacheItem, c.size)
c.enabled = true
}
func (c cache) GetItem(key string, offset int64) interface{} {
fmt.Println("Getting item " + key)
if item, ok := c.database[key]; ok {
item.dataMutex.RLock()
item.accessMutex.Lock()
defer item.dataMutex.RUnlock()
defer item.accessMutex.Unlock()
item.accessCount++
if (item.created+offset < time.Now().Unix()) || offset == -1 {
return item.data
}
}
return nil
}
func (c *cache) StoreItem(key string, data interface{}) {
fmt.Println("Storing item " + key)
if _, ok := c.database[key]; !ok {
c.database[key] = &cacheItem{
data: data,
created: time.Now().Unix(),
}
} else {
c.database[key].dataMutex.Lock()
c.database[key].data = data
c.database[key].created = time.Now().Unix()
c.database[key].dataMutex.Unlock()
}
}

View File

@ -64,6 +64,32 @@ func queryCallback(scope *Scope) {
scope.SQL += addExtraSpaceIfExist(fmt.Sprint(str)) scope.SQL += addExtraSpaceIfExist(fmt.Sprint(str))
} }
// Work out if we can return a result from cache
cacheOperation := scope.Cache()
writeToCache := false
readFromDB := true
key := fmt.Sprint(scope.SQL, scope.SQLVars)
if cacheOperation != nil {
// If the time is > 0, simply provide the cached results
if *cacheOperation > 0 || *cacheOperation == -1 {
cacheResults := scope.CacheStore().GetItem(key, *cacheOperation)
if cacheResults != nil {
results.Set(reflect.ValueOf(cacheResults))
readFromDB = false
} else {
readFromDB = true
writeToCache = true
}
} else {
readFromDB = true
writeToCache = true
}
}
if readFromDB {
if rows, err := scope.SQLDB().Query(scope.SQL, scope.SQLVars...); scope.Err(err) == nil { if rows, err := scope.SQLDB().Query(scope.SQL, scope.SQLVars...); scope.Err(err) == nil {
defer rows.Close() defer rows.Close()
@ -92,6 +118,13 @@ func queryCallback(scope *Scope) {
} else if scope.db.RowsAffected == 0 && !isSlice { } else if scope.db.RowsAffected == 0 && !isSlice {
scope.Err(ErrRecordNotFound) scope.Err(ErrRecordNotFound)
} }
// If we're allowed, write the results to the cache
}
}
if writeToCache {
scope.CacheStore().StoreItem(key, results.Interface())
} }
} }
} }

1
go.mod
View File

@ -5,6 +5,7 @@ go 1.12
require ( require (
github.com/denisenkom/go-mssqldb v0.0.0-20190515213511-eb9f6a1743f3 github.com/denisenkom/go-mssqldb v0.0.0-20190515213511-eb9f6a1743f3
github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5 github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5
github.com/go-redis/redis v6.15.2+incompatible
github.com/go-sql-driver/mysql v1.4.1 github.com/go-sql-driver/mysql v1.4.1
github.com/jinzhu/inflection v1.0.0 github.com/jinzhu/inflection v1.0.0
github.com/jinzhu/now v1.0.1 github.com/jinzhu/now v1.0.1

View File

@ -24,6 +24,7 @@ type Scope struct {
skipLeft bool skipLeft bool
fields *[]*Field fields *[]*Field
selectAttrs *[]string selectAttrs *[]string
cacheStore *cache
} }
// IndirectValue return scope's reflect value's indirect value // IndirectValue return scope's reflect value's indirect value
@ -45,6 +46,11 @@ func (scope *Scope) DB() *DB {
return scope.db return scope.db
} }
// CacheStore returns scope's cache store
func (scope *Scope) CacheStore() *cache {
return scope.cacheStore
}
// NewDB create a new DB without search information // NewDB create a new DB without search information
func (scope *Scope) NewDB() *DB { func (scope *Scope) NewDB() *DB {
if scope.db != nil { if scope.db != nil {
@ -323,10 +329,24 @@ type tabler interface {
TableName() string TableName() string
} }
type cacher interface {
Cache() *int64
}
type dbTabler interface { type dbTabler interface {
TableName(*DB) string TableName(*DB) string
} }
func (scope *Scope) Cache() *int64 {
if scope.cacheStore.enabled {
if cacher, ok := scope.Value.(cacher); ok {
return cacher.Cache()
}
}
return nil
}
// TableName return table name // TableName return table name
func (scope *Scope) TableName() string { func (scope *Scope) TableName() string {
if scope.Search != nil && scope.Search.tableName != nil { if scope.Search != nil && scope.Search.tableName != nil {