fix: add a benchmark around the GraphQL api function
This commit is contained in:
parent
ae7cde0433
commit
bdffe7b14e
|
@ -35,4 +35,5 @@ suppressions
|
||||||
release
|
release
|
||||||
.gofuzz
|
.gofuzz
|
||||||
*-fuzz.zip
|
*-fuzz.zip
|
||||||
|
*.test
|
||||||
|
|
||||||
|
|
|
@ -43,7 +43,7 @@ func main() {
|
||||||
log.Fatalf(err)
|
log.Fatalf(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
sg, err = core.NewSuperGraph(conf, db)
|
sg, err := core.NewSuperGraph(conf, db)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf(err)
|
log.Fatalf(err)
|
||||||
}
|
}
|
||||||
|
|
21
core/api.go
21
core/api.go
|
@ -24,7 +24,7 @@
|
||||||
log.Fatalf(err)
|
log.Fatalf(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
sg, err = core.NewSuperGraph(conf, db)
|
sg, err := core.NewSuperGraph(conf, db)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf(err)
|
log.Fatalf(err)
|
||||||
}
|
}
|
||||||
|
@ -82,6 +82,7 @@ type SuperGraph struct {
|
||||||
conf *Config
|
conf *Config
|
||||||
db *sql.DB
|
db *sql.DB
|
||||||
log *_log.Logger
|
log *_log.Logger
|
||||||
|
dbinfo *psql.DBInfo
|
||||||
schema *psql.DBSchema
|
schema *psql.DBSchema
|
||||||
allowList *allow.List
|
allowList *allow.List
|
||||||
encKey [32]byte
|
encKey [32]byte
|
||||||
|
@ -99,10 +100,20 @@ type SuperGraph struct {
|
||||||
// NewSuperGraph creates the SuperGraph struct, this involves querying the database to learn its
|
// NewSuperGraph creates the SuperGraph struct, this involves querying the database to learn its
|
||||||
// schemas and relationships
|
// schemas and relationships
|
||||||
func NewSuperGraph(conf *Config, db *sql.DB) (*SuperGraph, error) {
|
func NewSuperGraph(conf *Config, db *sql.DB) (*SuperGraph, error) {
|
||||||
|
return newSuperGraph(conf, db, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// newSuperGraph helps with writing tests and benchmarks
|
||||||
|
func newSuperGraph(conf *Config, db *sql.DB, dbinfo *psql.DBInfo) (*SuperGraph, error) {
|
||||||
|
if conf == nil {
|
||||||
|
conf = &Config{}
|
||||||
|
}
|
||||||
|
|
||||||
sg := &SuperGraph{
|
sg := &SuperGraph{
|
||||||
conf: conf,
|
conf: conf,
|
||||||
db: db,
|
db: db,
|
||||||
log: _log.New(os.Stdout, "", 0),
|
dbinfo: dbinfo,
|
||||||
|
log: _log.New(os.Stdout, "", 0),
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := sg.initConfig(); err != nil {
|
if err := sg.initConfig(); err != nil {
|
||||||
|
@ -199,6 +210,8 @@ func (sg *SuperGraph) GraphQL(c context.Context, query string, vars json.RawMess
|
||||||
return &ct.res, nil
|
return &ct.res, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GraphQLSchema function return the GraphQL schema for the underlying database connected
|
||||||
|
// to this instance of Super Graph
|
||||||
func (sg *SuperGraph) GraphQLSchema() (string, error) {
|
func (sg *SuperGraph) GraphQLSchema() (string, error) {
|
||||||
return sg.ge.Schema.String(), nil
|
return sg.ge.Schema.String(), nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,62 @@
|
||||||
|
package core
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/DATA-DOG/go-sqlmock"
|
||||||
|
"github.com/dosco/super-graph/core/internal/psql"
|
||||||
|
)
|
||||||
|
|
||||||
|
func BenchmarkGraphQL(b *testing.B) {
|
||||||
|
ct := context.WithValue(context.Background(), UserIDKey, "1")
|
||||||
|
|
||||||
|
db, _, err := sqlmock.New()
|
||||||
|
if err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
defer db.Close()
|
||||||
|
|
||||||
|
// mock.ExpectQuery(`^SELECT jsonb_build_object`).WithArgs()
|
||||||
|
|
||||||
|
sg, err := newSuperGraph(nil, db, psql.GetTestDBInfo())
|
||||||
|
if err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
query := `
|
||||||
|
query {
|
||||||
|
products {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
user {
|
||||||
|
full_name
|
||||||
|
phone
|
||||||
|
email
|
||||||
|
}
|
||||||
|
customers {
|
||||||
|
id
|
||||||
|
email
|
||||||
|
}
|
||||||
|
}
|
||||||
|
users {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
}
|
||||||
|
}`
|
||||||
|
|
||||||
|
b.ResetTimer()
|
||||||
|
b.ReportAllocs()
|
||||||
|
|
||||||
|
b.RunParallel(func(pb *testing.PB) {
|
||||||
|
for pb.Next() {
|
||||||
|
_, err = sg.GraphQL(ct, query, nil)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
fmt.Println(err)
|
||||||
|
|
||||||
|
//fmt.Println(mock.ExpectationsWereMet())
|
||||||
|
|
||||||
|
}
|
22
core/core.go
22
core/core.go
|
@ -50,20 +50,26 @@ type scontext struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sg *SuperGraph) initCompilers() error {
|
func (sg *SuperGraph) initCompilers() error {
|
||||||
di, err := psql.GetDBInfo(sg.db)
|
var err error
|
||||||
if err != nil {
|
|
||||||
|
// If sg.di is not null then it's probably set
|
||||||
|
// for tests
|
||||||
|
if sg.dbinfo == nil {
|
||||||
|
sg.dbinfo, err = psql.GetDBInfo(sg.db)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = addTables(sg.conf, sg.dbinfo); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = addTables(sg.conf, di); err != nil {
|
if err = addForeignKeys(sg.conf, sg.dbinfo); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = addForeignKeys(sg.conf, di); err != nil {
|
sg.schema, err = psql.NewDBSchema(sg.dbinfo, getDBTableAliases(sg.conf))
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
sg.schema, err = psql.NewDBSchema(di, getDBTableAliases(sg.conf))
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,7 @@ import (
|
||||||
var (
|
var (
|
||||||
qcompileTest, _ = qcode.NewCompiler(qcode.Config{})
|
qcompileTest, _ = qcode.NewCompiler(qcode.Config{})
|
||||||
|
|
||||||
schema = getTestSchema()
|
schema = GetTestSchema()
|
||||||
|
|
||||||
vars = NewVariables(map[string]string{
|
vars = NewVariables(map[string]string{
|
||||||
"admin_account_id": "5",
|
"admin_account_id": "5",
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package psql
|
package psql_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package psql
|
package psql_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package psql
|
package psql_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
@ -8,6 +8,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/dosco/super-graph/core/internal/psql"
|
||||||
"github.com/dosco/super-graph/core/internal/qcode"
|
"github.com/dosco/super-graph/core/internal/qcode"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -19,7 +20,7 @@ const (
|
||||||
|
|
||||||
var (
|
var (
|
||||||
qcompile *qcode.Compiler
|
qcompile *qcode.Compiler
|
||||||
pcompile *Compiler
|
pcompile *psql.Compiler
|
||||||
expected map[string][]string
|
expected map[string][]string
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -133,13 +134,16 @@ func TestMain(m *testing.M) {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
schema := getTestSchema()
|
schema, err := psql.GetTestSchema()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
vars := NewVariables(map[string]string{
|
vars := psql.NewVariables(map[string]string{
|
||||||
"admin_account_id": "5",
|
"admin_account_id": "5",
|
||||||
})
|
})
|
||||||
|
|
||||||
pcompile = NewCompiler(Config{
|
pcompile = psql.NewCompiler(psql.Config{
|
||||||
Schema: schema,
|
Schema: schema,
|
||||||
Vars: vars,
|
Vars: vars,
|
||||||
})
|
})
|
||||||
|
@ -173,7 +177,7 @@ func TestMain(m *testing.M) {
|
||||||
os.Exit(m.Run())
|
os.Exit(m.Run())
|
||||||
}
|
}
|
||||||
|
|
||||||
func compileGQLToPSQL(t *testing.T, gql string, vars Variables, role string) {
|
func compileGQLToPSQL(t *testing.T, gql string, vars psql.Variables, role string) {
|
||||||
generateTestFile := false
|
generateTestFile := false
|
||||||
|
|
||||||
if generateTestFile {
|
if generateTestFile {
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package psql
|
package psql_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
|
|
@ -57,9 +57,10 @@ type DBRel struct {
|
||||||
|
|
||||||
func NewDBSchema(info *DBInfo, aliases map[string][]string) (*DBSchema, error) {
|
func NewDBSchema(info *DBInfo, aliases map[string][]string) (*DBSchema, error) {
|
||||||
schema := &DBSchema{
|
schema := &DBSchema{
|
||||||
t: make(map[string]*DBTableInfo),
|
ver: info.Version,
|
||||||
rm: make(map[string]map[string]*DBRel),
|
t: make(map[string]*DBTableInfo),
|
||||||
fm: make(map[string]*DBFunction, len(info.Functions)),
|
rm: make(map[string]map[string]*DBRel),
|
||||||
|
fm: make(map[string]*DBFunction, len(info.Functions)),
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, t := range info.Tables {
|
for i, t := range info.Tables {
|
||||||
|
|
|
@ -14,7 +14,7 @@ type DBInfo struct {
|
||||||
Tables []DBTable
|
Tables []DBTable
|
||||||
Columns [][]DBColumn
|
Columns [][]DBColumn
|
||||||
Functions []DBFunction
|
Functions []DBFunction
|
||||||
colmap map[string]map[string]*DBColumn
|
colMap map[string]map[string]*DBColumn
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetDBInfo(db *sql.DB) (*DBInfo, error) {
|
func GetDBInfo(db *sql.DB) (*DBInfo, error) {
|
||||||
|
@ -36,22 +36,17 @@ func GetDBInfo(db *sql.DB) (*DBInfo, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
di.colmap = make(map[string]map[string]*DBColumn, len(di.Tables))
|
for _, t := range di.Tables {
|
||||||
|
|
||||||
for i, t := range di.Tables {
|
|
||||||
cols, err := GetColumns(db, "public", t.Name)
|
cols, err := GetColumns(db, "public", t.Name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
di.Columns = append(di.Columns, cols)
|
di.Columns = append(di.Columns, cols)
|
||||||
di.colmap[t.Key] = make(map[string]*DBColumn, len(cols))
|
|
||||||
|
|
||||||
for n, c := range di.Columns[i] {
|
|
||||||
di.colmap[t.Key][c.Key] = &di.Columns[i][n]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
di.colMap = newColMap(di.Tables, di.Columns)
|
||||||
|
|
||||||
di.Functions, err = GetFunctions(db)
|
di.Functions, err = GetFunctions(db)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -60,22 +55,37 @@ func GetDBInfo(db *sql.DB) (*DBInfo, error) {
|
||||||
return di, nil
|
return di, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func newColMap(tables []DBTable, columns [][]DBColumn) map[string]map[string]*DBColumn {
|
||||||
|
cm := make(map[string]map[string]*DBColumn, len(tables))
|
||||||
|
|
||||||
|
for i, t := range tables {
|
||||||
|
cols := columns[i]
|
||||||
|
cm[t.Key] = make(map[string]*DBColumn, len(cols))
|
||||||
|
|
||||||
|
for n, c := range cols {
|
||||||
|
cm[t.Key][c.Key] = &columns[i][n]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return cm
|
||||||
|
}
|
||||||
|
|
||||||
func (di *DBInfo) AddTable(t DBTable, cols []DBColumn) {
|
func (di *DBInfo) AddTable(t DBTable, cols []DBColumn) {
|
||||||
t.ID = di.Tables[len(di.Tables)-1].ID
|
t.ID = di.Tables[len(di.Tables)-1].ID
|
||||||
|
|
||||||
di.Tables = append(di.Tables, t)
|
di.Tables = append(di.Tables, t)
|
||||||
di.colmap[t.Key] = make(map[string]*DBColumn, len(cols))
|
di.colMap[t.Key] = make(map[string]*DBColumn, len(cols))
|
||||||
|
|
||||||
for i := range cols {
|
for i := range cols {
|
||||||
cols[i].ID = int16(i)
|
cols[i].ID = int16(i)
|
||||||
c := &cols[i]
|
c := &cols[i]
|
||||||
di.colmap[t.Key][c.Key] = c
|
di.colMap[t.Key][c.Key] = c
|
||||||
}
|
}
|
||||||
di.Columns = append(di.Columns, cols)
|
di.Columns = append(di.Columns, cols)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (di *DBInfo) GetColumn(table, column string) (*DBColumn, bool) {
|
func (di *DBInfo) GetColumn(table, column string) (*DBColumn, bool) {
|
||||||
v, ok := di.colmap[strings.ToLower(table)][strings.ToLower(column)]
|
v, ok := di.colMap[strings.ToLower(table)][strings.ToLower(column)]
|
||||||
return v, ok
|
return v, ok
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,10 @@
|
||||||
package psql
|
package psql
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"log"
|
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
func getTestSchema() *DBSchema {
|
func GetTestDBInfo() *DBInfo {
|
||||||
tables := []DBTable{
|
tables := []DBTable{
|
||||||
DBTable{Name: "customers", Type: "table"},
|
DBTable{Name: "customers", Type: "table"},
|
||||||
DBTable{Name: "users", Type: "table"},
|
DBTable{Name: "users", Type: "table"},
|
||||||
|
@ -74,36 +73,19 @@ func getTestSchema() *DBSchema {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
schema := &DBSchema{
|
return &DBInfo{
|
||||||
ver: 110000,
|
Version: 110000,
|
||||||
t: make(map[string]*DBTableInfo),
|
Tables: tables,
|
||||||
rm: make(map[string]map[string]*DBRel),
|
Columns: columns,
|
||||||
|
Functions: []DBFunction{},
|
||||||
|
colMap: newColMap(tables, columns),
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetTestSchema() (*DBSchema, error) {
|
||||||
aliases := map[string][]string{
|
aliases := map[string][]string{
|
||||||
"users": []string{"mes"},
|
"users": []string{"mes"},
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, t := range tables {
|
return NewDBSchema(GetTestDBInfo(), aliases)
|
||||||
err := schema.addTable(t, columns[i], aliases)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, t := range tables {
|
|
||||||
err := schema.firstDegreeRels(t, columns[i])
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, t := range tables {
|
|
||||||
err := schema.secondDegreeRels(t, columns[i])
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return schema
|
|
||||||
}
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package psql
|
package psql_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
|
1
go.mod
1
go.mod
|
@ -1,6 +1,7 @@
|
||||||
module github.com/dosco/super-graph
|
module github.com/dosco/super-graph
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
github.com/DATA-DOG/go-sqlmock v1.4.1
|
||||||
github.com/GeertJohan/go.rice v1.0.0
|
github.com/GeertJohan/go.rice v1.0.0
|
||||||
github.com/NYTimes/gziphandler v1.1.1
|
github.com/NYTimes/gziphandler v1.1.1
|
||||||
github.com/adjust/gorails v0.0.0-20171013043634-2786ed0c03d3
|
github.com/adjust/gorails v0.0.0-20171013043634-2786ed0c03d3
|
||||||
|
|
2
go.sum
2
go.sum
|
@ -1,6 +1,8 @@
|
||||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||||
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
|
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
|
||||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||||
|
github.com/DATA-DOG/go-sqlmock v1.4.1 h1:ThlnYciV1iM/V0OSF/dtkqWb6xo5qITT1TJBG1MRDJM=
|
||||||
|
github.com/DATA-DOG/go-sqlmock v1.4.1/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM=
|
||||||
github.com/GeertJohan/go.incremental v1.0.0 h1:7AH+pY1XUgQE4Y1HcXYaMqAI0m9yrFqo/jt0CW30vsg=
|
github.com/GeertJohan/go.incremental v1.0.0 h1:7AH+pY1XUgQE4Y1HcXYaMqAI0m9yrFqo/jt0CW30vsg=
|
||||||
github.com/GeertJohan/go.incremental v1.0.0/go.mod h1:6fAjUhbVuX1KcMD3c8TEgVUqmo4seqhv0i0kdATSkM0=
|
github.com/GeertJohan/go.incremental v1.0.0/go.mod h1:6fAjUhbVuX1KcMD3c8TEgVUqmo4seqhv0i0kdATSkM0=
|
||||||
github.com/GeertJohan/go.rice v1.0.0 h1:KkI6O9uMaQU3VEKaj01ulavtF7o1fWT7+pk/4voiMLQ=
|
github.com/GeertJohan/go.rice v1.0.0 h1:KkI6O9uMaQU3VEKaj01ulavtF7o1fWT7+pk/4voiMLQ=
|
||||||
|
|
Loading…
Reference in New Issue