@@ -100,7 +100,9 @@ type diagnosticContext struct {
100
100
reqDuration time.Duration
101
101
}
102
102
103
- func (action actionPessimisticLock ) handleSingleBatch (c * twoPhaseCommitter , bo * retry.Backoffer , batch batchMutations ) error {
103
+ func (action actionPessimisticLock ) handleSingleBatch (
104
+ c * twoPhaseCommitter , bo * retry.Backoffer , batch batchMutations ,
105
+ ) error {
104
106
convertMutationsToPb := func (committerMutations CommitterMutations ) []* kvrpcpb.Mutation {
105
107
mutations := make ([]* kvrpcpb.Mutation , committerMutations .Len ())
106
108
c .txn .GetMemBuffer ().RLock ()
@@ -120,26 +122,28 @@ func (action actionPessimisticLock) handleSingleBatch(c *twoPhaseCommitter, bo *
120
122
121
123
m := batch .mutations
122
124
mutations := convertMutationsToPb (m )
123
- req := tikvrpc .NewRequest (tikvrpc .CmdPessimisticLock , & kvrpcpb.PessimisticLockRequest {
124
- Mutations : mutations ,
125
- PrimaryLock : c .primary (),
126
- StartVersion : c .startTS ,
127
- ForUpdateTs : c .forUpdateTS ,
128
- IsFirstLock : c .isFirstLock ,
129
- WaitTimeout : action .LockWaitTime (),
130
- ReturnValues : action .ReturnValues ,
131
- CheckExistence : action .CheckExistence ,
132
- MinCommitTs : c .forUpdateTS + 1 ,
133
- WakeUpMode : action .wakeUpMode ,
134
- LockOnlyIfExists : action .LockOnlyIfExists ,
135
- }, kvrpcpb.Context {
136
- Priority : c .priority ,
137
- SyncLog : c .syncLog ,
138
- ResourceGroupTag : action .LockCtx .ResourceGroupTag ,
139
- MaxExecutionDurationMs : uint64 (client .MaxWriteExecutionTime .Milliseconds ()),
140
- RequestSource : c .txn .GetRequestSource (),
141
- ResourceGroupName : c .resourceGroupName ,
142
- })
125
+ req := tikvrpc .NewRequest (
126
+ tikvrpc .CmdPessimisticLock , & kvrpcpb.PessimisticLockRequest {
127
+ Mutations : mutations ,
128
+ PrimaryLock : c .primary (),
129
+ StartVersion : c .startTS ,
130
+ ForUpdateTs : c .forUpdateTS ,
131
+ IsFirstLock : c .isFirstLock ,
132
+ WaitTimeout : action .LockWaitTime (),
133
+ ReturnValues : action .ReturnValues ,
134
+ CheckExistence : action .CheckExistence ,
135
+ MinCommitTs : c .forUpdateTS + 1 ,
136
+ WakeUpMode : action .wakeUpMode ,
137
+ LockOnlyIfExists : action .LockOnlyIfExists ,
138
+ }, kvrpcpb.Context {
139
+ Priority : c .priority ,
140
+ SyncLog : c .syncLog ,
141
+ ResourceGroupTag : action .LockCtx .ResourceGroupTag ,
142
+ MaxExecutionDurationMs : uint64 (client .MaxWriteExecutionTime .Milliseconds ()),
143
+ RequestSource : c .txn .GetRequestSource (),
144
+ ResourceGroupName : c .resourceGroupName ,
145
+ },
146
+ )
143
147
if action .LockCtx .ResourceGroupTag == nil && action .LockCtx .ResourceGroupTagger != nil {
144
148
req .ResourceGroupTag = action .LockCtx .ResourceGroupTagger (req .Req .(* kvrpcpb.PessimisticLockRequest ))
145
149
}
@@ -168,8 +172,10 @@ func (action actionPessimisticLock) handleSingleBatch(c *twoPhaseCommitter, bo *
168
172
for _ , m := range mutations {
169
173
keys = append (keys , hex .EncodeToString (m .Key ))
170
174
}
171
- logutil .BgLogger ().Info ("[failpoint] injected lock ttl = 1 on pessimistic lock" ,
172
- zap .Uint64 ("txnStartTS" , c .startTS ), zap .Strings ("keys" , keys ))
175
+ logutil .BgLogger ().Info (
176
+ "[failpoint] injected lock ttl = 1 on pessimistic lock" ,
177
+ zap .Uint64 ("txnStartTS" , c .startTS ), zap .Strings ("keys" , keys ),
178
+ )
173
179
}
174
180
req .PessimisticLock ().LockTtl = ttl
175
181
if _ , err := util .EvalFailpoint ("PessimisticLockErrWriteConflict" ); err == nil {
@@ -221,7 +227,9 @@ func (action actionPessimisticLock) handleSingleBatch(c *twoPhaseCommitter, bo *
221
227
}
222
228
}
223
229
224
- func (action actionPessimisticLock ) handleRegionError (c * twoPhaseCommitter , bo * retry.Backoffer , batch * batchMutations , regionErr * errorpb.Error ) (finished bool , err error ) {
230
+ func (action actionPessimisticLock ) handleRegionError (
231
+ c * twoPhaseCommitter , bo * retry.Backoffer , batch * batchMutations , regionErr * errorpb.Error ,
232
+ ) (finished bool , err error ) {
225
233
// For other region error and the fake region error, backoff because
226
234
// there's something wrong.
227
235
// For the real EpochNotMatch error, don't backoff.
@@ -242,7 +250,13 @@ func (action actionPessimisticLock) handleRegionError(c *twoPhaseCommitter, bo *
242
250
return true , err
243
251
}
244
252
245
- func (action actionPessimisticLock ) handleKeyError (c * twoPhaseCommitter , keyErrs []* kvrpcpb.KeyError ) (locks []* txnlock.Lock , finished bool , err error ) {
253
+ // When handling wait timeout, if the current lock is updated within the threshold, do not try to resolve lock
254
+ // The default timeout in TiKV is 1 second. 300ms should be appropriate for common hot update workloads.
255
+ const skipResolveThresholdMs = 300
256
+
257
+ func (action actionPessimisticLock ) handleKeyErrorForResolve (
258
+ c * twoPhaseCommitter , keyErrs []* kvrpcpb.KeyError ,
259
+ ) (locks []* txnlock.Lock , finished bool , err error ) {
246
260
for _ , keyErr := range keyErrs {
247
261
// Check already exists error
248
262
if alreadyExist := keyErr .GetAlreadyExist (); alreadyExist != nil {
@@ -253,17 +267,32 @@ func (action actionPessimisticLock) handleKeyError(c *twoPhaseCommitter, keyErrs
253
267
return nil , true , errors .WithStack (& tikverr.ErrDeadlock {Deadlock : deadlock })
254
268
}
255
269
270
+ // Do not resolve the lock if the lock was recently updated which indicates the txn holding the lock is
271
+ // much likely alive.
272
+ // This should only happen for wait timeout.
273
+ if lockInfo := keyErr .GetLocked (); lockInfo != nil &&
274
+ lockInfo .DurationToLastUpdateMs > 0 &&
275
+ lockInfo .DurationToLastUpdateMs < skipResolveThresholdMs {
276
+ continue
277
+ }
278
+
256
279
// Extract lock from key error
257
280
lock , err1 := txnlock .ExtractLockFromKeyErr (keyErr )
258
281
if err1 != nil {
259
282
return nil , true , err1
260
283
}
261
284
locks = append (locks , lock )
262
285
}
286
+ if len (locks ) == 0 {
287
+ return nil , false , nil
288
+ }
263
289
return locks , false , nil
264
290
}
265
291
266
- func (action actionPessimisticLock ) handlePessimisticLockResponseNormalMode (c * twoPhaseCommitter , bo * retry.Backoffer , batch * batchMutations , mutationsPb []* kvrpcpb.Mutation , resp * tikvrpc.Response , diagCtx * diagnosticContext ) (finished bool , err error ) {
292
+ func (action actionPessimisticLock ) handlePessimisticLockResponseNormalMode (
293
+ c * twoPhaseCommitter , bo * retry.Backoffer , batch * batchMutations , mutationsPb []* kvrpcpb.Mutation ,
294
+ resp * tikvrpc.Response , diagCtx * diagnosticContext ,
295
+ ) (finished bool , err error ) {
267
296
regionErr , err := resp .GetRegionError ()
268
297
if err != nil {
269
298
return true , err
@@ -283,7 +312,12 @@ func (action actionPessimisticLock) handlePessimisticLockResponseNormalMode(c *t
283
312
if len (keyErrs ) == 0 {
284
313
285
314
if action .LockCtx .Stats != nil {
286
- action .LockCtx .Stats .MergeReqDetails (diagCtx .reqDuration , batch .region .GetID (), diagCtx .sender .GetStoreAddr (), lockResp .ExecDetailsV2 )
315
+ action .LockCtx .Stats .MergeReqDetails (
316
+ diagCtx .reqDuration ,
317
+ batch .region .GetID (),
318
+ diagCtx .sender .GetStoreAddr (),
319
+ lockResp .ExecDetailsV2 ,
320
+ )
287
321
}
288
322
289
323
if batch .isPrimary {
@@ -314,10 +348,14 @@ func (action actionPessimisticLock) handlePessimisticLockResponseNormalMode(c *t
314
348
}
315
349
return true , nil
316
350
}
317
- locks , finished , err := action .handleKeyError (c , keyErrs )
351
+
352
+ locks , finished , err := action .handleKeyErrorForResolve (c , keyErrs )
318
353
if err != nil {
319
354
return finished , err
320
355
}
356
+ if len (locks ) == 0 {
357
+ return false , nil
358
+ }
321
359
322
360
// Because we already waited on tikv, no need to Backoff here.
323
361
// tikv default will wait 3s(also the maximum wait value) when lock error occurs
@@ -360,7 +398,10 @@ func (action actionPessimisticLock) handlePessimisticLockResponseNormalMode(c *t
360
398
return false , nil
361
399
}
362
400
363
- func (action actionPessimisticLock ) handlePessimisticLockResponseForceLockMode (c * twoPhaseCommitter , bo * retry.Backoffer , batch * batchMutations , mutationsPb []* kvrpcpb.Mutation , resp * tikvrpc.Response , diagCtx * diagnosticContext ) (finished bool , err error ) {
401
+ func (action actionPessimisticLock ) handlePessimisticLockResponseForceLockMode (
402
+ c * twoPhaseCommitter , bo * retry.Backoffer , batch * batchMutations , mutationsPb []* kvrpcpb.Mutation ,
403
+ resp * tikvrpc.Response , diagCtx * diagnosticContext ,
404
+ ) (finished bool , err error ) {
364
405
regionErr , err := resp .GetRegionError ()
365
406
if err != nil {
366
407
return true , err
@@ -376,7 +417,9 @@ func (action actionPessimisticLock) handlePessimisticLockResponseForceLockMode(c
376
417
if len (mutationsPb ) > 1 || len (lockResp .Results ) > 1 {
377
418
panic ("unreachable" )
378
419
}
379
- if batch .isPrimary && len (lockResp .Results ) > 0 && lockResp .Results [0 ].Type != kvrpcpb .PessimisticLockKeyResultType_LockResultFailed {
420
+ if batch .isPrimary &&
421
+ len (lockResp .Results ) > 0 &&
422
+ lockResp .Results [0 ].Type != kvrpcpb .PessimisticLockKeyResultType_LockResultFailed {
380
423
// After locking the primary key, we should protect the primary lock from expiring.
381
424
c .run (c , action .LockCtx )
382
425
}
@@ -422,11 +465,16 @@ func (action actionPessimisticLock) handlePessimisticLockResponseForceLockMode(c
422
465
423
466
if len (lockResp .Results ) > 0 && ! isMutationFailed {
424
467
if action .LockCtx .Stats != nil {
425
- action .LockCtx .Stats .MergeReqDetails (diagCtx .reqDuration , batch .region .GetID (), diagCtx .sender .GetStoreAddr (), lockResp .ExecDetailsV2 )
468
+ action .LockCtx .Stats .MergeReqDetails (
469
+ diagCtx .reqDuration ,
470
+ batch .region .GetID (),
471
+ diagCtx .sender .GetStoreAddr (),
472
+ lockResp .ExecDetailsV2 ,
473
+ )
426
474
}
427
475
}
428
476
429
- locks , finished , err := action .handleKeyError (c , keyErrs )
477
+ locks , finished , err := action .handleKeyErrorForResolve (c , keyErrs )
430
478
if err != nil {
431
479
return finished , err
432
480
}
@@ -477,9 +525,9 @@ func (action actionPessimisticLock) handlePessimisticLockResponseForceLockMode(c
477
525
return false , nil
478
526
}
479
527
480
- // If the failedMutations is not empty and the error is not KeyIsLocked, the function should have already
481
- // returned before. So this is an unreachable path.
482
- return true , errors . New ( "Pessimistic lock response corrupted" )
528
+ // This can be the situation where KeyIsLocked errors are generated by timeout,
529
+ // and we decide not to resolve them. Instead, just retry
530
+ return false , nil
483
531
}
484
532
485
533
if len (locks ) != 0 {
@@ -497,16 +545,20 @@ func (action actionPessimisticLock) handlePessimisticLockResponseForceLockMode(c
497
545
return true , nil
498
546
}
499
547
500
- func (actionPessimisticRollback ) handleSingleBatch (c * twoPhaseCommitter , bo * retry.Backoffer , batch batchMutations ) error {
548
+ func (actionPessimisticRollback ) handleSingleBatch (
549
+ c * twoPhaseCommitter , bo * retry.Backoffer , batch batchMutations ,
550
+ ) error {
501
551
forUpdateTS := c .forUpdateTS
502
552
if c .maxLockedWithConflictTS > forUpdateTS {
503
553
forUpdateTS = c .maxLockedWithConflictTS
504
554
}
505
- req := tikvrpc .NewRequest (tikvrpc .CmdPessimisticRollback , & kvrpcpb.PessimisticRollbackRequest {
506
- StartVersion : c .startTS ,
507
- ForUpdateTs : forUpdateTS ,
508
- Keys : batch .mutations .GetKeys (),
509
- })
555
+ req := tikvrpc .NewRequest (
556
+ tikvrpc .CmdPessimisticRollback , & kvrpcpb.PessimisticRollbackRequest {
557
+ StartVersion : c .startTS ,
558
+ ForUpdateTs : forUpdateTS ,
559
+ Keys : batch .mutations .GetKeys (),
560
+ },
561
+ )
510
562
req .RequestSource = util .RequestSourceFromCtx (bo .GetCtx ())
511
563
req .MaxExecutionDurationMs = uint64 (client .MaxWriteExecutionTime .Milliseconds ())
512
564
resp , err := c .store .SendReq (bo , req , batch .region , client .ReadTimeoutShort )
@@ -528,7 +580,10 @@ func (actionPessimisticRollback) handleSingleBatch(c *twoPhaseCommitter, bo *ret
528
580
return nil
529
581
}
530
582
531
- func (c * twoPhaseCommitter ) pessimisticLockMutations (bo * retry.Backoffer , lockCtx * kv.LockCtx , lockWaitMode kvrpcpb.PessimisticLockWakeUpMode , mutations CommitterMutations ) error {
583
+ func (c * twoPhaseCommitter ) pessimisticLockMutations (
584
+ bo * retry.Backoffer , lockCtx * kv.LockCtx , lockWaitMode kvrpcpb.PessimisticLockWakeUpMode ,
585
+ mutations CommitterMutations ,
586
+ ) error {
532
587
if c .sessionID > 0 {
533
588
if val , err := util .EvalFailpoint ("beforePessimisticLock" ); err == nil {
534
589
// Pass multiple instructions in one string, delimited by commas, to trigger multiple behaviors, like
@@ -537,19 +592,27 @@ func (c *twoPhaseCommitter) pessimisticLockMutations(bo *retry.Backoffer, lockCt
537
592
for _ , action := range strings .Split (v , "," ) {
538
593
if action == "delay" {
539
594
duration := time .Duration (rand .Int63n (int64 (time .Second ) * 5 ))
540
- logutil .Logger (bo .GetCtx ()).Info ("[failpoint] injected delay at pessimistic lock" ,
541
- zap .Uint64 ("txnStartTS" , c .startTS ), zap .Duration ("duration" , duration ))
595
+ logutil .Logger (bo .GetCtx ()).Info (
596
+ "[failpoint] injected delay at pessimistic lock" ,
597
+ zap .Uint64 ("txnStartTS" , c .startTS ), zap .Duration ("duration" , duration ),
598
+ )
542
599
time .Sleep (duration )
543
600
} else if action == "fail" {
544
- logutil .Logger (bo .GetCtx ()).Info ("[failpoint] injected failure at pessimistic lock" ,
545
- zap .Uint64 ("txnStartTS" , c .startTS ))
601
+ logutil .Logger (bo .GetCtx ()).Info (
602
+ "[failpoint] injected failure at pessimistic lock" ,
603
+ zap .Uint64 ("txnStartTS" , c .startTS ),
604
+ )
546
605
return errors .New ("injected failure at pessimistic lock" )
547
606
}
548
607
}
549
608
}
550
609
}
551
610
}
552
- return c .doActionOnMutations (bo , actionPessimisticLock {LockCtx : lockCtx , wakeUpMode : lockWaitMode , isInternal : c .txn .isInternal ()}, mutations )
611
+ return c .doActionOnMutations (
612
+ bo ,
613
+ actionPessimisticLock {LockCtx : lockCtx , wakeUpMode : lockWaitMode , isInternal : c .txn .isInternal ()},
614
+ mutations ,
615
+ )
553
616
}
554
617
555
618
func (c * twoPhaseCommitter ) pessimisticRollbackMutations (bo * retry.Backoffer , mutations CommitterMutations ) error {
0 commit comments