Friday, May 17, 2013

Compensating Transactions: When ACID is too much (Part 1: Introduction)

ACID transactions are a useful tool for application developers and can provide very strong guarantees, even in the presence of failures. However, ACID transactions are not always appropriate for every situation. In this series of blog posts. I'll present several such scenarios and show how an alternative non-ACID transaction model can be used.

The isolation property of an ACID transaction is typically achieved through optimistic or pessimistic concurrency control. Both approaches can impose negative impacts on certain classes of applications, if the duration of the transaction exceeds a few seconds (see here for a good explanation). This can frequently be the case for transactions involving slow participants (humans, for example) or those distributed over high latency networks (such as the Internet). Also, some actions cannot simply be rolled back; such as, the sending of an email or the invocation of some third-party service.

A common strategy for applications that cannot use ACID, is to throw out transactions altogether. However, with this approach you are missing out on many of the benefits that transactions can provide. There are many alternative transaction models that relax some of the ACID properties, while still retaining many of the strong guarantees essential for building robust enterprise applications. These models are often referred to as "Extended Transaction models" and should be considered before deciding not to use transactions at all.

In the Narayana project, we have support for three Extended Transaction models; Nested Top Level Transactions, Nested Transactions and a compensation-based model based on Sagas. In this series of blog posts I'll be focusing on the compensation-based approach.


What is a ‘Compensation-based transaction’

Transaction systems typically use a two-phase protocol to acheive atomicity between participants. This is the case for both ACID transactions and our compensation-based transactions model. In the first phase, each individual participant, of an ACID transaction, will make durable any state changes that were made during the scope of the transaction. These state changes can either be rolled back or committed later once the outcome of the transaction has been determinned. However, participants in a compensation-based transaction behave slightly differently. Here any state changes, made in the scope of the transaction, are committed during (or prior) to the first phase. In order to make "rollback" possible, a compensation handler is logged during the first phase. This allows the state changes to be 'undone' if the transaction later fails.

What Affect Does this Have on the Isolation property of the Transaction?

The Isolation property of a transaction dictates what, if any, changes are visible outside of the transaction, prior to its completion. For ACID transactions, the isolation property is usually pretty strong with database vendors offering some degree of relaxation via the isolation level configuration. However, in a compensation-based transaction the isolation level is totally relaxed allowing units of work to be completed and visible to other transactions, as the current compensation-based transaction progresses. The benefit of this model is that database resources are not held for prelonged periods of time. However, the down-side is that this model is only applicable for applications that can tolerate this reduced level of isolation.

The following two diagrams show an example, where a client is coordinating invocations to multiple services that each make updates to a database. The diagrams are simplified in order to focus on the different isolation levels offered by a ACID and compensation-based transaction. The example also assumes a database is used by the service, but it could equally apply to other resources.


The diagram above shows a simplified sequence diagram of the interactions that occur in an ACID transaction. After the client begins the (ACID) transaction it invokes the first service. This service makes a change to a database and at this point database resources are held by the transaction. This example uses pessemistic locking. Had optimistic locking been used, the holding or database resources could have been delayed until the prepare phase, but this could result in more failures to prepare. The Client then invokes the other services, who may in turn hold resources on other transactional resources. Depending on the latency of the network and the nature of the work carried out by the services, this could take some time to complete. All the while, the DB resources are still held by the transaction. If all goes well, the client then requests that the transaction manager commit the transaction. The transaction manager invokes the two-phase commit protocol, by first preparing all the participants and then if all goes well, commits all the participants. It's not until the database participant is told to commit, that these database resources are released.

From the diagram, you can see how, in an ACID transaction, DB resources could be held for a relativley long period of time. Also, assuming the service does not wish to make a heuristic decision, this duration is beyond the control of the service. It must wait to be informed of the outcome of the protocol, which is subject to any delays introduced by the other participants.




The diagram above shows a simplified sequence diagram of the intercations that occur in a compensation-based transaction. The client begins a new (compensation-based) transaction and then invokes the first service. The service then sends an update to the database, which is committed imediatlly, in a relativly short, seperate ACID transaction. At this point (not shown in the diagram) the service informs the transaction manager that it has completed it's work, which causes the transaction manager to record the outcome of this participant's work to durable storage along with the details of the compensation handler and any state required to carry out the compensation. It's possible to delay the commit of the ACID tranaction until after the compensation handler has been logged (see here), this removes a failure window in which a non-atomic outcome could occur.

The client now invokes the other services who, in this example, behave similarly to the first service. Finally, the client can request that the Transaction Manager close (commit) or cancel (rollback) the compensation-based transaction. In the case of cancel, the transaction manager calls the compensating action asociated with each participant that previously completed some work. In this example, the compensating action makes an update to the database in a new, relativley short ACID transaction. The service can also be notified if/when the compensation-based transaction closes. We'll cover situations when this is useful later in this series. The notification of (close/compensate) is retried until it is acknowledged by the service. Although this is good for reliability, it does require that the logic of the handlers be idempotent.

From the diagram, you can see that the duration for which DB resources are held, is greatly reduced. This comes at a cost of relaxed isolation (see the 'changes visible' marker). However, in scenarios where compensation is rare, the relaxed isolation could be of little concern as the visible changes are usually valid.

It is also possible to mitigate this loss of isolation by marking the change as tentative in the first phase and then marking the change as confirmed/cancelled in the second phase. For example, the initial change could mark a seat on a plane as reserved; the seat could later be released or marked as booked, depending on the outcome of the transaction. Here we have traded the holding of database-level resources for the holding of application-level resources (in this case the seat). This approach is covered in more detail later in the series.


What's Coming up in the Next Posts?

The following three posts will each focus on particular set of use-cases where compensation-based transactions could prove to be a better fit than ACID transactions. In each part, I'll provide a code example, using the latest iteration of our new API for compensation-based transactions (first introduced here).


  • Part 2: Non-transactional Work. This part will cover situations where you need to coordinate multiple non-transactional resources, such as sending an email or invoking a third party service.
  • Part 3: Cross-domain Distributed Transactions: This part covers a scenario where the transaction is distributed, and potentially crosses multiple business domains.
  • Part 4: Long-lived Transactions. This part covers transactions that span long periods of time and shows how it's possible to continue the transaction even if some work fails.


In part five, I'll cover the status of our support for compensation-based transactions and present a roadmap for our future work.

Monday, May 6, 2013

When you need ACID and can't get it ...

What happens when you need traditional ACID transactions across your resource managers and they won't behave? By that I mean they're not two-phase aware so can't be driven by a coordinator to achieve all of the necessary properties. Of course if you've just one such resource manager (let's call it one-phase aware for now) then you can probably make use of the LRCO optimisation.

However, what if you've got more than one such resource manager? Well if you've checked out JBossTS in the past then you'll know that we allow you to enlist multiple one-phase resource managers, but there's a very big caveat. This really isn't an option that anyone should choose. Fortunately I blogged about a better option over 6 years ago (!): compensation transactions. There were a few follow up entries, but the original one is the place to start.

Now what got me revisiting this was the article I wrote earlier on banks, ACID and BASE. It wasn't so much the notion that banks don't use ACID as the fact that we still see a lot of popular (NoSQL) databases that don't support transactions. Of course some use transactions internally (local transactions), but what I'm talking about is what is often referred to as global transactions: those transactions that span multiple resource managers. When you want to send a message, update a traditional database and update a NoSQL instance all within the scope of the same transaction, in most cases you're out of luck. And this is something that many enterprise customers want to do or will want to do soon.

That is unless you can use LRCO, which may not be possible if the resource manager(s) don't support the necessary recovery semantics.

Therefore, you're left in a situation that has very few options. One would be to ignore the problem and assume that because failures are rare (they are rare, right?) it's unlikely to ever be a problem. Personally I wouldn't recommend this.

Another option would be to resolve any problems manually. Again, since failures are rare (we're sure, right?) the chances of having to do this are slim and if you do have to resolve then it's a good enough trade-off. Of course you've got to hope that you've got enough information to detect and handle the recovery. Again, this isn't something I'd recommend unless you're a company that can afford to employ people who do nothing each day other than resolve these problems. (Yes, they do exist.)

My recommended option is to use compensation transactions, as I outlined many years ago. They will automate the recovery (and detection) as well as allow you to seamlessly integrate with a range of resource managers which are "well behaved". I'm hoping that we'll get a chance to try these out soon with some enterprise applications that use NoSQL implementations that don't support global transactions. Once we've done this then it'll be a good reason for one of the team to come back and write something here.

Friday, May 3, 2013

Cross posting on banks, ACID and BASE

Just a cross post that I thought people might be interested in. Caused by a recent article that seemed to get interpreted as banks don't use ACID transactions!

Sunday, April 14, 2013

Simplified XTS Context Propagation

In this blog post I'd like to introduce a new feature from our recent Narayana 5.0.0.M2 release. It was developed by our newest addition to the Transactions Team, Gytis Trikleris. This feature simplifies the way you write clients for WS-AT and WS-BA enabled Web services.

Previously the developer was responsible for setting up the client side handler chain. This has proven problematic as the code is rather cumbersome and it's easy to make a mistake. Here's an example of what the client code used to look like:



import org.jboss.jbossts.txbridge.outbound.JaxWSTxOutboundBridgeHandler;
import org.jboss.jbossts.txbridge.outbound.JaxWSHeaderContextProcessor;
...
MyService client = service.getPort(portName, MyService.class);

BindingProvider bindingProvider = (BindingProvider) client;

List handlers = new ArrayList(); 
handlers.add(new JaxWSTxOutboundBridgeHandler()); 
handlers.add(new JaxWSHeaderContextProcessor()); 

bindingProvider.getBinding().setHandlerChain(handlers);


In this example, the application has begun a JTA transaction and is now invoking a Web service. The service supports WS-AT, and the developer would like the JTA transaction to be distributed to this service. Therefore, the developer carefully constructs a client side handler chain and ensures that the bridging handler is invoked before the XTS handler. Get this order wrong and the bridging will fail. This code is required for every Web service port that needs to propagate a WS-AT or WS-BA transaction.

We still support the above way of writing clients and the behaviour of XTS in that case has not changed. However, we now have some additional ways to enable a distributed transaction over Web services.

Enable Globally

You can now enable transaction propagation for all Web service calls that are invoked within a JTA, WS-AT or WS-BA transaction. This is done with the 'defaultContextPropagation' property in the XTS subsystem config of the standalone-xts.xml.


<?xml version="1.0" encoding="UTF-8"?>
<subsystem xmlns="urn:jboss:domain:xts:1.0">
   <xts-environment url="..." />
   <default-context-propagation enabled="true" />
</subsystem>


As this is enabled by default (for standalone-xts.xml), calls to all Web services that support WS-AT or WS-BA will automatically receive the transaction context allowing them to participate in the distributed transaction.

The transaction context is simply ignored if the service does not support WS-AT or WS-BA. This is done by setting MustUnderstand=”false” on the 'CoordinationContext' SOAP header. Unfortunately, this may cause issues when invoking WS-AT or WS-BA enabled Web services on other vendors’ application servers. This is because the WS-Coordination specification states that MustUnderstand must be set to true. If you are affected by this issue, you will need to explicitly enable the transaction propagation as described in the next section.

There is a slight overhead associated with having an unused WS-AT transaction in place. We intend to investigate this as part of https://issues.jboss.org/browse/JBTM-1515



Enable/Disable per Web service port

The default context propagation policy can also be overridden on a per Web Service port basis. This allows the developer to easily state which Web Service clients must and must-not propagate the transaction context.

In the following example, the developer states that the current JTA transaction must be distributed over the Web Service calls on this port:



import org.jboss.jbossts.txbridge.outbound.JTAOverWSATFeature;
...
MyService client = service.getPort(portName, MyService.classnew JTAOverWSATFeature());


This is done through the standard JAX-WS WebServiceFeature facility. A JAX-WS WebServiceFeature allows meta-information to be added to a port that describe cross-cutting behaviour, such as logging, security or compression. In our case we use the 'JTAOverWSATFeature' to state that any JTA (or WS-AT) transaction should be distributed via calls on this client. Calls to this service will fail if the Web service does not support WS-AT or WS-BA (in this case, XTS sets MustUnderstand=true on the 'CoordinationContext' SOAP header as the developer has explicitly stated that it is required).

The developer may also state that the transaction must-not be distributed over calls to this Web service. This is done by setting the 'JTAOverWSATFeature' feature to disabled:



import org.jboss.jbossts.txbridge.outbound.JTAOverWSATFeature;
...
MyService client = service.getPort(portName, MyService.classnew JTAOverWSATFeature(false));


The use of 'JTAOverWSATFeature' overrides whatever default context propagation is set to in the standalone-xts.xml.

Feedback

We've tried to come up with a solution that doesn't impact existing applications, whilst also being as intuitive as possible for developers of new applications. If you are having difficulties with this approach or if it simply doesn't make sense, please get in touch in the usual ways (comments on this post or via the forum).


Acknowledgements

I'd like to say a bit thank you to Alessio Soldano and the JBossWS team. They provided a lot of advice and also added new features to the JBossWS SPI to support these features.

Monday, April 8, 2013

API Improvements for Compensation-based Transactions

In a recent post I talked about API improvements we are introducing for applications that use ACID transactions. In this post I’ll cover what are we doing for users of compensation based transactions.

Even though we only have WS-BA for compensation-based transactions at the moment, we are still following the principle that the same transaction API should be used to develop the application, regardless of what transport is actually used to distribute the transaction. This will become more important when we support compensations over other technologies, such as REST or JBoss remoting.

Unfortunately there is no standard API for compensation based transactions, so we needed to develop our own. This API is still in the early stages of development. However, we are keen to get community feedback, so we made the early version available as part of our recent Narayana 5.0.0.M2 release.

I’ll cover the basics of the API in this post; so as to give you a feel for what we are proposing. You can take a look at the Narayana quickstarts for more complete examples. We also intend to blog more on this subject in the coming months as we develop our ideas further.

I’ve omitted a recap on compensation-based transactions and when you need them. This will be the subject of a future blog posting.

Example

Hopefully this example will give you an idea of how the new API works

The following code comprises part of a "Warehouse Service", implementad as an EJB exposed as a JAX-WS Web Service:


@Compensatable
@Stateless
//JAX-WS annotations omitted
public class WarehouseServiceImpl implements WarehouseService {
{
    @DataManagement private Map txDataMap;
    @PersistenceContext protected EntityManager em;

    @WebMethod
    @ServiceRequest
    public void shipItem(String item, String address) throws {
        //Use em to add order to DB
        txDataMap.put("orderID", orderID);
    }

    @Compensate
    private void cancelOrder() {
        Integer orderID = txDataMap.get("order");
        //Use em to lookup order by ID and cancel it
    }
}

The @Compensatable annotation is used to state that methods on the class should be invoked in a compensation-based transaction. This is similar to the @TransactionAttribute provided by JTA.
The ‘shipItem’ method represents the business logic of the service. It has a corresponding compensation handler which can be used to undo this work.

When ‘shipItem’ is invoked, a new entry is added to the database. This update is done in a regular JTA transaction that commits when the method completes successfully. The ID of the order is saved in the ‘txDataMap’ to be used later by the compensation handler.

The @DataManagement injected Map stores state for the lifetime of the transaction. The state is isolated to the transaction and is garbage collected when the transaction ends. In future releases, this data will also be available at recovery time.

@Compensate annotates the compensation handler for undoing the work done in the business method. In this example, the compensation action is to look up the ID of the order and then make an update to the DB to mark it as cancelled. This DB update is done in a separate transaction that commits when ‘cancelOrder’ completes successfully.

The current release only supports a single @Compensation method for all @ServiceRequest methods in the class. Subsequent releases will remove this limitation.

Getting Started

Hopefully you are now eager to get started and want to know where to go next! Here are our suggestions:

* Download and try the Quickstarts from here.
* Provide feedback and get help through our forum.
* Track the progress of issues here.
* Subscribe to this blog.
* Fork the Narayana repo and contribute. Of course, we always welcome community contribution. We can advise on good issues for new contributors, or you can suggest a feature that interests you.



Acknowledgements

I'd like to say a bit thank you to Alessio Soldano and the JBossWS team. They provided a lot of advice and also added new features to the JBossWS SPI to support these features.

Sunday, March 31, 2013

STM, vert.x and the Pi Part 1

Given other priorities (aka 'my day job'), I'm slowly working my way through another pet project I've had for a while: adding transactions, and specifically STM, to vert.x. Of course at the moment I also try to add an influence from my other pet project, the Raspberry Pi, so I'm doing it on the Pi as much as possible. OK, it may not be the fastest machine on the block, but it's a lot of fun!

We've had STM in Narayana for a while, so I won't cover the basic again. I'm also not going to cover any Pi-related set up: by this point you should know enough to do this yourself. So let's start with cloning the most recent version of Narayana:


Make sure your path and JAVA_HOME are set so we're using JDK 7:

 
Next let's build the components we need for STM:


Don't forget that this is going to take a while, so maybe time to go grab a coffee, read a book, play with your kids, or watch an episode of Lost.


With that build complete, we've already run a lot of STM examples in the form of unit tests. However, let's take a simple example:

import java.io.IOException;

import org.jboss.stm.annotations.Transactional;
import org.jboss.stm.annotations.ReadLock;
import org.jboss.stm.annotations.WriteLock;
import org.jboss.stm.internal.RecoverableContainer;

import com.arjuna.ats.arjuna.AtomicAction;

/**
 * @author Mark Little
 */


public class Example
{  
    @Transactional
    public interface Atomic
     {
     public void change (int value) throws Exception;
       
     public void set (int value) throws Exception;
       
     public int get () throws Exception;
    }
   
    @Transactional
    public class ExampleSTM implements Atomic
     {  
        @ReadLock
     public int get () throws Exception
     {
         return state;
     }

        @WriteLock
     public void set (int value) throws Exception
     {
         state = value;
     }
       
        @WriteLock
     public void change (int value) throws Exception
     {
         state += value;
     }

     private int state;
    }
   
    public void testExample () throws Exception
    {
        RecoverableContainer theContainer = new RecoverableContainer();
        ExampleSTM basic = new ExampleSTM();
        Atomic obj = null;
       
        try
        {
        obj = theContainer.enlist(basic);
        }
        catch (final Throwable ex)
        {
        ex.printStackTrace();
           
        return;
        }
       
        AtomicAction a = new AtomicAction();
       
        a.begin();
       
        obj.set(1234);
       
        a.commit();

    System.out.println("Should get() 1234 after commit: "+obj.get());
       
        a = new AtomicAction();

        a.begin();

        obj.change(1);
       
        a.abort();

    System.out.println("Should get() 1234 after abort: "+obj.get());
    }

    public static void main (String[] args)
    {
    Example ex = new Example();

    try
    {
        ex.testExample();
    }
    catch (final Exception e)
    {
        e.printStackTrace();
    }
    }
}


Stick this at the same level of Narayana if you want to use the same CLASSPATH as here:

narayana/ext/jboss-logging.jar:narayana/ArjunaCore/arjuna/target/test-classes:narayana/common/target/common-5.0.0.M3-SNAPSHOT.jar:narayana/STM/target/core-5.0.0.M3-SNAPSHOT.jar:narayana/ArjunaCore/txoj/target/txoj-5.0.0.M3-SNAPSHOT.jar:narayana/ArjunaCore/arjuna/target/arjuna-5.0.0.M3-SNAPSHOT.jar:.

After compiling, run the example and you should see the following:


Despite what I said at the start of this entry, there's a distinct lack of vert.x involved here at the moment. That's because there are a few changes we need to make to the STM implementation to make it easier to share transactional objects between address spaces. This is all stuff that's available "natively" in TXOJ, so developers could use that API immediately. However, the STM approach simplifies a lot so once those additional features from TXOJ are integrated there, we'll have a follow up article to discuss what, how and why.

Monday, March 25, 2013

API Improvements for WS-AT and REST-AT

In this post I’ll introduce some API changes we are doing to improve the way developers use WS-AT and REST-AT.

I’m a strong believer that the internal architecture of your application, and the means by which client’s communicate with it, are two orthogonal issues. You wouldn’t want to have to change the internals of your application, just because you need to change the way clients invoke your service.

We think you should be able to develop your applications using a common Transactions API. You should then be able to invoke remote services over whatever transport is appropriate and automatically have the transaction distributed. Furthermore, you shouldn’t be restricted to having all participants use the same transport.

There really is only one option for a Java “common Transactions API” - it’s JTA. JTA is well supported in Java EE application servers and it is well understood by developers. Furthermore, there are many applications out there already using JTA.

This is what we are moving towards with each milestone release of Narayana 5.0.0. With milestone two we made it very simple for a pure JTA client to invoke a JTA application over Web Services in a distributed transaction. Take a look at the following example:

Client


@Stateless
public class OrderClient {

  @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
  public void orderItem(String item, String address, double amount) {

    AccountService as = //Lookup AccountService WS client
    WarehouseService ws = //Lookup WarehouseService WS client

    ws.shipItem(item, address);
    as.invoiceCustomer(address, amount);
  }
}


Here the client is using a regular Stateless Session Bean. The method ‘orderItem’ is invoked in a JTA transaction that is committed if the method succeeds and rolled back if it fails.

The calls to the Warehouse and Account services automatically use WS-AT to distribute the JTA transaction. The middleware knows to do this because a JTA transaction was present when the Web Service call was invoked.

Transaction propagation can be configured, but that’s the subject of another blog post (stay tuned).

Service


@Transactional //Currently required, will be removed soon https://issues.jboss.org/browse/JBTM-1468
@Stateless
//JAX-WS annotations omitted for brevity
public class WarehouseServiceImpl implements WarehouseService {

    @PersistenceContext
    protected EntityManager em;

    @WebMethod
    public void shipItem(String item, String address) {
        // Use the Entity Manager to 
        // add an item and shipping address to DB
    }
}

Again the service is implemented as a simple Stateless Session bean that just happens to be offered as a Web Service. Here the application is using JTA to make a database update. The middleware automatically handles the mapping between the incoming WS-AT transaction and the JTA transaction.

What About Other Transports?

With milestone two we already support transparent propagation over Web Services (via WS-AT), Corba (via JTS) and JBoss Remoting. We hope to have REST-AT support in a subsequent milestone release (see JBTM-1468).

Getting Started

Hopefully you are now eager to try this out. The best place to look is at this quickstart, which demonstrates the new API in action.


Acknowledgements

I'd like to say a big thank you to Alessio Soldano and the JBossWS team. They provided a lot of advice and also added new features to the JBossWS SPI to support this feature.