Instance cache and commit options
An EJB container can be configured to use one of the two kinds of instance cache for entity beans: global (cross-transaction) instance cache and per-transaction instance cache.
Global instance cache can be accessed by concurrent transactions at the same time requesting different or the same instances. Therefore, before obtaining an instance from the global instance cache, the transaction must acquire an exclusive lock to that instance. In other words, global instance cache requires pessimistic locking policy.
Per-transaction instance cache is local to transaction cache, i.e. it is created when the transaction is started and is discarded when the transaction is committed or rolled back. Since, only one transaction (the owner) can access its own instance cache, transactions can work on their own copy of instances w/o acquisition of exclusive locks to them. I.e. per-transaction instance cache does not require pessimistic locking policy.
Instance cache is a container plug-in. Each instance cache implementation implements org.jboss.ejb.InstanceCache interface. Global instance cache implementation is
org.jboss.ejb.plugins.EntityInstanceCache. Per-transaction instance cache implementation is org.jboss.ejb.plugins.PerTxEntityInstanceCache. (Note: per-transaction instance cache as InstanceCache implementation was introduced in JBoss-3.2.4, earlier versions did support per-transaction instance caching but implemented this differently).
A container can be configured to use specific instance cache implementation with <instance-cache> element. For example,
<container-configuration> <container-name>Standard CMP 2.x EntityBean</container-name> ... <instance-cache>org.jboss.ejb.plugins.InvalidableEntityInstanceCache</instance-cache> ... </container-configuration>
Commit option A
The EJB 2.1 spec says:
+"Option A: The container caches a 'ready' instance between transactions. The container knows
that the bean instance has exclusive access to the state of the object in the persistent storage.
Therefore, the container does not have to synchronize the instance's state from the persistent
storage at the beginning of the next transaction or have to verify that the instance's state is in
sync with the persistent storage at the beginning of the next transaction."+
Since instances remain in the cache after transaction commits, to use this option, the container must be configured with the global instance cache and commit-option A. And the consequence of global instance cache usage, pessimistic locking is the requirement in this case.
Commit option B
The EJB 2.1 spec says:
+"The container caches a 'ready' instance between transactions. In contrast to Option
A, in this option the instance may not have exclusive access to the state of the object in the persistent
storage. Therefore, the container must synchronize the instance's state from the persistent
storage at the beginning of the next transaction if the instance's state in the persistent
storage has changed. Containers using optimistic concurrency control strategies may instead
choose to rollback the transaction if this invariant has not been met: The container must ensure
that in order for a transaction to be successfully committed, the transaction must only operate
on instance data that is in sync with the persistent storage at the beginning of the transaction."+
Like in case of commit option A, instances remain cached after transaction commits, so the container should use global instance cache.
Note, that in case of commit option B, when a transaction first time accesses an instance (invokes CMP/CMR getter or setter or another business method) even if the instance is cached its data will be loaded from the database (re-read the spec's quote). So, the cached data is not really used. Except for one case, i.e. when the instance or some of its CMP fields marked as read-only in jbosscmp-jdbc.xml In this case, read-only fields won't be reloaded from the database.
Commit option C
The EJB 2.1 spec says:
+"The container does not cache a 'ready' instance between transactions. The container
returns the instance to the pool of available instances after a transaction has completed."+
Instances are evicted from the cache when the transaction is finished (committed or rolled back). In this case eaither global or per-transaction instance cache can be used.
Commit option D
This option is not defined in the spec, it is JBoss-specific option. Commit option D as equivalent to commit option A with the exception that instances are cached for a specific period of time specified in optiond-refresh-rate element (the default value is 30 seconds). For example:
<container-configurations> <container-configuration extends="Standard CMP 2.x EntityBean"> <container-name>Option D container</container-name> <commit-option>D</commit-option> <optiond-refresh-rate>60</optiond-refresh-rate> </container-configuration> </container-configurations>
Like in case of commit option A, for commit option D the container must use global instance cache with pessimistic locking.
Read-ahead is a JBossCMP mechanism to pre-load data ahead when a finder (or ejbSelect) method is executed or an instance is loaded from the database.
The data preloaded with read-ahead is put into the read-ahead cache (not the instance cache). When an instance should be loaded, first, the read-ahead cache is checked. If the instance was preloaded, it is loaded from the read-ahead cache and put into the instance cache.
Read-ahead cache is a per-transaction cache. Each transaction has its own read-ahead cache and when the transaction commits or rolls back its read-ahead cache is discarded.
Read-ahead cache works with any instance cache implementation and any locking policy.
Be aware! If you are using pessimistic locking, read-ahead can break the sematics. An instance is locked when a method is invoked on it (CMP/CMR getter or setter or another business method), not when the instance is found with a finder or ejbSelect method.
For example, if transaction T1 found an instance with read-ahead on-find strategy which is pessimistically locked by another transaction T2. Note, that T1 pre-loaded and cached data from the database which is a stale data since T2 might modify it. The T1 transaction won't get access to the instance untill T2 transaction commits. Now, if T2 modified the instance and committed it to the database, T1 finally gets exclusive access to the instance but the instance is loaded from the T1's read-ahead cache (stale data), not from the database. Thus, after T1 commits the database might be in inconsistent state.
Note, a similar scenario is possible (though, not related to read-ahead) with multi-object finder. Suppose, transaction T1 found a set of instances, say with findAll(). Suppose, a subset of these instances were removed by concurrent transactions after T1 executed the findAll(). When T1 iterates over the finder result and attempts to load an already removed instance NoSuchObject[Local|Local]Exception will be thrown.