mirror of
https://github.com/lukaszraczylo/go-simple-graphql.git
synced 2026-10-12 00:45:31 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
42170f9b8e | ||
|
|
4f438b7eac | ||
|
|
569867d04b | ||
|
|
9a58df7a54 | ||
|
|
4e60a90ce4 |
Vendored
+52
-47
@@ -3,7 +3,6 @@ package libpack_cache
|
||||
import (
|
||||
"bytes"
|
||||
"compress/gzip"
|
||||
"flag"
|
||||
"io"
|
||||
"sync"
|
||||
"time"
|
||||
@@ -15,26 +14,32 @@ type CacheEntry struct {
|
||||
}
|
||||
|
||||
type Cache struct {
|
||||
bytePool sync.Pool
|
||||
entries sync.Map
|
||||
globalTTL time.Duration
|
||||
compressPool sync.Pool
|
||||
decompressPool sync.Pool
|
||||
entries sync.Map
|
||||
globalTTL time.Duration
|
||||
sync.RWMutex
|
||||
}
|
||||
|
||||
func New(globalTTL time.Duration) *Cache {
|
||||
cache := &Cache{
|
||||
globalTTL: globalTTL,
|
||||
compressPool: sync.Pool{
|
||||
New: func() interface{} {
|
||||
w := gzip.NewWriter(nil)
|
||||
return w
|
||||
},
|
||||
},
|
||||
decompressPool: sync.Pool{
|
||||
New: func() interface{} {
|
||||
// Ensure that new is returning a new reader initialized with an empty byte buffer
|
||||
r, _ := gzip.NewReader(bytes.NewReader([]byte{}))
|
||||
return r
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// Initialize the byte pool.
|
||||
cache.bytePool.New = func() interface{} {
|
||||
return make([]byte, 0)
|
||||
}
|
||||
|
||||
// Start the cache cleanup.
|
||||
if flag.Lookup("test.v") == nil {
|
||||
go cache.cleanupRoutine(globalTTL)
|
||||
}
|
||||
go cache.cleanupRoutine(globalTTL)
|
||||
return cache
|
||||
}
|
||||
|
||||
@@ -46,10 +51,10 @@ func (c *Cache) cleanupRoutine(globalTTL time.Duration) {
|
||||
c.CleanExpiredEntries()
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Cache) Set(key string, value []byte, ttl time.Duration) {
|
||||
c.Lock()
|
||||
c.Lock() // use the lock
|
||||
defer c.Unlock()
|
||||
|
||||
expiresAt := time.Now().Add(ttl)
|
||||
|
||||
compressedValue, err := c.compress(value)
|
||||
@@ -57,25 +62,15 @@ func (c *Cache) Set(key string, value []byte, ttl time.Duration) {
|
||||
return
|
||||
}
|
||||
|
||||
// Get a byte slice from the pool and ensure it's properly sized.
|
||||
b := c.bytePool.Get().([]byte)
|
||||
if cap(b) < len(compressedValue) {
|
||||
b = make([]byte, len(compressedValue))
|
||||
} else {
|
||||
b = b[:len(compressedValue)]
|
||||
}
|
||||
|
||||
copy(b, compressedValue)
|
||||
|
||||
entry := CacheEntry{
|
||||
Value: b,
|
||||
Value: compressedValue,
|
||||
ExpiresAt: expiresAt,
|
||||
}
|
||||
c.entries.Store(key, entry)
|
||||
}
|
||||
|
||||
func (c *Cache) Get(key string) ([]byte, bool) {
|
||||
c.RLock()
|
||||
c.RLock() // use the read lock
|
||||
defer c.RUnlock()
|
||||
|
||||
entry, ok := c.entries.Load(key)
|
||||
@@ -95,15 +90,11 @@ func (c *Cache) Delete(key string) {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
|
||||
entry, ok := c.entries.Load(key)
|
||||
_, ok := c.entries.Load(key)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
// Return the byte slice to the pool.
|
||||
c.bytePool.Put(entry.(CacheEntry).Value)
|
||||
|
||||
// Delete the entry from the cache.
|
||||
c.entries.Delete(key)
|
||||
}
|
||||
|
||||
@@ -112,37 +103,51 @@ func (c *Cache) CleanExpiredEntries() {
|
||||
c.entries.Range(func(key, value interface{}) bool {
|
||||
entry := value.(CacheEntry)
|
||||
if entry.ExpiresAt.Before(now) {
|
||||
// Return the byte slice to the pool.
|
||||
c.bytePool.Put(entry.Value)
|
||||
|
||||
// Delete the entry from the cache.
|
||||
c.entries.Delete(key)
|
||||
}
|
||||
|
||||
// Return true to continue iterating over the map.
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
func (c *Cache) compress(data []byte) ([]byte, error) {
|
||||
w := c.compressPool.Get().(*gzip.Writer)
|
||||
defer c.compressPool.Put(w)
|
||||
|
||||
var buf bytes.Buffer
|
||||
w := gzip.NewWriter(&buf)
|
||||
_, err := w.Write(data)
|
||||
if err != nil {
|
||||
w.Reset(&buf)
|
||||
if _, err := w.Write(data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = w.Close()
|
||||
if err != nil {
|
||||
if err := w.Close(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
func (c *Cache) decompress(data []byte) ([]byte, error) {
|
||||
r, err := gzip.NewReader(bytes.NewBuffer(data))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
r, ok := c.decompressPool.Get().(*gzip.Reader)
|
||||
if !ok || r == nil {
|
||||
// If r is nil or type assertion fails, create a new gzip.Reader
|
||||
var err error
|
||||
r, err = gzip.NewReader(bytes.NewReader(data))
|
||||
if err != nil {
|
||||
return nil, err // Handle the error if gzip.NewReader fails
|
||||
}
|
||||
} else {
|
||||
// Reset the existing reader with new data
|
||||
if err := r.Reset(bytes.NewReader(data)); err != nil {
|
||||
return nil, err // Handle the error if Reset fails
|
||||
}
|
||||
}
|
||||
defer r.Close()
|
||||
return io.ReadAll(r)
|
||||
|
||||
// Ensure the reader is returned to the pool
|
||||
defer c.decompressPool.Put(r)
|
||||
|
||||
// Read all the data from the reader
|
||||
decompressedData, err := io.ReadAll(r)
|
||||
if err != nil {
|
||||
return nil, err // Handle the error if reading fails
|
||||
}
|
||||
return decompressedData, nil
|
||||
}
|
||||
|
||||
Vendored
+46
@@ -0,0 +1,46 @@
|
||||
package libpack_cache
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Assume that New function initializes the cache and it is defined somewhere in the libpack_cache package.
|
||||
|
||||
func BenchmarkCacheSet(b *testing.B) {
|
||||
cache := New(30 * time.Second) // Initializing the cache with a TTL of 30 seconds
|
||||
key := "benchmark-key"
|
||||
value := []byte("benchmark-value")
|
||||
|
||||
b.ResetTimer() // Reset the timer to exclude the setup time from the benchmark
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
cache.Set(key, value, 5*time.Second)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkCacheGet(b *testing.B) {
|
||||
cache := New(30 * time.Second) // Initializing the cache
|
||||
key := "benchmark-key"
|
||||
value := []byte("benchmark-value")
|
||||
cache.Set(key, value, 5*time.Second) // Pre-set a value to retrieve
|
||||
|
||||
b.ResetTimer() // Start timing
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, _ = cache.Get(key)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkCacheExpire(b *testing.B) {
|
||||
key := "benchmark-expire-key"
|
||||
value := []byte("benchmark-value")
|
||||
ttl := 5 * time.Millisecond // Setting a short TTL for quick expiration
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
cache := New(30 * time.Second)
|
||||
cache.Set(key, value, ttl)
|
||||
time.Sleep(ttl) // Wait for the key to expire
|
||||
_, _ = cache.Get(key)
|
||||
}
|
||||
}
|
||||
Vendored
-36
@@ -110,39 +110,3 @@ func (suite *CacheTestSuite) Test_CacheExpire() {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (suite *CacheTestSuite) Test_CacheCleanExpiredEntries() {
|
||||
cache := New(5 * time.Second)
|
||||
tests := []struct {
|
||||
name string
|
||||
cache_value string
|
||||
ttl time.Duration
|
||||
}{
|
||||
{
|
||||
name: "test1",
|
||||
cache_value: "test1-123",
|
||||
ttl: 2 * time.Second,
|
||||
},
|
||||
{
|
||||
name: "test2",
|
||||
cache_value: "test2-123",
|
||||
ttl: 5 * time.Second,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
suite.T().Run(tt.name, func(t *testing.T) {
|
||||
cache.Set(tt.name, []byte(tt.name), tt.ttl)
|
||||
c, ok := cache.Get(tt.name)
|
||||
suite.Equal(true, ok)
|
||||
suite.Equal(tt.name, string(c))
|
||||
time.Sleep(tt.ttl)
|
||||
c, ok = cache.Get(tt.name)
|
||||
suite.Equal(false, ok)
|
||||
suite.Equal("", string(c))
|
||||
cache.CleanExpiredEntries()
|
||||
c, ok = cache.Get(tt.name)
|
||||
suite.Equal(false, ok)
|
||||
suite.Equal("", string(c))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
+50
-39
@@ -3,60 +3,71 @@ package gql
|
||||
import (
|
||||
"bytes"
|
||||
"compress/gzip"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/avast/retry-go/v4"
|
||||
"github.com/goccy/go-json"
|
||||
)
|
||||
|
||||
var bufferPool = sync.Pool{
|
||||
New: func() interface{} {
|
||||
return new(bytes.Buffer)
|
||||
},
|
||||
}
|
||||
|
||||
func (qe *QueryExecutor) executeQuery() ([]byte, error) {
|
||||
httpRequest, err := http.NewRequest("POST", qe.endpoint, bytes.NewBuffer(qe.Query))
|
||||
// Reuse buffer from pool to avoid allocations
|
||||
buf := bufferPool.Get().(*bytes.Buffer)
|
||||
defer bufferPool.Put(buf)
|
||||
buf.Reset()
|
||||
|
||||
_, err := buf.Write(qe.Query)
|
||||
if err != nil {
|
||||
qe.Logger.Error("Can't create HTTP request;", map[string]interface{}{"error": err})
|
||||
qe.Logger.Error("Can't write to buffer", map[string]interface{}{"error": err})
|
||||
return nil, err
|
||||
}
|
||||
|
||||
httpRequest, err := http.NewRequest(http.MethodPost, qe.endpoint, buf)
|
||||
if err != nil {
|
||||
qe.Logger.Error("Can't create HTTP request", map[string]interface{}{"error": err})
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for key, value := range qe.Headers {
|
||||
strValue, ok := value.(string)
|
||||
if !ok {
|
||||
strValue = fmt.Sprintf("%v", value)
|
||||
}
|
||||
httpRequest.Header.Set(key, strValue)
|
||||
httpRequest.Header.Set(key, fmt.Sprint(value))
|
||||
}
|
||||
|
||||
var retries_max int
|
||||
|
||||
retriesMax := 1
|
||||
if qe.Retries {
|
||||
qe.Logger.Debug("Retries enabled - setting max retries", map[string]interface{}{"retries": qe.retries_number})
|
||||
retries_max = qe.retries_number
|
||||
qe.Logger.Debug("Retries enabled", map[string]interface{}{"retries": qe.retries_number})
|
||||
retriesMax = qe.retries_number
|
||||
} else {
|
||||
qe.Logger.Debug("Retries disabled - setting max retries", map[string]interface{}{"retries": 1})
|
||||
retries_max = 1
|
||||
qe.Logger.Debug("Retries disabled", map[string]interface{}{"retries": 1})
|
||||
}
|
||||
|
||||
var httpResponse *http.Response
|
||||
var queryResult queryResults
|
||||
|
||||
err = retry.Do(
|
||||
func() error {
|
||||
httpResponse, err = qe.client.Do(httpRequest)
|
||||
httpResponse, err := qe.client.Do(httpRequest)
|
||||
if err != nil {
|
||||
qe.Logger.Debug("Error while executing http request", map[string]interface{}{"error": err.Error()})
|
||||
qe.Logger.Debug("Error executing HTTP request", map[string]interface{}{"error": err.Error()})
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
_, err := io.Copy(io.Discard, httpResponse.Body)
|
||||
if err != nil {
|
||||
qe.Logger.Debug("Error while discarding http response body;", map[string]interface{}{"error": err.Error()})
|
||||
qe.Logger.Debug("Error discarding HTTP response body", map[string]interface{}{"error": err.Error()})
|
||||
}
|
||||
httpResponse.Body.Close()
|
||||
}()
|
||||
|
||||
if httpResponse.StatusCode < 200 || httpResponse.StatusCode >= 204 {
|
||||
return fmt.Errorf("HTTP error - unacceptable status code: \"%s\" for \"%s\"", httpResponse.Status, httpRequest.URL)
|
||||
if httpResponse.StatusCode < http.StatusOK || httpResponse.StatusCode >= http.StatusNoContent {
|
||||
return fmt.Errorf("HTTP error - status code: %s for %s", httpResponse.Status, httpRequest.URL)
|
||||
}
|
||||
|
||||
var reader io.ReadCloser
|
||||
@@ -64,8 +75,8 @@ func (qe *QueryExecutor) executeQuery() ([]byte, error) {
|
||||
if encoding == "gzip" {
|
||||
reader, err = gzip.NewReader(httpResponse.Body)
|
||||
if err != nil {
|
||||
qe.Logger.Debug("Error while creating gzip reader;", map[string]interface{}{"error": err.Error()})
|
||||
return fmt.Errorf("Error while creating gzip reader: %s", err.Error())
|
||||
qe.Logger.Debug("Error creating gzip reader", map[string]interface{}{"error": err.Error()})
|
||||
return fmt.Errorf("error creating gzip reader: %w", err)
|
||||
}
|
||||
defer reader.Close()
|
||||
} else {
|
||||
@@ -74,50 +85,50 @@ func (qe *QueryExecutor) executeQuery() ([]byte, error) {
|
||||
|
||||
body, err := io.ReadAll(reader)
|
||||
if err != nil {
|
||||
qe.Logger.Debug("Error while reading http response;", map[string]interface{}{"error": err.Error()})
|
||||
return fmt.Errorf("Error while reading http response: %s", err.Error())
|
||||
qe.Logger.Debug("Error reading HTTP response", map[string]interface{}{"error": err.Error()})
|
||||
return fmt.Errorf("error reading HTTP response: %w", err)
|
||||
}
|
||||
|
||||
err = json.Unmarshal(body, &queryResult)
|
||||
if err != nil {
|
||||
qe.Logger.Debug("Error while unmarshalling http response;", map[string]interface{}{"error": err.Error()})
|
||||
return fmt.Errorf("Error while unmarshalling http response: %s", err.Error())
|
||||
qe.Logger.Debug("Error unmarshalling HTTP response", map[string]interface{}{"error": err.Error()})
|
||||
return fmt.Errorf("error unmarshalling HTTP response: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
retry.OnRetry(func(n uint, err error) {
|
||||
qe.Logger.Warning("Retrying query", map[string]interface{}{"error": err.Error(), "attempt": n})
|
||||
qe.Logger.Warning("Retrying query", map[string]interface{}{"error": err.Error(), "attempt": n + 1})
|
||||
}),
|
||||
retry.Attempts(uint(retries_max)),
|
||||
retry.Attempts(uint(retriesMax)),
|
||||
retry.DelayType(retry.BackOffDelay),
|
||||
retry.Delay(time.Duration(qe.retries_delay)),
|
||||
retry.LastErrorOnly(true),
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
qe.Logger.Debug("Error while executing http request - target server", map[string]interface{}{"error": err.Error()})
|
||||
qe.Logger.Debug("Error executing HTTP request", map[string]interface{}{"error": err.Error()})
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(queryResult.Errors) > 0 {
|
||||
qe.Logger.Debug("Error while executing query;", map[string]interface{}{"error": queryResult.Errors})
|
||||
return nil, fmt.Errorf("Error while executing query: %s", queryResult.Errors)
|
||||
qe.Logger.Debug("Error executing query", map[string]interface{}{"error": queryResult.Errors})
|
||||
return nil, fmt.Errorf("error executing query: %s", queryResult.Errors)
|
||||
}
|
||||
|
||||
if queryResult.Data == nil {
|
||||
qe.Logger.Debug("Error while executing query", map[string]interface{}{"error": "no data"})
|
||||
return nil, fmt.Errorf("Error while executing query: no data")
|
||||
qe.Logger.Debug("Error executing query", map[string]interface{}{"error": "no data"})
|
||||
return nil, errors.New("error executing query: no data")
|
||||
}
|
||||
|
||||
json_data, err := json.Marshal(queryResult.Data)
|
||||
jsonData, err := json.Marshal(queryResult.Data)
|
||||
if err != nil {
|
||||
qe.Logger.Debug("Error while marshalling query result;", map[string]interface{}{"error": err.Error(), "data": queryResult.Data})
|
||||
return nil, fmt.Errorf("Error while marshalling query result: %s. Data: %s", err.Error(), queryResult.Data)
|
||||
qe.Logger.Debug("Error marshalling query result", map[string]interface{}{"error": err.Error(), "data": queryResult.Data})
|
||||
return nil, fmt.Errorf("error marshalling query result: %w. Data: %s", err, queryResult.Data)
|
||||
}
|
||||
|
||||
if qe.CacheKey != "no-cache" {
|
||||
qe.cache.Set(qe.CacheKey, json_data, qe.CacheTTL)
|
||||
qe.cache.Set(qe.CacheKey, jsonData, qe.CacheTTL)
|
||||
}
|
||||
|
||||
return json_data, nil
|
||||
return jsonData, nil
|
||||
}
|
||||
|
||||
@@ -3,12 +3,12 @@ module github.com/lukaszraczylo/go-simple-graphql
|
||||
go 1.21
|
||||
|
||||
require (
|
||||
github.com/avast/retry-go/v4 v4.5.1
|
||||
github.com/avast/retry-go/v4 v4.6.0
|
||||
github.com/goccy/go-json v0.10.2
|
||||
github.com/gookit/goutil v0.6.15
|
||||
github.com/rs/zerolog v1.32.0
|
||||
github.com/stretchr/testify v1.8.4
|
||||
golang.org/x/net v0.20.0
|
||||
github.com/stretchr/testify v1.9.0
|
||||
golang.org/x/net v0.25.0
|
||||
)
|
||||
|
||||
require (
|
||||
@@ -18,9 +18,9 @@ require (
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
|
||||
golang.org/x/sync v0.6.0 // indirect
|
||||
golang.org/x/sys v0.16.0 // indirect
|
||||
golang.org/x/term v0.16.0 // indirect
|
||||
golang.org/x/text v0.14.0 // indirect
|
||||
golang.org/x/sync v0.7.0 // indirect
|
||||
golang.org/x/sys v0.20.0 // indirect
|
||||
golang.org/x/term v0.20.0 // indirect
|
||||
golang.org/x/text v0.15.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
github.com/avast/retry-go/v4 v4.5.1 h1:AxIx0HGi4VZ3I02jr78j5lZ3M6x1E0Ivxa6b0pUUh7o=
|
||||
github.com/avast/retry-go/v4 v4.5.1/go.mod h1:/sipNsvNB3RRuT5iNcb6h73nw3IBmXJ/H3XrCQYSOpc=
|
||||
github.com/avast/retry-go/v4 v4.6.0 h1:K9xNA+KeB8HHc2aWFuLb25Offp+0iVRXEvFx8IinRJA=
|
||||
github.com/avast/retry-go/v4 v4.6.0/go.mod h1:gvWlPhBVsvBbLkVGDg/KwvBv0bEkCOLRRSHKIr2PyOE=
|
||||
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
@@ -22,25 +22,25 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN
|
||||
github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
|
||||
github.com/rs/zerolog v1.32.0 h1:keLypqrlIjaFsbmJOBdB/qvyF8KEtCWHwobLp5l/mQ0=
|
||||
github.com/rs/zerolog v1.32.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss=
|
||||
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no=
|
||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM=
|
||||
golang.org/x/exp v0.0.0-20220909182711-5c715a9e8561 h1:MDc5xs78ZrZr3HMQugiXOAkSZtfTpbJLDr/lwfgO53E=
|
||||
golang.org/x/exp v0.0.0-20220909182711-5c715a9e8561/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE=
|
||||
golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo=
|
||||
golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
|
||||
golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
|
||||
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac=
|
||||
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
|
||||
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
|
||||
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU=
|
||||
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.16.0 h1:m+B6fahuftsE9qjo0VWp2FW0mB3MTJvR0BaMQrq0pmE=
|
||||
golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY=
|
||||
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
|
||||
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.20.0 h1:VnkxpohqXaOBYJtBmEppKUG6mXpi+4O6purfc2+sMhw=
|
||||
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
|
||||
golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk=
|
||||
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
|
||||
+3
-3
@@ -50,12 +50,12 @@ func (suite *Tests) TestBaseClient_decodeResponse() {
|
||||
response []byte
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want any
|
||||
wantErr bool
|
||||
name string
|
||||
setType string
|
||||
wantType string
|
||||
args args
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "TestBaseClient_decodeResponse_mapstring",
|
||||
|
||||
@@ -28,7 +28,7 @@ func (b *BaseClient) createHttpClient() (http_client *http.Client) {
|
||||
return http.ErrUseLastResponse
|
||||
},
|
||||
}
|
||||
b.Logger.Debug("Using HTTP/1.1 over http")
|
||||
b.Logger.Debug("Using HTTP/1.1 over http", nil)
|
||||
} else if strings.HasPrefix(b.endpoint, "https://") {
|
||||
tlsClientConfig := &tls.Config{}
|
||||
if strings.HasPrefix(b.endpoint, "https://") {
|
||||
@@ -46,9 +46,9 @@ func (b *BaseClient) createHttpClient() (http_client *http.Client) {
|
||||
return http.ErrUseLastResponse
|
||||
},
|
||||
}
|
||||
b.Logger.Debug("Using HTTP/2 over https")
|
||||
b.Logger.Debug("Using HTTP/2 over https", nil)
|
||||
} else {
|
||||
b.Logger.Critical("Invalid endpoint - neither http or https")
|
||||
b.Logger.Critical("Invalid endpoint - neither http or https", nil)
|
||||
}
|
||||
return http_client
|
||||
}
|
||||
|
||||
+64
-78
@@ -1,8 +1,8 @@
|
||||
package libpack_logging
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
@@ -11,124 +11,110 @@ import (
|
||||
)
|
||||
|
||||
type LogConfig struct {
|
||||
baseLogger zerolog.Logger
|
||||
errorLogger zerolog.Logger
|
||||
nopLogger zerolog.Logger
|
||||
mu sync.Mutex
|
||||
minLevel zerolog.Level
|
||||
logger zerolog.Logger
|
||||
}
|
||||
|
||||
var (
|
||||
baseLogger zerolog.Logger
|
||||
|
||||
eventPool = sync.Pool{
|
||||
New: func() interface{} {
|
||||
return new(zerolog.Event)
|
||||
},
|
||||
}
|
||||
|
||||
fieldMapPool = sync.Pool{
|
||||
New: func() interface{} {
|
||||
return make(map[string]interface{})
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
func init() {
|
||||
zerolog.TimeFieldFormat = time.RFC3339
|
||||
zerolog.MessageFieldName = "msg"
|
||||
zerolog.TimestampFieldName = "ts"
|
||||
zerolog.MessageFieldName = "short_message"
|
||||
zerolog.TimestampFieldName = "timestamp"
|
||||
zerolog.LevelFieldName = "level"
|
||||
zerolog.LevelFatalValue = "critical"
|
||||
}
|
||||
|
||||
func getMinLogLevel() zerolog.Level {
|
||||
levelStr := strings.ToLower(envutil.Getenv("LOG_LEVEL", "info"))
|
||||
return matchLogLevel(levelStr)
|
||||
}
|
||||
baseLogger = zerolog.New(os.Stdout).With().Timestamp().Logger()
|
||||
|
||||
// Add function set the minimum log level instead of using environment variable
|
||||
func (lc *LogConfig) SetLogLevel(level string) {
|
||||
lc.mu.Lock()
|
||||
defer lc.mu.Unlock()
|
||||
lc.minLevel = matchLogLevel(level)
|
||||
}
|
||||
|
||||
// Match the log level provided as string with the log level provided as zerolog.Level
|
||||
func matchLogLevel(level string) zerolog.Level {
|
||||
switch level {
|
||||
switch logLevel := envutil.Getenv("LOG_LEVEL", "info"); logLevel {
|
||||
case "debug":
|
||||
return zerolog.DebugLevel
|
||||
baseLogger = baseLogger.Level(zerolog.DebugLevel)
|
||||
case "warn":
|
||||
return zerolog.WarnLevel
|
||||
baseLogger = baseLogger.Level(zerolog.WarnLevel)
|
||||
case "error":
|
||||
return zerolog.ErrorLevel
|
||||
case "critical":
|
||||
return zerolog.FatalLevel
|
||||
baseLogger = baseLogger.Level(zerolog.ErrorLevel)
|
||||
default:
|
||||
return zerolog.InfoLevel
|
||||
baseLogger = baseLogger.Level(zerolog.InfoLevel)
|
||||
}
|
||||
}
|
||||
|
||||
func NewLogger() *LogConfig {
|
||||
zl := zerolog.New(os.Stdout).With().Timestamp().Logger()
|
||||
// Determine the minimum log level from environment variable
|
||||
return &LogConfig{
|
||||
baseLogger: zl,
|
||||
errorLogger: zl.Output(os.Stderr),
|
||||
nopLogger: zerolog.Nop(),
|
||||
minLevel: getMinLogLevel(),
|
||||
}
|
||||
return &LogConfig{logger: baseLogger}
|
||||
}
|
||||
|
||||
func (lc *LogConfig) getLogger(level zerolog.Level) zerolog.Logger {
|
||||
if level >= zerolog.ErrorLevel {
|
||||
return lc.errorLogger
|
||||
}
|
||||
if level < lc.minLevel {
|
||||
return lc.nopLogger
|
||||
}
|
||||
return lc.baseLogger
|
||||
}
|
||||
|
||||
func (lc *LogConfig) Log(level zerolog.Level, message string, fields []map[string]interface{}) {
|
||||
if level < lc.minLevel {
|
||||
return
|
||||
}
|
||||
logger := lc.getLogger(level)
|
||||
func (lw *LogConfig) log(w io.Writer, level zerolog.Level, message string, fields map[string]interface{}) {
|
||||
logger := lw.logger.Output(w)
|
||||
event := logger.WithLevel(level).CallerSkipFrame(3)
|
||||
if len(fields) == 0 {
|
||||
event.Msg(message)
|
||||
return
|
||||
}
|
||||
|
||||
field := fields[0]
|
||||
for k, val := range field {
|
||||
for k, val := range fields {
|
||||
switch v := val.(type) {
|
||||
case string:
|
||||
event.Str(k, v)
|
||||
event = event.Str(k, v)
|
||||
case int:
|
||||
event.Int(k, v)
|
||||
event = event.Int(k, v)
|
||||
case float64:
|
||||
event.Float64(k, v)
|
||||
event = event.Float64(k, v)
|
||||
default:
|
||||
event.Interface(k, v)
|
||||
event = event.Interface(k, val)
|
||||
}
|
||||
}
|
||||
|
||||
event.Msg(message)
|
||||
}
|
||||
|
||||
func (lc *LogConfig) Info(message string, fields ...map[string]interface{}) {
|
||||
lc.Log(zerolog.InfoLevel, message, fields)
|
||||
func (lw *LogConfig) logWithLevel(level zerolog.Level, message string, fields map[string]interface{}) {
|
||||
if lw.logger.GetLevel() <= level {
|
||||
w := os.Stdout
|
||||
if level >= zerolog.ErrorLevel {
|
||||
w = os.Stderr
|
||||
}
|
||||
lw.log(w, level, message, fields)
|
||||
}
|
||||
}
|
||||
|
||||
func (lc *LogConfig) Debug(message string, fields ...map[string]interface{}) {
|
||||
lc.Log(zerolog.DebugLevel, message, fields)
|
||||
func (lw *LogConfig) Debug(message string, fields map[string]interface{}) {
|
||||
lw.logWithLevel(zerolog.DebugLevel, message, fields)
|
||||
}
|
||||
|
||||
func (lc *LogConfig) Warn(message string, fields ...map[string]interface{}) {
|
||||
lc.Log(zerolog.WarnLevel, message, fields)
|
||||
func (lw *LogConfig) Info(message string, fields map[string]interface{}) {
|
||||
lw.logWithLevel(zerolog.InfoLevel, message, fields)
|
||||
}
|
||||
|
||||
// alias Warning to Warn
|
||||
func (lc *LogConfig) Warning(message string, fields ...map[string]interface{}) {
|
||||
lc.Warn(message, fields...)
|
||||
func (lw *LogConfig) Warning(message string, fields map[string]interface{}) {
|
||||
lw.logWithLevel(zerolog.WarnLevel, message, fields)
|
||||
}
|
||||
|
||||
func (lc *LogConfig) Error(message string, fields ...map[string]interface{}) {
|
||||
lc.Log(zerolog.ErrorLevel, message, fields)
|
||||
func (lw *LogConfig) Error(message string, fields map[string]interface{}) {
|
||||
lw.logWithLevel(zerolog.ErrorLevel, message, fields)
|
||||
}
|
||||
|
||||
func (lc *LogConfig) Critical(message string, fields ...map[string]interface{}) {
|
||||
lc.Log(zerolog.FatalLevel, message, fields)
|
||||
func (lw *LogConfig) Critical(message string, fields map[string]interface{}) {
|
||||
lw.logWithLevel(zerolog.FatalLevel, message, fields)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// alias Fatal to Critical
|
||||
func (lc *LogConfig) Fatal(message string, fields ...map[string]interface{}) {
|
||||
lc.Critical(message, fields...)
|
||||
// Helper function to get a new fields map from the pool
|
||||
func getFieldsMap() map[string]interface{} {
|
||||
return fieldMapPool.Get().(map[string]interface{})
|
||||
}
|
||||
|
||||
// Helper function to put a used fields map back into the pool
|
||||
func putFieldsMap(fields map[string]interface{}) {
|
||||
for k := range fields {
|
||||
delete(fields, k)
|
||||
}
|
||||
fieldMapPool.Put(fields)
|
||||
}
|
||||
|
||||
@@ -2,69 +2,84 @@ package gql
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"github.com/goccy/go-json"
|
||||
"github.com/gookit/goutil"
|
||||
"github.com/gookit/goutil/strutil"
|
||||
)
|
||||
|
||||
var jsonBufferPool = sync.Pool{
|
||||
New: func() interface{} {
|
||||
return new([]byte)
|
||||
},
|
||||
}
|
||||
|
||||
func (b *BaseClient) convertToJSON(v any) []byte {
|
||||
jsonBuffer := jsonBufferPool.Get().(*[]byte)
|
||||
defer jsonBufferPool.Put(jsonBuffer)
|
||||
|
||||
*jsonBuffer = (*jsonBuffer)[:0]
|
||||
jsonData, err := json.Marshal(v)
|
||||
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 jsonData
|
||||
*jsonBuffer = append(*jsonBuffer, jsonData...)
|
||||
return *jsonBuffer
|
||||
}
|
||||
|
||||
func (b *BaseClient) compileQuery(query_partials ...any) *Query {
|
||||
q := new(Query)
|
||||
for _, partial := range query_partials {
|
||||
func (b *BaseClient) compileQuery(queryPartials ...any) *Query {
|
||||
var query string
|
||||
var variables map[string]interface{}
|
||||
for _, partial := range queryPartials {
|
||||
switch val := partial.(type) {
|
||||
case string:
|
||||
q.Query = val
|
||||
query = val
|
||||
case map[string]interface{}:
|
||||
q.Variables = val
|
||||
variables = val
|
||||
}
|
||||
}
|
||||
if q.Query == "" {
|
||||
b.Logger.Error("Can't compile query;", map[string]interface{}{"error": "query is empty"})
|
||||
|
||||
if query == "" {
|
||||
b.Logger.Error("Can't compile query", map[string]interface{}{"error": "query is empty"})
|
||||
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{}) (returned_value any, err error) {
|
||||
func (b *BaseClient) Query(query string, variables map[string]interface{}, headers map[string]interface{}) (any, error) {
|
||||
compiledQuery := b.compileQuery(query, variables)
|
||||
if compiledQuery.JsonQuery == nil {
|
||||
b.Logger.Error("Can't compile query;", map[string]interface{}{"error": "query is empty"})
|
||||
return nil, fmt.Errorf("Can't compile query")
|
||||
b.Logger.Error("Can't compile query", map[string]interface{}{"error": "query is empty"})
|
||||
return nil, fmt.Errorf("can't compile query")
|
||||
}
|
||||
b.Logger.Debug("Compiled query", map[string]interface{}{"query": compiledQuery})
|
||||
|
||||
b.Logger.Debug("Compiled query;", map[string]interface{}{"query": compiledQuery})
|
||||
enable_cache, enable_retries, recompile_required := compiledQuery.parseHeadersAndVariables(headers)
|
||||
|
||||
if recompile_required {
|
||||
enableCache, enableRetries, recompileRequired := compiledQuery.parseHeadersAndVariables(headers)
|
||||
if recompileRequired {
|
||||
compiledQuery = b.compileQuery(query, variables)
|
||||
}
|
||||
|
||||
var queryHash string
|
||||
|
||||
if (enable_cache || b.cache_global) && strutil.HasPrefix(compiledQuery.Query, "query") {
|
||||
b.Logger.Debug("Cache enabled")
|
||||
if (enableCache || b.cache_global) && strutil.HasPrefix(compiledQuery.Query, "query") {
|
||||
b.Logger.Debug("Cache enabled", nil)
|
||||
queryHash = calculateHash(compiledQuery)
|
||||
cached_value := b.cacheLookup(queryHash)
|
||||
if cached_value != nil {
|
||||
if cachedValue := b.cacheLookup(queryHash); cachedValue != nil {
|
||||
b.Logger.Debug("Cache hit", map[string]interface{}{"query": compiledQuery})
|
||||
return cached_value, nil
|
||||
} else {
|
||||
b.Logger.Debug("Cache miss", map[string]interface{}{"query": compiledQuery})
|
||||
return cachedValue, nil
|
||||
}
|
||||
b.Logger.Debug("Cache miss", map[string]interface{}{"query": compiledQuery})
|
||||
}
|
||||
|
||||
if enable_retries || b.retries_enable {
|
||||
b.Logger.Debug("Retries enabled")
|
||||
if enableRetries || b.retries_enable {
|
||||
b.Logger.Debug("Retries enabled", nil)
|
||||
}
|
||||
|
||||
q := &QueryExecutor{
|
||||
@@ -74,33 +89,37 @@ func (b *BaseClient) Query(query string, variables map[string]interface{}, heade
|
||||
CacheKey: func() string {
|
||||
if queryHash != "" {
|
||||
return queryHash
|
||||
} else {
|
||||
return "no-cache"
|
||||
}
|
||||
return "no-cache"
|
||||
}(),
|
||||
Retries: enable_retries || b.retries_enable,
|
||||
Retries: enableRetries || b.retries_enable,
|
||||
}
|
||||
defer func() { q = nil }()
|
||||
|
||||
rv, err := q.executeQuery()
|
||||
if err != nil {
|
||||
b.Logger.Error("Error while executing query;", map[string]interface{}{"error": err.Error()})
|
||||
b.Logger.Error("Error executing query", map[string]interface{}{"error": err.Error()})
|
||||
return nil, err
|
||||
}
|
||||
returned_value, err = q.decodeResponse(rv)
|
||||
return returned_value, err
|
||||
|
||||
return q.decodeResponse(rv)
|
||||
}
|
||||
|
||||
func (q *Query) parseHeadersAndVariables(headers map[string]interface{}) (enable_cache bool, enable_retries bool, recompile_required bool) {
|
||||
if headers != nil {
|
||||
enable_cache, _ = goutil.ToBool(searchForKeysInMapStringInterface(headers, "gqlcache"))
|
||||
enable_retries, _ = goutil.ToBool(searchForKeysInMapStringInterface(headers, "gqlretries"))
|
||||
}
|
||||
func (q *Query) parseHeadersAndVariables(headers map[string]interface{}) (enableCache, enableRetries, recompileRequired bool) {
|
||||
enableCache, _ = goutil.ToBool(searchForKeysInMapStringInterface(headers, "gqlcache"))
|
||||
enableRetries, _ = goutil.ToBool(searchForKeysInMapStringInterface(headers, "gqlretries"))
|
||||
|
||||
if q.Variables != nil {
|
||||
enable_cache, _ = goutil.ToBool(searchForKeysInMapStringInterface(q.Variables, "gqlcache"))
|
||||
enable_retries, _ = goutil.ToBool(searchForKeysInMapStringInterface(q.Variables, "gqlretries"))
|
||||
delete(q.Variables, "gqlcache")
|
||||
delete(q.Variables, "gqlretries")
|
||||
recompile_required = true
|
||||
varEnableCache, _ := goutil.ToBool(searchForKeysInMapStringInterface(q.Variables, "gqlcache"))
|
||||
varEnableRetries, _ := goutil.ToBool(searchForKeysInMapStringInterface(q.Variables, "gqlretries"))
|
||||
enableCache = enableCache || varEnableCache
|
||||
enableRetries = enableRetries || varEnableRetries
|
||||
|
||||
if varEnableCache || varEnableRetries {
|
||||
delete(q.Variables, "gqlcache")
|
||||
delete(q.Variables, "gqlretries")
|
||||
recompileRequired = true
|
||||
}
|
||||
}
|
||||
return
|
||||
|
||||
return enableCache, enableRetries, recompileRequired
|
||||
}
|
||||
|
||||
+62
-3
@@ -1,6 +1,8 @@
|
||||
package gql
|
||||
|
||||
import "testing"
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func Benchmark_compileQuery(bn *testing.B) {
|
||||
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()
|
||||
variables := map[string]interface{}{
|
||||
"var1": "value1",
|
||||
}
|
||||
bn.ResetTimer()
|
||||
for i := 0; i < bn.N; i++ {
|
||||
b.convertToJSON(
|
||||
b.compileQuery(
|
||||
`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