Tuesday, March 8, 2011

Must Go Faster

Transaction management systems, like much other middleware, embody a very simple tradeoff: they make life easier for developers at the cost of a runtime overhead. Maximising the ease of use and minimising the overhead are things we spend a fair bit of time thinking about.

Conventional wisdom in the transaction community is that the runtime cost of a classic ACID transaction is dominated by the disk writing needed to create a log for crash recovery purposes. As with much folklore there is some truth in this, but it's not the whole story. For starters, there are some use cases where we don't need to write a log at all.

In transactions that have only one resource it's easy to optimise the 2PC protocol to a 1PC and avoid much of the overhead. But here is the snag: the design of the transaction API in Java EE does not allow for developers to communicate metadata about number of resource managers expected in the transaction ahead of time. In some cases it's not actually known, as it may depend on runtime data values. However, it's disappointing that you still need some parts of the XA protocol overhead even where the transaction is known at design time to be local (native) to a single RM.

xaresource1.start();

xaresource1.end();

xaresource1.commit();


is still three network rounds trips and one disk sync. That's a huge improvement on the eight trips and three syncs needed for a 2PC, but it's nevertheless more than the single trip and sync in the native case with

connection.commit();

Fortunately there is a workaround: define both XA and non-XA ('local-tx' in JBossAS -ds.xml terminology) datasources in the app server and use the local one wherever you know the transaction won't involve other RMs.

Perhaps one day we'll have a less clunky solution - maybe being able to define a single datasource as supporting both XA and non-XA cases, then annotating transactional methods with e.g. @OneResource or @MultiResource to tell the JCA and TM how to manage the connection. Or even being able to escalate an RM local tx to an XA one on demand rather than having to chose in advance, although that would need RM support as well as changes to the XA protocol and Java APIs. Dream On.

Even where it's running with 1PC optimization for a single RM, the transaction manager still provides benefits over the native connection based transaction management. The most critical is the ability to handle certain lifecycle events, in particular beforeCompletion(), a notification phase that allows in-memory caches such as an ORM session to be flushed to stable store and its companion afterCompletion() which allows for resource cleanup. The TM's ability to manage transaction timeouts is also important to prevent poorly written operations from locking up resources for a protracted period.

As with writing logs for 2PC recovery, the management of timeouts is one of those activities we have to do every time, even though it turns out to be required in only a tiny minority of cases. Efficiently managing the rollback of transactions that have exceeded their allotted lifetime is a seemingly trivial overhead compared to the log write and as a result the code for it received little attention until fairly recently. This is where the folklore came to bite us: conventional wisdom dictated that we should focus the performance tuning effort on the I/O paths and not worry too much about functions that just operated on in-memory structures.

WRONG.

For the reasons outlines above, in a typical app server workload there are an awful lot of transactions containing just a single resource and hence not doing a log write. D'Oh. For those use cases the overhead of the in-memory activity in the TM is actually significant. So we sat down, wrote a highly concurrent 1PC microbenchmark test scenario, put it though a profiler, shuddered and went down the pub.

When we'd recovered a bit we tuned the transaction reaper, a background process responsible for timing out transactions. By deferring much of the work as long as possible it turns out to be possible to skip it entirely for many transactions. A short lived tx does not always need to be inserted into the time ordered reaper queue - it may terminate normally long before the reaper needs to take any action. By being lazy we saved a lot of list sorting and the associated locking overhead.

As a result the recent JBossTS releases substantially outperform their predecessors in the single resource case, particularly when scaling to a large number of threads. Upgrade and Enjoy.

Next time: More Speed! Very fast I/O for 2PC logging.

Hello World

So apparently there is this thing called 'blogging', which seems to consist of equal parts narcissism and self-publicity. As far as I can tell the aim of the geek's version of this game is to make yourself insanely attractive to hiring managers who need to fill obscure technical vacancies. Oddly enough this activity not only counts as work, but is actively encouraged by management. Sign me up!

What's that? You want actual content? Oh, umm, right then. Well let's start with something simple shall we? Maybe I should introduce myself. I'm Jonathan Halliday, dev team lead for JBossTS. I spend my days working on transactions code, trying to remove more bugs than I add. After ten years or so I think I'm gradually getting the hang of it, which probably means it's time to find a new challenge. Meanwhile I'll try to spend a little more time communicating about the cool stuff I'm doing instead of just getting on with it. Starting, in the next exciting episode, with some impressive performance improvements. Because hey, who doesn't like faster code, right?

Thursday, February 17, 2011

Jonathan on Memory Resident Transactional Objects

I came across this gem from the JBossTS project lead. Well worth a read. It also reminds me that I need to write up some of the stuff I did over Christmas. Now where did I put that spare hour or so?

Monday, November 22, 2010

HAL FUD rebuttal

A while ago I got rather annoyed at the shoddy work that IBM did at FUD around JBossTS. I later said that we were going to produce a response (as if one was really needed after 25 years of work, but hey, this is the Youtube generation!) Well it's taken a little longer than I had expected, but it's now available. And unlike the original IBM mess, this demo includes a reference to the source code so you can download it and try it for yourself. I'd recommend to IBM that they give it a go and perhaps re-examine their own tests as a result, but I doubt they're interested in facts and the truth.

Thanks the the entire team for pulling this together! It was a great JBoss effort.

Wednesday, November 10, 2010

Friday, October 29, 2010

Standalone XTS and lightweight clients

I've recently been working with a community user to help restore the ability to deploy the Web Services Transactions (XTS) code outside of JBoss AS. XTS is a layer built on top of the JBoss Transactions core which allows you to implement transactional web services and transactional web service clients. Those long enough in the tooth to know about the previous incarnation of the XTS code when it was part of the Arjuna codebase will recall that in the good old, bad old days XTS had to be deployed bundled in with the web service client and/or the web service itself. When we re-implemented XTS to conform to the WSTX 1.1 standard we rebuilt it so that it deployed as a service inside JBoss AS. We also relaid the internal message transport on new foundations. Communications between the client or web service participants and the transaction coordinator were rebuilt over the JaxWS implementation provided by JBossWS-Native. More recently we upgraded XTS so that it also works with JBossWS-CXF.

Although this decision appears to tie XTS tightly into the JBoss AS product it was actually motivated by the desire to render the code independent of a specific transport layer. In theory JaxWS provides an abstraction that is available in many implementation flavours and, in theory, this means that it should be possible to deploy the 1.1 implementation in any container that
  • provides support for deployment of JaxWS endpoints on the server side and
  • allows for creation of JaxWS service proxies on the client side
That's pretty much all that is needed to allow XTS to run in any web service container, except ... it ignores a significant lacuna in the JaxWS specifications.

XTS requires the use of Web Service Addressing (WSA) headers on the protocol messages sent between the client and transaction coordinator or between the participant web service and the coordinator. The original draft JaxWS-WSA specification defined an API which enabled the two sides of a JaxWS exchange to access and manipulate WSA headers. However, this feature got shunted off into the long grass and the final version only provided a minimal WSA capability which made it impossible to implement the WSTX spec using only JaxWS standard functionality.

Luckily for me, JBossWS Native and the CXF project have both always provided an implementation of the original draft WSA feature and, modulo a few annoyingly variations (that's the problem with not standardising), this means that both these WS stacks include all that is needed to deploy the XTS service. JBossWS now includes a small library containing a simple abstraction layer which hides the minor differences in the WSA implementations. That is all that is needed for XTS to deploy inside JBoss AS over either JBossWS-CXF or JBossWS-Native. However, this also re-opens the possibility of bundling the XTS code with the JBossTS code and CXF as a standalone deployment.

Obviously, there is not a great deal of benefit in deploying a transactional web service outside of an application server. It's very likely that you will want such a web service to run inside a container located in a managed environment with 24/7 monitoring, support and so on. A web service is likely to need to employ a database and require configuration of data backups, crash recovery, centralised logging, IT staff on hand to deal with catastrophic failures etc.

Deployment of a lightweight transactional web service client is a much more interesting proposition. If you can bundle the transactional web service client software in a simple desktop Java application then this provides an easy management path for exposing reliable distributed service applications to users. The desktop client negotiate with a networked coordinator service when it creates a transaction. It can use normal web service invocations to request service from transactional web services located in the intranet or even on the internet. The web services negotiate with the same coordinator to ensure that they either all commit their changes or all revert to their original state. The more heavy-weight apparatus needed on the coordinator or web service hosts to ensure reliability, availability, security etc are not actually needed on the client node.

In theory all the transactional client needs to be able to do in order to participate in a Web Service Transaction is talk to the coordinator and the web services using JaxWS client capabilities built over an HTTP client library. There is no reason for the client to require all the capabilities of a full web service container and if definitely does not need to impose the management overheads that running a full container would mandate. The client only requires minimal installation, configuration and maintenance to operate as an HTTP client. It does not need to be involved in automatic or manual crash recovery procedures. It does not need to take part in centralised logging in order to allow monitoring/supervision by IT support staff. Nor does the client need to expose service endpoints on a public network interface and, in consequence, open holes in the client machine's firewall. Bundling the CXF client libraries with the XTS client code in a standalone Java app seems like a very attractive proposition.

In practice things are not quite so simple as that which is why I have been busy for the last month or two. The practical problems were twofold. The first problem was that XTS was built to be deployed as a monolith. When you install the XTS service inside JBoss AS you actually install the code required by all 3 of the agents involved in a transaction: the client, the participant web service and the transaction coordinator. The XTS code actually comprises about half a dozen main software services with their associated web services. These software services are layered so as to support the functions defined by the WSTX specifications. Unfortunately, the needs of the three agents, client, web service, coordinator, cut across these layers making it hard to decouple them.

So, when it comes to deploying a transactional application you might decide that you want to deploy your client application to one JBoss AS instance on Host1 and your web services on two other JBoss AS instances running on Host2 and Host3. For better reliability you might want to the coordinator services to operate in a separate JBoss AS instance on Host4. Unfortunately, up until a few weeks ago you would have to deploy, load and initialise the XTS services for all three types of agent on all of these hosts.

Well, not any more. There is now a simple configuration mechanism which allows you to enable or disable the client, participant web service or coordinator capabilities in a given XTS deployment. There is still a single XTS service archive to drop into the JBoss AS deploy directory. However, you can easily configure the deployment using either dependency injection or file-based property configuration to ensure that only the code and services you want get loaded and initialised. So, you can configure Host1 only to initialise the client capability, and so on.

If you want to deploy to a non-JBoss web container it is now also straightforward to isolate the configuration elements which control deployment of the JaxWS endpoints used internally by XTS. The XTS service archive contains several web archives which do nothing more than map endpoints exposed by the 3 XTS agents. These can easily be unbundled and the mapping can be redefined using an alternative deployment framework like, say, Spring. All you need to do to deploy XTS outside JBoss AS is to bundle CXF, the XTS and JBossTS libraries and the JBossWS WSA compatibility library with your deployment, configure which XTS agents you want to initialise then call the JBossTS and XTS start and stop methods during deployment and undeployment of your application.

This is all you need if you want to deploy to a web container. Unfortunately there is still a second barrier to deploying a simple, lightweight transactional client. This is a harder problem because it lies within the WSTX standard itself. Unfortunately, the WSTX specification, or at least the WSAT component (which specifies the Atomic Transaction protocol), requires a transactional client to expose a service endpoint. WSTX bypasses this issue as far as the WSBA (Business Activity) protocol specification is concerned, but only because it leaves the corresponding part of the WSBA specification undefined! In practice the same requirement applies for WSBA.

This may seem a bit bizarre but it's a rather unfortunate artefact of how WSTX was defined. I guess that at the time WSTX was defined it was felt that anyone implementing a transactional client would only want to deploy it inside an application server. So, this was not considered to be an issue. In order to explain why the spec is like this and what is required to fix it I'll need to go into some of the details of what the spec actually requires a client implementation to do.

The problem does not arise when an application client starts a transaction, either by calling UserTransaction.begin() or UserBusinessActivity.begin(). Under the hood these two calls use JaxWS to talk to an XTS service called the Activation Coordinator. This service employs a request-response message exchange pattern (MEP). The client opens an HTTP connection and sends a create request. The service creates a transaction and uses the HTTP back channel to hand back a transaction context containing a unique identifier for the transaction and the address of another coordinator service called the Registration Coordinator. This is the same information which gets smuggled into client requests to the transactional web services, allowing them to talk to the Registration Coordinator and register as a participant in the WSAT or WSBA transaction (although, confusingly, the standard term used for this operation is enlist).

The application client does not just stash away the context returned by the Activation Coordinator and then return fro the begin() call. It also registers (or enlists) as a participant in the transaction. Although it uses the same operation as the web services it does so for a different purpose. A transactional web service has to enlist so that it can be told when to prepare and commit (or, possibly, abort) any changes made during the transaction. The client has to enlist in order to be able to complete the transaction. In a WSAT transaction the client sends a message to the Registration Coordinator to enlist with the WSAT Completion Coordinator service. This allows it to send a termination request, either Commit or Rollback, later on. Once again the enlist operation uses a request-response MEP but this time the response on the back channel includes the address of the Completion Coordinator service. Likewise, a WSBA client enlists with the Termination Coordinator service and gets back that service's address.

The problem arises when the application client code calls UserTransaction.commit() or UserBusinessActivity.close() to terminate the transaction. The XTS code uses JaxWS to send, respectively, a Commit or Close request to the Completion or Termination Coordinator service. Alternatively, if the application client code calls UserTransaction.rollback() or UserBusinessActivity.cancel() the XTS code uses JaxWS to send, respectively, an Abort or Cancel request. Unfortunately, in all of these cases the WSAT specification mandates that this request empoloys one-way MEP. That means that the client sends the request then closes the connection without waiting for a response. So, how is it supposed to find out when the request has been handled and what happened?

Well, that is why the client needs to expose a JaxWS endpoint. The WSAT client has to implement a Completion Initiator service which will accept incoming messages using a one-way MEP, either a Committed or an Aborted message. After posting , say, a Commit request the XTS code places the calling thread in a wait until one or other messages is received by the Completion Initiator service. This service wakes up the waiting thread and notifies it of the outcome. So, what is essentially a request-response exchange is achieved using two independent messages on two independent HTTP connections one initiated from the application client to the coordinator host and one initiated from the coordinator host back to the client. The WSBA specification does not actually specify a termination protocol but the old Arjuna implementation adopted a similar model based on two one-way messages thus requiring a WSBA client also to expose an end point, in this case for the Termination Participant service.

So, how does the Completion or Termination Coordinator get hold of the address for the client endpoint? Well, the answer to that question really explains how and why this problem comes about. The enlist request supported by the Registration Service expects to be passed three values, a unique identifier for the enlisting party derived from the transaction id, the name of the service the caller wants to enlist for and, finally, a URL providing the missing address. Once the registration request has completed both sides have each others addresses. The protocol expects them to conduct any further communications using a sequence of one way messages labelled with the unique identifier supplied in the enlist call.

One way messages are not a bad idea for communications between the coordinator and the participant web services. Using a request-response MEP requires holding open a connection while waiting for the response. For, say, a Prepare-Prepared exchange this might take a long time, especially if a web service has made a lot of state changes which have to be logged to disk. Holding open lots of connections in a busy web server might cause a resource bottleneck. Also, request-response is only useful if the exchange is always a simple two-way exchange. WSBA sometimes requires longer message sequences not easily mapped onto the request-reponse model. For example, if a WSBA web service is told to complete its changes and finds that it cannot do so the 3-way message exchange, Complete, CannotComplete, NotCompleted is required.

At the client the first of these concerns is less important (there is normally only one connection per client) and the second does not apply. If the completion requests could be implemented using a simple request-response exchange then this would remove the need to expose service endpoints and, hence, allow deployment of a really lightweight client. So, the solution we have adopted is to augment the standard and provide an alternative version of the Completion and Termination Coordinator services which support this type of MEP. Our coordinator service implementations exposes two new endpoints alongside the original completion services, the Completion Coordinator RPC service and the Termination Coordinator RPC service. These services have no corresponding Initiator service. Our client implementation can be configured to use either the normal service or the RPC vwersion. So, if you deploy a client to a web container you can configure it to use the standard one-way MEP requests to the standard services. If you deploy a client as a standalone Java app you can configure it to use request-response MEPs to the extended completion services.

Of course, you can only use this lightweight client if you point it at a JBoss XTS coordinator. If you tried to use another vendor's implementation of WSTX then you would get an error when you begin your transaction because the 3rd party coordinator service would not recognise enlist requests for our extended completion services. This is not quite such a leap into the unknown as it appears. WSBA does not specify completion protocol. So, if you try to use another vendor's coordinator service with our WSBA client library it will fail whichever type of client you deploy. That's a very good reason why the WSTX specification should be extended to include a WSBA completion protocol and to add the request-response MEP variants of the completion protocols so they sit alongside the one-way MEP versions.

Sunday, June 6, 2010

Transactions and volatile state

So far we've looked at some of the basic of writing transactional applications using just JBossTS and TXOJ, as well as the importance of nested transactions. In this entry we'll look at another way of relaxing some of the traditional ACID properties, namely that of durability (persistence).

Hopefully you'll realize that normally when using transactions the Durability aspect kicks in with the transaction log, which maintains a record of the participants and the state of the transaction, as well as transactional participants (e.g., business objects), which have to record any state changes that are made in the context of the transaction. Both of these are needed to ensure atomicity in the presence of failures. But although disk speeds have improved significantly over the years, compared to improvements in processor speeds the disk is still the bottleneck. But it needn't be if all you want is ACI for your objects and applications. Such objects are often referred to as volatile or recoverable in that they have state that cannot survive machine crashes but which is still subject to atomic updates, i.e., previous states can be recovered if the transaction rolls back as long as there are no crashes.

But of course it's no good to simply stop your business objects from writing state, assuming you are even aware that they are doing so within a transactional application; you must have similar recognition by the transaction manager so that it doesn't write the log on behalf of volatile objects. Obviously if there are only volatile objects in the transaction then there's no need for a lot at all and performance can improve significantly. But of course you need to understand the trade-offs that using volatile objects give, which can essentially be summarized as performance vs crash recovery.

Fortunately JBossTS and TXOJ support volatile objects, including recognition by the coordinator. You don't need to do anything special when creating or starting your transactions: the coordinator will only create a log during commit if it detects more than one non-volatile object within its scope. And for your TXOJ objects you have to do very little, as illustrated by the modified AtomicObject code below from one of the previous articles:


public class AtomicObject extends LockManager
{
public AtomicObject ()
{
state = 0;
}

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;
}


The only real differences here are that we don't need the constructor call to LockManager with the ANDPERSISTENT flag (the default for all TXOJ objects is that they are recoverable) and it doesn't make sense to have a Uid constructor because there will never be any persistent state for this object, so it can never be recreated later. If you know that the object is only ever going to be recoverable then you may also be able to play tricks with state manipulation during save_state and restore_state calls, which is why there's a second parameter to them, which is either ANDPERSISTENT or RECOVERABLE.

But that's it. With these very minor changes to the code you can create atomic and recoverable objects that have a lot of the benefit of being used within a transaction, including transactional locking, but lacking the overhead of logging. And of course it's possible to mix recoverable and persistent objects in the same transaction seamlessly. But again, there's a price to be paid for turning off persistence. However, for some types of object and application this is a price worth paying, and one of those areas is STM. But it's not alone and you may find the idea of volatile transactional objects appealing for your own needs. In which case give it a go.