Description
When creating a connection using the r2dbc ConnectionFactoryOptions the Oracle adaptor will use ForkJoinPool.commonPool() as the executor.
Note: I raised this then noticed PR #131. However the suggestion below would be complimentary and make the solution more robust (both a default fallback and an eventual exception if it still fails).
If the executor has no free threads then the request to create a connection will hang indefinitely. The publishConnection Publisher should timeout and throw an exception as there is currently zero information that this is the cause of the failure.
The timeout default could be reasonably long, there just needs to be some feedback to troubleshoot.
public Publisher<? extends Connection> publishConnection(
DataSource dataSource, Executor executor) {
long timeout = 10000; // Get from configuration
OracleDataSource oracleDataSource = unwrapOracleDataSource(dataSource);
return Mono.from(adaptFlowPublisher(() ->
oracleDataSource
.createConnectionBuilder()
.executorOracle(executor)
.buildConnectionPublisherOracle()))
.timeout(Duration.ofMillis(timeout)) // PREVENT THIS FROM HANGING FOREVER!!!
.onErrorMap(R2dbcException.class, error ->
error.getErrorCode() == 18714 // ORA-18714 : Login timeout expired
? new R2dbcTimeoutException(error.getMessage(),
error.getSqlState(), error.getErrorCode(), error.getCause())
: error);
}
The work around is to create a custom Executor and supply this to the Connection factory.
var executor = Executors.newFixedThreadPool(threads);
var factory = ConnectionFactories.get(
ConnectionFactoryOptions.builder()
.option(OracleR2dbcOptions.EXECUTOR, executor)
.option(ConnectionFactoryOptions.DRIVER, "pool")
.option(ConnectionFactoryOptions.PROTOCOL, "oracle")
.option(ConnectionFactoryOptions.HOST, properties.getHost())
.option(ConnectionFactoryOptions.PORT, properties.getPort())
.option(ConnectionFactoryOptions.DATABASE, properties.getDatabase())
.option(ConnectionFactoryOptions.USER, properties.getUser())
.option(ConnectionFactoryOptions.PASSWORD, properties.getPassword())
.build());