github.com/whtcorpsinc/MilevaDB-Prod@v0.0.0-20211104133533-f57f4be3b597/causetstore/stochastik/session.go (about) 1 // Copyright 2020 The ql Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSES/QL-LICENSE file. 4 5 // Copyright 2020 WHTCORPS INC, Inc. 6 // 7 // Licensed under the Apache License, Version 2.0 (the "License"); 8 // you may not use this file except in compliance with the License. 9 // You may obtain a copy of the License at 10 // 11 // http://www.apache.org/licenses/LICENSE-2.0 12 // 13 // Unless required by applicable law or agreed to in writing, software 14 // distributed under the License is distributed on an "AS IS" BASIS, 15 // See the License for the specific language governing permissions and 16 // limitations under the License. 17 18 package stochastik 19 20 import ( 21 "context" 22 "crypto/tls" 23 "encoding/json" 24 "fmt" 25 "net" 26 "runtime/trace" 27 "strconv" 28 "strings" 29 "sync" 30 "sync/atomic" 31 "time" 32 33 "github.com/ngaut/pools" 34 "github.com/opentracing/opentracing-go" 35 "github.com/whtcorpsinc/BerolinaSQL" 36 "github.com/whtcorpsinc/BerolinaSQL/allegrosql" 37 "github.com/whtcorpsinc/BerolinaSQL/ast" 38 "github.com/whtcorpsinc/BerolinaSQL/auth" 39 "github.com/whtcorpsinc/BerolinaSQL/charset" 40 "github.com/whtcorpsinc/BerolinaSQL/perceptron" 41 "github.com/whtcorpsinc/BerolinaSQL/terror" 42 "github.com/whtcorpsinc/errors" 43 "github.com/whtcorpsinc/failpoint" 44 "github.com/whtcorpsinc/fidelpb/go-binlog" 45 "github.com/whtcorpsinc/milevadb/bindinfo" 46 "github.com/whtcorpsinc/milevadb/causet" 47 causetembedded "github.com/whtcorpsinc/milevadb/causet/embedded" 48 "github.com/whtcorpsinc/milevadb/causetstore/einsteindb" 49 "github.com/whtcorpsinc/milevadb/config" 50 "github.com/whtcorpsinc/milevadb/ekv" 51 "github.com/whtcorpsinc/milevadb/interlock" 52 "github.com/whtcorpsinc/milevadb/metrics" 53 "github.com/whtcorpsinc/milevadb/petri" 54 "github.com/whtcorpsinc/milevadb/plugin" 55 "github.com/whtcorpsinc/milevadb/privilege" 56 "github.com/whtcorpsinc/milevadb/privilege/privileges" 57 "github.com/whtcorpsinc/milevadb/schemareplicant" 58 "github.com/whtcorpsinc/milevadb/soliton" 59 "github.com/whtcorpsinc/milevadb/soliton/chunk" 60 "github.com/whtcorpsinc/milevadb/soliton/collate" 61 "github.com/whtcorpsinc/milevadb/soliton/ekvcache" 62 "github.com/whtcorpsinc/milevadb/soliton/execdetails" 63 "github.com/whtcorpsinc/milevadb/soliton/logutil" 64 "github.com/whtcorpsinc/milevadb/soliton/sqlexec" 65 "github.com/whtcorpsinc/milevadb/soliton/timeutil" 66 "github.com/whtcorpsinc/milevadb/spacetime" 67 "github.com/whtcorpsinc/milevadb/statistics/handle" 68 "github.com/whtcorpsinc/milevadb/stochastikctx" 69 "github.com/whtcorpsinc/milevadb/stochastikctx/binloginfo" 70 "github.com/whtcorpsinc/milevadb/stochastikctx/stmtctx" 71 "github.com/whtcorpsinc/milevadb/stochastikctx/variable" 72 "github.com/whtcorpsinc/milevadb/tenant" 73 "github.com/whtcorpsinc/milevadb/types" 74 "go.uber.org/zap" 75 ) 76 77 var ( 78 memexPerTransactionPessimisticOK = metrics.StatementPerTransaction.WithLabelValues(metrics.LblPessimistic, metrics.LblOK) 79 memexPerTransactionPessimisticError = metrics.StatementPerTransaction.WithLabelValues(metrics.LblPessimistic, metrics.LblError) 80 memexPerTransactionOptimisticOK = metrics.StatementPerTransaction.WithLabelValues(metrics.LblOptimistic, metrics.LblOK) 81 memexPerTransactionOptimisticError = metrics.StatementPerTransaction.WithLabelValues(metrics.LblOptimistic, metrics.LblError) 82 transactionDurationPessimisticCommit = metrics.TransactionDuration.WithLabelValues(metrics.LblPessimistic, metrics.LblCommit) 83 transactionDurationPessimisticAbort = metrics.TransactionDuration.WithLabelValues(metrics.LblPessimistic, metrics.LblAbort) 84 transactionDurationOptimisticCommit = metrics.TransactionDuration.WithLabelValues(metrics.LblOptimistic, metrics.LblCommit) 85 transactionDurationOptimisticAbort = metrics.TransactionDuration.WithLabelValues(metrics.LblOptimistic, metrics.LblAbort) 86 87 stochastikInterDircuteCompileDurationInternal = metrics.StochastikInterDircuteCompileDuration.WithLabelValues(metrics.LblInternal) 88 stochastikInterDircuteCompileDurationGeneral = metrics.StochastikInterDircuteCompileDuration.WithLabelValues(metrics.LblGeneral) 89 stochastikInterDircuteParseDurationInternal = metrics.StochastikInterDircuteParseDuration.WithLabelValues(metrics.LblInternal) 90 stochastikInterDircuteParseDurationGeneral = metrics.StochastikInterDircuteParseDuration.WithLabelValues(metrics.LblGeneral) 91 ) 92 93 // Stochastik context, it is consistent with the lifecycle of a client connection. 94 type Stochastik interface { 95 stochastikctx.Context 96 Status() uint16 // Flag of current status, such as autocommit. 97 LastInsertID() uint64 // LastInsertID is the last inserted auto_increment ID. 98 LastMessage() string // LastMessage is the info message that may be generated by last command 99 AffectedRows() uint64 // Affected rows by latest executed stmt. 100 // InterDircute is deprecated, use InterDircuteStmt() instead. 101 InterDircute(context.Context, string) ([]sqlexec.RecordSet, error) // InterDircute a allegrosql memex. 102 InterDircuteInternal(context.Context, string) ([]sqlexec.RecordSet, error) // InterDircute a internal allegrosql memex. 103 InterDircuteStmt(context.Context, ast.StmtNode) (sqlexec.RecordSet, error) 104 Parse(ctx context.Context, allegrosql string) ([]ast.StmtNode, error) 105 String() string // String is used to debug. 106 CommitTxn(context.Context) error 107 RollbackTxn(context.Context) 108 // PrepareStmt executes prepare memex in binary protocol. 109 PrepareStmt(allegrosql string) (stmtID uint32, paramCount int, fields []*ast.ResultField, err error) 110 // InterDircutePreparedStmt executes a prepared memex. 111 InterDircutePreparedStmt(ctx context.Context, stmtID uint32, param []types.Causet) (sqlexec.RecordSet, error) 112 DropPreparedStmt(stmtID uint32) error 113 SetClientCapability(uint32) // Set client capability flags. 114 SetConnectionID(uint64) 115 SetCommandValue(byte) 116 SetProcessInfo(string, time.Time, byte, uint64) 117 SetTLSState(*tls.ConnectionState) 118 SetDefCauslation(coID int) error 119 SetStochastikManager(soliton.StochastikManager) 120 Close() 121 Auth(user *auth.UserIdentity, auth []byte, salt []byte) bool 122 AuthWithoutVerification(user *auth.UserIdentity) bool 123 ShowProcess() *soliton.ProcessInfo 124 // PrepareTxnCtx is exported for test. 125 PrepareTxnCtx(context.Context) 126 // FieldList returns fields list of a causet. 127 FieldList(blockName string) (fields []*ast.ResultField, err error) 128 } 129 130 var ( 131 _ Stochastik = (*stochastik)(nil) 132 ) 133 134 type stmtRecord struct { 135 st sqlexec.Statement 136 stmtCtx *stmtctx.StatementContext 137 } 138 139 // StmtHistory holds all histories of memexs in a txn. 140 type StmtHistory struct { 141 history []*stmtRecord 142 } 143 144 // Add appends a stmt to history list. 145 func (h *StmtHistory) Add(st sqlexec.Statement, stmtCtx *stmtctx.StatementContext) { 146 s := &stmtRecord{ 147 st: st, 148 stmtCtx: stmtCtx, 149 } 150 h.history = append(h.history, s) 151 } 152 153 // Count returns the count of the history. 154 func (h *StmtHistory) Count() int { 155 return len(h.history) 156 } 157 158 type stochastik struct { 159 // processInfo is used by ShowProcess(), and should be modified atomically. 160 processInfo atomic.Value 161 txn TxnState 162 163 mu struct { 164 sync.RWMutex 165 values map[fmt.Stringer]interface{} 166 } 167 168 currentCtx context.Context // only use for runtime.trace, Please NEVER use it. 169 currentCauset causetembedded.Causet 170 171 causetstore ekv.CausetStorage 172 173 BerolinaSQL *BerolinaSQL.BerolinaSQL 174 175 preparedCausetCache *ekvcache.SimpleLRUCache 176 177 stochastikVars *variable.StochastikVars 178 stochastikManager soliton.StochastikManager 179 180 statsDefCauslector *handle.StochastikStatsDefCauslector 181 // dbsTenantChecker is used in `select milevadb_is_dbs_tenant()` memex; 182 dbsTenantChecker tenant.DBSTenantChecker 183 // lockedTables use to record the causet locks hold by the stochastik. 184 lockedTables map[int64]perceptron.TableLockTpInfo 185 186 // client shared interlock client per stochastik 187 client ekv.Client 188 } 189 190 // AddTableLock adds causet dagger to the stochastik dagger map. 191 func (s *stochastik) AddTableLock(locks []perceptron.TableLockTpInfo) { 192 for _, l := range locks { 193 s.lockedTables[l.TableID] = l 194 } 195 } 196 197 // ReleaseTableLocks releases causet dagger in the stochastik dagger map. 198 func (s *stochastik) ReleaseTableLocks(locks []perceptron.TableLockTpInfo) { 199 for _, l := range locks { 200 delete(s.lockedTables, l.TableID) 201 } 202 } 203 204 // ReleaseTableLockByTableIDs releases causet dagger in the stochastik dagger map by causet ID. 205 func (s *stochastik) ReleaseTableLockByTableIDs(blockIDs []int64) { 206 for _, tblID := range blockIDs { 207 delete(s.lockedTables, tblID) 208 } 209 } 210 211 // CheckTableLocked checks the causet dagger. 212 func (s *stochastik) CheckTableLocked(tblID int64) (bool, perceptron.TableLockType) { 213 lt, ok := s.lockedTables[tblID] 214 if !ok { 215 return false, perceptron.TableLockNone 216 } 217 return true, lt.Tp 218 } 219 220 // GetAllTableLocks gets all causet locks causet id and EDB id hold by the stochastik. 221 func (s *stochastik) GetAllTableLocks() []perceptron.TableLockTpInfo { 222 lockTpInfo := make([]perceptron.TableLockTpInfo, 0, len(s.lockedTables)) 223 for _, tl := range s.lockedTables { 224 lockTpInfo = append(lockTpInfo, tl) 225 } 226 return lockTpInfo 227 } 228 229 // HasLockedTables uses to check whether this stochastik locked any blocks. 230 // If so, the stochastik can only visit the causet which locked by self. 231 func (s *stochastik) HasLockedTables() bool { 232 b := len(s.lockedTables) > 0 233 return b 234 } 235 236 // ReleaseAllTableLocks releases all causet locks hold by the stochastik. 237 func (s *stochastik) ReleaseAllTableLocks() { 238 s.lockedTables = make(map[int64]perceptron.TableLockTpInfo) 239 } 240 241 // DBSTenantChecker returns s.dbsTenantChecker. 242 func (s *stochastik) DBSTenantChecker() tenant.DBSTenantChecker { 243 return s.dbsTenantChecker 244 } 245 246 func (s *stochastik) cleanRetryInfo() { 247 if s.stochastikVars.RetryInfo.Retrying { 248 return 249 } 250 251 retryInfo := s.stochastikVars.RetryInfo 252 defer retryInfo.Clean() 253 if len(retryInfo.DroppedPreparedStmtIDs) == 0 { 254 return 255 } 256 257 planCacheEnabled := causetembedded.PreparedCausetCacheEnabled() 258 var cacheKey ekvcache.Key 259 var preparedAst *ast.Prepared 260 if planCacheEnabled { 261 firstStmtID := retryInfo.DroppedPreparedStmtIDs[0] 262 if preparedPointer, ok := s.stochastikVars.PreparedStmts[firstStmtID]; ok { 263 preparedObj, ok := preparedPointer.(*causetembedded.CachedPrepareStmt) 264 if ok { 265 preparedAst = preparedObj.PreparedAst 266 cacheKey = causetembedded.NewPSTMTCausetCacheKey(s.stochastikVars, firstStmtID, preparedAst.SchemaVersion) 267 } 268 } 269 } 270 for i, stmtID := range retryInfo.DroppedPreparedStmtIDs { 271 if planCacheEnabled { 272 if i > 0 && preparedAst != nil { 273 causetembedded.SetPstmtIDSchemaVersion(cacheKey, stmtID, preparedAst.SchemaVersion, s.stochastikVars.IsolationReadEngines) 274 } 275 s.PreparedCausetCache().Delete(cacheKey) 276 } 277 s.stochastikVars.RemovePreparedStmt(stmtID) 278 } 279 } 280 281 func (s *stochastik) Status() uint16 { 282 return s.stochastikVars.Status 283 } 284 285 func (s *stochastik) LastInsertID() uint64 { 286 if s.stochastikVars.StmtCtx.LastInsertID > 0 { 287 return s.stochastikVars.StmtCtx.LastInsertID 288 } 289 return s.stochastikVars.StmtCtx.InsertID 290 } 291 292 func (s *stochastik) LastMessage() string { 293 return s.stochastikVars.StmtCtx.GetMessage() 294 } 295 296 func (s *stochastik) AffectedRows() uint64 { 297 return s.stochastikVars.StmtCtx.AffectedRows() 298 } 299 300 func (s *stochastik) SetClientCapability(capability uint32) { 301 s.stochastikVars.ClientCapability = capability 302 } 303 304 func (s *stochastik) SetConnectionID(connectionID uint64) { 305 s.stochastikVars.ConnectionID = connectionID 306 } 307 308 func (s *stochastik) SetTLSState(tlsState *tls.ConnectionState) { 309 // If user is not connected via TLS, then tlsState == nil. 310 if tlsState != nil { 311 s.stochastikVars.TLSConnectionState = tlsState 312 } 313 } 314 315 func (s *stochastik) SetCommandValue(command byte) { 316 atomic.StoreUint32(&s.stochastikVars.CommandValue, uint32(command)) 317 } 318 319 func (s *stochastik) SetDefCauslation(coID int) error { 320 cs, co, err := charset.GetCharsetInfoByID(coID) 321 if err != nil { 322 return err 323 } 324 for _, v := range variable.SetNamesVariables { 325 terror.Log(s.stochastikVars.SetSystemVar(v, cs)) 326 } 327 return s.stochastikVars.SetSystemVar(variable.DefCauslationConnection, co) 328 } 329 330 func (s *stochastik) PreparedCausetCache() *ekvcache.SimpleLRUCache { 331 return s.preparedCausetCache 332 } 333 334 func (s *stochastik) SetStochastikManager(sm soliton.StochastikManager) { 335 s.stochastikManager = sm 336 } 337 338 func (s *stochastik) GetStochastikManager() soliton.StochastikManager { 339 return s.stochastikManager 340 } 341 342 func (s *stochastik) StoreQueryFeedback(feedback interface{}) { 343 if s.statsDefCauslector != nil { 344 do, err := GetPetri(s.causetstore) 345 if err != nil { 346 logutil.BgLogger().Debug("petri not found", zap.Error(err)) 347 metrics.StoreQueryFeedbackCounter.WithLabelValues(metrics.LblError).Inc() 348 return 349 } 350 err = s.statsDefCauslector.StoreQueryFeedback(feedback, do.StatsHandle()) 351 if err != nil { 352 logutil.BgLogger().Debug("causetstore query feedback", zap.Error(err)) 353 metrics.StoreQueryFeedbackCounter.WithLabelValues(metrics.LblError).Inc() 354 return 355 } 356 metrics.StoreQueryFeedbackCounter.WithLabelValues(metrics.LblOK).Inc() 357 } 358 } 359 360 // FieldList returns fields list of a causet. 361 func (s *stochastik) FieldList(blockName string) ([]*ast.ResultField, error) { 362 is := schemareplicant.GetSchemaReplicant(s) 363 dbName := perceptron.NewCIStr(s.GetStochastikVars().CurrentDB) 364 tName := perceptron.NewCIStr(blockName) 365 pm := privilege.GetPrivilegeManager(s) 366 if pm != nil && s.stochastikVars.User != nil { 367 if !pm.RequestVerification(s.stochastikVars.ActiveRoles, dbName.O, tName.O, "", allegrosql.AllPrivMask) { 368 user := s.stochastikVars.User 369 u := user.Username 370 h := user.Hostname 371 if len(user.AuthUsername) > 0 && len(user.AuthHostname) > 0 { 372 u = user.AuthUsername 373 h = user.AuthHostname 374 } 375 return nil, causetembedded.ErrTableaccessDenied.GenWithStackByArgs("SELECT", u, h, blockName) 376 } 377 } 378 causet, err := is.TableByName(dbName, tName) 379 if err != nil { 380 return nil, err 381 } 382 383 defcaus := causet.DefCauss() 384 fields := make([]*ast.ResultField, 0, len(defcaus)) 385 for _, col := range causet.DefCauss() { 386 rf := &ast.ResultField{ 387 DeferredCausetAsName: col.Name, 388 TableAsName: tName, 389 DBName: dbName, 390 Block: causet.Meta(), 391 DeferredCauset: col.DeferredCausetInfo, 392 } 393 fields = append(fields, rf) 394 } 395 return fields, nil 396 } 397 398 func (s *stochastik) doCommit(ctx context.Context) error { 399 if !s.txn.Valid() { 400 return nil 401 } 402 defer func() { 403 s.txn.changeToInvalid() 404 s.stochastikVars.SetStatusFlag(allegrosql.ServerStatusInTrans, false) 405 }() 406 if s.txn.IsReadOnly() { 407 return nil 408 } 409 410 // mockCommitError and mockGetTSErrorInRetry use to test PR #8743. 411 failpoint.Inject("mockCommitError", func(val failpoint.Value) { 412 if val.(bool) && ekv.IsMockCommitErrorEnable() { 413 ekv.MockCommitErrorDisable() 414 failpoint.Return(ekv.ErrTxnRetryable) 415 } 416 }) 417 418 if s.stochastikVars.BinlogClient != nil { 419 prewriteValue := binloginfo.GetPrewriteValue(s, false) 420 if prewriteValue != nil { 421 prewriteData, err := prewriteValue.Marshal() 422 if err != nil { 423 return errors.Trace(err) 424 } 425 info := &binloginfo.BinlogInfo{ 426 Data: &binlog.Binlog{ 427 Tp: binlog.BinlogType_Prewrite, 428 PrewriteValue: prewriteData, 429 }, 430 Client: s.stochastikVars.BinlogClient, 431 } 432 s.txn.SetOption(ekv.BinlogInfo, info) 433 } 434 } 435 436 // Get the related causet or partition IDs. 437 relatedPhysicalTables := s.GetStochastikVars().TxnCtx.TableDeltaMap 438 physicalTableIDs := make([]int64, 0, len(relatedPhysicalTables)) 439 for id := range relatedPhysicalTables { 440 physicalTableIDs = append(physicalTableIDs, id) 441 } 442 // Set this option for 2 phase commit to validate schemaReplicant lease. 443 s.txn.SetOption(ekv.SchemaChecker, petri.NewSchemaChecker(petri.GetPetri(s), s.stochastikVars.TxnCtx.SchemaVersion, physicalTableIDs)) 444 s.txn.SetOption(ekv.SchemaReplicant, s.stochastikVars.TxnCtx.SchemaReplicant) 445 s.txn.SetOption(ekv.CommitHook, func(info ekv.TxnInfo, _ error) { s.stochastikVars.LastTxnInfo = info }) 446 if s.GetStochastikVars().EnableAmendPessimisticTxn { 447 s.txn.SetOption(ekv.SchemaAmender, NewSchemaAmenderForEinsteinDBTxn(s)) 448 } 449 450 return s.txn.Commit(stochastikctx.SetCommitCtx(ctx, s)) 451 } 452 453 func (s *stochastik) doCommitWithRetry(ctx context.Context) error { 454 defer func() { 455 s.GetStochastikVars().SetTxnIsolationLevelOneShotStateForNextTxn() 456 s.txn.changeToInvalid() 457 s.cleanRetryInfo() 458 }() 459 if !s.txn.Valid() { 460 // If the transaction is invalid, maybe it has already been rolled back by the client. 461 return nil 462 } 463 txnSize := s.txn.Size() 464 isPessimistic := s.txn.IsPessimistic() 465 if span := opentracing.SpanFromContext(ctx); span != nil && span.Tracer() != nil { 466 span1 := span.Tracer().StartSpan("stochastik.doCommitWitRetry", opentracing.ChildOf(span.Context())) 467 defer span1.Finish() 468 ctx = opentracing.ContextWithSpan(ctx, span1) 469 } 470 err := s.doCommit(ctx) 471 if err != nil { 472 commitRetryLimit := s.stochastikVars.RetryLimit 473 if !s.stochastikVars.TxnCtx.CouldRetry { 474 commitRetryLimit = 0 475 } 476 // Don't retry in BatchInsert mode. As a counter-example, insert into t1 select * from t2, 477 // BatchInsert already commit the first batch 1000 rows, then it commit 1000-2000 and retry the memex, 478 // Finally t1 will have more data than t2, with no errors return to user! 479 if s.isTxnRetryableError(err) && !s.stochastikVars.BatchInsert && commitRetryLimit > 0 && !isPessimistic { 480 logutil.Logger(ctx).Warn("allegrosql", 481 zap.String("label", s.getALLEGROSQLLabel()), 482 zap.Error(err), 483 zap.String("txn", s.txn.GoString())) 484 // Transactions will retry 2 ~ commitRetryLimit times. 485 // We make larger transactions retry less times to prevent cluster resource outage. 486 txnSizeRate := float64(txnSize) / float64(ekv.TxnTotalSizeLimit) 487 maxRetryCount := commitRetryLimit - int64(float64(commitRetryLimit-1)*txnSizeRate) 488 err = s.retry(ctx, uint(maxRetryCount)) 489 } else { 490 logutil.Logger(ctx).Warn("can not retry txn", 491 zap.String("label", s.getALLEGROSQLLabel()), 492 zap.Error(err), 493 zap.Bool("IsBatchInsert", s.stochastikVars.BatchInsert), 494 zap.Bool("IsPessimistic", isPessimistic), 495 zap.Bool("InRestrictedALLEGROSQL", s.stochastikVars.InRestrictedALLEGROSQL), 496 zap.Int64("milevadb_retry_limit", s.stochastikVars.RetryLimit), 497 zap.Bool("milevadb_disable_txn_auto_retry", s.stochastikVars.DisableTxnAutoRetry)) 498 } 499 } 500 counter := s.stochastikVars.TxnCtx.StatementCount 501 duration := time.Since(s.GetStochastikVars().TxnCtx.CreateTime).Seconds() 502 s.recordOnTransactionInterDircution(err, counter, duration) 503 504 if err != nil { 505 logutil.Logger(ctx).Warn("commit failed", 506 zap.String("finished txn", s.txn.GoString()), 507 zap.Error(err)) 508 return err 509 } 510 mapper := s.GetStochastikVars().TxnCtx.TableDeltaMap 511 if s.statsDefCauslector != nil && mapper != nil { 512 for id, item := range mapper { 513 s.statsDefCauslector.UFIDelate(id, item.Delta, item.Count, &item.DefCausSize) 514 } 515 } 516 return nil 517 } 518 519 func (s *stochastik) CommitTxn(ctx context.Context) error { 520 if span := opentracing.SpanFromContext(ctx); span != nil && span.Tracer() != nil { 521 span1 := span.Tracer().StartSpan("stochastik.CommitTxn", opentracing.ChildOf(span.Context())) 522 defer span1.Finish() 523 ctx = opentracing.ContextWithSpan(ctx, span1) 524 } 525 526 var commitDetail *execdetails.CommitDetails 527 ctx = context.WithValue(ctx, execdetails.CommitDetailCtxKey, &commitDetail) 528 err := s.doCommitWithRetry(ctx) 529 if commitDetail != nil { 530 s.stochastikVars.StmtCtx.MergeInterDircDetails(nil, commitDetail) 531 } 532 533 failpoint.Inject("keepHistory", func(val failpoint.Value) { 534 if val.(bool) { 535 failpoint.Return(err) 536 } 537 }) 538 539 s.stochastikVars.TxnCtx.Cleanup() 540 return err 541 } 542 543 func (s *stochastik) RollbackTxn(ctx context.Context) { 544 if span := opentracing.SpanFromContext(ctx); span != nil && span.Tracer() != nil { 545 span1 := span.Tracer().StartSpan("stochastik.RollbackTxn", opentracing.ChildOf(span.Context())) 546 defer span1.Finish() 547 } 548 549 if s.txn.Valid() { 550 terror.Log(s.txn.Rollback()) 551 } 552 if ctx.Value(inCloseStochastik{}) == nil { 553 s.cleanRetryInfo() 554 } 555 s.txn.changeToInvalid() 556 s.stochastikVars.TxnCtx.Cleanup() 557 s.stochastikVars.SetStatusFlag(allegrosql.ServerStatusInTrans, false) 558 } 559 560 func (s *stochastik) GetClient() ekv.Client { 561 return s.client 562 } 563 564 func (s *stochastik) String() string { 565 // TODO: how to print binded context in values appropriately? 566 sessVars := s.stochastikVars 567 data := map[string]interface{}{ 568 "id": sessVars.ConnectionID, 569 "user": sessVars.User, 570 "currDBName": sessVars.CurrentDB, 571 "status": sessVars.Status, 572 "strictMode": sessVars.StrictALLEGROSQLMode, 573 } 574 if s.txn.Valid() { 575 // if txn is committed or rolled back, txn is nil. 576 data["txn"] = s.txn.String() 577 } 578 if sessVars.SnapshotTS != 0 { 579 data["snapshotTS"] = sessVars.SnapshotTS 580 } 581 if sessVars.StmtCtx.LastInsertID > 0 { 582 data["lastInsertID"] = sessVars.StmtCtx.LastInsertID 583 } 584 if len(sessVars.PreparedStmts) > 0 { 585 data["preparedStmtCount"] = len(sessVars.PreparedStmts) 586 } 587 b, err := json.MarshalIndent(data, "", " ") 588 terror.Log(errors.Trace(err)) 589 return string(b) 590 } 591 592 const sqlLogMaxLen = 1024 593 594 // SchemaChangedWithoutRetry is used for testing. 595 var SchemaChangedWithoutRetry uint32 596 597 func (s *stochastik) getALLEGROSQLLabel() string { 598 if s.stochastikVars.InRestrictedALLEGROSQL { 599 return metrics.LblInternal 600 } 601 return metrics.LblGeneral 602 } 603 604 func (s *stochastik) isInternal() bool { 605 return s.stochastikVars.InRestrictedALLEGROSQL 606 } 607 608 func (s *stochastik) isTxnRetryableError(err error) bool { 609 if atomic.LoadUint32(&SchemaChangedWithoutRetry) == 1 { 610 return ekv.IsTxnRetryableError(err) 611 } 612 return ekv.IsTxnRetryableError(err) || petri.ErrSchemaReplicantChanged.Equal(err) 613 } 614 615 func (s *stochastik) checkTxnAborted(stmt sqlexec.Statement) error { 616 var err error 617 if atomic.LoadUint32(&s.GetStochastikVars().TxnCtx.LockExpire) > 0 { 618 err = einsteindb.ErrLockExpire 619 } else { 620 return nil 621 } 622 // If the transaction is aborted, the following memexs do not need to execute, except `commit` and `rollback`, 623 // because they are used to finish the aborted transaction. 624 if _, ok := stmt.(*interlock.InterDircStmt).StmtNode.(*ast.CommitStmt); ok { 625 return nil 626 } 627 if _, ok := stmt.(*interlock.InterDircStmt).StmtNode.(*ast.RollbackStmt); ok { 628 return nil 629 } 630 return err 631 } 632 633 func (s *stochastik) retry(ctx context.Context, maxCnt uint) (err error) { 634 var retryCnt uint 635 defer func() { 636 s.stochastikVars.RetryInfo.Retrying = false 637 // retryCnt only increments on retryable error, so +1 here. 638 metrics.StochastikRetry.Observe(float64(retryCnt + 1)) 639 s.stochastikVars.SetStatusFlag(allegrosql.ServerStatusInTrans, false) 640 if err != nil { 641 s.RollbackTxn(ctx) 642 } 643 s.txn.changeToInvalid() 644 }() 645 646 connID := s.stochastikVars.ConnectionID 647 s.stochastikVars.RetryInfo.Retrying = true 648 if atomic.LoadUint32(&s.stochastikVars.TxnCtx.ForUFIDelate) == 1 { 649 err = ErrForUFIDelateCantRetry.GenWithStackByArgs(connID) 650 return err 651 } 652 653 nh := GetHistory(s) 654 var schemaVersion int64 655 sessVars := s.GetStochastikVars() 656 orgStartTS := sessVars.TxnCtx.StartTS 657 label := s.getALLEGROSQLLabel() 658 for { 659 s.PrepareTxnCtx(ctx) 660 s.stochastikVars.RetryInfo.ResetOffset() 661 for i, sr := range nh.history { 662 st := sr.st 663 s.stochastikVars.StmtCtx = sr.stmtCtx 664 s.stochastikVars.StmtCtx.ResetForRetry() 665 s.stochastikVars.PreparedParams = s.stochastikVars.PreparedParams[:0] 666 schemaVersion, err = st.RebuildCauset(ctx) 667 if err != nil { 668 return err 669 } 670 671 if retryCnt == 0 { 672 // We do not have to log the query every time. 673 // We print the queries at the first try only. 674 allegrosql := sqlForLog(st.GetTextToLog()) 675 if !config.RedactLogEnabled() { 676 allegrosql += sessVars.PreparedParams.String() 677 } 678 logutil.Logger(ctx).Warn("retrying", 679 zap.Int64("schemaVersion", schemaVersion), 680 zap.Uint("retryCnt", retryCnt), 681 zap.Int("queryNum", i), 682 zap.String("allegrosql", allegrosql)) 683 } else { 684 logutil.Logger(ctx).Warn("retrying", 685 zap.Int64("schemaVersion", schemaVersion), 686 zap.Uint("retryCnt", retryCnt), 687 zap.Int("queryNum", i)) 688 } 689 _, err = st.InterDirc(ctx) 690 if err != nil { 691 s.StmtRollback() 692 break 693 } 694 s.StmtCommit() 695 } 696 logutil.Logger(ctx).Warn("transaction association", 697 zap.Uint64("retrying txnStartTS", s.GetStochastikVars().TxnCtx.StartTS), 698 zap.Uint64("original txnStartTS", orgStartTS)) 699 failpoint.Inject("preCommitHook", func() { 700 hook, ok := ctx.Value("__preCommitHook").(func()) 701 if ok { 702 hook() 703 } 704 }) 705 if err == nil { 706 err = s.doCommit(ctx) 707 if err == nil { 708 break 709 } 710 } 711 if !s.isTxnRetryableError(err) { 712 logutil.Logger(ctx).Warn("allegrosql", 713 zap.String("label", label), 714 zap.Stringer("stochastik", s), 715 zap.Error(err)) 716 metrics.StochastikRetryErrorCounter.WithLabelValues(label, metrics.LblUnretryable).Inc() 717 return err 718 } 719 retryCnt++ 720 if retryCnt >= maxCnt { 721 logutil.Logger(ctx).Warn("allegrosql", 722 zap.String("label", label), 723 zap.Uint("retry reached max count", retryCnt)) 724 metrics.StochastikRetryErrorCounter.WithLabelValues(label, metrics.LblReachMax).Inc() 725 return err 726 } 727 logutil.Logger(ctx).Warn("allegrosql", 728 zap.String("label", label), 729 zap.Error(err), 730 zap.String("txn", s.txn.GoString())) 731 ekv.BackOff(retryCnt) 732 s.txn.changeToInvalid() 733 s.stochastikVars.SetStatusFlag(allegrosql.ServerStatusInTrans, false) 734 } 735 return err 736 } 737 738 func sqlForLog(allegrosql string) string { 739 if len(allegrosql) > sqlLogMaxLen { 740 allegrosql = allegrosql[:sqlLogMaxLen] + fmt.Sprintf("(len:%d)", len(allegrosql)) 741 } 742 return interlock.QueryReplacer.Replace(allegrosql) 743 } 744 745 type stochastikPool interface { 746 Get() (pools.Resource, error) 747 Put(pools.Resource) 748 } 749 750 func (s *stochastik) sysStochastikPool() stochastikPool { 751 return petri.GetPetri(s).SysStochastikPool() 752 } 753 754 // InterDircRestrictedALLEGROSQL implements RestrictedALLEGROSQLInterlockingDirectorate interface. 755 // This is used for executing some restricted allegrosql memexs, usually executed during a normal memex execution. 756 // Unlike normal InterDirc, it doesn't reset memex status, doesn't commit or rollback the current transaction 757 // and doesn't write binlog. 758 func (s *stochastik) InterDircRestrictedALLEGROSQL(allegrosql string) ([]chunk.Row, []*ast.ResultField, error) { 759 return s.InterDircRestrictedALLEGROSQLWithContext(context.TODO(), allegrosql) 760 } 761 762 // InterDircRestrictedALLEGROSQLWithContext implements RestrictedALLEGROSQLInterlockingDirectorate interface. 763 func (s *stochastik) InterDircRestrictedALLEGROSQLWithContext(ctx context.Context, allegrosql string) ([]chunk.Row, []*ast.ResultField, error) { 764 // Use special stochastik to execute the allegrosql. 765 tmp, err := s.sysStochastikPool().Get() 766 if err != nil { 767 return nil, nil, err 768 } 769 se := tmp.(*stochastik) 770 // The special stochastik will share the `InspectionTableCache` with current stochastik 771 // if the current stochastik in inspection mode. 772 if cache := s.stochastikVars.InspectionTableCache; cache != nil { 773 se.stochastikVars.InspectionTableCache = cache 774 defer func() { se.stochastikVars.InspectionTableCache = nil }() 775 } 776 if ok := s.stochastikVars.OptimizerUseInvisibleIndexes; ok { 777 se.stochastikVars.OptimizerUseInvisibleIndexes = true 778 defer func() { se.stochastikVars.OptimizerUseInvisibleIndexes = false }() 779 } 780 defer func() { 781 if se != nil && se.GetStochastikVars().StmtCtx.WarningCount() > 0 { 782 warnings := se.GetStochastikVars().StmtCtx.GetWarnings() 783 s.GetStochastikVars().StmtCtx.AppendWarnings(warnings) 784 } 785 s.sysStochastikPool().Put(tmp) 786 }() 787 metrics.StochastikRestrictedALLEGROSQLCounter.Inc() 788 789 return execRestrictedALLEGROSQL(ctx, se, allegrosql) 790 } 791 792 // InterDircRestrictedALLEGROSQLWithSnapshot implements RestrictedALLEGROSQLInterlockingDirectorate interface. 793 // This is used for executing some restricted allegrosql memexs with snapshot. 794 // If current stochastik sets the snapshot timestamp, then execute with this snapshot timestamp. 795 // Otherwise, execute with the current transaction start timestamp if the transaction is valid. 796 func (s *stochastik) InterDircRestrictedALLEGROSQLWithSnapshot(allegrosql string) ([]chunk.Row, []*ast.ResultField, error) { 797 ctx := context.TODO() 798 799 // Use special stochastik to execute the allegrosql. 800 tmp, err := s.sysStochastikPool().Get() 801 if err != nil { 802 return nil, nil, err 803 } 804 se := tmp.(*stochastik) 805 // The special stochastik will share the `InspectionTableCache` with current stochastik 806 // if the current stochastik in inspection mode. 807 if cache := s.stochastikVars.InspectionTableCache; cache != nil { 808 se.stochastikVars.InspectionTableCache = cache 809 defer func() { se.stochastikVars.InspectionTableCache = nil }() 810 } 811 defer s.sysStochastikPool().Put(tmp) 812 metrics.StochastikRestrictedALLEGROSQLCounter.Inc() 813 var snapshot uint64 814 txn, err := s.Txn(false) 815 if err != nil { 816 return nil, nil, err 817 } 818 if txn.Valid() { 819 snapshot = s.txn.StartTS() 820 } 821 if s.stochastikVars.SnapshotTS != 0 { 822 snapshot = s.stochastikVars.SnapshotTS 823 } 824 // Set snapshot. 825 if snapshot != 0 { 826 se.stochastikVars.SnapshotschemaReplicant, err = petri.GetPetri(s).GetSnapshotSchemaReplicant(snapshot) 827 if err != nil { 828 return nil, nil, err 829 } 830 if err := se.stochastikVars.SetSystemVar(variable.MilevaDBSnapshot, strconv.FormatUint(snapshot, 10)); err != nil { 831 return nil, nil, err 832 } 833 defer func() { 834 if err := se.stochastikVars.SetSystemVar(variable.MilevaDBSnapshot, ""); err != nil { 835 logutil.BgLogger().Error("set milevadbSnapshot error", zap.Error(err)) 836 } 837 se.stochastikVars.SnapshotschemaReplicant = nil 838 }() 839 } 840 if ok := s.stochastikVars.OptimizerUseInvisibleIndexes; ok { 841 se.stochastikVars.OptimizerUseInvisibleIndexes = true 842 defer func() { se.stochastikVars.OptimizerUseInvisibleIndexes = false }() 843 } 844 return execRestrictedALLEGROSQL(ctx, se, allegrosql) 845 } 846 847 func execRestrictedALLEGROSQL(ctx context.Context, se *stochastik, allegrosql string) ([]chunk.Row, []*ast.ResultField, error) { 848 ctx = context.WithValue(ctx, execdetails.StmtInterDircDetailKey, &execdetails.StmtInterDircDetails{}) 849 startTime := time.Now() 850 recordSets, err := se.InterDircute(ctx, allegrosql) 851 if err != nil { 852 return nil, nil, err 853 } 854 855 var ( 856 rows []chunk.Row 857 fields []*ast.ResultField 858 ) 859 // InterDircute all recordset, take out the first one as result. 860 for i, rs := range recordSets { 861 tmp, err := drainRecordSet(ctx, se, rs) 862 if err != nil { 863 return nil, nil, err 864 } 865 if err = rs.Close(); err != nil { 866 return nil, nil, err 867 } 868 869 if i == 0 { 870 rows = tmp 871 fields = rs.Fields() 872 } 873 } 874 metrics.QueryDurationHistogram.WithLabelValues(metrics.LblInternal).Observe(time.Since(startTime).Seconds()) 875 return rows, fields, nil 876 } 877 878 func createStochastikFunc(causetstore ekv.CausetStorage) pools.Factory { 879 return func() (pools.Resource, error) { 880 se, err := createStochastik(causetstore) 881 if err != nil { 882 return nil, err 883 } 884 err = variable.SetStochastikSystemVar(se.stochastikVars, variable.AutoCommit, types.NewStringCauset("1")) 885 if err != nil { 886 return nil, err 887 } 888 err = variable.SetStochastikSystemVar(se.stochastikVars, variable.MaxInterDircutionTime, types.NewUintCauset(0)) 889 if err != nil { 890 return nil, errors.Trace(err) 891 } 892 err = variable.SetStochastikSystemVar(se.stochastikVars, variable.MaxAllowedPacket, types.NewStringCauset("67108864")) 893 if err != nil { 894 return nil, errors.Trace(err) 895 } 896 se.stochastikVars.CommonGlobalLoaded = true 897 se.stochastikVars.InRestrictedALLEGROSQL = true 898 return se, nil 899 } 900 } 901 902 func createStochastikWithPetriFunc(causetstore ekv.CausetStorage) func(*petri.Petri) (pools.Resource, error) { 903 return func(dom *petri.Petri) (pools.Resource, error) { 904 se, err := CreateStochastikWithPetri(causetstore, dom) 905 if err != nil { 906 return nil, err 907 } 908 err = variable.SetStochastikSystemVar(se.stochastikVars, variable.AutoCommit, types.NewStringCauset("1")) 909 if err != nil { 910 return nil, err 911 } 912 err = variable.SetStochastikSystemVar(se.stochastikVars, variable.MaxInterDircutionTime, types.NewUintCauset(0)) 913 if err != nil { 914 return nil, errors.Trace(err) 915 } 916 se.stochastikVars.CommonGlobalLoaded = true 917 se.stochastikVars.InRestrictedALLEGROSQL = true 918 return se, nil 919 } 920 } 921 922 func drainRecordSet(ctx context.Context, se *stochastik, rs sqlexec.RecordSet) ([]chunk.Row, error) { 923 var rows []chunk.Row 924 req := rs.NewChunk() 925 for { 926 err := rs.Next(ctx, req) 927 if err != nil || req.NumRows() == 0 { 928 return rows, err 929 } 930 iter := chunk.NewIterator4Chunk(req) 931 for r := iter.Begin(); r != iter.End(); r = iter.Next() { 932 rows = append(rows, r) 933 } 934 req = chunk.Renew(req, se.stochastikVars.MaxChunkSize) 935 } 936 } 937 938 // getInterDircRet executes restricted allegrosql and the result is one column. 939 // It returns a string value. 940 func (s *stochastik) getInterDircRet(ctx stochastikctx.Context, allegrosql string) (string, error) { 941 rows, fields, err := s.InterDircRestrictedALLEGROSQL(allegrosql) 942 if err != nil { 943 return "", err 944 } 945 if len(rows) == 0 { 946 return "", interlock.ErrResultIsEmpty 947 } 948 d := rows[0].GetCauset(0, &fields[0].DeferredCauset.FieldType) 949 value, err := d.ToString() 950 if err != nil { 951 return "", err 952 } 953 return value, nil 954 } 955 956 // GetAllSysVars implements GlobalVarAccessor.GetAllSysVars interface. 957 func (s *stochastik) GetAllSysVars() (map[string]string, error) { 958 if s.Value(stochastikctx.Initing) != nil { 959 return nil, nil 960 } 961 allegrosql := `SELECT VARIABLE_NAME, VARIABLE_VALUE FROM %s.%s;` 962 allegrosql = fmt.Sprintf(allegrosql, allegrosql.SystemDB, allegrosql.GlobalVariablesTable) 963 rows, _, err := s.InterDircRestrictedALLEGROSQL(allegrosql) 964 if err != nil { 965 return nil, err 966 } 967 ret := make(map[string]string, len(rows)) 968 for _, r := range rows { 969 k, v := r.GetString(0), r.GetString(1) 970 ret[k] = v 971 } 972 return ret, nil 973 } 974 975 // GetGlobalSysVar implements GlobalVarAccessor.GetGlobalSysVar interface. 976 func (s *stochastik) GetGlobalSysVar(name string) (string, error) { 977 if s.Value(stochastikctx.Initing) != nil { 978 // When running bootstrap or upgrade, we should not access global storage. 979 return "", nil 980 } 981 allegrosql := fmt.Sprintf(`SELECT VARIABLE_VALUE FROM %s.%s WHERE VARIABLE_NAME="%s";`, 982 allegrosql.SystemDB, allegrosql.GlobalVariablesTable, name) 983 sysVar, err := s.getInterDircRet(s, allegrosql) 984 if err != nil { 985 if interlock.ErrResultIsEmpty.Equal(err) { 986 if sv, ok := variable.SysVars[name]; ok { 987 return sv.Value, nil 988 } 989 return "", variable.ErrUnknownSystemVar.GenWithStackByArgs(name) 990 } 991 return "", err 992 } 993 return sysVar, nil 994 } 995 996 // SetGlobalSysVar implements GlobalVarAccessor.SetGlobalSysVar interface. 997 func (s *stochastik) SetGlobalSysVar(name, value string) error { 998 if name == variable.ALLEGROSQLModeVar { 999 value = allegrosql.FormatALLEGROSQLModeStr(value) 1000 if _, err := allegrosql.GetALLEGROSQLMode(value); err != nil { 1001 return err 1002 } 1003 } 1004 var sVal string 1005 var err error 1006 sVal, err = variable.ValidateSetSystemVar(s.stochastikVars, name, value, variable.ScopeGlobal) 1007 if err != nil { 1008 return err 1009 } 1010 name = strings.ToLower(name) 1011 variable.CheckDeprecationSetSystemVar(s.stochastikVars, name) 1012 allegrosql := fmt.Sprintf(`REPLACE %s.%s VALUES ('%s', '%s');`, 1013 allegrosql.SystemDB, allegrosql.GlobalVariablesTable, name, sVal) 1014 _, _, err = s.InterDircRestrictedALLEGROSQL(allegrosql) 1015 return err 1016 } 1017 1018 func (s *stochastik) ParseALLEGROSQL(ctx context.Context, allegrosql, charset, collation string) ([]ast.StmtNode, []error, error) { 1019 if span := opentracing.SpanFromContext(ctx); span != nil && span.Tracer() != nil { 1020 span1 := span.Tracer().StartSpan("stochastik.ParseALLEGROSQL", opentracing.ChildOf(span.Context())) 1021 defer span1.Finish() 1022 } 1023 defer trace.StartRegion(ctx, "ParseALLEGROSQL").End() 1024 s.BerolinaSQL.SetALLEGROSQLMode(s.stochastikVars.ALLEGROSQLMode) 1025 s.BerolinaSQL.EnableWindowFunc(s.stochastikVars.EnableWindowFunction) 1026 return s.BerolinaSQL.Parse(allegrosql, charset, collation) 1027 } 1028 1029 func (s *stochastik) SetProcessInfo(allegrosql string, t time.Time, command byte, maxInterDircutionTime uint64) { 1030 // If command == allegrosql.ComSleep, it means the ALLEGROALLEGROSQL execution is finished. The processinfo is reset to SLEEP. 1031 // If the ALLEGROALLEGROSQL finished and the stochastik is not in transaction, the current start timestamp need to reset to 0. 1032 // Otherwise, it should be set to the transaction start timestamp. 1033 // Why not reset the transaction start timestamp to 0 when transaction committed? 1034 // Because the select memex and other memexs need this timestamp to read data, 1035 // after the transaction is committed. e.g. SHOW MASTER STATUS; 1036 var curTxnStartTS uint64 1037 if command != allegrosql.ComSleep || s.GetStochastikVars().InTxn() { 1038 curTxnStartTS = s.stochastikVars.TxnCtx.StartTS 1039 } 1040 pi := soliton.ProcessInfo{ 1041 ID: s.stochastikVars.ConnectionID, 1042 EDB: s.stochastikVars.CurrentDB, 1043 Command: command, 1044 Causet: s.currentCauset, 1045 CausetExplainRows: causetembedded.GetExplainRowsForCauset(s.currentCauset), 1046 Time: t, 1047 State: s.Status(), 1048 Info: allegrosql, 1049 CurTxnStartTS: curTxnStartTS, 1050 StmtCtx: s.stochastikVars.StmtCtx, 1051 StatsInfo: causetembedded.GetStatsInfo, 1052 MaxInterDircutionTime: maxInterDircutionTime, 1053 } 1054 _, pi.Digest = s.stochastikVars.StmtCtx.ALLEGROSQLDigest() 1055 if s.stochastikVars.User != nil { 1056 pi.User = s.stochastikVars.User.Username 1057 pi.Host = s.stochastikVars.User.Hostname 1058 } 1059 s.processInfo.CausetStore(&pi) 1060 } 1061 1062 func (s *stochastik) InterDircuteInternal(ctx context.Context, allegrosql string) (recordSets []sqlexec.RecordSet, err error) { 1063 origin := s.stochastikVars.InRestrictedALLEGROSQL 1064 s.stochastikVars.InRestrictedALLEGROSQL = true 1065 defer func() { 1066 s.stochastikVars.InRestrictedALLEGROSQL = origin 1067 }() 1068 return s.InterDircute(ctx, allegrosql) 1069 } 1070 1071 func (s *stochastik) InterDircute(ctx context.Context, allegrosql string) (recordSets []sqlexec.RecordSet, err error) { 1072 if span := opentracing.SpanFromContext(ctx); span != nil && span.Tracer() != nil { 1073 span1 := span.Tracer().StartSpan("stochastik.InterDircute", opentracing.ChildOf(span.Context())) 1074 defer span1.Finish() 1075 ctx = opentracing.ContextWithSpan(ctx, span1) 1076 logutil.Eventf(ctx, "execute: %s", allegrosql) 1077 } 1078 1079 stmtNodes, err := s.Parse(ctx, allegrosql) 1080 if err != nil { 1081 return nil, err 1082 } 1083 if len(stmtNodes) != 1 { 1084 return nil, errors.New("InterDircute() API doesn't support multiple memexs any more") 1085 } 1086 1087 rs, err := s.InterDircuteStmt(ctx, stmtNodes[0]) 1088 if err != nil { 1089 s.stochastikVars.StmtCtx.AppendError(err) 1090 } 1091 if rs == nil { 1092 return nil, err 1093 } 1094 return []sqlexec.RecordSet{rs}, err 1095 } 1096 1097 // Parse parses a query string to raw ast.StmtNode. 1098 func (s *stochastik) Parse(ctx context.Context, allegrosql string) ([]ast.StmtNode, error) { 1099 charsetInfo, collation := s.stochastikVars.GetCharsetInfo() 1100 parseStartTime := time.Now() 1101 stmts, warns, err := s.ParseALLEGROSQL(ctx, allegrosql, charsetInfo, collation) 1102 if err != nil { 1103 s.rollbackOnError(ctx) 1104 1105 // Only print log message when this ALLEGROALLEGROSQL is from the user. 1106 // Mute the warning for internal ALLEGROSQLs. 1107 if !s.stochastikVars.InRestrictedALLEGROSQL { 1108 logutil.Logger(ctx).Warn("parse ALLEGROALLEGROSQL failed", zap.Error(err), zap.String("ALLEGROALLEGROSQL", allegrosql)) 1109 } 1110 return nil, soliton.SyntaxError(err) 1111 } 1112 1113 durParse := time.Since(parseStartTime) 1114 s.GetStochastikVars().DurationParse = durParse 1115 isInternal := s.isInternal() 1116 if isInternal { 1117 stochastikInterDircuteParseDurationInternal.Observe(durParse.Seconds()) 1118 } else { 1119 stochastikInterDircuteParseDurationGeneral.Observe(durParse.Seconds()) 1120 } 1121 for _, warn := range warns { 1122 s.stochastikVars.StmtCtx.AppendWarning(soliton.SyntaxWarn(warn)) 1123 } 1124 return stmts, nil 1125 } 1126 1127 func (s *stochastik) InterDircuteStmt(ctx context.Context, stmtNode ast.StmtNode) (sqlexec.RecordSet, error) { 1128 if span := opentracing.SpanFromContext(ctx); span != nil && span.Tracer() != nil { 1129 span1 := span.Tracer().StartSpan("stochastik.InterDircuteStmt", opentracing.ChildOf(span.Context())) 1130 defer span1.Finish() 1131 ctx = opentracing.ContextWithSpan(ctx, span1) 1132 } 1133 1134 s.PrepareTxnCtx(ctx) 1135 err := s.loadCommonGlobalVariablesIfNeeded() 1136 if err != nil { 1137 return nil, err 1138 } 1139 1140 s.stochastikVars.StartTime = time.Now() 1141 1142 // Some executions are done in compile stage, so we reset them before compile. 1143 if err := interlock.ResetContextOfStmt(s, stmtNode); err != nil { 1144 return nil, err 1145 } 1146 1147 // Transform abstract syntax tree to a physical plan(stored in interlock.InterDircStmt). 1148 compiler := interlock.Compiler{Ctx: s} 1149 stmt, err := compiler.Compile(ctx, stmtNode) 1150 if err != nil { 1151 s.rollbackOnError(ctx) 1152 1153 // Only print log message when this ALLEGROALLEGROSQL is from the user. 1154 // Mute the warning for internal ALLEGROSQLs. 1155 if !s.stochastikVars.InRestrictedALLEGROSQL { 1156 logutil.Logger(ctx).Warn("compile ALLEGROALLEGROSQL failed", zap.Error(err), zap.String("ALLEGROALLEGROSQL", stmtNode.Text())) 1157 } 1158 return nil, err 1159 } 1160 durCompile := time.Since(s.stochastikVars.StartTime) 1161 s.GetStochastikVars().DurationCompile = durCompile 1162 if s.isInternal() { 1163 stochastikInterDircuteCompileDurationInternal.Observe(durCompile.Seconds()) 1164 } else { 1165 stochastikInterDircuteCompileDurationGeneral.Observe(durCompile.Seconds()) 1166 } 1167 s.currentCauset = stmt.Causet 1168 1169 // InterDircute the physical plan. 1170 logStmt(stmt, s.stochastikVars) 1171 recordSet, err := runStmt(ctx, s, stmt) 1172 if err != nil { 1173 if !ekv.ErrKeyExists.Equal(err) { 1174 logutil.Logger(ctx).Warn("run memex failed", 1175 zap.Int64("schemaVersion", s.stochastikVars.TxnCtx.SchemaVersion), 1176 zap.Error(err), 1177 zap.String("stochastik", s.String())) 1178 } 1179 return nil, err 1180 } 1181 return recordSet, nil 1182 } 1183 1184 // runStmt executes the sqlexec.Statement and commit or rollback the current transaction. 1185 func runStmt(ctx context.Context, se *stochastik, s sqlexec.Statement) (rs sqlexec.RecordSet, err error) { 1186 if span := opentracing.SpanFromContext(ctx); span != nil && span.Tracer() != nil { 1187 span1 := span.Tracer().StartSpan("stochastik.runStmt", opentracing.ChildOf(span.Context())) 1188 span1.LogKV("allegrosql", s.OriginText()) 1189 defer span1.Finish() 1190 ctx = opentracing.ContextWithSpan(ctx, span1) 1191 } 1192 se.SetValue(stochastikctx.QueryString, s.OriginText()) 1193 if _, ok := s.(*interlock.InterDircStmt).StmtNode.(ast.DBSNode); ok { 1194 se.SetValue(stochastikctx.LastInterDircuteDBS, true) 1195 } else { 1196 se.ClearValue(stochastikctx.LastInterDircuteDBS) 1197 } 1198 1199 sessVars := se.stochastikVars 1200 // Save origTxnCtx here to avoid it reset in the transaction retry. 1201 origTxnCtx := sessVars.TxnCtx 1202 err = se.checkTxnAborted(s) 1203 if err != nil { 1204 return nil, err 1205 } 1206 rs, err = s.InterDirc(ctx) 1207 sessVars.TxnCtx.StatementCount++ 1208 if !s.IsReadOnly(sessVars) { 1209 // All the history should be added here. 1210 if err == nil && sessVars.TxnCtx.CouldRetry { 1211 GetHistory(se).Add(s, sessVars.StmtCtx) 1212 } 1213 1214 // Handle the stmt commit/rollback. 1215 if se.txn.Valid() { 1216 if err != nil { 1217 se.StmtRollback() 1218 } else { 1219 se.StmtCommit() 1220 } 1221 } 1222 } 1223 if rs != nil { 1224 return &execStmtResult{ 1225 RecordSet: rs, 1226 allegrosql: s, 1227 se: se, 1228 }, err 1229 } 1230 1231 err = finishStmt(ctx, se, err, s) 1232 1233 // If it is not a select memex, we record its slow log here, 1234 // then it could include the transaction commit time. 1235 s.(*interlock.InterDircStmt).FinishInterDircuteStmt(origTxnCtx.StartTS, err == nil, false) 1236 return nil, err 1237 } 1238 1239 // execStmtResult is the return value of InterDircuteStmt and it implements the sqlexec.RecordSet interface. 1240 // Why we need a struct to wrap a RecordSet and provide another RecordSet? 1241 // This is because there are so many stochastik state related things that definitely not belongs to the original 1242 // RecordSet, so this struct exists and RecordSet.Close() is overrided handle that. 1243 type execStmtResult struct { 1244 sqlexec.RecordSet 1245 se *stochastik 1246 allegrosql sqlexec.Statement 1247 } 1248 1249 func (rs *execStmtResult) Close() error { 1250 se := rs.se 1251 err := rs.RecordSet.Close() 1252 return finishStmt(context.Background(), se, err, rs.allegrosql) 1253 } 1254 1255 // rollbackOnError makes sure the next memex starts a new transaction with the latest SchemaReplicant. 1256 func (s *stochastik) rollbackOnError(ctx context.Context) { 1257 if !s.stochastikVars.InTxn() { 1258 s.RollbackTxn(ctx) 1259 } 1260 } 1261 1262 // PrepareStmt is used for executing prepare memex in binary protocol 1263 func (s *stochastik) PrepareStmt(allegrosql string) (stmtID uint32, paramCount int, fields []*ast.ResultField, err error) { 1264 if s.stochastikVars.TxnCtx.SchemaReplicant == nil { 1265 // We don't need to create a transaction for prepare memex, just get information schemaReplicant will do. 1266 s.stochastikVars.TxnCtx.SchemaReplicant = petri.GetPetri(s).SchemaReplicant() 1267 } 1268 err = s.loadCommonGlobalVariablesIfNeeded() 1269 if err != nil { 1270 return 1271 } 1272 1273 ctx := context.Background() 1274 inTxn := s.GetStochastikVars().InTxn() 1275 // NewPrepareInterDirc may need startTS to build the interlock, for example prepare memex has subquery in int. 1276 // So we have to call PrepareTxnCtx here. 1277 s.PrepareTxnCtx(ctx) 1278 s.PrepareTSFuture(ctx) 1279 prepareInterDirc := interlock.NewPrepareInterDirc(s, schemareplicant.GetSchemaReplicant(s), allegrosql) 1280 err = prepareInterDirc.Next(ctx, nil) 1281 if err != nil { 1282 return 1283 } 1284 if !inTxn { 1285 // We could start a transaction to build the prepare interlock before, we should rollback it here. 1286 s.RollbackTxn(ctx) 1287 } 1288 return prepareInterDirc.ID, prepareInterDirc.ParamCount, prepareInterDirc.Fields, nil 1289 } 1290 1291 func (s *stochastik) preparedStmtInterDirc(ctx context.Context, 1292 stmtID uint32, prepareStmt *causetembedded.CachedPrepareStmt, args []types.Causet) (sqlexec.RecordSet, error) { 1293 st, err := interlock.CompileInterDircutePreparedStmt(ctx, s, stmtID, args) 1294 if err != nil { 1295 return nil, err 1296 } 1297 stochastikInterDircuteCompileDurationGeneral.Observe(time.Since(s.stochastikVars.StartTime).Seconds()) 1298 logQuery(st.OriginText(), s.stochastikVars) 1299 return runStmt(ctx, s, st) 1300 } 1301 1302 // cachedCausetInterDirc short path currently ONLY for cached "point select plan" execution 1303 func (s *stochastik) cachedCausetInterDirc(ctx context.Context, 1304 stmtID uint32, prepareStmt *causetembedded.CachedPrepareStmt, args []types.Causet) (sqlexec.RecordSet, error) { 1305 prepared := prepareStmt.PreparedAst 1306 // compile InterDircStmt 1307 is := schemareplicant.GetSchemaReplicant(s) 1308 execAst := &ast.InterDircuteStmt{InterDircID: stmtID} 1309 if err := interlock.ResetContextOfStmt(s, execAst); err != nil { 1310 return nil, err 1311 } 1312 execAst.BinaryArgs = args 1313 execCauset, err := causet.OptimizeInterDircStmt(ctx, s, execAst, is) 1314 if err != nil { 1315 return nil, err 1316 } 1317 1318 stmtCtx := s.GetStochastikVars().StmtCtx 1319 stmt := &interlock.InterDircStmt{ 1320 GoCtx: ctx, 1321 SchemaReplicant: is, 1322 Causet: execCauset, 1323 StmtNode: execAst, 1324 Ctx: s, 1325 OutputNames: execCauset.OutputNames(), 1326 PsStmt: prepareStmt, 1327 } 1328 compileDuration := time.Since(s.stochastikVars.StartTime) 1329 stochastikInterDircuteCompileDurationGeneral.Observe(compileDuration.Seconds()) 1330 s.GetStochastikVars().DurationCompile = compileDuration 1331 1332 stmt.Text = prepared.Stmt.Text() 1333 stmtCtx.OriginalALLEGROSQL = stmt.Text 1334 stmtCtx.InitALLEGROSQLDigest(prepareStmt.NormalizedALLEGROSQL, prepareStmt.ALLEGROSQLDigest) 1335 stmtCtx.SetCausetDigest(prepareStmt.NormalizedCauset, prepareStmt.CausetDigest) 1336 logQuery(stmt.GetTextToLog(), s.stochastikVars) 1337 1338 // run InterDircStmt 1339 var resultSet sqlexec.RecordSet 1340 switch prepared.CachedCauset.(type) { 1341 case *causetembedded.PointGetCauset: 1342 resultSet, err = stmt.PointGet(ctx, is) 1343 s.txn.changeToInvalid() 1344 case *causetembedded.UFIDelate: 1345 s.PrepareTSFuture(ctx) 1346 stmtCtx.Priority = ekv.PriorityHigh 1347 resultSet, err = runStmt(ctx, s, stmt) 1348 default: 1349 prepared.CachedCauset = nil 1350 return nil, errors.Errorf("invalid cached plan type") 1351 } 1352 return resultSet, err 1353 } 1354 1355 // IsCachedInterDircOk check if we can execute using plan cached in prepared structure 1356 // Be careful for the short path, current precondition is ths cached plan satisfying 1357 // IsPointGetWithPKOrUniqueKeyByAutoCommit 1358 func (s *stochastik) IsCachedInterDircOk(ctx context.Context, preparedStmt *causetembedded.CachedPrepareStmt) (bool, error) { 1359 prepared := preparedStmt.PreparedAst 1360 if prepared.CachedCauset == nil { 1361 return false, nil 1362 } 1363 // check auto commit 1364 if !s.GetStochastikVars().IsAutocommit() { 1365 return false, nil 1366 } 1367 // check schemaReplicant version 1368 is := schemareplicant.GetSchemaReplicant(s) 1369 if prepared.SchemaVersion != is.SchemaMetaVersion() { 1370 prepared.CachedCauset = nil 1371 return false, nil 1372 } 1373 // maybe we'd better check cached plan type here, current 1374 // only point select/uFIDelate will be cached, see "getPhysicalCauset" func 1375 var ok bool 1376 var err error 1377 switch prepared.CachedCauset.(type) { 1378 case *causetembedded.PointGetCauset: 1379 ok = true 1380 case *causetembedded.UFIDelate: 1381 pointUFIDelate := prepared.CachedCauset.(*causetembedded.UFIDelate) 1382 _, ok = pointUFIDelate.SelectCauset.(*causetembedded.PointGetCauset) 1383 if !ok { 1384 err = errors.Errorf("cached uFIDelate plan not point uFIDelate") 1385 prepared.CachedCauset = nil 1386 return false, err 1387 } 1388 default: 1389 ok = false 1390 } 1391 return ok, err 1392 } 1393 1394 // InterDircutePreparedStmt executes a prepared memex. 1395 func (s *stochastik) InterDircutePreparedStmt(ctx context.Context, stmtID uint32, args []types.Causet) (sqlexec.RecordSet, error) { 1396 s.PrepareTxnCtx(ctx) 1397 var err error 1398 s.stochastikVars.StartTime = time.Now() 1399 preparedPointer, ok := s.stochastikVars.PreparedStmts[stmtID] 1400 if !ok { 1401 err = causetembedded.ErrStmtNotFound 1402 logutil.Logger(ctx).Error("prepared memex not found", zap.Uint32("stmtID", stmtID)) 1403 return nil, err 1404 } 1405 preparedStmt, ok := preparedPointer.(*causetembedded.CachedPrepareStmt) 1406 if !ok { 1407 return nil, errors.Errorf("invalid CachedPrepareStmt type") 1408 } 1409 interlock.CountStmtNode(preparedStmt.PreparedAst.Stmt, s.stochastikVars.InRestrictedALLEGROSQL) 1410 ok, err = s.IsCachedInterDircOk(ctx, preparedStmt) 1411 if err != nil { 1412 return nil, err 1413 } 1414 if ok { 1415 return s.cachedCausetInterDirc(ctx, stmtID, preparedStmt, args) 1416 } 1417 return s.preparedStmtInterDirc(ctx, stmtID, preparedStmt, args) 1418 } 1419 1420 func (s *stochastik) DropPreparedStmt(stmtID uint32) error { 1421 vars := s.stochastikVars 1422 if _, ok := vars.PreparedStmts[stmtID]; !ok { 1423 return causetembedded.ErrStmtNotFound 1424 } 1425 vars.RetryInfo.DroppedPreparedStmtIDs = append(vars.RetryInfo.DroppedPreparedStmtIDs, stmtID) 1426 return nil 1427 } 1428 1429 func (s *stochastik) Txn(active bool) (ekv.Transaction, error) { 1430 if !active { 1431 return &s.txn, nil 1432 } 1433 if !s.txn.validOrPending() { 1434 return &s.txn, errors.AddStack(ekv.ErrInvalidTxn) 1435 } 1436 if s.txn.pending() { 1437 defer func(begin time.Time) { 1438 s.stochastikVars.DurationWaitTS = time.Since(begin) 1439 }(time.Now()) 1440 // Transaction is lazy initialized. 1441 // PrepareTxnCtx is called to get a tso future, makes s.txn a pending txn, 1442 // If Txn() is called later, wait for the future to get a valid txn. 1443 if err := s.txn.changePendingToValid(s.currentCtx); err != nil { 1444 logutil.BgLogger().Error("active transaction fail", 1445 zap.Error(err)) 1446 s.txn.cleanup() 1447 s.stochastikVars.TxnCtx.StartTS = 0 1448 return &s.txn, err 1449 } 1450 s.stochastikVars.TxnCtx.StartTS = s.txn.StartTS() 1451 if s.stochastikVars.TxnCtx.IsPessimistic { 1452 s.txn.SetOption(ekv.Pessimistic, true) 1453 } 1454 if !s.stochastikVars.IsAutocommit() { 1455 s.stochastikVars.SetStatusFlag(allegrosql.ServerStatusInTrans, true) 1456 } 1457 s.stochastikVars.TxnCtx.CouldRetry = s.isTxnRetryable() 1458 s.txn.SetVars(s.stochastikVars.KVVars) 1459 if s.stochastikVars.GetReplicaRead().IsFollowerRead() { 1460 s.txn.SetOption(ekv.ReplicaRead, ekv.ReplicaReadFollower) 1461 } 1462 } 1463 return &s.txn, nil 1464 } 1465 1466 // isTxnRetryable (if returns true) means the transaction could retry. 1467 // If the transaction is in pessimistic mode, do not retry. 1468 // If the stochastik is already in transaction, enable retry or internal ALLEGROALLEGROSQL could retry. 1469 // If not, the transaction could always retry, because it should be auto committed transaction. 1470 // Anyway the retry limit is 0, the transaction could not retry. 1471 func (s *stochastik) isTxnRetryable() bool { 1472 sessVars := s.stochastikVars 1473 1474 // The pessimistic transaction no need to retry. 1475 if sessVars.TxnCtx.IsPessimistic { 1476 return false 1477 } 1478 1479 // If retry limit is 0, the transaction could not retry. 1480 if sessVars.RetryLimit == 0 { 1481 return false 1482 } 1483 1484 // If the stochastik is not InTxn, it is an auto-committed transaction. 1485 // The auto-committed transaction could always retry. 1486 if !sessVars.InTxn() { 1487 return true 1488 } 1489 1490 // The internal transaction could always retry. 1491 if sessVars.InRestrictedALLEGROSQL { 1492 return true 1493 } 1494 1495 // If the retry is enabled, the transaction could retry. 1496 if !sessVars.DisableTxnAutoRetry { 1497 return true 1498 } 1499 1500 return false 1501 } 1502 1503 func (s *stochastik) NewTxn(ctx context.Context) error { 1504 if s.txn.Valid() { 1505 txnID := s.txn.StartTS() 1506 err := s.CommitTxn(ctx) 1507 if err != nil { 1508 return err 1509 } 1510 vars := s.GetStochastikVars() 1511 logutil.Logger(ctx).Info("NewTxn() inside a transaction auto commit", 1512 zap.Int64("schemaVersion", vars.TxnCtx.SchemaVersion), 1513 zap.Uint64("txnStartTS", txnID)) 1514 } 1515 1516 txn, err := s.causetstore.Begin() 1517 if err != nil { 1518 return err 1519 } 1520 txn.SetVars(s.stochastikVars.KVVars) 1521 if s.GetStochastikVars().GetReplicaRead().IsFollowerRead() { 1522 txn.SetOption(ekv.ReplicaRead, ekv.ReplicaReadFollower) 1523 } 1524 s.txn.changeInvalidToValid(txn) 1525 is := petri.GetPetri(s).SchemaReplicant() 1526 s.stochastikVars.TxnCtx = &variable.TransactionContext{ 1527 SchemaReplicant: is, 1528 SchemaVersion: is.SchemaMetaVersion(), 1529 CreateTime: time.Now(), 1530 StartTS: txn.StartTS(), 1531 ShardStep: int(s.stochastikVars.ShardAllocateStep), 1532 } 1533 return nil 1534 } 1535 1536 func (s *stochastik) SetValue(key fmt.Stringer, value interface{}) { 1537 s.mu.Lock() 1538 s.mu.values[key] = value 1539 s.mu.Unlock() 1540 } 1541 1542 func (s *stochastik) Value(key fmt.Stringer) interface{} { 1543 s.mu.RLock() 1544 value := s.mu.values[key] 1545 s.mu.RUnlock() 1546 return value 1547 } 1548 1549 func (s *stochastik) ClearValue(key fmt.Stringer) { 1550 s.mu.Lock() 1551 delete(s.mu.values, key) 1552 s.mu.Unlock() 1553 } 1554 1555 type inCloseStochastik struct{} 1556 1557 // Close function does some clean work when stochastik end. 1558 // Close should release the causet locks which hold by the stochastik. 1559 func (s *stochastik) Close() { 1560 // TODO: do clean causet locks when stochastik exited without execute Close. 1561 // TODO: do clean causet locks when milevadb-server was `kill -9`. 1562 if s.HasLockedTables() && config.TableLockEnabled() { 1563 if ds := config.TableLockDelayClean(); ds > 0 { 1564 time.Sleep(time.Duration(ds) * time.Millisecond) 1565 } 1566 lockedTables := s.GetAllTableLocks() 1567 err := petri.GetPetri(s).DBS().UnlockTables(s, lockedTables) 1568 if err != nil { 1569 logutil.BgLogger().Error("release causet dagger failed", zap.Uint64("conn", s.stochastikVars.ConnectionID)) 1570 } 1571 } 1572 if s.statsDefCauslector != nil { 1573 s.statsDefCauslector.Delete() 1574 } 1575 bindValue := s.Value(bindinfo.StochastikBindInfoKeyType) 1576 if bindValue != nil { 1577 bindValue.(*bindinfo.StochastikHandle).Close() 1578 } 1579 ctx := context.WithValue(context.TODO(), inCloseStochastik{}, struct{}{}) 1580 s.RollbackTxn(ctx) 1581 if s.stochastikVars != nil { 1582 s.stochastikVars.WithdrawAllPreparedStmt() 1583 } 1584 } 1585 1586 // GetStochastikVars implements the context.Context interface. 1587 func (s *stochastik) GetStochastikVars() *variable.StochastikVars { 1588 return s.stochastikVars 1589 } 1590 1591 func (s *stochastik) Auth(user *auth.UserIdentity, authentication []byte, salt []byte) bool { 1592 pm := privilege.GetPrivilegeManager(s) 1593 1594 // Check IP or localhost. 1595 var success bool 1596 user.AuthUsername, user.AuthHostname, success = pm.ConnectionVerification(user.Username, user.Hostname, authentication, salt, s.stochastikVars.TLSConnectionState) 1597 if success { 1598 s.stochastikVars.User = user 1599 s.stochastikVars.ActiveRoles = pm.GetDefaultRoles(user.AuthUsername, user.AuthHostname) 1600 return true 1601 } else if user.Hostname == variable.DefHostname { 1602 return false 1603 } 1604 1605 // Check Hostname. 1606 for _, addr := range getHostByIP(user.Hostname) { 1607 u, h, success := pm.ConnectionVerification(user.Username, addr, authentication, salt, s.stochastikVars.TLSConnectionState) 1608 if success { 1609 s.stochastikVars.User = &auth.UserIdentity{ 1610 Username: user.Username, 1611 Hostname: addr, 1612 AuthUsername: u, 1613 AuthHostname: h, 1614 } 1615 s.stochastikVars.ActiveRoles = pm.GetDefaultRoles(u, h) 1616 return true 1617 } 1618 } 1619 return false 1620 } 1621 1622 // AuthWithoutVerification is required by the ResetConnection RPC 1623 func (s *stochastik) AuthWithoutVerification(user *auth.UserIdentity) bool { 1624 pm := privilege.GetPrivilegeManager(s) 1625 1626 // Check IP or localhost. 1627 var success bool 1628 user.AuthUsername, user.AuthHostname, success = pm.GetAuthWithoutVerification(user.Username, user.Hostname) 1629 if success { 1630 s.stochastikVars.User = user 1631 s.stochastikVars.ActiveRoles = pm.GetDefaultRoles(user.AuthUsername, user.AuthHostname) 1632 return true 1633 } else if user.Hostname == variable.DefHostname { 1634 return false 1635 } 1636 1637 // Check Hostname. 1638 for _, addr := range getHostByIP(user.Hostname) { 1639 u, h, success := pm.GetAuthWithoutVerification(user.Username, addr) 1640 if success { 1641 s.stochastikVars.User = &auth.UserIdentity{ 1642 Username: user.Username, 1643 Hostname: addr, 1644 AuthUsername: u, 1645 AuthHostname: h, 1646 } 1647 s.stochastikVars.ActiveRoles = pm.GetDefaultRoles(u, h) 1648 return true 1649 } 1650 } 1651 return false 1652 } 1653 1654 func getHostByIP(ip string) []string { 1655 if ip == "127.0.0.1" { 1656 return []string{variable.DefHostname} 1657 } 1658 addrs, err := net.LookupAddr(ip) 1659 if err != nil { 1660 // The error is ignorable. 1661 // The empty line here makes the golint tool (which complains err is not checked) happy. 1662 } 1663 return addrs 1664 } 1665 1666 // CreateStochastik4Test creates a new stochastik environment for test. 1667 func CreateStochastik4Test(causetstore ekv.CausetStorage) (Stochastik, error) { 1668 return CreateStochastik4TestWithOpt(causetstore, nil) 1669 } 1670 1671 // Opt describes the option for creating stochastik 1672 type Opt struct { 1673 PreparedCausetCache *ekvcache.SimpleLRUCache 1674 } 1675 1676 // CreateStochastik4TestWithOpt creates a new stochastik environment for test. 1677 func CreateStochastik4TestWithOpt(causetstore ekv.CausetStorage, opt *Opt) (Stochastik, error) { 1678 s, err := CreateStochastikWithOpt(causetstore, opt) 1679 if err == nil { 1680 // initialize stochastik variables for test. 1681 s.GetStochastikVars().InitChunkSize = 2 1682 s.GetStochastikVars().MaxChunkSize = 32 1683 } 1684 return s, err 1685 } 1686 1687 // CreateStochastik creates a new stochastik environment. 1688 func CreateStochastik(causetstore ekv.CausetStorage) (Stochastik, error) { 1689 return CreateStochastikWithOpt(causetstore, nil) 1690 } 1691 1692 // CreateStochastikWithOpt creates a new stochastik environment with option. 1693 // Use default option if opt is nil. 1694 func CreateStochastikWithOpt(causetstore ekv.CausetStorage, opt *Opt) (Stochastik, error) { 1695 s, err := createStochastikWithOpt(causetstore, opt) 1696 if err != nil { 1697 return nil, err 1698 } 1699 1700 // Add auth here. 1701 do, err := domap.Get(causetstore) 1702 if err != nil { 1703 return nil, err 1704 } 1705 pm := &privileges.UserPrivileges{ 1706 Handle: do.PrivilegeHandle(), 1707 } 1708 privilege.BindPrivilegeManager(s, pm) 1709 1710 stochastikBindHandle := bindinfo.NewStochastikBindHandle(s.BerolinaSQL) 1711 s.SetValue(bindinfo.StochastikBindInfoKeyType, stochastikBindHandle) 1712 // Add stats collector, and it will be freed by background stats worker 1713 // which periodically uFIDelates stats using the collected data. 1714 if do.StatsHandle() != nil && do.StatsUFIDelating() { 1715 s.statsDefCauslector = do.StatsHandle().NewStochastikStatsDefCauslector() 1716 } 1717 1718 return s, nil 1719 } 1720 1721 // loadSystemTZ loads systemTZ from allegrosql.milevadb 1722 func loadSystemTZ(se *stochastik) (string, error) { 1723 return loadParameter(se, "system_tz") 1724 } 1725 1726 // loadDefCauslationParameter loads collation parameter from allegrosql.milevadb 1727 func loadDefCauslationParameter(se *stochastik) (bool, error) { 1728 para, err := loadParameter(se, milevadbNewDefCauslationEnabled) 1729 if err != nil { 1730 return false, err 1731 } 1732 if para == varTrue { 1733 return true, nil 1734 } else if para == varFalse { 1735 return false, nil 1736 } 1737 logutil.BgLogger().Warn( 1738 "Unexpected value of 'new_collation_enabled' in 'allegrosql.milevadb', use 'False' instead", 1739 zap.String("value", para)) 1740 return false, nil 1741 } 1742 1743 // loadParameter loads read-only parameter from allegrosql.milevadb 1744 func loadParameter(se *stochastik, name string) (string, error) { 1745 allegrosql := "select variable_value from allegrosql.milevadb where variable_name = '" + name + "'" 1746 rss, errLoad := se.InterDircute(context.Background(), allegrosql) 1747 if errLoad != nil { 1748 return "", errLoad 1749 } 1750 // the record of allegrosql.milevadb under where condition: variable_name = $name should shall only be one. 1751 defer func() { 1752 if err := rss[0].Close(); err != nil { 1753 logutil.BgLogger().Error("close result set error", zap.Error(err)) 1754 } 1755 }() 1756 req := rss[0].NewChunk() 1757 if err := rss[0].Next(context.Background(), req); err != nil { 1758 return "", err 1759 } 1760 return req.GetRow(0).GetString(0), nil 1761 } 1762 1763 // BootstrapStochastik runs the first time when the MilevaDB server start. 1764 func BootstrapStochastik(causetstore ekv.CausetStorage) (*petri.Petri, error) { 1765 cfg := config.GetGlobalConfig() 1766 if len(cfg.Plugin.Load) > 0 { 1767 err := plugin.Load(context.Background(), plugin.Config{ 1768 Plugins: strings.Split(cfg.Plugin.Load, ","), 1769 PluginDir: cfg.Plugin.Dir, 1770 GlobalSysVar: &variable.SysVars, 1771 PluginVarNames: &variable.PluginVarNames, 1772 }) 1773 if err != nil { 1774 return nil, err 1775 } 1776 } 1777 1778 initLoadCommonGlobalVarsALLEGROSQL() 1779 1780 ver := getStoreBootstrapVersion(causetstore) 1781 if ver == notBootstrapped { 1782 runInBootstrapStochastik(causetstore, bootstrap) 1783 } else if ver < currentBootstrapVersion { 1784 runInBootstrapStochastik(causetstore, upgrade) 1785 } 1786 1787 se, err := createStochastik(causetstore) 1788 if err != nil { 1789 return nil, err 1790 } 1791 // get system tz from allegrosql.milevadb 1792 tz, err := loadSystemTZ(se) 1793 if err != nil { 1794 return nil, err 1795 } 1796 timeutil.SetSystemTZ(tz) 1797 1798 // get the flag from `allegrosql`.`milevadb` which indicating if new collations are enabled. 1799 newDefCauslationEnabled, err := loadDefCauslationParameter(se) 1800 if err != nil { 1801 return nil, err 1802 } 1803 1804 if newDefCauslationEnabled { 1805 collate.EnableNewDefCauslations() 1806 } 1807 1808 dom := petri.GetPetri(se) 1809 dom.InitExpensiveQueryHandle() 1810 1811 se2, err := createStochastik(causetstore) 1812 if err != nil { 1813 return nil, err 1814 } 1815 se3, err := createStochastik(causetstore) 1816 if err != nil { 1817 return nil, err 1818 } 1819 // We should make the load bind-info loop before other loops which has internal ALLEGROALLEGROSQL. 1820 // Because the internal ALLEGROALLEGROSQL may access the global bind-info handler. As the result, the data race occurs here as the 1821 // LoadBindInfoLoop inits global bind-info handler. 1822 err = dom.LoadBindInfoLoop(se2, se3) 1823 if err != nil { 1824 return nil, err 1825 } 1826 1827 if !config.GetGlobalConfig().Security.SkipGrantTable { 1828 err = dom.LoadPrivilegeLoop(se) 1829 if err != nil { 1830 return nil, err 1831 } 1832 } 1833 1834 if len(cfg.Plugin.Load) > 0 { 1835 err := plugin.Init(context.Background(), plugin.Config{EtcdClient: dom.GetEtcdClient()}) 1836 if err != nil { 1837 return nil, err 1838 } 1839 } 1840 1841 err = interlock.LoadExprPushdownBlacklist(se) 1842 if err != nil { 1843 return nil, err 1844 } 1845 1846 err = interlock.LoadOptMemruleBlacklist(se) 1847 if err != nil { 1848 return nil, err 1849 } 1850 1851 dom.TelemetryLoop(se) 1852 1853 se1, err := createStochastik(causetstore) 1854 if err != nil { 1855 return nil, err 1856 } 1857 err = dom.UFIDelateTableStatsLoop(se1) 1858 if err != nil { 1859 return nil, err 1860 } 1861 if raw, ok := causetstore.(einsteindb.EtcdBackend); ok { 1862 err = raw.StartGCWorker() 1863 if err != nil { 1864 return nil, err 1865 } 1866 } 1867 1868 return dom, err 1869 } 1870 1871 // GetPetri gets the associated petri for causetstore. 1872 func GetPetri(causetstore ekv.CausetStorage) (*petri.Petri, error) { 1873 return domap.Get(causetstore) 1874 } 1875 1876 // runInBootstrapStochastik create a special stochastik for bootstrap to run. 1877 // If no bootstrap and storage is remote, we must use a little lease time to 1878 // bootstrap quickly, after bootstrapped, we will reset the lease time. 1879 // TODO: Using a bootstrap tool for doing this may be better later. 1880 func runInBootstrapStochastik(causetstore ekv.CausetStorage, bootstrap func(Stochastik)) { 1881 s, err := createStochastik(causetstore) 1882 if err != nil { 1883 // Bootstrap fail will cause program exit. 1884 logutil.BgLogger().Fatal("createStochastik error", zap.Error(err)) 1885 } 1886 1887 s.SetValue(stochastikctx.Initing, true) 1888 bootstrap(s) 1889 finishBootstrap(causetstore) 1890 s.ClearValue(stochastikctx.Initing) 1891 1892 dom := petri.GetPetri(s) 1893 dom.Close() 1894 domap.Delete(causetstore) 1895 } 1896 1897 func createStochastik(causetstore ekv.CausetStorage) (*stochastik, error) { 1898 return createStochastikWithOpt(causetstore, nil) 1899 } 1900 1901 func createStochastikWithOpt(causetstore ekv.CausetStorage, opt *Opt) (*stochastik, error) { 1902 dom, err := domap.Get(causetstore) 1903 if err != nil { 1904 return nil, err 1905 } 1906 s := &stochastik{ 1907 causetstore: causetstore, 1908 BerolinaSQL: BerolinaSQL.New(), 1909 stochastikVars: variable.NewStochastikVars(), 1910 dbsTenantChecker: dom.DBS().TenantManager(), 1911 client: causetstore.GetClient(), 1912 } 1913 if causetembedded.PreparedCausetCacheEnabled() { 1914 if opt != nil && opt.PreparedCausetCache != nil { 1915 s.preparedCausetCache = opt.PreparedCausetCache 1916 } else { 1917 s.preparedCausetCache = ekvcache.NewSimpleLRUCache(causetembedded.PreparedCausetCacheCapacity, 1918 causetembedded.PreparedCausetCacheMemoryGuardRatio, causetembedded.PreparedCausetCacheMaxMemory.Load()) 1919 } 1920 } 1921 s.mu.values = make(map[fmt.Stringer]interface{}) 1922 s.lockedTables = make(map[int64]perceptron.TableLockTpInfo) 1923 petri.BindPetri(s, dom) 1924 // stochastik implements variable.GlobalVarAccessor. Bind it to ctx. 1925 s.stochastikVars.GlobalVarsAccessor = s 1926 s.stochastikVars.BinlogClient = binloginfo.GetPumpsClient() 1927 s.txn.init() 1928 1929 stochastikBindHandle := bindinfo.NewStochastikBindHandle(s.BerolinaSQL) 1930 s.SetValue(bindinfo.StochastikBindInfoKeyType, stochastikBindHandle) 1931 return s, nil 1932 } 1933 1934 // CreateStochastikWithPetri creates a new Stochastik and binds it with a Petri. 1935 // We need this because when we start DBS in Petri, the DBS need a stochastik 1936 // to change some system blocks. But at that time, we have been already in 1937 // a dagger context, which cause we can't call createSesion directly. 1938 func CreateStochastikWithPetri(causetstore ekv.CausetStorage, dom *petri.Petri) (*stochastik, error) { 1939 s := &stochastik{ 1940 causetstore: causetstore, 1941 BerolinaSQL: BerolinaSQL.New(), 1942 stochastikVars: variable.NewStochastikVars(), 1943 client: causetstore.GetClient(), 1944 } 1945 if causetembedded.PreparedCausetCacheEnabled() { 1946 s.preparedCausetCache = ekvcache.NewSimpleLRUCache(causetembedded.PreparedCausetCacheCapacity, 1947 causetembedded.PreparedCausetCacheMemoryGuardRatio, causetembedded.PreparedCausetCacheMaxMemory.Load()) 1948 } 1949 s.mu.values = make(map[fmt.Stringer]interface{}) 1950 s.lockedTables = make(map[int64]perceptron.TableLockTpInfo) 1951 petri.BindPetri(s, dom) 1952 // stochastik implements variable.GlobalVarAccessor. Bind it to ctx. 1953 s.stochastikVars.GlobalVarsAccessor = s 1954 s.txn.init() 1955 return s, nil 1956 } 1957 1958 const ( 1959 notBootstrapped = 0 1960 currentBootstrapVersion = version51 1961 ) 1962 1963 func getStoreBootstrapVersion(causetstore ekv.CausetStorage) int64 { 1964 storeBootstrappedLock.Lock() 1965 defer storeBootstrappedLock.Unlock() 1966 // check in memory 1967 _, ok := storeBootstrapped[causetstore.UUID()] 1968 if ok { 1969 return currentBootstrapVersion 1970 } 1971 1972 var ver int64 1973 // check in ekv causetstore 1974 err := ekv.RunInNewTxn(causetstore, false, func(txn ekv.Transaction) error { 1975 var err error 1976 t := spacetime.NewMeta(txn) 1977 ver, err = t.GetBootstrapVersion() 1978 return err 1979 }) 1980 1981 if err != nil { 1982 logutil.BgLogger().Fatal("check bootstrapped failed", 1983 zap.Error(err)) 1984 } 1985 1986 if ver > notBootstrapped { 1987 // here mean memory is not ok, but other server has already finished it 1988 storeBootstrapped[causetstore.UUID()] = true 1989 } 1990 1991 return ver 1992 } 1993 1994 func finishBootstrap(causetstore ekv.CausetStorage) { 1995 setStoreBootstrapped(causetstore.UUID()) 1996 1997 err := ekv.RunInNewTxn(causetstore, true, func(txn ekv.Transaction) error { 1998 t := spacetime.NewMeta(txn) 1999 err := t.FinishBootstrap(currentBootstrapVersion) 2000 return err 2001 }) 2002 if err != nil { 2003 logutil.BgLogger().Fatal("finish bootstrap failed", 2004 zap.Error(err)) 2005 } 2006 } 2007 2008 const quoteCommaQuote = "', '" 2009 2010 var builtinGlobalVariable = []string{ 2011 variable.AutoCommit, 2012 variable.ALLEGROSQLModeVar, 2013 variable.MaxAllowedPacket, 2014 variable.TimeZone, 2015 variable.BlockEncryptionMode, 2016 variable.WaitTimeout, 2017 variable.InteractiveTimeout, 2018 variable.MaxPreparedStmtCount, 2019 variable.InitConnect, 2020 variable.TxnIsolation, 2021 variable.TxReadOnly, 2022 variable.TransactionIsolation, 2023 variable.TransactionReadOnly, 2024 variable.NetBufferLength, 2025 variable.QueryCacheType, 2026 variable.QueryCacheSize, 2027 variable.CharacterSetServer, 2028 variable.AutoIncrementIncrement, 2029 variable.AutoIncrementOffset, 2030 variable.DefCauslationServer, 2031 variable.NetWriteTimeout, 2032 variable.MaxInterDircutionTime, 2033 variable.InnodbLockWaitTimeout, 2034 variable.WindowingUseHighPrecision, 2035 variable.ALLEGROSQLSelectLimit, 2036 2037 /* MilevaDB specific global variables: */ 2038 variable.MilevaDBSkipASCIICheck, 2039 variable.MilevaDBSkipUTF8Check, 2040 variable.MilevaDBIndexJoinBatchSize, 2041 variable.MilevaDBIndexLookupSize, 2042 variable.MilevaDBIndexLookupConcurrency, 2043 variable.MilevaDBIndexLookupJoinConcurrency, 2044 variable.MilevaDBIndexSerialScanConcurrency, 2045 variable.MilevaDBHashJoinConcurrency, 2046 variable.MilevaDBProjectionConcurrency, 2047 variable.MilevaDBHashAggPartialConcurrency, 2048 variable.MilevaDBHashAggFinalConcurrency, 2049 variable.MilevaDBWindowConcurrency, 2050 variable.MilevaDBInterlockingDirectorateConcurrency, 2051 variable.MilevaDBBackoffLockFast, 2052 variable.MilevaDBBackOffWeight, 2053 variable.MilevaDBConstraintCheckInPlace, 2054 variable.MilevaDBDBSReorgWorkerCount, 2055 variable.MilevaDBDBSReorgBatchSize, 2056 variable.MilevaDBDBSErrorCountLimit, 2057 variable.MilevaDBOptInSubqToJoinAnPosetDagg, 2058 variable.MilevaDBOptCorrelationThreshold, 2059 variable.MilevaDBOptCorrelationExpFactor, 2060 variable.MilevaDBOptCPUFactor, 2061 variable.MilevaDBOptCopCPUFactor, 2062 variable.MilevaDBOptNetworkFactor, 2063 variable.MilevaDBOptScanFactor, 2064 variable.MilevaDBOptDescScanFactor, 2065 variable.MilevaDBOptMemoryFactor, 2066 variable.MilevaDBOptDiskFactor, 2067 variable.MilevaDBOptConcurrencyFactor, 2068 variable.MilevaDBDistALLEGROSQLScanConcurrency, 2069 variable.MilevaDBInitChunkSize, 2070 variable.MilevaDBMaxChunkSize, 2071 variable.MilevaDBEnableCascadesCausetAppend, 2072 variable.MilevaDBRetryLimit, 2073 variable.MilevaDBDisableTxnAutoRetry, 2074 variable.MilevaDBEnableWindowFunction, 2075 variable.MilevaDBEnableTablePartition, 2076 variable.MilevaDBEnableVectorizedExpression, 2077 variable.MilevaDBEnableFastAnalyze, 2078 variable.MilevaDBExpensiveQueryTimeThreshold, 2079 variable.MilevaDBEnableNoopFuncs, 2080 variable.MilevaDBEnableIndexMerge, 2081 variable.MilevaDBTxnMode, 2082 variable.MilevaDBAllowBatchCop, 2083 variable.MilevaDBOptBCJ, 2084 variable.MilevaDBRowFormatVersion, 2085 variable.MilevaDBEnableStmtSummary, 2086 variable.MilevaDBStmtSummaryInternalQuery, 2087 variable.MilevaDBStmtSummaryRefreshInterval, 2088 variable.MilevaDBStmtSummaryHistorySize, 2089 variable.MilevaDBStmtSummaryMaxStmtCount, 2090 variable.MilevaDBStmtSummaryMaxALLEGROSQLLength, 2091 variable.MilevaDBMaxDeltaSchemaCount, 2092 variable.MilevaDBCaptureCausetBaseline, 2093 variable.MilevaDBUseCausetBaselines, 2094 variable.MilevaDBEvolveCausetBaselines, 2095 variable.MilevaDBIsolationReadEngines, 2096 variable.MilevaDBStoreLimit, 2097 variable.MilevaDBAllowAutoRandExplicitInsert, 2098 variable.MilevaDBEnableClusteredIndex, 2099 variable.MilevaDBPartitionPruneMode, 2100 variable.MilevaDBSlowLogMasking, 2101 variable.MilevaDBRedactLog, 2102 variable.MilevaDBEnableTelemetry, 2103 variable.MilevaDBShardAllocateStep, 2104 variable.MilevaDBEnableChangeDeferredCausetType, 2105 variable.MilevaDBEnableAmendPessimisticTxn, 2106 } 2107 2108 var ( 2109 loadCommonGlobalVarsALLEGROSQLOnce sync.Once 2110 loadCommonGlobalVarsALLEGROSQL string 2111 ) 2112 2113 func initLoadCommonGlobalVarsALLEGROSQL() { 2114 loadCommonGlobalVarsALLEGROSQLOnce.Do(func() { 2115 vars := append(make([]string, 0, len(builtinGlobalVariable)+len(variable.PluginVarNames)), builtinGlobalVariable...) 2116 if len(variable.PluginVarNames) > 0 { 2117 vars = append(vars, variable.PluginVarNames...) 2118 } 2119 loadCommonGlobalVarsALLEGROSQL = "select HIGH_PRIORITY * from allegrosql.global_variables where variable_name in ('" + strings.Join(vars, quoteCommaQuote) + "')" 2120 }) 2121 } 2122 2123 // loadCommonGlobalVariablesIfNeeded loads and applies commonly used global variables for the stochastik. 2124 func (s *stochastik) loadCommonGlobalVariablesIfNeeded() error { 2125 initLoadCommonGlobalVarsALLEGROSQL() 2126 vars := s.stochastikVars 2127 if vars.CommonGlobalLoaded { 2128 return nil 2129 } 2130 if s.Value(stochastikctx.Initing) != nil { 2131 // When running bootstrap or upgrade, we should not access global storage. 2132 return nil 2133 } 2134 2135 var err error 2136 // Use GlobalVariableCache if MilevaDB just loaded global variables within 2 second ago. 2137 // When a lot of connections connect to MilevaDB simultaneously, it can protect EinsteinDB spacetime region from overload. 2138 gvc := petri.GetPetri(s).GetGlobalVarsCache() 2139 loadFunc := func() ([]chunk.Row, []*ast.ResultField, error) { 2140 return s.InterDircRestrictedALLEGROSQL(loadCommonGlobalVarsALLEGROSQL) 2141 } 2142 rows, fields, err := gvc.LoadGlobalVariables(loadFunc) 2143 if err != nil { 2144 logutil.BgLogger().Warn("failed to load global variables", 2145 zap.Uint64("conn", s.stochastikVars.ConnectionID), zap.Error(err)) 2146 return err 2147 } 2148 vars.CommonGlobalLoaded = true 2149 2150 for _, event := range rows { 2151 varName := event.GetString(0) 2152 varVal := event.GetCauset(1, &fields[1].DeferredCauset.FieldType) 2153 if _, ok := vars.GetSystemVar(varName); !ok { 2154 err = variable.SetStochastikSystemVar(s.stochastikVars, varName, varVal) 2155 if err != nil { 2156 return err 2157 } 2158 } 2159 } 2160 2161 // when client set Capability Flags CLIENT_INTERACTIVE, init wait_timeout with interactive_timeout 2162 if vars.ClientCapability&allegrosql.ClientInteractive > 0 { 2163 if varVal, ok := vars.GetSystemVar(variable.InteractiveTimeout); ok { 2164 if err := vars.SetSystemVar(variable.WaitTimeout, varVal); err != nil { 2165 return err 2166 } 2167 } 2168 } 2169 2170 vars.CommonGlobalLoaded = true 2171 return nil 2172 } 2173 2174 // PrepareTxnCtx starts a goroutine to begin a transaction if needed, and creates a new transaction context. 2175 // It is called before we execute a allegrosql query. 2176 func (s *stochastik) PrepareTxnCtx(ctx context.Context) { 2177 s.currentCtx = ctx 2178 if s.txn.validOrPending() { 2179 return 2180 } 2181 2182 is := petri.GetPetri(s).SchemaReplicant() 2183 s.stochastikVars.TxnCtx = &variable.TransactionContext{ 2184 SchemaReplicant: is, 2185 SchemaVersion: is.SchemaMetaVersion(), 2186 CreateTime: time.Now(), 2187 ShardStep: int(s.stochastikVars.ShardAllocateStep), 2188 } 2189 if !s.stochastikVars.IsAutocommit() || s.stochastikVars.RetryInfo.Retrying { 2190 if s.stochastikVars.TxnMode == ast.Pessimistic { 2191 s.stochastikVars.TxnCtx.IsPessimistic = true 2192 } 2193 } 2194 } 2195 2196 // PrepareTSFuture uses to try to get ts future. 2197 func (s *stochastik) PrepareTSFuture(ctx context.Context) { 2198 if !s.txn.validOrPending() { 2199 // Prepare the transaction future if the transaction is invalid (at the beginning of the transaction). 2200 txnFuture := s.getTxnFuture(ctx) 2201 s.txn.changeInvalidToPending(txnFuture) 2202 } else if s.txn.Valid() && s.GetStochastikVars().IsPessimisticReadConsistency() { 2203 // Prepare the memex future if the transaction is valid in RC transactions. 2204 s.GetStochastikVars().TxnCtx.SetStmtFutureForRC(s.getTxnFuture(ctx).future) 2205 } 2206 } 2207 2208 // RefreshTxnCtx implements context.RefreshTxnCtx interface. 2209 func (s *stochastik) RefreshTxnCtx(ctx context.Context) error { 2210 if err := s.doCommit(ctx); err != nil { 2211 return err 2212 } 2213 2214 return s.NewTxn(ctx) 2215 } 2216 2217 // InitTxnWithStartTS create a transaction with startTS. 2218 func (s *stochastik) InitTxnWithStartTS(startTS uint64) error { 2219 if s.txn.Valid() { 2220 return nil 2221 } 2222 2223 // no need to get txn from txnFutureCh since txn should init with startTs 2224 txn, err := s.causetstore.BeginWithStartTS(startTS) 2225 if err != nil { 2226 return err 2227 } 2228 txn.SetVars(s.stochastikVars.KVVars) 2229 s.txn.changeInvalidToValid(txn) 2230 err = s.loadCommonGlobalVariablesIfNeeded() 2231 if err != nil { 2232 return err 2233 } 2234 return nil 2235 } 2236 2237 // GetStore gets the causetstore of stochastik. 2238 func (s *stochastik) GetStore() ekv.CausetStorage { 2239 return s.causetstore 2240 } 2241 2242 func (s *stochastik) ShowProcess() *soliton.ProcessInfo { 2243 var pi *soliton.ProcessInfo 2244 tmp := s.processInfo.Load() 2245 if tmp != nil { 2246 pi = tmp.(*soliton.ProcessInfo) 2247 } 2248 return pi 2249 } 2250 2251 // logStmt logs some crucial ALLEGROALLEGROSQL including: CREATE USER/GRANT PRIVILEGE/CHANGE PASSWORD/DBS etc and normal ALLEGROALLEGROSQL 2252 // if variable.ProcessGeneralLog is set. 2253 func logStmt(execStmt *interlock.InterDircStmt, vars *variable.StochastikVars) { 2254 switch stmt := execStmt.StmtNode.(type) { 2255 case *ast.CreateUserStmt, *ast.DropUserStmt, *ast.AlterUserStmt, *ast.SetPwdStmt, *ast.GrantStmt, 2256 *ast.RevokeStmt, *ast.AlterTableStmt, *ast.CreateDatabaseStmt, *ast.CreateIndexStmt, *ast.CreateTableStmt, 2257 *ast.DroFIDelatabaseStmt, *ast.DropIndexStmt, *ast.DropTableStmt, *ast.RenameTableStmt, *ast.TruncateTableStmt: 2258 user := vars.User 2259 schemaVersion := vars.TxnCtx.SchemaVersion 2260 if ss, ok := execStmt.StmtNode.(ast.SensitiveStmtNode); ok { 2261 logutil.BgLogger().Info("CRUCIAL OPERATION", 2262 zap.Uint64("conn", vars.ConnectionID), 2263 zap.Int64("schemaVersion", schemaVersion), 2264 zap.String("secure text", ss.SecureText()), 2265 zap.Stringer("user", user)) 2266 } else { 2267 logutil.BgLogger().Info("CRUCIAL OPERATION", 2268 zap.Uint64("conn", vars.ConnectionID), 2269 zap.Int64("schemaVersion", schemaVersion), 2270 zap.String("cur_db", vars.CurrentDB), 2271 zap.String("allegrosql", stmt.Text()), 2272 zap.Stringer("user", user)) 2273 } 2274 default: 2275 logQuery(execStmt.GetTextToLog(), vars) 2276 } 2277 } 2278 2279 func logQuery(query string, vars *variable.StochastikVars) { 2280 if atomic.LoadUint32(&variable.ProcessGeneralLog) != 0 && !vars.InRestrictedALLEGROSQL { 2281 query = interlock.QueryReplacer.Replace(query) 2282 if !config.RedactLogEnabled() { 2283 query = query + vars.PreparedParams.String() 2284 } 2285 logutil.BgLogger().Info("GENERAL_LOG", 2286 zap.Uint64("conn", vars.ConnectionID), 2287 zap.Stringer("user", vars.User), 2288 zap.Int64("schemaVersion", vars.TxnCtx.SchemaVersion), 2289 zap.Uint64("txnStartTS", vars.TxnCtx.StartTS), 2290 zap.Uint64("forUFIDelateTS", vars.TxnCtx.GetForUFIDelateTS()), 2291 zap.Bool("isReadConsistency", vars.IsReadConsistencyTxn()), 2292 zap.String("current_db", vars.CurrentDB), 2293 zap.String("txn_mode", vars.GetReadableTxnMode()), 2294 zap.String("allegrosql", query)) 2295 } 2296 } 2297 2298 func (s *stochastik) recordOnTransactionInterDircution(err error, counter int, duration float64) { 2299 if s.stochastikVars.TxnCtx.IsPessimistic { 2300 if err != nil { 2301 memexPerTransactionPessimisticError.Observe(float64(counter)) 2302 transactionDurationPessimisticAbort.Observe(duration) 2303 } else { 2304 memexPerTransactionPessimisticOK.Observe(float64(counter)) 2305 transactionDurationPessimisticCommit.Observe(duration) 2306 } 2307 } else { 2308 if err != nil { 2309 memexPerTransactionOptimisticError.Observe(float64(counter)) 2310 transactionDurationOptimisticAbort.Observe(duration) 2311 } else { 2312 memexPerTransactionOptimisticOK.Observe(float64(counter)) 2313 transactionDurationOptimisticCommit.Observe(duration) 2314 } 2315 } 2316 }