mirror of
https://github.com/grafana/grafana.git
synced 2024-12-01 04:59:15 -06:00
78596a6756
Fixes #30144 Co-authored-by: dsotirakis <sotirakis.dim@gmail.com> Co-authored-by: Marcus Efraimsson <marcus.efraimsson@gmail.com> Co-authored-by: Ida Furjesova <ida.furjesova@grafana.com> Co-authored-by: Jack Westbrook <jack.westbrook@gmail.com> Co-authored-by: Will Browne <wbrowne@users.noreply.github.com> Co-authored-by: Leon Sorokin <leeoniya@gmail.com> Co-authored-by: Andrej Ocenas <mr.ocenas@gmail.com> Co-authored-by: spinillos <selenepinillos@gmail.com> Co-authored-by: Karl Persson <kalle.persson@grafana.com> Co-authored-by: Leonard Gram <leo@xlson.com>
138 lines
3.6 KiB
Go
138 lines
3.6 KiB
Go
package remotecache
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"encoding/gob"
|
|
"errors"
|
|
"time"
|
|
|
|
"github.com/grafana/grafana/pkg/infra/log"
|
|
"github.com/grafana/grafana/pkg/registry"
|
|
"github.com/grafana/grafana/pkg/services/sqlstore"
|
|
"github.com/grafana/grafana/pkg/setting"
|
|
)
|
|
|
|
var (
|
|
// ErrCacheItemNotFound is returned if cache does not exist
|
|
ErrCacheItemNotFound = errors.New("cache item not found")
|
|
|
|
// ErrInvalidCacheType is returned if the type is invalid
|
|
ErrInvalidCacheType = errors.New("invalid remote cache name")
|
|
|
|
defaultMaxCacheExpiration = time.Hour * 24
|
|
)
|
|
|
|
const (
|
|
ServiceName = "RemoteCache"
|
|
)
|
|
|
|
func ProvideService(cfg *setting.Cfg, sqlStore *sqlstore.SQLStore) (*RemoteCache, error) {
|
|
client, err := createClient(cfg.RemoteCacheOptions, sqlStore)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
s := &RemoteCache{
|
|
SQLStore: sqlStore,
|
|
Cfg: cfg,
|
|
log: log.New("cache.remote"),
|
|
client: client,
|
|
}
|
|
return s, nil
|
|
}
|
|
|
|
// CacheStorage allows the caller to set, get and delete items in the cache.
|
|
// Cached items are stored as byte arrays and marshalled using "encoding/gob"
|
|
// so any struct added to the cache needs to be registered with `remotecache.Register`
|
|
// ex `remotecache.Register(CacheableStruct{})``
|
|
type CacheStorage interface {
|
|
// Get reads object from Cache
|
|
Get(key string) (interface{}, error)
|
|
|
|
// Set sets an object into the cache. if `expire` is set to zero it will default to 24h
|
|
Set(key string, value interface{}, expire time.Duration) error
|
|
|
|
// Delete object from cache
|
|
Delete(key string) error
|
|
}
|
|
|
|
// RemoteCache allows Grafana to cache data outside its own process
|
|
type RemoteCache struct {
|
|
log log.Logger
|
|
client CacheStorage
|
|
SQLStore *sqlstore.SQLStore
|
|
Cfg *setting.Cfg
|
|
}
|
|
|
|
// Get reads object from Cache
|
|
func (ds *RemoteCache) Get(key string) (interface{}, error) {
|
|
return ds.client.Get(key)
|
|
}
|
|
|
|
// Set sets an object into the cache. if `expire` is set to zero it will default to 24h
|
|
func (ds *RemoteCache) Set(key string, value interface{}, expire time.Duration) error {
|
|
if expire == 0 {
|
|
expire = defaultMaxCacheExpiration
|
|
}
|
|
|
|
return ds.client.Set(key, value, expire)
|
|
}
|
|
|
|
// Delete object from cache
|
|
func (ds *RemoteCache) Delete(key string) error {
|
|
return ds.client.Delete(key)
|
|
}
|
|
|
|
// Run starts the backend processes for cache clients.
|
|
func (ds *RemoteCache) Run(ctx context.Context) error {
|
|
// create new interface if more clients need GC jobs
|
|
backgroundjob, ok := ds.client.(registry.BackgroundService)
|
|
if ok {
|
|
return backgroundjob.Run(ctx)
|
|
}
|
|
|
|
<-ctx.Done()
|
|
return ctx.Err()
|
|
}
|
|
|
|
func createClient(opts *setting.RemoteCacheOptions, sqlstore *sqlstore.SQLStore) (CacheStorage, error) {
|
|
if opts.Name == redisCacheType {
|
|
return newRedisStorage(opts)
|
|
}
|
|
|
|
if opts.Name == memcachedCacheType {
|
|
return newMemcachedStorage(opts), nil
|
|
}
|
|
|
|
if opts.Name == databaseCacheType {
|
|
return newDatabaseCache(sqlstore), nil
|
|
}
|
|
|
|
return nil, ErrInvalidCacheType
|
|
}
|
|
|
|
// Register records a type, identified by a value for that type, under its
|
|
// internal type name. That name will identify the concrete type of a value
|
|
// sent or received as an interface variable. Only types that will be
|
|
// transferred as implementations of interface values need to be registered.
|
|
// Expecting to be used only during initialization, it panics if the mapping
|
|
// between types and names is not a bijection.
|
|
func Register(value interface{}) {
|
|
gob.Register(value)
|
|
}
|
|
|
|
type cachedItem struct {
|
|
Val interface{}
|
|
}
|
|
|
|
func encodeGob(item *cachedItem) ([]byte, error) {
|
|
buf := bytes.NewBuffer(nil)
|
|
err := gob.NewEncoder(buf).Encode(item)
|
|
return buf.Bytes(), err
|
|
}
|
|
|
|
func decodeGob(data []byte, out *cachedItem) error {
|
|
buf := bytes.NewBuffer(data)
|
|
return gob.NewDecoder(buf).Decode(&out)
|
|
}
|