blob: 630ad49b49742dc2296b2d19b277328ed6f4a871 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more contributor license
* agreements. See the NOTICE file distributed with this work for additional information regarding
* copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License. You may obtain a
* copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package org.apache.geode.cache;
import java.util.concurrent.TimeUnit;
/**
* <p>
* The CacheTransactionManager interface allows applications to manage transactions on a per
* {@link Cache} basis.
*
* <p>
* The life cycle of a GemFire transaction starts with a begin operation. The life cycle ends with
* either a commit or rollback operation. Between the begin and the commit/rollback are typically
* {@link Region} operations. In general, those that either create, destroy, invalidate or update
* {@link Region.Entry} are considered transactional, that is they modify transactional state.
*
* <p>
* A GemFire transaction may involve operations on multiple regions, each of which may have
* different attributes.
*
* <p>
* While a GemFire transaction and its operations are invoked in the local VM, the resulting
* transaction state is distributed to other VM's at commit time as per the attributes of each
* participant Region.
*
* <p>
* A transaction can have no more than one thread associated with it and conversely a thread can
* only operate on one transaction at any given time. Child threads will not inherit the existing
* transaction.
*
* <p>
* Each of the following methods operate on the current thread. All methods throw
* {@link CacheClosedException} if the Cache is closed.
*
* <p>
* GemFire Transactions currently only support Read Committed isolation. In addition, they are
* optimistic transactions in that write locking and conflict checks are performed as part of the
* commit operation.
*
* <p>
* For guaranteed Read Committed isolation, avoid making "in place" changes, because such changes
* will be "seen" by other transactions and break the Read Committed isolation guarantee. e.g.
*
* <pre>
* CacheTransactionManager txMgr = cache.getCacheTransactionManager();
* txMgr.begin();
* StringBuffer s = (StringBuffer) r.get("stringBuf");
* s.append("Changes seen before commit. NOT Read Committed!");
* r.put("stringBuf", s);
* txMgr.commit();
* </pre>
*
* <p>
* To aid in creating copies, the "copy on read" <code>Cache</code> attribute and the
* {@link org.apache.geode.CopyHelper#copy} method are provided. The following is a Read Committed
* safe example using the <code>CopyHelper.copy</code> method.
*
* <pre>
* CacheTransactionManager txMgr = cache.getCacheTransactionManager();
* txMgr.begin();
* Object o = r.get("stringBuf");
* StringBuffer s = (StringBuffer) CopyHelper.copy(o);
* s.append("Changes unseen before commit. Read Committed.");
* r.put("stringBuf", s);
* txMgr.commit();
* </pre>
*
* <p>
* Its important to note that creating copies can negatively impact both performance and memory
* consumption.
*
* <p>
* Partitioned Regions, Distributed No Ack and Distributed Ack Regions are supported (see
* {@link AttributesFactory} for Scope). For both scopes, a consistent configuration (per VM) is
* enforced.
*
* <p>
* Global Regions, client Regions (see org.apache.geode.cache.client package) and persistent Regions
* (see {@link DiskWriteAttributes}) do not support transactions.
*
* <p>
* When PartitionedRegions are involved in a transaction, all data in the transaction must be
* colocated together on one data node. See the GemFire Developer Guide for details on using
* transactions with Partitioned Regions.
*
*
* @since GemFire 4.0
*
* @see Cache
*
*/
public interface CacheTransactionManager {
/**
* Creates a new transaction and associates it with the current thread.
*
* @throws IllegalStateException if the thread is already associated with a transaction
*
* @since GemFire 4.0
*/
void begin();
/**
* Commit the transaction associated with the current thread. If the commit operation fails due to
* a conflict it will destroy the transaction state and throw a {@link CommitConflictException}.
* If the commit operation succeeds, it returns after the transaction state has been merged with
* committed state. When this method completes, the thread is no longer associated with a
* transaction.
*
* @throws IllegalStateException if the thread is not associated with a transaction
*
* @throws CommitConflictException if the commit operation fails due to a write conflict.
*
* @throws TransactionDataNodeHasDepartedException if the node hosting the transaction data has
* departed. This is only relevant for transaction that involve PartitionedRegions.
*
* @throws TransactionDataNotColocatedException if at commit time, the data involved in the
* transaction has moved away from the transaction hosting node. This can only happen if
* rebalancing/recovery happens during a transaction that involves a PartitionedRegion.
*
* @throws TransactionInDoubtException when GemFire cannot tell which nodes have applied the
* transaction and which have not. This only occurs if nodes fail mid-commit, and only
* then in very rare circumstances.
*/
void commit() throws CommitConflictException;
/**
* Roll back the transaction associated with the current thread. When this method completes, the
* thread is no longer associated with a transaction and the transaction context is destroyed.
*
* @since GemFire 4.0
*
* @throws IllegalStateException if the thread is not associated with a transaction
*/
void rollback();
/**
* Suspends the transaction on the current thread. All subsequent operations performed by this
* thread will be non-transactional. The suspended transaction can be resumed by calling
* {@link #resume(TransactionId)}
*
* @return the transaction identifier of the suspended transaction or null if the thread was not
* associated with a transaction
* @since GemFire 6.6.2
*/
TransactionId suspend();
/**
* On the current thread, resumes a transaction that was previously suspended using
* {@link #suspend()}
*
* @param transactionId the transaction to resume
* @throws IllegalStateException if the thread is associated with a transaction or if
* {@link #isSuspended(TransactionId)} would return false for the given transactionId
* @since GemFire 6.6.2
* @see #tryResume(TransactionId)
*/
void resume(TransactionId transactionId);
/**
* This method can be used to determine if a transaction with the given transaction identifier is
* currently suspended locally. This method does not check other members for transaction status.
*
* @return true if the transaction is in suspended state, false otherwise
* @since GemFire 6.6.2
* @see #exists(TransactionId)
*/
boolean isSuspended(TransactionId transactionId);
/**
* On the current thread, resumes a transaction that was previously suspended using
* {@link #suspend()}.
*
* This method is equivalent to
*
* <pre>
* if (isSuspended(txId)) {
* resume(txId);
* }
* </pre>
*
* except that this action is performed atomically
*
* @param transactionId the transaction to resume
* @return true if the transaction was resumed, false otherwise
* @since GemFire 6.6.2
*/
boolean tryResume(TransactionId transactionId);
/**
* On the current thread, resumes a transaction that was previously suspended using
* {@link #suspend()}, or waits for the specified timeout interval if the transaction has not been
* suspended. This method will return if:
* <ul>
* <li>Another thread suspends the transaction</li>
* <li>Another thread calls commit/rollback on the transaction</li>
* <li>This thread has waited for the specified timeout</li>
* </ul>
*
* This method returns immediately if {@link #exists(TransactionId)} returns false.
*
* @param transactionId the transaction to resume
* @param time the maximum time to wait
* @param unit the time unit of the <code>time</code> argument
* @return true if the transaction was resumed, false otherwise
* @since GemFire 6.6.2
* @see #tryResume(TransactionId)
*/
boolean tryResume(TransactionId transactionId, long time, TimeUnit unit);
/**
* Reports the existence of a transaction for the given transactionId. This method can be used to
* determine if a transaction with the given transaction identifier is currently in progress
* locally.
*
* @param transactionId the given transaction identifier
* @return true if the transaction is in progress, false otherwise.
* @since GemFire 6.6.2
* @see #isSuspended(TransactionId)
*/
boolean exists(TransactionId transactionId);
/**
* Reports the existence of a Transaction for this thread
*
* @return true if a transaction exists, false otherwise
*
* @since GemFire 4.0
*/
boolean exists();
/**
* Returns the transaction identifier for the current thread
*
* @return the transaction identifier or null if no transaction exists
*
* @since GemFire 4.0
*/
TransactionId getTransactionId();
/**
* Gets the transaction listener for this Cache.
*
* @return The TransactionListener instance or null if no listener.
* @throws IllegalStateException if more than one listener exists on this cache
* @deprecated as of GemFire 5.0, use {@link #getListeners} instead
*/
@Deprecated
TransactionListener getListener();
/**
* Returns an array of all the transaction listeners on this cache. Modifications to the returned
* array will not effect what listeners are on this cache.
*
* @return the cache's <code>TransactionListener</code>s; an empty array if no listeners
* @since GemFire 5.0
*/
TransactionListener[] getListeners();
/**
* Sets the transaction listener for this Cache.
*
* @param newListener the TransactionListener to register with the Cache. Use a <code>null</code>
* to deregister the current listener without registering a new one.
* @return the previous TransactionListener
* @throws IllegalStateException if more than one listener exists on this cache
* @deprecated as of GemFire 5.0, use {@link #addListener} or {@link #initListeners} instead.
*/
@Deprecated
TransactionListener setListener(TransactionListener newListener);
/**
* Adds a transaction listener to the end of the list of transaction listeners on this cache.
*
* @param aListener the user defined transaction listener to add to the cache.
* @throws IllegalArgumentException if <code>aListener</code> is null
* @since GemFire 5.0
*/
void addListener(TransactionListener aListener);
/**
* Removes a transaction listener from the list of transaction listeners on this cache. Does
* nothing if the specified listener has not been added. If the specified listener has been added
* then {@link CacheCallback#close} will be called on it; otherwise does nothing.
*
* @param aListener the transaction listener to remove from the cache.
* @throws IllegalArgumentException if <code>aListener</code> is null
* @since GemFire 5.0
*/
void removeListener(TransactionListener aListener);
/**
* Removes all transaction listeners, calling {@link CacheCallback#close} on each of them, and
* then adds each listener in the specified array.
*
* @param newListeners a possibly null or empty array of listeners to add to this cache.
* @throws IllegalArgumentException if the <code>newListeners</code> array has a null element
* @since GemFire 5.0
*/
void initListeners(TransactionListener[] newListeners);
/**
* Set the TransactionWriter for the cache
*
* @see TransactionWriter
* @since GemFire 6.5
*/
void setWriter(TransactionWriter writer);
/**
* Returns the current {@link TransactionWriter}
*
* @see CacheTransactionManager#setWriter(TransactionWriter)
* @return the current {@link TransactionWriter}
* @since GemFire 6.5
*/
TransactionWriter getWriter();
/**
* Sets whether transactions should be executed in distributed or non-distributed mode. Once set
* this mode should not be changed during the course of transactions.
*
* @throws IllegalStateException if a transaction is already in progress and this method sets the
* distributed mode to a different value.
* @since Geode 1.0
*/
void setDistributed(boolean distributed);
/**
* Returns the execution mode of transactions
*
* @return true if distributed, false otherwise.
* @since Geode 1.0
*/
boolean isDistributed();
}