From b8044c5d67fdd3eb80c68738280dfc4b618787e6 Mon Sep 17 00:00:00 2001 From: Lukasz Raczylo Date: Sat, 8 Apr 2023 03:01:19 +0100 Subject: [PATCH] Fix concurrency issue. --- query.go | 53 +++++++++++++++++++----------------- query_test.go | 74 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 102 insertions(+), 25 deletions(-) diff --git a/query.go b/query.go index dfb21d1..c05032f 100644 --- a/query.go +++ b/query.go @@ -70,51 +70,54 @@ func (c *BaseClient) NewQuery(q ...any) *Query { // It looks a bit weird because of the backward compatibility func (c *BaseClient) Query(queryContent string, queryVariables interface{}, queryHeaders map[string]interface{}) (any, error) { - query := c.NewQuery(queryContent, queryVariables) - var queryHash string var cachedResponse []byte - var cacheBaseClient BaseClient + + cbc := reflect.ValueOf(*c).Interface().(BaseClient) + cacheBaseClient := &cbc + + query := cacheBaseClient.NewQuery(queryContent, queryVariables) // Check for library specific headers if len(queryHeaders) > 0 { - cacheBaseClient = reflect.ValueOf(*c).Interface().(BaseClient) - queryHeaders = c.parseQueryHeaders(queryHeaders) - defer func() { - *c = cacheBaseClient - }() - } + queryHeadersModified := c.parseQueryHeaders(queryHeaders) + // compare if there are any changes - if c.cache.enabled { - queryHash = strutil.Md5(fmt.Sprintf("%s-%+v", query.compiledQuery, queryHeaders)) - c.Logger.Debug(c, "Hash calculated;", "hash:", queryHash) - - cachedResponse = c.cacheLookup(queryHash) - if cachedResponse != nil { - c.Logger.Debug(c, "Found cached response") - return c.decodeResponse(cachedResponse), nil - } else { - c.Logger.Debug(c, "No cached response found") + if !reflect.DeepEqual(queryHeadersModified, queryHeaders) { + cacheBaseClient.Logger.Debug(cacheBaseClient, "Headers modified, creating a new client") } } - response, err := c.executeQuery(query.compiledQuery, queryHeaders) + if cacheBaseClient.cache.enabled { + queryHash = strutil.Md5(fmt.Sprintf("%s-%+v", query.compiledQuery, queryHeaders)) + cacheBaseClient.Logger.Debug(cacheBaseClient, "Hash calculated;", "hash:", queryHash) + + cachedResponse = cacheBaseClient.cacheLookup(queryHash) + if cachedResponse != nil { + cacheBaseClient.Logger.Debug(cacheBaseClient, "Found cached response") + return cacheBaseClient.decodeResponse(cachedResponse), nil + } else { + cacheBaseClient.Logger.Debug(cacheBaseClient, "No cached response found") + } + } + + response, err := cacheBaseClient.executeQuery(query.compiledQuery, queryHeaders) if err != nil { - c.Logger.Error(c, "Error while executing query;", "error", err.Error()) + cacheBaseClient.Logger.Error(cacheBaseClient, "Error while executing query;", "error", err.Error()) return nil, err } jsonData, err := json.Marshal(response) if err != nil { - c.Logger.Error(c, "Error while converting to json;", "error", err.Error()) + cacheBaseClient.Logger.Error(cacheBaseClient, "Error while converting to json;", "error", err.Error()) return nil, err } - if c.cache.enabled { - c.cache.client.Set(queryHash, jsonData) + if cacheBaseClient.cache.enabled { + cacheBaseClient.cache.client.Set(queryHash, jsonData) } - return c.decodeResponse(jsonData), err + return cacheBaseClient.decodeResponse(jsonData), err } func (c *BaseClient) decodeResponse(jsonData []byte) any { diff --git a/query_test.go b/query_test.go index f4c4524..38d4e02 100644 --- a/query_test.go +++ b/query_test.go @@ -339,3 +339,77 @@ func TestBaseClient_parseQueryHeaders(t *testing.T) { }) } } + +func TestBaseClient_QueryCache(t *testing.T) { + type fields struct { + graphql_endpoint string + cache_enabled bool + } + type args struct { + queryVariables interface{} + queryHeaders map[string]interface{} + queryContent string + } + tests := []struct { + want any + args args + name string + fields fields + wantErr bool + }{ + { + name: "Test QueryCache - enabled", + fields: fields{ + graphql_endpoint: "https://spacex-production.up.railway.app/", + cache_enabled: true, + }, + args: args{ + queryContent: `query Dragons { + dragons { + name + } + }`, + queryHeaders: map[string]interface{}{ + "x-apollo-operation-name": "Missions-Potato-Test-Golang", + "content-type": "application/json", + }, + }, + }, + { + name: "Test QueryCache - disabled", + fields: fields{ + graphql_endpoint: "https://spacex-production.up.railway.app/", + cache_enabled: false, + }, + args: args{ + queryContent: `query Dragons { + dragons { + name + } + }`, + queryHeaders: map[string]interface{}{ + "x-apollo-operation-name": "Missions-Potato-Test-Golang", + "content-type": "application/json", + }, + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + c := NewConnection() + c.cache.enabled = tt.fields.cache_enabled + + if tt.fields.graphql_endpoint != "" { + c.endpoint = tt.fields.graphql_endpoint + } + + _, err := c.Query(tt.args.queryContent, tt.args.queryVariables, tt.args.queryHeaders) + if !tt.wantErr { + assert.NoError(t, err) + } else { + assert.Error(t, err) + } + }) + } +}