We use Spring and jOOQ and at present read/write from primary DB. We do have replica DB's and are trying to have non critical reads (like from quartz jobs, pure read api's, etc.) to go to read.
I referred to https://vladmihalcea.com/read-write-read-only-transaction-routing-spring/ and understand that we need a transactional routing layer introduced which would point one way or the other depending on
@Transactional (readonly = true)
However, this doesn't seem to be working in our case, wherein all routing seems to be happening to primary db itself.
Here's the code configurations we use for primary
@Bean(name = "primaryTransactionAwareDataSourceProxy")
public TransactionAwareDataSourceProxy primaryTransactionAwareDataSource(
@Qualifier("primaryDataSource") DataSource primaryDataSource) {
return new TransactionAwareDataSourceProxy(primaryDataSource);
}
@Bean(name = "xyz")
public DataSourceConnectionProvider primaryConnectionProvider(
@Qualifier("primaryTransactionAwareDataSourceProxy") TransactionAwareDataSourceProxy datasource) {
return new DataSourceConnectionProvider(datasource);
}
@Bean(name = "primaryDsl")
@Primary
public DefaultDSLContext primaryDsl(
@Qualifier("primaryConfiguration") DefaultConfiguration configuration) {
return new DefaultDSLContext(configuration);
}
@Bean(name = "primaryConfiguration")
public DefaultConfiguration primaryConfiguration(
@Qualifier("primaryDatabaseProperties") DatabaseProperties props,
@Qualifier("xyz") DataSourceConnectionProvider provider) {
DefaultConfiguration jooqConfiguration = new DefaultConfiguration();
jooqConfiguration.set(provider);
jooqConfiguration.set(SQLDialect.MYSQL);
Settings settings = new Settings()
.withRenderMapping(new RenderMapping()
.withSchemata(new MappedSchema()
.withInput(<name>)
.withOutput(props.getName())));
jooqConfiguration.set(settings);
return jooqConfiguration;
}
and for replica we have along similar lines
@Bean(name = "replicaTransactionAwareDataSourceProxy")
public TransactionAwareDataSourceProxy replicaTransactionAwareDataSource(
@Qualifier("replicaDataSource") DataSource replicaDataSource) {
return new TransactionAwareDataSourceProxy(replicaDataSource);
}
@Bean(name = "xyz")
public DataSourceConnectionProvider replicaConnectionProvider(
@Qualifier("replicaTransactionAwareDataSourceProxy") TransactionAwareDataSourceProxy datasource) {
return new DataSourceConnectionProvider(datasource);
}
@Bean(name = "replicaDsl")
public DefaultDSLContext replicaDsl(
@Qualifier("replicaConfiguration") DefaultConfiguration configuration) {
return new DefaultDSLContext(configuration);
}
@Bean(name = "replicaConfiguration")
public DefaultConfiguration replicaConfiguration(
@Qualifier("replicaDatabaseProperties") DatabaseProperties props,
@Qualifier("xyz") DataSourceConnectionProvider provider) {
DefaultConfiguration jooqConfiguration = new DefaultConfiguration();
jooqConfiguration.set(provider);
jooqConfiguration.set(SQLDialect.MYSQL);
Settings settings = new Settings()
.withRenderMapping(new RenderMapping()
.withSchemata(new MappedSchema()
.withInput(<name>)
.withOutput(props.getName())));
jooqConfiguration.set(settings);
return jooqConfiguration;
}
The data sources are configured as
@Bean(name = "primaryDatabaseProperties")
@ConfigurationProperties(prefix = "database")
public DatabaseProperties primaryDatabaseProperties() {
return new DatabaseProperties ();
}
@Bean(name = "replicaDatabaseProperties")
@ConfigurationProperties(prefix = "database.replica")
public DatabaseProperties replicaDatabaseProperties() {
return new DatabaseProperties ();
}
@Bean(name = "primaryDataSource")
public DataSource primaryDataSource(
@Qualifier("primaryDatabaseProperties") DatabaseProperties props) {
return dataSource(props);
}
@Bean(name = "replicaDataSource")
public DataSource replicaDataSource(
@Qualifier("replicaDatabaseProperties") DatabaseProperties props) {
return dataSource(props);
}
And transactional routing data source defined as
@Bean
public TransactionRoutingDataSource actualDataSource(
@Qualifier("primaryTransactionAwareDataSourceProxy") DataSource primaryDataSource,
@Qualifier("replicaTransactionAwareDataSourceProxy") DataSource replicaDataSource) {
TransactionRoutingDataSource routingDataSource = new TransactionRoutingDataSource();
Map<Object, Object> dataSourceMap = new HashMap<>() {{
put(DataSourceType.READ_WRITE, primaryDataSource);
put(DataSourceType.READ_ONLY, replicaDataSource);
}};
routingDataSource.setTargetDataSources(dataSourceMap);
return routingDataSource;
}
I have an implementation of AbstractRoutingDataSource defined to do the conditional check of if the current transaction is ready only and route to correct datasource accordingly
public class TransactionRoutingDataSource extends AbstractRoutingDataSource {
@Nullable
@Override
protected Object determineCurrentLookupKey() {
return TransactionSynchronizationManager.isCurrentTransactionReadOnly() ?
DataSourceType.READ_ONLY : DataSourceType.READ_WRITE;
}
}
And I use annotations to indicate the necessary routing in the service layer
@Override
@Transactional(readOnly = true)
public <return_type> <method_name>() {}
Any pointers as to what I am missing to have jOOQ route correctly to read db?