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 }