To begin with, this question is not about applying an external XA transaction manager, as this would definitely solve the problem. The question is how go around without it assuming the following:
Now let's suppose the following scenario:
The way I feel it, everything calls for a "nested transaction" solution, which is fortunately supported by Spring DataSourceTransactionManager
. The issue is, Propagation.NESTED
assumes that both transaction X and Y are executed in the same database (DataSource
), and possibly over the same underlying JDBC Connection
. But this is clearly not the case I have, as databases have individual connections and are able to support independent transactions.
Another possible solution I tried is to create two DataSourceTransactionManager
instances, one for each database. From the first glance, it looks a cleaner solution - but then I realized that standard Spring classes rely heavily on static, thread-local fields, thus guaranteeing stomping on each other when trying to use two managers concurrently by the same thread (see assumption above). No go.
Now I am thinking about subclassing all relevant Spring transaction management classes to "separate" those shared static fields between packages. It feels like inventing a bicycle, though, so I would prefer not to do it.
As external XA transaction manager is seen as an overkill (due to very loose consistency requirements, see above), is the only solution to go down on JDBC level and programmatically manage transaction Y (begin, read, write data, commit)? Or am I missing some advanced concept in spring-tx
?
I'm not a Spring expert (thus I can't say anything to the idea of subclassing) but what I know the Spring transactions uses abilities of JTA.
As you said the DataSourceTransactionManager
works just per resource and NESTED
functionality is possible because of the JDBC api and its functionality to work with safepoints (https://docs.oracle.com/javase/8/docs/api/java/sql/Connection.html#setSavepoint--). This ability is limited to one Connection
.
I think you can go with manual JDBC management as you suggested. Or I would still consider the transaction manager. The transaction manager not only manage XA transactions but it provides the implementation of the JTA api for you can use the declarative or programmatic approach. The most overhead for the transaction management is this XA handling - the data needs to be saved to the drive during prepare at the application and at the database side. If you use only the transaction management capability with non-XA resource then transaction manager gives you the JTA api to run transaction, does not provide the consistency (it's not what you need) and not using the XA overhead.
If you use transaction manager and two non-XA resources (DriverManagerDataSource
) then you can drive transactions like - begin - update data - suspend #1 - begin - update data - commit #2 - resume #1 - commit.
Unfortunately, your particular case fits the best the nested transaction model which is not supported in JTA.
But even with the NESTED
Spring scope this case is precisely what you need. The nested works in way that if the nested transaction is rolled-back then the outer transaction is not rolled-back automatically. In other words rollback of the nested transaction (transaction Y
) does not mean the outer transaction is rolled-back as well (transaction X
).