Transactional support for JAX RS based applications

Overview

Specification of how to model transactions as RESTful resources

 

This wiki article will discuss two approaches to providing transactional guarantees for updating resources modeled using the REST paradigm.

 

An implementation of the protocol is available in the download section below (or via svn from the private JBossTS workspace repository - svn co https://svn.jboss.org/repos/labs/labs/jbosstm/workspace/resttx). It uses the RESTEasy implementation of the JAX RS specification. The JBossTS transaction manager is employed as the protocol coordinator.

 

We do not discuss whether or not transactions and REST are a good architectural fit suffice to say that there are proponents for both sides of the argument. Certainly it is worth pointing out that if a system cannot be made reliable then it can be of only limited utility. That said it is a worthwhile exercise to show how a REST based system can be made reliable.

 

The two proposals are:

  1. classic transactions obeying ACID properties;
  2. compensation based transactions avoiding the need to lock resources for extended periods of time.

 

Approach 1 is discussed in depth whilst the second is covered in a separate wiki article.

 

Disclaimer: Although the following protocol is viable it was in fact written over 8 years ago by another community member, namely Mark Little. Things have moved on since then and we do plan to re-evaluate the specification.

 

The protocol makes use of three resource types:

  • A participant is an entity responsible for ensuring that changes to a resource can be driven through a 2PC protocol. It must ensure that changes to resources are recoverable in the presence of failures and that changes are durable and isolated from other changes. Participants are analogous to Resource Managers in the X/Open XA specification.
  • The transaction coordinator is responsible for enlisting participants in the transaction and for driving participants through a 2PC protocol.
  • A recovery coordinator provides the mechanism to support recovery after failure of participants. Participants are allowed to change their URLs provided they notify the coordinator.

 

A client is the entity that wishes to manipulate resources within the context of a transaction. It may or may not be modeled as a resource.

Protocol Description

All actors are modeled as URLs, the idea being that doing PUT, DELETE, GET and POST to the various URLs will control how a transaction makes progress and how changes to resources are processed reliably (ie obey ACID properties).

 

  • The transaction coordinator address is <scheme>://<authority>/transaction-coordinator. In the following this address is referred to as TC;
  • The recovery coordinator address is <scheme>://<authority>/recovery-coordinator. In the following this address is referred to as RC;
  • A participant address is unspecified but the semantics of PUT, DELETE, GET and POST to the address are specified. In the following this address is referred to as P-URL.

 

POST is used to create resources, PUT will modify a resource and GET retrieves a resource:

  • a successful PUT returns status code 200 (OK) and any XML data in the body;
  • a successful POST returns status code 201 (created) and a Location header containing the URI of the newly created resource;
  • a successful GET returns status code 200 (OK) and any XML data in the body;
  • DELETE is not used and therefore returns 401 (Unauthorized)

 

URLGETPUTPOST
TCReturn all transactions (1)

TC/recoveryReturn all recovering transactions (2)

TC/activeReturn all active transactions (3)

TC/begin?clientId={id}

Start a transaction (with default timeout) returns url TC/{txid} which is deleted after the timeout or after completion (any HTTP method relating to {txid} thereafter returns 404)
TC/begin?clientId={id}&timeout={timeout}

Start a transaction (with specified timeout) returns url TC/{txid} which is deleted after the timeout or after completion (any HTTP method relating to {txid} thereafter returns 404)
TC/{txid}/commit
Trigger commit (4)
TC/{txid}Return the transaction status (5)

TC/{txid}?P-URL={url}
Enlist url in the transaction returning a unique resource for that participant of the form RC/{RecCoordId}401
RC/{RecCoordId}Return the participant URLReplace the participant URL (7)401
P-URLReturn the status of this participant. A status is any of the statuses returned by prepare and commit

P-URL/prepare400401The participant prepares any work done in the context of the transaction (see note 6).  Returns a status URL indicating the outcome:               
  • P-URL/prepare-ok
  • P-URL/prepare-readonly
  • P-URL/prepare-notok
A 404 response indicates that the participant has rolled back.
P-URL/commit400401The participant commits any work done in the context of the transaction (see note 6).  Returns a status URL indicating the outcome:               
  • P-URL/committed
  • P-URL/rolledback
  • P-URL/heuristic (8)
A 404 response indicates that the participant has already terminated.
P-URL/rollback400401The participant rolls back any work done in the context of the transaction (see note 6). Returns a status URL indicating the outcome:               
  • P-URL/rolledback
  • P-URL/heuristic (8)
A 404 response indicates that the transaction has already terminated.
P-URL/forgetReturns the status if a heuristic exists and 404 otherwise401The participant forgets any heuristic decision

Notes

  1. The body of the response contains an XML document exposing a list of transactions.
  2. A recovering transaction is one that is completing but one or more participants have failed to respond either because of network failure or a server has failed.
  3. An active transaction is one which has not completed and is not recovering.
  4. Upon successful termination, the URL is implicitly deleted. An HTTP OK response means the transaction either committed or rolled back. If the client wishes to discriminate between the two outcomes he must register a participant with the transaction. If some participants committed whilst others aborted then the coordinator remembers this heuristic outcome.
  5. The status is only returned if the transaction is still active or is recovering. The body of the response contains an XML document exposing the status of the transaction.
  6. Participant URLs must be unique with respect to the transaction but is otherwise unconstrained. This URL can be asked to prepare and, if it were not for this restriction, the participant would have no means of knowing which transaction was being prepared.
  7. This operation means that participants can tell the recovery coordinator that they have moved in the event of a failure. Implementation Note: After failure the recovery system will periodically attempt to contact in doubt participants. If the participant has moved it will get HTTP status 404 (Not Found). In this case the recovery subsystem issues a GET on RC/{RecCoordId} if the participant has moved. If it gets a different URL back it will switch over to using the new one.
  8. If the participant has already committed when asked to rollback or vice-versa the participant must remember the decision until told to forget it.

Running the Implementation Prototype

The prototype is available via svn (the recommended approach) or from the download section below.

Unpack the distribution (or check out from svn), make sure you have maven installed, and run the tests:

     mvn clean install

 

The tests start up an embedded servlet container for testing the protocol. To run using a stand-alone container (the distribution uses jetty6, but any container would do) type:

     mvn jetty6:run-exploded

 

To skip the tests pass -Dmaven.test.skip=true mvn (via MAVEN_OPTS).

 

There is also an ant build script which performs a simple test against the jetty container.

 

The best test for the implementation is to run the demo application which includes testing of recovery after failure. Look in the demo directory for a file called go which describes how to run the demo. Some of the RESTful clients are written in Perl so that is a separate prerequisite (the other dependencies are downloaded automatically via maven).

 

You can change logging levels via the log4j script in the file src/test/resources/log4j.xml

 

If you wisht to test recovery without running the demo, edit the test org.jboss.jbossts.rts.testAppTest.test6 and change the crashVM variable to true and run the tests. The VM running the test will halt. Change crashVM variable back to false and start the server. You will see the recovery sub-system replaying the failed transaction. The way I've got things set up at the moment is for the example participant to refuse the the request since it does not persist its work. I might change this in the next update of the prototype.