I spent a lot of time over the past few months with the JBoss ESB and jBPM teams. It was my first real hands-on experience with the orchestration of both local and remote “web” services. Usually, as a middleware developer you think in terms of milliseconds. How fast can I transmit information from one point to the next and back again? How can I optimize this query to run faster? The jBPM guys think about things very differently. For them, the processes they model could span minutes, hours, days and even weeks or months. Its a very different way of looking at things. For instance, what if you have a process that spans days and something goes wrong? How do you handle failure and abort conditions? You sure as hell can’t do long running activities within a JTA transaction. This would hold up things like database resources too long. No, what you really need is the ability to undo operations in abort situations.
In middleware land, undo is more commonly referred to as Compensating Transactions. What’s an example? Cancellation of an order is a common compensation. When a customer makes an order over the internet, the business process usually goes through a set of committed states before the product is delivered. Let’s take the ordering of a computer for instance:
- Bill credit card
- Pull parts from inventory
- Assemble computer
- Install software
You don’t want to build the computer without billing the customer. Once parts are pulled from inventory, inventory counts must be decremented. No way this can be done in a traditional ACID transaction. Each state transaction must be its own transaction that is committed. If an order is canceled, there are a number of things that must be done. The buyer must have their credit card credited. The computer must also be disassembled and parts sent back to inventory.
Long running conversations aren’t just a property of a business process run inside something like jBPM. Web applications are a perfect example of a long running activity where you don’t want to hold up resources. The Hibernate guys like to handle these situations by holding database updates in memory and flushing them when the activity commits. They suggest the combination of optimistic concurrency and flush mode NEVER/MANUAL to do this. Flush mode NEVER holds all database updates within the Hibernate session until you invoke flush(). Optimistic concurrency ensures you that your database is in the state you want it to be in when you perform the flush. With this approach compensation is not needed at all, because nothing is committed to the database. The problem is that you might have a resource that can’t be managed by Hibernate or that has a high probability of failing optimistic concurrency checks. In that scenario, compensation is the only thing you can do.
So, that’s my explanation of what compensating transactions are. In my next blog I want to talk about how middleware interacts with compensating transactions and whether or not you need a special compensating transaction engine or business activity service to enable compensating transactions.