Skip to content

kv: implement Update lock strength for unreplicated locks #49684

@nvb

Description

@nvb

The lockTable currently only supports the exclusive lock strength, but was designed to support varying degrees of lock strengths. Specifically, we'd eventually like to add support for Shared and Upgrade locks as well.

// +-----------+-----------+-----------+-----------+-----------+
// | | None | Shared | Upgrade | Exclusive |
// +-----------+-----------+-----------+-----------+-----------+
// | None | | | | X^† |
// +-----------+-----------+-----------+-----------+-----------+
// | Shared | | | X | X |
// +-----------+-----------+-----------+-----------+-----------+
// | Upgrade | | X | X | X |
// +-----------+-----------+-----------+-----------+-----------+
// | Exclusive | X^† | X | X | X |
// +-----------+-----------+-----------+-----------+-----------+

We currently have no need for Shared locks, but Upgrade locks would be immediately useful for unreplicated locks acquired during the initial row-fetch of an UPDATE statement (implicit SFU) or by a SELECT ... FOR UPDATE statement (explicit SFU).

// Upgrade (U) locks are a hybrid of Shared and Exclusive locks which are
// used to prevent a common form of deadlock. When a transaction intends to
// modify existing KVs, it is often the case that it reads the KVs first and
// then attempts to modify them. Under pessimistic concurrency control, this
// would correspond to first acquiring a Shared lock on the keys and then
// converting the lock to an Exclusive lock when modifying the keys. If two
// transactions were to acquire the Shared lock initially and then attempt
// to update the keys concurrently, both transactions would get stuck
// waiting for the other to release its Shared lock and a deadlock would
// occur. To resolve the deadlock, one of the two transactions would need to
// be aborted.
//
// To avoid this potential deadlock problem, an Upgrade lock can be used in
// place of a Shared lock. Upgrade locks are not compatible with any other
// form of locking. As with Shared locks, the lock holder of a Shared lock
// on a key is only allowed to read from the key while the lock is held.
// This resolves the deadlock scenario presented above because only one of
// the transactions would have been able to acquire an Upgrade lock at a
// time while reading the initial state of the KVs. This means that the
// Shared-to-Exclusive lock upgrade would never need to wait on another
// transaction to release its locks.
//
// Under pure pessimistic concurrency control, an Upgrade lock is equivalent
// to an Exclusive lock. However, unlike with Exclusive locks, reads under
// optimistic concurrency control do not conflict with Upgrade locks. This
// is because a transaction can only hold an Upgrade lock on keys that it
// has not yet modified. This improves concurrency between read and write
// transactions compared to if the writing transaction had immediately
// acquired an Exclusive lock.
//
// The trade-off here is twofold. First, if the Upgrade lock holder does
// convert its lock on a key to an Exclusive lock after an optimistic read
// has observed the state of the key, the transaction that performed the
// optimistic read may be unable to perform a successful read refresh if it
// attempts to refresh to a timestamp at or past the timestamp of the lock
// conversion. Second, the optimistic reads permitted while the Upgrade lock
// is held will bump the timestamp cache. This may result in the Upgrade
// lock holder being forced to increase its write timestamp when converting
// to an Exclusive lock, which in turn may force it to restart if its read
// refresh fails.
Upgrade = 2;

The major benefit of this form of lock is that it does not conflict with non-locking reads. This avoids blocking these reads, at the expense of undermining some of the protection against transactions retries that is provided by SFU. This is because these non-locking reads are allowed to slip underneath Upgrade locks and bump the timestamp cache, which may force the UPDATE to have to push its timestamp when it returns to write an intent. This is probably ok, and will certainly be once we also support Shared locks and SELECT ... FOR SHARE so that users can have full control over row-level locking.

This should improve throughput on YCSB-B (95% reads, 5% updates). It's unclear how it will affect YCSB-A (50% reads, 50% updates).

Jira issue: CRDB-4209

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-kv-transactionsRelating to MVCC and the transactional model.C-enhancementSolution expected to add code/behavior + preserve backward-compat (pg compat issues are exception)C-performancePerf of queries or internals. Solution not expected to change functional behavior.T-kvKV Team

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions