浏览代码

refactor datasource caching

Marcus Efraimsson 7 年之前
父节点
当前提交
cfb061ddab

+ 5 - 45
pkg/api/dataproxy.go

@@ -1,62 +1,22 @@
 package api
 package api
 
 
 import (
 import (
-	"fmt"
-	"github.com/pkg/errors"
-	"time"
-
 	"github.com/grafana/grafana/pkg/api/pluginproxy"
 	"github.com/grafana/grafana/pkg/api/pluginproxy"
-	"github.com/grafana/grafana/pkg/bus"
 	"github.com/grafana/grafana/pkg/metrics"
 	"github.com/grafana/grafana/pkg/metrics"
 	m "github.com/grafana/grafana/pkg/models"
 	m "github.com/grafana/grafana/pkg/models"
 	"github.com/grafana/grafana/pkg/plugins"
 	"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) {
 func (hs *HTTPServer) ProxyDataSourceRequest(c *m.ReqContext) {
 	c.TimeRequest(metrics.M_DataSource_ProxyReq_Timer)
 	c.TimeRequest(metrics.M_DataSource_ProxyReq_Timer)
 
 
 	dsId := c.ParamsInt64(":id")
 	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 != nil {
+		if err == m.ErrDataSourceAccessDenied {
+			c.JsonApiErr(403, "Access denied to datasource", nil)
+			return
+		}
 		c.JsonApiErr(500, "Unable to load datasource meta data", err)
 		c.JsonApiErr(500, "Unable to load datasource meta data", err)
 		return
 		return
 	}
 	}

+ 10 - 8
pkg/api/http_server.go

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

+ 4 - 1
pkg/api/metrics.go

@@ -25,8 +25,11 @@ func (hs *HTTPServer) QueryMetrics(c *m.ReqContext, reqDto dtos.MetricRequest) R
 		return Error(400, "Query missing datasourceId", nil)
 		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 != nil {
+		if err == m.ErrDataSourceAccessDenied {
+			return Error(403, "Access denied to datasource", nil)
+		}
 		return Error(500, "Unable to load datasource meta data", err)
 		return Error(500, "Unable to load datasource meta data", err)
 	}
 	}
 
 

+ 8 - 1
pkg/cmd/grafana-server/server.go

@@ -15,9 +15,15 @@ import (
 	"github.com/grafana/grafana/pkg/api"
 	"github.com/grafana/grafana/pkg/api"
 	"github.com/grafana/grafana/pkg/api/routing"
 	"github.com/grafana/grafana/pkg/api/routing"
 	"github.com/grafana/grafana/pkg/bus"
 	"github.com/grafana/grafana/pkg/bus"
-	_ "github.com/grafana/grafana/pkg/extensions"
 	"github.com/grafana/grafana/pkg/log"
 	"github.com/grafana/grafana/pkg/log"
 	"github.com/grafana/grafana/pkg/login"
 	"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/metrics"
 	"github.com/grafana/grafana/pkg/middleware"
 	"github.com/grafana/grafana/pkg/middleware"
 	_ "github.com/grafana/grafana/pkg/plugins"
 	_ "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: bus.GetBus()})
 	serviceGraph.Provide(&inject.Object{Value: g.cfg})
 	serviceGraph.Provide(&inject.Object{Value: g.cfg})
 	serviceGraph.Provide(&inject.Object{Value: routing.NewRouteRegister(middleware.RequestMetrics, middleware.RequestTracing)})
 	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
 	// self registered services
 	services := registry.GetServices()
 	services := registry.GetServices()

+ 14 - 0
pkg/middleware/headers.go

@@ -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"
+	}
+}

+ 1 - 0
pkg/middleware/middleware.go

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

+ 1 - 0
pkg/models/context.go

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

+ 17 - 0
pkg/services/cache/cache.go

@@ -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),
+	}
+}

+ 49 - 0
pkg/services/datasources/cache.go

@@ -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
+}