Add `-db_connect_timeout_ms` for timing out mysqld conns.

While testing vttablet-mysqld over TCP, we noticed mysql.Connect
sometimes blocks forever if the backend mysqld hit max conns. This
patch adds a timeout to this codepath. It defaults to 0 (no timeout)
for back compatibility.

Signed-off-by: Adam Saponara <as@php.net>
This commit is contained in:
Adam Saponara 2020-02-19 14:00:13 -05:00
Родитель 5ed9f2faf1
Коммит e8b87f9469
4 изменённых файлов: 67 добавлений и 6 удалений

Просмотреть файл

@ -30,11 +30,12 @@ type ConnParams struct {
// The following SSL flags are only used when flags |= 2048
// is set (CapabilityClientSSL).
SslCa string `json:"ssl_ca"`
SslCaPath string `json:"ssl_ca_path"`
SslCert string `json:"ssl_cert"`
SslKey string `json:"ssl_key"`
ServerName string `json:"server_name"`
SslCa string `json:"ssl_ca"`
SslCaPath string `json:"ssl_ca_path"`
SslCert string `json:"ssl_cert"`
SslKey string `json:"ssl_key"`
ServerName string `json:"server_name"`
ConnectTimeoutMs uint64 `json:"connect_timeout_ms"`
// The following is only set when the deprecated "dbname" flags are
// supplied and will be removed.

Просмотреть файл

@ -103,7 +103,7 @@ func registerBaseFlags() {
flag.StringVar(&baseConfig.SslCert, "db_ssl_cert", "", "connection ssl certificate")
flag.StringVar(&baseConfig.SslKey, "db_ssl_key", "", "connection ssl key")
flag.StringVar(&baseConfig.ServerName, "db_server_name", "", "server name of the DB we are connecting to.")
flag.Uint64Var(&baseConfig.ConnectTimeoutMs, "db_connect_timeout_ms", 0, "connection timeout to mysqld in milliseconds (0 for no timeout)")
}
// The flags will change the global singleton
@ -287,6 +287,7 @@ func Init(defaultSocketFile string) (*DBConfigs, error) {
uc.param.SslKey = baseConfig.SslKey
uc.param.ServerName = baseConfig.ServerName
}
uc.param.ConnectTimeoutMs = baseConfig.ConnectTimeoutMs
}
// See if the CredentialsServer is working. We do not use the

Просмотреть файл

@ -261,6 +261,60 @@ func TestInit(t *testing.T) {
}
}
func TestInitTimeout(t *testing.T) {
f := saveDBConfigs()
defer f()
baseConfig = mysql.ConnParams{
Host: "a",
Port: 1,
Uname: "b",
Pass: "c",
DbName: "d",
UnixSocket: "e",
Charset: "f",
Flags: 2,
Flavor: "flavor",
ConnectTimeoutMs: 250,
}
dbConfigs = DBConfigs{
userConfigs: map[string]*userConfig{
App: {
param: mysql.ConnParams{
Uname: "app",
Pass: "apppass",
},
},
},
}
dbc, err := Init("default")
if err != nil {
t.Fatal(err)
}
want := &DBConfigs{
userConfigs: map[string]*userConfig{
App: {
param: mysql.ConnParams{
Host: "a",
Port: 1,
Uname: "app",
Pass: "apppass",
UnixSocket: "e",
Charset: "f",
Flags: 2,
Flavor: "flavor",
ConnectTimeoutMs: 250,
},
},
},
}
if !reflect.DeepEqual(dbc.userConfigs[App].param, want.userConfigs[App].param) {
t.Errorf("dbc: \n%#v, want \n%#v", dbc.userConfigs[App].param, want.userConfigs[App].param)
}
}
func TestAccessors(t *testing.T) {
dbc := &DBConfigs{
userConfigs: map[string]*userConfig{

Просмотреть файл

@ -124,6 +124,11 @@ func NewDBConnection(info *mysql.ConnParams, mysqlStats *stats.Timings) (*DBConn
return nil, err
}
ctx := context.Background()
if info.ConnectTimeoutMs != 0 {
var cancel context.CancelFunc
ctx, cancel = context.WithTimeout(ctx, time.Duration(info.ConnectTimeoutMs)*time.Millisecond)
defer cancel()
}
c, err := mysql.Connect(ctx, params)
if err != nil {
mysqlStats.Record("ConnectError", start)