How GemFire Cache Transactions Work
This section provides an explanation of how transactions work on GemFire caches.
All the regions in a GemFire member cache can participate in a transaction. A Java application can operate on the cache using multiple transactions. A transaction is associated with only one thread, and a thread can operate on only one transaction at a time. Child threads do not inherit existing transactions.
A transaction is isolated from changes made concurrently to the cache. Each transaction has its own private view of the cache, including the entries it has read and the changes it has made. The first time the transaction touches an entry in the cache, either to read or write, it produces a snapshot of that entry’s state in the transaction’s view. The transaction maintains its current view of the entry, which reflects only the changes made within the transaction. The transaction remembers the entry’s original state and uses it at commit time to discover write conflicts.
When a commit succeeds, the changes recorded in the transaction view are merged into the cache. If the commit fails or the transaction is rolled back, all of its changes are dropped.
When a transaction is committed, the transaction management system uses a two-phase commit protocol:
- Reserves all the entries involved in the transaction from changes by any other transactional thread. For distributed regions, it reserves the entries in the entire distributed system. For partitioned regions, it reserves them on the data store, where the transaction is running.
- Checks the cache for conflicts on affected keys, to make sure all entries are still in the same state they were in when this transaction first accessed them.
- If any conflict is detected, the manager rolls back the transaction.
If no conflict is detected, the manager:
- Calls the
TransactionWriterin the member where the transaction is running. This allows the system to write through transactional updates to an external data source.
- Updates the local cache and distributes the updates to the other members holding the data. Cache listeners are called for these updates, in each cache where the changes are made, the same as for non-transactional operations.
- Calls the
TransactionListeners in the member where the transaction is running.
- Calls the
Releases the transaction reservations on the entries.
The manager updates the local cache and distributes the updates to other members in a non-atomic way.
- If other threads read the keys the transaction is modifying, they may see some in their pre-transaction state and some in their post-transaction state.
- If other, non-transactional sources update the keys the transaction is modifying, the changes may intermingle with this transaction’s changes. The other sources can include distributions from remote members, loading activities, and other direct cache modification calls from the same member. When this happens, after your commit finishes, the cache state may not be what you expected.
If the transaction fails to complete any of the steps, a CommitConflictException is thrown to the calling application.
Once the members involved in the transaction have been asked to commit, the transaction completes even if one of the participating members were to leave the system during the commit. The transaction completes successfully so long as all remaining members are in agreement.
Each member participating in the transaction maintains a membership listener on the transaction coordinator. If the transaction coordinator goes away after issuing the final commit call, the transaction completes in the remaining members.