Query: Fix concurrency handling for mixed datasource queries (#70100)

* split queries and merge responses

* increase concurrency again

* update unit test to verify the headers are merged

* fix lint issue

* fix race condition in unit test

* Fix function name and add a bit more documentation about how the func should be used

* update function call after rename

* check for duplicate header vals

* make concurrent query limit configurable

* Update conf/sample.ini

Co-authored-by: Sofia Papagiannaki <1632407+papagian@users.noreply.github.com>

---------

Co-authored-by: Sofia Papagiannaki <1632407+papagian@users.noreply.github.com>
This commit is contained in:
Michael Mandrus
2023-07-06 17:15:43 +03:00
committed by GitHub
co-authored by Sofia Papagiannaki
parent d6c468c1c2
commit ff6d6659fb
6 changed files with 115 additions and 12 deletions
+28 -2
View File
@@ -6,7 +6,10 @@ import (
"errors"
"fmt"
"net/http"
"net/http/httptest"
"sync"
"testing"
"time"
"github.com/grafana/grafana-plugin-sdk-go/backend"
"github.com/stretchr/testify/assert"
@@ -21,6 +24,7 @@ import (
"github.com/grafana/grafana/pkg/infra/tracing"
"github.com/grafana/grafana/pkg/models/roletype"
"github.com/grafana/grafana/pkg/plugins"
"github.com/grafana/grafana/pkg/services/contexthandler"
"github.com/grafana/grafana/pkg/services/contexthandler/ctxkey"
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
"github.com/grafana/grafana/pkg/services/datasources"
@@ -289,9 +293,23 @@ func TestQueryDataMultipleSources(t *testing.T) {
PublicDashboardAccessToken: "abc123",
}
_, err = tc.queryService.QueryData(context.Background(), tc.signedInUser, true, reqDTO)
req, err := http.NewRequest("POST", "http://localhost:3000", nil)
require.NoError(t, err)
reqCtx := &contextmodel.ReqContext{
SkipQueryCache: false,
Context: &web.Context{
Resp: web.NewResponseWriter(http.MethodGet, httptest.NewRecorder()),
Req: req,
},
}
ctx := ctxkey.Set(context.Background(), reqCtx)
_, err = tc.queryService.QueryData(ctx, tc.signedInUser, true, reqDTO)
require.NoError(t, err)
// response headers should be merged
header := contexthandler.FromContext(ctx).Resp.Header()
assert.Len(t, header.Values("test"), 2)
})
t.Run("can query multiple datasources with an expression present", func(t *testing.T) {
@@ -527,9 +545,13 @@ func (c *fakeDataSourceCache) GetDatasourceByUID(ctx context.Context, datasource
type fakePluginClient struct {
plugins.Client
req *backend.QueryDataRequest
mu sync.Mutex
}
func (c *fakePluginClient) QueryData(ctx context.Context, req *backend.QueryDataRequest) (*backend.QueryDataResponse, error) {
c.mu.Lock()
defer c.mu.Unlock()
c.req = req
// If an expression query ends up getting directly queried, we want it to return an error in our test.
@@ -541,5 +563,9 @@ func (c *fakePluginClient) QueryData(ctx context.Context, req *backend.QueryData
return nil, errors.New("plugin client failed")
}
if reqCtx := contexthandler.FromContext(ctx); reqCtx != nil && reqCtx.Resp != nil {
reqCtx.Resp.Header().Add("test", fmt.Sprintf("header-%d", time.Now().Nanosecond()))
}
return &backend.QueryDataResponse{Responses: make(backend.Responses)}, nil
}