github.com/KinWaiYuen/client-go/v2@v2.5.4/txnkv/transaction/txn.go (about) 1 // Copyright 2021 TiKV Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 // NOTE: The code in this file is based on code from the 16 // TiDB project, licensed under the Apache License v 2.0 17 // 18 // https://github.com/pingcap/tidb/tree/cc5e161ac06827589c4966674597c137cc9e809c/store/tikv/txn.go 19 // 20 21 // Copyright 2016 PingCAP, Inc. 22 // 23 // Licensed under the Apache License, Version 2.0 (the "License"); 24 // you may not use this file except in compliance with the License. 25 // You may obtain a copy of the License at 26 // 27 // http://www.apache.org/licenses/LICENSE-2.0 28 // 29 // Unless required by applicable law or agreed to in writing, software 30 // distributed under the License is distributed on an "AS IS" BASIS, 31 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 32 // See the License for the specific language governing permissions and 33 // limitations under the License. 34 35 package transaction 36 37 import ( 38 "bytes" 39 "context" 40 "encoding/json" 41 "fmt" 42 "math/rand" 43 "runtime/trace" 44 "sort" 45 "sync" 46 "sync/atomic" 47 "time" 48 49 "github.com/KinWaiYuen/client-go/v2/config" 50 tikverr "github.com/KinWaiYuen/client-go/v2/error" 51 "github.com/KinWaiYuen/client-go/v2/internal/logutil" 52 "github.com/KinWaiYuen/client-go/v2/internal/retry" 53 "github.com/KinWaiYuen/client-go/v2/internal/unionstore" 54 tikv "github.com/KinWaiYuen/client-go/v2/kv" 55 "github.com/KinWaiYuen/client-go/v2/metrics" 56 "github.com/KinWaiYuen/client-go/v2/txnkv/txnsnapshot" 57 "github.com/KinWaiYuen/client-go/v2/txnkv/txnutil" 58 "github.com/KinWaiYuen/client-go/v2/util" 59 "github.com/dgryski/go-farm" 60 "github.com/opentracing/opentracing-go" 61 "github.com/pingcap/errors" 62 "github.com/pingcap/failpoint" 63 "github.com/pingcap/kvproto/pkg/kvrpcpb" 64 "go.uber.org/zap" 65 ) 66 67 // MaxTxnTimeUse is the max time a Txn may use (in ms) from its begin to commit. 68 // We use it to abort the transaction to guarantee GC worker will not influence it. 69 const MaxTxnTimeUse = 24 * 60 * 60 * 1000 70 71 // SchemaAmender is used by pessimistic transactions to amend commit mutations for schema change during 2pc. 72 type SchemaAmender interface { 73 // AmendTxn is the amend entry, new mutations will be generated based on input mutations using schema change info. 74 // The returned results are mutations need to prewrite and mutations need to cleanup. 75 AmendTxn(ctx context.Context, startInfoSchema SchemaVer, change *RelatedSchemaChange, mutations CommitterMutations) (CommitterMutations, error) 76 } 77 78 // KVTxn contains methods to interact with a TiKV transaction. 79 type KVTxn struct { 80 snapshot *txnsnapshot.KVSnapshot 81 us *unionstore.KVUnionStore 82 store kvstore // for connection to region. 83 startTS uint64 84 startTime time.Time // Monotonic timestamp for recording txn time consuming. 85 commitTS uint64 86 mu sync.Mutex // For thread-safe LockKeys function. 87 setCnt int64 88 vars *tikv.Variables 89 committer *twoPhaseCommitter 90 lockedCnt int 91 92 valid bool 93 94 // schemaVer is the infoSchema fetched at startTS. 95 schemaVer SchemaVer 96 // SchemaAmender is used amend pessimistic txn commit mutations for schema change 97 schemaAmender SchemaAmender 98 // commitCallback is called after current transaction gets committed 99 commitCallback func(info string, err error) 100 101 binlog BinlogExecutor 102 schemaLeaseChecker SchemaLeaseChecker 103 syncLog bool 104 priority txnutil.Priority 105 isPessimistic bool 106 enableAsyncCommit bool 107 enable1PC bool 108 causalConsistency bool 109 scope string 110 kvFilter KVFilter 111 resourceGroupTag []byte 112 diskFullOpt kvrpcpb.DiskFullOpt 113 } 114 115 // NewTiKVTxn creates a new KVTxn. 116 func NewTiKVTxn(store kvstore, snapshot *txnsnapshot.KVSnapshot, startTS uint64, scope string) (*KVTxn, error) { 117 cfg := config.GetGlobalConfig() 118 newTiKVTxn := &KVTxn{ 119 snapshot: snapshot, 120 us: unionstore.NewUnionStore(snapshot), 121 store: store, 122 startTS: startTS, 123 startTime: time.Now(), 124 valid: true, 125 vars: tikv.DefaultVars, 126 scope: scope, 127 enableAsyncCommit: cfg.EnableAsyncCommit, 128 enable1PC: cfg.Enable1PC, 129 diskFullOpt: kvrpcpb.DiskFullOpt_NotAllowedOnFull, 130 } 131 return newTiKVTxn, nil 132 } 133 134 // SetSuccess is used to probe if kv variables are set or not. It is ONLY used in test cases. 135 var SetSuccess = false 136 137 // SetVars sets variables to the transaction. 138 func (txn *KVTxn) SetVars(vars *tikv.Variables) { 139 txn.vars = vars 140 txn.snapshot.SetVars(vars) 141 if val, err := util.EvalFailpoint("probeSetVars"); err == nil { 142 if val.(bool) { 143 SetSuccess = true 144 } 145 } 146 } 147 148 // GetVars gets variables from the transaction. 149 func (txn *KVTxn) GetVars() *tikv.Variables { 150 return txn.vars 151 } 152 153 // Get implements transaction interface. 154 func (txn *KVTxn) Get(ctx context.Context, k []byte) ([]byte, error) { 155 ret, err := txn.us.Get(ctx, k) 156 if tikverr.IsErrNotFound(err) { 157 return nil, err 158 } 159 if err != nil { 160 return nil, errors.Trace(err) 161 } 162 163 return ret, nil 164 } 165 166 // BatchGet gets kv from the memory buffer of statement and transaction, and the kv storage. 167 // Do not use len(value) == 0 or value == nil to represent non-exist. 168 // If a key doesn't exist, there shouldn't be any corresponding entry in the result map. 169 func (txn *KVTxn) BatchGet(ctx context.Context, keys [][]byte) (map[string][]byte, error) { 170 return NewBufferBatchGetter(txn.GetMemBuffer(), txn.GetSnapshot()).BatchGet(ctx, keys) 171 } 172 173 // Set sets the value for key k as v into kv store. 174 // v must NOT be nil or empty, otherwise it returns ErrCannotSetNilValue. 175 func (txn *KVTxn) Set(k []byte, v []byte) error { 176 txn.setCnt++ 177 return txn.us.GetMemBuffer().Set(k, v) 178 } 179 180 // String implements fmt.Stringer interface. 181 func (txn *KVTxn) String() string { 182 return fmt.Sprintf("%d", txn.StartTS()) 183 } 184 185 // Iter creates an Iterator positioned on the first entry that k <= entry's key. 186 // If such entry is not found, it returns an invalid Iterator with no error. 187 // It yields only keys that < upperBound. If upperBound is nil, it means the upperBound is unbounded. 188 // The Iterator must be Closed after use. 189 func (txn *KVTxn) Iter(k []byte, upperBound []byte) (unionstore.Iterator, error) { 190 return txn.us.Iter(k, upperBound) 191 } 192 193 // IterReverse creates a reversed Iterator positioned on the first entry which key is less than k. 194 func (txn *KVTxn) IterReverse(k []byte) (unionstore.Iterator, error) { 195 return txn.us.IterReverse(k) 196 } 197 198 // Delete removes the entry for key k from kv store. 199 func (txn *KVTxn) Delete(k []byte) error { 200 return txn.us.GetMemBuffer().Delete(k) 201 } 202 203 // SetSchemaLeaseChecker sets a hook to check schema version. 204 func (txn *KVTxn) SetSchemaLeaseChecker(checker SchemaLeaseChecker) { 205 txn.schemaLeaseChecker = checker 206 } 207 208 // EnableForceSyncLog indicates tikv to always sync log for the transaction. 209 func (txn *KVTxn) EnableForceSyncLog() { 210 txn.syncLog = true 211 } 212 213 // SetPessimistic indicates if the transaction should use pessimictic lock. 214 func (txn *KVTxn) SetPessimistic(b bool) { 215 txn.isPessimistic = b 216 } 217 218 // SetSchemaVer updates schema version to validate transaction. 219 func (txn *KVTxn) SetSchemaVer(schemaVer SchemaVer) { 220 txn.schemaVer = schemaVer 221 } 222 223 // SetPriority sets the priority for both write and read. 224 func (txn *KVTxn) SetPriority(pri txnutil.Priority) { 225 txn.priority = pri 226 txn.GetSnapshot().SetPriority(pri) 227 } 228 229 // SetResourceGroupTag sets the resource tag for both write and read. 230 func (txn *KVTxn) SetResourceGroupTag(tag []byte) { 231 txn.resourceGroupTag = tag 232 txn.GetSnapshot().SetResourceGroupTag(tag) 233 } 234 235 // SetSchemaAmender sets an amender to update mutations after schema change. 236 func (txn *KVTxn) SetSchemaAmender(sa SchemaAmender) { 237 txn.schemaAmender = sa 238 } 239 240 // SetCommitCallback sets up a function that will be called when the transaction 241 // is finished. 242 func (txn *KVTxn) SetCommitCallback(f func(string, error)) { 243 txn.commitCallback = f 244 } 245 246 // SetEnableAsyncCommit indicates if the transaction will try to use async commit. 247 func (txn *KVTxn) SetEnableAsyncCommit(b bool) { 248 txn.enableAsyncCommit = b 249 } 250 251 // SetEnable1PC indicates that the transaction will try to use 1 phase commit(which should be faster). 252 // 1PC does not work if the keys to update in the current txn are in multiple regions. 253 func (txn *KVTxn) SetEnable1PC(b bool) { 254 txn.enable1PC = b 255 } 256 257 // SetCausalConsistency indicates if the transaction does not need to 258 // guarantee linearizability. Default value is false which means 259 // linearizability is guaranteed. 260 func (txn *KVTxn) SetCausalConsistency(b bool) { 261 txn.causalConsistency = b 262 } 263 264 // SetScope sets the geographical scope of the transaction. 265 func (txn *KVTxn) SetScope(scope string) { 266 txn.scope = scope 267 } 268 269 // SetKVFilter sets the filter to ignore key-values in memory buffer. 270 func (txn *KVTxn) SetKVFilter(filter KVFilter) { 271 txn.kvFilter = filter 272 } 273 274 // SetDiskFullOpt sets whether current operation is allowed in each TiKV disk usage level. 275 func (txn *KVTxn) SetDiskFullOpt(level kvrpcpb.DiskFullOpt) { 276 txn.diskFullOpt = level 277 } 278 279 // GetDiskFullOpt gets the options of current operation in each TiKV disk usage level. 280 func (txn *KVTxn) GetDiskFullOpt() kvrpcpb.DiskFullOpt { 281 return txn.diskFullOpt 282 } 283 284 // ClearDiskFullOpt clears the options of current operation in each tikv disk usage level. 285 func (txn *KVTxn) ClearDiskFullOpt() { 286 txn.diskFullOpt = kvrpcpb.DiskFullOpt_NotAllowedOnFull 287 } 288 289 // IsPessimistic returns true if it is pessimistic. 290 func (txn *KVTxn) IsPessimistic() bool { 291 return txn.isPessimistic 292 } 293 294 // IsCasualConsistency returns if the transaction allows linearizability 295 // inconsistency. 296 func (txn *KVTxn) IsCasualConsistency() bool { 297 return txn.causalConsistency 298 } 299 300 // GetScope returns the geographical scope of the transaction. 301 func (txn *KVTxn) GetScope() string { 302 return txn.scope 303 } 304 305 // Commit commits the transaction operations to KV store. 306 func (txn *KVTxn) Commit(ctx context.Context) error { 307 if span := opentracing.SpanFromContext(ctx); span != nil && span.Tracer() != nil { 308 span1 := span.Tracer().StartSpan("tikvTxn.Commit", opentracing.ChildOf(span.Context())) 309 defer span1.Finish() 310 ctx = opentracing.ContextWithSpan(ctx, span1) 311 } 312 defer trace.StartRegion(ctx, "CommitTxn").End() 313 314 if !txn.valid { 315 return tikverr.ErrInvalidTxn 316 } 317 defer txn.close() 318 319 if val, err := util.EvalFailpoint("mockCommitError"); err == nil && val.(bool) { 320 if _, err := util.EvalFailpoint("mockCommitErrorOpt"); err == nil { 321 failpoint.Disable("tikvclient/mockCommitErrorOpt") 322 return errors.New("mock commit error") 323 } 324 } 325 326 start := time.Now() 327 defer func() { metrics.TxnCmdHistogramWithCommit.Observe(time.Since(start).Seconds()) }() 328 329 // sessionID is used for log. 330 var sessionID uint64 331 val := ctx.Value(util.SessionID) 332 if val != nil { 333 sessionID = val.(uint64) 334 } 335 336 var err error 337 // If the txn use pessimistic lock, committer is initialized. 338 committer := txn.committer 339 if committer == nil { 340 committer, err = newTwoPhaseCommitter(txn, sessionID) 341 if err != nil { 342 return errors.Trace(err) 343 } 344 txn.committer = committer 345 } 346 347 txn.committer.SetDiskFullOpt(txn.diskFullOpt) 348 349 defer committer.ttlManager.close() 350 351 initRegion := trace.StartRegion(ctx, "InitKeys") 352 err = committer.initKeysAndMutations() 353 initRegion.End() 354 if err != nil { 355 return errors.Trace(err) 356 } 357 if committer.mutations.Len() == 0 { 358 return nil 359 } 360 361 defer func() { 362 detail := committer.getDetail() 363 detail.Mu.Lock() 364 metrics.TiKVTxnCommitBackoffSeconds.Observe(float64(detail.Mu.CommitBackoffTime) / float64(time.Second)) 365 metrics.TiKVTxnCommitBackoffCount.Observe(float64(len(detail.Mu.BackoffTypes))) 366 detail.Mu.Unlock() 367 368 ctxValue := ctx.Value(util.CommitDetailCtxKey) 369 if ctxValue != nil { 370 commitDetail := ctxValue.(**util.CommitDetails) 371 if *commitDetail != nil { 372 (*commitDetail).TxnRetry++ 373 } else { 374 *commitDetail = detail 375 } 376 } 377 }() 378 // latches disabled 379 // pessimistic transaction should also bypass latch. 380 if txn.store.TxnLatches() == nil || txn.IsPessimistic() { 381 err = committer.execute(ctx) 382 if val == nil || sessionID > 0 { 383 txn.onCommitted(err) 384 } 385 logutil.Logger(ctx).Debug("[kv] txnLatches disabled, 2pc directly", zap.Error(err)) 386 return errors.Trace(err) 387 } 388 389 // latches enabled 390 // for transactions which need to acquire latches 391 start = time.Now() 392 lock := txn.store.TxnLatches().Lock(committer.startTS, committer.mutations.GetKeys()) 393 commitDetail := committer.getDetail() 394 commitDetail.LocalLatchTime = time.Since(start) 395 if commitDetail.LocalLatchTime > 0 { 396 metrics.TiKVLocalLatchWaitTimeHistogram.Observe(commitDetail.LocalLatchTime.Seconds()) 397 } 398 defer txn.store.TxnLatches().UnLock(lock) 399 if lock.IsStale() { 400 return &tikverr.ErrWriteConflictInLatch{StartTS: txn.startTS} 401 } 402 err = committer.execute(ctx) 403 if val == nil || sessionID > 0 { 404 txn.onCommitted(err) 405 } 406 if err == nil { 407 lock.SetCommitTS(committer.commitTS) 408 } 409 logutil.Logger(ctx).Debug("[kv] txnLatches enabled while txn retryable", zap.Error(err)) 410 return errors.Trace(err) 411 } 412 413 func (txn *KVTxn) close() { 414 txn.valid = false 415 txn.ClearDiskFullOpt() 416 } 417 418 // Rollback undoes the transaction operations to KV store. 419 func (txn *KVTxn) Rollback() error { 420 if !txn.valid { 421 return tikverr.ErrInvalidTxn 422 } 423 start := time.Now() 424 // Clean up pessimistic lock. 425 if txn.IsPessimistic() && txn.committer != nil { 426 err := txn.rollbackPessimisticLocks() 427 txn.committer.ttlManager.close() 428 if err != nil { 429 logutil.BgLogger().Error(err.Error()) 430 } 431 } 432 txn.close() 433 logutil.BgLogger().Debug("[kv] rollback txn", zap.Uint64("txnStartTS", txn.StartTS())) 434 metrics.TxnCmdHistogramWithRollback.Observe(time.Since(start).Seconds()) 435 return nil 436 } 437 438 func (txn *KVTxn) rollbackPessimisticLocks() error { 439 if txn.lockedCnt == 0 { 440 return nil 441 } 442 bo := retry.NewBackofferWithVars(context.Background(), cleanupMaxBackoff, txn.vars) 443 keys := txn.collectLockedKeys() 444 return txn.committer.pessimisticRollbackMutations(bo, &PlainMutations{keys: keys}) 445 } 446 447 func (txn *KVTxn) collectLockedKeys() [][]byte { 448 keys := make([][]byte, 0, txn.lockedCnt) 449 buf := txn.GetMemBuffer() 450 var err error 451 for it := buf.IterWithFlags(nil, nil); it.Valid(); err = it.Next() { 452 _ = err 453 if it.Flags().HasLocked() { 454 keys = append(keys, it.Key()) 455 } 456 } 457 return keys 458 } 459 460 // TxnInfo is used to keep track the info of a committed transaction (mainly for diagnosis and testing) 461 type TxnInfo struct { 462 TxnScope string `json:"txn_scope"` 463 StartTS uint64 `json:"start_ts"` 464 CommitTS uint64 `json:"commit_ts"` 465 TxnCommitMode string `json:"txn_commit_mode"` 466 AsyncCommitFallback bool `json:"async_commit_fallback"` 467 OnePCFallback bool `json:"one_pc_fallback"` 468 ErrMsg string `json:"error,omitempty"` 469 } 470 471 func (txn *KVTxn) onCommitted(err error) { 472 if txn.commitCallback != nil { 473 isAsyncCommit := txn.committer.isAsyncCommit() 474 isOnePC := txn.committer.isOnePC() 475 476 commitMode := "2pc" 477 if isOnePC { 478 commitMode = "1pc" 479 } else if isAsyncCommit { 480 commitMode = "async_commit" 481 } 482 483 info := TxnInfo{ 484 TxnScope: txn.GetScope(), 485 StartTS: txn.startTS, 486 CommitTS: txn.commitTS, 487 TxnCommitMode: commitMode, 488 AsyncCommitFallback: txn.committer.hasTriedAsyncCommit && !isAsyncCommit, 489 OnePCFallback: txn.committer.hasTriedOnePC && !isOnePC, 490 } 491 if err != nil { 492 info.ErrMsg = err.Error() 493 } 494 infoStr, err2 := json.Marshal(info) 495 _ = err2 496 txn.commitCallback(string(infoStr), err) 497 } 498 } 499 500 // LockKeysWithWaitTime tries to lock the entries with the keys in KV store. 501 // lockWaitTime in ms, 0 means nowait lock. 502 func (txn *KVTxn) LockKeysWithWaitTime(ctx context.Context, lockWaitTime int64, keysInput ...[]byte) (err error) { 503 forUpdateTs := txn.startTS 504 if txn.IsPessimistic() { 505 bo := retry.NewBackofferWithVars(context.Background(), TsoMaxBackoff, nil) 506 forUpdateTs, err = txn.store.GetTimestampWithRetry(bo, txn.scope) 507 if err != nil { 508 return err 509 } 510 } 511 lockCtx := tikv.NewLockCtx(forUpdateTs, lockWaitTime, time.Now()) 512 513 return txn.LockKeys(ctx, lockCtx, keysInput...) 514 } 515 516 // LockKeys tries to lock the entries with the keys in KV store. 517 // lockCtx is the context for lock, lockCtx.lockWaitTime in ms 518 func (txn *KVTxn) LockKeys(ctx context.Context, lockCtx *tikv.LockCtx, keysInput ...[]byte) error { 519 // Exclude keys that are already locked. 520 var err error 521 keys := make([][]byte, 0, len(keysInput)) 522 startTime := time.Now() 523 txn.mu.Lock() 524 defer txn.mu.Unlock() 525 defer func() { 526 metrics.TxnCmdHistogramWithLockKeys.Observe(time.Since(startTime).Seconds()) 527 if err == nil { 528 if lockCtx.PessimisticLockWaited != nil { 529 if atomic.LoadInt32(lockCtx.PessimisticLockWaited) > 0 { 530 timeWaited := time.Since(lockCtx.WaitStartTime) 531 atomic.StoreInt64(lockCtx.LockKeysDuration, int64(timeWaited)) 532 metrics.TiKVPessimisticLockKeysDuration.Observe(timeWaited.Seconds()) 533 } 534 } 535 } 536 if lockCtx.LockKeysCount != nil { 537 *lockCtx.LockKeysCount += int32(len(keys)) 538 } 539 if lockCtx.Stats != nil { 540 lockCtx.Stats.TotalTime = time.Since(startTime) 541 ctxValue := ctx.Value(util.LockKeysDetailCtxKey) 542 if ctxValue != nil { 543 lockKeysDetail := ctxValue.(**util.LockKeysDetails) 544 *lockKeysDetail = lockCtx.Stats 545 } 546 } 547 }() 548 memBuf := txn.us.GetMemBuffer() 549 for _, key := range keysInput { 550 // The value of lockedMap is only used by pessimistic transactions. 551 var valueExist, locked, checkKeyExists bool 552 if flags, err := memBuf.GetFlags(key); err == nil { 553 locked = flags.HasLocked() 554 valueExist = flags.HasLockedValueExists() 555 checkKeyExists = flags.HasNeedCheckExists() 556 } 557 if !locked { 558 keys = append(keys, key) 559 } else if txn.IsPessimistic() { 560 if checkKeyExists && valueExist { 561 alreadyExist := kvrpcpb.AlreadyExist{Key: key} 562 e := &tikverr.ErrKeyExist{AlreadyExist: &alreadyExist} 563 return txn.committer.extractKeyExistsErr(e) 564 } 565 } 566 if lockCtx.ReturnValues && locked { 567 // An already locked key can not return values, we add an entry to let the caller get the value 568 // in other ways. 569 lockCtx.Values[string(key)] = tikv.ReturnedValue{AlreadyLocked: true} 570 } 571 } 572 if len(keys) == 0 { 573 return nil 574 } 575 keys = deduplicateKeys(keys) 576 if txn.IsPessimistic() && lockCtx.ForUpdateTS > 0 { 577 if txn.committer == nil { 578 // sessionID is used for log. 579 var sessionID uint64 580 var err error 581 val := ctx.Value(util.SessionID) 582 if val != nil { 583 sessionID = val.(uint64) 584 } 585 txn.committer, err = newTwoPhaseCommitter(txn, sessionID) 586 if err != nil { 587 return err 588 } 589 } 590 var assignedPrimaryKey bool 591 if txn.committer.primaryKey == nil { 592 txn.committer.primaryKey = keys[0] 593 assignedPrimaryKey = true 594 } 595 596 lockCtx.Stats = &util.LockKeysDetails{ 597 LockKeys: int32(len(keys)), 598 } 599 bo := retry.NewBackofferWithVars(ctx, pessimisticLockMaxBackoff, txn.vars) 600 txn.committer.forUpdateTS = lockCtx.ForUpdateTS 601 // If the number of keys greater than 1, it can be on different region, 602 // concurrently execute on multiple regions may lead to deadlock. 603 txn.committer.isFirstLock = txn.lockedCnt == 0 && len(keys) == 1 604 err = txn.committer.pessimisticLockMutations(bo, lockCtx, &PlainMutations{keys: keys}) 605 if bo.GetTotalSleep() > 0 { 606 atomic.AddInt64(&lockCtx.Stats.BackoffTime, int64(bo.GetTotalSleep())*int64(time.Millisecond)) 607 lockCtx.Stats.Mu.Lock() 608 lockCtx.Stats.Mu.BackoffTypes = append(lockCtx.Stats.Mu.BackoffTypes, bo.GetTypes()...) 609 lockCtx.Stats.Mu.Unlock() 610 } 611 if lockCtx.Killed != nil { 612 // If the kill signal is received during waiting for pessimisticLock, 613 // pessimisticLockKeys would handle the error but it doesn't reset the flag. 614 // We need to reset the killed flag here. 615 atomic.CompareAndSwapUint32(lockCtx.Killed, 1, 0) 616 } 617 if err != nil { 618 for _, key := range keys { 619 if txn.us.HasPresumeKeyNotExists(key) { 620 txn.us.UnmarkPresumeKeyNotExists(key) 621 } 622 } 623 keyMayBeLocked := !(tikverr.IsErrWriteConflict(err) || tikverr.IsErrKeyExist(err)) 624 // If there is only 1 key and lock fails, no need to do pessimistic rollback. 625 if len(keys) > 1 || keyMayBeLocked { 626 dl, isDeadlock := errors.Cause(err).(*tikverr.ErrDeadlock) 627 if isDeadlock { 628 if hashInKeys(dl.DeadlockKeyHash, keys) { 629 dl.IsRetryable = true 630 } 631 if lockCtx.OnDeadlock != nil { 632 // Call OnDeadlock before pessimistic rollback. 633 lockCtx.OnDeadlock(dl) 634 } 635 } 636 637 wg := txn.asyncPessimisticRollback(ctx, keys) 638 639 if isDeadlock { 640 logutil.Logger(ctx).Debug("deadlock error received", zap.Uint64("startTS", txn.startTS), zap.Stringer("deadlockInfo", dl)) 641 if dl.IsRetryable { 642 // Wait for the pessimistic rollback to finish before we retry the statement. 643 wg.Wait() 644 // Sleep a little, wait for the other transaction that blocked by this transaction to acquire the lock. 645 time.Sleep(time.Millisecond * 5) 646 if _, err := util.EvalFailpoint("SingleStmtDeadLockRetrySleep"); err == nil { 647 time.Sleep(300 * time.Millisecond) 648 } 649 } 650 } 651 } 652 if assignedPrimaryKey { 653 // unset the primary key and stop heartbeat if we assigned primary key when failed to lock it. 654 txn.committer.primaryKey = nil 655 txn.committer.ttlManager.reset() 656 } 657 return err 658 } 659 } 660 for _, key := range keys { 661 valExists := tikv.SetKeyLockedValueExists 662 // PointGet and BatchPointGet will return value in pessimistic lock response, the value may not exist. 663 // For other lock modes, the locked key values always exist. 664 if lockCtx.ReturnValues { 665 val := lockCtx.Values[string(key)] 666 if len(val.Value) == 0 { 667 valExists = tikv.SetKeyLockedValueNotExists 668 } 669 } 670 memBuf.UpdateFlags(key, tikv.SetKeyLocked, tikv.DelNeedCheckExists, valExists) 671 } 672 txn.lockedCnt += len(keys) 673 return nil 674 } 675 676 // deduplicateKeys deduplicate the keys, it use sort instead of map to avoid memory allocation. 677 func deduplicateKeys(keys [][]byte) [][]byte { 678 sort.Slice(keys, func(i, j int) bool { 679 return bytes.Compare(keys[i], keys[j]) < 0 680 }) 681 deduped := keys[:1] 682 for i := 1; i < len(keys); i++ { 683 if !bytes.Equal(deduped[len(deduped)-1], keys[i]) { 684 deduped = append(deduped, keys[i]) 685 } 686 } 687 return deduped 688 } 689 690 const pessimisticRollbackMaxBackoff = 20000 691 692 func (txn *KVTxn) asyncPessimisticRollback(ctx context.Context, keys [][]byte) *sync.WaitGroup { 693 // Clone a new committer for execute in background. 694 committer := &twoPhaseCommitter{ 695 store: txn.committer.store, 696 sessionID: txn.committer.sessionID, 697 startTS: txn.committer.startTS, 698 forUpdateTS: txn.committer.forUpdateTS, 699 primaryKey: txn.committer.primaryKey, 700 } 701 wg := new(sync.WaitGroup) 702 wg.Add(1) 703 go func() { 704 if val, err := util.EvalFailpoint("beforeAsyncPessimisticRollback"); err == nil { 705 if s, ok := val.(string); ok { 706 if s == "skip" { 707 logutil.Logger(ctx).Info("[failpoint] injected skip async pessimistic rollback", 708 zap.Uint64("txnStartTS", txn.startTS)) 709 wg.Done() 710 return 711 } else if s == "delay" { 712 duration := time.Duration(rand.Int63n(int64(time.Second) * 2)) 713 logutil.Logger(ctx).Info("[failpoint] injected delay before async pessimistic rollback", 714 zap.Uint64("txnStartTS", txn.startTS), zap.Duration("duration", duration)) 715 time.Sleep(duration) 716 } 717 } 718 } 719 720 err := committer.pessimisticRollbackMutations(retry.NewBackofferWithVars(ctx, pessimisticRollbackMaxBackoff, txn.vars), &PlainMutations{keys: keys}) 721 if err != nil { 722 logutil.Logger(ctx).Warn("[kv] pessimisticRollback failed.", zap.Error(err)) 723 } 724 wg.Done() 725 }() 726 return wg 727 } 728 729 func hashInKeys(deadlockKeyHash uint64, keys [][]byte) bool { 730 for _, key := range keys { 731 if farm.Fingerprint64(key) == deadlockKeyHash { 732 return true 733 } 734 } 735 return false 736 } 737 738 // IsReadOnly checks if the transaction has only performed read operations. 739 func (txn *KVTxn) IsReadOnly() bool { 740 return !txn.us.GetMemBuffer().Dirty() 741 } 742 743 // StartTS returns the transaction start timestamp. 744 func (txn *KVTxn) StartTS() uint64 { 745 return txn.startTS 746 } 747 748 // Valid returns if the transaction is valid. 749 // A transaction become invalid after commit or rollback. 750 func (txn *KVTxn) Valid() bool { 751 return txn.valid 752 } 753 754 // Len returns the number of entries in the DB. 755 func (txn *KVTxn) Len() int { 756 return txn.us.GetMemBuffer().Len() 757 } 758 759 // Size returns sum of keys and values length. 760 func (txn *KVTxn) Size() int { 761 return txn.us.GetMemBuffer().Size() 762 } 763 764 // Reset reset the Transaction to initial states. 765 func (txn *KVTxn) Reset() { 766 txn.us.GetMemBuffer().Reset() 767 } 768 769 // GetUnionStore returns the UnionStore binding to this transaction. 770 func (txn *KVTxn) GetUnionStore() *unionstore.KVUnionStore { 771 return txn.us 772 } 773 774 // GetMemBuffer return the MemBuffer binding to this transaction. 775 func (txn *KVTxn) GetMemBuffer() *unionstore.MemDB { 776 return txn.us.GetMemBuffer() 777 } 778 779 // GetSnapshot returns the Snapshot binding to this transaction. 780 func (txn *KVTxn) GetSnapshot() *txnsnapshot.KVSnapshot { 781 return txn.snapshot 782 } 783 784 // SetBinlogExecutor sets the method to perform binlong synchronization. 785 func (txn *KVTxn) SetBinlogExecutor(binlog BinlogExecutor) { 786 txn.binlog = binlog 787 if txn.committer != nil { 788 txn.committer.binlog = binlog 789 } 790 } 791 792 // GetClusterID returns store's cluster id. 793 func (txn *KVTxn) GetClusterID() uint64 { 794 return txn.store.GetClusterID() 795 }