Caching initial
This commit is contained in:
parent
7fe81acec6
commit
dffbfc9fe9
80
cache_store.go
Normal file
80
cache_store.go
Normal 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()
|
||||||
|
}
|
||||||
|
}
|
@ -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
1
go.mod
@ -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
|
||||||
|
20
scope.go
20
scope.go
@ -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 {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user