Class LockManager
- java.lang.Object
-
- org.opends.server.types.LockManager
-
@PublicAPI(stability=UNCOMMITTED, mayInstantiate=false, mayExtend=false, mayInvoke=true) public final class LockManager extends Object
A lock manager coordinates directory update operations so that the DIT structure remains in a consistent state, as well as providing repeatable read isolation. When accessing entries components need to ensure that they have the appropriate lock:- repeatable reads: repeatable read isolation is rarely needed in practice, since all backend
reads are guaranteed to be performed with read-committed isolation, which is normally sufficient.
Specifically, read-only operations such as compare and search do not require any additional
locking. If repeatable read isolation is required then lock the entry using
tryReadLockEntry(DN) - modifying an entry: acquire an entry write-lock for the target entry using
tryWriteLockEntry(DN). Updates are typically performed using a read-modify-write cycle, so the write lock should be acquired before performing the initial read in order to ensure consistency - adding an entry: client code must acquire an entry write-lock for the target entry using
tryWriteLockEntry(DN). The parent entry will automatically be protected from deletion by an implicit subtree read lock on the parent - deleting an entry: client code must acquire a subtree write lock for the target entry using
tryWriteLockSubtree(DN) - renaming an entry: client code must acquire a subtree write lock for the old entry, and a
subtree write lock for the new entry using
tryWriteLockSubtree(DN). Care should be taken to avoid deadlocks, e.g. by locking the DN which sorts first.
Implementation Notes
The lock table is conceptually a cache of locks keyed on DN, i.e. a
Map<DN, DNLock>. Locks must be kept in the cache while they are locked, but may be removed once they are no longer locked by any threads. Locks are represented using a pair of read-write locks: the first lock is the "subtree" lock and the second is the "entry" lock.In order to lock an entry for read or write a subtree read lock is first acquired on each of the parent entries from the root DN down to the immediate parent of the entry to be locked. Then the appropriate read or write entry lock is acquired for the target entry. Subtree write locking is performed by acquiring a subtree read lock on each of the parent entries from the root DN down to the immediate parent of the subtree to be locked. Then a subtree write lock is acquired for the target subtree.
The lock table itself is not represented using a
ConcurrentHashMapbecause the JDK6/7 APIs do not provide the ability to atomically add-and-lock or unlock-and-remove locks (this capability is provided in JDK8). Instead, we provide our own implementation comprising of a fixed number of buckets, a bucket being aLinkedListofDNLocks. In addition, it is important to be able to efficiently iterate up and down a chain of hierarchically related locks, so each lock maintains a reference to its parent lock. Modern directories tend to have a flat structure so it is also important to avoid contention on "hot" parent DNs. Typically, a lock attempt against a DN will involve a cache miss for the target DN and a cache hit for the parent, but the parent will be the same parent for all lock requests, resulting in a lot of contention on the same lock bucket. To avoid this the lock manager maintains a small-thread local cache of locks, so that parent locks can be acquired using a lock-free algorithm.Since the thread local cache may reference locks which are not actively locked by anyone, a reference counting mechanism is used in order to prevent cached locks from being removed from the underlying lock table. The reference counting mechanism is also used for references between a lock and its parent lock. To summarize, locking a DN involves the following steps:
- get the lock from the thread local cache. If the lock was not in the thread local cache then
try fetching it from the lock table:
- found - store it in the thread local cache and bump the reference count
- not found - create a new lock. First fetch the parent lock using the same process, i.e. looking in the thread local cache, etc. Then create a new lock referencing the parent lock (bumps the reference count for the parent lock), and store it in the lock table and the thread local cache with a reference count of 1.
- return the lock to the application and increment its reference count since the application now also has a reference to the lock.
- repeatable reads: repeatable read isolation is rarely needed in practice, since all backend
reads are guaranteed to be performed with read-committed isolation, which is normally sufficient.
Specifically, read-only operations such as compare and search do not require any additional
locking. If repeatable read isolation is required then lock the entry using
-
-
Nested Class Summary
Nested Classes Modifier and Type Class Description classLockManager.DNLockA lock on an entry or subtree.
-
Field Summary
Fields Modifier and Type Field Description static longDEFAULT_LOCK_TIMEOUTHow long a lock manager created with the default configuration - which is the one the server runs with - waits for each of the locks an operation needs before it gives up on the entry.static TimeUnitDEFAULT_LOCK_TIMEOUT_UNITSThe unit ofDEFAULT_LOCK_TIMEOUT.
-
Constructor Summary
Constructors Constructor Description LockManager()Creates a new lock manager with a lock timeout of 9 seconds and an automatically chosen number of lock table buckets based on the number of processors.LockManager(long lockTimeout, TimeUnit lockTimeoutUnit)Creates a new lock manager with the specified lock timeout and an automatically chosen number of lock table buckets based on the number of processors.
-
Method Summary
All Methods Instance Methods Concrete Methods Modifier and Type Method Description StringtoString()LockManager.DNLocktryReadLockEntry(DN entry)Acquires the read lock for the specified entry.LockManager.DNLocktryWriteLockEntry(DN entry)Acquires the write lock for the specified entry.LockManager.DNLocktryWriteLockSubtree(DN subtree)Acquires the write lock for the specified subtree.
-
-
-
Field Detail
-
DEFAULT_LOCK_TIMEOUT
public static final long DEFAULT_LOCK_TIMEOUT
How long a lock manager created with the default configuration - which is the one the server runs with - waits for each of the locks an operation needs before it gives up on the entry. An operation is made to wait for it more than once: the subtree lock and the entry lock are taken one after the other, each with this timeout of its own.- See Also:
DEFAULT_LOCK_TIMEOUT_UNITS, Constant Field Values
-
DEFAULT_LOCK_TIMEOUT_UNITS
public static final TimeUnit DEFAULT_LOCK_TIMEOUT_UNITS
The unit ofDEFAULT_LOCK_TIMEOUT.
-
-
Constructor Detail
-
LockManager
public LockManager()
Creates a new lock manager with a lock timeout of 9 seconds and an automatically chosen number of lock table buckets based on the number of processors.
-
LockManager
public LockManager(long lockTimeout, TimeUnit lockTimeoutUnit)Creates a new lock manager with the specified lock timeout and an automatically chosen number of lock table buckets based on the number of processors.- Parameters:
lockTimeout- The lock timeout.lockTimeoutUnit- The lock timeout units.
-
-
Method Detail
-
tryReadLockEntry
public LockManager.DNLock tryReadLockEntry(DN entry)
Acquires the read lock for the specified entry. This method will block if the entry is already write locked or if the entry, or any of its parents, have the subtree write lock taken.- Parameters:
entry- The entry whose read lock is required.- Returns:
- The lock, or
nullif the lock attempt timed out.
-
tryWriteLockEntry
public LockManager.DNLock tryWriteLockEntry(DN entry)
Acquires the write lock for the specified entry. This method will block if the entry is already read or write locked or if the entry, or any of its parents, have the subtree write lock taken.- Parameters:
entry- The entry whose write lock is required.- Returns:
- The lock, or
nullif the lock attempt timed out.
-
tryWriteLockSubtree
public LockManager.DNLock tryWriteLockSubtree(DN subtree)
Acquires the write lock for the specified subtree. This method will block if any entry or subtree within the subtree is already read or write locked or if any of the parent entries of the subtree have the subtree write lock taken.- Parameters:
subtree- The subtree whose write lock is required.- Returns:
- The lock, or
nullif the lock attempt timed out.
-
-