blob: 41d0162b75829e3b7cb56d024224838c03ca2436 [file] [log] [blame]
/*=========================================================================
* Copyright (c) 2002-2014 Pivotal Software, Inc. All Rights Reserved.
* This product is protected by U.S. and international copyright
* and intellectual property laws. Pivotal products are covered by
* more patents listed at http://www.pivotal.io/patents.
*========================================================================
*/
package com.gemstone.gemfire.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
* com.gemstone.gemfire.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 {@link com.gemstone.gemfire.cache.client})
* 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.
*
* @author Mitch Thomas
*
* @since 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 4.0
*/
public 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.
*/
public 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 4.0
*
* @throws IllegalStateException if the thread is not associated with a transaction
*/
public 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 6.6.2
*/
public 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 6.6.2
* @see #tryResume(TransactionId)
*/
public 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.
*
* @param transactionId
* @return true if the transaction is in suspended state, false otherwise
* @since 6.6.2
* @see #exists(TransactionId)
*/
public 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 6.6.2
*/
public 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 6.6.2
* @see #tryResume(TransactionId)
*/
public 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 6.6.2
* @see #isSuspended(TransactionId)
*/
public boolean exists(TransactionId transactionId);
/** Reports the existence of a Transaction for this thread
*
* @return true if a transaction exists, false otherwise
*
* @since 4.0
*/
public boolean exists();
/** Returns the transaction identifier for the current thread
*
* @return the transaction identifier or null if no transaction exists
*
* @since 4.0
*/
public 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
public 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 5.0
*/
public 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
public 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 5.0
*/
public 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 5.0
*/
public 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 5.0
*/
public void initListeners(TransactionListener[] newListeners);
/**
* Set the TransactionWriter for the cache
* @param writer
* @see TransactionWriter
* @since 6.5
*/
public void setWriter(TransactionWriter writer);
/**
* Returns the current {@link TransactionWriter}
* @see CacheTransactionManager#setWriter(TransactionWriter)
* @return the current {@link TransactionWriter}
* @since 6.5
*/
public 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 9.0
*/
public void setDistributed(boolean distributed);
/**
* Returns the execution mode of transactions
* @return true if distributed,
* false otherwise.
* @since 9.0
*/
public boolean isDistributed();
}