Ben Rowlands opened SPR-3311 and commented
To implement a custom local transaction manager we'd like to listen to what Spring is enlisting in the transaction. We don't use a distributed transaction manager or 2PC but instead use a local transaction manager that orders items in 'best-efforts' order and does single-phase commit.
If we could listen to the TransactionSynchronizationManager we could reuse all of Springs current transaction support/integration to handle transaction resource management (using the Spring strategy of binding resources to the thread-local, ie in DataSourceUtils).
In our implementation when the listeners are fired we update our list of transaction items so we can track the order in which items need to be committed (we commit the initiating transaction item last, typically this is the messaging resource so in the event of failure the message is rolled back and replayed - a limitation of this strategy is potential duplicate messages but no lost messages, however it is much simpler than using 2PC and distributed transaction manager and avoids the management overhead).
An additional complexity is the implementation of Savepoints; we take a snapshot of the synchronizations and resources bound to the transaction when the savepoint is created and restore this state when we rollback to a savepoint (we rollback any resources added after the savepoint was created). If we could listen to the TransactionSynchronizationManager we could achieve this by tracking any new synchronizations/resources added after a savepoint was created and then remove them upon rollback of the savepoint.
For example:
public interface TransactionSynchronizationManagerListener { public void resourceBound( Object key, Object resource );
public void resourceUnbound( Object key, Object resource );
public void synchronizationRegistered( TransactionSynchronization synchronization );
public void synchronizationUnregistered( TransactionSynchronization synchronization );
}
With API to register a listener for the current thread (and clear).
TransactionSynchronizationManager#setListener( TransactionSynchronizationManagerListener listener )
To handle the savepoint issue (i.e., shrink list of synchronizations to the state they were before the savepoint) an API to selectively remove synchronizations would be ideal:
TransactionSynchronizationManager#unregisterSynchronization( TransactionSynchronization synchronization )
Issue Links: - #9710 Add extension to TransactionSynchronizationManager to allow custom resolution of unbound transaction resources to allow "lazy-enlistment" TransactionManagers
13 votes, 19 watchers
Comment From: spring-projects-issues
Dave Syer commented
I would maybe go further and suggest that Spring should provide the "best efforts" transaction manager out of the box. I would love to see it replace the horrible transaction configuration in message listener containers. If I want a transaction I need to use a transaction manager - the only choice should be which implementation to use. With message listerner containers I have too many choices - sessionTransacted, transactionManager, TransactionAwareConnectionFactoryProxy, ...
Comment From: spring-projects-issues
Juergen Hoeller commented
(First of all, sorry for the delay here... I'm finally also about to create a separate JIRA issue for the "best efforts" transaction manager as suggested by Dave.)
Moving forward, his issue here is supposed to serve for two related enhancements: * general transaction listener facility * customizable transaction logging, in particular with a detailed transaction trace log for the rollback case
Spring's transaction infrastructure is supposed to allow for such extensions. There are a variety of use cases that have been suggested to us from various sides; we intend to those distill those into one unified extension model for Spring's transaction synchronization facilities.
Some of this functionality still has a chance of making the upcoming Spring 2.1 deadline. However, the general effort is scheduled for the next major release.
Juergen
Comment From: spring-projects-issues
Joe Kearney commented
Since filing this request further work on our transaction manager has identified the need for an additional API in the listener, to allow us to circumvent the special case JMS handling and allow lazy binding of resources. Details below.
Problem:
- Spring's transaction managers bind transacted resources to the transaction up front, in particular at doBegin().
- For a transaction using JMS within another transaction, there is special handling to piggy-back the JMS transaction behaviour onto the synchronizations of the DB transaction.
- We want to control the session, rather than allowing the synchronisation to do this. In particular we need to force ConnectionFactoryUtils#doGetTransactionalSession neither to create its own session nor to register synchronizations against the existing transaction.
- In addition we want to lazily enlist resources into the transaction. We want to be able to create this ourselves when requested.
- Behaviour we need in order to do this: when getting a transacted resource, we need the JMS/DB/whatever utils to be given that resource in the state it would expect if it had created it itself when starting up (e.g. JMSTransactionManager creates its JmsResourceHolder up front, when transaction is started. Similarly for DataSourceTransactionManager).
Solution:
- We'd like to add API TransactionSynchronisationManagerListener#handleUnboundResource(Object key)
- To be fired in TransactionSynchronisationManager#getResource(). If the resource is not in the map (value = map.get(key) is null) then
- Invoke TransactionSynchronisationManagerListener#handleUnboundResource(key) to create that resource (if it can)
- Bind new resource into transaction (if created)
Comment From: jhoeller
Closing this issue along with #8524.