Showing posts sorted by relevance for query stm. Sort by date Show all posts
Showing posts sorted by relevance for query stm. Sort by date Show all posts

Sunday, September 9, 2018

Tips on how to evaluate STM implementations

Software Transactional Memory (STM) is a way of providing transactional behaviour for threads operating on shared memory. The transaction is an atomic and isolated set of changes to memory such that prior to commit no other thread sees the memory updates and after commit the changes appear to take effect instantaneously so other threads never see partial updates but on abort all of the updates are discarded.

Unlike other models such as XA, OTS, JTA, WS-AT etc, with STM there is no accepted standard for developers to program against. Consequently the various implementations of STM differ in important respects which have consequences for how application developers build their software. I recently came upon an excellent book on Transactional Memory where the authors James Larus and Ravi Rajwar presented a taxonomy of features and characteristics that can be used to differentiate the various STM implementations from each other. In this and subsequent blogs I will explain the taxonomy and identify where the Narayana STM solution (which was introduced in Mark Little's initial blog on the topic) fits into it. Towards the end of the series I will include some tips, best practices and advice on how you can get the most out of the Narayana implementation of STM.

In this first article I will cover isolation, nesting and exception handling. In later articles I will discuss topics such as conflict detection and resolution, transaction granularity, concurrency control etc.

By way of motivation, why would one want to use STM in favour of other transaction models and concurrency control mechanisms:
  • The STM approach of mutating data inside of a transaction has some nice features:
    • It is less error prone since the demarcation of an atomic block of code is primitive but other synchronisation approaches are many and varied. Techniques such as locks, semaphores, signals etc are tricky to get right, for example the programmer must ensure that accesses are protected with the correct locks and in the correct order. With conventional concurrency control, imagine trying reverse all the changes made during a computation if a problem such as deadlock or data race is detected, whereas code changes that are protected by STM can be aborted in a single statement.
    • Transactional updates make it easier for the programmer to reason about his code (it is clear how different threads affect each other) and data (because it simplifies the sharing of state between threads).
  • The declarative approach (where the programmer simply marks which code blocks are transactional) means concurrent programming is more intuitive with no explicit locks or synchronisation to worry about.
  • Can perform much better than fine grained locking (which can lead to deadlock) and coarse grained locking (which inhibits concurrency):
    • If a thread takes a lock and is context switched or incurs cache misses or page faults etc then other threads that need the lock are stalled until the thread is rescheduled or until the needed data is retrieved.
    • With STM, updates can be batched up and speculatively committed together.
    • The runtime manages lock acquisition and release and resolves conflicts (using approaches such as timeouts and retries).
  • It is easier to compose operations using a technique called nesting (traditionally composing two operations can produce concurrency problems unless one analyses in detail the locking approach used by those operations).

Properties of a STM system

In the following I will describe the design choices available to STM systems in general and in particular I will illustrate the choices made by the Narayana STM implementation using code examples. The examples will be made available in the Narayana STM test suite so that you can also experiment with the particular properties of the implementation. Each of the examples will be using the same transactional object which is defined as follows:

    @Transactional
    public interface AtomicInt {
        int get() throws Exception;
        void set(int value) throws Exception;
    }

    public class AtomicIntImpl implements AtomicInt {
        private int state;

        @ReadLock
        public int get() throws Exception {
            return state;
        }

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

The @Transactional annotation on the AtomicInt interface tells the system that instances of the interface are candidates to be managed by the STM system. The implementation of the interface defines a pair of methods for reading and writing the the shared state (by default all state is tracked but this default can be overridden via the @NotState annotation).

Property 1: Interaction with non transactional code

If uncommitted transactional memory updates are visible to non-transactional code and vice-versa (i.e. updates made by non-transactional code are visible to running transactions) then the isolation model is said to be weak. On the other hand if non-transactional accesses are upgraded to a transactional access then the model is said to be strong.

The weak access model, although common, can lead to data races. A data race occurs if two threads T1 and T2 access memory, T1 for writing, say, and the other for reading then the value of the memory read is indeterminate. If, for example T1 writes data inside a transaction and T2 reads that data, then if T1 aborts but T2 has made a decision based on the value it read then we have an incorrect program since aborted transactions must not have side effects (recall the "all or nothing" characteristic of atomicity).

Narayana STM follows the weak isolation model. The following test updates shared memory inside a transaction and then triggers a thread to perform non-transactional reads and writes on it while the transaction is still running. The test shows that the two threads interfere with each other producing indeterminate results:
    public void testWeakIsolation() throws Exception {
        AtomicIntImpl aiImple = new AtomicIntImpl();
        // STM is managed by Containers. Enlisting the above implementation
        // with the container returns a proxy which will enforce STM semantics
        AtomicInt ai = new RecoverableContainer().enlist(aiImple);
        AtomicAction tx = new AtomicAction();

        // set up the code that will access the memory outside of a transaction
        Thread ot = new Thread(() -> {
            try {
                synchronized (tx) {
                    tx.wait(); // for the other thread to start a transaction

                    // weak isolation implies that this thread (which is running
                    // outside of a transaction) can observe transactional updates
                    assertEquals(2, aiImple.get()); // the other thread set it to 2
                    aiImple.set(10); // this update is visible to transactional code
            } catch (Exception e) {
                e.printStackTrace();
            }
        });

        ot.start();

        ai.set(1); // initialise the shared memory
        tx.begin(); // start a transaction
        {
            ai.set(2); // conditionally set the value to 2

            synchronized (tx) {
                tx.notify(); // trigger non-transactional code to update the memory
            }

            // weak isolation means that this transactional code may see the
            // changes made by the non transactional code
            assertEquals(10, ai.get()); // the other thread set it to 10
            tx.commit(); // commit the changes made to the shared memory
        }

        // changes made by non transactional code are still visible after commit
        assertEquals(10, ai.get());
        assertEquals(aiImple.get(), ai.get());
    }

As an aside, notice in this example that the code first had to declare the shared data using the @Transactional annotation and then had to access it via a proxy returned from a RecoverableContainer. Some systems introduce new keywords into the language that demarcate the atomic blocks and in such systems any memory updates made by the atomic block would be managed by the STM implementation. That type of system takes some of the burden of ensuring correctness away from the programmer but are harder to implement (for example a common technique requires compiler extensions).

Property 2: Nested transactions

A nested transaction (the child) is one that is started in the context of an outer one (the parent). The child sees the changes made by the parent. Aborting the parent will abort each child. A parent that does not have any parents is called top level.

The effects of committing/aborting either transaction (the child or parent) and the visibility of changes depend upon which model is being used:

Flattened:

  • The parent and child transactions see each others updates.
  • If the child aborts the parent aborts too.
  • Changes made by the child only become visible to other threads when the parent commits
Pros - easy to implement
Cons - breaks composition (if the child aborts it causes all work done by the parent transaction to abort)

Closed Nested

  • Changes are hidden from the parent transaction (and from other transactions) until the child commits, at which time any changes made by the child become part of the parent transactions' set of updates (therefore, in contrast to open nested transactions, other transactions will not see the updates until the parent commits);
  • aborting the child does not abort the parent;
Pros - Is arguably the most natural model for application designers

Open Nested

  • When the child transaction commits, all other transactions see the updates even if the parent aborts which is useful if we want unrelated code to make permanent changes during the transaction even if the parent aborts.
Pros - enables work to be made permanent even if the parent aborts (for example logging code made by the child)

Narayana STM follows the closed model as is demonstrated by the following test case:
    public void testIsClosedNestedCommit() throws Exception {
        AtomicInt ai = new RecoverableContainer().enlist(new AtomicIntImpl());
        AtomicAction parent = new AtomicAction();
        AtomicAction child = new AtomicAction();

        ai.set(1); // initialise the shared memory
        parent.begin(); // start a top level transaction
        {
            ai.set(2); // update the memory in the context of the parent transaction
            child.begin(); // start a child transaction
            {
                ai.set(3); // update the memory in a child transaction
                // NB the parent would still see the value as 2
                // (not shown in this test)
                child.commit();
            }
            // since the child committed the parent should see the value as 3
            assertEquals(3, ai.get());
            // NB other transactions would not see the value 3 however until
            // the parent commits (not demonstrated in this test)
        }
        parent.commit();

        assertEquals(3, ai.get());
    }

Isolation amongst child transactions

The concept of isolation applies to nested transactions as well as to top level transactions. It seems most natural for siblings to use the same model as is used for isolation with respect to other transactions (ie transactions that are not in ancestor hierarchy of a particular child). For example the CORBA Object Transaction Service (OTS) supports the closed model and children do not see each others updates until the parent commits.

Property 3: Exception Handling

On exception the options are to either terminate or ignore the exception or to use a mixture of both where the programmer tells the system which exceptions should abort and which ones should commit the transaction which is similar to what the JTA 1.2 spec provides with its rollbackOn and dontRollbackOn annotation attributes.

The Narayana STM implementation takes the view that the programmer is best placed to make decisions about what to do under exceptional circumstances. The following test demonstrates this behaviour:
    public void testExceptionDoesNotAbort() throws Exception {
        AtomicInt ai = new RecoverableContainer().enlist(new AtomicIntImpl());
        AtomicAction tx = new AtomicAction();

        ai.set(1);
        tx.begin();
        {
            try {
                ai.set(2);
                throw new Exception();
            } catch (Exception e) {
                assertEquals(2, ai.get());
                // the transaction should still be active
                ai.set(3);
                tx.commit();
            }
        }

        assertEquals(3, ai.get());
    }

What's Next

That's all for this week. In the next instalment I will cover conflict detection and resolution, transaction granularity and concurrency control.

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.

Wednesday, November 8, 2017

Software Transactional Memory for the Cloud at µCon London 2017

I have just returned from µCon London 2017: The Microservices Conference. I was down there talking about our Software Transactional Memory implementation which our regular readers may recall was first introduced by Mark back in 2013. In particular I wanted to show how it can be used with the actor model features of Vert.x and the nice scaling features of OpenShift.

I have put the presentation slides in the narayana git repo so feel free to go and take a look to see what you missed out on. You can also access the source code I used for the practical demonstration of the technology from the same repository. The abstract for the talk gives a good overview of the topics discussed:

"Vert.x is the leading JVM-based stack for developing asynchronous, event-driven applications. Traditional ACID transactions, especially distributed transactions, are typically difficult to use in such an environment due to their blocking nature. However, the transactional actor model, which pre-dates Java, has been successful in a number of areas over the years. In this talk you will learn how they have been integrating this model using Narayana transactions, Software Transactional Memory and Vert.x. Michael will go through an example and show how Vert.x developers can now utilise volatile, persistent and nested transactions in their applications, as well as what this might mean for enterprise deployments."

And the demo that this abstract is referring to is pretty trivial but it does serve to draw out some of the impressive benefits of combining actors with STM. Here's how I introduced the technology:

  1. Firstly I showed how to write a simple Vert.x flight booking application that maintains an integer count of the number of bookings.
  2. Run the application using multiple Vert.x verticle instances.
  3. Demonstrate concurrency issues using parallel workloads.
  4. Fix the concurrency issue by showing how to add volatile STM support to the application.

I put the without STM and the with STM versions of the demo code in the same repository as the presentation slides.

I then showed how to deploy the application to the cloud using a single-node OpenShift cluster running on my laptop using Red Hat's minishift solution. For this I used "persistent" STM which allows state to be shared between JVMs and this gave me the opportunity to show some of the elastic scaling features of STM, Vert.x and OpenShift.

Sunday, June 26, 2011

STM Arjuna

I'd forgotten how long ago I'd promised to talk about some of the STM work we've been doing. Well I haven't been able to do much more on it for a while, but I do have time to at least outline what's possible at the moment. So let's just remember a bit about the current way in which you can use JBossTS to build transactional POJOs without the need for a database; we'll use an example to illustrate:
  public class AtomicObject extends LockManager
{
public AtomicObject()
{
super();

state = 0;

AtomicAction A = new AtomicAction();

A.begin();

if (setlock(new Lock(LockMode.WRITE), 0) == LockResult.GRANTED)
{
if (A.commit() == ActionStatus.COMMITTED)
System.out.println("Created persistent object " + get_uid());
else
System.out.println("Action.commit error.");
}
else
{
A.abort();

System.out.println("setlock error.");
}
}

public void incr (int value) throws Exception
{
AtomicAction A = new AtomicAction();

A.begin();

if (setlock(new Lock(LockMode.WRITE), 0) == LockResult.GRANTED)
{
state += value;

if (A.commit() != ActionStatus.COMMITTED)
throw new Exception("Action commit error.");
else
return;
}

A.abort();

throw new Exception("Write lock error.");
}

public void set (int value) throws Exception
{
AtomicAction A = new AtomicAction();

A.begin();

if (setlock(new Lock(LockMode.WRITE), 0) == LockResult.GRANTED)
{
state = value;

if (A.commit() != ActionStatus.COMMITTED)
throw new Exception("Action commit error.");
else
return;
}

A.abort();

throw new Exception("Write lock error.");
}

public int get () throws Exception
{
AtomicAction A = new AtomicAction();
int value = -1;

A.begin();

if (setlock(new Lock(LockMode.READ), 0) == LockResult.GRANTED)
{
value = state;

if (A.commit() == ActionStatus.COMMITTED)
return value;
else
throw new Exception("Action commit error.");
}

A.abort();

throw new Exception("Read lock error.");
}

public boolean save_state (OutputObjectState os, int ot)
{
boolean result = super.save_state(os, ot);

if (!result)
return false;

try
{
os.packInt(state);
}
catch (IOException e)
{
result = false;
}

return result;
}

public boolean restore_state (InputObjectState os, int ot)
{
boolean result = super.restore_state(os, ot);

if (!result)
return false;

try
{
state = os.unpackInt();
}
catch (IOException e)
{
result = false;
}

return result;
}

public String type ()
{
return "/StateManager/LockManager/AtomicObject";
}

private int state;
}
Yes, quite a bit of code for a transactional integer. But if you consider what's going on here and why, such as setting locks and creating potentially nested transactions within each method, it starts to make sense. So how would we use this class? We'll let's just take a look at a unit test to see:
     AtomicObject obj = new AtomicObject();
AtomicAction a = new AtomicAction();

a.begin();

obj.set(1234);

a.commit();

assertEquals(obj.get(), 1234);

a = new AtomicAction();

a.begin();

obj.incr(1);

a.abort();

assertEquals(obj.get(), 1234);
But we can do a lot better in terms of ease of use, especially if you consider what's behind STM: where objects are volatile (don't survive machine crashes). And this is where our approach comes in. Let's look at the example above and create an interface for the AtomicObject:
public interface Atomic
{
public void incr (int value) throws Exception;

public void set (int value) throws Exception;

public int get () throws Exception;
}
And now we can create an implementation of this:
@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 incr (int value) throws Exception
{
state += value;
}

@TransactionalState
private int state;
}
Here we now simply use annotations to specify what we want. The @Transactional is needed to indicate that this will be a transactional object. Then we use @ReadLock or @WriteLock to indicate the types of locks that we need on a per method basis. (If you don't define these then the default is to assume @WriteLock). And we use @TransactionalState to indicate to the STM implementation which state we want to operate on and have the isolation and atomicity properties (remember, with STM there's no requirement for the D in ACID). If there's more state in the implementation than we want to recover (e.g., it can be recomputed on each method invocation) then we don't have to annotate it.

But that's it: the AtomicObject and ExampleSTM classes are identical. So let's take a look at another unit test:
      RecoverableContainer theContainer = new RecoverableContainer();
ExampleSTM basic = new ExampleSTM();
Atomic obj = obj = theContainer.enlist(basic);
AtomicAction a = new AtomicAction();

a.begin();

obj.set(1234);

a.commit();

assertEquals(obj.get(), 1234);

a = new AtomicAction();

a.begin();

obj.incr(1);

a.abort();

assertEquals(obj.get(), 1234);
Think of the RecoverableContainer as the unit of software transactional memory, within which we can place objects that it will manage. In this case ExampleSTM. Once placed, we get back a reference to the instance within the unit of memory, and as long as we use this reference from that point forward we will have the A, C and I properties that we want.

Monday, October 7, 2019

Software Transactional Memory with Quarkus

We have recently contributed a quarkus extension called quarkus-narayana-stm which simplifies the use of STM in your microservices.

It will be available in the 0.24.0 quarkus release. If you would like to experiment with it before this release then you can either take one of the nightly builds or you can build it locally by git cloning the quarkus repo and then run the build. This will add the io.quarkus:quarkus-narayana-stm:999-SNAPSHOT maven dependency to your local maven repository and you may then get started by following the guide. There is also a quickstart that provides a worked example of how to use it in your microservices. The example shows how to manage concurrent accesses to a single counter. More sophisticated usage patterns are the norm but this simple example does give a flavour of how easy it is to manage concurrency with the Narayana STM implementation.

Sunday, February 19, 2012

Optimistic STM

It's been a while since I last posted anything on the Software Transactional Memory work that's been going on in JBossTS (aka Narayana). At that time we had everything in place for you to write transactional applications that have all of the ACID properties or, more common for STM, without the D. Per object concurrency control is done through locks and type specific concurrency control is available. You can define locks on a per object and per method basis, and combined with nested transactions this provides for a flexible way of structuring applications that would typically not block threads unless there is really high contention:

@Transactional

public class SampleLockable implements Sample

{

public SampleLockable (int init)

{

_isState = init;

}

@ReadLock

public int value ()

{

return _isState;

}


@WriteLock

public void increment ()

{

_isState++;

}

@WriteLock

public void decrement ()

{

_isState--;

}


@State

private int _isState;

}


If you recall from previous articles, all but the @Transactional annotation are optional, with sensible defaults taken for everything else including locks and state. And you create and use instances fairly simply:


RecoverableContainer theContainer = new RecoverableContainer();

Sample obj1 = theContainer.enlist(new SampleLockable(10));


However, the locking strategy we had originally was pessimistic. As I mentioned separately:

"Most transaction systems utilize what is commonly referred to as pessimistic concurrency control mechanisms: in essence, whenever a data structure or other transactional resource is accessed, a lock is obtained on it as described earlier. This lock will remain held on that resource for the duration of the transaction and the benefit of this is that other users will not be able to modify (and possibly not even observe) the resource until the holding transaction has terminated. There are a number of disadvantages of this style: (i) the overhead of acquiring and maintaining concurrency control information in an environment where conflict or data sharing is not high, (ii) deadlocks may occur, where one user waits for another to release a lock not realizing that that user is waiting for the release of a lock held by the first."

And the obvious alternative to this approach is optimistic:

"Therefore, optimistic concurrency control assumes that conflicts are not high and tries to ensure locks are held only for brief periods of time: essentially locks are only acquired at the end of the transaction when it is about to terminate. This kind of concurrency control requires a means to detect if an update to a resource does conflict with any updates that may have occurred in the interim and how to recover from such conflicts. Typically detection will happen using timestamps, whereby the system takes a snapshot of the timestamps associated with resources it is about to use or modify and compares them with the timestamps available when the transaction commits."

Therefore, you can probably guess what's just been added to our STM implementation? Yes, that's right ... optimistic concurrency control! In line with the annotation based approach we've used so far, there are now two new annotations: @Optimistic and @Pessimistic, with Pessimistic being the default. These are defined on a per interface basis and define the type of concurrency control implementation that is used whenever locks are needed:

@Transactional

@Optimistic

public class SampleLockable implements Sample

{

public SampleLockable (int init)

{

_isState = init;

}

@ReadLock

public int value ()

{

return _isState;

}


@WriteLock

public void increment ()

{

_isState++;

}

@WriteLock

public void decrement ()

{

_isState--;

}


@State

private int _isState;

}


And that's it. No other changes are needed to the interface or to the implementation. However, at present there is a subtle change in the way in which you create your objects. Recall how that was done previously and then compare it with the style necessary when using optimistic concurrency control:

RecoverableContainer theContainer = new RecoverableContainer();

Sample obj1 = theContainer.enlist(new SampleLockable(10));

Sample obj2 = theContainer.enlist(new SampleLockable(10),

theContainer.getUidForHandle(obj1));


In the original pessimistic approach the instance obj1 can be shared between any number of threads and the STM implementation, along with JBossTS, will ensure that the state is manipulated consistently and safely. However, with optimistic concurrency we need to have one instance of the state per thread. So in the above code we first create the object (obj1) and then we create a copy of it (obj2), passing a reference to the original to the container.

It is likely that the API for this will change soon in order to unify optimistic and pessimistic: the aim is to make it as opaque as possible to the application so you only have to modify an annotation when you want to change the implementation. One other thing that we're considering is changing the implementation dynamically, perhaps based on monitored metrics for contention. But for now that's it and it's all available in the Narayana git repository.

Sunday, September 25, 2011

Workshop on the Theory of Transactional Memory

I'm just back from the WTTM in Rome. It was good to go to the workshop and listen to presentations from the likes of Maurice Herlihy on advances in both hardware and software transactional memory. I was there representing the Cloud-TM work that we're doing in collaboration with others and specifically around Infinispan and JBossTS.

One of my favourite presentations of the event was on the use of pessimistic concurrency control in STM. Most STM approaches use an optimistic approach, which obviously works well in cases where contention is limited. The problem with pessimistic is retaining locks for the duration even when updates are infrequent. However, as the presenter showed, with suitable a priori ordering, pessimistic can be more efficient even in low contention environments. This was good because we tend to use pessimistic concurrency control in JBossTS a lot, and as was shown in the presentation, it's also a lot easier for developers to understand and reason about.

Unfortunately I had to leave the workshop before the last session, so I didn't get to listen to Torvald who represents Red Hat on the transactional C++ working group. That's a shame, because I've been following this effort for several years and know that we may be seeing this in gcc sometime soon. And who knows, maybe EE8 will see some STM eventually.

Monday, December 28, 2015

Software Transactional Memory with WildFly-Swarm

A long time ago (not in a Galaxy Far Far Away!) I wrote about the STM implementation we were adding to Narayana. Over the intervening years this implementation was added to Vert.x and even the RaspberryPi made an appearance! Now whilst the implementation has been available in WildFly for many years, we tend not to mention it because, well, it's not Java EE compliant. However, with the advent of WildFly-Swarm things may be about to change.

Sure, when you're looking at using Swarm it's likely that at least initially you'll be coming at a problem from the perspective of Java EE, but the more you look to decompose your application into constituent (micro) services the more chances there are that you'll also start to look at functionality and frameworks that aren't necessarily just about Java EE. As we've mentioned before, STM is compatible with JTA and JTS transactions as well, as long as you understand what it means to mix-and-match them. Therefore, we've added an example of STM usage within WildFly-Swarm, which hopefully will become part of the mainline Swarm examples eventually. Take a look and give us any feedback, either in the Swarm group/IRC/Twitter or the usual Narayana routes.

Saturday, March 24, 2012

Update to STM API

A few weeks ago we saw details of the new optimistic concurrency control (MVCC) for the STM implementation. At the time I mentioned that the syntax for using pessimistic and optimistic was different and also slightly more verbose than it really needed to be. So there have been a few changes recently and although it's not quite the way I want it, it's worth discussing now to at least give you an idea of where we are going with things. Especially important if you're trying to develop something using the STM and the previous examples as a template, since although all of those original classes and methods continue to exist, they may have moved packages!

I'll assume that you can compare and contrast by looking at the original posting, so here's how you create and use objects now:

Container theContainer = new Container();

Sample1 obj1 = theContainer.create(new SampleImple(10));

Sample1 obj2 = theContainer.create(new SampleImple(10));

Sample1 obj3 = theContainer.clone(new SampleImple(), obj1);

Sample1 obj4 = theContainer.clone(new SampleImple(), obj2);


It no longer matters whether they are @Pessimistic or @Optimistic (the default) - the syntax remains the same. Here we have two objects, obj1 and obj2 and two clones (copies) of them, obj3 and obj4. They're all recoverable (state will not survive a machine crash), since that's the default behaviour for Container, but that can be changed via the constructor which allows for PERSISTENT or RECOVERABLE to be specified at creation time.

You can probably guess where further improvements are going to happen. For a start, the fact that the clone method takes a "blank" instance to copy the state into is necessary, but it would be better if it could be inferred. In addition, there are some state management aspects of the implementation that need tidying up because they currently escape through into the application. However, these are both fairly straightforward aspects to fix and the plan is to get them finalised soon. But for now, these changes should be sufficient to try things out. Enjoy!

Sunday, May 2, 2010

Building transactional applications with JBossTS

Towards the end of last year I mentioned that we've been doing some work on STM around JBossTS. I said I'd give some hints at what we've been doing, but in order to do so we need to cover some background on how you can construct transactional applications with the existing code using Transactional Objects for Java (TXOJ). You'll find a lot more information about what I'm going to summarize in the documentation and various papers.

In order for your applications to possess all of the necessary ACID properties when using transactions, the objects manipulated by those transactions need to be both durable and isolated from conflicts. If your application objects aren't backed by a database, then you have the headache of providing these capabilities yourself, unless you have your friendly neighborhood transactional framework like TXOJ. Without going into all of the details (which really are in the documentation) probably the most important class is StateManager which provides all of the basic support mechanisms required by an object for state management purposes. As with concurrency control, which we'll come on to later, durability is obtained through inheritance.

Objects in TXOJ are assumed to be of three possible basic flavours. They may simply be recoverable, in which case StateManager will attempt to generate and maintain appropriate recovery information for the object. Such objects have lifetimes that do not exceed the application program that creates them. Objects may be recoverable and persistent, in which case the lifetime of the object is assumed to be greater than that of the creating or accessing application so that in addition to maintaining recovery information StateManager will attempt to automatically load (unload) any existing persistent state for the object at appropriate times. Finally, objects may possess none of these capabilities in which case no recovery information is ever kept nor is object activation/deactivation ever automatically attempted.

There are quite a few StateManager methods that your application could utilize directly, but for now the only three that are of immediate importance are save_state, restore_state and type. The first two methods are responsible for saving (or restoring) the state of the object, whereas the last is used when positioning the state in the transactional object store. Now although the store may also be the location of the transaction log, the application object states aren't necessarily maintained within the log. (Yes, I'm glossing over subjects such as activating and passivating objects, but we may come back to those in a later posting.)

With these three StateManager methods defined you could manage the state of your object's within the scope of a transaction. But typically you'll also want isolation, so let's move on to LockManager, which inherits from StateManager. So your application objects can become isolated and durable by having their corresponding classes inherit from LockManager. Although you have to be concerned about the three StateManager methods mentioned earlier, there's nothing else here that you need worry about in terms of overriding or defining. But of course you do need to tell the system when and how to isolate your objects. The primary programmer interface to the concurrency controller is via the setlock operation. By default the runtime enforces strict two-phase locking following a multiple reader, single writer policy on a per object basis. Although lock acquisition is under programmer control, lock release is normally under control of the system and requires no further intervention by the programmer. This ensures that the two-phase property can be correctly maintained. (Again, I'll ignore subjects like type specific concurrency control for this posting.)

But sometimes a picture paints a thousand words, so here's an example:


public class AtomicObject extends LockManager
{
public AtomicObject ()
{
super(ObjectType.ANDPERSISTENT);

state = 0;

AtomicAction A = new AtomicAction();

A.begin();

if (setlock(new Lock(LockMode.WRITE), 0) == LockResult.GRANTED)
{
if (A.commit() == ActionStatus.COMMITTED)
System.out.println("Created persistent object " + get_uid());
else
System.out.println("Action.commit error.");
}
else
{
A.abort();

System.out.println("setlock error.");
}
}

public AtomicObject (Uid u)
{
super(u, ObjectType.ANDPERSISTENT);

state = -1;

AtomicAction A = new AtomicAction();

A.begin();

if (setlock(new Lock(LockMode.READ), 0) == LockResult.GRANTED)
{
System.out.println("Recreated object " + u);
A.commit();
}
else
{
System.out.println("Error recreating object " + u);
A.abort();
}
}

public void incr (int value) throws TestException
{
AtomicAction A = new AtomicAction();

A.begin();

if (setlock(new Lock(LockMode.WRITE), retry) == LockResult.GRANTED)
{
state += value;

if (A.commit() != ActionStatus.COMMITTED)
throw new TestException("Action commit error.");
else
return;
}

A.abort();

throw new TestException("Write lock error.");
}

public void set (int value) throws TestException
{
AtomicAction A = new AtomicAction();

A.begin();

if (setlock(new Lock(LockMode.WRITE), retry) == LockResult.GRANTED)
{
state = value;

if (A.commit() != ActionStatus.COMMITTED)
throw new TestException("Action commit error.");
else
return;
}

A.abort();

throw new TestException("Write lock error.");
}

public int get () throws TestException
{
AtomicAction A = new AtomicAction();
int value = -1;

A.begin();

if (setlock(new Lock(LockMode.READ), retry) == LockResult.GRANTED)
{
value = state;

if (A.commit() == ActionStatus.COMMITTED)
return value;
else
throw new TestException("Action commit error.");
}

A.abort();

throw new TestException("Read lock error.");
}

public boolean save_state (OutputObjectState os, int ot)
{
boolean result = super.save_state(os, ot);

if (!result)
return false;

try
{
os.packInt(state);
}
catch (IOException e)
{
result = false;
}

return result;
}

public boolean restore_state (InputObjectState os, int ot)
{
boolean result = super.restore_state(os, ot);

if (!result)
return false;

try
{
state = os.unpackInt();
}
catch (IOException e)
{
result = false;
}

return result;
}

public String type ()
{
return "/StateManager/LockManager/AtomicObject";
}

private int state;
}


What we have here is a durable and isolatable integer. It may look like quite a bit of code, but there's a lot of defensive programming here to illustrate explicitly some of the problems that could occur. It's left as an exercise for the reader to optimize the code.

However, what you can see here is the use of AtomicAction for creating transactions (which will be automatically nested if there's already a transaction associated with the thread of control) and then the acquiring of the right types of Lock within the application object code (READ or WRITE to signify whether or not the state of the object may be modified). If you consider what's needed to ensure that the object is transactional, including full recovery, there isn't a lot for the developer to do. OK, it's a bit more than you might expect if you're used to EJB3s, for example, but then there's a bit more going on here, and some of it could well be automated (future posting hint). But when you consider that an entire database system was built on this basis back in the early 1990's (OK, the C++ version of the transaction system and not Java, but very little difference), it helps to illustrate the power of the abstractions as well as the implementation.

OK, that's enough for now. In a later post we may go into more detail on some of the concepts mentioned here (or glossed over so far) before heading off into STM land. If there's anything specific you'd like concentrating on then just suggest via the comments.

Saturday, May 16, 2015

XA and microservices

I had a very interesting series of discussions this week with a few friends/colleagues about whether or not transactions are applicable in a microservices architecture. I've already covered these points in an earlier entry so I won't repeat them here. However, several things did come up that are worthy of trying to address, including the assumption that they're too hard to use and too hard to test your application for correctness when using them. Now as I've mentioned in several presentations there are valid concerns like these around using or not using transactions in general, i.e., irrespective of microservices.

These points that came up recently are not just to be dismissed out of hand. Having worked in transactions for almost 30 years I've spent a long time hearing about these and other concerns and trying to address them within our industry with many others from a range of companies. For instance, if you look at what we've achieved within Java EE with CDI annotations for transactions, or within Narayana for similar approaches towards compensations, I think we've come a long way to simplifying how developers can use transactions. The STM work that we've done is also another example of putting transactions into the hands of developers and not requiring them to have to worry even about demarcation or state management.

Testing applications and their use of transactions is obviously important, in just the same way as when adding any component or capability, such as Hibernate, Zookeeper or Map/Reduce. However, once again we've made a lot of progress over the years. Within Narayana we have 1000s of unit and QA tests which not only help to prove the correctness of the underlying implementation but also feed into best practices etc. for developers. Then there's transaction extensions to Arquillian which also help to make testing much easier. Now in terms of proving the correctness of transaction systems we're also pretty safe there (no pun intended). Since transactions have been used in mission critical environments for over five decades there's a large body of associated work to prove the various aspects are correct for implementations and applications. Overall I'm fairly sure that these concerns have been addressed sufficiently well to not be a meaningful reason to avoid transactions, in general or with microservices. As an industry could we do better? Of course. We should always be striving to improve the overall developer experience and how transactions fit in.

However, one other thing which did come up during the conversations I referred to initially is that there's often a concern that transactions equals XA, or put another way, that the problems perceived to be present with transactions are the result of design issues inherent within the original XA protocol and associated implementations. As I keep saying, 2PC and transactions existed before XA and many transaction system implementations aren't tied to only supporting the XA protocol. All of the transaction standards have their quirks, just as most standards do no matter their domain. But XA has a few more than its fair share, often due to the fact it's been around for so long that the ways in which we tend to develop applications have changed radically since it was defined. Yet we're tied to it due to the fact that relational databases still represent the majority way in which we store data. Of course we've developed new transaction standards which don't rely upon XA, such as OTS or WS-AT, but most developers still see and use XA and hence why there may be concerns about transactions which are really more to do with XA.

Back to the topic: I'm not suggesting that XA doesn't have a role to play within microservices and between microservices. At least in the short term it most definitely does for some (small) set of applications. But really when I'm suggesting transactions have a role I'm looking well beyond XA. I'm still considering ACID as well as compensation transactions though because as I've said, some implementations aren't tied to XA and yet provide the optimisations you expect from XA (one-phase commit, read-only optimisation) as well as going beyond (interposition, nested transactions). So whilst I definitely understand concerns about transactions, I still believe they either have been addressed and we need to do a better job of developer education, or we can address them; the benefits they bring are worth it.

Friday, April 6, 2012

Transactions and parallelism and actors, oh my!

I wanted to cross-post an article I just wrote on the benefits of transactions in a massively parallel environment (not just a distributed system), not just because some may find it interesting in itself, but also because it references some of the work we've been doing in the project over the past decades. I'm hoping that as we do more work on STM and mobile transactions, to name but two areas of work, that we'll bring some of the techniques that were written about 20+ years ago to a new audience and maybe out of the realm of research. We have got a great transaction system and team here that is second to none. I'm convinced that just as Arjuna pushed the boundaries of standards and adoption of transactions for the past decades, so too can JBossTS/Narayana into the future.

Sunday, May 23, 2010

Nested transactions

Here's another periodic entry on our road to discussing the STM work we're doing around JBossTS. We're going to build on the Transactional Objects for Java toolkit that I described before. But before doing so it's worth stressing that everything I'm talking about here is based on the ArjunaCore module, so it doesn't need an ORB or SOAP stack. In fact it doesn't actually need anything from an application server, whether or not that is J(2)EE based. So yes, you could take that module and embed it within your application, framework or whatever and gain full ACID transaction capabilities.

Hopefully if you're reading these entries then you already understand the concept of traditional transactions, which we'll refer to as top-level transactions here. If not then there are enough articles, presentations and books to get you there, and the JBossTS documentation will help too. But suffice it to say that with a top-level transaction, you get the all-or-nothing (Atomic) affect: no matter how many resources are updated within the scope of that transaction, if one of them commits the work then all of them will and likewise, if one of them rolls back the work then all of the others will also roll back their work.

Now this is a great capability to have, especially if you have to do very little as a user to get there. However, for some applications and objects it can be restrictive. For instance, if your transactions last for a long period of time (say minutes), updating different resources throughout, and just at the very end there's a failure, then everything has to be done again. This and other limitations with top-level transactions lead us and others to look at various extended transaction models, and one of the earliest approaches was nested transactions from 1981, also known as subtransactions (not to be confused with subordinate coordinators).

Transactions which are contained within the other transaction are said to be nested (or subtransactions), and the resulting transaction is referred to as the enclosing transaction. The enclosing transaction is sometimes referred to as the parent of a nested (or child) transaction. A hierarchical transaction structure can thus result, with the root of the hierarchy being referred to as the top-level transaction. An important difference exists between nested and top-level transactions: the effect of a nested transaction is provisional upon the commit/roll back of its enclosing transaction(s), i.e., the effects will be recovered if the enclosing transaction aborts, even if the nested transaction has committed.

Subtransactions are a useful mechanism for two reasons:


  • fault-isolation: if subtransaction rolls back (e.g., because an object it was using fails) then this does not require the enclosing transaction to rollback, thus undoing all of the work performed so far.

  • modularity: if there is already a transaction associated with a call when a new transaction is begun, then the transaction will be nested within it. Therefore, a programmer who knows that an object require transactions can use them within the object: if the object’s methods are invoked without a client transaction, then the object’s transactions will simply be top-level; otherwise, they will be nested within the scope of the client’s transactions. Likewise, a client need not know that the object is transactional, and can begin its own transaction.



Imagine what you could do with nested transactions, particularly if you get to control them programmatically. You can start transactions within your application or objects without having to be concerned about whether or not there are transactions already in scope. If you do know and care, then you can utilize them for application needs. For instance, nested transactions give you the ability to try alternate paths in a decision tree without having to worry about the impact on transactional resources; you try each option in a separate nested transaction and roll back the paths you don't chose, relying on the transaction system to undo that work automatically, and then you simply commit the selected path (nested transaction).

So how do you use them? Well in JBossTS there are no special constructs for nesting of transactions: if a transaction is begun while another is running on the thread then it is automatically nested. This allows for a modular structure to applications, whereby objects can be implemented using transactions within their operations without the application programmer having to worry about the applications which use them, i.e., whether or not the applications will use transactions as well. Thus, in some applications those transactions may be top-level, whereas in others they may be nested. Objects written in this way can then be shared between applications and JBossTS will guarantee their consistency. If a nested transaction is rolled back then all of its work will be undone, although strict two-phase locking means that any locks it may have obtained will be retained until the top-level transaction commits or rolls back. As mentioned before, if a nested transaction commits then the work it has performed will only be committed by the system if the top-level transaction commits; if the top-level transaction aborts then all of the work will be undone.

What all of the above means is that if you want to use nested transactions in your applications then all you need to is start them programmatically as we saw earlier. So for example, if we wanted to use the AtomicObject we defined earlier within a top-level transaction, the code could look like:


AtomicAction A = new AtomicAction();
AtomicObject obj = new AtomicObject();

A.begin();

obj.incr(1);

A.commit();


The fact that the AtomicObject incr method uses a transaction internally is hidden from the invoker. In this case it'll be nested. If the code looked like ...


AtomicObject obj = new AtomicObject();

obj.incr(1);


... then the transaction would be top-level. Hopefully the differences in behaviour are pretty obvious by now, but just in case: in the former case we get a chance to undo the increment if we were to roll back the enclosing transaction, whereas in the latter, assuming the incr method didn't fail, then there is no opportunity to roll back; we would have to do an application driven undo which isn't guaranteed to be equivalent from an ACID perspective (e.g., another user could have seen the state between the increment and the decrement). And of course that leads us towards compensating transactions, but they are definitely out of scope for this article.

So I hope I've given you a flavour to nested transactions and some of the power they could bring to applications and objects. Why not give them a go?

Tuesday, May 29, 2012

A transactional Android example

OK, so as I announced a couple of days ago, the initial code that allows transactional applications to be written on Android is available. There's even an example demo. Therefore, I thought I'd go through the application and its associated configuration in case people wanted to give it a try in its current state. Also rather than try to address all of the idiosyncrasies of the various Android handsets out there, we'll focus on using the Eclipse emulator this time around.

OK, so let's get started. I'll assume that you've checked out the source for the example (it comes with everything you need, so although you can also check out the source for JBossTS, it's not strictly necessary). As a result, you should have something that looks like:


Now before we take a look at the application code, it's important to remember that we're trying to add transactions to these applications. I won't reiterate all of what this means (I'll leave it as an exercise to the reader to either look through the other articles in this blog or check out transactional resources elsewhere). However, for the purposes of this entry it's important to remember/realise that a transaction needs a durable location where it can store the log to be used in the event of a failure and recovery. In the case of Android, we'll assume this is on an SD card somewhere. In terms of the application and the Android emulator, this has an impact. First, for the emulator we need to create an Android Virtual Device (AVD) and ensure it has an emulated SD card. You can do this easily using the AVD creator:


As you can see, in this case we've selected 512 Meg for the storage. That should be plenty of room for quite a few transactions, especially as most of the time the logs will be created and almost immediately deleted. So you don't have to go overboard with the amount of storage allocated.

Now for the application we need to ensure that it is allowed to access the SD card (external storage). We do that by editing the AndroidManifest.xml:


In this configuration we also need to allow the application to access the internet. This is because JBossTS needs to access the IP address of the device in order to create unique transaction identifiers. We'll look at whether this is something we can change later, but it shouldn't be an issue at this stage.

We're now ready to look at the application. If you've read this blog for a while then you'll know about ArjunaCore and the transactional toolkit within. Not only is it the basis for the work we're doing on STM, but we're using it here to create our application. I won't go into details, because the transactional object is your standard transactional int AtomicObject derived from LockManager and providing operations to read and write the state. It uses nested transactions as well!

So how do we use instances of this transactional int?


Pretty simple. As you can see above, we simply create a new transaction via AtomicAction; create an instance of the AtomicObject; start the transaction and modify the state of the object before committing the transaction. If all goes according to plan, when you deploy this application and run it, you should first see something like:

And then see output similar to below (your transaction id will be different):


That's it! Fairly simple and straightforward. At some point in the hopefully not-too-distant future, I'll get the JBossTS code changes into the mainline code in github and look at a more complex application. This one doesn't cover recovery for instance, and I'd like to more tests. But for now it shows what's possible and the direction in which we're heading. Enjoy!

Tuesday, December 23, 2014

Announcing the release of Narayana 5.0.4

I am very pleased to share with you the news that the release of Narayana 5.0.4 is now officially available from all our usual outlets and just in time for Christmas!

This release of Narayana contains a phenominal amount of work from the team. As well as the usual improvements to performance and usability that can be expected of our Narayana 5.x series, it is also wraps up several new features that you might have heard us talking about here on the blog or over in the community forums over the last year or so: the Narayana Transaction Analyser (NTA), Compensations, STM and running the transaction manager in interesting places such as Docker, Android etc being chief among them. The full release notes for Narayana are over here. The release notes for 5.0.4 itself are over here.

You can get it:
  • As a distribution format from our website
  • Within the latest nightly builds of WildFly
  • From the JBoss.org Maven repository

All of our contact details are available on the community page and we look forward to hearing what you think of our project over on IRC or our forums.

Have a great 2015!
Tom

Saturday, December 19, 2009

STM for Arjuna

For many years we've discussed the benefits of object-oriented programming for developing transactional applications, with Transactional Objects for Java (TXOJ) as the implementation of those ideas. As we've discussed before, this ties in with other advanced techniques such as nested transactions.

However, in order for developers to use TXOJ you have to use inheritance (remember: object-oriented principles) and modify your classes, whether C++ or Java. These days POJOs (such an overloaded term as well) are the preferred way to develop applications, acquiring enterprise capabilities through the likes of annotations and AOP. So a few months back we started to look at other approaches to TXOJ with some of the researchers at Newcastle University (the historical home of the Arjuna Project). We also looked at Software Transactional Memory and have managed to tie the two requirements together. It's too early to report on everything we've been doing (it's far from complete just yet anyway), but I hope to give some hints in some future postings.

Saturday, March 12, 2011

Transactions and TorqueBox

About a year ago Bob McWhirter and I were talking about TorqueBox and the subject of transactions came up. Bob and team are doing a great job of exposing the enterprise capabilities that Java developers take for granted to the Ruby community, so once they'd added messaging I think transactions were the next obvious step. Since I was looking for an opportunity to improve my Ruby skills and Bob was looking to add transactions, I think it was definitely a Casablanca moment.

We scoped out the requirements for transactions, both short term and long, using JBossTS within JBossAS. Short term was pretty simple: just adding the ability to start and end transactions, with the assumption that the underlying components written in Java, such as a messaging system, would use any transaction context associated with the thread to "do the right thing" as far as transactional resources are concerned. So really this is no more than adding a Ruby client interface to something like UserTransaction in the JTA. Useful, but limiting. That work was done and should hopefully be coming to a TorqueBox release near you soon.

Longer term we want to give the Ruby developer full access to a toolkit to write transactional applications from the ground up. That means writing transactions objects and participants, not just being able to start or stop transactions. And because the Ruby community isn't as tied to the legacy of XA, we don't need to mandate XAResources, top-level transactions only or two-phase commit. Now this is where it really gets interesting. I'm hoping to use a lot of the underlying ArjunaCore implementation (and possibly the STM implementation too, which reminds me that I need to finish the blog entries around that soon). I want to provide something that is incredibly simple and yet also incredibly powerful if you want to really get into the details.

I like the way that the TorqueBox team have architected their system and I like the opportunities that it and Ruby in general provides. Transaction support should be something that fits into that ethos and I think that having a relatively clean slate, which we don't really have with Java, is a good thing. It's almost back to the old days when we were writing Arjuna in C++, which at that time hadn't even been released officially! Happy days ahead!

Wednesday, May 24, 2017

Narayana 5.6.0.Final released

The team are pleased to announce our latest release of Narayana - the premier open source transaction manager. As normal, the release may be downloaded from our website over here:
http://narayana.io/

The release notes for this version may be found over here:

This release brings with it a collection of improvements including the latest patches and feature work to improve our integration into Tomcat. The best way to get started with that is to take a look at our new quickstarts:

We also added an interesting Software Transactional Memory and vert.x example over here:

We have also managed to get CORBA JTS interop propagation working with Glassfish. You can read more about that in https://issues.jboss.org/browse/JBTM-2623. However validating recovery completes correctly is still in progress so stay tuned for further details in the coming releases...