github.com/insionng/yougam@v0.0.0-20170714101924-2bc18d833463/libraries/pingcap/tidb/session.go (about)

     1  // Copyright 2013 The ql Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSES/QL-LICENSE file.
     4  
     5  // Copyright 2015 PingCAP, Inc.
     6  //
     7  // Licensed under the Apache License, Version 2.0 (the "License");
     8  // you may not use this file except in compliance with the License.
     9  // You may obtain a copy of the License at
    10  //
    11  //     http://www.apache.org/licenses/LICENSE-2.0
    12  //
    13  // Unless required by applicable law or agreed to in writing, software
    14  // distributed under the License is distributed on an "AS IS" BASIS,
    15  // See the License for the specific language governing permissions and
    16  // limitations under the License.
    17  
    18  package tidb
    19  
    20  import (
    21  	"bytes"
    22  	"encoding/json"
    23  	"fmt"
    24  	"strings"
    25  	"sync"
    26  	"sync/atomic"
    27  	"time"
    28  
    29  	"github.com/insionng/yougam/libraries/juju/errors"
    30  	"github.com/insionng/yougam/libraries/ngaut/log"
    31  	"github.com/insionng/yougam/libraries/pingcap/tidb/ast"
    32  	"github.com/insionng/yougam/libraries/pingcap/tidb/context"
    33  	"github.com/insionng/yougam/libraries/pingcap/tidb/executor"
    34  	"github.com/insionng/yougam/libraries/pingcap/tidb/kv"
    35  	"github.com/insionng/yougam/libraries/pingcap/tidb/meta"
    36  	"github.com/insionng/yougam/libraries/pingcap/tidb/mysql"
    37  	"github.com/insionng/yougam/libraries/pingcap/tidb/perfschema"
    38  	"github.com/insionng/yougam/libraries/pingcap/tidb/privilege"
    39  	"github.com/insionng/yougam/libraries/pingcap/tidb/privilege/privileges"
    40  	"github.com/insionng/yougam/libraries/pingcap/tidb/sessionctx"
    41  	"github.com/insionng/yougam/libraries/pingcap/tidb/sessionctx/autocommit"
    42  	"github.com/insionng/yougam/libraries/pingcap/tidb/sessionctx/db"
    43  	"github.com/insionng/yougam/libraries/pingcap/tidb/sessionctx/forupdate"
    44  	"github.com/insionng/yougam/libraries/pingcap/tidb/sessionctx/variable"
    45  	"github.com/insionng/yougam/libraries/pingcap/tidb/store/localstore"
    46  	"github.com/insionng/yougam/libraries/pingcap/tidb/terror"
    47  	"github.com/insionng/yougam/libraries/pingcap/tidb/util"
    48  	"github.com/insionng/yougam/libraries/pingcap/tidb/util/types"
    49  )
    50  
    51  // Session context
    52  type Session interface {
    53  	Status() uint16                              // Flag of current status, such as autocommit
    54  	LastInsertID() uint64                        // Last inserted auto_increment id
    55  	AffectedRows() uint64                        // Affected rows by latest executed stmt
    56  	Execute(sql string) ([]ast.RecordSet, error) // Execute a sql statement
    57  	String() string                              // For debug
    58  	FinishTxn(rollback bool) error
    59  	// For execute prepare statement in binary protocol
    60  	PrepareStmt(sql string) (stmtID uint32, paramCount int, fields []*ast.ResultField, err error)
    61  	// Execute a prepared statement
    62  	ExecutePreparedStmt(stmtID uint32, param ...interface{}) (ast.RecordSet, error)
    63  	DropPreparedStmt(stmtID uint32) error
    64  	SetClientCapability(uint32) // Set client capability flags
    65  	SetConnectionID(uint64)
    66  	Close() error
    67  	Retry() error
    68  	Auth(user string, auth []byte, salt []byte) bool
    69  }
    70  
    71  var (
    72  	_         Session = (*session)(nil)
    73  	sessionID int64
    74  	sessionMu sync.Mutex
    75  )
    76  
    77  type stmtRecord struct {
    78  	stmtID uint32
    79  	st     ast.Statement
    80  	params []interface{}
    81  }
    82  
    83  type stmtHistory struct {
    84  	history []*stmtRecord
    85  }
    86  
    87  func (h *stmtHistory) add(stmtID uint32, st ast.Statement, params ...interface{}) {
    88  	s := &stmtRecord{
    89  		stmtID: stmtID,
    90  		st:     st,
    91  		params: append(([]interface{})(nil), params...),
    92  	}
    93  	h.history = append(h.history, s)
    94  }
    95  
    96  func (h *stmtHistory) reset() {
    97  	if len(h.history) > 0 {
    98  		h.history = h.history[:0]
    99  	}
   100  }
   101  
   102  func (h *stmtHistory) clone() *stmtHistory {
   103  	nh := *h
   104  	nh.history = make([]*stmtRecord, len(h.history))
   105  	copy(nh.history, h.history)
   106  	return &nh
   107  }
   108  
   109  const unlimitedRetryCnt = -1
   110  
   111  type session struct {
   112  	txn         kv.Transaction // Current transaction
   113  	values      map[fmt.Stringer]interface{}
   114  	store       kv.Storage
   115  	sid         int64
   116  	history     stmtHistory
   117  	initing     bool // Running bootstrap using this session.
   118  	maxRetryCnt int  // Max retry times. If maxRetryCnt <=0, there is no limitation for retry times.
   119  
   120  	debugInfos map[string]interface{} // Vars for debug and unit tests.
   121  
   122  	// For performance_schema only.
   123  	stmtState *perfschema.StatementState
   124  }
   125  
   126  func (s *session) cleanRetryInfo() {
   127  	if !variable.GetSessionVars(s).RetryInfo.Retrying {
   128  		variable.GetSessionVars(s).RetryInfo.Clean()
   129  	}
   130  }
   131  
   132  func (s *session) Status() uint16 {
   133  	return variable.GetSessionVars(s).Status
   134  }
   135  
   136  func (s *session) LastInsertID() uint64 {
   137  	return variable.GetSessionVars(s).LastInsertID
   138  }
   139  
   140  func (s *session) AffectedRows() uint64 {
   141  	return variable.GetSessionVars(s).AffectedRows
   142  }
   143  
   144  func (s *session) resetHistory() {
   145  	s.ClearValue(forupdate.ForUpdateKey)
   146  	s.history.reset()
   147  }
   148  
   149  func (s *session) SetClientCapability(capability uint32) {
   150  	variable.GetSessionVars(s).ClientCapability = capability
   151  }
   152  
   153  func (s *session) SetConnectionID(connectionID uint64) {
   154  	variable.GetSessionVars(s).ConnectionID = connectionID
   155  }
   156  
   157  func (s *session) FinishTxn(rollback bool) error {
   158  	// transaction has already been committed or rolled back
   159  	if s.txn == nil {
   160  		return nil
   161  	}
   162  	defer func() {
   163  		s.txn = nil
   164  		variable.GetSessionVars(s).SetStatusFlag(mysql.ServerStatusInTrans, false)
   165  		// Update tps metrics
   166  		if !variable.GetSessionVars(s).RetryInfo.Retrying {
   167  			tpsMetrics.Add(1)
   168  		}
   169  
   170  	}()
   171  
   172  	if rollback {
   173  		s.resetHistory()
   174  		s.cleanRetryInfo()
   175  		return s.txn.Rollback()
   176  	}
   177  
   178  	err := s.txn.Commit()
   179  	if err != nil {
   180  		if !variable.GetSessionVars(s).RetryInfo.Retrying && kv.IsRetryableError(err) {
   181  			err = s.Retry()
   182  		}
   183  		if err != nil {
   184  			log.Warnf("txn:%s, %v", s.txn, err)
   185  			return errors.Trace(err)
   186  		}
   187  	}
   188  
   189  	s.resetHistory()
   190  	s.cleanRetryInfo()
   191  	return nil
   192  }
   193  
   194  func (s *session) String() string {
   195  	// TODO: how to print binded context in values appropriately?
   196  	data := map[string]interface{}{
   197  		"currDBName": db.GetCurrentSchema(s),
   198  		"sid":        s.sid,
   199  	}
   200  
   201  	if s.txn != nil {
   202  		// if txn is committed or rolled back, txn is nil.
   203  		data["txn"] = s.txn.String()
   204  	}
   205  
   206  	b, _ := json.MarshalIndent(data, "", "  ")
   207  	return string(b)
   208  }
   209  
   210  func (s *session) Retry() error {
   211  	variable.GetSessionVars(s).RetryInfo.Retrying = true
   212  	nh := s.history.clone()
   213  	// Debug infos.
   214  	if len(nh.history) == 0 {
   215  		s.debugInfos[retryEmptyHistoryList] = true
   216  	} else {
   217  		s.debugInfos[retryEmptyHistoryList] = false
   218  	}
   219  	defer func() {
   220  		s.history.history = nh.history
   221  		variable.GetSessionVars(s).RetryInfo.Retrying = false
   222  	}()
   223  
   224  	if forUpdate := s.Value(forupdate.ForUpdateKey); forUpdate != nil {
   225  		return errors.Errorf("can not retry select for update statement")
   226  	}
   227  	var err error
   228  	retryCnt := 0
   229  	for {
   230  		s.resetHistory()
   231  		s.FinishTxn(true)
   232  		success := true
   233  		variable.GetSessionVars(s).RetryInfo.ResetOffset()
   234  		for _, sr := range nh.history {
   235  			st := sr.st
   236  			log.Warnf("Retry %s", st.OriginText())
   237  			_, err = runStmt(s, st)
   238  			if err != nil {
   239  				if kv.IsRetryableError(err) {
   240  					success = false
   241  					break
   242  				}
   243  				log.Warnf("session:%v, err:%v", s, err)
   244  				return errors.Trace(err)
   245  			}
   246  		}
   247  		if success {
   248  			err = s.FinishTxn(false)
   249  			if !kv.IsRetryableError(err) {
   250  				break
   251  			}
   252  		}
   253  		retryCnt++
   254  		if (s.maxRetryCnt != unlimitedRetryCnt) && (retryCnt >= s.maxRetryCnt) {
   255  			return errors.Trace(err)
   256  		}
   257  		kv.BackOff(retryCnt)
   258  	}
   259  	return err
   260  }
   261  
   262  // ExecRestrictedSQL implements SQLHelper interface.
   263  // This is used for executing some restricted sql statements.
   264  func (s *session) ExecRestrictedSQL(ctx context.Context, sql string) (ast.RecordSet, error) {
   265  	rawStmts, err := Parse(ctx, sql)
   266  	if err != nil {
   267  		return nil, errors.Trace(err)
   268  	}
   269  	if len(rawStmts) != 1 {
   270  		log.Errorf("ExecRestrictedSQL only executes one statement. Too many/few statement in %s", sql)
   271  		return nil, errors.New("Wrong number of statement.")
   272  	}
   273  	st, err := Compile(s, rawStmts[0])
   274  	if err != nil {
   275  		log.Errorf("Compile %s with error: %v", sql, err)
   276  		return nil, errors.Trace(err)
   277  	}
   278  	// Check statement for some restriction
   279  	// For example only support DML on system meta table.
   280  	// TODO: Add more restrictions.
   281  	log.Debugf("Executing %s [%s]", st.OriginText(), sql)
   282  	rs, err := st.Exec(ctx)
   283  	return rs, errors.Trace(err)
   284  }
   285  
   286  // getExecRet executes restricted sql and the result is one column.
   287  // It returns a string value.
   288  func (s *session) getExecRet(ctx context.Context, sql string) (string, error) {
   289  	cleanTxn := s.txn == nil
   290  	rs, err := s.ExecRestrictedSQL(ctx, sql)
   291  	if err != nil {
   292  		return "", errors.Trace(err)
   293  	}
   294  	defer rs.Close()
   295  	row, err := rs.Next()
   296  	if err != nil {
   297  		return "", errors.Trace(err)
   298  	}
   299  	if row == nil {
   300  		return "", terror.ExecResultIsEmpty
   301  	}
   302  	value, err := types.ToString(row.Data[0].GetValue())
   303  	if err != nil {
   304  		return "", errors.Trace(err)
   305  	}
   306  	if cleanTxn {
   307  		// This function has some side effect. Run select may create new txn.
   308  		// We should make environment unchanged.
   309  		s.txn = nil
   310  	}
   311  	return value, nil
   312  }
   313  
   314  // GetGlobalSysVar implements GlobalVarAccessor.GetGlobalSysVar interface.
   315  func (s *session) GetGlobalSysVar(ctx context.Context, name string) (string, error) {
   316  	sql := fmt.Sprintf(`SELECT VARIABLE_VALUE FROM %s.%s WHERE VARIABLE_NAME="%s";`,
   317  		mysql.SystemDB, mysql.GlobalVariablesTable, name)
   318  	sysVar, err := s.getExecRet(ctx, sql)
   319  	if err != nil {
   320  		if terror.ExecResultIsEmpty.Equal(err) {
   321  			return "", variable.UnknownSystemVar.Gen("unknown sys variable:%s", name)
   322  		}
   323  		return "", errors.Trace(err)
   324  	}
   325  	return sysVar, nil
   326  }
   327  
   328  // SetGlobalSysVar implements GlobalVarAccessor.SetGlobalSysVar interface.
   329  func (s *session) SetGlobalSysVar(ctx context.Context, name string, value string) error {
   330  	sql := fmt.Sprintf(`UPDATE  %s.%s SET VARIABLE_VALUE="%s" WHERE VARIABLE_NAME="%s";`,
   331  		mysql.SystemDB, mysql.GlobalVariablesTable, value, strings.ToLower(name))
   332  	_, err := s.ExecRestrictedSQL(ctx, sql)
   333  	return errors.Trace(err)
   334  }
   335  
   336  // IsAutocommit checks if it is in the auto-commit mode.
   337  func (s *session) isAutocommit(ctx context.Context) bool {
   338  	autocommit, ok := variable.GetSessionVars(ctx).Systems["autocommit"]
   339  	if !ok {
   340  		if s.initing {
   341  			return false
   342  		}
   343  		var err error
   344  		autocommit, err = s.GetGlobalSysVar(ctx, "autocommit")
   345  		if err != nil {
   346  			log.Errorf("Get global sys var error: %v", err)
   347  			return false
   348  		}
   349  		variable.GetSessionVars(ctx).Systems["autocommit"] = autocommit
   350  		ok = true
   351  	}
   352  	if ok && (autocommit == "ON" || autocommit == "on" || autocommit == "1") {
   353  		variable.GetSessionVars(ctx).SetStatusFlag(mysql.ServerStatusAutocommit, true)
   354  		return true
   355  	}
   356  	variable.GetSessionVars(ctx).SetStatusFlag(mysql.ServerStatusAutocommit, false)
   357  	return false
   358  }
   359  
   360  func (s *session) ShouldAutocommit(ctx context.Context) bool {
   361  	// With START TRANSACTION, autocommit remains disabled until you end
   362  	// the transaction with COMMIT or ROLLBACK.
   363  	if variable.GetSessionVars(ctx).Status&mysql.ServerStatusInTrans == 0 && s.isAutocommit(ctx) {
   364  		return true
   365  	}
   366  	return false
   367  }
   368  
   369  func (s *session) Execute(sql string) ([]ast.RecordSet, error) {
   370  	rawStmts, err := Parse(s, sql)
   371  	if err != nil {
   372  		return nil, errors.Trace(err)
   373  	}
   374  	var rs []ast.RecordSet
   375  	ph := sessionctx.GetDomain(s).PerfSchema()
   376  	for i, rst := range rawStmts {
   377  		st, err1 := Compile(s, rst)
   378  		if err1 != nil {
   379  			log.Errorf("Syntax error: %s", sql)
   380  			log.Errorf("Error occurs at %s.", err1)
   381  			return nil, errors.Trace(err1)
   382  		}
   383  		id := variable.GetSessionVars(s).ConnectionID
   384  		s.stmtState = ph.StartStatement(sql, id, perfschema.CallerNameSessionExecute, rawStmts[i])
   385  		r, err := runStmt(s, st)
   386  		ph.EndStatement(s.stmtState)
   387  		if err != nil {
   388  			log.Warnf("session:%v, err:%v", s, err)
   389  			return nil, errors.Trace(err)
   390  		}
   391  		if r != nil {
   392  			rs = append(rs, r)
   393  		}
   394  	}
   395  	return rs, nil
   396  }
   397  
   398  // For execute prepare statement in binary protocol
   399  func (s *session) PrepareStmt(sql string) (stmtID uint32, paramCount int, fields []*ast.ResultField, err error) {
   400  	prepareExec := &executor.PrepareExec{
   401  		IS:      sessionctx.GetDomain(s).InfoSchema(),
   402  		Ctx:     s,
   403  		SQLText: sql,
   404  	}
   405  	prepareExec.DoPrepare()
   406  	return prepareExec.ID, prepareExec.ParamCount, prepareExec.ResultFields, prepareExec.Err
   407  }
   408  
   409  // checkArgs makes sure all the arguments' types are known and can be handled.
   410  // integer types are converted to int64 and uint64, time.Time is converted to mysql.Time.
   411  // time.Duration is converted to mysql.Duration, other known types are leaved as it is.
   412  func checkArgs(args ...interface{}) error {
   413  	for i, v := range args {
   414  		switch x := v.(type) {
   415  		case bool:
   416  			if x {
   417  				args[i] = int64(1)
   418  			} else {
   419  				args[i] = int64(0)
   420  			}
   421  		case int8:
   422  			args[i] = int64(x)
   423  		case int16:
   424  			args[i] = int64(x)
   425  		case int32:
   426  			args[i] = int64(x)
   427  		case int:
   428  			args[i] = int64(x)
   429  		case uint8:
   430  			args[i] = uint64(x)
   431  		case uint16:
   432  			args[i] = uint64(x)
   433  		case uint32:
   434  			args[i] = uint64(x)
   435  		case uint:
   436  			args[i] = uint64(x)
   437  		case int64:
   438  		case uint64:
   439  		case float32:
   440  		case float64:
   441  		case string:
   442  		case []byte:
   443  		case time.Duration:
   444  			args[i] = mysql.Duration{Duration: x}
   445  		case time.Time:
   446  			args[i] = mysql.Time{Time: x, Type: mysql.TypeDatetime}
   447  		case nil:
   448  		default:
   449  			return errors.Errorf("cannot use arg[%d] (type %T):unsupported type", i, v)
   450  		}
   451  	}
   452  	return nil
   453  }
   454  
   455  // Execute a prepared statement
   456  func (s *session) ExecutePreparedStmt(stmtID uint32, args ...interface{}) (ast.RecordSet, error) {
   457  	err := checkArgs(args...)
   458  	if err != nil {
   459  		return nil, err
   460  	}
   461  	st := executor.CompileExecutePreparedStmt(s, stmtID, args...)
   462  	r, err := runStmt(s, st, args...)
   463  	return r, errors.Trace(err)
   464  }
   465  
   466  func (s *session) DropPreparedStmt(stmtID uint32) error {
   467  	vars := variable.GetSessionVars(s)
   468  	if _, ok := vars.PreparedStmts[stmtID]; !ok {
   469  		return executor.ErrStmtNotFound
   470  	}
   471  	delete(vars.PreparedStmts, stmtID)
   472  	return nil
   473  }
   474  
   475  // If forceNew is true, GetTxn() must return a new transaction.
   476  // In this situation, if current transaction is still in progress,
   477  // there will be an implicit commit and create a new transaction.
   478  func (s *session) GetTxn(forceNew bool) (kv.Transaction, error) {
   479  	var err error
   480  	if s.txn == nil {
   481  		s.resetHistory()
   482  		s.txn, err = s.store.Begin()
   483  		if err != nil {
   484  			return nil, errors.Trace(err)
   485  		}
   486  		if !s.isAutocommit(s) {
   487  			variable.GetSessionVars(s).SetStatusFlag(mysql.ServerStatusInTrans, true)
   488  		}
   489  		log.Infof("New txn:%s in session:%d", s.txn, s.sid)
   490  		return s.txn, nil
   491  	}
   492  	if forceNew {
   493  		err = s.FinishTxn(false)
   494  		if err != nil {
   495  			return nil, errors.Trace(err)
   496  		}
   497  		s.txn, err = s.store.Begin()
   498  		if err != nil {
   499  			return nil, errors.Trace(err)
   500  		}
   501  		if !s.isAutocommit(s) {
   502  			variable.GetSessionVars(s).SetStatusFlag(mysql.ServerStatusInTrans, true)
   503  		}
   504  		log.Warnf("Force new txn:%s in session:%d", s.txn, s.sid)
   505  	}
   506  	return s.txn, nil
   507  }
   508  
   509  func (s *session) SetValue(key fmt.Stringer, value interface{}) {
   510  	s.values[key] = value
   511  }
   512  
   513  func (s *session) Value(key fmt.Stringer) interface{} {
   514  	value := s.values[key]
   515  	return value
   516  }
   517  
   518  func (s *session) ClearValue(key fmt.Stringer) {
   519  	delete(s.values, key)
   520  }
   521  
   522  // Close function does some clean work when session end.
   523  func (s *session) Close() error {
   524  	return s.FinishTxn(true)
   525  }
   526  
   527  func (s *session) getPassword(name, host string) (string, error) {
   528  	// Get password for name and host.
   529  	authSQL := fmt.Sprintf("SELECT Password FROM %s.%s WHERE User='%s' and Host='%s';", mysql.SystemDB, mysql.UserTable, name, host)
   530  	pwd, err := s.getExecRet(s, authSQL)
   531  	if err == nil {
   532  		return pwd, nil
   533  	} else if !terror.ExecResultIsEmpty.Equal(err) {
   534  		return "", errors.Trace(err)
   535  	}
   536  	//Try to get user password for name with any host(%).
   537  	authSQL = fmt.Sprintf("SELECT Password FROM %s.%s WHERE User='%s' and Host='%%';", mysql.SystemDB, mysql.UserTable, name)
   538  	pwd, err = s.getExecRet(s, authSQL)
   539  	return pwd, errors.Trace(err)
   540  }
   541  
   542  func (s *session) Auth(user string, auth []byte, salt []byte) bool {
   543  	strs := strings.Split(user, "@")
   544  	if len(strs) != 2 {
   545  		log.Warnf("Invalid format for user: %s", user)
   546  		return false
   547  	}
   548  	// Get user password.
   549  	name := strs[0]
   550  	host := strs[1]
   551  	pwd, err := s.getPassword(name, host)
   552  	if err != nil {
   553  		if terror.ExecResultIsEmpty.Equal(err) {
   554  			log.Errorf("User [%s] not exist %v", name, err)
   555  		} else {
   556  			log.Errorf("Get User [%s] password from SystemDB error %v", name, err)
   557  		}
   558  		return false
   559  	}
   560  	if len(pwd) != 0 && len(pwd) != 40 {
   561  		log.Errorf("User [%s] password from SystemDB not like a sha1sum", name)
   562  		return false
   563  	}
   564  	hpwd, err := util.DecodePassword(pwd)
   565  	if err != nil {
   566  		log.Errorf("Decode password string error %v", err)
   567  		return false
   568  	}
   569  	checkAuth := util.CalcPassword(salt, hpwd)
   570  	if !bytes.Equal(auth, checkAuth) {
   571  		return false
   572  	}
   573  	variable.GetSessionVars(s).SetCurrentUser(user)
   574  	return true
   575  }
   576  
   577  // Some vars name for debug.
   578  const (
   579  	retryEmptyHistoryList = "RetryEmptyHistoryList"
   580  )
   581  
   582  func chooseMinLease(n1 time.Duration, n2 time.Duration) time.Duration {
   583  	if n1 <= n2 {
   584  		return n1
   585  	}
   586  	return n2
   587  }
   588  
   589  // CreateSession creates a new session environment.
   590  func CreateSession(store kv.Storage) (Session, error) {
   591  	s := &session{
   592  		values:      make(map[fmt.Stringer]interface{}),
   593  		store:       store,
   594  		sid:         atomic.AddInt64(&sessionID, 1),
   595  		debugInfos:  make(map[string]interface{}),
   596  		maxRetryCnt: 10,
   597  	}
   598  	domain, err := domap.Get(store)
   599  	if err != nil {
   600  		return nil, err
   601  	}
   602  	sessionctx.BindDomain(s, domain)
   603  
   604  	variable.BindSessionVars(s)
   605  	variable.GetSessionVars(s).SetStatusFlag(mysql.ServerStatusAutocommit, true)
   606  
   607  	// session implements variable.GlobalVarAccessor. Bind it to ctx.
   608  	variable.BindGlobalVarAccessor(s, s)
   609  
   610  	// session implements autocommit.Checker. Bind it to ctx
   611  	autocommit.BindAutocommitChecker(s, s)
   612  	sessionMu.Lock()
   613  	defer sessionMu.Unlock()
   614  
   615  	ok := isBoostrapped(store)
   616  	if !ok {
   617  		// if no bootstrap and storage is remote, we must use a little lease time to
   618  		// bootstrap quickly, after bootstrapped, we will reset the lease time.
   619  		// TODO: Using a bootstap tool for doing this may be better later.
   620  		if !localstore.IsLocalStore(store) {
   621  			sessionctx.GetDomain(s).SetLease(chooseMinLease(100*time.Millisecond, schemaLease))
   622  		}
   623  
   624  		s.initing = true
   625  		bootstrap(s)
   626  		s.initing = false
   627  
   628  		if !localstore.IsLocalStore(store) {
   629  			sessionctx.GetDomain(s).SetLease(schemaLease)
   630  		}
   631  
   632  		finishBoostrap(store)
   633  	}
   634  
   635  	// TODO: Add auth here
   636  	privChecker := &privileges.UserPrivileges{}
   637  	privilege.BindPrivilegeChecker(s, privChecker)
   638  	return s, nil
   639  }
   640  
   641  func isBoostrapped(store kv.Storage) bool {
   642  	// check in memory
   643  	_, ok := storeBootstrapped[store.UUID()]
   644  	if ok {
   645  		return true
   646  	}
   647  
   648  	// check in kv store
   649  	err := kv.RunInNewTxn(store, false, func(txn kv.Transaction) error {
   650  		var err error
   651  		t := meta.NewMeta(txn)
   652  		ok, err = t.IsBootstrapped()
   653  		return errors.Trace(err)
   654  	})
   655  
   656  	if err != nil {
   657  		log.Fatalf("check bootstrapped err %v", err)
   658  	}
   659  
   660  	if ok {
   661  		// here mean memory is not ok, but other server has already finished it
   662  		storeBootstrapped[store.UUID()] = true
   663  	}
   664  
   665  	return ok
   666  }
   667  
   668  func finishBoostrap(store kv.Storage) {
   669  	storeBootstrapped[store.UUID()] = true
   670  
   671  	err := kv.RunInNewTxn(store, true, func(txn kv.Transaction) error {
   672  		t := meta.NewMeta(txn)
   673  		err := t.FinishBootstrap()
   674  		return errors.Trace(err)
   675  	})
   676  	if err != nil {
   677  		log.Fatalf("finish bootstrap err %v", err)
   678  	}
   679  }