Sunday, March 29, 2009
JBossTS offsite
We had another good offsite meeting earlier this week. As usual Jonathan was spot-on with the selection of venue (the Black Swan Hotel): good food and a great atmosphere for discussions and planning. We had the JBossTS and Blacktie teams together in one place, so were able to cover a lot of things from TOAST through RESTful transactions and OSGi. Jonathan was able to plan out the next 6 months or so for the team and it's a good mix of definite current needs and beliefs on future requirements. I can't wait for the next meeting!
Wednesday, March 25, 2009
Compiled Configurable TOAST
In between completing fully functional crash recovery for the Web Services Transactions (XTS) code and writing a comprehensive test suite for it I've been able to squeeze in a couple of nice extensions to the functionality of TOAST, the code injection tool I have been using to automate XTS crash recovery testing.
Attentive readers may recall that TOAST is a nifty, easy to use tool for injecting side-effects into Java code. I have been using TOAST to test XTS recovery by injecting code which crashes the JVM in the middle of a transaction commit. I also use TOAST to inject further faults during recovery in order to delay or drop messages exchanged between the transaction coordinator and the web service participants it is trying to corral into a successful commit. Finally, I inject trace statements at suitable locations in the code which track the progress of the test and allow the test client to identify successful or failed execution.
None of this requires any modifications to or recompilation of the deliverable application being tested. TOAST just needs to be supplied with a script identifying the trigger points, locations in the application code which need to be tweaked, and the Java expressions which are to be (conditionally) executed at those trigger points. Point your JVM at the TOAST jar and the script when you start your Java application and TOAST makes sure all the required side effects get executed.
For those who have not yet had a look at TOAST I'll recap a little. Scripts comprise a sequence of Event Condition Action (ECA) rules. The Event part is the specification of the trigger point, a class name, a method located in that class and some location in the method code. Locations identify a point in the source code through reference to the code structure. Examples might be: the start of the method (AT ENTRY) when a return occurs (AT EXIT), before the first read of an instance field (AT READ myField), after the 2nd call to a method (AFTER CALL getMyField() 2) and so on. The event specifies WHEN the side-effects coded in the rule are are considered for execution by describing a trigger point. Whenever a thread reaches this trigger point it enters the rule engine to decide whether to fire the associated rule.
The Condition part is a Java expression specifying WHETHER to fire the rule. If this evaluates to true then the rule fires and executes its Action. If the condition is false then control returns to the trigger method and execution continues as normal.
The Action part specifies WHAT side-effects need to occur. It is a sequence of Java expressions to be evaluated one after another. The last expression may be a return or a throw action. These cause an immediate abnormal return of control from the triggering method bypassing normal execution of the rest of the method code. If the method is non-void then the return action must include an expression evaluated to compute the return value. A throw action must include an exception type and arguments for the exception constructor.
In order to simplify testing of multi-threaded code TOAST's rule language also includes built-in methods for use in conditions and actions. These are a variety of operations which simplify the development of multi-threaded tests. For example, one rule might include a call to waitFor() in its action list, suspending any thread which fires the rule. Another rule might include call to signalWake() in its action. If a second thread fires this rule then the call to signalWake() wakes up the suspended thread. This can be used to ensure that the first thread is only allowed to progress once the second thread has reached a suitable point during its execution.
So, what are the two updates which I have sneaked in? Well, the first one is to compile the rule code to bytecode. Up until now the rule execution engine has worked by interpreting the rule parse tree. Method and builtin calls were executed using reflection. Java operations were executed by evaluating their arguments and then combining them based on the operation type. Well, the latest version of TOAST now compiles the rule code into Java bytecode attached to a helper class associated with the rule. This bytecode is handed to the JIT compiler which means it can be compiled to native machine code and optimized like any other Java method. This will not make a lot of difference for rules which are only executed once or twice but it will make a big difference to the usability of TOAST in cases where rules are triggered many times.
The second update is a very nice generalization of the built-in functionality. The rule parser, type-checker and compiler don't actually care much about the presence of builtin methods. As far as they are concerned a call is a call is a call. The type checker, in particular, attempts to resolve calls by looking at the method name and signature i.e. the types of the call arguments. Any calls whose name and signature matches a public instance method of a class called Helper are treated as built-in calls. When the rule is triggered an instance of Helper is created to store the list of event bindings. A built-in call can be executed by calling the matching method with this instance as the method target. The compiler actually generates a subclass of Helper whose execute() method comprises the compiled rule bytecode. So, built-in calls are actually just invocations of methods on this.
Now, this mechanism is completely independent of the details of how class Helper is defined. Any public POJO class could actually be used to define the rule language built-ins. So, a simple declaration in the rule source specifying the name of an alternative class will allow the rules to use a different helper more appropriate to the application in question. A domain-specific helper class makes scripting of side-effects simpler and tidier by encoding complex, application-specific actions as single operations. If you like you can keep the current built-ins and add some more of your own by extending class Helper and adding extra public methods. If you don't like how one of the default built-in operations works you can override the version Helper provides to re-implement it.
These upgrades are described more fully in the TOAST documentation. Please download TOAST, take a look and give it a test run.
Attentive readers may recall that TOAST is a nifty, easy to use tool for injecting side-effects into Java code. I have been using TOAST to test XTS recovery by injecting code which crashes the JVM in the middle of a transaction commit. I also use TOAST to inject further faults during recovery in order to delay or drop messages exchanged between the transaction coordinator and the web service participants it is trying to corral into a successful commit. Finally, I inject trace statements at suitable locations in the code which track the progress of the test and allow the test client to identify successful or failed execution.
None of this requires any modifications to or recompilation of the deliverable application being tested. TOAST just needs to be supplied with a script identifying the trigger points, locations in the application code which need to be tweaked, and the Java expressions which are to be (conditionally) executed at those trigger points. Point your JVM at the TOAST jar and the script when you start your Java application and TOAST makes sure all the required side effects get executed.
For those who have not yet had a look at TOAST I'll recap a little. Scripts comprise a sequence of Event Condition Action (ECA) rules. The Event part is the specification of the trigger point, a class name, a method located in that class and some location in the method code. Locations identify a point in the source code through reference to the code structure. Examples might be: the start of the method (AT ENTRY) when a return occurs (AT EXIT), before the first read of an instance field (AT READ myField), after the 2nd call to a method (AFTER CALL getMyField() 2) and so on. The event specifies WHEN the side-effects coded in the rule are are considered for execution by describing a trigger point. Whenever a thread reaches this trigger point it enters the rule engine to decide whether to fire the associated rule.
The Condition part is a Java expression specifying WHETHER to fire the rule. If this evaluates to true then the rule fires and executes its Action. If the condition is false then control returns to the trigger method and execution continues as normal.
The Action part specifies WHAT side-effects need to occur. It is a sequence of Java expressions to be evaluated one after another. The last expression may be a return or a throw action. These cause an immediate abnormal return of control from the triggering method bypassing normal execution of the rest of the method code. If the method is non-void then the return action must include an expression evaluated to compute the return value. A throw action must include an exception type and arguments for the exception constructor.
In order to simplify testing of multi-threaded code TOAST's rule language also includes built-in methods for use in conditions and actions. These are a variety of operations which simplify the development of multi-threaded tests. For example, one rule might include a call to waitFor() in its action list, suspending any thread which fires the rule. Another rule might include call to signalWake() in its action. If a second thread fires this rule then the call to signalWake() wakes up the suspended thread. This can be used to ensure that the first thread is only allowed to progress once the second thread has reached a suitable point during its execution.
So, what are the two updates which I have sneaked in? Well, the first one is to compile the rule code to bytecode. Up until now the rule execution engine has worked by interpreting the rule parse tree. Method and builtin calls were executed using reflection. Java operations were executed by evaluating their arguments and then combining them based on the operation type. Well, the latest version of TOAST now compiles the rule code into Java bytecode attached to a helper class associated with the rule. This bytecode is handed to the JIT compiler which means it can be compiled to native machine code and optimized like any other Java method. This will not make a lot of difference for rules which are only executed once or twice but it will make a big difference to the usability of TOAST in cases where rules are triggered many times.
The second update is a very nice generalization of the built-in functionality. The rule parser, type-checker and compiler don't actually care much about the presence of builtin methods. As far as they are concerned a call is a call is a call. The type checker, in particular, attempts to resolve calls by looking at the method name and signature i.e. the types of the call arguments. Any calls whose name and signature matches a public instance method of a class called Helper are treated as built-in calls. When the rule is triggered an instance of Helper is created to store the list of event bindings. A built-in call can be executed by calling the matching method with this instance as the method target. The compiler actually generates a subclass of Helper whose execute() method comprises the compiled rule bytecode. So, built-in calls are actually just invocations of methods on this.
Now, this mechanism is completely independent of the details of how class Helper is defined. Any public POJO class could actually be used to define the rule language built-ins. So, a simple declaration in the rule source specifying the name of an alternative class will allow the rules to use a different helper more appropriate to the application in question. A domain-specific helper class makes scripting of side-effects simpler and tidier by encoding complex, application-specific actions as single operations. If you like you can keep the current built-ins and add some more of your own by extending class Helper and adding extra public methods. If you don't like how one of the default built-in operations works you can override the version Helper provides to re-implement it.
These upgrades are described more fully in the TOAST documentation. Please download TOAST, take a look and give it a test run.
Monday, March 16, 2009
Nested transaction support
For well over 20 years we've been saying that JBossTS (aka ArjunaTS aka Arjuna) supports nested transactions. If you check out most of the papers, documents or presentations that have been written on the subject over the years they'll all tell you that nested transactions (subtransactions) are good because:
Now nested transactions have been around for a while (since the early 80's) but they have had limited adoption outside of academia. We did manage to get them adopted within the OTS, even if in a fairly brain-dead/limited approach. But other standards either ignored them (e.g., XA) or made such vague statements about them as to be useless (e.g., JTA). The reason for this is mainly because database vendors have not traditionally supported nested transactions and is primarily why I'm writing this blog entry: I've noticed a few new transaction manager implementations that say they support nested transactions. But what does that actually mean?
If you look at either the JBossTS architecture or even the OTS interfaces, there's not a lot you have to do to the coordinator in order for it to support a parent-child relationship. That's the easy part! (In fact it's easier for the coordinator to support nested transactions because there's no requirement for durability.) But as with a top-level transaction implementation, you need participants (e.g., databases) to be able to do anything useful. The semantics of nested transactions (relaxing ACID properties) mean that a top-level aware participant can't be used for a nested transactions (e.g., your database is going to write the state changes to disk when the XAResource is driven through 2PC and what about lock inheritance?)
Thus whenever we said "JBossTS supports nested transactions" we meant that the coordinator had the parent-child relationship, but we also provided nested transaction-aware resources. Rather than doing very little to the coordinator and pushing most of the real effort on to the application developer or resource implementer, we provide a complete toolkit for developing transactional applications. As a developer you don't have to worry about whether or not a transaction is nested and whether or not you've got nested transaction-aware resources in the mix somehow. All of that is taken care of by JBossTS. So be careful if you come across a transaction manager that says it "supports" nested transactions: it's a meaningless statement unless this covers the coordinator and participants (state and concurrency control). And of course you want to do this in a distributed environment as well as in a flexible manner! (Yes, we do that too!)
- 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.
Now nested transactions have been around for a while (since the early 80's) but they have had limited adoption outside of academia. We did manage to get them adopted within the OTS, even if in a fairly brain-dead/limited approach. But other standards either ignored them (e.g., XA) or made such vague statements about them as to be useless (e.g., JTA). The reason for this is mainly because database vendors have not traditionally supported nested transactions and is primarily why I'm writing this blog entry: I've noticed a few new transaction manager implementations that say they support nested transactions. But what does that actually mean?
If you look at either the JBossTS architecture or even the OTS interfaces, there's not a lot you have to do to the coordinator in order for it to support a parent-child relationship. That's the easy part! (In fact it's easier for the coordinator to support nested transactions because there's no requirement for durability.) But as with a top-level transaction implementation, you need participants (e.g., databases) to be able to do anything useful. The semantics of nested transactions (relaxing ACID properties) mean that a top-level aware participant can't be used for a nested transactions (e.g., your database is going to write the state changes to disk when the XAResource is driven through 2PC and what about lock inheritance?)
Thus whenever we said "JBossTS supports nested transactions" we meant that the coordinator had the parent-child relationship, but we also provided nested transaction-aware resources. Rather than doing very little to the coordinator and pushing most of the real effort on to the application developer or resource implementer, we provide a complete toolkit for developing transactional applications. As a developer you don't have to worry about whether or not a transaction is nested and whether or not you've got nested transaction-aware resources in the mix somehow. All of that is taken care of by JBossTS. So be careful if you come across a transaction manager that says it "supports" nested transactions: it's a meaningless statement unless this covers the coordinator and participants (state and concurrency control). And of course you want to do this in a distributed environment as well as in a flexible manner! (Yes, we do that too!)
Wednesday, March 11, 2009
RESTful transactions reborn
I've mentioned a few times about work we did almost 10 years ago on transactional REST services. This was when JBossTS was part of the HP Netaction suite, we were working on BTP, as well as the precursor to WS-TX/WS-CAF and the world's first Web Services transactions product HP-WST.
Without reminiscing too much, in those days I was participating in the W3C Web Services Architecture working group and the topic of REST came up there quite regularly. So we decided to give it a try as far as transactions were concerned and 'hey presto' RESTful transactions were born. This was way before JAX-RS so we were hand crafting the interactions ourselves, but we demonstrated it could work with the involvement of several companies. And there is remained once HP divested themselves of their middleware division.
But having transactional REST-based services has been a pet project for a long time, so it was bound to rear its head. Especially as some people (rightly) push the Web as an integration platform (shouldn't really be news to people since the WWW has always been about integrating data). Of course there are those who say you don't need transactions in the Web or can (somehow) program around them, but they miss the point. It's a bit like saying you don't need an airbag in your car but can use a pillow (and quick reflexes) yourself to save money. Yes, transactions aren't right for all situations and some bespoke approaches may be more efficient, but if you think about what you get from a transaction system and what you'd have to do in order to obtain the same guarantees, it's more often than not worth using them.
So we originally created a student project to dust off the old REST transactions protocol(s) and implement them today. This started life before JAX-RS (again) but by the time something really happened JAX-RS (and RESTeasy) was around. Michael from the JBossTS team took this work on and did a great job. It's not finished (only the atomic outcome protocol has been implemented so far, for example). Plus the protocols haven't been changed at all since they were created back in 2000/2001. So there are still some things we need to look at and more interesting work ahead.
Is this something you should be looking at for your projects? Should you believe those people who say transactions and REST don't go together? Well as I'm fond of saying "One size does not fit all." Transactions have played a critical part in all distributed systems for over four decades and for very good reasons. Of course we have XTS and Web Services Transactions support if you're using SOAP. But maybe SOAP isn't "your thing" and HTTP is all you can use. Well in that case take a look at this. In just the same way that you wouldn't want to be using a tank in the ocean and a submarine on land, use the right software tool for the right job!
Without reminiscing too much, in those days I was participating in the W3C Web Services Architecture working group and the topic of REST came up there quite regularly. So we decided to give it a try as far as transactions were concerned and 'hey presto' RESTful transactions were born. This was way before JAX-RS so we were hand crafting the interactions ourselves, but we demonstrated it could work with the involvement of several companies. And there is remained once HP divested themselves of their middleware division.
But having transactional REST-based services has been a pet project for a long time, so it was bound to rear its head. Especially as some people (rightly) push the Web as an integration platform (shouldn't really be news to people since the WWW has always been about integrating data). Of course there are those who say you don't need transactions in the Web or can (somehow) program around them, but they miss the point. It's a bit like saying you don't need an airbag in your car but can use a pillow (and quick reflexes) yourself to save money. Yes, transactions aren't right for all situations and some bespoke approaches may be more efficient, but if you think about what you get from a transaction system and what you'd have to do in order to obtain the same guarantees, it's more often than not worth using them.
So we originally created a student project to dust off the old REST transactions protocol(s) and implement them today. This started life before JAX-RS (again) but by the time something really happened JAX-RS (and RESTeasy) was around. Michael from the JBossTS team took this work on and did a great job. It's not finished (only the atomic outcome protocol has been implemented so far, for example). Plus the protocols haven't been changed at all since they were created back in 2000/2001. So there are still some things we need to look at and more interesting work ahead.
Is this something you should be looking at for your projects? Should you believe those people who say transactions and REST don't go together? Well as I'm fond of saying "One size does not fit all." Transactions have played a critical part in all distributed systems for over four decades and for very good reasons. Of course we have XTS and Web Services Transactions support if you're using SOAP. But maybe SOAP isn't "your thing" and HTTP is all you can use. Well in that case take a look at this. In just the same way that you wouldn't want to be using a tank in the ocean and a submarine on land, use the right software tool for the right job!
Sunday, February 15, 2009
Lessons learned and magic numbers.
One of the things you do when you start developing software, especially in a new or cutting-edge area, is believe you know what's best for your users and try to hide any complexities from them. That's one of the reasons we pushed RPC as the best way in which to develop distributed applications in the 1980's. Well back when we started developing Arjuna we took hiding complexity to heart, since simplifying the development of transactional applications was core to all of our PhDs. And I think we did a very good job with the initial releases.
However, once we gave the system to people (the source was made freely available by FTP in 1991, but various industry sponsors were using it before then) the feedback we got was very interesting: users will always do things you didn't expect and demand flexibility in what you produce.
In Arjuna this didn't impact the interfaces users saw but many of the internal development parameters that we had used, thinking that they would never need to be configurable (statically or dynamically). For example, when making a remote invocation on an object failures can occur (the network could partition, the machine hosting the object could crash, etc.) In the absence of a perfect failure detector you use failure suspicion and timeouts. Basically if a response does not come back from the object in time T then you assume the object has failed and act accordingly. But if you get the timeout value wrong then you can make the wrong decision with associated consequences. The timeout value really needs to take into account how long work might take to execute, how overloaded a machine may be, network congestion etc. So one value is rarely right for every application. But we didn't take that into account initially and hard-coded a magic number into the system.
There were other examples of magic numbers, including: the number of RPC retransmissions to use before assuming that a request (or response) cannot get through to the endpoint (why should 5 be any better than 2 or worse than 10?); the number of clustered name server instances; the object store location; the orphan detection period; the lock-conflict detection timeout etc. These were all things we believed had the best (sensible) values, but with hindsight it was clear that it was all based on limited deployment knowledge.
What this all lead to quite quickly was a methodology of expect the unexpected and develop accordingly as far as your users are concerned. We made the system extremely flexible and configurable, where many of the old magic numbers could be overridden either as the system ran or during deployment, with sensible defaults (trying to hit Pareto's 80/20 principle). Those magic numbers we didn't make configurable were clearly documented (both so we could remember as much as users could determine why something was behaving the way it was).
From the feedback we received over the past 20 years I think we managed to come close to the right set of compromises. It's true that most users are happy with the default values we set (which were/are revised based on feedback). But those smaller users who really do need the ability to change things now (or since 20 years ago) have the ability to modify them without rebuilding the system. This has been important in the systems adoption and it's a lesson that we continue to apply. So if you're developing software, beware of using too many magic numbers and if you don't make them configurable you need to understand (and believe) why that's the case and, importantly, document them just in case!
However, once we gave the system to people (the source was made freely available by FTP in 1991, but various industry sponsors were using it before then) the feedback we got was very interesting: users will always do things you didn't expect and demand flexibility in what you produce.
In Arjuna this didn't impact the interfaces users saw but many of the internal development parameters that we had used, thinking that they would never need to be configurable (statically or dynamically). For example, when making a remote invocation on an object failures can occur (the network could partition, the machine hosting the object could crash, etc.) In the absence of a perfect failure detector you use failure suspicion and timeouts. Basically if a response does not come back from the object in time T then you assume the object has failed and act accordingly. But if you get the timeout value wrong then you can make the wrong decision with associated consequences. The timeout value really needs to take into account how long work might take to execute, how overloaded a machine may be, network congestion etc. So one value is rarely right for every application. But we didn't take that into account initially and hard-coded a magic number into the system.
There were other examples of magic numbers, including: the number of RPC retransmissions to use before assuming that a request (or response) cannot get through to the endpoint (why should 5 be any better than 2 or worse than 10?); the number of clustered name server instances; the object store location; the orphan detection period; the lock-conflict detection timeout etc. These were all things we believed had the best (sensible) values, but with hindsight it was clear that it was all based on limited deployment knowledge.
What this all lead to quite quickly was a methodology of expect the unexpected and develop accordingly as far as your users are concerned. We made the system extremely flexible and configurable, where many of the old magic numbers could be overridden either as the system ran or during deployment, with sensible defaults (trying to hit Pareto's 80/20 principle). Those magic numbers we didn't make configurable were clearly documented (both so we could remember as much as users could determine why something was behaving the way it was).
From the feedback we received over the past 20 years I think we managed to come close to the right set of compromises. It's true that most users are happy with the default values we set (which were/are revised based on feedback). But those smaller users who really do need the ability to change things now (or since 20 years ago) have the ability to modify them without rebuilding the system. This has been important in the systems adoption and it's a lesson that we continue to apply. So if you're developing software, beware of using too many magic numbers and if you don't make them configurable you need to understand (and believe) why that's the case and, importantly, document them just in case!
Monday, February 9, 2009
JBossTS and Blacktie: the only combination you'll need!
[Cue advertizing jingle.]
Are you a major transaction user, or do you know someone who is? Are you worried about your legacy transaction infrastructure because the vendor may not be reliable? Do you have sleepless nights wondering how to drag your transactional applications into the modern era? Or are just concerned about the ever escalating costs for the transaction system you're currently tied into? Well worry no more: what you need is Blacktie from JBoss, the company that brought you those other useful gadgets like JBossAS, the JBoss T-shirt, the JBoss thong (picture no longer available!), and the JBoss World Virtual Conference.
Unlike offerings from other vendors, Blacktie is entirely open source. It comes in a convenient source or binary bundle for easy deployment to tackle those stubborn legacy applications. Blacktie also builds upon other mature products from your favourite JBoss vendor so it fits nicely into your existing investments from them. And if this is your first entry into open source and JBoss, then it's a great way to start. Blacktie will be the only legacy transaction implementation you'll ever need to solve those annoying problems at a price you'll love!
And coming soon iBlacktie: because everything needs a little i magic!
[Cue advertizing jingle.]
Go on. Check it out. You know you want to!
Are you a major transaction user, or do you know someone who is? Are you worried about your legacy transaction infrastructure because the vendor may not be reliable? Do you have sleepless nights wondering how to drag your transactional applications into the modern era? Or are just concerned about the ever escalating costs for the transaction system you're currently tied into? Well worry no more: what you need is Blacktie from JBoss, the company that brought you those other useful gadgets like JBossAS, the JBoss T-shirt, the JBoss thong (picture no longer available!), and the JBoss World Virtual Conference.
Unlike offerings from other vendors, Blacktie is entirely open source. It comes in a convenient source or binary bundle for easy deployment to tackle those stubborn legacy applications. Blacktie also builds upon other mature products from your favourite JBoss vendor so it fits nicely into your existing investments from them. And if this is your first entry into open source and JBoss, then it's a great way to start. Blacktie will be the only legacy transaction implementation you'll ever need to solve those annoying problems at a price you'll love!
And coming soon iBlacktie: because everything needs a little i magic!
[Cue advertizing jingle.]
Go on. Check it out. You know you want to!
Tuesday, February 3, 2009
Stay away from pseudo-transactions!
I tried to stay clear of commenting on this article, but while participating in tonight's WS-RA meeting I let my defenses down! In short, the article can be summarized by: "How to use non-transactional resources in a transaction when you can't be bothered to do it right in the first place." Or "How I learned to break transactional semantics and put my data consistency on the line."
I'm fairly sure the authors are trying to help their audience, but they really aren't. They ignore critical problems with their proposal, either deliberately or because they simply haven't given the problem space enough thought. What they are trying to do is emulate the last-resource commit optimization within the application, but whereas a transaction manager will do this and provide support for crash failures, the article ignores that completely. There is no reference to failure recovery at all.
The article also appears to assume that because a datasource is managed by an XAResource it will always honor the business logic agreement when the transaction commits, e.g., if the table update succeeded through the session instance then prepare/commit will work later. I hate to break it to the authors, but that isn't always the case. This works in a transaction manager because we manage the resource ordering and durability very carefully to cope with failures, i.e., there are good reasons we don't require the application programmer to do this!
Oh and the way in which multiple non-transactional resources are managed in the transaction just scares the %$&* out of me! Look, these are resources that do their work when told to and you can't undo that (if you could, then wrap the &%*& things in an XAResource!) So if you crash part way through the normal flow of execution, or part way through the "rollback", what is the state of the application? How do you find out what happened to whom and when? Where's the log?! (I would hate to be a systems administrator in this situation when the sh*t hits the fan!)
The authors say that "Although this is not as robust and comprehensive as a truly transactional interface, it can provide excellent coverage at a fraction of the development cost of a JTA compliant interface." Unfortunately it does not. That's a bit like saying a car with worn brakes is roadworthy in all situations, when it clearly isn't! Ask yourself this: what happens to my data in the cases that aren't covered by this approach and can I really afford to lose it or spend the new hours/days/weeks repairing it manually? If the answer is yes, then you probably don't want to use transactions at all. If the answer is no, then stay away from this approach and go with a transaction system and transactional resources.
In conclusion: use transactions correctly and if you can't make your data items transactional then try using compensating transactions. At least you get logging and recovery!
I'm fairly sure the authors are trying to help their audience, but they really aren't. They ignore critical problems with their proposal, either deliberately or because they simply haven't given the problem space enough thought. What they are trying to do is emulate the last-resource commit optimization within the application, but whereas a transaction manager will do this and provide support for crash failures, the article ignores that completely. There is no reference to failure recovery at all.
The article also appears to assume that because a datasource is managed by an XAResource it will always honor the business logic agreement when the transaction commits, e.g., if the table update succeeded through the session instance then prepare/commit will work later. I hate to break it to the authors, but that isn't always the case. This works in a transaction manager because we manage the resource ordering and durability very carefully to cope with failures, i.e., there are good reasons we don't require the application programmer to do this!
Oh and the way in which multiple non-transactional resources are managed in the transaction just scares the %$&* out of me! Look, these are resources that do their work when told to and you can't undo that (if you could, then wrap the &%*& things in an XAResource!) So if you crash part way through the normal flow of execution, or part way through the "rollback", what is the state of the application? How do you find out what happened to whom and when? Where's the log?! (I would hate to be a systems administrator in this situation when the sh*t hits the fan!)
The authors say that "Although this is not as robust and comprehensive as a truly transactional interface, it can provide excellent coverage at a fraction of the development cost of a JTA compliant interface." Unfortunately it does not. That's a bit like saying a car with worn brakes is roadworthy in all situations, when it clearly isn't! Ask yourself this: what happens to my data in the cases that aren't covered by this approach and can I really afford to lose it or spend the new hours/days/weeks repairing it manually? If the answer is yes, then you probably don't want to use transactions at all. If the answer is no, then stay away from this approach and go with a transaction system and transactional resources.
In conclusion: use transactions correctly and if you can't make your data items transactional then try using compensating transactions. At least you get logging and recovery!
Subscribe to:
Posts (Atom)