Implement basic executor caching layer

This commit is contained in:
Tobie Morgan Hitchcock 2018-09-28 22:06:07 +01:00
parent 1a0c8b018f
commit e52f0d3243
3 changed files with 146 additions and 15 deletions

48
db/cache.go Normal file
View file

@ -0,0 +1,48 @@
// Copyright © 2016 Abcum Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package db
import (
"sync"
)
type cache struct {
items sync.Map
}
func (c *cache) Clr() {
c.items.Range(func(key interface{}, _ interface{}) bool {
c.items.Delete(key)
return true
})
}
func (c *cache) Del(key string) {
c.items.Delete(key)
}
func (c *cache) Has(key string) bool {
_, ok := c.items.Load(key)
return ok
}
func (c *cache) Get(key string) interface{} {
val, _ := c.items.Load(key)
return val
}
func (c *cache) Put(key string, val interface{}) {
c.items.Store(key, val)
}

View file

@ -33,6 +33,7 @@ type executor struct {
time int64 time int64
lock *mutex lock *mutex
send chan *Response send chan *Response
cache *cache
} }
func newExecutor() (e *executor) { func newExecutor() (e *executor) {
@ -43,6 +44,8 @@ func newExecutor() (e *executor) {
e.send = make(chan *Response) e.send = make(chan *Response)
e.cache = new(cache)
return return
} }

View file

@ -16,6 +16,7 @@ package db
import ( import (
"context" "context"
"fmt"
"math" "math"
"reflect" "reflect"
"regexp" "regexp"
@ -350,6 +351,12 @@ func (e *executor) fetchThing(ctx context.Context, val *sql.Thing, doc *data.Doc
return nil, err return nil, err
} }
key := fmt.Sprintf("%d %s", ver, val)
if e.cache.Has(key) {
return e.cache.Get(key), nil
}
res, err := e.executeSelect(ctx, &sql.SelectStatement{ res, err := e.executeSelect(ctx, &sql.SelectStatement{
KV: cnf.Settings.DB.Base, KV: cnf.Settings.DB.Base,
NS: ctx.Value(ctxKeyNs).(string), NS: ctx.Value(ctxKeyNs).(string),
@ -365,6 +372,7 @@ func (e *executor) fetchThing(ctx context.Context, val *sql.Thing, doc *data.Doc
} }
if len(res) > 0 { if len(res) > 0 {
e.cache.Put(key, res[0])
return res[0], nil return res[0], nil
} }
@ -379,6 +387,12 @@ func (e *executor) fetchArray(ctx context.Context, val []interface{}, doc *data.
return nil, err return nil, err
} }
key := fmt.Sprintf("%d %s", ver, val)
if e.cache.Has(key) {
return e.cache.Get(key), nil
}
res, err := e.executeSelect(ctx, &sql.SelectStatement{ res, err := e.executeSelect(ctx, &sql.SelectStatement{
KV: cnf.Settings.DB.Base, KV: cnf.Settings.DB.Base,
NS: ctx.Value(ctxKeyNs).(string), NS: ctx.Value(ctxKeyNs).(string),
@ -393,7 +407,12 @@ func (e *executor) fetchArray(ctx context.Context, val []interface{}, doc *data.
return nil, err return nil, err
} }
if len(res) > 0 {
e.cache.Put(key, res)
return res, nil return res, nil
}
return nil, nil
} }
@ -1063,17 +1082,17 @@ func chkArrayL(op sql.Token, a []interface{}, i interface{}) (val bool) {
case sql.MAT: case sql.MAT:
switch s := i.(type) { switch s := i.(type) {
case string: case string:
return chkSearch(op, a, s) return chkSearchL(op, a, s)
} }
case sql.NAT: case sql.NAT:
switch s := i.(type) { switch s := i.(type) {
case string: case string:
return chkSearch(op, a, s) return chkSearchL(op, a, s)
} }
case sql.MAY: case sql.MAY:
switch s := i.(type) { switch s := i.(type) {
case string: case string:
return chkSearch(op, a, s) return chkSearchL(op, a, s)
} }
} }
return negOp(op) return negOp(op)
@ -1112,17 +1131,17 @@ func chkArrayR(op sql.Token, i interface{}, a []interface{}) (val bool) {
case sql.MAT: case sql.MAT:
switch s := i.(type) { switch s := i.(type) {
case string: case string:
return chkSearch(op, a, s) return chkSearchR(op, a, s)
} }
case sql.NAT: case sql.NAT:
switch s := i.(type) { switch s := i.(type) {
case string: case string:
return chkSearch(op, a, s) return chkSearchR(op, a, s)
} }
case sql.MAY: case sql.MAY:
switch s := i.(type) { switch s := i.(type) {
case string: case string:
return chkSearch(op, a, s) return chkSearchR(op, a, s)
} }
} }
return negOp(op) return negOp(op)
@ -1250,7 +1269,7 @@ func chkMatch(op sql.Token, a []interface{}, r *regexp.Regexp) (val bool) {
} }
func chkSearch(op sql.Token, a []interface{}, r string) (val bool) { func chkSearchL(op sql.Token, a []interface{}, p string) (val bool) {
if len(a) == 0 { if len(a) == 0 {
return op == sql.NAT return op == sql.NAT
@ -1276,21 +1295,82 @@ func chkSearch(op sql.Token, a []interface{}, r string) (val bool) {
} }
if op == sql.MAT { if op == sql.MAT {
b, e := search.New(language.Und, search.Loose).IndexString(s, r) b, e := search.New(language.Und, search.Loose).IndexString(s, p)
if b == -1 && e == -1 { if b == -1 && e == -1 {
return false return false
} }
} }
if op == sql.NAT { if op == sql.NAT {
b, e := search.New(language.Und, search.Loose).IndexString(s, r) b, e := search.New(language.Und, search.Loose).IndexString(s, p)
if b == -1 && e == -1 { if b == -1 && e == -1 {
return true return true
} }
} }
if op == sql.MAY { if op == sql.MAY {
b, e := search.New(language.Und, search.Loose).IndexString(s, r) b, e := search.New(language.Und, search.Loose).IndexString(s, p)
if b != -1 && e != -1 {
return true
}
}
}
switch op {
case sql.MAT:
return true
case sql.NAT:
return false
case sql.MAY:
return false
}
return
}
func chkSearchR(op sql.Token, a []interface{}, p string) (val bool) {
if len(a) == 0 {
return op == sql.NAT
}
for _, v := range a {
var s string
switch c := v.(type) {
default:
return false
case string:
s = c
case bool:
s = strconv.FormatBool(c)
case int64:
s = strconv.FormatInt(c, 10)
case float64:
s = strconv.FormatFloat(c, 'g', -1, 64)
case time.Time:
s = c.String()
}
if op == sql.MAT {
b, e := search.New(language.Und, search.Loose).IndexString(p, s)
if b == -1 && e == -1 {
return false
}
}
if op == sql.NAT {
b, e := search.New(language.Und, search.Loose).IndexString(p, s)
if b == -1 && e == -1 {
return true
}
}
if op == sql.MAY {
b, e := search.New(language.Und, search.Loose).IndexString(p, s)
if b != -1 && e != -1 { if b != -1 && e != -1 {
return true return true
} }