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 @@ -261,6 +261,9 @@ type Config struct {
// EnableGlobalKill indicates whether to enable global kill.
TrxSummary TrxSummary `toml:"transaction-summary" json:"transaction-summary"`
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
37 changes: 37 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 @@ -526,6 +527,7 @@ func bootstrap(s Session) {
if dom.DDL().OwnerManager().IsOwner() {
doDDLWorks(s)
doDMLWorks(s)
runBootstrapSQLFile = true
logutil.BgLogger().Info("bootstrap successful",
zap.Duration("take time", time.Since(startTime)))
return
Expand Down Expand Up @@ -746,6 +748,9 @@ var currentBootstrapVersion int64 = version109
// DDL owner key's expired time is ManagerSessionTTL seconds, we should wait the time and give more time to have a chance to finish it.
var internalSQLTimeout = owner.ManagerSessionTTL + 15

// whether to run the sql file in bootstrap.
var runBootstrapSQLFile = false

var (
bootstrapVersion = []func(Session, int64){
upgradeToVer2,
Expand Down Expand Up @@ -2321,6 +2326,38 @@ func doDDLWorks(s Session) {
mustExecute(s, CreateTTLTableStatus)
}

// 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
ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnBootstrap)
if sqlFile == "" {
return
}
logutil.BgLogger().Info("executing -initialize-sql-file", zap.String("file", sqlFile))
b, err := ioutil.ReadFile(sqlFile) //nolint:gosec
if err != nil {
logutil.BgLogger().Fatal("unable to read InitializeSQLFile", zap.Error(err))
}
stmts, err := s.Parse(ctx, string(b))
if err != nil {
logutil.BgLogger().Fatal("unable to parse InitializeSQLFile", zap.Error(err))
}
for _, stmt := range stmts {
rs, err := s.ExecuteStmt(ctx, 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.
if err := rs.Close(); err != nil {
logutil.BgLogger().Fatal("unable to close result", zap.Error(err))
}
}
}
}

// 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
56 changes: 56 additions & 0 deletions session/bootstrap_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,14 @@ package session
import (
"context"
"fmt"
"os"
"strconv"
"strings"
"testing"
"time"

"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 @@ -1043,6 +1045,60 @@ func TestUpgradeToVer85(t *testing.T) {
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, err := exec(se, `SHOW VARIABLES LIKE 'query_cache_type'`)
require.NoError(t, err)
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, err = exec(se, `SHOW VARIABLES LIKE 'tidb_enable_noop_variables'`)
require.NoError(t, err)
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())
}

func TestTiDBEnablePagingVariable(t *testing.T) {
store, dom := createStoreAndBootstrap(t)
se := createSessionAndSetID(t, store)
Expand Down
10 changes: 8 additions & 2 deletions session/session.go
Original file line number Diff line number Diff line change
Expand Up @@ -3297,7 +3297,7 @@ func BootstrapSession(store kv.Storage) (*domain.Domain, error) {

analyzeConcurrencyQuota := int(config.GetGlobalConfig().Performance.AnalyzePartitionConcurrencyQuota)
concurrency := int(config.GetGlobalConfig().Performance.StatsLoadConcurrency)
ses, err := createSessions(store, 9)
ses, err := createSessions(store, 10)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -3397,7 +3397,13 @@ func BootstrapSession(store kv.Storage) (*domain.Domain, error) {
// setup historical stats worker
dom.SetupHistoricalStatsWorker(ses[8])
dom.StartHistoricalStatsWorker()

if runBootstrapSQLFile {
pm := &privileges.UserPrivileges{
Handle: dom.PrivilegeHandle(),
}
privilege.BindPrivilegeManager(ses[9], pm)
doBootstrapSQLFile(ses[9])
}
// A sub context for update table stats, and other contexts for concurrent stats loading.
cnt := 1 + concurrency
syncStatsCtxs, err := createSessions(store, cnt)
Expand Down
18 changes: 15 additions & 3 deletions tidb-server/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ const (

nmInitializeSecure = "initialize-secure"
nmInitializeInsecure = "initialize-insecure"
nmInitializeSQLFile = "initialize-sql-file"
nmDisconnectOnExpiredPassword = "disconnect-on-expired-password"
)

Expand Down Expand Up @@ -166,9 +167,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. (Deprecated: as proxy protocol using lazy mode, header read timeout no longer used)")

// 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")
disconnectOnExpiredPassword = flagBoolean(nmDisconnectOnExpiredPassword, true, "the server disconnects the client when the password is expired")
)

Expand Down Expand Up @@ -531,7 +533,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 @@ -550,9 +552,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