I was inspired by Carl Papa’s use of aspects with the Spring Framework to determine the DataSource
to use (either read-write or read-only). So, I’m writing this post.
I must admit that I have long been familiar with Spring’s AbstractRoutingDataSource. But I did not have a good idea where it can be used. Thanks to Carl and team, and one of their projects. Now, I know a good use case.
@Transactional
With Spring, read-only transactions are typically marked with annotations.
public class SomeTransactionalComponent {
@Transactional(readOnly=true)
public void ...() {...}
@Transactional // read-write
public void ...() {...}
}
To take advantage of this, we use Spring’s TransactionSynchronizationManager to determine if the current transaction is read-only or not.
AbstractRoutingDataSource
Here, we use Spring’s AbstractRoutingDataSource
to route to the read-only replica if the current transaction is read-only. Otherwise, it routes to the default which is the master.
public class ... extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
if (TransactionSynchronizationManager
.isCurrentTransactionReadOnly() ...) {
// return key to a replica
}
return null; // use default
}
...
}
Upon using the above approach, we found out that the TransactionSynchronizationManager
is one step behind because Spring will have already called DataSource.getConnection()
before a synchronization is established. Thus, a LazyConnectionDataSourceProxy needs to be configured as well.
As we were discussing this, we figured if there was another way to determine if the current transaction is read-only or not (without resorting to LazyConnectionDataSourceProxy)
. So, we came up with an experimental approach where an aspect captures the TransactionDefinition
(from the @Transactional
annotation, if any) as a thread-local variable, and an AbstractRoutingDataSource
that routes based on the captured information.
The relevant source code can be found on GitHub. Thanks again, Carl! BTW, Carl is also an award-winning movie director. Wow, talent definitely knows no boundaries.
Originally posted at: DataSource Routing with Spring @Transactional