Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

*: add support for -initialize-sql-file on first bootstrap #35625

Merged
merged 11 commits into from
Dec 28, 2022
3 changes: 3 additions & 0 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,9 @@ type Config struct {
BallastObjectSize int `toml:"ballast-object-size" json:"ballast-object-size"`
// EnableGlobalKill indicates whether to enable global kill.
EnableGlobalKill bool `toml:"enable-global-kill" json:"enable-global-kill"`
// InitializeSQLFile is a file that will be executed after first bootstrap only.
// It can be used to set GLOBAL system variable values
InitializeSQLFile string `toml:"initialize-sql-file" json:"initialize-sql-file"`

// The following items are deprecated. We need to keep them here temporarily
// to support the upgrade process. They can be removed in future.
Expand Down
31 changes: 31 additions & 0 deletions session/bootstrap.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (
"encoding/hex"
"flag"
"fmt"
"io/ioutil"
osuser "os/user"
"runtime/debug"
"strconv"
Expand Down Expand Up @@ -444,6 +445,7 @@ func bootstrap(s Session) {
if dom.DDL().OwnerManager().IsOwner() {
doDDLWorks(s)
doDMLWorks(s)
doBootstrapSQLFile(s)
logutil.BgLogger().Info("bootstrap successful",
zap.Duration("take time", time.Since(startTime)))
return
Expand Down Expand Up @@ -1971,6 +1973,35 @@ func doDDLWorks(s Session) {
mustExecute(s, CreateAdvisoryLocks)
}

// doBootstrapSQLFile executes SQL commands in a file as the last stage of bootstrap.
// It is useful for setting the initial value of GLOBAL variables.
func doBootstrapSQLFile(s Session) {
sqlFile := config.GetGlobalConfig().InitializeSQLFile
if sqlFile == "" {
return
}
logutil.BgLogger().Info("executing -initialize-sql-file", zap.String("file", sqlFile))
b, err := ioutil.ReadFile(sqlFile)
if err != nil {
logutil.BgLogger().Fatal("unable to read InitializeSQLFile", zap.Error(err))
}
stmts, err := s.Parse(context.Background(), string(b))
if err != nil {
logutil.BgLogger().Fatal("unable to parse InitializeSQLFile", zap.Error(err))
}
for _, stmt := range stmts {
rs, err := s.ExecuteStmt(context.Background(), stmt)
if err != nil {
logutil.BgLogger().Warn("InitializeSQLFile error", zap.Error(err))
}
if rs != nil {
// I don't believe we need to drain the result-set in bootstrap mode
// but if required we can do this here in future.
rs.Close()
}
}
}

// inTestSuite checks if we are bootstrapping in the context of tests.
// There are some historical differences in behavior between tests and non-tests.
func inTestSuite() bool {
Expand Down
54 changes: 54 additions & 0 deletions session/bootstrap_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,13 @@ package session
import (
"context"
"fmt"
"os"
"strconv"
"strings"
"testing"

"github.com/pingcap/tidb/bindinfo"
"github.com/pingcap/tidb/config"
"github.com/pingcap/tidb/domain"
"github.com/pingcap/tidb/meta"
"github.com/pingcap/tidb/parser/auth"
Expand Down Expand Up @@ -1025,3 +1027,55 @@ func TestUpgradeToVer85(t *testing.T) {
require.NoError(t, r.Close())
mustExec(t, se, "delete from mysql.bind_info where default_db = 'test'")
}

func TestInitializeSQLFile(t *testing.T) {
// We create an initialize-sql-file and then bootstrap the server with it.
// The observed behavior should be that tidb_enable_noop_variables is now
// disabled, and the feature works as expected.
initializeSQLFile, err := os.CreateTemp("", "init.sql")
require.NoError(t, err)
defer func() {
path := initializeSQLFile.Name()
err = initializeSQLFile.Close()
require.NoError(t, err)
err = os.Remove(path)
require.NoError(t, err)
}()
// Implicitly test multi-line init files
_, err = initializeSQLFile.WriteString(
"CREATE DATABASE initsqlfiletest;\n" +
"SET GLOBAL tidb_enable_noop_variables = OFF;\n")
require.NoError(t, err)

// Create a mock store
// Set the config parameter for initialize sql file
store, err := mockstore.NewMockStore()
require.NoError(t, err)
config.GetGlobalConfig().InitializeSQLFile = initializeSQLFile.Name()
defer func() {
require.NoError(t, store.Close())
config.GetGlobalConfig().InitializeSQLFile = ""
}()

// Bootstrap with the InitializeSQLFile config option
dom, err := BootstrapSession(store)
require.NoError(t, err)
defer dom.Close()
se := createSessionAndSetID(t, store)
ctx := context.Background()
r := mustExec(t, se, `SHOW VARIABLES LIKE 'query_cache_type'`)
req := r.NewChunk(nil)
err = r.Next(ctx, req)
require.NoError(t, err)
require.Equal(t, 0, req.NumRows()) // not shown in noopvariables mode
require.NoError(t, r.Close())

r = mustExec(t, se, `SHOW VARIABLES LIKE 'tidb_enable_noop_variables'`)
req = r.NewChunk(nil)
err = r.Next(ctx, req)
require.NoError(t, err)
require.Equal(t, 1, req.NumRows())
row := req.GetRow(0)
require.Equal(t, []byte("OFF"), row.GetBytes(1))
require.NoError(t, r.Close())
}
18 changes: 15 additions & 3 deletions tidb-server/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ const (

nmInitializeSecure = "initialize-secure"
nmInitializeInsecure = "initialize-insecure"
nmInitializeSQLFile = "initialize-sql-file"
)

var (
Expand Down Expand Up @@ -156,9 +157,10 @@ var (
proxyProtocolNetworks = flag.String(nmProxyProtocolNetworks, "", "proxy protocol networks allowed IP or *, empty mean disable proxy protocol support")
proxyProtocolHeaderTimeout = flag.Uint(nmProxyProtocolHeaderTimeout, 5, "proxy protocol header read timeout, unit is second.")

// Security
// Bootstrap and security
initializeSecure = flagBoolean(nmInitializeSecure, false, "bootstrap tidb-server in secure mode")
initializeInsecure = flagBoolean(nmInitializeInsecure, true, "bootstrap tidb-server in insecure mode")
initializeSQLFile = flag.String(nmInitializeSQLFile, "", "SQL file to execute on first bootstrap")
)

func main() {
Expand Down Expand Up @@ -511,7 +513,7 @@ func overrideConfig(cfg *config.Config) {

// Sanity check: can't specify both options
if actualFlags[nmInitializeSecure] && actualFlags[nmInitializeInsecure] {
err = fmt.Errorf("the options --initialize-insecure and --initialize-secure are mutually exclusive")
err = fmt.Errorf("the options -initialize-insecure and -initialize-secure are mutually exclusive")
terror.MustNil(err)
}
// The option --initialize-secure=true ensures that a secure bootstrap is used.
Expand All @@ -527,9 +529,19 @@ func overrideConfig(cfg *config.Config) {
// which is not supported on windows. Only the insecure bootstrap
// method is supported.
if runtime.GOOS == "windows" && cfg.Security.SecureBootstrap {
err = fmt.Errorf("the option --initialize-secure is not supported on Windows")
err = fmt.Errorf("the option -initialize-secure is not supported on Windows")
terror.MustNil(err)
}
// Initialize SQL File is used to run a set of SQL statements after first bootstrap.
// It is important in the use case that you want to set GLOBAL variables, which
// are persisted to the cluster and not read from a config file.
if actualFlags[nmInitializeSQLFile] {
if _, err := os.Stat(*initializeSQLFile); err != nil {
err = fmt.Errorf("can not access -initialize-sql-file %s", *initializeSQLFile)
terror.MustNil(err)
}
cfg.InitializeSQLFile = *initializeSQLFile
}
}

func setVersions() {
Expand Down