Adopting, designing, and governing SOA well

SOA Best Practices Digest

Subscribe to SOA Best Practices Digest: eMailAlertsEmail Alerts newslettersWeekly Newsletters
Get SOA Best Practices Digest: homepageHomepage mobileMobile rssRSS facebookFacebook twitterTwitter linkedinLinkedIn


SOA Best Practices Authors: Jason Bloomberg, Hollis Tibbetts, Pat Romanski, Liz McMillan, Lori MacVittie

Related Topics: SOA Best Practices Digest, SOA & WOA Magazine

SOA Best Practices: Article

SOA Patterns: Basic Structural Patterns – Part 2

The Transactional Service Pattern

Another option is to handle messages synchronously. Synchronous operation can prove to be very problematic in terms of performance, especially when the service needs to interact with external services, systems, or resources, as then the whole process has to wait for the other party to react before the service can return its reply. Even more important is the fact that this doesn't really solve the problem. If the service fails anywhere in the process, we don't really know where we are at. The only thing we can know is which message failed and we need the consumer's help for that.

It seems we can solve the problem, if the service will save its state into a persistent storage such as a database. I think that is a step in the right direction; however, we are not safe yet. We can still be in trouble if the service crashes just before persisting its state and the incoming message would still be lost without the service knowing. Another aspect we need to note is that using a persistent storage we can track where in the process a failure occurred - but we can't be sure if messages to other services were sent or not.

To solve these last issues as well as the whole reliability problem we need the Transactional Service.

The Solution
Apply the Transactional Service Pattern and handle all the flow from reading the message to sending out responses and reactions in a single transaction.

The main component of the Transactional Service Pattern is the message pump (see Figure 7). The message pump listens on the endpoint or edge for incoming messages. When a message arrives, the message pump can then begin a transaction, read the message, pass it to some other component/classes to handle the message, and when the processing is finished, wrap the transaction (commit/abort). If it is possible to send replies or requests in a transactional manner, they can also participate in the transaction, otherwise you will need compensation logic if the transaction aborts.

The advantage of a using a transactional programming model is that it's all or nothing semantics, which means you don't need to deal with edge cases. Due to the ACID properties of transactions, all the operations and messages are guaranteed to be either completed or not, so you have a high assurance that if a message left the service, the incoming message that triggered that reaction has been fully handled.

ACID Transactions
A transaction is a complete unit of work. A unit of work is qualified as a transaction if it demonstrates four tenets that go by the acronym ACID:

  • Atomic: Everything that happens in a transaction happens as one atomic unit. Either all the actions happen or they all don't happen.
  • Consistent: The transaction aware resource is left in a consistent state whether the transaction committed or failed and throughout any interim step.
  • Isolated: Any external observers (that don't participate in the transaction) do not see the interim states. The either see the state before the transaction began or the state after it completed.
  • Durable: Changes made in the transaction are saved in persistent storage so that they are available after a system restart.

The tradeoff you are making when you go with the Transactional Service Pattern is, of course, performance. Transactions are always slower than working without them because of the preparation, the IO needed for durability, lock managements, etc. What I usually do is define target scenarios and test early to make sure the solution is good enough.

One option to implementing the Transactional Service Pattern is to use a transactional message transport for all the messages that flow between the services. Having a transactional message transport makes implementing the pattern very easy -just follow the steps mentioned earlier: begin transaction, read, handle, send, commit. Another option , which I guess is the more common scenario, is to put incoming messages into a transactional resource such as a queue or database table upon receiving it, and then send an acknowledgment as a reply to service consumers. Since the initial message handling in this case is not transactional, you need to be able to cope with a message arriving multiple times if the consumer, for example, didn't get the acknowledge message and sends a request to withdrew $1 million - again.

Figure 8 shows a redesign of the example in Figure 6 in light of the Transactional Service Pattern. To recap, the scenario talks about an e-commerce front end that talks to an ordering service. The ordering service then registers the order, sends the order out to suppliers, and notifies a billing service. When everything is done, it sends a confirmation to the e-commerce front-end application.

Using the Transactional Service Pattern, steps 2.0 to 2.5 in Figure 8, which are the actions taken by the ordering service, are in the same transaction. This means that if you don't handle the place order message because of a crash or other mishap, no message leaves the service. This is a boon since now we don't have to write complicated compensation logic to handle such failures. A subtle issue here is what might happen if the ordering service crashes somewhere between steps 1.0 and 1.2. The scenario is not 100% fail-safe; there's a slight chance that we queue the incoming message for processing but then crash before we acknowledge the message. This may result in accepting the same request more than once. One way to handle these duplicate messages on the service's side is to look at the incoming queue on service startup and send acknowledgments for all the messages in the queue, in this case the consumer might get more than one acknowledgment for messages it sent.

Note that, for the example, using a single transaction would work only as long as the billing process just produces an invoice. It won't work if the billing service activity is to process a credit card and the ordering needs the confirmation to continue. When a single transaction won't work, the process needs to be broken into smaller transactions and the whole process becomes what's known as a long-running operation. Another reason besides long-running processes to break the flow into a few smaller transactions is if the service is distributed.

•   •   •

This article is based on the book SOA Patterns (http://www.manning.com/rotem) scheduled to print February 2009. This article is courtesy of Manning Publications (http://www.manning.com). The ebook is available and sold exclusively through Manning Publications.

More Stories By Arnon Rotem-Gal-Oz

For the past 10 years Arnon Rotem-Gal-Oz has been an architecture and system designer of large distributed systems including C4ISR systems, IP customer care and billing systems, and BI engines. He has experience with a variety of technologies (.Net, J2EE, CORBA, COM+, X-Windows) on diverse platforms (Unix, Windows, Dos, AS/400). He currently works for Rafael as the Biometric line development manager.

Comments (0)

Share your thoughts on this story.

Add your comment
You must be signed in to add a comment. Sign-in | Register

In accordance with our Comment Policy, we encourage comments that are on topic, relevant and to-the-point. We will remove comments that include profanity, personal attacks, racial slurs, threats of violence, or other inappropriate material that violates our Terms and Conditions, and will block users who make repeated violations. We ask all readers to expect diversity of opinion and to treat one another with dignity and respect.