Skip to content
This repository has been archived by the owner on Aug 16, 2022. It is now read-only.

Commit

Permalink
perf: reduce database queries to obtain scene IDs (#119)
Browse files Browse the repository at this point in the history
  • Loading branch information
rot1024 committed Mar 7, 2022
1 parent d1a38e2 commit 7843321
Show file tree
Hide file tree
Showing 43 changed files with 1,013 additions and 720 deletions.
2 changes: 1 addition & 1 deletion internal/adapter/gql/resolver_mutation_dataset.go
Expand Up @@ -72,7 +72,7 @@ func (r *mutationResolver) SyncDataset(ctx context.Context, input gqlmodel.SyncD

func (r *mutationResolver) RemoveDatasetSchema(ctx context.Context, input gqlmodel.RemoveDatasetSchemaInput) (*gqlmodel.RemoveDatasetSchemaPayload, error) {
res, err := usecases(ctx).Dataset.RemoveDatasetSchema(ctx, interfaces.RemoveDatasetSchemaParam{
SchemaId: id.DatasetSchemaID(input.SchemaID),
SchemaID: id.DatasetSchemaID(input.SchemaID),
Force: input.Force,
}, getOperator(ctx))
if err != nil {
Expand Down
72 changes: 47 additions & 25 deletions internal/app/app.go
Expand Up @@ -2,8 +2,10 @@ package app

import (
"errors"
"io/fs"
"net/http"
"net/http/pprof"
"os"

"github.com/99designs/gqlgen/graphql/playground"
"github.com/labstack/echo/v4"
Expand All @@ -23,12 +25,12 @@ func initEcho(cfg *ServerConfig) *echo.Echo {
e.Debug = cfg.Debug
e.HideBanner = true
e.HidePort = true
e.HTTPErrorHandler = errorHandler(e.DefaultHTTPErrorHandler)

// basic middleware
logger := GetEchoLogger()
e.Logger = logger
e.Use(logger.Hook())

e.Use(middleware.Recover(), otelecho.Middleware("reearth-backend"))
e.Use(logger.Hook(), middleware.Recover(), otelecho.Middleware("reearth-backend"))
origins := allowedOrigins(cfg)
if len(origins) > 0 {
e.Use(
Expand All @@ -38,8 +40,8 @@ func initEcho(cfg *ServerConfig) *echo.Echo {
)
}

// enable pprof
if e.Debug {
// enable pprof
pprofGroup := e.Group("/debug/pprof")
pprofGroup.Any("/cmdline", echo.WrapHandler(http.HandlerFunc(pprof.Cmdline)))
pprofGroup.Any("/profile", echo.WrapHandler(http.HandlerFunc(pprof.Profile)))
Expand All @@ -48,49 +50,69 @@ func initEcho(cfg *ServerConfig) *echo.Echo {
pprofGroup.Any("/*", echo.WrapHandler(http.HandlerFunc(pprof.Index)))
}

e.HTTPErrorHandler = func(err error, c echo.Context) {
if c.Response().Committed {
return
}

code, msg := errorMessage(err, func(f string, args ...interface{}) {
c.Echo().Logger.Errorf(f, args...)
})
if err := c.JSON(code, map[string]string{
"error": msg,
}); err != nil {
e.DefaultHTTPErrorHandler(err, c)
}
}

// GraphQL Playground without auth
if cfg.Debug || cfg.Config.Dev {
// GraphQL Playground without auth
e.GET("/graphql", echo.WrapHandler(
playground.Handler("reearth-backend", "/api/graphql"),
))
}

// init usecases
var publishedIndexHTML string
if cfg.Config.Published.IndexURL == nil || cfg.Config.Published.IndexURL.String() == "" {
if html, err := fs.ReadFile(os.DirFS("."), "web/published.html"); err == nil {
publishedIndexHTML = string(html)
}
}
usecases := interactor.NewContainer(cfg.Repos, cfg.Gateways, interactor.ContainerConfig{
SignupSecret: cfg.Config.SignupSecret,
SignupSecret: cfg.Config.SignupSecret,
PublishedIndexHTML: publishedIndexHTML,
PublishedIndexURL: cfg.Config.Published.IndexURL,
})

e.Use(UsecaseMiddleware(&usecases))

// apis
api := e.Group("/api")
publicAPI(e, api, cfg.Config, cfg.Repos, cfg.Gateways)
jwks := &JwksSyncOnce{}
api.GET("/ping", Ping())
api.POST("/signup", Signup())
api.GET("/published/:name", PublishedMetadata())
api.GET("/published_data/:name", PublishedData())

privateApi := api.Group("")
jwks := &JwksSyncOnce{}
authRequired(privateApi, jwks, cfg)
graphqlAPI(e, privateApi, cfg, usecases)
graphqlAPI(e, privateApi, cfg)
privateAPI(e, privateApi, cfg.Repos)

published := e.Group("/p")
publishedRoute(e, published, cfg.Config, cfg.Repos, cfg.Gateways)
auth := PublishedAuthMiddleware()
published.GET("/:name/data.json", PublishedData(), auth)
published.GET("/:name/", PublishedIndex(), auth)

serveFiles(e, cfg.Gateways.File)
web(e, cfg.Config.Web, cfg.Config.Auth0)

return e
}

func errorHandler(next func(error, echo.Context)) func(error, echo.Context) {
return func(err error, c echo.Context) {
if c.Response().Committed {
return
}

code, msg := errorMessage(err, func(f string, args ...interface{}) {
c.Echo().Logger.Errorf(f, args...)
})
if err := c.JSON(code, map[string]string{
"error": msg,
}); err != nil {
next(err, c)
}
}
}

func authRequired(g *echo.Group, jwks Jwks, cfg *ServerConfig) {
g.Use(jwtEchoMiddleware(jwks, cfg))
g.Use(parseJwtMiddleware(cfg))
Expand Down
23 changes: 21 additions & 2 deletions internal/app/auth.go
Expand Up @@ -86,11 +86,30 @@ func generateOperator(ctx context.Context, cfg *ServerConfig, u *user.User) (*us
if u == nil {
return nil, nil
}
teams, err := cfg.Repos.Team.FindByUser(ctx, u.ID())

uid := u.ID()
teams, err := cfg.Repos.Team.FindByUser(ctx, uid)
if err != nil {
return nil, err
}
scenes, err := cfg.Repos.Scene.FindByTeam(ctx, teams.IDs()...)
if err != nil {
return nil, err
}
return usecase.OperatorFrom(u.ID(), teams), nil

readableTeams := teams.FilterByUserRole(uid, user.RoleReader).IDs()
writableTeams := teams.FilterByUserRole(uid, user.RoleWriter).IDs()
owningTeams := teams.FilterByUserRole(uid, user.RoleOwner).IDs()

return &usecase.Operator{
User: uid,
ReadableTeams: readableTeams,
WritableTeams: writableTeams,
OwningTeams: owningTeams,
ReadableScenes: scenes.FilterByTeam(readableTeams...).IDs(),
WritableScenes: scenes.FilterByTeam(writableTeams...).IDs(),
OwningScenes: scenes.FilterByTeam(owningTeams...).IDs(),
}, nil
}

func addAuth0SubToUser(ctx context.Context, u *user.User, a user.Auth, cfg *ServerConfig) error {
Expand Down
6 changes: 2 additions & 4 deletions internal/app/graphql.go
Expand Up @@ -12,7 +12,6 @@ import (
"github.com/ravilushqa/otelgqlgen"
"github.com/reearth/reearth-backend/internal/adapter"
"github.com/reearth/reearth-backend/internal/adapter/gql"
"github.com/reearth/reearth-backend/internal/usecase/interfaces"
"github.com/vektah/gqlparser/v2/gqlerror"
)

Expand All @@ -22,7 +21,6 @@ func graphqlAPI(
ec *echo.Echo,
r *echo.Group,
conf *ServerConfig,
usecases interfaces.Container,
) {
playgroundEnabled := conf.Debug || conf.Config.Dev

Expand Down Expand Up @@ -65,8 +63,8 @@ func graphqlAPI(
req := c.Request()
ctx := req.Context()

ctx = adapter.AttachUsecases(ctx, &usecases)
ctx = gql.AttachUsecases(ctx, &usecases, enableDataLoaders)
usecases := adapter.Usecases(ctx)
ctx = gql.AttachUsecases(ctx, usecases, enableDataLoaders)
c.SetRequest(req.WithContext(ctx))

srv.ServeHTTP(c.Response(), c.Request())
Expand Down
32 changes: 3 additions & 29 deletions internal/app/private.go
@@ -1,15 +1,13 @@
package app

import (
"context"
"errors"
"io"
"net/http"
"strings"

"github.com/labstack/echo/v4"
"github.com/reearth/reearth-backend/internal/adapter"
"github.com/reearth/reearth-backend/internal/usecase"
"github.com/reearth/reearth-backend/internal/usecase/repo"
"github.com/reearth/reearth-backend/pkg/id"
"github.com/reearth/reearth-backend/pkg/layer/encoding"
Expand All @@ -27,17 +25,6 @@ var (
ErrBadParameter = errors.New("id.ext is needed")
)

func checkScene(ctx context.Context, id id.SceneID, op *usecase.Operator, sr repo.Scene) error {
res, err := sr.HasSceneTeam(ctx, id, op.ReadableTeams)
if err != nil {
return err
}
if !res {
return ErrOpDenied
}
return nil
}

func getEncoder(w io.Writer, ext string) (encoding.Encoder, string) {
switch strings.ToLower(ext) {
case "kml":
Expand Down Expand Up @@ -68,6 +55,7 @@ func privateAPI(
if op == nil {
return &echo.HTTPError{Code: http.StatusUnauthorized, Message: ErrOpDenied}
}
scenes := op.AllReadableScenes()

param := c.Param("param")
params := strings.Split(param, ".")
Expand All @@ -80,29 +68,15 @@ func privateAPI(
return &echo.HTTPError{Code: http.StatusBadRequest, Message: ErrBadID}
}

scenes, err := repos.Scene.FindIDsByTeam(ctx, op.ReadableTeams)
if err != nil {
if errors.Is(rerror.ErrNotFound, err) {
return &echo.HTTPError{Code: http.StatusNotFound, Message: err}
}
return &echo.HTTPError{Code: http.StatusInternalServerError, Message: err}
}

layer, err := repos.Layer.FindByID(ctx, lid, scenes)
if err != nil {
if errors.Is(rerror.ErrNotFound, err) {
return &echo.HTTPError{Code: http.StatusNotFound, Message: err}
}
return &echo.HTTPError{Code: http.StatusInternalServerError, Message: err}
}

err = checkScene(ctx, layer.Scene(), op, repos.Scene)
if err != nil {
if errors.Is(ErrOpDenied, err) {
return &echo.HTTPError{Code: http.StatusUnauthorized, Message: ErrOpDenied}
}

return &echo.HTTPError{Code: http.StatusInternalServerError, Message: err}
if !op.IsReadableScene(layer.Scene()) {
return &echo.HTTPError{Code: http.StatusUnauthorized, Message: ErrOpDenied}
}
ext := params[1]

Expand Down

0 comments on commit 7843321

Please sign in to comment.