Skip to content

Commit

Permalink
server optimizations
Browse files Browse the repository at this point in the history
  • Loading branch information
matthewpeterkort committed Jan 7, 2025
1 parent 6cc1a7a commit aba2d71
Show file tree
Hide file tree
Showing 4 changed files with 95 additions and 83 deletions.
6 changes: 3 additions & 3 deletions .github/workflows/build.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,11 @@ jobs:
# Login to Quay.io and build image
docker login quay.io
docker build -t $REPO:$BRANCH .
docker buildx create --use
docker buildx build --platform linux/amd64,linux/arm64 -t $REPO:$BRANCH . --push
# Add 'latest' tag to 'main' image
if [[ $BRANCH == 'main' ]]; then
docker image tag $REPO:main $REPO:latest
docker buildx build --platform linux/amd64,linux/arm64 -t $REPO:latest . --push
fi
# Push the tagged image to Quay.io
Expand Down
106 changes: 64 additions & 42 deletions gql-gen/graph/collectFields.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,13 @@ import (
"fmt"
"os"

"reflect"
"strconv"
"strings"

"github.com/99designs/gqlgen/graphql"
"github.com/bmeg/grip/gripql"
"github.com/bmeg/grip/gripql/inspect"
"github.com/vektah/gqlparser/v2/ast"
//"google.golang.org/protobuf/types/known/structpb"
)

type Resolver struct {
Expand Down Expand Up @@ -73,11 +71,6 @@ func queryBuild(query **gripql.Query, selSet ast.SelectionSet, curElement string
}
currentTree[curElement] = rt.rFieldPaths[curElement]
} else {

/*fmt.Printf("OBJ DEF: %#v\n", sel.ObjectDefinition)
fmt.Printf("DEF TYPE: %#v\n", sel.Definition.Type)
fmt.Printf("DEF TYPE ELEM: %#v\n", sel.Definition.Type.Elem)
fmt.Println("PARENT PATH: ", newParentPath)*/
if sel.Definition.Type.Elem != nil {
rPath := rt.rFieldPaths[curElement]
rPath.unwindPath = append(rPath.unwindPath, newParentPath)
Expand All @@ -91,7 +84,6 @@ func queryBuild(query **gripql.Query, selSet ast.SelectionSet, curElement string
currentTree[rt.prevName] = map[string]any{"__typename": sel.TypeCondition}
}
fragmentTree := currentTree[rt.prevName].(map[string]interface{})
//[sel.TypeCondition].(map[string]interface{})
*query = (*query).OutNull(rt.prevName + "_" + sel.TypeCondition[:len(sel.TypeCondition)-4]).As(elem)
queryBuild(query, sel.SelectionSet, elem, rt, "", fragmentTree)
rt.moved = true
Expand All @@ -114,23 +106,42 @@ func (r *queryResolver) GetSelectedFieldsAst(ctx context.Context, sourceType str
fmt.Printf("RNAME TREE: %#v\n", rt.rFieldPaths)
fmt.Printf("R TREE: %#v\n", rt.rTree)

render := map[string]any{}
render := make(map[string]any, len(rt.rFieldPaths)*2)
for checkpoint, paths := range rt.rFieldPaths {
render[checkpoint+"_gid"] = "$" + checkpoint + ".id"
checkpointPrefix := "$" + checkpoint + "."
render[checkpoint+"_gid"] = checkpointPrefix + "id"
for _, path := range paths.path {
render[path+"_data"] = "$" + checkpoint + "." + path
render[path+"_data"] = checkpointPrefix + path
}
}

q = q.Select("f0")
applyUnwinds(&q, rt)
q = q.As("f0")
fmt.Printf("ARGS: %#v\n", resctx.Args)
err := applyFilters(&q, resctx.Args)
if err != nil {
return nil, err

if filter, ok := resctx.Args["filter"]; ok {
if filter != nil && len(filter.(map[string]any)) > 0 {
err := rt.applyFilters(&q, filter.(map[string]any))
if err != nil {
return nil, err
}
} else {
return nil, fmt.Errorf("filter is specified but filter not populated in variables")
}
}

if first, ok := resctx.Args["first"]; ok {
firstPtr, _ := first.(*int)
if firstPtr == nil {
q = q.Limit(uint32(10))
} else {
q = q.Limit(uint32(*firstPtr))
}
}
if offset, ok := resctx.Args["offset"]; ok {
if offset.(*int) != nil {
q = q.Skip(uint32(*offset.(*int)))
}
}
err = applyRewinds(&q, rt)
q = q.As("f0")

if os.Getenv("AUTH_ENABLED") == "true" {
authList, ok := ctx.Value("auth_list").([]interface{})
Expand All @@ -156,55 +167,49 @@ func (r *queryResolver) GetSelectedFieldsAst(ctx context.Context, sourceType str
q.Statements = FilteredGS
}
q = q.Render(render)
fmt.Println("QUERY AFTER: ", q)
fmt.Println("QUERY AFTER RENDER: ", q)

result, err := r.GripDb.Traversal(context.Background(), &gripql.GraphQuery{Graph: "CALIPER", Query: q.Statements})
if err != nil {
fmt.Println("HELLO WE HERE: ", err)
return nil, fmt.Errorf("Traversal Error: %s", err)
}

cachedTree := make(map[string]any, len(rt.rTree))
buildTreeStructure(cachedTree, rt.rTree)

out := []any{}
for r := range result {
values := r.GetRender().GetStructValue().AsMap()
//fmt.Printf("VALUES: %#v\n", values)
data := buildOutputTree(rt.rTree, values)
//fmt.Printf("DATA: %#v\n", data)
data := buildFilteredResponseTree(cachedTree, values)
out = append(out, data)
}
return out, nil
}

func buildOutputTree(renderTree map[string]interface{}, values map[string]interface{}) map[string]interface{} {
output := map[string]interface{}{}
func buildTreeStructure(output map[string]any, renderTree map[string]any) {
for key, val := range renderTree {
switch v := val.(type) {
case renderTreePath:
for _, fieldPath := range v.path {
segments := strings.Split(fieldPath, ".")
current := output
for i := 0; i < len(segments)-1; i++ {
//fmt.Println("CURRENT: ", current, "SEGMENTS[i]", segments[i])
//fmt.Println("VALUES: ", values)
if _, exists := current[segments[i]]; !exists {
current[segments[i]] = map[string]interface{}{}
segment := segments[i]
if next, exists := current[segment]; exists {
current = next.(map[string]any)
} else {
newMap := make(map[string]any, len(segments)-i-1)
current[segment] = newMap
current = newMap
}
//current = current[segments[i]].(map[string]interface{})
}
lastSegment := segments[len(segments)-1]
fieldKey := fieldPath + "_data"
if renderedValue, exists := values[fieldKey]; exists {
current[lastSegment] = renderedValue
// if rendered value is string and render was not found return nil instead of string.
if reflect.TypeOf("$f") == reflect.TypeOf(renderedValue) && strings.HasPrefix(renderedValue.(string), "$f") {
current[lastSegment] = nil
}
} else {
current[lastSegment] = nil
}
current[lastSegment] = nil
}
case map[string]interface{}:
output[key] = buildOutputTree(v, values)
case map[string]any:
subTree := make(map[string]any)
buildTreeStructure(subTree, v)
output[key] = subTree
case string:
if key == "__typename" {
output[key] = val
Expand All @@ -215,6 +220,23 @@ func buildOutputTree(renderTree map[string]interface{}, values map[string]interf
fmt.Printf("Unexpected type: %T\n", val)
}
}
}

func buildFilteredResponseTree(cachedTree, values map[string]any) map[string]any {
output := make(map[string]any, len(cachedTree))
for key, val := range cachedTree {
switch v := val.(type) {
case map[string]any:
if filteredSubTree := buildFilteredResponseTree(v, values); len(filteredSubTree) > 0 {
output[key] = filteredSubTree
}
case nil:
if renderedValue, exists := values[key+"_data"]; exists {
output[key] = renderedValue
}
default:
output[key] = val
}
}
return output
}
43 changes: 16 additions & 27 deletions gql-gen/graph/filters.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,10 @@ import (
"google.golang.org/protobuf/types/known/structpb"
)

func applyUnwinds(query **gripql.Query, rt *renderTree) {
func (rt *renderTree) applyUnwinds(query **gripql.Query) {
/* Assumes query is at f0 and only applies unwinds to that node currently*/
for _, val := range rt.rFieldPaths["f0"].unwindPath {
*query = (*query).Unwind(val)
fmt.Println(*query)
}
}

Expand All @@ -21,7 +20,7 @@ Note since this function only has access to one row at a time it cannot merge
the rows and do a full rewind it can only add list objects to make it valid with the existing schema.
TODO: create proper merge function in grip
*/
func applyRewinds(query **gripql.Query, rt *renderTree) error {
func (rt *renderTree) applyRewinds(query **gripql.Query) error {
/*
Applies a JS function to every row in the grip query
Args:
Expand Down Expand Up @@ -64,33 +63,23 @@ function RewindObj(x, args) {
return nil
}

func applyFilters(query **gripql.Query, args map[string]any) error {
func (rt *renderTree) applyFilters(query **gripql.Query, filter map[string]any) error {
//Todo: support "sort" operations
//fmt.Printf("FIRST: %v, TYPE: %T\n", args, args["first"])
if filter, ok := args["filter"]; ok {
if filter != nil && len(filter.(map[string]any)) > 0 {
chainedFilter, err := applyJsonFilter(filter.(map[string]any))
if err != nil {
fmt.Println("ERR != NIL: ", err)
return err
}
//fmt.Printf("CHAINED FILTER: %s\n", chainedFilter.String())
*query = (*query).Has(chainedFilter)
}
}
if first, ok := args["first"]; ok {
firstPtr, _ := first.(*int)
if firstPtr == nil {
*query = (*query).Limit(uint32(10))
} else {
*query = (*query).Limit(uint32(*firstPtr))
}

rt.applyUnwinds(query)
*query = (*query).As("f0")
chainedFilter, err := applyJsonFilter(filter)
if err != nil {
return err
}
if offset, ok := args["offset"]; ok {
if offset.(*int) != nil {
*query = (*query).Skip(uint32(*offset.(*int)))
}

*query = (*query).Has(chainedFilter)
err = rt.applyRewinds(query)
if err != nil {
return err
}
*query = (*query).As("f0")

return nil
}

Expand Down
23 changes: 12 additions & 11 deletions gql-gen/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ func (gh *Handler) graphqlHandler(client gripql.Client, jwtHandler middleware.JW
GripDb: client,
}
srv := handler.New(generated.NewExecutableSchema(generated.Config{Resolvers: resolvers}))
//srv.Use(extension.FixedComplexityLimit(50)) If you want to limit complexity

gh.handler = srv

gh.handler.AddTransport(transport.Options{})
Expand Down Expand Up @@ -94,7 +96,6 @@ func (gh *Handler) graphqlHandler(client gripql.Client, jwtHandler middleware.JW
}

func (gh *Handler) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
//log.Infoln("HELLO INSIDE SERVE HTTP ", request)
gh.router.ServeHTTP(writer, request)
}

Expand Down Expand Up @@ -124,17 +125,17 @@ func NewHTTPHandler(client gripql.Client, config map[string]string) (http.Handle
}
log.ConfigureLogger(logConfig)
r.Use(gin.Logger())
/*r.NoRoute(func(c *gin.Context) {
log.WithFields(log.Fields{
"graph": nil,
"status": "404",
}).Info(c.Request.URL.Path + " Not Found")
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{
"status": "404",
"message": c.Request.URL.Path + " Not Found",
"data": nil,
r.NoRoute(func(c *gin.Context) {
log.WithFields(log.Fields{
"graph": nil,
"status": "404",
}).Info(c.Request.URL.Path + " Not Found")
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{
"status": "404",
"message": c.Request.URL.Path + " Not Found",
"data": nil,
})
})
})*/
r.Use(gin.Recovery())

r.RemoveExtraSlash = true
Expand Down

0 comments on commit aba2d71

Please sign in to comment.