mirror of
https://github.com/lukaszraczylo/go-simple-graphql.git
synced 2026-10-12 00:45:31 +00:00
Revamp the query compilation process.
This commit is contained in:
@@ -2,37 +2,56 @@ package gql
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"sync"
|
||||||
|
|
||||||
"github.com/goccy/go-json"
|
"github.com/goccy/go-json"
|
||||||
"github.com/gookit/goutil"
|
"github.com/gookit/goutil"
|
||||||
"github.com/gookit/goutil/strutil"
|
"github.com/gookit/goutil/strutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var jsonBufferPool = sync.Pool{
|
||||||
|
New: func() interface{} {
|
||||||
|
return new([]byte)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
func (b *BaseClient) convertToJSON(v any) []byte {
|
func (b *BaseClient) convertToJSON(v any) []byte {
|
||||||
|
jsonBuffer := jsonBufferPool.Get().(*[]byte)
|
||||||
|
defer jsonBufferPool.Put(jsonBuffer)
|
||||||
|
|
||||||
|
*jsonBuffer = (*jsonBuffer)[:0]
|
||||||
jsonData, err := json.Marshal(v)
|
jsonData, err := json.Marshal(v)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
b.Logger.Error("Can't convert to JSON", map[string]interface{}{"error": err.Error()})
|
b.Logger.Error("Can't convert to JSON", map[string]interface{}{"error": err.Error()})
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return jsonData
|
*jsonBuffer = append(*jsonBuffer, jsonData...)
|
||||||
|
return *jsonBuffer
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *BaseClient) compileQuery(queryPartials ...any) *Query {
|
func (b *BaseClient) compileQuery(queryPartials ...any) *Query {
|
||||||
q := &Query{}
|
var query string
|
||||||
|
var variables map[string]interface{}
|
||||||
for _, partial := range queryPartials {
|
for _, partial := range queryPartials {
|
||||||
switch val := partial.(type) {
|
switch val := partial.(type) {
|
||||||
case string:
|
case string:
|
||||||
q.Query = val
|
query = val
|
||||||
case map[string]interface{}:
|
case map[string]interface{}:
|
||||||
q.Variables = val
|
variables = val
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if q.Query == "" {
|
|
||||||
|
if query == "" {
|
||||||
b.Logger.Error("Can't compile query", map[string]interface{}{"error": "query is empty"})
|
b.Logger.Error("Can't compile query", map[string]interface{}{"error": "query is empty"})
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
q.JsonQuery = b.convertToJSON(q)
|
|
||||||
return q
|
jsonQuery := b.convertToJSON(&Query{Query: query, Variables: variables})
|
||||||
|
return &Query{
|
||||||
|
Query: query,
|
||||||
|
Variables: variables,
|
||||||
|
JsonQuery: jsonQuery,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *BaseClient) Query(query string, variables map[string]interface{}, headers map[string]interface{}) (any, error) {
|
func (b *BaseClient) Query(query string, variables map[string]interface{}, headers map[string]interface{}) (any, error) {
|
||||||
@@ -52,8 +71,7 @@ func (b *BaseClient) Query(query string, variables map[string]interface{}, heade
|
|||||||
if (enableCache || b.cache_global) && strutil.HasPrefix(compiledQuery.Query, "query") {
|
if (enableCache || b.cache_global) && strutil.HasPrefix(compiledQuery.Query, "query") {
|
||||||
b.Logger.Debug("Cache enabled", nil)
|
b.Logger.Debug("Cache enabled", nil)
|
||||||
queryHash = calculateHash(compiledQuery)
|
queryHash = calculateHash(compiledQuery)
|
||||||
cachedValue := b.cacheLookup(queryHash)
|
if cachedValue := b.cacheLookup(queryHash); cachedValue != nil {
|
||||||
if cachedValue != nil {
|
|
||||||
b.Logger.Debug("Cache hit", map[string]interface{}{"query": compiledQuery})
|
b.Logger.Debug("Cache hit", map[string]interface{}{"query": compiledQuery})
|
||||||
return cachedValue, nil
|
return cachedValue, nil
|
||||||
}
|
}
|
||||||
@@ -76,7 +94,6 @@ func (b *BaseClient) Query(query string, variables map[string]interface{}, heade
|
|||||||
}(),
|
}(),
|
||||||
Retries: enableRetries || b.retries_enable,
|
Retries: enableRetries || b.retries_enable,
|
||||||
}
|
}
|
||||||
defer func() { q = nil }()
|
|
||||||
|
|
||||||
rv, err := q.executeQuery()
|
rv, err := q.executeQuery()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -84,8 +101,7 @@ func (b *BaseClient) Query(query string, variables map[string]interface{}, heade
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
returnedValue, err := q.decodeResponse(rv)
|
return q.decodeResponse(rv)
|
||||||
return returnedValue, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (q *Query) parseHeadersAndVariables(headers map[string]interface{}) (enableCache, enableRetries, recompileRequired bool) {
|
func (q *Query) parseHeadersAndVariables(headers map[string]interface{}) (enableCache, enableRetries, recompileRequired bool) {
|
||||||
@@ -97,10 +113,13 @@ func (q *Query) parseHeadersAndVariables(headers map[string]interface{}) (enable
|
|||||||
varEnableRetries, _ := goutil.ToBool(searchForKeysInMapStringInterface(q.Variables, "gqlretries"))
|
varEnableRetries, _ := goutil.ToBool(searchForKeysInMapStringInterface(q.Variables, "gqlretries"))
|
||||||
enableCache = enableCache || varEnableCache
|
enableCache = enableCache || varEnableCache
|
||||||
enableRetries = enableRetries || varEnableRetries
|
enableRetries = enableRetries || varEnableRetries
|
||||||
|
|
||||||
|
if varEnableCache || varEnableRetries {
|
||||||
delete(q.Variables, "gqlcache")
|
delete(q.Variables, "gqlcache")
|
||||||
delete(q.Variables, "gqlretries")
|
delete(q.Variables, "gqlretries")
|
||||||
recompileRequired = true
|
recompileRequired = true
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return enableCache, enableRetries, recompileRequired
|
return enableCache, enableRetries, recompileRequired
|
||||||
}
|
}
|
||||||
|
|||||||
+62
-3
@@ -1,6 +1,8 @@
|
|||||||
package gql
|
package gql
|
||||||
|
|
||||||
import "testing"
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
func Benchmark_compileQuery(bn *testing.B) {
|
func Benchmark_compileQuery(bn *testing.B) {
|
||||||
b := NewConnection()
|
b := NewConnection()
|
||||||
@@ -12,12 +14,69 @@ func Benchmark_compileQuery(bn *testing.B) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func Benchmark_convertToJson(bn *testing.B) {
|
func Benchmark_compileQueryWithVariables(bn *testing.B) {
|
||||||
b := NewConnection()
|
b := NewConnection()
|
||||||
|
variables := map[string]interface{}{
|
||||||
|
"var1": "value1",
|
||||||
|
}
|
||||||
bn.ResetTimer()
|
bn.ResetTimer()
|
||||||
for i := 0; i < bn.N; i++ {
|
for i := 0; i < bn.N; i++ {
|
||||||
b.convertToJSON(
|
b.compileQuery(
|
||||||
`query { viewer { login } }`,
|
`query { viewer { login } }`,
|
||||||
|
variables,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Benchmark_convertToJson(bn *testing.B) {
|
||||||
|
b := NewConnection()
|
||||||
|
query := `query { viewer { login } }`
|
||||||
|
bn.ResetTimer()
|
||||||
|
for i := 0; i < bn.N; i++ {
|
||||||
|
b.convertToJSON(query)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Benchmark_Query(bn *testing.B) {
|
||||||
|
b := NewConnection()
|
||||||
|
query := `query { viewer { login } }`
|
||||||
|
variables := map[string]interface{}{
|
||||||
|
"var1": "value1",
|
||||||
|
}
|
||||||
|
headers := map[string]interface{}{
|
||||||
|
"Authorization": "Bearer token",
|
||||||
|
"gqlcache": "true",
|
||||||
|
}
|
||||||
|
bn.ResetTimer()
|
||||||
|
for i := 0; i < bn.N; i++ {
|
||||||
|
_, _ = b.Query(query, variables, headers)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Benchmark_QueryNoCacheNoRetry(bn *testing.B) {
|
||||||
|
b := NewConnection()
|
||||||
|
query := `query { viewer { login } }`
|
||||||
|
variables := map[string]interface{}{
|
||||||
|
"var1": "value1",
|
||||||
|
}
|
||||||
|
headers := map[string]interface{}{
|
||||||
|
"Authorization": "Bearer token",
|
||||||
|
}
|
||||||
|
bn.ResetTimer()
|
||||||
|
for i := 0; i < bn.N; i++ {
|
||||||
|
_, _ = b.Query(query, variables, headers)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Benchmark_QueryWithEmptyVariables(bn *testing.B) {
|
||||||
|
b := NewConnection()
|
||||||
|
query := `query { viewer { login } }`
|
||||||
|
headers := map[string]interface{}{
|
||||||
|
"Authorization": "Bearer token",
|
||||||
|
"gqlcache": "true",
|
||||||
|
}
|
||||||
|
bn.ResetTimer()
|
||||||
|
for i := 0; i < bn.N; i++ {
|
||||||
|
_, _ = b.Query(query, nil, headers)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user