package org.infinispan.configuration.cache;
import java.util.concurrent.TimeUnit;
import org.infinispan.commons.configuration.attributes.Attribute;
import org.infinispan.commons.configuration.attributes.AttributeDefinition;
import org.infinispan.commons.configuration.attributes.AttributeSet;
import org.infinispan.commons.configuration.attributes.IdentityAttributeCopier;
import org.infinispan.transaction.LockingMode;
import org.infinispan.transaction.TransactionMode;
import org.infinispan.transaction.TransactionProtocol;
import org.infinispan.transaction.lookup.GenericTransactionManagerLookup;
import org.infinispan.transaction.lookup.TransactionManagerLookup;
import org.infinispan.transaction.lookup.TransactionSynchronizationRegistryLookup;
/**
* Defines transactional (JTA) characteristics of the cache.
*
* @author pmuir
* @author Pedro Ruivo
*
*/
public class TransactionConfiguration {
public static final AttributeDefinition<Boolean> AUTO_COMMIT = AttributeDefinition.builder("auto-commit", true).immutable().build();
public static final AttributeDefinition<Long> CACHE_STOP_TIMEOUT = AttributeDefinition.builder("stop-timeout", TimeUnit.SECONDS.toMillis(30)).build();
public static final AttributeDefinition<LockingMode> LOCKING_MODE = AttributeDefinition.builder("locking", LockingMode.OPTIMISTIC).build();
public static final AttributeDefinition<TransactionManagerLookup> TRANSACTION_MANAGER_LOOKUP = AttributeDefinition.<TransactionManagerLookup>builder("transaction-manager-lookup", GenericTransactionManagerLookup.INSTANCE).copier(IdentityAttributeCopier.INSTANCE).autoPersist(false).build();
public static final AttributeDefinition<TransactionSynchronizationRegistryLookup> TRANSACTION_SYNCHRONIZATION_REGISTRY_LOOKUP = AttributeDefinition.builder("transaction-synchronization-registry-lookup", null, TransactionSynchronizationRegistryLookup.class).copier(IdentityAttributeCopier.INSTANCE).autoPersist(false).build();
public static final AttributeDefinition<TransactionMode> TRANSACTION_MODE = AttributeDefinition.builder("mode", TransactionMode.NON_TRANSACTIONAL).immutable().autoPersist(false).build();
public static final AttributeDefinition<Boolean> USE_SYNCHRONIZATION = AttributeDefinition.builder("synchronization", false).immutable().autoPersist(false).build();
public static final AttributeDefinition<Boolean> USE_1_PC_FOR_AUTO_COMMIT_TRANSACTIONS = AttributeDefinition.builder("single-phase-auto-commit", false).build();
public static final AttributeDefinition<Long> REAPER_WAKE_UP_INTERVAL = AttributeDefinition.builder("reaper-wake-up-interval", 30000L).immutable().xmlName("reaper-interval").build();
public static final AttributeDefinition<Long> COMPLETED_TX_TIMEOUT = AttributeDefinition.builder("complete-timeout", 60000L).immutable().build();
public static final AttributeDefinition<TransactionProtocol> TRANSACTION_PROTOCOL = AttributeDefinition.builder("transaction-protocol", TransactionProtocol.DEFAULT).immutable().xmlName("protocol").build();
public static final AttributeDefinition<Boolean> NOTIFICATIONS = AttributeDefinition.builder("notifications", true).immutable().build();
static AttributeSet attributeDefinitionSet() {
return new AttributeSet(TransactionConfiguration.class, AUTO_COMMIT, CACHE_STOP_TIMEOUT, LOCKING_MODE,
TRANSACTION_MANAGER_LOOKUP, TRANSACTION_SYNCHRONIZATION_REGISTRY_LOOKUP, TRANSACTION_MODE, USE_SYNCHRONIZATION, USE_1_PC_FOR_AUTO_COMMIT_TRANSACTIONS,
REAPER_WAKE_UP_INTERVAL, COMPLETED_TX_TIMEOUT, TRANSACTION_PROTOCOL, NOTIFICATIONS);
}
private final Attribute<Boolean> autoCommit;
private final Attribute<Long> cacheStopTimeout;
private final Attribute<LockingMode> lockingMode;
private final Attribute<TransactionManagerLookup> transactionManagerLookup;
private final Attribute<TransactionSynchronizationRegistryLookup> transactionSynchronizationRegistryLookup;
private final Attribute<TransactionMode> transactionMode;
private final Attribute<Boolean> useSynchronization;
private final Attribute<Boolean> use1PcForAutoCommitTransactions;
private final Attribute<Long> reaperWakeUpInterval;
private final Attribute<Long> completedTxTimeout;
private final Attribute<TransactionProtocol> transactionProtocol;
private final Attribute<Boolean> notifications;
private final AttributeSet attributes;
private final RecoveryConfiguration recovery;
TransactionConfiguration(AttributeSet attributes, RecoveryConfiguration recovery) {
this.attributes = attributes.checkProtection();
autoCommit = attributes.attribute(AUTO_COMMIT);
cacheStopTimeout = attributes.attribute(CACHE_STOP_TIMEOUT);
lockingMode = attributes.attribute(LOCKING_MODE);
transactionManagerLookup = attributes.attribute(TRANSACTION_MANAGER_LOOKUP);
transactionSynchronizationRegistryLookup = attributes.attribute(TRANSACTION_SYNCHRONIZATION_REGISTRY_LOOKUP);
transactionMode = attributes.attribute(TRANSACTION_MODE);
useSynchronization = attributes.attribute(USE_SYNCHRONIZATION);
use1PcForAutoCommitTransactions = attributes.attribute(USE_1_PC_FOR_AUTO_COMMIT_TRANSACTIONS);
reaperWakeUpInterval = attributes.attribute(REAPER_WAKE_UP_INTERVAL);
completedTxTimeout = attributes.attribute(COMPLETED_TX_TIMEOUT);
transactionProtocol = attributes.attribute(TRANSACTION_PROTOCOL);
notifications = attributes.attribute(NOTIFICATIONS);
this.recovery = recovery;
}
/**
* If the cache is transactional (i.e. {@link #transactionMode()} == TransactionMode.TRANSACTIONAL)
* and transactionAutoCommit is enabled then for single operation transactions
* the user doesn't need to manually start a transaction, but a transactions
* is injected by the system. Defaults to true.
*/
public boolean autoCommit() {
return autoCommit.get();
}
/**
* If there are any ongoing transactions when a cache is stopped, Infinispan waits for ongoing
* remote and local transactions to finish. The amount of time to wait for is defined by the
* cache stop timeout. It is recommended that this value does not exceed the transaction timeout
* because even if a new transaction was started just before the cache was stopped, this could
* only last as long as the transaction timeout allows it.
*/
public TransactionConfiguration cacheStopTimeout(long l) {
cacheStopTimeout.set(l);
return this;
}
/**
* If there are any ongoing transactions when a cache is stopped, Infinispan waits for ongoing
* remote and local transactions to finish. The amount of time to wait for is defined by the
* cache stop timeout. It is recommended that this value does not exceed the transaction timeout
* because even if a new transaction was started just before the cache was stopped, this could
* only last as long as the transaction timeout allows it.
*/
public long cacheStopTimeout() {
return cacheStopTimeout.get();
}
/**
* Configures whether the cache uses optimistic or pessimistic locking.
* If the cache is not transactional then the locking mode is ignored.
*
* @see TransactionConfiguration#transactionMode()
*/
public LockingMode lockingMode() {
return lockingMode.get();
}
/**
* Configures whether the cache uses optimistic or pessimistic locking.
* If the cache is not transactional then the locking mode is ignored.
*
* @see TransactionConfiguration#transactionMode()
*/
public TransactionConfiguration lockingMode(LockingMode lockingMode) {
this.lockingMode.set(lockingMode);
return this;
}
/**
* If true, the cluster-wide commit phase in two-phase commit (2PC) transactions will be
* synchronous, so Infinispan will wait for responses from all nodes to which the commit was
* sent. Otherwise, the commit phase will be asynchronous. Keeping it as false improves
* performance of 2PC transactions, but it can lead to inconsistencies when the primary owner releases
* the lock before the backup commits the change.
*
* @deprecated since 9.0. no longer supported
*/
@Deprecated
public boolean syncCommitPhase() {
return true;
}
/**
* If true, the cluster-wide commit phase in two-phase commit (2PC) transactions will be
* synchronous, so Infinispan will wait for responses from all nodes to which the commit was
* sent. Otherwise, the commit phase will be asynchronous. Keeping it as false improves
* performance of 2PC transactions, but it can lead to inconsistencies when the primary owner releases
* the lock before the backup commits the change.
*
* @deprecated The syncRollbackPhase setting can no longer be modified at runtime. It must be the same on all nodes.
*/
@Deprecated
public TransactionConfiguration syncCommitPhase(boolean b) {
return this;
}
/**
* If true, the cluster-wide rollback phase in two-phase commit (2PC) transactions will be
* synchronous, so Infinispan will wait for responses from all nodes to which the rollback was
* sent. Otherwise, the rollback phase will be asynchronous. Keeping it as false improves
* performance of 2PC transactions.
*
* @deprecated since 9.0. no longer supported
*/
@Deprecated
public boolean syncRollbackPhase() {
return true;
}
/**
* If true, the cluster-wide rollback phase in two-phase commit (2PC) transactions will be
* synchronous, so Infinispan will wait for responses from all nodes to which the rollback was
* sent.
*
* @deprecated The syncRollbackPhase setting can no longer be modified at runtime. It must be the same on all nodes.
*/
@Deprecated
public TransactionConfiguration syncRollbackPhase(boolean b) {
return this;
}
/**
* Configure Transaction manager lookup directly using an instance of TransactionManagerLookup.
* Calling this method marks the cache as transactional.
*/
public TransactionManagerLookup transactionManagerLookup() {
return transactionManagerLookup.get();
}
/**
* @deprecated since 9.0. The transaction manager is only looked up once, during cache startup.
*/
@Deprecated
public TransactionConfiguration transactionManagerLookup(TransactionManagerLookup transactionManagerLookup) {
this.transactionManagerLookup.set(transactionManagerLookup);
return this;
}
/**
* Configure Transaction Synchronization Registry lookup directly using an instance of
* TransactionManagerLookup. Calling this method marks the cache as transactional.
*/
public TransactionSynchronizationRegistryLookup transactionSynchronizationRegistryLookup() {
return transactionSynchronizationRegistryLookup.get();
}
public TransactionMode transactionMode() {
return transactionMode.get();
}
public boolean useSynchronization() {
return useSynchronization.get();
}
/**
* This method allows configuration of the transaction recovery cache. When this method is
* called, it automatically enables recovery. So, if you want it to be disabled, make sure you
* call {@link RecoveryConfigurationBuilder#enabled(boolean)} with false as parameter
*/
public RecoveryConfiguration recovery() {
return recovery;
}
/**
* @see TransactionConfigurationBuilder#reaperWakeUpInterval(long)
*/
public long reaperWakeUpInterval() {
return reaperWakeUpInterval.get();
}
/**
* @see TransactionConfigurationBuilder#completedTxTimeout(long)
*/
public long completedTxTimeout() {
return completedTxTimeout.get();
}
/**
* Before Infinispan 5.1 you could access the cache both transactionally and
* non-transactionally. Naturally the non-transactional access is faster and
* offers less consistency guarantees. From Infinispan 5.1 onwards, mixed
* access is no longer supported, so if you wanna speed up transactional
* caches and you're ready to trade some consistency guarantees, you can
* enable use1PcForAutoCommitTransactions. <p/>
*
* What this configuration option does is force an induced transaction,
* that has been started by Infinispan as a result of enabling autoCommit,
* to commit in a single phase. So only 1 RPC instead of 2RPCs as in the
* case of a full 2 Phase Commit (2PC).
* <p/>
* <b>N.B.</b> this option should NOT be used when modifying the
* same key from multiple transactions as 1PC does not offer any consistency
* guarantees under concurrent access.
*/
public boolean use1PcForAutoCommitTransactions() {
return use1PcForAutoCommitTransactions.get();
}
/**
* @return the transaction protocol in use (2PC or Total Order)
*/
public TransactionProtocol transactionProtocol() {
return transactionProtocol.get();
}
/**
* @return are transactional notifications (
* {@link org.infinispan.notifications.cachelistener.annotation.TransactionRegistered} and
* {@link org.infinispan.notifications.cachelistener.annotation.TransactionCompleted}) triggered?
*/
public boolean notifications() {
return notifications.get();
}
public AttributeSet attributes() {
return attributes;
}
@Override
public String toString() {
return "TransactionConfiguration [attributes=" + attributes + ", recovery=" + recovery + "]";
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
TransactionConfiguration other = (TransactionConfiguration) obj;
if (attributes == null) {
if (other.attributes != null)
return false;
} else if (!attributes.equals(other.attributes))
return false;
if (recovery == null) {
if (other.recovery != null)
return false;
} else if (!recovery.equals(other.recovery))
return false;
return true;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((attributes == null) ? 0 : attributes.hashCode());
result = prime * result + ((recovery == null) ? 0 : recovery.hashCode());
return result;
}
}