functions: performance improvements - LRU & singleflight DB calls (#322)

* functions: add cache and singleflight to ease database load

* runner: upgrade

* deps: upgrade glide files

* license: add third party notifications

* functions: fix handling of implicitly created apps

* functions: code deduplication

* functions: fix missing variable
This commit is contained in:
C Cirello
2016-11-21 19:48:11 +01:00
committed by GitHub
parent a4d360fb2e
commit f6d19c3cc9
14 changed files with 289 additions and 69 deletions

View File

@@ -76,13 +76,11 @@ func (s *Server) handleRequest(c *gin.Context, enqueue models.Enqueue) {
c.JSON(http.StatusBadRequest, simpleError(models.ErrAppsNotFound))
return
}
route := c.Param("route")
if route == "" {
route = c.Request.URL.Path
path := c.Param("route")
if path == "" {
path = c.Request.URL.Path
}
log.WithFields(logrus.Fields{"app": appName, "path": route}).Debug("Finding route on datastore")
app, err := Api.Datastore.GetApp(appName)
if err != nil || app == nil {
log.WithError(err).Error(models.ErrAppsNotFound)
@@ -90,30 +88,56 @@ func (s *Server) handleRequest(c *gin.Context, enqueue models.Enqueue) {
return
}
routes, err := Api.Datastore.GetRoutesByApp(appName, &models.RouteFilter{AppName: appName, Path: route})
log.WithFields(logrus.Fields{"app": appName, "path": path}).Debug("Finding route on LRU cache")
route, ok := s.cacheget(appName, path)
if ok && s.serve(c, log, appName, route, app, path, reqID, payload, enqueue) {
s.refreshcache(appName, route)
return
}
log.WithFields(logrus.Fields{"app": appName, "path": path}).Debug("Finding route on datastore")
routes, err := s.loadroutes(models.RouteFilter{AppName: appName, Path: path})
if err != nil {
log.WithError(err).Error(models.ErrRoutesList)
c.JSON(http.StatusInternalServerError, simpleError(models.ErrRoutesList))
return
}
log.WithField("routes", routes).Debug("Got routes from datastore")
if len(routes) == 0 {
log.WithError(err).Error(models.ErrRunnerRouteNotFound)
c.JSON(http.StatusNotFound, simpleError(models.ErrRunnerRouteNotFound))
return
}
found := routes[0]
log = log.WithFields(logrus.Fields{
"app": appName, "route": found.Path, "image": found.Image})
log.WithField("routes", len(routes)).Debug("Got routes from datastore")
route = routes[0]
log = log.WithFields(logrus.Fields{"app": appName, "path": route.Path, "image": route.Image})
if s.serve(c, log, appName, route, app, path, reqID, payload, enqueue) {
s.refreshcache(appName, route)
return
}
log.Error(models.ErrRunnerRouteNotFound)
c.JSON(http.StatusNotFound, simpleError(models.ErrRunnerRouteNotFound))
}
func (s *Server) loadroutes(filter models.RouteFilter) ([]*models.Route, error) {
resp, err := s.singleflight.do(
filter,
func() (interface{}, error) {
return Api.Datastore.GetRoutesByApp(filter.AppName, &filter)
},
)
return resp.([]*models.Route), err
}
func (s *Server) serve(c *gin.Context, log logrus.FieldLogger, appName string, found *models.Route, app *models.App, route, reqID string, payload io.Reader, enqueue models.Enqueue) (ok bool) {
log = log.WithFields(logrus.Fields{"app": appName, "route": found.Path, "image": found.Image})
params, match := matchRoute(found.Path, route)
if !match {
log.WithError(err).Error(models.ErrRunnerRouteNotFound)
c.JSON(http.StatusNotFound, simpleError(models.ErrRunnerRouteNotFound))
return
return false
}
var stdout bytes.Buffer // TODO: should limit the size of this, error if gets too big. akin to: https://golang.org/pkg/io/#LimitReader
@@ -162,7 +186,7 @@ func (s *Server) handleRequest(c *gin.Context, enqueue models.Enqueue) {
if err != nil {
log.WithError(err).Error(models.ErrInvalidPayload)
c.JSON(http.StatusBadRequest, simpleError(models.ErrInvalidPayload))
return
return true
}
// Create Task
@@ -176,13 +200,12 @@ func (s *Server) handleRequest(c *gin.Context, enqueue models.Enqueue) {
task.EnvVars = cfg.Env
task.Payload = string(pl)
// Push to queue
enqueue(ctx, s.MQ, task)
enqueue(c, s.MQ, task)
log.Info("Added new task to queue")
c.JSON(http.StatusAccepted, map[string]string{"call_id": task.ID})
default:
result, err := runner.RunTask(s.tasks, ctx, cfg)
result, err := runner.RunTask(s.tasks, c, cfg)
if err != nil {
break
}
@@ -197,6 +220,8 @@ func (s *Server) handleRequest(c *gin.Context, enqueue models.Enqueue) {
}
}
return true
}
var fakeHandler = func(http.ResponseWriter, *http.Request, Params) {}