refactor datasource caching

This commit is contained in:
Marcus Efraimsson 2018-10-26 10:40:33 +02:00
parent 1dc8b898bb
commit cfb061ddab
No known key found for this signature in database
GPG Key ID: EBFE0FB04612DD4A
9 changed files with 109 additions and 55 deletions

View File

@ -1,62 +1,22 @@
package api
import (
"fmt"
"github.com/pkg/errors"
"time"
"github.com/grafana/grafana/pkg/api/pluginproxy"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/metrics"
m "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/plugins"
)
const HeaderNameNoBackendCache = "X-Grafana-NoCache"
func (hs *HTTPServer) getDatasourceFromCache(id int64, c *m.ReqContext) (*m.DataSource, error) {
userPermissionsQuery := m.GetDataSourcePermissionsForUserQuery{
User: c.SignedInUser,
}
if err := bus.Dispatch(&userPermissionsQuery); err != nil {
if err != bus.ErrHandlerNotFound {
return nil, err
}
} else {
permissionType, exists := userPermissionsQuery.Result[id]
if exists && permissionType != m.DsPermissionQuery {
return nil, errors.New("User not allowed to access datasource")
}
}
nocache := c.Req.Header.Get(HeaderNameNoBackendCache) == "true"
cacheKey := fmt.Sprintf("ds-%d", id)
if !nocache {
if cached, found := hs.cache.Get(cacheKey); found {
ds := cached.(*m.DataSource)
if ds.OrgId == c.OrgId {
return ds, nil
}
}
}
query := m.GetDataSourceByIdQuery{Id: id, OrgId: c.OrgId}
if err := bus.Dispatch(&query); err != nil {
return nil, err
}
hs.cache.Set(cacheKey, query.Result, time.Second*5)
return query.Result, nil
}
func (hs *HTTPServer) ProxyDataSourceRequest(c *m.ReqContext) {
c.TimeRequest(metrics.M_DataSource_ProxyReq_Timer)
dsId := c.ParamsInt64(":id")
ds, err := hs.getDatasourceFromCache(dsId, c)
ds, err := hs.DatasourceCache.GetDatasource(dsId, c.SignedInUser, c.SkipCache)
if err != nil {
if err == m.ErrDataSourceAccessDenied {
c.JsonApiErr(403, "Access denied to datasource", nil)
return
}
c.JsonApiErr(500, "Unable to load datasource meta data", err)
return
}

View File

@ -16,7 +16,6 @@ import (
"github.com/prometheus/client_golang/prometheus/promhttp"
gocache "github.com/patrickmn/go-cache"
macaron "gopkg.in/macaron.v1"
"github.com/grafana/grafana/pkg/api/live"
@ -28,6 +27,8 @@ import (
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/plugins"
"github.com/grafana/grafana/pkg/registry"
"github.com/grafana/grafana/pkg/services/cache"
"github.com/grafana/grafana/pkg/services/datasources"
"github.com/grafana/grafana/pkg/services/hooks"
"github.com/grafana/grafana/pkg/services/rendering"
"github.com/grafana/grafana/pkg/setting"
@ -46,19 +47,19 @@ type HTTPServer struct {
macaron *macaron.Macaron
context context.Context
streamManager *live.StreamManager
cache *gocache.Cache
httpSrv *http.Server
RouteRegister routing.RouteRegister `inject:""`
Bus bus.Bus `inject:""`
RenderService rendering.Service `inject:""`
Cfg *setting.Cfg `inject:""`
HooksService *hooks.HooksService `inject:""`
RouteRegister routing.RouteRegister `inject:""`
Bus bus.Bus `inject:""`
RenderService rendering.Service `inject:""`
Cfg *setting.Cfg `inject:""`
HooksService *hooks.HooksService `inject:""`
CacheService *cache.CacheService `inject:""`
DatasourceCache datasources.CacheService `inject:""`
}
func (hs *HTTPServer) Init() error {
hs.log = log.New("http.server")
hs.cache = gocache.New(5*time.Minute, 10*time.Minute)
hs.streamManager = live.NewStreamManager()
hs.macaron = hs.newMacaron()
@ -231,6 +232,7 @@ func (hs *HTTPServer) addMiddlewaresAndStaticRoutes() {
m.Use(middleware.ValidateHostHeader(setting.Domain))
}
m.Use(middleware.HandleNoCacheHeader())
m.Use(middleware.AddDefaultResponseHeaders())
}

View File

@ -25,8 +25,11 @@ func (hs *HTTPServer) QueryMetrics(c *m.ReqContext, reqDto dtos.MetricRequest) R
return Error(400, "Query missing datasourceId", nil)
}
ds, err := hs.getDatasourceFromCache(datasourceId, c)
ds, err := hs.DatasourceCache.GetDatasource(datasourceId, c.SignedInUser, c.SkipCache)
if err != nil {
if err == m.ErrDataSourceAccessDenied {
return Error(403, "Access denied to datasource", nil)
}
return Error(500, "Unable to load datasource meta data", err)
}

View File

@ -15,9 +15,15 @@ import (
"github.com/grafana/grafana/pkg/api"
"github.com/grafana/grafana/pkg/api/routing"
"github.com/grafana/grafana/pkg/bus"
_ "github.com/grafana/grafana/pkg/extensions"
"github.com/grafana/grafana/pkg/log"
"github.com/grafana/grafana/pkg/login"
"github.com/grafana/grafana/pkg/services/cache"
"github.com/grafana/grafana/pkg/setting"
"github.com/grafana/grafana/pkg/social"
// self registering services
_ "github.com/grafana/grafana/pkg/extensions"
_ "github.com/grafana/grafana/pkg/metrics"
"github.com/grafana/grafana/pkg/middleware"
_ "github.com/grafana/grafana/pkg/plugins"
@ -72,6 +78,7 @@ func (g *GrafanaServerImpl) Run() error {
serviceGraph.Provide(&inject.Object{Value: bus.GetBus()})
serviceGraph.Provide(&inject.Object{Value: g.cfg})
serviceGraph.Provide(&inject.Object{Value: routing.NewRouteRegister(middleware.RequestMetrics, middleware.RequestTracing)})
serviceGraph.Provide(&inject.Object{Value: cache.New(5*time.Minute, 10*time.Minute)})
// self registered services
services := registry.GetServices()

14
pkg/middleware/headers.go Normal file
View File

@ -0,0 +1,14 @@
package middleware
import (
m "github.com/grafana/grafana/pkg/models"
macaron "gopkg.in/macaron.v1"
)
const HeaderNameNoBackendCache = "X-Grafana-NoCache"
func HandleNoCacheHeader() macaron.Handler {
return func(ctx *m.ReqContext) {
ctx.SkipCache = ctx.Req.Header.Get(HeaderNameNoBackendCache) == "true"
}
}

View File

@ -29,6 +29,7 @@ func GetContextHandler() macaron.Handler {
Session: session.GetSession(),
IsSignedIn: false,
AllowAnonymous: false,
SkipCache: false,
Logger: log.New("context"),
}

View File

@ -20,6 +20,7 @@ type ReqContext struct {
IsSignedIn bool
IsRenderCall bool
AllowAnonymous bool
SkipCache bool
Logger log.Logger
}

17
pkg/services/cache/cache.go vendored Normal file
View File

@ -0,0 +1,17 @@
package cache
import (
"time"
gocache "github.com/patrickmn/go-cache"
)
type CacheService struct {
*gocache.Cache
}
func New(defaultExpiration, cleanupInterval time.Duration) *CacheService {
return &CacheService{
Cache: gocache.New(defaultExpiration, cleanupInterval),
}
}

View File

@ -0,0 +1,49 @@
package datasources
import (
"fmt"
"time"
"github.com/grafana/grafana/pkg/bus"
m "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/registry"
"github.com/grafana/grafana/pkg/services/cache"
)
type CacheService interface {
GetDatasource(datasourceID int64, user *m.SignedInUser, skipCache bool) (*m.DataSource, error)
}
type CacheServiceImpl struct {
Bus bus.Bus `inject:""`
CacheService *cache.CacheService `inject:""`
}
func init() {
registry.RegisterService(&CacheServiceImpl{})
}
func (dc *CacheServiceImpl) Init() error {
return nil
}
func (dc *CacheServiceImpl) GetDatasource(datasourceID int64, user *m.SignedInUser, skipCache bool) (*m.DataSource, error) {
cacheKey := fmt.Sprintf("ds-%d", datasourceID)
if !skipCache {
if cached, found := dc.CacheService.Get(cacheKey); found {
ds := cached.(*m.DataSource)
if ds.OrgId == user.OrgId {
return ds, nil
}
}
}
query := m.GetDataSourceByIdQuery{Id: datasourceID, OrgId: user.OrgId}
if err := dc.Bus.Dispatch(&query); err != nil {
return nil, err
}
dc.CacheService.Set(cacheKey, query.Result, time.Second*5)
return query.Result, nil
}