diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/config/FineractProperties.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/config/FineractProperties.java index 2a38776f0..41c05d1f7 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/config/FineractProperties.java +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/config/FineractProperties.java @@ -86,6 +86,27 @@ public class FineractProperties { private String readOnlyPassword; private String readOnlyParameters; private String readOnlyName; + + private FineractConfigProperties config; + } + + /** + * Configuration properties to override configurations stored in the tenants database + */ + @Getter + @Setter + public static class FineractConfigProperties { + + private int minPoolSize; + private int maxPoolSize; + + public boolean isMinPoolSizeSet() { + return minPoolSize != -1; + } + + public boolean isMaxPoolSizeSet() { + return maxPoolSize != -1; + } } @Getter diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/service/database/DataSourcePerTenantServiceFactory.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/service/database/DataSourcePerTenantServiceFactory.java index e3e6bfc1b..410b3db96 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/service/database/DataSourcePerTenantServiceFactory.java +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/service/database/DataSourcePerTenantServiceFactory.java @@ -92,8 +92,8 @@ public class DataSourcePerTenantServiceFactory { config.setPoolName(schemaName + "_pool"); config.setUsername(schemaUsername); config.setPassword(databasePasswordEncryptor.decrypt(schemaPassword)); - config.setMinimumIdle(tenantConnection.getInitialSize()); - config.setMaximumPoolSize(tenantConnection.getMaxActive()); + config.setMinimumIdle(getMinPoolSize(tenantConnection)); + config.setMaximumPoolSize(getMaxPoolSize(tenantConnection)); config.setValidationTimeout(tenantConnection.getValidationInterval()); config.setDriverClassName(hikariConfig.getDriverClassName()); config.setConnectionTestQuery(hikariConfig.getConnectionTestQuery()); @@ -111,4 +111,26 @@ public class DataSourcePerTenantServiceFactory { return hikariDataSourceFactory.create(config); } + private int getMaxPoolSize(FineractPlatformTenantConnection tenantConnection) { + FineractProperties.FineractConfigProperties configOverride = fineractProperties.getTenant().getConfig(); + if (configOverride.isMaxPoolSizeSet()) { + int maxPoolSize = configOverride.getMaxPoolSize(); + log.info("Overriding tenant datasource maximum pool size configuration to {}", maxPoolSize); + return maxPoolSize; + } else { + return tenantConnection.getMaxActive(); + } + } + + private int getMinPoolSize(FineractPlatformTenantConnection tenantConnection) { + FineractProperties.FineractConfigProperties configOverride = fineractProperties.getTenant().getConfig(); + if (configOverride.isMinPoolSizeSet()) { + int minPoolSize = configOverride.getMinPoolSize(); + log.info("Overriding tenant datasource minimum pool size configuration to {}", minPoolSize); + return minPoolSize; + } else { + return tenantConnection.getInitialSize(); + } + } + } diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/service/migration/TenantDatabaseUpgradeService.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/service/migration/TenantDatabaseUpgradeService.java index 2a19c12aa..c65b6b101 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/service/migration/TenantDatabaseUpgradeService.java +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/service/migration/TenantDatabaseUpgradeService.java @@ -19,6 +19,7 @@ package org.apache.fineract.infrastructure.core.service.migration; import static org.apache.commons.collections4.CollectionUtils.isNotEmpty; +import static org.apache.commons.lang3.StringUtils.isNotBlank; import java.util.Arrays; import java.util.List; @@ -29,7 +30,6 @@ import liquibase.exception.LiquibaseException; import liquibase.integration.spring.SpringLiquibase; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang3.StringUtils; import org.apache.fineract.infrastructure.core.boot.FineractProfiles; import org.apache.fineract.infrastructure.core.config.FineractProperties; import org.apache.fineract.infrastructure.core.domain.FineractPlatformTenant; @@ -102,19 +102,22 @@ public class TenantDatabaseUpgradeService implements InitializingBean { } private void logTenantStoreDetails() { - log.info("- fineract.tenant.username: {}", fineractProperties.getTenant().getUsername()); + FineractProperties.FineractTenantProperties tenant = fineractProperties.getTenant(); + log.info("- fineract.tenant.username: {}", tenant.getUsername()); log.info("- fineract.tenant.password: ****"); - log.info("- fineract.tenant.parameters: {}", fineractProperties.getTenant().getParameters()); - log.info("- fineract.tenant.timezone: {}", fineractProperties.getTenant().getTimezone()); - log.info("- fineract.tenant.description: {}", fineractProperties.getTenant().getDescription()); - log.info("- fineract.tenant.identifier: {}", fineractProperties.getTenant().getIdentifier()); - log.info("- fineract.tenant.name: {}", fineractProperties.getTenant().getName()); + log.info("- fineract.tenant.parameters: {}", tenant.getParameters()); + log.info("- fineract.tenant.timezone: {}", tenant.getTimezone()); + log.info("- fineract.tenant.description: {}", tenant.getDescription()); + log.info("- fineract.tenant.identifier: {}", tenant.getIdentifier()); + log.info("- fineract.tenant.name: {}", tenant.getName()); - log.info("- fineract.tenant.readonly.username: {}", fineractProperties.getTenant().getReadOnlyUsername()); - log.info("- fineract.tenant.readonly.password: {}", - StringUtils.isNotBlank(fineractProperties.getTenant().getReadOnlyPassword()) ? "****" : ""); - log.info("- fineract.tenant.readonly.parameters: {}", fineractProperties.getTenant().getReadOnlyParameters()); - log.info("- fineract.tenant.readonly.name: {}", fineractProperties.getTenant().getReadOnlyName()); + String readOnlyUsername = tenant.getReadOnlyUsername(); + if (isNotBlank(readOnlyUsername)) { + log.info("- fineract.tenant.readonly.username: {}", readOnlyUsername); + log.info("- fineract.tenant.readonly.password: {}", isNotBlank(tenant.getReadOnlyPassword()) ? "****" : ""); + log.info("- fineract.tenant.readonly.parameters: {}", tenant.getReadOnlyParameters()); + log.info("- fineract.tenant.readonly.name: {}", tenant.getReadOnlyName()); + } } diff --git a/fineract-provider/src/main/resources/application.properties b/fineract-provider/src/main/resources/application.properties index 24252be91..009b11daf 100644 --- a/fineract-provider/src/main/resources/application.properties +++ b/fineract-provider/src/main/resources/application.properties @@ -44,6 +44,9 @@ fineract.tenant.read-only-password=${FINERACT_DEFAULT_TENANTDB_RO_PWD:} fineract.tenant.read-only-parameters=${FINERACT_DEFAULT_TENANTDB_RO_CONN_PARAMS:} fineract.tenant.read-only-name=${FINERACT_DEFAULT_TENANTDB_RO_NAME:} +fineract.tenant.config.min-pool-size=${FINERACT_CONFIG_MIN_POOL_SIZE:-1} +fineract.tenant.config.max-pool-size=${FINERACT_CONFIG_MAX_POOL_SIZE:-1} + fineract.mode.read-enabled=${FINERACT_MODE_READ_ENABLED:true} fineract.mode.write-enabled=${FINERACT_MODE_WRITE_ENABLED:true} fineract.mode.batch-worker-enabled=${FINERACT_MODE_BATCH_WORKER_ENABLED:true} diff --git a/fineract-provider/src/test/java/org/apache/fineract/infrastructure/core/DataSourcePerTenantServiceFactoryTest.java b/fineract-provider/src/test/java/org/apache/fineract/infrastructure/core/DataSourcePerTenantServiceFactoryTest.java index b099cd397..5ead9f5f0 100644 --- a/fineract-provider/src/test/java/org/apache/fineract/infrastructure/core/DataSourcePerTenantServiceFactoryTest.java +++ b/fineract-provider/src/test/java/org/apache/fineract/infrastructure/core/DataSourcePerTenantServiceFactoryTest.java @@ -155,9 +155,16 @@ public class DataSourcePerTenantServiceFactoryTest { given(tenantHikariConfig.isAutoCommit()).willReturn(MASTER_DB_AUTO_COMMIT_ENABLED); given(hikariDataSourceFactory.create(any())).willReturn(mock(HikariDataSource.class)); + + FineractProperties.FineractConfigProperties configProperties = new FineractProperties.FineractConfigProperties(); + configProperties.setMinPoolSize(-1); + configProperties.setMaxPoolSize(-1); + FineractProperties.FineractTenantProperties tenantPropertiesMock = mock(FineractProperties.FineractTenantProperties.class); given(tenantPropertiesMock.getEncryption()).willReturn(MASTER_ENCRYPTION); given(tenantPropertiesMock.getMasterPassword()).willReturn(MASTER_MASTER_PASSWORD); + + given(tenantPropertiesMock.getConfig()).willReturn(configProperties); given(fineractProperties.getTenant()).willReturn(tenantPropertiesMock); given(databasePasswordEncryptor.isMasterPasswordHashValid(any())).willReturn(true); @@ -195,6 +202,104 @@ public class DataSourcePerTenantServiceFactoryTest { assertEquals(MASTER_DB_AUTO_COMMIT_ENABLED, hikariConfig.isAutoCommit()); } + @Test + void testCreateNewDataSourceFor_ShouldOverridesMinPoolConfiguration_WhenConfigured() { + // given + FineractProperties.FineractModeProperties modeProperties = createModeProps(MASTER_DB_AUTO_COMMIT_ENABLED, + MASTER_DB_AUTO_COMMIT_ENABLED, MASTER_DB_AUTO_COMMIT_ENABLED, MASTER_DB_AUTO_COMMIT_ENABLED); + given(fineractProperties.getMode()).willReturn(modeProperties); + + int minPoolSize = 10; + + FineractProperties.FineractConfigProperties config = fineractProperties.getTenant().getConfig(); + config.setMinPoolSize(minPoolSize); + + // when + DataSource dataSource = underTest.createNewDataSourceFor(defaultTenant.getConnection()); + + // then + assertNotNull(dataSource); + verify(hikariDataSourceFactory).create(hikariConfigCaptor.capture()); + HikariConfig hikariConfig = hikariConfigCaptor.getValue(); + assertFalse(hikariConfig.isReadOnly()); + assertEquals(MASTER_DB_JDBC_URL, hikariConfig.getJdbcUrl()); + assertEquals(MASTER_DB_SCHEMA_NAME + "_pool", hikariConfig.getPoolName()); + assertEquals(MASTER_DB_USERNAME, hikariConfig.getUsername()); + assertEquals(MASTER_DB_PASSWORD, hikariConfig.getPassword()); + assertEquals(minPoolSize, hikariConfig.getMinimumIdle()); + assertEquals(MASTER_DB_MAX_ACTIVE, hikariConfig.getMaximumPoolSize()); + assertEquals(MASTER_DB_VALIDATION_INTERVAL, hikariConfig.getValidationTimeout()); + assertEquals(MASTER_DB_DRIVER_CLASS_NAME, hikariConfig.getDriverClassName()); + assertEquals(MASTER_DB_CONN_TEST_QUERY, hikariConfig.getConnectionTestQuery()); + assertEquals(MASTER_DB_AUTO_COMMIT_ENABLED, hikariConfig.isAutoCommit()); + } + + @Test + void testCreateNewDataSourceFor_ShouldOverridesMaxPoolConfiguration_WhenConfigured() { + // given + FineractProperties.FineractModeProperties modeProperties = createModeProps(MASTER_DB_AUTO_COMMIT_ENABLED, + MASTER_DB_AUTO_COMMIT_ENABLED, MASTER_DB_AUTO_COMMIT_ENABLED, MASTER_DB_AUTO_COMMIT_ENABLED); + given(fineractProperties.getMode()).willReturn(modeProperties); + + int maxPoolSize = 10; + + FineractProperties.FineractConfigProperties config = fineractProperties.getTenant().getConfig(); + config.setMaxPoolSize(maxPoolSize); + + // when + DataSource dataSource = underTest.createNewDataSourceFor(defaultTenant.getConnection()); + + // then + assertNotNull(dataSource); + verify(hikariDataSourceFactory).create(hikariConfigCaptor.capture()); + HikariConfig hikariConfig = hikariConfigCaptor.getValue(); + assertFalse(hikariConfig.isReadOnly()); + assertEquals(MASTER_DB_JDBC_URL, hikariConfig.getJdbcUrl()); + assertEquals(MASTER_DB_SCHEMA_NAME + "_pool", hikariConfig.getPoolName()); + assertEquals(MASTER_DB_USERNAME, hikariConfig.getUsername()); + assertEquals(MASTER_DB_PASSWORD, hikariConfig.getPassword()); + assertEquals(MASTER_DB_INITIAL_SIZE, hikariConfig.getMinimumIdle()); + assertEquals(maxPoolSize, hikariConfig.getMaximumPoolSize()); + assertEquals(MASTER_DB_VALIDATION_INTERVAL, hikariConfig.getValidationTimeout()); + assertEquals(MASTER_DB_DRIVER_CLASS_NAME, hikariConfig.getDriverClassName()); + assertEquals(MASTER_DB_CONN_TEST_QUERY, hikariConfig.getConnectionTestQuery()); + assertEquals(MASTER_DB_AUTO_COMMIT_ENABLED, hikariConfig.isAutoCommit()); + } + + @Test + void testCreateNewDataSourceFor_ShouldOverridesMinAndMaxPoolConfiguration_WhenBothConfigured() { + // given + FineractProperties.FineractModeProperties modeProperties = createModeProps(MASTER_DB_AUTO_COMMIT_ENABLED, + MASTER_DB_AUTO_COMMIT_ENABLED, MASTER_DB_AUTO_COMMIT_ENABLED, MASTER_DB_AUTO_COMMIT_ENABLED); + given(fineractProperties.getMode()).willReturn(modeProperties); + + int minPoolSize = 10; + int maxPoolSize = 10; + + FineractProperties.FineractConfigProperties config = fineractProperties.getTenant().getConfig(); + config.setMinPoolSize(minPoolSize); + config.setMaxPoolSize(maxPoolSize); + + // when + DataSource dataSource = underTest.createNewDataSourceFor(defaultTenant.getConnection()); + + // then + assertNotNull(dataSource); + verify(hikariDataSourceFactory).create(hikariConfigCaptor.capture()); + HikariConfig hikariConfig = hikariConfigCaptor.getValue(); + assertFalse(hikariConfig.isReadOnly()); + assertEquals(MASTER_DB_JDBC_URL, hikariConfig.getJdbcUrl()); + assertEquals(MASTER_DB_SCHEMA_NAME + "_pool", hikariConfig.getPoolName()); + assertEquals(MASTER_DB_USERNAME, hikariConfig.getUsername()); + assertEquals(MASTER_DB_PASSWORD, hikariConfig.getPassword()); + assertEquals(minPoolSize, hikariConfig.getMinimumIdle()); + assertEquals(maxPoolSize, hikariConfig.getMaximumPoolSize()); + assertEquals(MASTER_DB_VALIDATION_INTERVAL, hikariConfig.getValidationTimeout()); + assertEquals(MASTER_DB_DRIVER_CLASS_NAME, hikariConfig.getDriverClassName()); + assertEquals(MASTER_DB_CONN_TEST_QUERY, hikariConfig.getConnectionTestQuery()); + assertEquals(MASTER_DB_AUTO_COMMIT_ENABLED, hikariConfig.isAutoCommit()); + } + @Test void testCreateNewDataSourceFor_ShouldUseReadOnlyConfiguration_WhenInReadOnlyMode() { // given