Skip to content

Commit

Permalink
*: add support for -initialize-sql-file on first bootstrap (#35625)
Browse files Browse the repository at this point in the history
close #35624
  • Loading branch information
morgo authored and ti-chi-bot committed Feb 17, 2023
1 parent daee8c5 commit d18f498
Show file tree
Hide file tree
Showing 5 changed files with 118 additions and 4 deletions.
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 @@ -525,6 +526,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 @@ -743,6 +745,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 @@ -2311,6 +2316,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 @@ -1047,6 +1049,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
8 changes: 7 additions & 1 deletion session/session.go
Original file line number Diff line number Diff line change
Expand Up @@ -3359,7 +3359,13 @@ func BootstrapSession(store kv.Storage) (*domain.Domain, error) {
// setup historical stats worker
dom.SetupHistoricalStatsWorker(ses[9])
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 @@ -121,6 +121,7 @@ const (

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

Expand Down Expand Up @@ -165,9 +166,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 @@ -528,7 +530,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 @@ -547,9 +549,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

0 comments on commit d18f498

Please sign in to comment.