Design Considerations
Designs that incorporate more complex features introduce further considerations. This section discusses how transactions interact with other Geode features.
- Colocate Partitioned Regions
- Region Operations Return References
- First Operation with Mixed Region Types
- Allowing Transactions to Work on Persistent Regions
- Mixing Transactions with Queries and Indexes
- Mixing Transactions with Eviction
- Mixing Transactions with Expiration
- Mixing Transactions with Non-transactional Operations
- Changing the Handling of Dirty Reads
Colocate Partitioned Regions
For performance, transactions that operate on more than one partitioned region require that those partitioned regions colocate their entries. Colocate Data from Different Partitioned Regions describes how to colocate entries.
Region Operations Return References
For performance, server-invoked region operations return references to region entries. Any assignment to that reference changes the entry within the region. This subverts the system’s ability to maintain consistency and the callback chain for handlers such as cache writers and cache loaders.
Changing an entry using a reference from within a transaction executing on a server has the same consistency issues, but is even worse, as the change will not be seen as part of the transactional state.
There are two ways to work with a reference: make a copy, or configure the system to return copies instead of references. There is a performance penalty to having the system return copies. Both ways are detailed in Copy on Read Behavior.
First Operation with Mixed Region Types
When more than one region participates in a transaction,
and there is at least one partitioned and at least one
replicated region,
the code must do its first operation on the partitioned
region to avoid a TransactionDataNotColocatedException
.
Write the transaction to do its first operation on a partitioned
region, even if the operation will be spurious.
Allowing Transactions to Work on Persistent Regions
Geode’s implementation of atomic transactions prohibits
regions with persistence from participating in transactions.
The invocation of a persistent region operation within a transaction
throws an UnsupportedOperationException
with an associated message of
Operations on persist-backup regions are not allowed because this thread
has an active transaction
An application that wishes to allow operations on a persistent region during a transaction can set this system property:
-Dgemfire.ALLOW_PERSISTENT_TRANSACTIONS=true
Setting this system property eliminates the exception. It does not change the fact that atomicity is not enforced for disk writes that occur with the commit of a transaction. A server crash during the commit may succeed in some, but not all of the disk writes.
Mixing Transactions with Queries and Indexes
Queries and query results reflect region state, and not any state or changes that occur within a transaction. Likewise, the contents and updates to an index do not intersect with any changes made within a transaction. Therefore, do not mix transactions with queries or indexed regions.
Mixing Transactions with Eviction
LRU eviction and transactions work well together. Any eviction operation on a region entry that is operated on from within a transaction is deferred until the transaction is committed. Further, because any entry touched by the transaction has had its LRU clock reset, eviction is not likely to choose those entries as victims immediately after the commit.
Mixing Transactions with Expiration
A transaction disables expiration on any region entries affected by the transaction.
Mixing Transactions with Non-transactional Operations
For best performance, non-transactional operations do not acquire the exclusive locks used to check for conflicts in a transaction. A transaction operating on the same data as a non-transactional actor is unable to detect the conflict caused by a non-transactional operation.
If using transactions, an application should adopt the policy of designating certain regions or sets of entries exclusively for transactional puts, updates, and deletions, so transactional entries will not be modified by non-transactional operations.
If other, non-transactional sources update the keys the transaction is modifying, the changes may intermingle with the 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.
Changing the Handling of Dirty Reads
An application requiring a strict, but slower isolation model, such that dirty reads of transitional states are not allowed, should set a property and encapsulate read operations within the transaction. Configure this strict isolation model with the property:
-Dgemfire.detectReadConflicts=true
This property causes read operations to succeed only when they
read a consistent pre- or post-transactional state.
If not consistent,
Geode throws a CommitConflictException
.