Skip to content

Commit a98be1f

Browse files
committed
kv: support unreplicated locks, hook up SELECT FOR UPDATE
Closes cockroachdb#40205. Informs cockroachdb#41720. This change teaches the KV client and the KV API about unreplicated locks. It then adds a KeyLocking mode to ScanRequest and ReverseScanRequest, which allows their users to select the locking strength that they would like the scan to use. This locking strength defaults to None, which corresponds to the current behavior. However, some users will want to acquire locks on each row scanned, which is now possible by setting the locking strength to a stronger level. For now, only the Exclusive strength is supported. The change then revisits SQL's row-level locking support, which is supported all the way down to the row fetcher for implicit (e.g. UPDATE) and explicit (e.g. SELECT ... FOR UPDATE) upgrade locking. The change uses the new key-locking functionality in the KV API to hook up row-level locking, completing the integration of SELECT FOR UPDATE with the KV layer and, in particular, the new lock-table structure. cockroachdb#43775 described the three main benefits of this change: - higher throughput under contention - lower latency and improved fairness under contention - a reduction in transaction retries under contention I've revisited those results a few times in the last two months and seen that the results continue to hold, and in some cases they have improved. I intend to update this PR with a more complete analysis of its impact on those three areas. Release note (sql change): SELECT FOR UPDATE now hooks into a new leaseholder-only locking mechanism. This allows the feature to be used to improve performance of transactions that read, modify, and write contended to rows. Similarly, UPDATE statements now use this new mechanism by default, meaning that their performance under contention is improved. This is only enabled for UPDATE statements that can push their filter all the way into their key-value scan. To determine whether an UPDATE statement is implicitly using SELECT FOR UPDATE locking, look for a "locking strength" field in the EXPLAIN output for the statement.
1 parent c1d217f commit a98be1f

34 files changed

Lines changed: 1926 additions & 1159 deletions

c-deps/libroach/protos/roachpb/api.pb.cc

Lines changed: 80 additions & 8 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

c-deps/libroach/protos/roachpb/api.pb.h

Lines changed: 43 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

c-deps/libroach/protos/roachpb/data.pb.h

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/ccl/changefeedccl/kvfeed/scanner.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ func (p *scanRequestScanner) exportSpan(
107107
for remaining := span; ; {
108108
start := timeutil.Now()
109109
b := txn.NewBatch()
110-
r := roachpb.NewScan(remaining.Key, remaining.EndKey).(*roachpb.ScanRequest)
110+
r := roachpb.NewScan(remaining.Key, remaining.EndKey, false /* forUpdate */).(*roachpb.ScanRequest)
111111
r.ScanFormat = roachpb.BATCH_RESPONSE
112112
b.Header.TargetBytes = targetBytesPerScan
113113
// NB: We use a raw request rather than the Scan() method because we want

pkg/internal/client/batch.go

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -520,7 +520,7 @@ func (b *Batch) Inc(key interface{}, value int64) {
520520
b.initResult(1, 1, notRaw, nil)
521521
}
522522

523-
func (b *Batch) scan(s, e interface{}, isReverse bool) {
523+
func (b *Batch) scan(s, e interface{}, isReverse, forUpdate bool) {
524524
begin, err := marshalKey(s)
525525
if err != nil {
526526
b.initResult(0, 0, notRaw, err)
@@ -532,22 +532,34 @@ func (b *Batch) scan(s, e interface{}, isReverse bool) {
532532
return
533533
}
534534
if !isReverse {
535-
b.appendReqs(roachpb.NewScan(begin, end))
535+
b.appendReqs(roachpb.NewScan(begin, end, forUpdate))
536536
} else {
537-
b.appendReqs(roachpb.NewReverseScan(begin, end))
537+
b.appendReqs(roachpb.NewReverseScan(begin, end, forUpdate))
538538
}
539539
b.initResult(1, 0, notRaw, nil)
540540
}
541541

542542
// Scan retrieves the key/values between begin (inclusive) and end (exclusive) in
543543
// ascending order.
544544
//
545-
// A new result will be appended to the batch which will contain "rows" (each
545+
// A new result will be appended to the batch which will contain "rows" (each
546546
// row is a key/value pair) and Result.Err will indicate success or failure.
547547
//
548548
// key can be either a byte slice or a string.
549549
func (b *Batch) Scan(s, e interface{}) {
550-
b.scan(s, e, false)
550+
b.scan(s, e, false /* isReverse */, false /* forUpdate */)
551+
}
552+
553+
// ScanForUpdate retrieves the key/values between begin (inclusive) and end
554+
// (exclusive) in ascending order. Unreplicated, exclusive locks are acquired on
555+
// each of the returned keys.
556+
//
557+
// A new result will be appended to the batch which will contain "rows" (each
558+
// row is a key/value pair) and Result.Err will indicate success or failure.
559+
//
560+
// key can be either a byte slice or a string.
561+
func (b *Batch) ScanForUpdate(s, e interface{}) {
562+
b.scan(s, e, false /* isReverse */, true /* forUpdate */)
551563
}
552564

553565
// ReverseScan retrieves the rows between begin (inclusive) and end (exclusive)
@@ -558,7 +570,19 @@ func (b *Batch) Scan(s, e interface{}) {
558570
//
559571
// key can be either a byte slice or a string.
560572
func (b *Batch) ReverseScan(s, e interface{}) {
561-
b.scan(s, e, true)
573+
b.scan(s, e, true /* isReverse */, false /* forUpdate */)
574+
}
575+
576+
// ReverseScanForUpdate retrieves the rows between begin (inclusive) and end
577+
// (exclusive) in descending order. Unreplicated, exclusive locks are acquired
578+
// on each of the returned keys.
579+
//
580+
// A new result will be appended to the batch which will contain "rows" (each
581+
// "row" is a key/value pair) and Result.Err will indicate success or failure.
582+
//
583+
// key can be either a byte slice or a string.
584+
func (b *Batch) ReverseScanForUpdate(s, e interface{}) {
585+
b.scan(s, e, true /* isReverse */, true /* forUpdate */)
562586
}
563587

564588
// Del deletes one or more keys.

0 commit comments

Comments
 (0)