This is a technical description of Confluence's Bandana caching mechanism. It is primarily designed for Confluence developers, but published here because it might prove useful to some plugin developers.
For an overview of all of Confluence's persistence mechanisms, see Persistence in Confluence.
Confluence's Bandana subsystem is used for persisting configuration settings for Confluence and its plugins. Any persistence mechanism requires careful thought with regard to updates. Transactions are the main mechanism for controlled updates to shared data, and it's important that transactions are treated consistently across all the subsystems involved.
Confluence 2.3 has moved Bandana data to the database in order for it to be shared among clustered nodes. Using Hibernate meant that the updates done to the database were immediately transactional, but the Bandana caching layer still needed to be updated to be transaction-aware.
This document describes the caching system used by Bandana in Confluence 2.3 which allows it to deal correctly with transactional updates. The caching system may be used more extensively for other areas in Confluence going forward.
The caching layer for Bandana is necessary because all the data is persisted as XML. When configuration objects are retrieved from the data store, they are deserialized back into Java objects via XStream. This deserialization occurs after the XML has been retrieved by Hibernate, and is a time-consuming process. Because Bandana objects are used so frequently (at least one per request), a cache of configuration objects, independent of the Hibernate cache of XML, is required.
The interaction between the key components in the Bandana caching system is shown in the flowchart below.
Bandana caching flowchart
As you can see from the diagram, the CachingBandanaPersister is solely responsible for reading and updating the cache, only delegating queries to the HibernateBandanaPersister when the required data is not already in the case.
Problems to overcome
Having a cache separate to your transactional data store (Hibernate) presents a few tricky problems:
- A cache update is visible to other clients immediately; a database update is only visible to other clients once the transaction commits.
- A cache update can never be rolled back; if the associated database update gets rolled back, the cache is now inconsistent with the data.
- Two concurrent transactions which update multiple caches could interleave their changes, so that neither operation is completed in its entirety. This is one type of 'lost update' problem.
- Read-through cache updates (where a cache is empty and to be populated with data read from the database) should not result in an inconsistent cache when updates occur concurrently. This is another type of 'lost update' problem and was a serious bug in Confluence 2.2.
None of these problems is insurmountable, but the solution is fairly complex. The Bandana caching in Confluence 2.3 will have the following features:
- Cache updates (except read-throughs) will be enacted on the Coherence cache only after the related database transaction has been completed successfully.
- Read-through cache updates will be enacted immediately.
- All cache updates will use locking when they are processed to prevent lost updates.
- All cache updates will be visible when reading from the same cache during the same transaction, prior to commit.
These features are provided by a Confluence transactional cache, which is described in detail below.
The transactional cache makes a best attempt at synchronising the data in the cache and the database when a transaction commits. A transactional cache consists of two components:
- Deferred operations cache, which keeps track of update operations to an underlying cache but doesn't actually peform them.
- Deferred cache transaction synchronization, which performs the deferred updates on the cache once it gets notified of a successful transaction completion.
These two components collaborate with Spring for transaction management, and the locking and caching subsystems in Confluence.