XAP

Transactions

The Spring Framework provides a transaction manager abstraction using the PlatformTransactionManager interface with several different built-in implementations, such as JDBC Data Source and JTA. GigaSpaces provides several implementations for Spring's PlatformTransactionManager, allowing you to use the GigaSpaces's local and Jini Distributed Transaction Managers. By implementing Spring's PlatformTransactionManager, the GigaSpaces API allows users to utilize Spring's rich support for declarative transaction management. The declarative transaction support can be easily utilized with the GigaSpace Interface.

When using Spring declarative transaction, a proxy is generated for the classes annotated with @Transactional methods. In this case, only external method calls coming in through the proxy are intercepted. This means that "self-invocation', i.e. a method within the target object calling some other method of the target object, won't lead to an actual transaction at runtime even if the invoked method is marked with @Transactional.

In order to make The GigaSpace Interface transactional, the transaction manager must be provided to it when constructing the GigaSpace bean. The following should be added to your pu.xml to enable the configuration of transactional behavior based on annotations:

<beans ....
       xmlns:tx="http://www.springframework.org/schema/tx"
       http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">

       <tx:annotation-driven transaction-manager="transactionManager"/>
</beans>

Transaction Manager Types

GigaSpaces provides several transaction managers, and changing the implementation you work with is just a matter of changing the the configuration. The different types of transaction managers that are supported by GigaSpaces are described further on in this topic. Each transaction manager implements Spring's PlatformTransactionManager interface and therefore supports the Spring transaction framework (see below).

Constructing the GigaSpaces Transaction Manager

The distributed Jini Transaction Manager starts an embedded distributed (Mahalo) Jini Transaction Manager, which is then wrapped with an implementation of the Spring PlatformTransactionManager. This transaction manager is used to perform distributed transactions spanning multiple Space instances.

The following is an example of how it can be defined in a Spring application context:

<os-core:embedded-space id="space" space-name="mySpace"/>
<os-core:distributed-tx-manager id="transactionManager" />
<os-core:giga-space id="gigaSpace" space="space" tx-manager="transactionManager" />
<bean id="space" class="org.openspaces.core.space.EmbeddedSpaceFactoryBean">
    <property name="name" value="space"  />
</bean>

<bean id="transactionManager" class="org.openspaces.core.transaction.manager.DistributedJiniTransactionManager" />

<bean id="gigaSpace" class="org.openspaces.core.GigaSpaceFactoryBean">
    <property name="space" ref="space" />
    <property name="transactionManager" ref="transactionManager" />
</bean>
EmbeddedSpaceConfigurer configurer = new EmbeddedSpaceConfigurer("mySpace");
PlatformTransactionManager ptm = new DistributedJiniTxManagerConfigurer().transactionManager();
GigaSpace gigaSpace = new GigaSpaceConfigurer(configurer).transactionManager(ptm).gigaSpace();

Timeout Values

The Jini distributed (Mahalo) transaction manager allows setting the default timeout value for transactions. A timeout value is used when a transaction is not committed/rolled back (for example, due to a JVM crash) to control when the transaction will be discarded. By default, the timeout value is 90 and is set in seconds.

For example, to change the default timeout to 2 minutes, use the following configuration:

<os-core:embedded-space id="space" space-name="mySpace"/>
<os-core:distributed-tx-manager id="transactionManager" default-timeout="120"/>
<os-core:giga-space id="gigaSpace" space="space" tx-manager="transactionManager"/>
<bean id="space" class="org.openspaces.core.space.EmbeddedSpaceFactoryBean">
    <property  name="name" value="space" />
</bean>

<bean id="transactionManager" class="org.openspaces.core.transaction.manager.DistributedJiniTransactionManager">
    <property name="defaultTimeout" value="120" />
</bean>

<bean id="gigaSpace" class="org.openspaces.core.GigaSpaceFactoryBean">
    <property name="space" ref="space" />
    <property name="transactionManager" ref="transactionManager" />
</bean>
EmbeddedSpaceConfigurer configurer = new EmbeddedSpaceConfigurer("mySpace");
PlatformTransactionManager ptm = new DistributedJiniTxManagerConfigurer().defaultTimeout(120).transactionManager();
GigaSpace gigaSpace = new GigaSpaceConfigurer(configurer).transactionManager(ptm).gigaSpace();

The default-timeout parameter is specified in seconds. Other parameters, such as the commit and abort timeout, lookup-timeout, and others are specified in milliseconds.

When using Spring declarative transaction management, a transaction timeout can be set on the transaction scope. For more details, see below.

When using Jini-based transactions, a timeout value can be set for both the commit and abort operations. This values can also be set on the transaction manager.

Connecting to the GigaSpaces Transaction Manager

The Jini Transaction Manager Lookup allows you to use the Jini lookup mechanism in order to lookup a Jini Transaction Manager that is present somewhere in the cluster (as opposed to being started locally in your application), which is then wrapped with an implementation of the Spring PlatformTransactionManager. This transaction manager is usually used in order to obtain a remote Jini Mahalo transaction manager for distributed transactions spanning multiple Space instances.

The following is an example of how it can be defined in a Spring application context:

<os-core:embedded-space id="space" space-name="mySpace"/>
<os-core:jini-tx-manager id="transactionManager" lookup-timeout="5000" />
<os-core:giga-space id="gigaSpace" space="space" tx-manager="transactionManager" />
<bean id="space" class="org.openspaces.core.space.EmbeddedSpaceFactoryBean">
    <property name="name" value="space" />
</bean>

<bean id="transactionManager" class="org.openspaces.core.transaction.manager.LookupJiniTransactionManager">
    <property name="lookupTimeout" value="5000" />
</bean>

<bean id="gigaSpace" class="org.openspaces.core.GigaSpaceFactoryBean">
    <property name="space" ref="space" />
    <property name="transactionManager" ref="transactionManager" />
</bean>
EmbeddedSpaceConfigurer configurer = new EmbeddedSpaceConfigurer("mySpace");
PlatformTransactionManager ptm = new LookupJiniTxManagerConfigurer().lookupTimeout(5000).transactionManager();
GigaSpace gigaSpace = new GigaSpaceConfigurer(configurer).transactionManager(ptm).gigaSpace();

When using Spring declarative transaction management, a transaction timeout can be set on the transaction scope. For more details, see below.

When using Jini-based transactions, a timeout value can be set for both the commit and abort operations. This values can also be set on the transaction manager.

Renewing Transactions

Jini transactions allow you to configure automatic renewing of ongoing transactions. This feature is very useful if you have to configure a long transaction timeout, and have it expire earlier in case of a complete failure (for example, a JVM crash). Expiring the transaction is important so objects held under a transaction lock are released as soon as possible.

The following is an example of how this can be configured:

<os-core:embedded-space id="space" space-name="mySpace"/>
<os-core:distributed-tx-manager id="transactionManager" >
    <os-core:renew pool-size="2" duration="1000" round-trip-time="500" />
</os-core:distributed-tx-manager>
<os-core:giga-space id="gigaSpace" space="space" tx-manager="transactionManager"/>
<bean id="space" class="org.openspaces.core.space.EmbeddedSpaceFactoryBean">
    <property name="name" value="space" />
</bean>

<bean id="transactionManager" class="org.openspaces.core.transaction.manager.DistributedJiniTransactionManager">
    <property name="leaseRenewalConfig">
        <bean class="org.openspaces.core.transaction.manager.TransactionLeaseRenewalConfig">
            <property name="poolSize" value="2" />
            <proeprty name="renewRTT" value="500" />
            <property name="renewDuration" value="1000" />
        </bean>
    </property>
</bean>

<bean id="gigaSpace" class="org.openspaces.core.GigaSpaceFactoryBean">
    <property name="space" ref="space" />
    <property name="transactionManager" ref="transactionManager" />
</bean>
EmbeddedSpaceConfigurer configurer = new EmbeddedSpaceConfigurer("mySpace");
TransactionLeaseRenewalConfig config = new TransactionLeaseRenewalConfig();
config.setPoolSize(2);
config.setRenewDuration(1000);
config.setRenewRTT(500);
PlatformTransactionManager ptm = new DistributedJiniTxManagerConfigurer().leaseRenewalConfig(config).transactionManager();
GigaSpace gigaSpace = new GigaSpaceConfigurer(configurer).transactionManager(ptm).gigaSpace();

The above configuration creates a Distributed Transaction Manager with a pool of 2 transaction (lease) renewal managers (a single manager can handle multiple transactions, more managers allow for better concurrency). Each transaction is renewed every 1 second (1000 milliseconds) with an expected round trip time of 500 milliseconds. This means that a transaction with a timeout of 10 seconds is renewed 10 times (approximately) and if the JVM crashes, the transaction expires within a second (at most).

More information regarding Lease Renewal Manager can be found here.

XA/JTA Support

GigaSpaces can be used within an XA transaction using JTA. The OpenSpaces API allows you to work with Spring's JTATransactionManager and provides support for declarative transaction management. The following is an example of how OpenSpaces JTA support can be used (using Jboss):

<os-core:embedded-space id="space" space-name="mySpace"/>
<bean id="jboss" class="com.arjuna.ats.internal.jta.transaction.arjunacore.UserTransactionImple" />
<bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager">
    <property name="userTransaction" ref="jboss" />
</bean>

<os-core:giga-space id="gigaSpace" space="space" tx-manager="transactionManager" />
<bean id="space" class="org.openspaces.core.space.EmbeddedSpaceFactoryBean">
    <property name="name" value="space" />
</bean>

<bean id="jboss" class="com.arjuna.ats.internal.jta.transaction.arjunacore.UserTransactionImple" />

<bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager">
    <property name="userTransaction" ref="jboss" />
</bean>

<bean id="gigaSpace" class="org.openspaces.core.GigaSpaceFactoryBean">
    <property name="space" ref="space" />
    <property name="transactionManager" ref="transactionManager" />
</bean>
UserTransaction userTransaction = ... //get UserTransaction via JDNI / instantiation
PlatformTransactionManager ptm = new JtaTransactionManager(userTransaction);
EmbeddedSpaceConfigurer configurer = new EmbeddedSpaceConfigurer("mySpace");
GigaSpace gigaSpace = new GigaSpaceConfigurer(configurer).transactionManager(ptm).gigaSpace()

GigaSpaces JTA implementation supports both local and distributed transaction managers. That means that you can enlist multiple space partitions as a single XA resource in an XA transaction.

See the JTA-XA Example for fully running demonstration how to integrate GigaSpaces with an external JMS Server.

XA transactions should be carefully considered. The overhead of managing a 2PC transaction over two or more resources is often a performance killer.

Multiple Transaction Managers

Starting with Spring 3, you can specify multiple Transaction managers within the same Spring context (pu.xml) and use the @Transactional annotation. If you use multiple Transaction managers, make sure you specify the name of the transaction manager your class/method should use:

<os-core:distributed-tx-manager id=" txManager1" />
<os-core:giga-space id="gigaspace" space="space"  tx-manager="txManager1"/>
<tx:annotation-driven transaction-manager=" txManager1" />

<os-core:distributed-tx-manager id=" txManager2" />
<os-core:giga-space id="gigaspace2" space="space"  tx-manager="txManager2"/>
<tx:annotation-driven transaction-manager=" txManager2" />
@Transactional(value="txManager1")
public String getFoo()
{
    return dao.find("foo");
}
@Transactional(value="txManager2")
public String getFoo()
{
    return dao.find("foo");
}

How to Demarcate Transactions in Your Code

There are two ways to demarcate the transaction boundaries in your code:

  1. Annotate your methods with the Spring @Transactional annotation, and configure Spring to process the annotation such that every call to the annotated methods will be automatically performed under a transaction. In this case, the transaction starts before the method is called and commits when the method call returns. If an exception is thrown from the method, the transaction is rolled back automatically. You can control various aspects of the transaction by using the attributes of the @Transactional annotation. Refer to the API documentation of this class for more details.

  2. Programmatically create the required transaction manager (see below) and a TransactionDefinition instance, and call the Space operations you would to perform under a transaction.

Declarative Transaction Demarcation

The following is an example of how a method should be annotated to support declarative transaction management:

@Transactional (propagation=Propagation.REQUIRED)
public void myTransactionalMethod(Object data) {
  gigaSpace.write(mySpaceObject);
  gigaSpace.take(mytemplate);
  ...
  }

To enable the declarative transaction management:

  1. In your processing unit XML, your pu.xml should include the <tx:annotation-driven> tag. See the example below for enabling the @Transactional annotation processing for the pu.xml. The system will search for @Transactional annotations on all of the beans in the pu.xml.

  2. For the transactional behavior to take effect, the calling code must be injected with the transactional bean. In the example below, we have the transactionalBean bean, which annotates some of its methods with the @Transactional annotation. The callingBean is injected with it. Under the hood, Spring wraps the transactionalBean with a transactional proxy that initiates a transaction before the method is called and commits it or does rollback according to the invocation result.

  3. The @Transactional annotation is only applied for proxied beans, i.e. beans that have been processed by the framework and injected to other beans which use them.

  4. If you try to call an annotated method from within the same class, for example calling it on this, no transaction will be started because your code actually accesses a direct reference and not a proxied bean.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:os-core="http://www.openspaces.org/schema/core"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:os-events="http://www.openspaces.org/schema/events"
       xmlns:os-remoting="http://www.openspaces.org/schema/remoting"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
       http://www.openspaces.org/schema/core http://www.openspaces.org/schema/16.2/core/openspaces-core.xsd
       http://www.openspaces.org/schema/events http://www.openspaces.org/schema/16.2/events/openspaces-events.xsd
       http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
       http://www.openspaces.org/schema/remoting http://www.openspaces.org/schema/16.2/remoting/openspaces-remoting.xsd">

    <os-core:embedded-space id="space" space-name="mySpace"/>

    <os-core:giga-space id="gigaSpace" space="space" tx-manager="transactionManager"/>

    <!-- Defines a distributed Jini transaction manager. -->
    <os-core:distributed-tx-manager id="transactionManager"/>
        <bean id="transactionalBean" class="MyClass"/>
        <bean id="callingBean" class="MyOtherClass">
            <property name="myClass" ref="transactionalBean"/>
        </bean>
    <tx:annotation-driven transaction-manager="transactionManager" />
</beans>

You can also annotate beans exposed via space based remoting. If you include the <tx:annotation-driven> element in your pu.xml file, it will be processed as any other bean and the remoting mechanism will use the proxied instance, thus making the remote call to the bean transactional.

Programmatic Transaction Management

If you don't want to leverage Spring's declarative transaction management, or have an application that is not configured by Spring, you can start, commit and roll back transactions explicitly from within your code by using Spring's transaction API.

Here is how you should use the Transaction manager via the API:

1. Create a Transaction Manager using:

PlatformTransactionManager ptm = new DistributedJiniTxManagerConfigurer().transactionManager();

or get a reference to an exiting Transaction Manager:

PlatformTransactionManager ptm = new LookupJiniTxManagerConfigurer().lookupTimeout(5000).transactionManager();

2. Create a GigaSpace using the relevant API and have the "transactionManager' associated with it:

GigaSpace gigaSpace = new GigaSpaceConfigurer(new SpaceProxyConfigurer("space")).transactionManager(ptm).gigaSpace();

The following is a full example where the GigaSpace is used to execute Space operations and rollback/commit using the DistributedJiniTxManagerConfigurer created, without having Spring involved:

//get reference to a GigaSpace instance - with the example below we use a remote proxy
GigaSpace gigaSpace = new GigaSpaceConfigurer(new SpaceProxyConfigurer("space")).transactionManager(ptm).gigaSpace();

//get a reference to a GigaSpaces PlatformTransactionManager instance as described in step one above.
PlatformTransactionManager ptm = new DistributedJiniTxManagerConfigurer().commitTimeout(2000L).defaultTimeout(30).transactionManager();

DefaultTransactionDefinition definition = new DefaultTransactionDefinition();
//configure the transaction definition
definition.setPropagationBehavior(Propagation.REQUIRES_NEW.ordinal());
TransactionStatus status = ptm.getTransaction(definition);
try {
    //do things with the GigaSpace instance...
    gigaSpace.write(myObject);
}
catch (MyException e) {
    ptm.rollback(status);
    throw e;
}
ptm.commit(status);

You can also use Spring's TransactionTemplate if you prefer. This is documented in full in the Spring reference guide.

When using Programmatic Transaction Management you should be expecting to handle the org.springframework.transaction.TransactionException that will have the exact reason as an instance of net.jini.core.transaction.TransactionException. This has the following subclasses exceptions: CannotAbortException, CannotCommitException, CannotJoinException, CannotNestException, CrashCountException, TimeoutExpiredException,UnknownTransactionException.

Spring TransactionDefinition Mapping to GigaSpaces ReadModifiers

The following table describes the mapping between the Spring TransactionDefinition Mapping to GigaSpaces ReadModifiers:

Spring TransactionDefinition GigaSpaces ReadModifiers
ISOLATION_READ_UNCOMMITTED DIRTY_READ
ISOLATION_READ_COMMITTED READ_COMMITTED
ISOLATION_REPEATABLE_READ REPEATABLE_READ
ISOLATION_SERIALIZABLE Not Supported

Viewing Transactions

You can view and inspect ongoing Space transactions with the GigaSpaces Management Center.

Starting Embedded Mahalo

You can enable this option in one of the following ways:

  • Set the following option to true in your container schema:

    <embedded-services>
    ...
    <mahalo>
        <!-- If true, will start an embedded Mahalo Jini Transaction Manager. Default value: false -->
        <start-embedded-mahalo>${com.gs.start-embedded-mahalo}</start-embedded-mahalo>
    </mahalo>
    
  • Set the following option as a JVM argument:

    -Dcom.gs.start-embedded-mahalo=true
    
  • Set XPath in the $GS_HOME\config\gs.properties file:

    com.j_spaces.core.container.embedded-services.mahalo.start-embedded-mahalo=true