vitess.io/vitess@v0.16.2/go/vt/vttablet/tabletserver/tabletserver.go (about)

     1  /*
     2  Copyright 2019 The Vitess Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package tabletserver
    18  
    19  import (
    20  	"bytes"
    21  	"context"
    22  	"encoding/json"
    23  	"fmt"
    24  	"net/http"
    25  	"os"
    26  	"os/signal"
    27  	"sort"
    28  	"strconv"
    29  	"strings"
    30  	"sync"
    31  	"syscall"
    32  	"time"
    33  
    34  	"google.golang.org/protobuf/proto"
    35  
    36  	"vitess.io/vitess/go/acl"
    37  	"vitess.io/vitess/go/mysql"
    38  	"vitess.io/vitess/go/pools"
    39  	"vitess.io/vitess/go/sqltypes"
    40  	"vitess.io/vitess/go/stats"
    41  	"vitess.io/vitess/go/sync2"
    42  	"vitess.io/vitess/go/tb"
    43  	"vitess.io/vitess/go/trace"
    44  	"vitess.io/vitess/go/vt/callerid"
    45  	"vitess.io/vitess/go/vt/dbconfigs"
    46  	"vitess.io/vitess/go/vt/log"
    47  	"vitess.io/vitess/go/vt/logutil"
    48  	"vitess.io/vitess/go/vt/mysqlctl"
    49  	"vitess.io/vitess/go/vt/servenv"
    50  	"vitess.io/vitess/go/vt/sqlparser"
    51  	"vitess.io/vitess/go/vt/srvtopo"
    52  	"vitess.io/vitess/go/vt/tableacl"
    53  	"vitess.io/vitess/go/vt/topo"
    54  	"vitess.io/vitess/go/vt/topo/topoproto"
    55  	"vitess.io/vitess/go/vt/vterrors"
    56  	"vitess.io/vitess/go/vt/vttablet/onlineddl"
    57  	"vitess.io/vitess/go/vt/vttablet/queryservice"
    58  	"vitess.io/vitess/go/vt/vttablet/tabletserver/gc"
    59  	"vitess.io/vitess/go/vt/vttablet/tabletserver/messager"
    60  	"vitess.io/vitess/go/vt/vttablet/tabletserver/planbuilder"
    61  	"vitess.io/vitess/go/vt/vttablet/tabletserver/repltracker"
    62  	"vitess.io/vitess/go/vt/vttablet/tabletserver/rules"
    63  	"vitess.io/vitess/go/vt/vttablet/tabletserver/schema"
    64  	"vitess.io/vitess/go/vt/vttablet/tabletserver/tabletenv"
    65  	"vitess.io/vitess/go/vt/vttablet/tabletserver/throttle"
    66  	"vitess.io/vitess/go/vt/vttablet/tabletserver/txserializer"
    67  	"vitess.io/vitess/go/vt/vttablet/tabletserver/txthrottler"
    68  	"vitess.io/vitess/go/vt/vttablet/tabletserver/vstreamer"
    69  	"vitess.io/vitess/go/vt/vttablet/vexec"
    70  
    71  	binlogdatapb "vitess.io/vitess/go/vt/proto/binlogdata"
    72  	querypb "vitess.io/vitess/go/vt/proto/query"
    73  	topodatapb "vitess.io/vitess/go/vt/proto/topodata"
    74  	vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc"
    75  )
    76  
    77  // logPoolFull is for throttling transaction / query pool full messages in the log.
    78  var logPoolFull = logutil.NewThrottledLogger("PoolFull", 1*time.Minute)
    79  
    80  var logComputeRowSerializerKey = logutil.NewThrottledLogger("ComputeRowSerializerKey", 1*time.Minute)
    81  
    82  // TabletServer implements the RPC interface for the query service.
    83  // TabletServer is initialized in the following sequence:
    84  // NewTabletServer->InitDBConfig->SetServingType.
    85  // Subcomponents of TabletServer are initialized using one of the
    86  // following sequences:
    87  // New->InitDBConfig->Init->Open, or New->InitDBConfig->Open.
    88  // Essentially, InitDBConfig is a continuation of New. However,
    89  // the db config is not initially available. For this reason,
    90  // the initialization is done in two phases.
    91  // Some subcomponents have Init functions. Such functions usually
    92  // perform one-time initializations and must be idempotent.
    93  // Open and Close can be called repeatedly during the lifetime of
    94  // a subcomponent. These should also be idempotent.
    95  type TabletServer struct {
    96  	exporter               *servenv.Exporter
    97  	config                 *tabletenv.TabletConfig
    98  	stats                  *tabletenv.Stats
    99  	QueryTimeout           sync2.AtomicDuration
   100  	TerseErrors            bool
   101  	enableHotRowProtection bool
   102  	topoServer             *topo.Server
   103  
   104  	// These are sub-components of TabletServer.
   105  	statelessql  *QueryList
   106  	statefulql   *QueryList
   107  	olapql       *QueryList
   108  	se           *schema.Engine
   109  	rt           *repltracker.ReplTracker
   110  	vstreamer    *vstreamer.Engine
   111  	tracker      *schema.Tracker
   112  	watcher      *BinlogWatcher
   113  	qe           *QueryEngine
   114  	txThrottler  *txthrottler.TxThrottler
   115  	te           *TxEngine
   116  	messager     *messager.Engine
   117  	hs           *healthStreamer
   118  	lagThrottler *throttle.Throttler
   119  	tableGC      *gc.TableGC
   120  
   121  	// sm manages state transitions.
   122  	sm                *stateManager
   123  	onlineDDLExecutor *onlineddl.Executor
   124  
   125  	// alias is used for identifying this tabletserver in healthcheck responses.
   126  	alias *topodatapb.TabletAlias
   127  
   128  	// This field is only stored for testing
   129  	checkMysqlGaugeFunc *stats.GaugeFunc
   130  }
   131  
   132  var _ queryservice.QueryService = (*TabletServer)(nil)
   133  
   134  // RegisterFunctions is a list of all the
   135  // RegisterFunction that will be called upon
   136  // Register() on a TabletServer
   137  var RegisterFunctions []func(Controller)
   138  
   139  // NewServer creates a new TabletServer based on the command line flags.
   140  func NewServer(name string, topoServer *topo.Server, alias *topodatapb.TabletAlias) *TabletServer {
   141  	return NewTabletServer(name, tabletenv.NewCurrentConfig(), topoServer, alias)
   142  }
   143  
   144  var (
   145  	tsOnce        sync.Once
   146  	srvTopoServer srvtopo.Server
   147  )
   148  
   149  // NewTabletServer creates an instance of TabletServer. Only the first
   150  // instance of TabletServer will expose its state variables.
   151  func NewTabletServer(name string, config *tabletenv.TabletConfig, topoServer *topo.Server, alias *topodatapb.TabletAlias) *TabletServer {
   152  	exporter := servenv.NewExporter(name, "Tablet")
   153  	tsv := &TabletServer{
   154  		exporter:               exporter,
   155  		stats:                  tabletenv.NewStats(exporter),
   156  		config:                 config,
   157  		QueryTimeout:           sync2.NewAtomicDuration(config.Oltp.QueryTimeoutSeconds.Get()),
   158  		TerseErrors:            config.TerseErrors,
   159  		enableHotRowProtection: config.HotRowProtection.Mode != tabletenv.Disable,
   160  		topoServer:             topoServer,
   161  		alias:                  proto.Clone(alias).(*topodatapb.TabletAlias),
   162  	}
   163  
   164  	tsOnce.Do(func() { srvTopoServer = srvtopo.NewResilientServer(topoServer, "TabletSrvTopo") })
   165  
   166  	tabletTypeFunc := func() topodatapb.TabletType {
   167  		if tsv.sm == nil {
   168  			return topodatapb.TabletType_UNKNOWN
   169  		}
   170  		return tsv.sm.Target().TabletType
   171  	}
   172  
   173  	tsv.statelessql = NewQueryList("oltp-stateless")
   174  	tsv.statefulql = NewQueryList("oltp-stateful")
   175  	tsv.olapql = NewQueryList("olap")
   176  	tsv.hs = newHealthStreamer(tsv, alias)
   177  	tsv.se = schema.NewEngine(tsv)
   178  	tsv.rt = repltracker.NewReplTracker(tsv, alias)
   179  	tsv.lagThrottler = throttle.NewThrottler(tsv, srvTopoServer, topoServer, alias.Cell, tsv.rt.HeartbeatWriter(), tabletTypeFunc)
   180  	tsv.vstreamer = vstreamer.NewEngine(tsv, srvTopoServer, tsv.se, tsv.lagThrottler, alias.Cell)
   181  	tsv.tracker = schema.NewTracker(tsv, tsv.vstreamer, tsv.se)
   182  	tsv.watcher = NewBinlogWatcher(tsv, tsv.vstreamer, tsv.config)
   183  	tsv.qe = NewQueryEngine(tsv, tsv.se)
   184  	tsv.txThrottler = txthrottler.NewTxThrottler(tsv.config, topoServer)
   185  	tsv.te = NewTxEngine(tsv)
   186  	tsv.messager = messager.NewEngine(tsv, tsv.se, tsv.vstreamer)
   187  
   188  	tsv.onlineDDLExecutor = onlineddl.NewExecutor(tsv, alias, topoServer, tsv.lagThrottler, tabletTypeFunc, tsv.onlineDDLExecutorToggleTableBuffer)
   189  	tsv.tableGC = gc.NewTableGC(tsv, topoServer, tsv.lagThrottler)
   190  
   191  	tsv.sm = &stateManager{
   192  		statelessql: tsv.statelessql,
   193  		statefulql:  tsv.statefulql,
   194  		olapql:      tsv.olapql,
   195  		hs:          tsv.hs,
   196  		se:          tsv.se,
   197  		rt:          tsv.rt,
   198  		vstreamer:   tsv.vstreamer,
   199  		tracker:     tsv.tracker,
   200  		watcher:     tsv.watcher,
   201  		qe:          tsv.qe,
   202  		txThrottler: tsv.txThrottler,
   203  		te:          tsv.te,
   204  		messager:    tsv.messager,
   205  		ddle:        tsv.onlineDDLExecutor,
   206  		throttler:   tsv.lagThrottler,
   207  		tableGC:     tsv.tableGC,
   208  	}
   209  
   210  	tsv.exporter.NewGaugeFunc("TabletState", "Tablet server state", func() int64 { return int64(tsv.sm.State()) })
   211  	tsv.checkMysqlGaugeFunc = tsv.exporter.NewGaugeFunc("CheckMySQLRunning", "Check MySQL operation currently in progress", tsv.sm.isCheckMySQLRunning)
   212  	tsv.exporter.Publish("TabletStateName", stats.StringFunc(tsv.sm.IsServingString))
   213  
   214  	// TabletServerState exports the same information as the above two stats (TabletState / TabletStateName),
   215  	// but exported with TabletStateName as a label for Prometheus, which doesn't support exporting strings as stat values.
   216  	tsv.exporter.NewGaugesFuncWithMultiLabels("TabletServerState", "Tablet server state labeled by state name", []string{"name"}, func() map[string]int64 {
   217  		return map[string]int64{tsv.sm.IsServingString(): 1}
   218  	})
   219  	tsv.exporter.NewGaugeDurationFunc("QueryTimeout", "Tablet server query timeout", tsv.QueryTimeout.Get)
   220  
   221  	tsv.registerHealthzHealthHandler()
   222  	tsv.registerDebugHealthHandler()
   223  	tsv.registerQueryzHandler()
   224  	tsv.registerQueryListHandlers([]*QueryList{tsv.statelessql, tsv.statefulql, tsv.olapql})
   225  	tsv.registerTwopczHandler()
   226  	tsv.registerMigrationStatusHandler()
   227  	tsv.registerThrottlerHandlers()
   228  	tsv.registerDebugEnvHandler()
   229  
   230  	return tsv
   231  }
   232  
   233  // onlineDDLExecutorToggleTableBuffer is called by onlineDDLExecutor as a callback function. onlineDDLExecutor
   234  // uses it to start/stop query buffering for a given table.
   235  // It is onlineDDLExecutor's responsibility to make sure beffering is stopped after some definite amount of time.
   236  // There are two layers to buffering/unbuffering:
   237  //  1. the creation and destruction of a QueryRuleSource. The existence of such source affects query plan rules
   238  //     for all new queries (see Execute() function and call to GetPlan())
   239  //  2. affecting already existing rules: a Rule has a concext.WithCancel, that is cancelled by onlineDDLExecutor
   240  func (tsv *TabletServer) onlineDDLExecutorToggleTableBuffer(bufferingCtx context.Context, tableName string, bufferQueries bool) {
   241  	queryRuleSource := fmt.Sprintf("onlineddl/%s", tableName)
   242  
   243  	if bufferQueries {
   244  		tsv.RegisterQueryRuleSource(queryRuleSource)
   245  		bufferRules := rules.New()
   246  		bufferRules.Add(rules.NewBufferedTableQueryRule(bufferingCtx, tableName, "buffered for cut-over"))
   247  		tsv.SetQueryRules(queryRuleSource, bufferRules)
   248  	} else {
   249  		tsv.UnRegisterQueryRuleSource(queryRuleSource) // new rules will not have buffering. Existing rules will be affected by bufferingContext.Done()
   250  	}
   251  }
   252  
   253  // InitDBConfig initializes the db config variables for TabletServer. You must call this function
   254  // to complete the creation of TabletServer.
   255  func (tsv *TabletServer) InitDBConfig(target *querypb.Target, dbcfgs *dbconfigs.DBConfigs, mysqld mysqlctl.MysqlDaemon) error {
   256  	if tsv.sm.State() != StateNotConnected {
   257  		return vterrors.NewErrorf(vtrpcpb.Code_UNAVAILABLE, vterrors.ServerNotAvailable, "Server isn't available")
   258  	}
   259  	tsv.sm.Init(tsv, target)
   260  	tsv.sm.target = proto.Clone(target).(*querypb.Target)
   261  	tsv.config.DB = dbcfgs
   262  
   263  	tsv.se.InitDBConfig(tsv.config.DB.DbaWithDB())
   264  	tsv.rt.InitDBConfig(target, mysqld)
   265  	tsv.txThrottler.InitDBConfig(target)
   266  	tsv.vstreamer.InitDBConfig(target.Keyspace, target.Shard)
   267  	tsv.hs.InitDBConfig(target, tsv.config.DB.DbaWithDB())
   268  	tsv.onlineDDLExecutor.InitDBConfig(target.Keyspace, target.Shard, dbcfgs.DBName)
   269  	tsv.lagThrottler.InitDBConfig(target.Keyspace, target.Shard)
   270  	tsv.tableGC.InitDBConfig(target.Keyspace, target.Shard, dbcfgs.DBName)
   271  	return nil
   272  }
   273  
   274  // Register prepares TabletServer for serving by calling
   275  // all the registrations functions.
   276  func (tsv *TabletServer) Register() {
   277  	for _, f := range RegisterFunctions {
   278  		f(tsv)
   279  	}
   280  }
   281  
   282  // Exporter satisfies tabletenv.Env.
   283  func (tsv *TabletServer) Exporter() *servenv.Exporter {
   284  	return tsv.exporter
   285  }
   286  
   287  // Config satisfies tabletenv.Env.
   288  func (tsv *TabletServer) Config() *tabletenv.TabletConfig {
   289  	return tsv.config
   290  }
   291  
   292  // Stats satisfies tabletenv.Env.
   293  func (tsv *TabletServer) Stats() *tabletenv.Stats {
   294  	return tsv.stats
   295  }
   296  
   297  // LogError satisfies tabletenv.Env.
   298  func (tsv *TabletServer) LogError() {
   299  	if x := recover(); x != nil {
   300  		log.Errorf("Uncaught panic:\n%v\n%s", x, tb.Stack(4))
   301  		tsv.stats.InternalErrors.Add("Panic", 1)
   302  	}
   303  }
   304  
   305  // RegisterQueryRuleSource registers ruleSource for setting query rules.
   306  func (tsv *TabletServer) RegisterQueryRuleSource(ruleSource string) {
   307  	tsv.qe.queryRuleSources.RegisterSource(ruleSource)
   308  }
   309  
   310  // UnRegisterQueryRuleSource unregisters ruleSource from query rules.
   311  func (tsv *TabletServer) UnRegisterQueryRuleSource(ruleSource string) {
   312  	tsv.qe.queryRuleSources.UnRegisterSource(ruleSource)
   313  }
   314  
   315  // SetQueryRules sets the query rules for a registered ruleSource.
   316  func (tsv *TabletServer) SetQueryRules(ruleSource string, qrs *rules.Rules) error {
   317  	err := tsv.qe.queryRuleSources.SetRules(ruleSource, qrs)
   318  	if err != nil {
   319  		return err
   320  	}
   321  	tsv.qe.ClearQueryPlanCache()
   322  	return nil
   323  }
   324  
   325  func (tsv *TabletServer) initACL(tableACLConfigFile string, enforceTableACLConfig bool) {
   326  	// tabletacl.Init loads ACL from file if *tableACLConfig is not empty
   327  	err := tableacl.Init(
   328  		tableACLConfigFile,
   329  		func() {
   330  			tsv.ClearQueryPlanCache()
   331  		},
   332  	)
   333  	if err != nil {
   334  		log.Errorf("Fail to initialize Table ACL: %v", err)
   335  		if enforceTableACLConfig {
   336  			log.Exit("Need a valid initial Table ACL when enforce-tableacl-config is set, exiting.")
   337  		}
   338  	}
   339  }
   340  
   341  // InitACL loads the table ACL and sets up a SIGHUP handler for reloading it.
   342  func (tsv *TabletServer) InitACL(tableACLConfigFile string, enforceTableACLConfig bool, reloadACLConfigFileInterval time.Duration) {
   343  	tsv.initACL(tableACLConfigFile, enforceTableACLConfig)
   344  
   345  	sigChan := make(chan os.Signal, 1)
   346  	signal.Notify(sigChan, syscall.SIGHUP)
   347  	go func() {
   348  		for range sigChan {
   349  			tsv.initACL(tableACLConfigFile, enforceTableACLConfig)
   350  		}
   351  	}()
   352  
   353  	if reloadACLConfigFileInterval != 0 {
   354  		ticker := time.NewTicker(reloadACLConfigFileInterval)
   355  		go func() {
   356  			for range ticker.C {
   357  				sigChan <- syscall.SIGHUP
   358  			}
   359  		}()
   360  	}
   361  }
   362  
   363  // SetServingType changes the serving type of the tabletserver. It starts or
   364  // stops internal services as deemed necessary.
   365  // Returns true if the state of QueryService or the tablet type changed.
   366  func (tsv *TabletServer) SetServingType(tabletType topodatapb.TabletType, terTimestamp time.Time, serving bool, reason string) error {
   367  	state := StateNotServing
   368  	if serving {
   369  		state = StateServing
   370  	}
   371  	return tsv.sm.SetServingType(tabletType, terTimestamp, state, reason)
   372  }
   373  
   374  // StartService is a convenience function for InitDBConfig->SetServingType
   375  // with serving=true.
   376  func (tsv *TabletServer) StartService(target *querypb.Target, dbcfgs *dbconfigs.DBConfigs, mysqld mysqlctl.MysqlDaemon) error {
   377  	if err := tsv.InitDBConfig(target, dbcfgs, mysqld); err != nil {
   378  		return err
   379  	}
   380  	// StartService is only used for testing. So, we cheat by aggressively setting replication to healthy.
   381  	return tsv.sm.SetServingType(target.TabletType, time.Time{}, StateServing, "")
   382  }
   383  
   384  // StopService shuts down the tabletserver to the uninitialized state.
   385  // It first transitions to StateShuttingDown, then waits for active
   386  // services to shut down. Then it shuts down the rest. This function
   387  // should be called before process termination, or if MySQL is unreachable.
   388  // Under normal circumstances, SetServingType should be called.
   389  func (tsv *TabletServer) StopService() {
   390  	tsv.sm.StopService()
   391  }
   392  
   393  // IsHealthy returns nil for non-serving types or if the query service is healthy (able to
   394  // connect to the database and serving traffic), or an error explaining
   395  // the unhealthiness otherwise.
   396  func (tsv *TabletServer) IsHealthy() error {
   397  	if topoproto.IsServingType(tsv.sm.Target().TabletType) {
   398  		_, err := tsv.Execute(
   399  			tabletenv.LocalContext(),
   400  			nil,
   401  			"/* health */ select 1 from dual",
   402  			nil,
   403  			0,
   404  			0,
   405  			nil,
   406  		)
   407  		return err
   408  	}
   409  	return nil
   410  }
   411  
   412  // ReloadSchema reloads the schema.
   413  func (tsv *TabletServer) ReloadSchema(ctx context.Context) error {
   414  	return tsv.se.Reload(ctx)
   415  }
   416  
   417  // WaitForSchemaReset blocks the TabletServer until there's been at least `timeout` duration without
   418  // any schema changes. This is useful for tests that need to wait for all the currently existing schema
   419  // changes to finish being applied.
   420  func (tsv *TabletServer) WaitForSchemaReset(timeout time.Duration) {
   421  	onSchemaChange := make(chan struct{}, 1)
   422  	tsv.se.RegisterNotifier("_tsv_wait", func(_ map[string]*schema.Table, _, _, _ []string) {
   423  		onSchemaChange <- struct{}{}
   424  	})
   425  	defer tsv.se.UnregisterNotifier("_tsv_wait")
   426  
   427  	after := time.NewTimer(timeout)
   428  	defer after.Stop()
   429  
   430  	for {
   431  		select {
   432  		case <-after.C:
   433  			return
   434  		case <-onSchemaChange:
   435  			if !after.Stop() {
   436  				<-after.C
   437  			}
   438  			after.Reset(timeout)
   439  		}
   440  	}
   441  }
   442  
   443  // ClearQueryPlanCache clears internal query plan cache
   444  func (tsv *TabletServer) ClearQueryPlanCache() {
   445  	// We should ideally bracket this with start & endErequest,
   446  	// but query plan cache clearing is safe to call even if the
   447  	// tabletserver is down.
   448  	tsv.qe.ClearQueryPlanCache()
   449  }
   450  
   451  // QueryService returns the QueryService part of TabletServer.
   452  func (tsv *TabletServer) QueryService() queryservice.QueryService {
   453  	return tsv
   454  }
   455  
   456  // OnlineDDLExecutor returns the onlineddl.Executor part of TabletServer.
   457  func (tsv *TabletServer) OnlineDDLExecutor() vexec.Executor {
   458  	return tsv.onlineDDLExecutor
   459  }
   460  
   461  // LagThrottler returns the throttle.Throttler part of TabletServer.
   462  func (tsv *TabletServer) LagThrottler() *throttle.Throttler {
   463  	return tsv.lagThrottler
   464  }
   465  
   466  // TableGC returns the tableDropper part of TabletServer.
   467  func (tsv *TabletServer) TableGC() *gc.TableGC {
   468  	return tsv.tableGC
   469  }
   470  
   471  // TwoPCEngineWait waits until the TwoPC engine has been opened, and the redo read
   472  func (tsv *TabletServer) TwoPCEngineWait() {
   473  	tsv.te.twoPCReady.Wait()
   474  }
   475  
   476  // SchemaEngine returns the SchemaEngine part of TabletServer.
   477  func (tsv *TabletServer) SchemaEngine() *schema.Engine {
   478  	return tsv.se
   479  }
   480  
   481  // Begin starts a new transaction. This is allowed only if the state is StateServing.
   482  func (tsv *TabletServer) Begin(ctx context.Context, target *querypb.Target, options *querypb.ExecuteOptions) (state queryservice.TransactionState, err error) {
   483  	return tsv.begin(ctx, target, nil, 0, nil, options)
   484  }
   485  
   486  func (tsv *TabletServer) begin(ctx context.Context, target *querypb.Target, savepointQueries []string, reservedID int64, settings []string, options *querypb.ExecuteOptions) (state queryservice.TransactionState, err error) {
   487  	state.TabletAlias = tsv.alias
   488  	err = tsv.execRequest(
   489  		ctx, tsv.QueryTimeout.Get(),
   490  		"Begin", "begin", nil,
   491  		target, options, false, /* allowOnShutdown */
   492  		func(ctx context.Context, logStats *tabletenv.LogStats) error {
   493  			startTime := time.Now()
   494  			if tsv.txThrottler.Throttle() {
   495  				return vterrors.Errorf(vtrpcpb.Code_RESOURCE_EXHAUSTED, "Transaction throttled")
   496  			}
   497  			var connSetting *pools.Setting
   498  			if len(settings) > 0 {
   499  				connSetting, err = tsv.qe.GetConnSetting(ctx, settings)
   500  				if err != nil {
   501  					return err
   502  				}
   503  			}
   504  			transactionID, beginSQL, sessionStateChanges, err := tsv.te.Begin(ctx, savepointQueries, reservedID, connSetting, options)
   505  			state.TransactionID = transactionID
   506  			state.SessionStateChanges = sessionStateChanges
   507  			logStats.TransactionID = transactionID
   508  			logStats.ReservedID = reservedID
   509  
   510  			// Record the actual statements that were executed in the logStats.
   511  			// If nothing was actually executed, don't count the operation in
   512  			// the tablet metrics, and clear out the logStats Method so that
   513  			// handlePanicAndSendLogStats doesn't log the no-op.
   514  			logStats.OriginalSQL = beginSQL
   515  			if beginSQL != "" {
   516  				tsv.stats.QueryTimings.Record("BEGIN", startTime)
   517  			} else {
   518  				logStats.Method = ""
   519  			}
   520  			return err
   521  		},
   522  	)
   523  	return state, err
   524  }
   525  
   526  // Commit commits the specified transaction.
   527  func (tsv *TabletServer) Commit(ctx context.Context, target *querypb.Target, transactionID int64) (newReservedID int64, err error) {
   528  	err = tsv.execRequest(
   529  		ctx, tsv.QueryTimeout.Get(),
   530  		"Commit", "commit", nil,
   531  		target, nil, true, /* allowOnShutdown */
   532  		func(ctx context.Context, logStats *tabletenv.LogStats) error {
   533  			startTime := time.Now()
   534  			logStats.TransactionID = transactionID
   535  
   536  			var commitSQL string
   537  			newReservedID, commitSQL, err = tsv.te.Commit(ctx, transactionID)
   538  			if newReservedID > 0 {
   539  				// commit executed on old reserved id.
   540  				logStats.ReservedID = transactionID
   541  			}
   542  
   543  			// If nothing was actually executed, don't count the operation in
   544  			// the tablet metrics, and clear out the logStats Method so that
   545  			// handlePanicAndSendLogStats doesn't log the no-op.
   546  			if commitSQL != "" {
   547  				tsv.stats.QueryTimings.Record("COMMIT", startTime)
   548  			} else {
   549  				logStats.Method = ""
   550  			}
   551  			return err
   552  		},
   553  	)
   554  	return newReservedID, err
   555  }
   556  
   557  // Rollback rollsback the specified transaction.
   558  func (tsv *TabletServer) Rollback(ctx context.Context, target *querypb.Target, transactionID int64) (newReservedID int64, err error) {
   559  	err = tsv.execRequest(
   560  		ctx, tsv.QueryTimeout.Get(),
   561  		"Rollback", "rollback", nil,
   562  		target, nil, true, /* allowOnShutdown */
   563  		func(ctx context.Context, logStats *tabletenv.LogStats) error {
   564  			defer tsv.stats.QueryTimings.Record("ROLLBACK", time.Now())
   565  			logStats.TransactionID = transactionID
   566  			newReservedID, err = tsv.te.Rollback(ctx, transactionID)
   567  			if newReservedID > 0 {
   568  				// rollback executed on old reserved id.
   569  				logStats.ReservedID = transactionID
   570  			}
   571  			return err
   572  		},
   573  	)
   574  	return newReservedID, err
   575  }
   576  
   577  // Prepare prepares the specified transaction.
   578  func (tsv *TabletServer) Prepare(ctx context.Context, target *querypb.Target, transactionID int64, dtid string) (err error) {
   579  	return tsv.execRequest(
   580  		ctx, tsv.QueryTimeout.Get(),
   581  		"Prepare", "prepare", nil,
   582  		target, nil, true, /* allowOnShutdown */
   583  		func(ctx context.Context, logStats *tabletenv.LogStats) error {
   584  			txe := &TxExecutor{
   585  				ctx:      ctx,
   586  				logStats: logStats,
   587  				te:       tsv.te,
   588  			}
   589  			return txe.Prepare(transactionID, dtid)
   590  		},
   591  	)
   592  }
   593  
   594  // CommitPrepared commits the prepared transaction.
   595  func (tsv *TabletServer) CommitPrepared(ctx context.Context, target *querypb.Target, dtid string) (err error) {
   596  	return tsv.execRequest(
   597  		ctx, tsv.QueryTimeout.Get(),
   598  		"CommitPrepared", "commit_prepared", nil,
   599  		target, nil, true, /* allowOnShutdown */
   600  		func(ctx context.Context, logStats *tabletenv.LogStats) error {
   601  			txe := &TxExecutor{
   602  				ctx:      ctx,
   603  				logStats: logStats,
   604  				te:       tsv.te,
   605  			}
   606  			return txe.CommitPrepared(dtid)
   607  		},
   608  	)
   609  }
   610  
   611  // RollbackPrepared commits the prepared transaction.
   612  func (tsv *TabletServer) RollbackPrepared(ctx context.Context, target *querypb.Target, dtid string, originalID int64) (err error) {
   613  	return tsv.execRequest(
   614  		ctx, tsv.QueryTimeout.Get(),
   615  		"RollbackPrepared", "rollback_prepared", nil,
   616  		target, nil, true, /* allowOnShutdown */
   617  		func(ctx context.Context, logStats *tabletenv.LogStats) error {
   618  			txe := &TxExecutor{
   619  				ctx:      ctx,
   620  				logStats: logStats,
   621  				te:       tsv.te,
   622  			}
   623  			return txe.RollbackPrepared(dtid, originalID)
   624  		},
   625  	)
   626  }
   627  
   628  // CreateTransaction creates the metadata for a 2PC transaction.
   629  func (tsv *TabletServer) CreateTransaction(ctx context.Context, target *querypb.Target, dtid string, participants []*querypb.Target) (err error) {
   630  	return tsv.execRequest(
   631  		ctx, tsv.QueryTimeout.Get(),
   632  		"CreateTransaction", "create_transaction", nil,
   633  		target, nil, true, /* allowOnShutdown */
   634  		func(ctx context.Context, logStats *tabletenv.LogStats) error {
   635  			txe := &TxExecutor{
   636  				ctx:      ctx,
   637  				logStats: logStats,
   638  				te:       tsv.te,
   639  			}
   640  			return txe.CreateTransaction(dtid, participants)
   641  		},
   642  	)
   643  }
   644  
   645  // StartCommit atomically commits the transaction along with the
   646  // decision to commit the associated 2pc transaction.
   647  func (tsv *TabletServer) StartCommit(ctx context.Context, target *querypb.Target, transactionID int64, dtid string) (err error) {
   648  	return tsv.execRequest(
   649  		ctx, tsv.QueryTimeout.Get(),
   650  		"StartCommit", "start_commit", nil,
   651  		target, nil, true, /* allowOnShutdown */
   652  		func(ctx context.Context, logStats *tabletenv.LogStats) error {
   653  			txe := &TxExecutor{
   654  				ctx:      ctx,
   655  				logStats: logStats,
   656  				te:       tsv.te,
   657  			}
   658  			return txe.StartCommit(transactionID, dtid)
   659  		},
   660  	)
   661  }
   662  
   663  // SetRollback transitions the 2pc transaction to the Rollback state.
   664  // If a transaction id is provided, that transaction is also rolled back.
   665  func (tsv *TabletServer) SetRollback(ctx context.Context, target *querypb.Target, dtid string, transactionID int64) (err error) {
   666  	return tsv.execRequest(
   667  		ctx, tsv.QueryTimeout.Get(),
   668  		"SetRollback", "set_rollback", nil,
   669  		target, nil, true, /* allowOnShutdown */
   670  		func(ctx context.Context, logStats *tabletenv.LogStats) error {
   671  			txe := &TxExecutor{
   672  				ctx:      ctx,
   673  				logStats: logStats,
   674  				te:       tsv.te,
   675  			}
   676  			return txe.SetRollback(dtid, transactionID)
   677  		},
   678  	)
   679  }
   680  
   681  // ConcludeTransaction deletes the 2pc transaction metadata
   682  // essentially resolving it.
   683  func (tsv *TabletServer) ConcludeTransaction(ctx context.Context, target *querypb.Target, dtid string) (err error) {
   684  	return tsv.execRequest(
   685  		ctx, tsv.QueryTimeout.Get(),
   686  		"ConcludeTransaction", "conclude_transaction", nil,
   687  		target, nil, true, /* allowOnShutdown */
   688  		func(ctx context.Context, logStats *tabletenv.LogStats) error {
   689  			txe := &TxExecutor{
   690  				ctx:      ctx,
   691  				logStats: logStats,
   692  				te:       tsv.te,
   693  			}
   694  			return txe.ConcludeTransaction(dtid)
   695  		},
   696  	)
   697  }
   698  
   699  // ReadTransaction returns the metadata for the specified dtid.
   700  func (tsv *TabletServer) ReadTransaction(ctx context.Context, target *querypb.Target, dtid string) (metadata *querypb.TransactionMetadata, err error) {
   701  	err = tsv.execRequest(
   702  		ctx, tsv.QueryTimeout.Get(),
   703  		"ReadTransaction", "read_transaction", nil,
   704  		target, nil, true, /* allowOnShutdown */
   705  		func(ctx context.Context, logStats *tabletenv.LogStats) error {
   706  			txe := &TxExecutor{
   707  				ctx:      ctx,
   708  				logStats: logStats,
   709  				te:       tsv.te,
   710  			}
   711  			metadata, err = txe.ReadTransaction(dtid)
   712  			return err
   713  		},
   714  	)
   715  	return metadata, err
   716  }
   717  
   718  // Execute executes the query and returns the result as response.
   719  func (tsv *TabletServer) Execute(ctx context.Context, target *querypb.Target, sql string, bindVariables map[string]*querypb.BindVariable, transactionID, reservedID int64, options *querypb.ExecuteOptions) (result *sqltypes.Result, err error) {
   720  	span, ctx := trace.NewSpan(ctx, "TabletServer.Execute")
   721  	trace.AnnotateSQL(span, sqlparser.Preview(sql))
   722  	defer span.Finish()
   723  
   724  	if transactionID != 0 && reservedID != 0 && transactionID != reservedID {
   725  		return nil, vterrors.New(vtrpcpb.Code_INTERNAL, "[BUG] transactionID and reserveID must match if both are non-zero")
   726  	}
   727  
   728  	return tsv.execute(ctx, target, sql, bindVariables, transactionID, reservedID, nil, options)
   729  }
   730  
   731  func (tsv *TabletServer) execute(ctx context.Context, target *querypb.Target, sql string, bindVariables map[string]*querypb.BindVariable, transactionID int64, reservedID int64, settings []string, options *querypb.ExecuteOptions) (result *sqltypes.Result, err error) {
   732  	allowOnShutdown := false
   733  	timeout := tsv.QueryTimeout.Get()
   734  	if transactionID != 0 {
   735  		allowOnShutdown = true
   736  		// Execute calls happen for OLTP only, so we can directly fetch the
   737  		// OLTP TX timeout.
   738  		txTimeout := tsv.config.TxTimeoutForWorkload(querypb.ExecuteOptions_OLTP)
   739  		// Use the smaller of the two values (0 means infinity).
   740  		// TODO(sougou): Assign deadlines to each transaction and set query timeout accordingly.
   741  		timeout = smallerTimeout(timeout, txTimeout)
   742  	}
   743  	err = tsv.execRequest(
   744  		ctx, timeout,
   745  		"Execute", sql, bindVariables,
   746  		target, options, allowOnShutdown,
   747  		func(ctx context.Context, logStats *tabletenv.LogStats) error {
   748  			if bindVariables == nil {
   749  				bindVariables = make(map[string]*querypb.BindVariable)
   750  			}
   751  			query, comments := sqlparser.SplitMarginComments(sql)
   752  			plan, err := tsv.qe.GetPlan(ctx, logStats, query, skipQueryPlanCache(options))
   753  			if err != nil {
   754  				return err
   755  			}
   756  			if err = plan.IsValid(reservedID != 0, len(settings) > 0); err != nil {
   757  				return err
   758  			}
   759  			// If both the values are non-zero then by design they are same value. So, it is safe to overwrite.
   760  			connID := reservedID
   761  			if transactionID != 0 {
   762  				connID = transactionID
   763  			}
   764  			logStats.ReservedID = reservedID
   765  			logStats.TransactionID = transactionID
   766  
   767  			var connSetting *pools.Setting
   768  			if len(settings) > 0 {
   769  				connSetting, err = tsv.qe.GetConnSetting(ctx, settings)
   770  				if err != nil {
   771  					return err
   772  				}
   773  			}
   774  			qre := &QueryExecutor{
   775  				query:          query,
   776  				marginComments: comments,
   777  				bindVars:       bindVariables,
   778  				connID:         connID,
   779  				options:        options,
   780  				plan:           plan,
   781  				ctx:            ctx,
   782  				logStats:       logStats,
   783  				tsv:            tsv,
   784  				tabletType:     target.GetTabletType(),
   785  				setting:        connSetting,
   786  			}
   787  			result, err = qre.Execute()
   788  			if err != nil {
   789  				return err
   790  			}
   791  			result = result.StripMetadata(sqltypes.IncludeFieldsOrDefault(options))
   792  
   793  			// Change database name in mysql output to the keyspace name
   794  			if tsv.sm.target.Keyspace != tsv.config.DB.DBName && sqltypes.IncludeFieldsOrDefault(options) == querypb.ExecuteOptions_ALL {
   795  				switch qre.plan.PlanID {
   796  				case planbuilder.PlanSelect, planbuilder.PlanSelectImpossible:
   797  					dbName := tsv.config.DB.DBName
   798  					ksName := tsv.sm.target.Keyspace
   799  					for _, f := range result.Fields {
   800  						if f.Database == dbName {
   801  							f.Database = ksName
   802  						}
   803  					}
   804  				}
   805  			}
   806  			return nil
   807  		},
   808  	)
   809  	return result, err
   810  }
   811  
   812  // smallerTimeout returns the smaller of the two timeouts.
   813  // 0 is treated as infinity.
   814  func smallerTimeout(t1, t2 time.Duration) time.Duration {
   815  	if t1 == 0 {
   816  		return t2
   817  	}
   818  	if t2 == 0 {
   819  		return t1
   820  	}
   821  	if t1 < t2 {
   822  		return t1
   823  	}
   824  	return t2
   825  }
   826  
   827  // StreamExecute executes the query and streams the result.
   828  // The first QueryResult will have Fields set (and Rows nil).
   829  // The subsequent QueryResult will have Rows set (and Fields nil).
   830  func (tsv *TabletServer) StreamExecute(ctx context.Context, target *querypb.Target, sql string, bindVariables map[string]*querypb.BindVariable, transactionID int64, reservedID int64, options *querypb.ExecuteOptions, callback func(*sqltypes.Result) error) (err error) {
   831  	if transactionID != 0 && reservedID != 0 && transactionID != reservedID {
   832  		return vterrors.New(vtrpcpb.Code_INTERNAL, "[BUG] transactionID and reserveID must match if both are non-zero")
   833  	}
   834  
   835  	return tsv.streamExecute(ctx, target, sql, bindVariables, transactionID, reservedID, nil, options, callback)
   836  }
   837  
   838  func (tsv *TabletServer) streamExecute(ctx context.Context, target *querypb.Target, sql string, bindVariables map[string]*querypb.BindVariable, transactionID int64, reservedID int64, settings []string, options *querypb.ExecuteOptions, callback func(*sqltypes.Result) error) error {
   839  	allowOnShutdown := false
   840  	var timeout time.Duration
   841  	if transactionID != 0 {
   842  		allowOnShutdown = true
   843  		// Use the transaction timeout. StreamExecute calls happen for OLAP only,
   844  		// so we can directly fetch the OLAP TX timeout.
   845  		timeout = tsv.config.TxTimeoutForWorkload(querypb.ExecuteOptions_OLAP)
   846  	}
   847  
   848  	return tsv.execRequest(
   849  		ctx, timeout,
   850  		"StreamExecute", sql, bindVariables,
   851  		target, options, allowOnShutdown,
   852  		func(ctx context.Context, logStats *tabletenv.LogStats) error {
   853  			if bindVariables == nil {
   854  				bindVariables = make(map[string]*querypb.BindVariable)
   855  			}
   856  			query, comments := sqlparser.SplitMarginComments(sql)
   857  			plan, err := tsv.qe.GetStreamPlan(query)
   858  			if err != nil {
   859  				return err
   860  			}
   861  			if err = plan.IsValid(reservedID != 0, len(settings) > 0); err != nil {
   862  				return err
   863  			}
   864  			// If both the values are non-zero then by design they are same value. So, it is safe to overwrite.
   865  			connID := reservedID
   866  			if transactionID != 0 {
   867  				connID = transactionID
   868  			}
   869  			logStats.ReservedID = reservedID
   870  			logStats.TransactionID = transactionID
   871  
   872  			var connSetting *pools.Setting
   873  			if len(settings) > 0 {
   874  				connSetting, err = tsv.qe.GetConnSetting(ctx, settings)
   875  				if err != nil {
   876  					return err
   877  				}
   878  			}
   879  			qre := &QueryExecutor{
   880  				query:          query,
   881  				marginComments: comments,
   882  				bindVars:       bindVariables,
   883  				connID:         connID,
   884  				options:        options,
   885  				plan:           plan,
   886  				ctx:            ctx,
   887  				logStats:       logStats,
   888  				tsv:            tsv,
   889  				setting:        connSetting,
   890  			}
   891  			return qre.Stream(callback)
   892  		},
   893  	)
   894  }
   895  
   896  // BeginExecute combines Begin and Execute.
   897  func (tsv *TabletServer) BeginExecute(ctx context.Context, target *querypb.Target, preQueries []string, sql string, bindVariables map[string]*querypb.BindVariable, reservedID int64, options *querypb.ExecuteOptions) (queryservice.TransactionState, *sqltypes.Result, error) {
   898  
   899  	// Disable hot row protection in case of reserve connection.
   900  	if tsv.enableHotRowProtection && reservedID == 0 {
   901  		txDone, err := tsv.beginWaitForSameRangeTransactions(ctx, target, options, sql, bindVariables)
   902  		if err != nil {
   903  			return queryservice.TransactionState{}, nil, err
   904  		}
   905  		if txDone != nil {
   906  			defer txDone()
   907  		}
   908  	}
   909  
   910  	state, err := tsv.begin(ctx, target, preQueries, reservedID, nil, options)
   911  	if err != nil {
   912  		return state, nil, err
   913  	}
   914  
   915  	result, err := tsv.Execute(ctx, target, sql, bindVariables, state.TransactionID, reservedID, options)
   916  	return state, result, err
   917  }
   918  
   919  // BeginStreamExecute combines Begin and StreamExecute.
   920  func (tsv *TabletServer) BeginStreamExecute(
   921  	ctx context.Context,
   922  	target *querypb.Target,
   923  	preQueries []string,
   924  	sql string,
   925  	bindVariables map[string]*querypb.BindVariable,
   926  	reservedID int64,
   927  	options *querypb.ExecuteOptions,
   928  	callback func(*sqltypes.Result) error,
   929  ) (queryservice.TransactionState, error) {
   930  	state, err := tsv.begin(ctx, target, preQueries, reservedID, nil, options)
   931  	if err != nil {
   932  		return state, err
   933  	}
   934  
   935  	err = tsv.StreamExecute(ctx, target, sql, bindVariables, state.TransactionID, reservedID, options, callback)
   936  	return state, err
   937  }
   938  
   939  func (tsv *TabletServer) beginWaitForSameRangeTransactions(ctx context.Context, target *querypb.Target, options *querypb.ExecuteOptions, sql string, bindVariables map[string]*querypb.BindVariable) (txserializer.DoneFunc, error) {
   940  	// Serialize the creation of new transactions *if* the first
   941  	// UPDATE or DELETE query has the same WHERE clause as a query which is
   942  	// already running in a transaction (only other BeginExecute() calls are
   943  	// considered). This avoids exhausting all txpool slots due to a hot row.
   944  	//
   945  	// Known Issue: There can be more than one transaction pool slot in use for
   946  	// the same row because the next transaction is unblocked after this
   947  	// BeginExecute() call is done and before Commit() on this transaction has
   948  	// been called. Due to the additional MySQL locking, this should result into
   949  	// two transaction pool slots per row at most. (This transaction pending on
   950  	// COMMIT, the next one waiting for MySQL in BEGIN+EXECUTE.)
   951  	var txDone txserializer.DoneFunc
   952  
   953  	err := tsv.execRequest(
   954  		// Use (potentially longer) -queryserver-config-query-timeout and not
   955  		// -queryserver-config-txpool-timeout (defaults to 1s) to limit the waiting.
   956  		ctx, tsv.QueryTimeout.Get(),
   957  		"", "waitForSameRangeTransactions", nil,
   958  		target, options, false, /* allowOnShutdown */
   959  		func(ctx context.Context, logStats *tabletenv.LogStats) error {
   960  			k, table := tsv.computeTxSerializerKey(ctx, logStats, sql, bindVariables)
   961  			if k == "" {
   962  				// Query is not subject to tx serialization/hot row protection.
   963  				return nil
   964  			}
   965  
   966  			startTime := time.Now()
   967  			done, waited, waitErr := tsv.qe.txSerializer.Wait(ctx, k, table)
   968  			txDone = done
   969  			if waited {
   970  				tsv.stats.WaitTimings.Record("TxSerializer", startTime)
   971  			}
   972  
   973  			return waitErr
   974  		})
   975  	return txDone, err
   976  }
   977  
   978  // computeTxSerializerKey returns a unique string ("key") used to determine
   979  // whether two queries would update the same row (range).
   980  // Additionally, it returns the table name (needed for updating stats vars).
   981  // It returns an empty string as key if the row (range) cannot be parsed from
   982  // the query and bind variables or the table name is empty.
   983  func (tsv *TabletServer) computeTxSerializerKey(ctx context.Context, logStats *tabletenv.LogStats, sql string, bindVariables map[string]*querypb.BindVariable) (string, string) {
   984  	// Strip trailing comments so we don't pollute the query cache.
   985  	sql, _ = sqlparser.SplitMarginComments(sql)
   986  	plan, err := tsv.qe.GetPlan(ctx, logStats, sql, false)
   987  	if err != nil {
   988  		logComputeRowSerializerKey.Errorf("failed to get plan for query: %v err: %v", sql, err)
   989  		return "", ""
   990  	}
   991  
   992  	switch plan.PlanID {
   993  	// Serialize only UPDATE or DELETE queries.
   994  	case planbuilder.PlanUpdate, planbuilder.PlanUpdateLimit,
   995  		planbuilder.PlanDelete, planbuilder.PlanDeleteLimit:
   996  	default:
   997  		return "", ""
   998  	}
   999  
  1000  	tableName := plan.TableName()
  1001  	if tableName.IsEmpty() || plan.WhereClause == nil {
  1002  		// Do not serialize any queries without table name or where clause
  1003  		return "", ""
  1004  	}
  1005  
  1006  	where, err := plan.WhereClause.GenerateQuery(bindVariables, nil)
  1007  	if err != nil {
  1008  		logComputeRowSerializerKey.Errorf("failed to substitute bind vars in where clause: %v query: %v bind vars: %v", err, sql, bindVariables)
  1009  		return "", ""
  1010  	}
  1011  
  1012  	// Example: table1 where id = 1 and sub_id = 2
  1013  	key := fmt.Sprintf("%s%s", tableName, where)
  1014  	return key, tableName.String()
  1015  }
  1016  
  1017  // MessageStream streams messages from the requested table.
  1018  func (tsv *TabletServer) MessageStream(ctx context.Context, target *querypb.Target, name string, callback func(*sqltypes.Result) error) (err error) {
  1019  	return tsv.execRequest(
  1020  		ctx, 0,
  1021  		"MessageStream", "stream", nil,
  1022  		target, nil, false, /* allowOnShutdown */
  1023  		func(ctx context.Context, logStats *tabletenv.LogStats) error {
  1024  			plan, err := tsv.qe.GetMessageStreamPlan(name)
  1025  			if err != nil {
  1026  				return err
  1027  			}
  1028  			qre := &QueryExecutor{
  1029  				query:    "stream from msg",
  1030  				plan:     plan,
  1031  				ctx:      ctx,
  1032  				logStats: logStats,
  1033  				tsv:      tsv,
  1034  			}
  1035  			return qre.MessageStream(callback)
  1036  		},
  1037  	)
  1038  }
  1039  
  1040  // MessageAck acks the list of messages for a given message table.
  1041  // It returns the number of messages successfully acked.
  1042  func (tsv *TabletServer) MessageAck(ctx context.Context, target *querypb.Target, name string, ids []*querypb.Value) (count int64, err error) {
  1043  	sids := make([]string, 0, len(ids))
  1044  	for _, val := range ids {
  1045  		sids = append(sids, sqltypes.ProtoToValue(val).ToString())
  1046  	}
  1047  	querygen, err := tsv.messager.GetGenerator(name)
  1048  	if err != nil {
  1049  		return 0, err
  1050  	}
  1051  	count, err = tsv.execDML(ctx, target, func() (string, map[string]*querypb.BindVariable, error) {
  1052  		query, bv := querygen.GenerateAckQuery(sids)
  1053  		return query, bv, nil
  1054  	})
  1055  	if err != nil {
  1056  		return 0, err
  1057  	}
  1058  	messager.MessageStats.Add([]string{name, "Acked"}, count)
  1059  	return count, nil
  1060  }
  1061  
  1062  // PostponeMessages postpones the list of messages for a given message table.
  1063  // It returns the number of messages successfully postponed.
  1064  func (tsv *TabletServer) PostponeMessages(ctx context.Context, target *querypb.Target, querygen messager.QueryGenerator, ids []string) (count int64, err error) {
  1065  	return tsv.execDML(ctx, target, func() (string, map[string]*querypb.BindVariable, error) {
  1066  		query, bv := querygen.GeneratePostponeQuery(ids)
  1067  		return query, bv, nil
  1068  	})
  1069  }
  1070  
  1071  // PurgeMessages purges messages older than specified time in Unix Nanoseconds.
  1072  // It purges at most 500 messages. It returns the number of messages successfully purged.
  1073  func (tsv *TabletServer) PurgeMessages(ctx context.Context, target *querypb.Target, querygen messager.QueryGenerator, timeCutoff int64) (count int64, err error) {
  1074  	return tsv.execDML(ctx, target, func() (string, map[string]*querypb.BindVariable, error) {
  1075  		query, bv := querygen.GeneratePurgeQuery(timeCutoff)
  1076  		return query, bv, nil
  1077  	})
  1078  }
  1079  
  1080  func (tsv *TabletServer) execDML(ctx context.Context, target *querypb.Target, queryGenerator func() (string, map[string]*querypb.BindVariable, error)) (count int64, err error) {
  1081  	if err = tsv.sm.StartRequest(ctx, target, false /* allowOnShutdown */); err != nil {
  1082  		return 0, err
  1083  	}
  1084  	defer tsv.sm.EndRequest()
  1085  	defer tsv.handlePanicAndSendLogStats("ack", nil, nil)
  1086  
  1087  	query, bv, err := queryGenerator()
  1088  	if err != nil {
  1089  		return 0, err
  1090  	}
  1091  
  1092  	state, err := tsv.Begin(ctx, target, nil)
  1093  	if err != nil {
  1094  		return 0, err
  1095  	}
  1096  	// If transaction was not committed by the end, it means
  1097  	// that there was an error, roll it back.
  1098  	defer func() {
  1099  		if state.TransactionID != 0 {
  1100  			tsv.Rollback(ctx, target, state.TransactionID)
  1101  		}
  1102  	}()
  1103  	qr, err := tsv.Execute(ctx, target, query, bv, state.TransactionID, 0, nil)
  1104  	if err != nil {
  1105  		return 0, err
  1106  	}
  1107  	if _, err = tsv.Commit(ctx, target, state.TransactionID); err != nil {
  1108  		state.TransactionID = 0
  1109  		return 0, err
  1110  	}
  1111  	state.TransactionID = 0
  1112  	return int64(qr.RowsAffected), nil
  1113  }
  1114  
  1115  // VStream streams VReplication events.
  1116  func (tsv *TabletServer) VStream(ctx context.Context, request *binlogdatapb.VStreamRequest, send func([]*binlogdatapb.VEvent) error) error {
  1117  	if err := tsv.sm.VerifyTarget(ctx, request.Target); err != nil {
  1118  		return err
  1119  	}
  1120  	return tsv.vstreamer.Stream(ctx, request.Position, request.TableLastPKs, request.Filter, send)
  1121  }
  1122  
  1123  // VStreamRows streams rows from the specified starting point.
  1124  func (tsv *TabletServer) VStreamRows(ctx context.Context, request *binlogdatapb.VStreamRowsRequest, send func(*binlogdatapb.VStreamRowsResponse) error) error {
  1125  	if err := tsv.sm.VerifyTarget(ctx, request.Target); err != nil {
  1126  		return err
  1127  	}
  1128  	var row []sqltypes.Value
  1129  	if request.Lastpk != nil {
  1130  		r := sqltypes.Proto3ToResult(request.Lastpk)
  1131  		if len(r.Rows) != 1 {
  1132  			return vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "unexpected lastpk input: %v", request.Lastpk)
  1133  		}
  1134  		row = r.Rows[0]
  1135  	}
  1136  	return tsv.vstreamer.StreamRows(ctx, request.Query, row, send)
  1137  }
  1138  
  1139  // VStreamResults streams rows from the specified starting point.
  1140  func (tsv *TabletServer) VStreamResults(ctx context.Context, target *querypb.Target, query string, send func(*binlogdatapb.VStreamResultsResponse) error) error {
  1141  	if err := tsv.sm.VerifyTarget(ctx, target); err != nil {
  1142  		return err
  1143  	}
  1144  	return tsv.vstreamer.StreamResults(ctx, query, send)
  1145  }
  1146  
  1147  // ReserveBeginExecute implements the QueryService interface
  1148  func (tsv *TabletServer) ReserveBeginExecute(ctx context.Context, target *querypb.Target, preQueries []string, postBeginQueries []string, sql string, bindVariables map[string]*querypb.BindVariable, options *querypb.ExecuteOptions) (state queryservice.ReservedTransactionState, result *sqltypes.Result, err error) {
  1149  	if tsv.config.EnableSettingsPool {
  1150  		state, result, err = tsv.beginExecuteWithSettings(ctx, target, preQueries, postBeginQueries, sql, bindVariables, options)
  1151  		// If there is an error and the error message is about allowing query in reserved connection only,
  1152  		// then we do not return an error from here and continue to use the reserved connection path.
  1153  		// This is specially for get_lock function call from vtgate that needs a reserved connection.
  1154  		if err == nil || !strings.Contains(err.Error(), "not allowed without reserved connection") {
  1155  			return state, result, err
  1156  		}
  1157  		// rollback if transaction was started.
  1158  		if state.TransactionID != 0 {
  1159  			_, _ = tsv.Rollback(ctx, target, state.TransactionID)
  1160  		}
  1161  	}
  1162  	var connID int64
  1163  	var sessionStateChanges string
  1164  	state.TabletAlias = tsv.alias
  1165  
  1166  	err = tsv.execRequest(
  1167  		ctx, tsv.QueryTimeout.Get(),
  1168  		"ReserveBegin", "begin", bindVariables,
  1169  		target, options, false, /* allowOnShutdown */
  1170  		func(ctx context.Context, logStats *tabletenv.LogStats) error {
  1171  			defer tsv.stats.QueryTimings.Record("RESERVE", time.Now())
  1172  			connID, sessionStateChanges, err = tsv.te.ReserveBegin(ctx, options, preQueries, postBeginQueries)
  1173  			if err != nil {
  1174  				return err
  1175  			}
  1176  			logStats.TransactionID = connID
  1177  			logStats.ReservedID = connID
  1178  			return nil
  1179  		},
  1180  	)
  1181  
  1182  	if err != nil {
  1183  		return state, nil, err
  1184  	}
  1185  	state.ReservedID = connID
  1186  	state.TransactionID = connID
  1187  	state.SessionStateChanges = sessionStateChanges
  1188  
  1189  	result, err = tsv.execute(ctx, target, sql, bindVariables, state.TransactionID, state.ReservedID, nil, options)
  1190  	return state, result, err
  1191  }
  1192  
  1193  // ReserveBeginStreamExecute combines Begin and StreamExecute.
  1194  func (tsv *TabletServer) ReserveBeginStreamExecute(
  1195  	ctx context.Context,
  1196  	target *querypb.Target,
  1197  	preQueries []string,
  1198  	postBeginQueries []string,
  1199  	sql string,
  1200  	bindVariables map[string]*querypb.BindVariable,
  1201  	options *querypb.ExecuteOptions,
  1202  	callback func(*sqltypes.Result) error,
  1203  ) (state queryservice.ReservedTransactionState, err error) {
  1204  	if tsv.config.EnableSettingsPool {
  1205  		return tsv.beginStreamExecuteWithSettings(ctx, target, preQueries, postBeginQueries, sql, bindVariables, options, callback)
  1206  	}
  1207  
  1208  	var connID int64
  1209  	var sessionStateChanges string
  1210  
  1211  	err = tsv.execRequest(
  1212  		ctx, tsv.QueryTimeout.Get(),
  1213  		"ReserveBegin", "begin", bindVariables,
  1214  		target, options, false, /* allowOnShutdown */
  1215  		func(ctx context.Context, logStats *tabletenv.LogStats) error {
  1216  			defer tsv.stats.QueryTimings.Record("RESERVE", time.Now())
  1217  			connID, sessionStateChanges, err = tsv.te.ReserveBegin(ctx, options, preQueries, postBeginQueries)
  1218  			if err != nil {
  1219  				return err
  1220  			}
  1221  			logStats.TransactionID = connID
  1222  			logStats.ReservedID = connID
  1223  			return nil
  1224  		},
  1225  	)
  1226  
  1227  	if err != nil {
  1228  		return state, err
  1229  	}
  1230  	state.ReservedID = connID
  1231  	state.TransactionID = connID
  1232  	state.TabletAlias = tsv.alias
  1233  	state.SessionStateChanges = sessionStateChanges
  1234  
  1235  	err = tsv.streamExecute(ctx, target, sql, bindVariables, state.TransactionID, state.ReservedID, nil, options, callback)
  1236  	return state, err
  1237  }
  1238  
  1239  // ReserveExecute implements the QueryService interface
  1240  func (tsv *TabletServer) ReserveExecute(ctx context.Context, target *querypb.Target, preQueries []string, sql string, bindVariables map[string]*querypb.BindVariable, transactionID int64, options *querypb.ExecuteOptions) (state queryservice.ReservedState, result *sqltypes.Result, err error) {
  1241  	if tsv.config.EnableSettingsPool {
  1242  		result, err = tsv.executeWithSettings(ctx, target, preQueries, sql, bindVariables, transactionID, options)
  1243  		// If there is an error and the error message is about allowing query in reserved connection only,
  1244  		// then we do not return an error from here and continue to use the reserved connection path.
  1245  		// This is specially for get_lock function call from vtgate that needs a reserved connection.
  1246  		if err == nil || !strings.Contains(err.Error(), "not allowed without reserved connection") {
  1247  			return state, result, err
  1248  		}
  1249  	}
  1250  
  1251  	state.TabletAlias = tsv.alias
  1252  
  1253  	allowOnShutdown := false
  1254  	timeout := tsv.QueryTimeout.Get()
  1255  	if transactionID != 0 {
  1256  		allowOnShutdown = true
  1257  		// ReserveExecute is for OLTP only, so we can directly fetch the OLTP
  1258  		// TX timeout.
  1259  		txTimeout := tsv.config.TxTimeoutForWorkload(querypb.ExecuteOptions_OLTP)
  1260  		// Use the smaller of the two values (0 means infinity).
  1261  		timeout = smallerTimeout(timeout, txTimeout)
  1262  	}
  1263  
  1264  	err = tsv.execRequest(
  1265  		ctx, timeout,
  1266  		"Reserve", "", bindVariables,
  1267  		target, options, allowOnShutdown,
  1268  		func(ctx context.Context, logStats *tabletenv.LogStats) error {
  1269  			defer tsv.stats.QueryTimings.Record("RESERVE", time.Now())
  1270  			state.ReservedID, err = tsv.te.Reserve(ctx, options, transactionID, preQueries)
  1271  			if err != nil {
  1272  				return err
  1273  			}
  1274  			logStats.TransactionID = transactionID
  1275  			logStats.ReservedID = state.ReservedID
  1276  			return nil
  1277  		},
  1278  	)
  1279  
  1280  	if err != nil {
  1281  		return state, nil, err
  1282  	}
  1283  
  1284  	result, err = tsv.execute(ctx, target, sql, bindVariables, transactionID, state.ReservedID, nil, options)
  1285  	return state, result, err
  1286  }
  1287  
  1288  // ReserveStreamExecute combines Begin and StreamExecute.
  1289  func (tsv *TabletServer) ReserveStreamExecute(
  1290  	ctx context.Context,
  1291  	target *querypb.Target,
  1292  	preQueries []string,
  1293  	sql string,
  1294  	bindVariables map[string]*querypb.BindVariable,
  1295  	transactionID int64,
  1296  	options *querypb.ExecuteOptions,
  1297  	callback func(*sqltypes.Result) error,
  1298  ) (state queryservice.ReservedState, err error) {
  1299  	if tsv.config.EnableSettingsPool {
  1300  		return state, tsv.streamExecute(ctx, target, sql, bindVariables, transactionID, 0, preQueries, options, callback)
  1301  	}
  1302  
  1303  	state.TabletAlias = tsv.alias
  1304  
  1305  	allowOnShutdown := false
  1306  	var timeout time.Duration
  1307  	if transactionID != 0 {
  1308  		allowOnShutdown = true
  1309  		// Use the transaction timeout. ReserveStreamExecute is used for OLAP
  1310  		// only, so we can directly fetch the OLAP TX timeout.
  1311  		timeout = tsv.config.TxTimeoutForWorkload(querypb.ExecuteOptions_OLAP)
  1312  	}
  1313  
  1314  	err = tsv.execRequest(
  1315  		ctx, timeout,
  1316  		"Reserve", "", bindVariables,
  1317  		target, options, allowOnShutdown,
  1318  		func(ctx context.Context, logStats *tabletenv.LogStats) error {
  1319  			defer tsv.stats.QueryTimings.Record("RESERVE", time.Now())
  1320  			state.ReservedID, err = tsv.te.Reserve(ctx, options, transactionID, preQueries)
  1321  			if err != nil {
  1322  				return err
  1323  			}
  1324  			logStats.TransactionID = state.ReservedID
  1325  			logStats.ReservedID = state.ReservedID
  1326  			return nil
  1327  		},
  1328  	)
  1329  
  1330  	if err != nil {
  1331  		return state, err
  1332  	}
  1333  
  1334  	err = tsv.streamExecute(ctx, target, sql, bindVariables, transactionID, state.ReservedID, nil, options, callback)
  1335  	return state, err
  1336  }
  1337  
  1338  // Release implements the QueryService interface
  1339  func (tsv *TabletServer) Release(ctx context.Context, target *querypb.Target, transactionID, reservedID int64) error {
  1340  	if reservedID == 0 && transactionID == 0 {
  1341  		return vterrors.NewErrorf(vtrpcpb.Code_INVALID_ARGUMENT, vterrors.NoSuchSession, "connection ID and transaction ID do not exist")
  1342  	}
  1343  	return tsv.execRequest(
  1344  		ctx, tsv.QueryTimeout.Get(),
  1345  		"Release", "", nil,
  1346  		target, nil, true, /* allowOnShutdown */
  1347  		func(ctx context.Context, logStats *tabletenv.LogStats) error {
  1348  			defer tsv.stats.QueryTimings.Record("RELEASE", time.Now())
  1349  			logStats.TransactionID = transactionID
  1350  			logStats.ReservedID = reservedID
  1351  			if reservedID != 0 {
  1352  				// Release to close the underlying connection.
  1353  				return tsv.te.Release(reservedID)
  1354  			}
  1355  			// Rollback to cleanup the transaction before returning to the pool.
  1356  			_, err := tsv.te.Rollback(ctx, transactionID)
  1357  			return err
  1358  		},
  1359  	)
  1360  }
  1361  
  1362  func (tsv *TabletServer) executeWithSettings(ctx context.Context, target *querypb.Target, settings []string, sql string, bindVariables map[string]*querypb.BindVariable, transactionID int64, options *querypb.ExecuteOptions) (result *sqltypes.Result, err error) {
  1363  	span, ctx := trace.NewSpan(ctx, "TabletServer.ExecuteWithSettings")
  1364  	trace.AnnotateSQL(span, sqlparser.Preview(sql))
  1365  	defer span.Finish()
  1366  
  1367  	return tsv.execute(ctx, target, sql, bindVariables, transactionID, 0, settings, options)
  1368  }
  1369  
  1370  func (tsv *TabletServer) beginExecuteWithSettings(ctx context.Context, target *querypb.Target, settings []string, savepointQueries []string, sql string, bindVariables map[string]*querypb.BindVariable, options *querypb.ExecuteOptions) (queryservice.ReservedTransactionState, *sqltypes.Result, error) {
  1371  	txState, err := tsv.begin(ctx, target, savepointQueries, 0, settings, options)
  1372  	if err != nil {
  1373  		return txToReserveState(txState), nil, err
  1374  	}
  1375  
  1376  	result, err := tsv.execute(ctx, target, sql, bindVariables, txState.TransactionID, 0, settings, options)
  1377  	return txToReserveState(txState), result, err
  1378  }
  1379  
  1380  func (tsv *TabletServer) beginStreamExecuteWithSettings(ctx context.Context, target *querypb.Target, settings []string, savepointQueries []string, sql string, bindVariables map[string]*querypb.BindVariable, options *querypb.ExecuteOptions, callback func(*sqltypes.Result) error) (queryservice.ReservedTransactionState, error) {
  1381  	txState, err := tsv.begin(ctx, target, savepointQueries, 0, settings, options)
  1382  	if err != nil {
  1383  		return txToReserveState(txState), err
  1384  	}
  1385  
  1386  	err = tsv.streamExecute(ctx, target, sql, bindVariables, txState.TransactionID, 0, settings, options, callback)
  1387  	return txToReserveState(txState), err
  1388  }
  1389  
  1390  func txToReserveState(state queryservice.TransactionState) queryservice.ReservedTransactionState {
  1391  	return queryservice.ReservedTransactionState{
  1392  		TabletAlias:         state.TabletAlias,
  1393  		TransactionID:       state.TransactionID,
  1394  		SessionStateChanges: state.SessionStateChanges,
  1395  	}
  1396  }
  1397  
  1398  // GetSchema returns table definitions for the specified tables.
  1399  func (tsv *TabletServer) GetSchema(ctx context.Context, target *querypb.Target, tableType querypb.SchemaTableType, tableNames []string, callback func(schemaRes *querypb.GetSchemaResponse) error) (err error) {
  1400  	err = tsv.execRequest(
  1401  		ctx, tsv.QueryTimeout.Get(),
  1402  		"GetSchema", "", nil,
  1403  		target, nil, false, /* allowOnShutdown */
  1404  		func(ctx context.Context, logStats *tabletenv.LogStats) error {
  1405  			defer tsv.stats.QueryTimings.Record("GetSchema", time.Now())
  1406  
  1407  			qre := &QueryExecutor{
  1408  				ctx:      ctx,
  1409  				logStats: logStats,
  1410  				tsv:      tsv,
  1411  			}
  1412  			return qre.GetSchemaDefinitions(tableType, tableNames, callback)
  1413  		},
  1414  	)
  1415  	return
  1416  }
  1417  
  1418  // execRequest performs verifications, sets up the necessary environments
  1419  // and calls the supplied function for executing the request.
  1420  func (tsv *TabletServer) execRequest(
  1421  	ctx context.Context, timeout time.Duration,
  1422  	requestName, sql string, bindVariables map[string]*querypb.BindVariable,
  1423  	target *querypb.Target, options *querypb.ExecuteOptions, allowOnShutdown bool,
  1424  	exec func(ctx context.Context, logStats *tabletenv.LogStats) error,
  1425  ) (err error) {
  1426  	span, ctx := trace.NewSpan(ctx, "TabletServer."+requestName)
  1427  	if options != nil {
  1428  		span.Annotate("isolation-level", options.TransactionIsolation)
  1429  	}
  1430  	trace.AnnotateSQL(span, sqlparser.Preview(sql))
  1431  	if target != nil {
  1432  		span.Annotate("cell", target.Cell)
  1433  		span.Annotate("shard", target.Shard)
  1434  		span.Annotate("keyspace", target.Keyspace)
  1435  	}
  1436  	defer span.Finish()
  1437  
  1438  	logStats := tabletenv.NewLogStats(ctx, requestName)
  1439  	logStats.Target = target
  1440  	logStats.OriginalSQL = sql
  1441  	logStats.BindVariables = sqltypes.CopyBindVariables(bindVariables)
  1442  	defer tsv.handlePanicAndSendLogStats(sql, bindVariables, logStats)
  1443  
  1444  	if err = tsv.sm.StartRequest(ctx, target, allowOnShutdown); err != nil {
  1445  		return err
  1446  	}
  1447  
  1448  	ctx, cancel := withTimeout(ctx, timeout, options)
  1449  	defer func() {
  1450  		cancel()
  1451  		tsv.sm.EndRequest()
  1452  	}()
  1453  
  1454  	err = exec(ctx, logStats)
  1455  	if err != nil {
  1456  		return tsv.convertAndLogError(ctx, sql, bindVariables, err, logStats)
  1457  	}
  1458  	return nil
  1459  }
  1460  
  1461  func (tsv *TabletServer) handlePanicAndSendLogStats(
  1462  	sql string,
  1463  	bindVariables map[string]*querypb.BindVariable,
  1464  	logStats *tabletenv.LogStats,
  1465  ) {
  1466  	if x := recover(); x != nil {
  1467  		// Redaction/sanitization of the client error message is controlled by TerseErrors while
  1468  		// the log message is controlled by SanitizeLogMessages.
  1469  		// We are handling an unrecoverable panic, so the cost of the dual message handling is
  1470  		// not a concern.
  1471  		var messagef, errMessage, logMessage string
  1472  		messagef = fmt.Sprintf("Uncaught panic for %%v:\n%v\n%s", x, tb.Stack(4) /* Skip the last 4 boiler-plate frames. */)
  1473  		errMessage = fmt.Sprintf(messagef, queryAsString(sql, bindVariables, tsv.TerseErrors))
  1474  		terr := vterrors.Errorf(vtrpcpb.Code_UNKNOWN, "%s", errMessage)
  1475  		if tsv.TerseErrors == tsv.Config().SanitizeLogMessages {
  1476  			logMessage = errMessage
  1477  		} else {
  1478  			logMessage = fmt.Sprintf(messagef, queryAsString(sql, bindVariables, tsv.Config().SanitizeLogMessages))
  1479  		}
  1480  		log.Error(logMessage)
  1481  		tsv.stats.InternalErrors.Add("Panic", 1)
  1482  		if logStats != nil {
  1483  			logStats.Error = terr
  1484  		}
  1485  	}
  1486  	// Examples where we don't send the log stats:
  1487  	// - ExecuteBatch() (logStats == nil)
  1488  	// - beginWaitForSameRangeTransactions() (Method == "")
  1489  	// - Begin / Commit in autocommit mode
  1490  	if logStats != nil && logStats.Method != "" {
  1491  		logStats.Send()
  1492  	}
  1493  }
  1494  
  1495  func (tsv *TabletServer) convertAndLogError(ctx context.Context, sql string, bindVariables map[string]*querypb.BindVariable, err error, logStats *tabletenv.LogStats) error {
  1496  	if err == nil {
  1497  		return nil
  1498  	}
  1499  
  1500  	errCode := convertErrorCode(err)
  1501  	tsv.stats.ErrorCounters.Add(errCode.String(), 1)
  1502  
  1503  	callerID := ""
  1504  	cid := callerid.ImmediateCallerIDFromContext(ctx)
  1505  	if cid != nil {
  1506  		callerID = fmt.Sprintf(" (CallerID: %s)", cid.Username)
  1507  	}
  1508  
  1509  	logMethod := log.Errorf
  1510  	// Suppress or demote some errors in logs.
  1511  	switch errCode {
  1512  	case vtrpcpb.Code_FAILED_PRECONDITION, vtrpcpb.Code_ALREADY_EXISTS:
  1513  		logMethod = nil
  1514  	case vtrpcpb.Code_RESOURCE_EXHAUSTED:
  1515  		logMethod = logPoolFull.Errorf
  1516  	case vtrpcpb.Code_ABORTED:
  1517  		logMethod = log.Warningf
  1518  	case vtrpcpb.Code_INVALID_ARGUMENT, vtrpcpb.Code_DEADLINE_EXCEEDED:
  1519  		logMethod = log.Infof
  1520  	}
  1521  
  1522  	// If TerseErrors is on, strip the error message returned by MySQL and only
  1523  	// keep the error number and sql state.
  1524  	// We assume that bind variable have PII, which are included in the MySQL
  1525  	// query and come back as part of the error message. Removing the MySQL
  1526  	// error helps us avoid leaking PII.
  1527  	// There is one exception:
  1528  	// 1. FAILED_PRECONDITION errors. These are caused when a failover is in progress.
  1529  	// If so, we don't want to suppress the error. This will allow VTGate to
  1530  	// detect and perform buffering during failovers.
  1531  	var message string
  1532  	sqlErr, ok := err.(*mysql.SQLError)
  1533  	if ok {
  1534  		sqlState := sqlErr.SQLState()
  1535  		errnum := sqlErr.Number()
  1536  		if tsv.TerseErrors && errCode != vtrpcpb.Code_FAILED_PRECONDITION {
  1537  			err = vterrors.Errorf(errCode, "(errno %d) (sqlstate %s)%s: %s", errnum, sqlState, callerID, queryAsString(sql, bindVariables, tsv.TerseErrors))
  1538  			if logMethod != nil {
  1539  				message = fmt.Sprintf("(errno %d) (sqlstate %s)%s: %s", errnum, sqlState, callerID, truncateSQLAndBindVars(sql, bindVariables, tsv.Config().SanitizeLogMessages))
  1540  			}
  1541  		} else {
  1542  			err = vterrors.Errorf(errCode, "%s (errno %d) (sqlstate %s)%s: %s", sqlErr.Message, errnum, sqlState, callerID, queryAsString(sql, bindVariables, false))
  1543  			if logMethod != nil {
  1544  				message = fmt.Sprintf("%s (errno %d) (sqlstate %s)%s: %s", sqlErr.Message, errnum, sqlState, callerID, truncateSQLAndBindVars(sql, bindVariables, tsv.Config().SanitizeLogMessages))
  1545  			}
  1546  		}
  1547  	} else {
  1548  		err = vterrors.Errorf(errCode, "%v%s", err.Error(), callerID)
  1549  		if logMethod != nil {
  1550  			message = fmt.Sprintf("%v: %v", err, truncateSQLAndBindVars(sql, bindVariables, tsv.Config().SanitizeLogMessages))
  1551  		}
  1552  	}
  1553  
  1554  	if logMethod != nil {
  1555  		logMethod(message)
  1556  	}
  1557  
  1558  	if logStats != nil {
  1559  		logStats.Error = err
  1560  	}
  1561  
  1562  	return err
  1563  }
  1564  
  1565  // truncateSQLAndBindVars calls TruncateForLog which:
  1566  //
  1567  //	splits off trailing comments, truncates the query, re-adds the trailing comments,
  1568  //	if sanitize is false appends quoted bindvar:value pairs in sorted order, and
  1569  //	lastly it truncates the resulting string
  1570  func truncateSQLAndBindVars(sql string, bindVariables map[string]*querypb.BindVariable, sanitize bool) string {
  1571  	truncatedQuery := sqlparser.TruncateForLog(sql)
  1572  	buf := &bytes.Buffer{}
  1573  	fmt.Fprintf(buf, "BindVars: {")
  1574  	if len(bindVariables) > 0 {
  1575  		if sanitize {
  1576  			fmt.Fprintf(buf, "[REDACTED]")
  1577  		} else {
  1578  			var keys []string
  1579  			for key := range bindVariables {
  1580  				keys = append(keys, key)
  1581  			}
  1582  			sort.Strings(keys)
  1583  			for _, key := range keys {
  1584  				fmt.Fprintf(buf, "%s: %q", key, fmt.Sprintf("%v", bindVariables[key]))
  1585  			}
  1586  		}
  1587  	}
  1588  	fmt.Fprintf(buf, "}")
  1589  	bv := buf.String()
  1590  	maxLen := sqlparser.GetTruncateErrLen()
  1591  	if maxLen != 0 && len(bv) > maxLen {
  1592  		bv = bv[:maxLen-12] + " [TRUNCATED]"
  1593  	}
  1594  	return fmt.Sprintf("Sql: %q, %s", truncatedQuery, bv)
  1595  }
  1596  
  1597  func convertErrorCode(err error) vtrpcpb.Code {
  1598  	errCode := vterrors.Code(err)
  1599  	sqlErr, ok := err.(*mysql.SQLError)
  1600  	if !ok {
  1601  		return errCode
  1602  	}
  1603  
  1604  	switch sqlErr.Number() {
  1605  	case mysql.ERNotSupportedYet:
  1606  		errCode = vtrpcpb.Code_UNIMPLEMENTED
  1607  	case mysql.ERDiskFull, mysql.EROutOfMemory, mysql.EROutOfSortMemory, mysql.ERConCount, mysql.EROutOfResources, mysql.ERRecordFileFull, mysql.ERHostIsBlocked,
  1608  		mysql.ERCantCreateThread, mysql.ERTooManyDelayedThreads, mysql.ERNetPacketTooLarge, mysql.ERTooManyUserConnections, mysql.ERLockTableFull, mysql.ERUserLimitReached:
  1609  		errCode = vtrpcpb.Code_RESOURCE_EXHAUSTED
  1610  	case mysql.ERLockWaitTimeout:
  1611  		errCode = vtrpcpb.Code_DEADLINE_EXCEEDED
  1612  	case mysql.CRServerGone, mysql.ERServerShutdown, mysql.ERServerIsntAvailable, mysql.CRConnectionError, mysql.CRConnHostError:
  1613  		errCode = vtrpcpb.Code_UNAVAILABLE
  1614  	case mysql.ERFormNotFound, mysql.ERKeyNotFound, mysql.ERBadFieldError, mysql.ERNoSuchThread, mysql.ERUnknownTable, mysql.ERCantFindUDF, mysql.ERNonExistingGrant,
  1615  		mysql.ERNoSuchTable, mysql.ERNonExistingTableGrant, mysql.ERKeyDoesNotExist:
  1616  		errCode = vtrpcpb.Code_NOT_FOUND
  1617  	case mysql.ERDBAccessDenied, mysql.ERAccessDeniedError, mysql.ERKillDenied, mysql.ERNoPermissionToCreateUsers:
  1618  		errCode = vtrpcpb.Code_PERMISSION_DENIED
  1619  	case mysql.ERNoDb, mysql.ERNoSuchIndex, mysql.ERCantDropFieldOrKey, mysql.ERTableNotLockedForWrite, mysql.ERTableNotLocked, mysql.ERTooBigSelect, mysql.ERNotAllowedCommand,
  1620  		mysql.ERTooLongString, mysql.ERDelayedInsertTableLocked, mysql.ERDupUnique, mysql.ERRequiresPrimaryKey, mysql.ERCantDoThisDuringAnTransaction, mysql.ERReadOnlyTransaction,
  1621  		mysql.ERCannotAddForeign, mysql.ERNoReferencedRow, mysql.ERRowIsReferenced, mysql.ERCantUpdateWithReadLock, mysql.ERNoDefault, mysql.EROperandColumns,
  1622  		mysql.ERSubqueryNo1Row, mysql.ERNonUpdateableTable, mysql.ERFeatureDisabled, mysql.ERDuplicatedValueInType, mysql.ERRowIsReferenced2,
  1623  		mysql.ErNoReferencedRow2, mysql.ERWarnDataOutOfRange:
  1624  		errCode = vtrpcpb.Code_FAILED_PRECONDITION
  1625  	case mysql.EROptionPreventsStatement:
  1626  		errCode = vtrpcpb.Code_CLUSTER_EVENT
  1627  	case mysql.ERTableExists, mysql.ERDupEntry, mysql.ERFileExists, mysql.ERUDFExists:
  1628  		errCode = vtrpcpb.Code_ALREADY_EXISTS
  1629  	case mysql.ERGotSignal, mysql.ERForcingClose, mysql.ERAbortingConnection, mysql.ERLockDeadlock:
  1630  		// For ERLockDeadlock, a deadlock rolls back the transaction.
  1631  		errCode = vtrpcpb.Code_ABORTED
  1632  	case mysql.ERUnknownComError, mysql.ERBadNullError, mysql.ERBadDb, mysql.ERBadTable, mysql.ERNonUniq, mysql.ERWrongFieldWithGroup, mysql.ERWrongGroupField,
  1633  		mysql.ERWrongSumSelect, mysql.ERWrongValueCount, mysql.ERTooLongIdent, mysql.ERDupFieldName, mysql.ERDupKeyName, mysql.ERWrongFieldSpec, mysql.ERParseError,
  1634  		mysql.EREmptyQuery, mysql.ERNonUniqTable, mysql.ERInvalidDefault, mysql.ERMultiplePriKey, mysql.ERTooManyKeys, mysql.ERTooManyKeyParts, mysql.ERTooLongKey,
  1635  		mysql.ERKeyColumnDoesNotExist, mysql.ERBlobUsedAsKey, mysql.ERTooBigFieldLength, mysql.ERWrongAutoKey, mysql.ERWrongFieldTerminators, mysql.ERBlobsAndNoTerminated,
  1636  		mysql.ERTextFileNotReadable, mysql.ERWrongSubKey, mysql.ERCantRemoveAllFields, mysql.ERUpdateTableUsed, mysql.ERNoTablesUsed, mysql.ERTooBigSet,
  1637  		mysql.ERBlobCantHaveDefault, mysql.ERWrongDbName, mysql.ERWrongTableName, mysql.ERUnknownProcedure, mysql.ERWrongParamCountToProcedure,
  1638  		mysql.ERWrongParametersToProcedure, mysql.ERFieldSpecifiedTwice, mysql.ERInvalidGroupFuncUse, mysql.ERTableMustHaveColumns, mysql.ERUnknownCharacterSet,
  1639  		mysql.ERTooManyTables, mysql.ERTooManyFields, mysql.ERTooBigRowSize, mysql.ERWrongOuterJoin, mysql.ERNullColumnInIndex, mysql.ERFunctionNotDefined,
  1640  		mysql.ERWrongValueCountOnRow, mysql.ERInvalidUseOfNull, mysql.ERRegexpError, mysql.ERMixOfGroupFuncAndFields, mysql.ERIllegalGrantForTable, mysql.ERSyntaxError,
  1641  		mysql.ERWrongColumnName, mysql.ERWrongKeyColumn, mysql.ERBlobKeyWithoutLength, mysql.ERPrimaryCantHaveNull, mysql.ERTooManyRows, mysql.ERUnknownSystemVariable,
  1642  		mysql.ERSetConstantsOnly, mysql.ERWrongArguments, mysql.ERWrongUsage, mysql.ERWrongNumberOfColumnsInSelect, mysql.ERDupArgument, mysql.ERLocalVariable,
  1643  		mysql.ERGlobalVariable, mysql.ERWrongValueForVar, mysql.ERWrongTypeForVar, mysql.ERVarCantBeRead, mysql.ERCantUseOptionHere, mysql.ERIncorrectGlobalLocalVar,
  1644  		mysql.ERWrongFKDef, mysql.ERKeyRefDoNotMatchTableRef, mysql.ERCyclicReference, mysql.ERCollationCharsetMismatch, mysql.ERCantAggregate2Collations,
  1645  		mysql.ERCantAggregate3Collations, mysql.ERCantAggregateNCollations, mysql.ERVariableIsNotStruct, mysql.ERUnknownCollation, mysql.ERWrongNameForIndex,
  1646  		mysql.ERWrongNameForCatalog, mysql.ERBadFTColumn, mysql.ERTruncatedWrongValue, mysql.ERTooMuchAutoTimestampCols, mysql.ERInvalidOnUpdate, mysql.ERUnknownTimeZone,
  1647  		mysql.ERInvalidCharacterString, mysql.ERIllegalReference, mysql.ERDerivedMustHaveAlias, mysql.ERTableNameNotAllowedHere, mysql.ERDataTooLong, mysql.ERDataOutOfRange,
  1648  		mysql.ERTruncatedWrongValueForField, mysql.ERIllegalValueForType:
  1649  		errCode = vtrpcpb.Code_INVALID_ARGUMENT
  1650  	case mysql.ERSpecifiedAccessDenied:
  1651  		errCode = vtrpcpb.Code_PERMISSION_DENIED
  1652  		// This code is also utilized for Google internal failover error code.
  1653  		if strings.Contains(err.Error(), "failover in progress") {
  1654  			errCode = vtrpcpb.Code_FAILED_PRECONDITION
  1655  		}
  1656  	case mysql.CRServerLost:
  1657  		// Query was killed.
  1658  		errCode = vtrpcpb.Code_CANCELED
  1659  	}
  1660  
  1661  	return errCode
  1662  }
  1663  
  1664  // StreamHealth streams the health status to callback.
  1665  func (tsv *TabletServer) StreamHealth(ctx context.Context, callback func(*querypb.StreamHealthResponse) error) error {
  1666  	return tsv.hs.Stream(ctx, callback)
  1667  }
  1668  
  1669  // BroadcastHealth will broadcast the current health to all listeners
  1670  func (tsv *TabletServer) BroadcastHealth() {
  1671  	tsv.sm.Broadcast()
  1672  }
  1673  
  1674  // EnterLameduck causes tabletserver to enter the lameduck state. This
  1675  // state causes health checks to fail, but the behavior of tabletserver
  1676  // otherwise remains the same. Any subsequent calls to SetServingType will
  1677  // cause the tabletserver to exit this mode.
  1678  func (tsv *TabletServer) EnterLameduck() {
  1679  	tsv.sm.EnterLameduck()
  1680  }
  1681  
  1682  // ExitLameduck causes the tabletserver to exit the lameduck mode.
  1683  func (tsv *TabletServer) ExitLameduck() {
  1684  	tsv.sm.ExitLameduck()
  1685  }
  1686  
  1687  // IsServing returns true if TabletServer is in SERVING state.
  1688  func (tsv *TabletServer) IsServing() bool {
  1689  	return tsv.sm.IsServing()
  1690  }
  1691  
  1692  // CheckMySQL initiates a check to see if MySQL is reachable.
  1693  // If not, it shuts down the query service. The check is rate-limited
  1694  // to no more than once per second.
  1695  // The function satisfies tabletenv.Env.
  1696  func (tsv *TabletServer) CheckMySQL() {
  1697  	tsv.sm.checkMySQL()
  1698  }
  1699  
  1700  // TopoServer returns the topo server.
  1701  func (tsv *TabletServer) TopoServer() *topo.Server {
  1702  	return tsv.topoServer
  1703  }
  1704  
  1705  // HandlePanic is part of the queryservice.QueryService interface
  1706  func (tsv *TabletServer) HandlePanic(err *error) {
  1707  	if x := recover(); x != nil {
  1708  		*err = fmt.Errorf("uncaught panic: %v\n. Stack-trace:\n%s", x, tb.Stack(4))
  1709  	}
  1710  }
  1711  
  1712  // Close is a no-op.
  1713  func (tsv *TabletServer) Close(ctx context.Context) error {
  1714  	return nil
  1715  }
  1716  
  1717  var okMessage = []byte("ok\n")
  1718  
  1719  // Health check
  1720  // Returns ok if we are in the desired serving state
  1721  func (tsv *TabletServer) registerHealthzHealthHandler() {
  1722  	tsv.exporter.HandleFunc("/healthz", tsv.healthzHandler)
  1723  }
  1724  
  1725  func (tsv *TabletServer) healthzHandler(w http.ResponseWriter, r *http.Request) {
  1726  	if err := acl.CheckAccessHTTP(r, acl.MONITORING); err != nil {
  1727  		acl.SendError(w, err)
  1728  		return
  1729  	}
  1730  	if (tsv.sm.wantState == StateServing || tsv.sm.wantState == StateNotConnected) && !tsv.sm.IsServing() {
  1731  		http.Error(w, "500 internal server error: vttablet is not serving", http.StatusInternalServerError)
  1732  		return
  1733  	}
  1734  	w.Header().Set("Content-Length", fmt.Sprintf("%v", len(okMessage)))
  1735  	w.WriteHeader(http.StatusOK)
  1736  	w.Write(okMessage)
  1737  }
  1738  
  1739  // Query service health check
  1740  // Returns ok if a query can go all the way to database and back
  1741  func (tsv *TabletServer) registerDebugHealthHandler() {
  1742  	tsv.exporter.HandleFunc("/debug/health", func(w http.ResponseWriter, r *http.Request) {
  1743  		if err := acl.CheckAccessHTTP(r, acl.MONITORING); err != nil {
  1744  			acl.SendError(w, err)
  1745  			return
  1746  		}
  1747  		w.Header().Set("Content-Type", "text/plain")
  1748  		if err := tsv.IsHealthy(); err != nil {
  1749  			http.Error(w, fmt.Sprintf("not ok: %v", err), http.StatusInternalServerError)
  1750  			return
  1751  		}
  1752  		w.Write([]byte("ok"))
  1753  	})
  1754  }
  1755  
  1756  func (tsv *TabletServer) registerQueryzHandler() {
  1757  	tsv.exporter.HandleFunc("/queryz", func(w http.ResponseWriter, r *http.Request) {
  1758  		queryzHandler(tsv.qe, w, r)
  1759  	})
  1760  }
  1761  
  1762  func (tsv *TabletServer) registerQueryListHandlers(queryLists []*QueryList) {
  1763  	tsv.exporter.HandleFunc("/livequeryz/", func(w http.ResponseWriter, r *http.Request) {
  1764  		livequeryzHandler(queryLists, w, r)
  1765  	})
  1766  	tsv.exporter.HandleFunc("/livequeryz/terminate", func(w http.ResponseWriter, r *http.Request) {
  1767  		livequeryzTerminateHandler(queryLists, w, r)
  1768  	})
  1769  }
  1770  
  1771  func (tsv *TabletServer) registerTwopczHandler() {
  1772  	tsv.exporter.HandleFunc("/twopcz", func(w http.ResponseWriter, r *http.Request) {
  1773  		ctx := tabletenv.LocalContext()
  1774  		txe := &TxExecutor{
  1775  			ctx:      ctx,
  1776  			logStats: tabletenv.NewLogStats(ctx, "twopcz"),
  1777  			te:       tsv.te,
  1778  		}
  1779  		twopczHandler(txe, w, r)
  1780  	})
  1781  }
  1782  
  1783  func (tsv *TabletServer) registerMigrationStatusHandler() {
  1784  	tsv.exporter.HandleFunc("/schema-migration/report-status", func(w http.ResponseWriter, r *http.Request) {
  1785  		ctx := tabletenv.LocalContext()
  1786  		query := r.URL.Query()
  1787  		if err := tsv.onlineDDLExecutor.OnSchemaMigrationStatus(ctx, query.Get("uuid"), query.Get("status"), query.Get("dryrun"), query.Get("progress"), query.Get("eta"), query.Get("rowscopied"), query.Get("hint")); err != nil {
  1788  			http.Error(w, fmt.Sprintf("not ok: %v", err), http.StatusInternalServerError)
  1789  			return
  1790  		}
  1791  		w.Write([]byte("ok"))
  1792  	})
  1793  }
  1794  
  1795  // registerThrottlerCheckHandlers registers throttler "check" requests
  1796  func (tsv *TabletServer) registerThrottlerCheckHandlers() {
  1797  	handle := func(path string, checkType throttle.ThrottleCheckType) {
  1798  		tsv.exporter.HandleFunc(path, func(w http.ResponseWriter, r *http.Request) {
  1799  			ctx := tabletenv.LocalContext()
  1800  			remoteAddr := r.Header.Get("X-Forwarded-For")
  1801  			if remoteAddr == "" {
  1802  				remoteAddr = r.RemoteAddr
  1803  				remoteAddr = strings.Split(remoteAddr, ":")[0]
  1804  			}
  1805  			appName := r.URL.Query().Get("app")
  1806  			if appName == "" {
  1807  				appName = throttle.DefaultAppName
  1808  			}
  1809  			flags := &throttle.CheckFlags{
  1810  				LowPriority:           (r.URL.Query().Get("p") == "low"),
  1811  				SkipRequestHeartbeats: (r.URL.Query().Get("s") == "true"),
  1812  			}
  1813  			checkResult := tsv.lagThrottler.CheckByType(ctx, appName, remoteAddr, flags, checkType)
  1814  			if checkResult.StatusCode == http.StatusNotFound && flags.OKIfNotExists {
  1815  				checkResult.StatusCode = http.StatusOK // 200
  1816  			}
  1817  
  1818  			if r.Method == http.MethodGet {
  1819  				w.Header().Set("Content-Type", "application/json")
  1820  			}
  1821  			w.WriteHeader(checkResult.StatusCode)
  1822  			if r.Method == http.MethodGet {
  1823  				json.NewEncoder(w).Encode(checkResult)
  1824  			}
  1825  		})
  1826  	}
  1827  	handle("/throttler/check", throttle.ThrottleCheckPrimaryWrite)
  1828  	handle("/throttler/check-self", throttle.ThrottleCheckSelf)
  1829  }
  1830  
  1831  // registerThrottlerStatusHandler registers a throttler "status" request
  1832  func (tsv *TabletServer) registerThrottlerStatusHandler() {
  1833  	tsv.exporter.HandleFunc("/throttler/status", func(w http.ResponseWriter, r *http.Request) {
  1834  		status := tsv.lagThrottler.Status()
  1835  
  1836  		w.Header().Set("Content-Type", "application/json")
  1837  		json.NewEncoder(w).Encode(status)
  1838  	})
  1839  }
  1840  
  1841  // registerThrottlerThrottleAppHandler registers a throttler "throttle-app" request
  1842  func (tsv *TabletServer) registerThrottlerThrottleAppHandler() {
  1843  	tsv.exporter.HandleFunc("/throttler/throttle-app", func(w http.ResponseWriter, r *http.Request) {
  1844  		appName := r.URL.Query().Get("app")
  1845  		d, err := time.ParseDuration(r.URL.Query().Get("duration"))
  1846  		if err != nil {
  1847  			http.Error(w, fmt.Sprintf("not ok: %v", err), http.StatusInternalServerError)
  1848  			return
  1849  		}
  1850  		var ratio = 1.0
  1851  		if ratioParam := r.URL.Query().Get("ratio"); ratioParam != "" {
  1852  			ratio, err = strconv.ParseFloat(ratioParam, 64)
  1853  			if err != nil {
  1854  				http.Error(w, fmt.Sprintf("not ok: %v", err), http.StatusInternalServerError)
  1855  				return
  1856  			}
  1857  		}
  1858  		appThrottle := tsv.lagThrottler.ThrottleApp(appName, time.Now().Add(d), ratio)
  1859  
  1860  		w.Header().Set("Content-Type", "application/json")
  1861  		json.NewEncoder(w).Encode(appThrottle)
  1862  	})
  1863  	tsv.exporter.HandleFunc("/throttler/unthrottle-app", func(w http.ResponseWriter, r *http.Request) {
  1864  		appName := r.URL.Query().Get("app")
  1865  		appThrottle := tsv.lagThrottler.UnthrottleApp(appName)
  1866  
  1867  		w.Header().Set("Content-Type", "application/json")
  1868  		json.NewEncoder(w).Encode(appThrottle)
  1869  	})
  1870  	tsv.exporter.HandleFunc("/throttler/throttled-apps", func(w http.ResponseWriter, r *http.Request) {
  1871  		throttledApps := tsv.lagThrottler.ThrottledApps()
  1872  
  1873  		w.Header().Set("Content-Type", "application/json")
  1874  		json.NewEncoder(w).Encode(throttledApps)
  1875  	})
  1876  }
  1877  
  1878  // registerThrottlerHandlers registers all throttler handlers
  1879  func (tsv *TabletServer) registerThrottlerHandlers() {
  1880  	tsv.registerThrottlerCheckHandlers()
  1881  	tsv.registerThrottlerStatusHandler()
  1882  	tsv.registerThrottlerThrottleAppHandler()
  1883  }
  1884  
  1885  func (tsv *TabletServer) registerDebugEnvHandler() {
  1886  	tsv.exporter.HandleFunc("/debug/env", func(w http.ResponseWriter, r *http.Request) {
  1887  		debugEnvHandler(tsv, w, r)
  1888  	})
  1889  }
  1890  
  1891  // EnableHeartbeat forces heartbeat to be on or off.
  1892  // Only to be used for testing.
  1893  func (tsv *TabletServer) EnableHeartbeat(enabled bool) {
  1894  	tsv.rt.EnableHeartbeat(enabled)
  1895  }
  1896  
  1897  // EnableThrottler forces throttler to be on or off.
  1898  // When throttler is off, it responds to all check requests with HTTP 200 OK
  1899  // Only to be used for testing.
  1900  func (tsv *TabletServer) EnableThrottler(enabled bool) {
  1901  	tsv.Config().EnableLagThrottler = enabled
  1902  }
  1903  
  1904  // SetTracking forces tracking to be on or off.
  1905  // Only to be used for testing.
  1906  func (tsv *TabletServer) SetTracking(enabled bool) {
  1907  	tsv.tracker.Enable(enabled)
  1908  }
  1909  
  1910  // EnableHistorian forces historian to be on or off.
  1911  // Only to be used for testing.
  1912  func (tsv *TabletServer) EnableHistorian(enabled bool) {
  1913  	_ = tsv.se.EnableHistorian(enabled)
  1914  }
  1915  
  1916  // SetPoolSize changes the pool size to the specified value.
  1917  func (tsv *TabletServer) SetPoolSize(val int) {
  1918  	if val <= 0 {
  1919  		return
  1920  	}
  1921  	tsv.qe.conns.SetCapacity(val)
  1922  }
  1923  
  1924  // PoolSize returns the pool size.
  1925  func (tsv *TabletServer) PoolSize() int {
  1926  	return int(tsv.qe.conns.Capacity())
  1927  }
  1928  
  1929  // SetStreamPoolSize changes the pool size to the specified value.
  1930  func (tsv *TabletServer) SetStreamPoolSize(val int) {
  1931  	tsv.qe.streamConns.SetCapacity(val)
  1932  }
  1933  
  1934  // SetStreamConsolidationBlocking sets whether the stream consolidator should wait for slow clients
  1935  func (tsv *TabletServer) SetStreamConsolidationBlocking(block bool) {
  1936  	tsv.qe.streamConsolidator.SetBlocking(block)
  1937  }
  1938  
  1939  // StreamPoolSize returns the pool size.
  1940  func (tsv *TabletServer) StreamPoolSize() int {
  1941  	return int(tsv.qe.streamConns.Capacity())
  1942  }
  1943  
  1944  // SetTxPoolSize changes the tx pool size to the specified value.
  1945  func (tsv *TabletServer) SetTxPoolSize(val int) {
  1946  	tsv.te.txPool.scp.conns.SetCapacity(val)
  1947  }
  1948  
  1949  // TxPoolSize returns the tx pool size.
  1950  func (tsv *TabletServer) TxPoolSize() int {
  1951  	return tsv.te.txPool.scp.Capacity()
  1952  }
  1953  
  1954  // SetQueryPlanCacheCap changes the plan cache capacity to the specified value.
  1955  func (tsv *TabletServer) SetQueryPlanCacheCap(val int) {
  1956  	tsv.qe.SetQueryPlanCacheCap(val)
  1957  }
  1958  
  1959  // QueryPlanCacheCap returns the plan cache capacity
  1960  func (tsv *TabletServer) QueryPlanCacheCap() int {
  1961  	return tsv.qe.QueryPlanCacheCap()
  1962  }
  1963  
  1964  // QueryPlanCacheLen returns the plan cache length
  1965  func (tsv *TabletServer) QueryPlanCacheLen() int {
  1966  	return tsv.qe.QueryPlanCacheLen()
  1967  }
  1968  
  1969  // QueryPlanCacheWait waits until the query plan cache has processed all recent queries
  1970  func (tsv *TabletServer) QueryPlanCacheWait() {
  1971  	tsv.qe.plans.Wait()
  1972  }
  1973  
  1974  // SetMaxResultSize changes the max result size to the specified value.
  1975  func (tsv *TabletServer) SetMaxResultSize(val int) {
  1976  	tsv.qe.maxResultSize.Set(int64(val))
  1977  }
  1978  
  1979  // MaxResultSize returns the max result size.
  1980  func (tsv *TabletServer) MaxResultSize() int {
  1981  	return int(tsv.qe.maxResultSize.Get())
  1982  }
  1983  
  1984  // SetWarnResultSize changes the warn result size to the specified value.
  1985  func (tsv *TabletServer) SetWarnResultSize(val int) {
  1986  	tsv.qe.warnResultSize.Set(int64(val))
  1987  }
  1988  
  1989  // WarnResultSize returns the warn result size.
  1990  func (tsv *TabletServer) WarnResultSize() int {
  1991  	return int(tsv.qe.warnResultSize.Get())
  1992  }
  1993  
  1994  // SetThrottleMetricThreshold changes the throttler metric threshold
  1995  func (tsv *TabletServer) SetThrottleMetricThreshold(val float64) {
  1996  	tsv.lagThrottler.MetricsThreshold.Set(val)
  1997  }
  1998  
  1999  // ThrottleMetricThreshold returns the throttler metric threshold
  2000  func (tsv *TabletServer) ThrottleMetricThreshold() float64 {
  2001  	return tsv.lagThrottler.MetricsThreshold.Get()
  2002  }
  2003  
  2004  // SetPassthroughDMLs changes the setting to pass through all DMLs
  2005  // It should only be used for testing
  2006  func (tsv *TabletServer) SetPassthroughDMLs(val bool) {
  2007  	planbuilder.PassthroughDMLs = val
  2008  }
  2009  
  2010  // SetConsolidatorMode sets the consolidator mode.
  2011  func (tsv *TabletServer) SetConsolidatorMode(mode string) {
  2012  	switch mode {
  2013  	case tabletenv.NotOnPrimary, tabletenv.Enable, tabletenv.Disable:
  2014  		tsv.qe.consolidatorMode.Set(mode)
  2015  	}
  2016  }
  2017  
  2018  // ConsolidatorMode returns the consolidator mode.
  2019  func (tsv *TabletServer) ConsolidatorMode() string {
  2020  	return tsv.qe.consolidatorMode.Get()
  2021  }
  2022  
  2023  // queryAsString returns a readable normalized version of the query and if sanitize
  2024  // is false it also includes the bind variables.
  2025  func queryAsString(sql string, bindVariables map[string]*querypb.BindVariable, sanitize bool) string {
  2026  	buf := &bytes.Buffer{}
  2027  	// sql is the normalized query without the bind vars
  2028  	fmt.Fprintf(buf, "Sql: %q", sql)
  2029  	// Add the bind vars unless this needs to be sanitized, e.g. for log messages
  2030  	fmt.Fprintf(buf, ", BindVars: {")
  2031  	if len(bindVariables) > 0 {
  2032  		if sanitize {
  2033  			fmt.Fprintf(buf, "[REDACTED]")
  2034  		} else {
  2035  			var keys []string
  2036  			for key := range bindVariables {
  2037  				keys = append(keys, key)
  2038  			}
  2039  			sort.Strings(keys)
  2040  			var valString string
  2041  			for _, key := range keys {
  2042  				valString = fmt.Sprintf("%v", bindVariables[key])
  2043  				fmt.Fprintf(buf, "%s: %q", key, valString)
  2044  			}
  2045  		}
  2046  	}
  2047  	fmt.Fprintf(buf, "}")
  2048  	return buf.String()
  2049  }
  2050  
  2051  // withTimeout returns a context based on the specified timeout.
  2052  // If the context is local or if timeout is 0, the
  2053  // original context is returned as is.
  2054  func withTimeout(ctx context.Context, timeout time.Duration, options *querypb.ExecuteOptions) (context.Context, context.CancelFunc) {
  2055  	if timeout == 0 || options.GetWorkload() == querypb.ExecuteOptions_DBA || tabletenv.IsLocalContext(ctx) {
  2056  		return ctx, func() {}
  2057  	}
  2058  	return context.WithTimeout(ctx, timeout)
  2059  }
  2060  
  2061  // skipQueryPlanCache returns true if the query plan should be cached
  2062  func skipQueryPlanCache(options *querypb.ExecuteOptions) bool {
  2063  	if options == nil {
  2064  		return false
  2065  	}
  2066  	return options.SkipQueryPlanCache || options.HasCreatedTempTables
  2067  }