diff --git a/config/config.go b/config/config.go index 48e069bcfb50b..0dc6cabc75727 100644 --- a/config/config.go +++ b/config/config.go @@ -117,6 +117,7 @@ var ( map[string]string{ "check-mb4-value-in-utf8": "tidb_check_mb4_value_in_utf8", "enable-collect-execution-info": "tidb_enable_collect_execution_info", + "max-server-connections": "max_connections", }, }, { @@ -474,6 +475,7 @@ type Instance struct { EnableCollectExecutionInfo bool `toml:"tidb_enable_collect_execution_info" json:"tidb_enable_collect_execution_info"` PluginDir string `toml:"plugin_dir" json:"plugin_dir"` PluginLoad string `toml:"plugin_load" json:"plugin_load"` + MaxConnections uint32 `toml:"max_connections" json:"max_connections"` } func (l *Log) getDisableTimestamp() bool { @@ -850,6 +852,7 @@ var defaultConf = Config{ EnableCollectExecutionInfo: true, PluginDir: "/data/deploy/plugin", PluginLoad: "", + MaxConnections: 0, }, Status: Status{ ReportStatus: true, diff --git a/config/config.toml.example b/config/config.toml.example index 1e1fc7aae73f5..afc97e60e74f8 100644 --- a/config/config.toml.example +++ b/config/config.toml.example @@ -89,9 +89,6 @@ repair-mode = false # In repair mode, repairing table which is not in repair list will get wrong database or wrong table error. repair-table-list = [] -# The maximum permitted number of simultaneous client connections. When the value is 0, the number of connections is unlimited. -max-server-connections = 0 - # Whether new collations are enabled, as indicated by its name, this configuration entry take effect ONLY when a TiDB cluster bootstraps for the first time. new_collations_enabled_on_first_bootstrap = true @@ -468,3 +465,6 @@ tidb_slow_log_threshold = 300 # tidb_record_plan_in_slow_log is used to enable record query plan in slow log. # 0 is disable. 1 is enable. tidb_record_plan_in_slow_log = 1 + +# The maximum permitted number of simultaneous client connections. When the value is 0, the number of connections is unlimited. +max_connections = 0 diff --git a/config/config_test.go b/config/config_test.go index 391bd874d3942..2e044062bd4e8 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -282,9 +282,6 @@ repair-mode = false # In repair mode, repairing table which is not in repair list will get wrong database or wrong table error. repair-table-list = [] -# The maximum permitted number of simultaneous client connections. When the value is 0, the number of connections is unlimited. -max-server-connections = 0 - # Whether new collations are enabled, as indicated by its name, this configuration entry take effect ONLY when a TiDB cluster bootstraps for the first time. new_collations_enabled_on_first_bootstrap = true @@ -309,6 +306,11 @@ deprecate-integer-display-length = false # See https://dev.mysql.com/doc/refman/8.0/en/string-type-syntax.html for more details. enable-enum-length-limit = true +[instance] + +# The maximum permitted number of simultaneous client connections. When the value is 0, the number of connections is unlimited. +max_connections = 0 + [log] # Log level: debug, info, warn, error, fatal. level = "info" @@ -707,7 +709,7 @@ unrecognized-option-test = true match, err := regexp.Match("(?:.|\n)*invalid configuration option(?:.|\n)*", []byte(err.Error())) require.NoError(t, err) require.True(t, match) - require.Equal(t, uint32(0), conf.MaxServerConnections) + require.Equal(t, uint32(0), conf.Instance.MaxConnections) err = f.Truncate(0) require.NoError(t, err) @@ -722,7 +724,6 @@ delay-clean-table-lock = 5 split-region-max-num=10000 server-version = "test_version" repair-mode = true -max-server-connections = 200 max-index-length = 3080 index-limit = 70 table-column-count-limit = 4000 @@ -768,6 +769,8 @@ grpc-keepalive-timeout = 10 grpc-concurrent-streams = 2048 grpc-initial-window-size = 10240 grpc-max-send-msg-size = 40960 +[instance] +max_connections = 200 `) require.NoError(t, err) @@ -797,7 +800,7 @@ grpc-max-send-msg-size = 40960 require.Equal(t, uint64(10000), conf.SplitRegionMaxNum) require.True(t, conf.RepairMode) require.Equal(t, uint64(16), conf.TiKVClient.ResolveLockLiteThreshold) - require.Equal(t, uint32(200), conf.MaxServerConnections) + require.Equal(t, uint32(200), conf.Instance.MaxConnections) require.Equal(t, []string{"tiflash"}, conf.IsolationRead.Engines) require.Equal(t, 3080, conf.MaxIndexLength) require.Equal(t, 70, conf.IndexLimit) diff --git a/executor/set_test.go b/executor/set_test.go index eb171e872d8c4..9a7213571fddc 100644 --- a/executor/set_test.go +++ b/executor/set_test.go @@ -1021,16 +1021,17 @@ func TestValidateSetVar(t *testing.T) { result.Check(testkit.Rows("SYSTEM")) // The following cases test value out of range and illegal type when setting system variables. - // See https://dev.mysql.com/doc/refman/5.7/en/server-system-variables.html for more details. + // See https://dev.mysql.com/doc/refman/8.0/en/server-system-variables.html for more details. tk.MustExec("set @@global.max_connections=100001") tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", "Warning|1292|Truncated incorrect max_connections value: '100001'")) result = tk.MustQuery("select @@global.max_connections;") result.Check(testkit.Rows("100000")) + // "max_connections == 0" means there is no limitation on the number of connections. tk.MustExec("set @@global.max_connections=-1") tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", "Warning|1292|Truncated incorrect max_connections value: '-1'")) result = tk.MustQuery("select @@global.max_connections;") - result.Check(testkit.Rows("1")) + result.Check(testkit.Rows("0")) err = tk.ExecToErr("set @@global.max_connections='hello'") require.True(t, terror.ErrorEqual(err, variable.ErrWrongTypeForVar)) @@ -1077,7 +1078,7 @@ func TestValidateSetVar(t *testing.T) { tk.MustExec("set @@global.max_connections=-1") tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", "Warning|1292|Truncated incorrect max_connections value: '-1'")) result = tk.MustQuery("select @@global.max_connections;") - result.Check(testkit.Rows("1")) + result.Check(testkit.Rows("0")) err = tk.ExecToErr("set @@global.max_connections='hello'") require.True(t, terror.ErrorEqual(err, variable.ErrWrongTypeForVar)) @@ -1333,15 +1334,15 @@ func TestSelectGlobalVar(t *testing.T) { defer clean() tk := testkit.NewTestKit(t, store) - tk.MustQuery("select @@global.max_connections;").Check(testkit.Rows("151")) - tk.MustQuery("select @@max_connections;").Check(testkit.Rows("151")) + tk.MustQuery("select @@global.max_connections;").Check(testkit.Rows("0")) + tk.MustQuery("select @@max_connections;").Check(testkit.Rows("0")) tk.MustExec("set @@global.max_connections=100;") tk.MustQuery("select @@global.max_connections;").Check(testkit.Rows("100")) tk.MustQuery("select @@max_connections;").Check(testkit.Rows("100")) - tk.MustExec("set @@global.max_connections=151;") + tk.MustExec("set @@global.max_connections=0;") // test for unknown variable. err := tk.ExecToErr("select @@invalid") diff --git a/expression/integration_test.go b/expression/integration_test.go index 8c10fc96b5d30..b3dc43fe084d1 100644 --- a/expression/integration_test.go +++ b/expression/integration_test.go @@ -6233,11 +6233,11 @@ func TestGlobalCacheCorrectness(t *testing.T) { defer clean() tk := testkit.NewTestKit(t, store) - tk.MustQuery("SHOW VARIABLES LIKE 'max_connections'").Check(testkit.Rows("max_connections 151")) + tk.MustQuery("SHOW VARIABLES LIKE 'max_connections'").Check(testkit.Rows("max_connections 0")) tk.MustExec("SET GLOBAL max_connections=1234") tk.MustQuery("SHOW VARIABLES LIKE 'max_connections'").Check(testkit.Rows("max_connections 1234")) // restore - tk.MustExec("SET GLOBAL max_connections=151") + tk.MustExec("SET GLOBAL max_connections=0") } func TestRedundantColumnResolve(t *testing.T) { diff --git a/server/server.go b/server/server.go index 90b4a7e27101b..13ed052391f59 100644 --- a/server/server.go +++ b/server/server.go @@ -352,7 +352,7 @@ func setTxnScope() { // Export config-related metrics func (s *Server) reportConfig() { metrics.ConfigStatus.WithLabelValues("token-limit").Set(float64(s.cfg.TokenLimit)) - metrics.ConfigStatus.WithLabelValues("max-server-connections").Set(float64(s.cfg.MaxServerConnections)) + metrics.ConfigStatus.WithLabelValues("max_connections").Set(float64(s.cfg.Instance.MaxConnections)) } // Run runs the server. @@ -514,11 +514,18 @@ func (s *Server) onConn(conn *clientConn) { }) terror.Log(err) } - if errors.Cause(err) == io.EOF { + switch errors.Cause(err) { + case io.EOF: // `EOF` means the connection is closed normally, we do not treat it as a noticeable error and log it in 'DEBUG' level. logutil.BgLogger().With(zap.Uint64("conn", conn.connectionID)). Debug("EOF", zap.String("remote addr", conn.bufReadConn.RemoteAddr().String())) - } else { + case errConCount: + if err := conn.writeError(ctx, err); err != nil { + logutil.BgLogger().With(zap.Uint64("conn", conn.connectionID)). + Warn("error in writing errConCount", zap.Error(err), + zap.String("remote addr", conn.bufReadConn.RemoteAddr().String())) + } + default: metrics.HandShakeErrorCounter.Inc() logutil.BgLogger().With(zap.Uint64("conn", conn.connectionID)). Warn("Server.onConn handshake", zap.Error(err), @@ -605,8 +612,8 @@ func (cc *clientConn) connectInfo() *variable.ConnectionInfo { } func (s *Server) checkConnectionCount() error { - // When the value of MaxServerConnections is 0, the number of connections is unlimited. - if int(s.cfg.MaxServerConnections) == 0 { + // When the value of Instance.MaxConnections is 0, the number of connections is unlimited. + if int(s.cfg.Instance.MaxConnections) == 0 { return nil } @@ -614,9 +621,9 @@ func (s *Server) checkConnectionCount() error { conns := len(s.clients) s.rwlock.RUnlock() - if conns >= int(s.cfg.MaxServerConnections) { + if conns >= int(s.cfg.Instance.MaxConnections) { logutil.BgLogger().Error("too many connections", - zap.Uint32("max connections", s.cfg.MaxServerConnections), zap.Error(errConCount)) + zap.Uint32("max connections", s.cfg.Instance.MaxConnections), zap.Error(errConCount)) return errConCount } return nil diff --git a/sessionctx/variable/noop.go b/sessionctx/variable/noop.go index 4f2cdac1aa690..6eb70beabfc99 100644 --- a/sessionctx/variable/noop.go +++ b/sessionctx/variable/noop.go @@ -24,7 +24,6 @@ import ( // but changing them has no effect on behavior. var noopSysVars = []*SysVar{ - {Scope: ScopeGlobal, Name: MaxConnections, Value: "151", Type: TypeUnsigned, MinValue: 1, MaxValue: 100000}, // It is unsafe to pretend that any variation of "read only" is enabled when the server // does not support it. It is possible that these features will be supported in future, // but until then... diff --git a/sessionctx/variable/sysvar.go b/sessionctx/variable/sysvar.go index aa91b574dc517..2e7865ac57040 100644 --- a/sessionctx/variable/sysvar.go +++ b/sessionctx/variable/sysvar.go @@ -431,6 +431,12 @@ var defaultSysVars = []*SysVar{ {Scope: ScopeInstance, Name: PluginDir, Value: "/data/deploy/plugin", ReadOnly: true, GetGlobal: func(s *SessionVars) (string, error) { return config.GetGlobalConfig().Instance.PluginDir, nil }}, + {Scope: ScopeInstance, Name: MaxConnections, Value: strconv.FormatUint(uint64(config.GetGlobalConfig().Instance.MaxConnections), 10), Type: TypeUnsigned, MinValue: 0, MaxValue: 100000, SetGlobal: func(s *SessionVars, val string) error { + config.GetGlobalConfig().Instance.MaxConnections = uint32(TidbOptInt64(val, 0)) + return nil + }, GetGlobal: func(s *SessionVars) (string, error) { + return strconv.FormatUint(uint64(config.GetGlobalConfig().Instance.MaxConnections), 10), nil + }}, /* The system variables below have GLOBAL scope */ {Scope: ScopeGlobal, Name: MaxPreparedStmtCount, Value: strconv.FormatInt(DefMaxPreparedStmtCount, 10), Type: TypeInt, MinValue: -1, MaxValue: 1048576}, diff --git a/tidb-server/main.go b/tidb-server/main.go index 763d4bd5bc6a8..bc8795875f360 100644 --- a/tidb-server/main.go +++ b/tidb-server/main.go @@ -559,6 +559,8 @@ func setGlobalVars() { cfg.Instance.CheckMb4ValueInUTF8.Store(cfg.CheckMb4ValueInUTF8.Load()) case "enable-collect-execution-info": cfg.Instance.EnableCollectExecutionInfo = cfg.EnableCollectExecutionInfo + case "max-server-connections": + cfg.Instance.MaxConnections = cfg.MaxServerConnections } case "log": switch oldName {