Chapter 11. Transactions and Concurrency

11.1. Concurrent Access

JBoss Cache is a thread safe caching API, and uses its own efficient mechanisms of controlling concurrent access. It uses an innovative implementation of multi-versioned concurrency control (MVCC) as the default locking scheme. Versions of JBoss Cache prior to 3.x offered Optimistic and Pessimistic Locking schemes, both of which are now deprecated in favor of MVCC.

11.1.1. Multi-Version Concurrency Control (MVCC)

MVCC is a locking scheme commonly used by modern database implementations to control fast, safe concurrent access to shared data.

11.1.1.1. MVCC Concepts

MVCC is designed to provide the following features for concurrent access:
  • Readers that do not block writers
  • Writers that fail fast
and achieves this by using data versioning and copying for concurrent writers. The theory is that readers continue reading shared state, while writers copy the shared state, increment a version id, and write that shared state back after verifying that the version is still valid (i.e., another concurrent writer has not changed this state first).
This allows readers to continue reading while not preventing writers from writing, and repeatable read semantics are maintained by allowing readers to read off the old version of the state.

11.1.1.2. MVCC Implementation

JBoss Cache's implementation of MVCC is based on a few features:
  • Readers do not acquire any locks
  • Only one additional version is maintained for shared state, for a single writer
  • All writes happen sequentially, to provide fail-fast semantics
The extremely high performance of JBoss Cache's MVCC implementation for reading threads is achieved by not requiring any synchronization or locking for readers. For each reader thread, the MVCCLockingInterceptor wraps state in a lightweight container object, which is placed in the thread's InvocationContext (or TransactionContext if running in a transaction). All subsequent operations on the state happens via the container object. This use of Java references allows for repeatable read semantics even if the actual state changes simultaneously.
Writer threads, on the other hand, need to acquire a lock before any writing can commence. Currently, we use lock striping to improve the memory performance of the cache, and the size of the shared lock pool can be tuned using the concurrencyLevel attribute of the locking element. See the Chapter 12, Configuration References for details. After acquiring an exclusive lock on an Fqn, the writer thread then wraps the state to be modified in a container as well, just like with reader threads, and then copies this state for writing. When copying, a reference to the original version is still maintained in the container (for rollbacks). Changes are then made to the copy and the copy is finally written to the data structure when the write completes.
This way, subsequent readers see the new version while existing readers still hold a reference to the original version in their context.
If a writer is unable to acquire the write lock after some time, a TimeoutException is thrown. This lock acquisition timeout defaults to 10000 milliseconds and can be configured using the lockAcquisitionTimeout attribute of the locking element. See the Chapter 12, Configuration References for details.
11.1.1.2.1. Isolation Levels
JBoss Cache 3.x supports two isolation levels: REPEATABLE_READ and READ_COMMITTED, which correspond in semantic to database-style isolation levels. Previous versions of JBoss Cache supported all 5 database isolation levels, and if an unsupported isolation level is configured, it is either upgraded or downgraded to the closest supported level.
REPEATABLE_READ is the default isolation level, to maintain compatibility with previous versions of JBoss Cache. READ_COMMITTED, while providing a slightly weaker isolation, has a significant performance benefit over REPEATABLE_READ.
11.1.1.2.2. Concurrent Writers and Write-Skews
Although MVCC forces writers to obtain a write lock, a phenomenon known as write skews may occur when using REPEATABLE_READ:
This happens when concurrent transactions performing a read and then a write, based on the value that was read. Since reads involve holding on to the reference to the state in the transaction context, a subsequent write would work off that original state read, which may now be stale.
The default behavior with dealing with a write skew is to throw a DataVersioningException, when it is detected when copying state for writing. However, in most applications, a write skew may not be an issue (for example, if the state written has no relationship to the state originally read) and should be allowed. If your application does not care about write skews, you can allow them to happen by setting the writeSkewCheck configuration attribute to false. See the Chapter 12, Configuration References for details.
Note that write skews cannot happen when using READ_COMMITTED since threads always work off committed state.

11.1.1.3. Configuring Locking

Configuring MVCC involves using the <locking /> configuration tag, as follows:
   <locking
      isolationLevel="REPEATABLE_READ"
      lockAcquisitionTimeout="10234"
      nodeLockingScheme="mvcc"
      writeSkewCheck="false"
      concurrencyLevel="1000" />
  • nodeLockingScheme - the node locking scheme used. Defaults to MVCC if not provided, deprecated schemes such as pessimistic or optimistic may be used but is not encouraged.
  • isolationLevel - transaction isolation level. Defaults to REPEATABLE_READ if not provided.
  • writeSkewCheck - defaults to true if not provided.
  • concurrencyLevel - defaults to 500 if not provided.
  • lockAcquisitionTimeout - only applies to writers when using MVCC. Defaults to 10000 if not provided.

11.1.2. Pessimistic and Optimistic Locking Schemes

From JBoss Cache 3.x onwards, pessimistic and optimistic locking schemes are deprecated in favor of Section 11.1.1, “Multi-Version Concurrency Control (MVCC)”. It is recommended that existing applications move off these legacy locking schemes as support for them will eventually be dropped altogether in future releases.
Documentation for legacy locking schemes are not included in this user guide, and if necessary, can be referenced in previous versions of this document, which can be found on the JBoss Cache website.