Datasource: Improve default timeout settings for HTTP client provider (#36621)

Make sure that default timeout settings are based on configuration
parameters. This now applies for core data sources using old TSDB
contracts and new SDK contracts. Before it was only applied for old TSDB
contracts.
Also moves global setting variables to non-global (setting.Cfg).
This commit is contained in:
Marcus Efraimsson
2021-07-15 14:30:06 +02:00
committed by GitHub
parent 456dac1303
commit a6b2e1865c
7 changed files with 79 additions and 44 deletions

View File

@@ -64,7 +64,7 @@ func ApplyRoute(ctx context.Context, req *http.Request, proxyPath string, route
} }
} }
if setting.DataProxyLogging { if cfg.DataProxyLogging {
logger.Debug("Requesting", "url", req.URL.String()) logger.Debug("Requesting", "url", req.URL.String())
} }
} }

View File

@@ -304,7 +304,7 @@ func (proxy *DataSourceProxy) validateRequest() error {
} }
func (proxy *DataSourceProxy) logRequest() { func (proxy *DataSourceProxy) logRequest() {
if !setting.DataProxyLogging { if !proxy.cfg.DataProxyLogging {
return return
} }

View File

@@ -3,6 +3,7 @@ package httpclientprovider
import ( import (
"fmt" "fmt"
"net/http" "net/http"
"time"
sdkhttpclient "github.com/grafana/grafana-plugin-sdk-go/backend/httpclient" sdkhttpclient "github.com/grafana/grafana-plugin-sdk-go/backend/httpclient"
"github.com/grafana/grafana/pkg/infra/httpclient" "github.com/grafana/grafana/pkg/infra/httpclient"
@@ -18,6 +19,7 @@ var newProviderFunc = sdkhttpclient.NewProvider
func New(cfg *setting.Cfg) httpclient.Provider { func New(cfg *setting.Cfg) httpclient.Provider {
logger := log.New("httpclient") logger := log.New("httpclient")
userAgent := fmt.Sprintf("Grafana/%s", cfg.BuildVersion) userAgent := fmt.Sprintf("Grafana/%s", cfg.BuildVersion)
middlewares := []sdkhttpclient.Middleware{ middlewares := []sdkhttpclient.Middleware{
TracingMiddleware(logger), TracingMiddleware(logger),
DataSourceMetricsMiddleware(), DataSourceMetricsMiddleware(),
@@ -30,6 +32,8 @@ func New(cfg *setting.Cfg) httpclient.Provider {
middlewares = append(middlewares, SigV4Middleware()) middlewares = append(middlewares, SigV4Middleware())
} }
setDefaultTimeoutOptions(cfg)
return newProviderFunc(sdkhttpclient.ProviderOptions{ return newProviderFunc(sdkhttpclient.ProviderOptions{
Middlewares: middlewares, Middlewares: middlewares,
ConfigureTransport: func(opts sdkhttpclient.Options, transport *http.Transport) { ConfigureTransport: func(opts sdkhttpclient.Options, transport *http.Transport) {
@@ -56,3 +60,20 @@ func newConntrackRoundTripper(name string, transport *http.Transport) *http.Tran
) )
return transport return transport
} }
// setDefaultTimeoutOptions overrides the default timeout options for the SDK.
//
// Note: Not optimal changing global state, but hard to not do in this case.
func setDefaultTimeoutOptions(cfg *setting.Cfg) {
sdkhttpclient.DefaultTimeoutOptions = sdkhttpclient.TimeoutOptions{
Timeout: time.Duration(cfg.DataProxyTimeout) * time.Second,
DialTimeout: time.Duration(cfg.DataProxyDialTimeout) * time.Second,
KeepAlive: time.Duration(cfg.DataProxyKeepAlive) * time.Second,
TLSHandshakeTimeout: time.Duration(cfg.DataProxyTLSHandshakeTimeout) * time.Second,
ExpectContinueTimeout: time.Duration(cfg.DataProxyExpectContinueTimeout) * time.Second,
MaxConnsPerHost: cfg.DataProxyMaxConnsPerHost,
MaxIdleConns: cfg.DataProxyMaxIdleConns,
MaxIdleConnsPerHost: cfg.DataProxyMaxIdleConns,
IdleConnTimeout: time.Duration(cfg.DataProxyIdleConnTimeout) * time.Second,
}
}

View File

@@ -11,7 +11,6 @@ import (
sdkhttpclient "github.com/grafana/grafana-plugin-sdk-go/backend/httpclient" sdkhttpclient "github.com/grafana/grafana-plugin-sdk-go/backend/httpclient"
"github.com/grafana/grafana/pkg/components/simplejson" "github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/infra/httpclient" "github.com/grafana/grafana/pkg/infra/httpclient"
"github.com/grafana/grafana/pkg/setting"
) )
func (ds *DataSource) getTimeout() time.Duration { func (ds *DataSource) getTimeout() time.Duration {
@@ -27,8 +26,9 @@ func (ds *DataSource) getTimeout() time.Duration {
} }
} }
if timeout <= 0 { if timeout <= 0 {
timeout = setting.DataProxyTimeout return sdkhttpclient.DefaultTimeoutOptions.Timeout
} }
return time.Duration(timeout) * time.Second return time.Duration(timeout) * time.Second
} }
@@ -86,14 +86,14 @@ func (ds *DataSource) HTTPClientOptions() sdkhttpclient.Options {
tlsOptions := ds.TLSOptions() tlsOptions := ds.TLSOptions()
timeouts := &sdkhttpclient.TimeoutOptions{ timeouts := &sdkhttpclient.TimeoutOptions{
Timeout: ds.getTimeout(), Timeout: ds.getTimeout(),
DialTimeout: time.Duration(setting.DataProxyDialTimeout) * time.Second, DialTimeout: sdkhttpclient.DefaultTimeoutOptions.DialTimeout,
KeepAlive: time.Duration(setting.DataProxyKeepAlive) * time.Second, KeepAlive: sdkhttpclient.DefaultTimeoutOptions.KeepAlive,
TLSHandshakeTimeout: time.Duration(setting.DataProxyTLSHandshakeTimeout) * time.Second, TLSHandshakeTimeout: sdkhttpclient.DefaultTimeoutOptions.TLSHandshakeTimeout,
ExpectContinueTimeout: time.Duration(setting.DataProxyExpectContinueTimeout) * time.Second, ExpectContinueTimeout: sdkhttpclient.DefaultTimeoutOptions.ExpectContinueTimeout,
MaxConnsPerHost: setting.DataProxyMaxConnsPerHost, MaxConnsPerHost: sdkhttpclient.DefaultTimeoutOptions.MaxConnsPerHost,
MaxIdleConns: setting.DataProxyMaxIdleConns, MaxIdleConns: sdkhttpclient.DefaultTimeoutOptions.MaxIdleConns,
MaxIdleConnsPerHost: setting.DataProxyMaxIdleConns, MaxIdleConnsPerHost: sdkhttpclient.DefaultTimeoutOptions.MaxIdleConnsPerHost,
IdleConnTimeout: time.Duration(setting.DataProxyIdleConnTimeout) * time.Second, IdleConnTimeout: sdkhttpclient.DefaultTimeoutOptions.IdleConnTimeout,
} }
opts := sdkhttpclient.Options{ opts := sdkhttpclient.Options{
Timeouts: timeouts, Timeouts: timeouts,

View File

@@ -312,14 +312,19 @@ func TestDataSource_GetHttpTransport(t *testing.T) {
} }
func TestDataSource_getTimeout(t *testing.T) { func TestDataSource_getTimeout(t *testing.T) {
setting.DataProxyTimeout = 30 originalTimeout := sdkhttpclient.DefaultTimeoutOptions.Timeout
sdkhttpclient.DefaultTimeoutOptions.Timeout = 60 * time.Second
t.Cleanup(func() {
sdkhttpclient.DefaultTimeoutOptions.Timeout = originalTimeout
})
testCases := []struct { testCases := []struct {
jsonData *simplejson.Json jsonData *simplejson.Json
expectedTimeout time.Duration expectedTimeout time.Duration
}{ }{
{jsonData: simplejson.New(), expectedTimeout: 30 * time.Second}, {jsonData: simplejson.New(), expectedTimeout: 60 * time.Second},
{jsonData: simplejson.NewFromAny(map[string]interface{}{"timeout": nil}), expectedTimeout: 30 * time.Second}, {jsonData: simplejson.NewFromAny(map[string]interface{}{"timeout": nil}), expectedTimeout: 60 * time.Second},
{jsonData: simplejson.NewFromAny(map[string]interface{}{"timeout": 0}), expectedTimeout: 30 * time.Second}, {jsonData: simplejson.NewFromAny(map[string]interface{}{"timeout": 0}), expectedTimeout: 60 * time.Second},
{jsonData: simplejson.NewFromAny(map[string]interface{}{"timeout": 1}), expectedTimeout: time.Second}, {jsonData: simplejson.NewFromAny(map[string]interface{}{"timeout": 1}), expectedTimeout: time.Second},
{jsonData: simplejson.NewFromAny(map[string]interface{}{"timeout": "2"}), expectedTimeout: 2 * time.Second}, {jsonData: simplejson.NewFromAny(map[string]interface{}{"timeout": "2"}), expectedTimeout: 2 * time.Second},
} }

View File

@@ -78,17 +78,7 @@ var (
CustomInitPath = "conf/custom.ini" CustomInitPath = "conf/custom.ini"
// HTTP server options // HTTP server options
DataProxyLogging bool StaticRootPath string
DataProxyTimeout int
DataProxyDialTimeout int
DataProxyTLSHandshakeTimeout int
DataProxyExpectContinueTimeout int
DataProxyMaxConnsPerHost int
DataProxyMaxIdleConns int
DataProxyMaxIdleConnsPerHost int
DataProxyKeepAlive int
DataProxyIdleConnTimeout int
StaticRootPath string
// Security settings. // Security settings.
SecretKey string SecretKey string
@@ -322,7 +312,16 @@ type Cfg struct {
JWTAuthJWKSetFile string JWTAuthJWKSetFile string
// Dataproxy // Dataproxy
SendUserHeader bool SendUserHeader bool
DataProxyLogging bool
DataProxyTimeout int
DataProxyDialTimeout int
DataProxyTLSHandshakeTimeout int
DataProxyExpectContinueTimeout int
DataProxyMaxConnsPerHost int
DataProxyMaxIdleConns int
DataProxyKeepAlive int
DataProxyIdleConnTimeout int
// DistributedCache // DistributedCache
RemoteCacheOptions *RemoteCacheOptions RemoteCacheOptions *RemoteCacheOptions
@@ -843,22 +842,8 @@ func (cfg *Cfg) Load(args *CommandLineArgs) error {
return err return err
} }
// read data proxy settings if err := readDataProxySettings(iniFile, cfg); err != nil {
dataproxy := iniFile.Section("dataproxy") return err
DataProxyLogging = dataproxy.Key("logging").MustBool(false)
DataProxyTimeout = dataproxy.Key("timeout").MustInt(10)
DataProxyDialTimeout = dataproxy.Key("dialTimeout").MustInt(30)
DataProxyKeepAlive = dataproxy.Key("keep_alive_seconds").MustInt(30)
DataProxyTLSHandshakeTimeout = dataproxy.Key("tls_handshake_timeout_seconds").MustInt(10)
DataProxyExpectContinueTimeout = dataproxy.Key("expect_continue_timeout_seconds").MustInt(1)
DataProxyMaxConnsPerHost = dataproxy.Key("max_conns_per_host").MustInt(0)
DataProxyMaxIdleConns = dataproxy.Key("max_idle_connections").MustInt()
DataProxyIdleConnTimeout = dataproxy.Key("idle_conn_timeout_seconds").MustInt(90)
cfg.SendUserHeader = dataproxy.Key("send_user_header").MustBool(false)
if val, err := dataproxy.Key("max_idle_connections_per_host").Int(); err == nil {
cfg.Logger.Warn("[Deprecated] the configuration setting 'max_idle_connections_per_host' is deprecated, please use 'max_idle_connections' instead")
DataProxyMaxIdleConns = val
} }
if err := readSecuritySettings(iniFile, cfg); err != nil { if err := readSecuritySettings(iniFile, cfg); err != nil {

View File

@@ -0,0 +1,24 @@
package setting
import "gopkg.in/ini.v1"
func readDataProxySettings(iniFile *ini.File, cfg *Cfg) error {
dataproxy := iniFile.Section("dataproxy")
cfg.SendUserHeader = dataproxy.Key("send_user_header").MustBool(false)
cfg.DataProxyLogging = dataproxy.Key("logging").MustBool(false)
cfg.DataProxyTimeout = dataproxy.Key("timeout").MustInt(10)
cfg.DataProxyDialTimeout = dataproxy.Key("dialTimeout").MustInt(30)
cfg.DataProxyKeepAlive = dataproxy.Key("keep_alive_seconds").MustInt(30)
cfg.DataProxyTLSHandshakeTimeout = dataproxy.Key("tls_handshake_timeout_seconds").MustInt(10)
cfg.DataProxyExpectContinueTimeout = dataproxy.Key("expect_continue_timeout_seconds").MustInt(1)
cfg.DataProxyMaxConnsPerHost = dataproxy.Key("max_conns_per_host").MustInt(0)
cfg.DataProxyMaxIdleConns = dataproxy.Key("max_idle_connections").MustInt()
cfg.DataProxyIdleConnTimeout = dataproxy.Key("idle_conn_timeout_seconds").MustInt(90)
if val, err := dataproxy.Key("max_idle_connections_per_host").Int(); err == nil {
cfg.Logger.Warn("[Deprecated] the configuration setting 'max_idle_connections_per_host' is deprecated, please use 'max_idle_connections' instead")
cfg.DataProxyMaxIdleConns = val
}
return nil
}