Instance cache: flush, age-out, store-not-flushed
Note: this article is about a global instance cache, i.e. a cache that is required for commit options A and D (also can be used with B and C), a cache that can be accessed from concurrent transactions. It is not about a per-transaction cache.
The goal of caching is to improve performance by avoiding loading data from the database. The risk of using a cache is in potential inconsistency between the cached data and the data in the database. (Another problem with the global cache is pessimistic locking.) To prevent the inconsistency, the data in the cache should be re-loaded or at least removed from the cache at the "right" time.
There are a few ways to remove the data from the cache: configuring a time- or size-based eviction policy, using commit option D with refresh-period, sending an invalidating message to the cache and finally invoking flush() method on the cache. This article talks about issues related to the eviction of entity instances from the global cache.
Most frequently used way to evict instances from the cache is configuring a time- or a size-based eviction policy. In the standard container configuration we use max-bean-age to specify the amount of time that should pass since the last access to an instance after which the instance should be removed from the cache and max-capacity to specify the size of the cache which when reached should trigger eviction of the least recently used instances from the cache.
In case of max-bean-age, there is a thread that periodically checks for how long instances have been staying in the cache. What happens if an instance stayed in the cache longer than max-bean-age and at the moment of checking is accessed by a transaction? The instance won't be removed from the cache: neither at the moment of its age checking nor immediately after the transaction commits. It will stay cached at least until the next age check.
What happens if the size of the cache has reached the max-capacity, all instances in the cache are in use and we need to access one more instance? Instances in use cannot be removed. In this case, the capacity will be temporary increased beyond the max-capacity value and you'll see a warning in the log e.g.:
15:46:23,137 WARN [LRUEnterpriseContextCachePolicy] Cache has reached maximum capacity for container jboss.j2ee:jndiName=ALocal,service=EJB - probably because all instances are in use. Temporarily increasing the size to 4
Commit option D
In fact, commit option D will periodically invoke flush() on the cache. So, see the description of the cache.flush() below.
Whenever an invlidation message is received the instance[S|s] will be removed and passivated immediately or, if the instance[S|s] is in use, it will be removed and passivated at the end of the transaction it is used in despite the commit option of the container.
The flush method will try to empty the cache by removing and passivating all the instances immediately. If an instance is in use and cannot be passivated (and, hence, removed) immediately then the passivation and removal will be performed immediately after the transaction that used the instance commits disregarding the container's commit option.
Since 3.2.8SP2, 4.0.4.GA
Imagine a scenario when an application uses entity beans with commit option A and a client modifies the data in the database directly with an SQL client or JDBC. Now, to avoid inconsistency between entity bean instances in the cache and the data in the database we need to flush the entity cache.
Suppose, one instance is being used in a transaction at the moment we invoke cache.flush(). The instance will be removed and passivated after the transaction commits. But what if the transaction modified the instance which still contains the old data? If the transaction commits then the data in the database might become incosistent. If that is the case then we must not commit and rollback the transaction.
This is what store-not-flushed is for. If its value is true then the transaction will commit the changes. If the value is false and the instance was in fact modified then the transaction will be rolledback and the client will receive an exception.
The default value for store-not-flushed is true since this reflects the behaviour of previous JBoss releases.