public class ConsistentKeyLocker extends AbstractLocker<ConsistentKeyLockStatus> implements Locker
Locker that resolves inter-thread lock contention via
AbstractLocker and resolves inter-process contention by reading and
writing lock data using KeyColumnValueStore.
LocalLockMediator class. This mediator uses standard
java.util.concurrent classes to guarantee that at most one thread
holds a lock on any given KeyColumn at any given time. The code that
uses a mediator to resolve inter-thread lock contention is common to multiple
Locker implementations and lives in the abstract base class
AbstractLocker.
However, the mediator has no way to perform inter-process communication. The
mediator can't detect or prevent a thread in another process (potentially on
different machine) acquiring the same lock. This is addressed in the next
section.
KeyColumnValueStore to check
whether it is the only process that holds the lock. These Cassandra
operations go to a dedicated store holding nothing but locking data (a
"store" in this context means a Cassandra column family, an HBase table,
etc.)
KeyColumn.getKey() followed by KeyColumn.getColumn().rid (an opaque identifier which uniquely identifie
this process either globally or at least within the Titan cluster)lockWait to complete
successfully, then retry the write with an updated timestamp and everything
else the same until we either exceed the configured retry count (in which
case we abort the lock attempt) or successfully complete the write in less
than lockWait.lockWait has passed
between the timestamp on our successful write and the current time.lockExpire.rid, then we hold the lock. Otherwise,
another process holds the lock and we have failed to acquire it.AbstractLocker to obtain
and release an intra-process lock before and after the sequence of steps
listed above. The mediator step is necessary for thread-safety, because
rid is only unique at the process level. Without a mediator, distinct
threads could write lock columns with the same rid and be unable to
tell their lock claims apart.| Modifier and Type | Class and Description |
|---|---|
static class |
ConsistentKeyLocker.Builder |
| Modifier and Type | Field and Description |
|---|---|
static StaticBuffer |
LOCK_COL_END |
static StaticBuffer |
LOCK_COL_START |
llm, lockExpire, lockState, rid, serializer, times| Modifier and Type | Method and Description |
|---|---|
protected void |
checkSingleLock(KeyColumn kc,
ConsistentKeyLockStatus ls,
StoreTransaction tx)
Try to verify that the lock identified by
lockID is already held
by tx. |
protected void |
deleteSingleLock(KeyColumn kc,
ConsistentKeyLockStatus ls,
StoreTransaction tx)
Try to unlock/release/delete the lock identified by
lockID and
both held by and verified for tx. |
protected ConsistentKeyLockStatus |
writeSingleLock(KeyColumn lockID,
StoreTransaction txh)
Try to write a lock record remotely up to the configured number of
times.
|
checkLocks, deleteLocks, writeLockclone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, waitcheckLocks, deleteLocks, writeLockpublic static final StaticBuffer LOCK_COL_START
public static final StaticBuffer LOCK_COL_END
protected ConsistentKeyLockStatus writeSingleLock(KeyColumn lockID, StoreTransaction txh) throws Throwable
TemporaryLockingException, then we'll call mutate again to add a
new column with an updated timestamp and to delete the column that tried
to write when the store threw an exception. We continue like that up to
the retry limit. If the store throws anything else, such as an unchecked
exception or a PermanentBackendException, then we'll try to
delete whatever we added and return without further retries.writeSingleLock in class AbstractLocker<ConsistentKeyLockStatus>lockID - lock to acquiretxh - transactionTemporaryLockingException - if the lock retry count is exceeded without successfully
writing the lock in less than the wait limitThrowable - if the storage layer throws anything elseprotected void checkSingleLock(KeyColumn kc, ConsistentKeyLockStatus ls, StoreTransaction tx) throws BackendException, InterruptedException
AbstractLockerlockID is already held
by tx. The lockStatus argument refers to the object
returned by a previous call to
AbstractLocker.writeSingleLock(KeyColumn, StoreTransaction). This should be a
read-only operation: return if the lock is already held, but this method
finds that it is not held, then throw an exception instead of trying to
acquire it.
This method is only useful with nonblocking locking implementations try
to lock and then check the outcome of the attempt in two separate stages.
For implementations that build writeSingleLock(...) on a
synchronous locking primitive, such as a blocking lock() method
or a blocking semaphore p(), this method is redundant with
writeSingleLock(...) and may unconditionally return true.checkSingleLock in class AbstractLocker<ConsistentKeyLockStatus>kc - identifies the lock to checkls - the result of a prior successful writeSingleLock(...)
call on this lockID and txtx - identifies the process claiming this lockBackendExceptionInterruptedExceptionprotected void deleteSingleLock(KeyColumn kc, ConsistentKeyLockStatus ls, StoreTransaction tx)
AbstractLockerlockID and
both held by and verified for tx. This method is only called with
arguments for which AbstractLocker.writeSingleLock(KeyColumn, StoreTransaction)
and AbstractLocker.checkSingleLock(KeyColumn, LockStatus, StoreTransaction)
both returned successfully (i.e. without exceptions).deleteSingleLock in class AbstractLocker<ConsistentKeyLockStatus>kc - identifies the lock to releasels - the result of a prior successful writeSingleLock(...)
followed by a successful checkSingleLock(...)tx - identifies the process that wrote and checked this lockCopyright © 2012–2015. All rights reserved.