Issue
I am trying to implement a database read/write seperation using the spring class ( I used this tutorial)
AbstractRoutingDataSource
The application I am working on is using multinenacy and connection pooling (hikari). So I created a ( master/replica datasources for each tenant)
This is how I create the datasources
public DataSource RoutingDatasource(String tenantId, String databaseMasterUrl, String databaseReplicaUrl, String user, String password) { final RoutingDataSource routingDataSource = new RoutingDataSource();
final DataSource masterDataSource = buildTargetDataSource(tenantId,
cachePrepStmtsValue,
prepStmtCacheSize,
prepStmtCacheSqlLimit,
databaseMasterUrl,
driverClass,
user,
password,
MASTER_DATASOURCE_PREFIX);
final DataSource replicaDataSource = buildTargetDataSource(poolName + tenantId,
cachePrepStmtsValue,
prepStmtCacheSize,
prepStmtCacheSqlLimit,
databaseReplicaUrl,
driverClass,
user,
password,
REPLICA_DATASOURCE_PREFIX);
final Map<Object, Object> targetDataSources = new HashMap<>();
targetDataSources.put(DbContext.DbType.MASTER, masterDataSource);
targetDataSources.put(DbContext.DbType.REPLICA, replicaDataSource);
routingDataSource.setTargetDataSources(targetDataSources);
routingDataSource.setDefaultTargetDataSource(masterDataSource);
routingDataSource.afterPropertiesSet();
return routingDataSource;
}
This is how the context is determined
public class RoutingDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
return DbContext.getDbType();
}
and this is how I tell to transaction to switch context
@Transactional(readOnly = true)
public Opportunite consulter(UUID personUuid) {
DbContext.setDbType(DbContext.DbType.REPLICA);
//some work
DbContext.reset();
return some_result ;
}
The code is compiling fine but is not really switching context. In fact after debugging the problem was that the datasource was requested before the transaction is setup. When the transaction is finally setup, it is too late, the datasource has already been loaded.
How can I fix this behavior ? thank you.
Solution
To solve the problem I got to define which database to use before entring to the transaction to do so I have defined a filter ( I am using spring)
public class DbFilter extends {
//les urls that do not only GET method
private final static String NOT_ONLY_GET_URL= "/api/parametre";
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain filterChain) throws IOException, ServletException, RestException {
DbContext.setDbType(DbContext.DbType.MASTER);
if (!NOT_ONLY_GET_URL.equals(request.getRequestURI()) && request.getMethod().equals("GET")) {
DbContext.setDbType(DbContext.DbType.REPLICA);
}
filterChain.doFilter(request, response);
}
This method is runned before calling any @transactional method and is switching the database if it is a GET method.
I have deleted DbContext.setDbType(DbContext.DbType.REPLICA); and Dbcontext.reset() in the @transactional methods.
the "not only get urls parts" is only defined because in my application there are some get methods that do an update inside so I detected those urls and I affected them to the master databse .
Answered By - Ennar.ch
Answer Checked By - Clifford M. (JavaFixing Volunteer)