Files
releases/server/cache.go
T
Patrick East feaf745da9 server: Cache PreparedEvalQuery's for data requests
This adds in caching of prepared queries for versioned and unversioned
data queries (POST/GET to `/`, `/data`, and `/v1/data`).

The cache has a max size of 100 and just starts acting as a circular
buffer for queries (FIFO, no smarts for LRU caching or anything). It
seems like it would be unlikely for these API's to hit the cache max
size. Most OPA use-cases have a single query that is re-used over
and over with different inputs.

There is a new metric `counter_server_query_cache_hit` which will
show whether or not a request used the query cache or not. It is there
primarily to help explain away why sometimes a handful of the other
metrics aren't there (the query parse/compile/etc).

In the future this could be added to the query API's too. This change
does not touch anything other than the "data" API's.

Closes: #1567
Signed-off-by: Patrick East <east.patrick@gmail.com>
2019-12-18 11:07:14 -08:00

54 lines
993 B
Go

package server
import "sync"
type cache struct {
data map[string]interface{}
keylist []string
idx int
maxSize int
mtx sync.RWMutex
}
func newCache(maxSize int) *cache {
return &cache{
data: map[string]interface{}{},
keylist: []string{},
maxSize: maxSize,
}
}
func (c *cache) Get(k string) (interface{}, bool) {
c.mtx.RLock()
v, ok := c.data[k]
c.mtx.RUnlock()
return v, ok
}
func (c *cache) Insert(k string, v interface{}) {
// Short path if its already in the cache
_, ok := c.Get(k)
if ok {
return
}
// Slow path, grab the write lock and insert
c.mtx.Lock()
_, ok = c.data[k]
if !ok {
c.data[k] = v
if len(c.keylist) < c.maxSize {
// Haven't reached max size yet, keep adding keys.
c.keylist = append(c.keylist, k)
} else {
// Start recycling spots in the key list and
// dropping cache entries for them.
delete(c.data, c.keylist[c.idx])
c.keylist[c.idx] = k
c.idx = (c.idx + 1) % c.maxSize
}
}
c.mtx.Unlock()
}