Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

concurrency: allow shared locking requests into the lock table #107550

Merged
merged 1 commit into from
Jul 31, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 9 additions & 6 deletions pkg/kv/kvserver/concurrency/lock_table.go
Original file line number Diff line number Diff line change
Expand Up @@ -764,23 +764,24 @@ func (g *lockTableGuardImpl) curStrength() lock.Strength {
// request. The value returned by this method are mutable as the request's scan
// of the lock table progresses from lock to lock.
func (g *lockTableGuardImpl) curLockMode() lock.Mode {
var reqMode lock.Mode
switch g.curStrength() {
case lock.None:
iso := isolation.Serializable
if g.txn != nil {
iso = g.txn.IsoLevel
}
reqMode = lock.MakeModeNone(g.ts, iso)
return lock.MakeModeNone(g.ts, iso)
case lock.Shared:
assert(g.txn != nil, "only transactional requests can acquire shared locks")
return lock.MakeModeShared()
case lock.Exclusive:
assert(g.txn != nil, "only transactional requests can acquire exclusive locks")
reqMode = lock.MakeModeExclusive(g.ts, g.txn.IsoLevel)
return lock.MakeModeExclusive(g.ts, g.txn.IsoLevel)
case lock.Intent:
reqMode = lock.MakeModeIntent(g.ts)
return lock.MakeModeIntent(g.ts)
default:
panic(fmt.Sprintf("unhandled request strength: %s", g.curStrength()))
}
return reqMode
}

// takeToResolveUnreplicated returns the list of unreplicated locks accumulated
Expand Down Expand Up @@ -1856,7 +1857,7 @@ func (l *lockState) getLockMode() lock.Mode {
case lock.Exclusive:
return lock.MakeModeExclusive(lockHolderTS, lockHolderTxn.IsoLevel)
case lock.Shared:
panic(fmt.Sprintf("unexpected lock strength %s", str))
return lock.MakeModeShared()
default:
panic(fmt.Sprintf("unexpected lock strength %s", str))
}
Expand Down Expand Up @@ -3423,6 +3424,8 @@ func (t *lockTableImpl) AcquireLock(acq *roachpb.LockAcquisition) error {
assert(acq.Durability == lock.Replicated, "incorrect durability")
case lock.Exclusive:
assert(acq.Durability == lock.Unreplicated, "incorrect durability")
case lock.Shared:
assert(acq.Durability == lock.Unreplicated, "incorrect durability")
default:
return errors.AssertionFailedf("unsupported lock strength %s", acq.Strength)
}
Expand Down
7 changes: 7 additions & 0 deletions pkg/kv/kvserver/concurrency/lock_table_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -713,6 +713,13 @@ func scanSpans(
sa = spanset.SpanReadWrite
case lock.Exclusive:
sa = spanset.SpanReadWrite
case lock.Shared:
// Unlike non-locking reads, shared-locking reads are isolated at all
// timestamps (not just the request's timestamp); so we acquire a read
// latch at max timestamp. See
// https://github.com/cockroachdb/cockroach/issues/102264.
sa = spanset.SpanReadOnly
ts = hlc.MaxTimestamp
default:
d.Fatalf(t, "unsupported lock strength: %s", str)
}
Expand Down
103 changes: 103 additions & 0 deletions pkg/kv/kvserver/concurrency/testdata/lock_table/shared_locks
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
new-lock-table maxlocks=10000
----

new-txn txn=txn1 ts=10 epoch=0 seq=0
----

new-txn txn=txn2 ts=10 epoch=0 seq=0
----

new-request r=req1 txn=txn1 ts=10 spans=shared@a
----

acquire r=req1 k=a durability=u strength=shared
----
num=1
lock: "a"
holder: txn: 00000000-0000-0000-0000-000000000001 epoch: 0, iso: Serializable, ts: 10.000000000,0, info: unrepl [(str: Shared seq: 0)]

# ------------------------------------------------------------------------------
# Ensure conflict resolution semantics for shared locks are sane -- that is,
# if a shared lock is held on a key, {shared, non} locking requests are allowed
# to proceed; {intent, exclusive} locking requests are not.
# ------------------------------------------------------------------------------

new-request r=req2 txn=txn2 ts=10 spans=none@a
----

scan r=req2
----
start-waiting: false

new-request r=req3 txn=txn2 ts=10 spans=shared@a
----

# req3 should not actively wait, as it's locking strength is shared, but it
# should be able to acquire a joint claim.
scan r=req3
----
start-waiting: false

print
----
num=1
lock: "a"
holder: txn: 00000000-0000-0000-0000-000000000001 epoch: 0, iso: Serializable, ts: 10.000000000,0, info: unrepl [(str: Shared seq: 0)]
queued writers:
active: false req: 2, txn: 00000000-0000-0000-0000-000000000002

# TODO(arul): This is currently a limitation of the lock table, as it doesn't
# allow multiple locks on a single key from different transactions.
acquire r=req3 k=a durability=u strength=shared
----
existing lock cannot be acquired by different transaction

new-request r=req4 txn=txn2 ts=10 spans=exclusive@a
----

scan r=req4
----
start-waiting: true

new-request r=req5 txn=txn2 ts=10 spans=intent@a
----

scan r=req5
----
start-waiting: true

print
----
num=1
lock: "a"
holder: txn: 00000000-0000-0000-0000-000000000001 epoch: 0, iso: Serializable, ts: 10.000000000,0, info: unrepl [(str: Shared seq: 0)]
queued writers:
active: false req: 2, txn: 00000000-0000-0000-0000-000000000002
active: true req: 3, txn: 00000000-0000-0000-0000-000000000002
active: true req: 4, txn: 00000000-0000-0000-0000-000000000002
distinguished req: 3

# ------------------------------------------------------------------------------
# Ensure requests with locking strength shared actively wait if there are active
# waiters with conflicting lock strengths (even though the lock itself is
# compatible with the shared lock request).
# ------------------------------------------------------------------------------

new-request r=req6 txn=txn2 ts=10 spans=shared@a
----

scan r=req6
----
start-waiting: true

print
----
num=1
lock: "a"
holder: txn: 00000000-0000-0000-0000-000000000001 epoch: 0, iso: Serializable, ts: 10.000000000,0, info: unrepl [(str: Shared seq: 0)]
queued writers:
active: false req: 2, txn: 00000000-0000-0000-0000-000000000002
active: true req: 3, txn: 00000000-0000-0000-0000-000000000002
active: true req: 4, txn: 00000000-0000-0000-0000-000000000002
active: true req: 5, txn: 00000000-0000-0000-0000-000000000002
distinguished req: 3