FINERACT-2081: Avoid creating unnecessary connection pools

This commit is contained in:
Arnold Galovics
2024-12-11 17:26:07 +01:00
committed by Arnold Gálovics
parent bb8480a03d
commit 263866c03a
8 changed files with 32 additions and 29 deletions
@@ -26,6 +26,7 @@ import lombok.extern.slf4j.Slf4j;
import org.apache.fineract.infrastructure.configuration.exception.GlobalConfigurationException; import org.apache.fineract.infrastructure.configuration.exception.GlobalConfigurationException;
import org.apache.fineract.infrastructure.core.domain.FineractPlatformTenant; import org.apache.fineract.infrastructure.core.domain.FineractPlatformTenant;
import org.apache.fineract.infrastructure.core.service.JdbcTemplateFactory; import org.apache.fineract.infrastructure.core.service.JdbcTemplateFactory;
import org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil;
import org.apache.fineract.infrastructure.core.service.tenant.TenantDetailsService; import org.apache.fineract.infrastructure.core.service.tenant.TenantDetailsService;
import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.InitializingBean;
import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.JdbcTemplate;
@@ -50,6 +51,7 @@ public class GlobalConfigurationValidationService implements InitializingBean {
if (isNotEmpty(tenants)) { if (isNotEmpty(tenants)) {
for (FineractPlatformTenant tenant : tenants) { for (FineractPlatformTenant tenant : tenants) {
ThreadLocalContextUtil.setTenant(tenant);
validateGlobalConfigurationForIndividualTenant(tenant); validateGlobalConfigurationForIndividualTenant(tenant);
} }
} }
@@ -18,10 +18,9 @@
*/ */
package org.apache.fineract.infrastructure.core.service; package org.apache.fineract.infrastructure.core.service;
import javax.sql.DataSource;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.apache.fineract.infrastructure.core.domain.FineractPlatformTenant; import org.apache.fineract.infrastructure.core.domain.FineractPlatformTenant;
import org.apache.fineract.infrastructure.core.service.migration.TenantDataSourceFactory; import org.apache.fineract.infrastructure.core.service.database.RoutingDataSource;
import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@@ -30,15 +29,13 @@ import org.springframework.stereotype.Component;
@Component @Component
public class JdbcTemplateFactory { public class JdbcTemplateFactory {
private final TenantDataSourceFactory tenantDataSourceFactory; private final RoutingDataSource routingDataSource;
public JdbcTemplate create(FineractPlatformTenant tenant) { public JdbcTemplate create(FineractPlatformTenant tenant) {
DataSource tenantDataSource = tenantDataSourceFactory.create(tenant); return new JdbcTemplate(routingDataSource);
return new JdbcTemplate(tenantDataSource);
} }
public NamedParameterJdbcTemplate createNamedParameterJdbcTemplate(FineractPlatformTenant tenant) { public NamedParameterJdbcTemplate createNamedParameterJdbcTemplate(FineractPlatformTenant tenant) {
DataSource tenantDataSource = tenantDataSourceFactory.create(tenant); return new NamedParameterJdbcTemplate(routingDataSource);
return new NamedParameterJdbcTemplate(tenantDataSource);
} }
} }
@@ -22,7 +22,6 @@ import static org.apache.fineract.infrastructure.core.domain.FineractPlatformTen
import static org.apache.fineract.infrastructure.core.domain.FineractPlatformTenantConnection.toProtocol; import static org.apache.fineract.infrastructure.core.domain.FineractPlatformTenantConnection.toProtocol;
import com.zaxxer.hikari.HikariDataSource; import com.zaxxer.hikari.HikariDataSource;
import javax.sql.DataSource;
import org.apache.fineract.infrastructure.core.domain.FineractPlatformTenant; import org.apache.fineract.infrastructure.core.domain.FineractPlatformTenant;
import org.apache.fineract.infrastructure.core.domain.FineractPlatformTenantConnection; import org.apache.fineract.infrastructure.core.domain.FineractPlatformTenantConnection;
import org.apache.fineract.infrastructure.core.service.database.DatabasePasswordEncryptor; import org.apache.fineract.infrastructure.core.service.database.DatabasePasswordEncryptor;
@@ -48,7 +47,7 @@ public class TenantDataSourceFactory {
this.databasePasswordEncryptor = databasePasswordEncryptor; this.databasePasswordEncryptor = databasePasswordEncryptor;
} }
public DataSource create(FineractPlatformTenant tenant) { public HikariDataSource create(FineractPlatformTenant tenant) {
HikariDataSource dataSource = new HikariDataSource(); HikariDataSource dataSource = new HikariDataSource();
dataSource.setDriverClassName(tenantDataSource.getDriverClassName()); dataSource.setDriverClassName(tenantDataSource.getDriverClassName());
dataSource.setDataSourceProperties(tenantDataSource.getDataSourceProperties()); dataSource.setDataSourceProperties(tenantDataSource.getDataSourceProperties());
@@ -30,6 +30,7 @@ import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.collections4.CollectionUtils;
import org.apache.fineract.infrastructure.core.domain.FineractPlatformTenant; import org.apache.fineract.infrastructure.core.domain.FineractPlatformTenant;
import org.apache.fineract.infrastructure.core.service.JdbcTemplateFactory; import org.apache.fineract.infrastructure.core.service.JdbcTemplateFactory;
import org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil;
import org.apache.fineract.infrastructure.core.service.tenant.TenantDetailsService; import org.apache.fineract.infrastructure.core.service.tenant.TenantDetailsService;
import org.apache.fineract.infrastructure.event.business.domain.BulkBusinessEvent; import org.apache.fineract.infrastructure.event.business.domain.BulkBusinessEvent;
import org.apache.fineract.infrastructure.event.business.domain.BusinessEvent; import org.apache.fineract.infrastructure.event.business.domain.BusinessEvent;
@@ -62,6 +63,7 @@ public class ExternalEventConfigurationValidationService implements Initializing
if (isNotEmpty(tenants)) { if (isNotEmpty(tenants)) {
for (FineractPlatformTenant tenant : tenants) { for (FineractPlatformTenant tenant : tenants) {
ThreadLocalContextUtil.setTenant(tenant);
validateEventConfigurationForIndividualTenant(tenant, eventClasses); validateEventConfigurationForIndividualTenant(tenant, eventClasses);
} }
} }
@@ -21,6 +21,7 @@ package org.apache.fineract.infrastructure.core.service.migration;
import static org.apache.commons.collections4.CollectionUtils.isNotEmpty; import static org.apache.commons.collections4.CollectionUtils.isNotEmpty;
import static org.apache.commons.lang3.StringUtils.isNotBlank; import static org.apache.commons.lang3.StringUtils.isNotBlank;
import com.zaxxer.hikari.HikariDataSource;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
@@ -179,19 +180,20 @@ public class TenantDatabaseUpgradeService implements InitializingBean {
*/ */
private void upgradeIndividualTenant(FineractPlatformTenant tenant) throws LiquibaseException { private void upgradeIndividualTenant(FineractPlatformTenant tenant) throws LiquibaseException {
log.info("Upgrade for tenant {} has started", tenant.getTenantIdentifier()); log.info("Upgrade for tenant {} has started", tenant.getTenantIdentifier());
DataSource tenantDataSource = tenantDataSourceFactory.create(tenant); try (HikariDataSource tenantDataSource = tenantDataSourceFactory.create(tenant)) {
// 'initial_switch' and 'custom_changelog' contexts should be controlled by the application configuration // 'initial_switch' and 'custom_changelog' contexts should be controlled by the application configuration
// settings, and we should not use them to control the script order // settings, and we should not use them to control the script order
if (databaseStateVerifier.isFirstLiquibaseMigration(tenantDataSource)) { if (databaseStateVerifier.isFirstLiquibaseMigration(tenantDataSource)) {
ExtendedSpringLiquibase liquibase = liquibaseFactory.create(tenantDataSource, TENANT_DB_CONTEXT, CUSTOM_CHANGELOG_CONTEXT, ExtendedSpringLiquibase liquibase = liquibaseFactory.create(tenantDataSource, TENANT_DB_CONTEXT, CUSTOM_CHANGELOG_CONTEXT,
INITIAL_SWITCH_CONTEXT, tenant.getTenantIdentifier()); INITIAL_SWITCH_CONTEXT, tenant.getTenantIdentifier());
applyInitialLiquibase(tenantDataSource, liquibase, tenant.getTenantIdentifier(), applyInitialLiquibase(tenantDataSource, liquibase, tenant.getTenantIdentifier(),
(ds) -> !databaseStateVerifier.isTenantOnLatestUpgradableVersion(ds)); (ds) -> !databaseStateVerifier.isTenantOnLatestUpgradableVersion(ds));
}
SpringLiquibase tenantLiquibase = liquibaseFactory.create(tenantDataSource, TENANT_DB_CONTEXT, CUSTOM_CHANGELOG_CONTEXT,
tenant.getTenantIdentifier());
tenantLiquibase.afterPropertiesSet();
log.info("Upgrade for tenant {} has finished", tenant.getTenantIdentifier());
} }
SpringLiquibase tenantLiquibase = liquibaseFactory.create(tenantDataSource, TENANT_DB_CONTEXT, CUSTOM_CHANGELOG_CONTEXT,
tenant.getTenantIdentifier());
tenantLiquibase.afterPropertiesSet();
log.info("Upgrade for tenant {} has finished", tenant.getTenantIdentifier());
} }
private void applyInitialLiquibase(DataSource dataSource, ExtendedSpringLiquibase liquibase, String id, private void applyInitialLiquibase(DataSource dataSource, ExtendedSpringLiquibase liquibase, String id,
@@ -59,11 +59,11 @@ public class StuckJobListener implements ApplicationListener<ContextRefreshedEve
if (!jobRegistry.getJobNames().isEmpty()) { if (!jobRegistry.getJobNames().isEmpty()) {
List<FineractPlatformTenant> allTenants = tenantDetailsService.findAllTenants(); List<FineractPlatformTenant> allTenants = tenantDetailsService.findAllTenants();
allTenants.forEach(tenant -> { allTenants.forEach(tenant -> {
ThreadLocalContextUtil.setTenant(tenant);
NamedParameterJdbcTemplate namedParameterJdbcTemplate = jdbcTemplateFactory.createNamedParameterJdbcTemplate(tenant); NamedParameterJdbcTemplate namedParameterJdbcTemplate = jdbcTemplateFactory.createNamedParameterJdbcTemplate(tenant);
List<String> stuckJobNames = jobExecutionRepository.getStuckJobNames(namedParameterJdbcTemplate); List<String> stuckJobNames = jobExecutionRepository.getStuckJobNames(namedParameterJdbcTemplate);
if (!stuckJobNames.isEmpty()) { if (!stuckJobNames.isEmpty()) {
try { try {
ThreadLocalContextUtil.setTenant(tenant);
HashMap<BusinessDateType, LocalDate> businessDates = businessDateReadPlatformService.getBusinessDates(); HashMap<BusinessDateType, LocalDate> businessDates = businessDateReadPlatformService.getBusinessDates();
ThreadLocalContextUtil.setActionContext(ActionContext.DEFAULT); ThreadLocalContextUtil.setActionContext(ActionContext.DEFAULT);
ThreadLocalContextUtil.setBusinessDates(businessDates); ThreadLocalContextUtil.setBusinessDates(businessDates);
@@ -24,7 +24,6 @@ import static org.mockito.Mockito.mock;
import com.zaxxer.hikari.HikariDataSource; import com.zaxxer.hikari.HikariDataSource;
import java.util.List; import java.util.List;
import javax.sql.DataSource;
import liquibase.change.custom.CustomTaskChange; import liquibase.change.custom.CustomTaskChange;
import okhttp3.OkHttpClient; import okhttp3.OkHttpClient;
import org.apache.fineract.infrastructure.core.config.FineractProperties; import org.apache.fineract.infrastructure.core.config.FineractProperties;
@@ -33,6 +32,7 @@ import org.apache.fineract.infrastructure.core.service.database.DatabaseIndepend
import org.apache.fineract.infrastructure.core.service.database.DatabasePasswordEncryptor; import org.apache.fineract.infrastructure.core.service.database.DatabasePasswordEncryptor;
import org.apache.fineract.infrastructure.core.service.database.DatabaseType; import org.apache.fineract.infrastructure.core.service.database.DatabaseType;
import org.apache.fineract.infrastructure.core.service.database.DatabaseTypeResolver; import org.apache.fineract.infrastructure.core.service.database.DatabaseTypeResolver;
import org.apache.fineract.infrastructure.core.service.database.RoutingDataSource;
import org.apache.fineract.infrastructure.core.service.migration.ExtendedSpringLiquibaseFactory; import org.apache.fineract.infrastructure.core.service.migration.ExtendedSpringLiquibaseFactory;
import org.apache.fineract.infrastructure.core.service.migration.TenantDataSourceFactory; import org.apache.fineract.infrastructure.core.service.migration.TenantDataSourceFactory;
import org.apache.fineract.infrastructure.core.service.migration.TenantDatabaseStateVerifier; import org.apache.fineract.infrastructure.core.service.migration.TenantDatabaseStateVerifier;
@@ -94,8 +94,8 @@ public class TestConfiguration {
return new TenantDataSourceFactory(null, databasePasswordEncryptor) { return new TenantDataSourceFactory(null, databasePasswordEncryptor) {
@Override @Override
public DataSource create(FineractPlatformTenant tenant) { public HikariDataSource create(FineractPlatformTenant tenant) {
return mock(DataSource.class); return mock(HikariDataSource.class);
} }
}; };
} }
@@ -111,8 +111,8 @@ public class TestConfiguration {
* DataSource with Mockito RETURNS_MOCKS black magic. * DataSource with Mockito RETURNS_MOCKS black magic.
*/ */
@Bean @Bean
public DataSource hikariTenantDataSource() { public RoutingDataSource hikariTenantDataSource() {
HikariDataSource mockDataSource = mock(HikariDataSource.class, Mockito.RETURNS_MOCKS); RoutingDataSource mockDataSource = mock(RoutingDataSource.class, Mockito.RETURNS_MOCKS);
return mockDataSource; return mockDataSource;
} }
@@ -31,6 +31,7 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoInteractions; import static org.mockito.Mockito.verifyNoInteractions;
import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.verifyNoMoreInteractions;
import com.zaxxer.hikari.HikariDataSource;
import io.cucumber.java8.En; import io.cucumber.java8.En;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
@@ -66,7 +67,7 @@ public class LiquibaseStepDefinitions implements En {
private TenantDatabaseUpgradeService tenantDatabaseUpgradeService; private TenantDatabaseUpgradeService tenantDatabaseUpgradeService;
private List<FineractPlatformTenant> allTenants; private List<FineractPlatformTenant> allTenants;
private SchemaUpgradeNeededException executionException; private SchemaUpgradeNeededException executionException;
private DataSource defaultTenantDataSource; private HikariDataSource defaultTenantDataSource;
private Environment environment; private Environment environment;
public LiquibaseStepDefinitions() { public LiquibaseStepDefinitions() {
@@ -174,7 +175,7 @@ public class LiquibaseStepDefinitions implements En {
initialTenantStoreLiquibase = mock(ExtendedSpringLiquibase.class); initialTenantStoreLiquibase = mock(ExtendedSpringLiquibase.class);
tenantStoreLiquibase = mock(ExtendedSpringLiquibase.class); tenantStoreLiquibase = mock(ExtendedSpringLiquibase.class);
defaultTenantDataSource = mock(DataSource.class); defaultTenantDataSource = mock(HikariDataSource.class);
TenantPasswordEncryptionTask tenantPasswordEncryptor = mock(TenantPasswordEncryptionTask.class); TenantPasswordEncryptionTask tenantPasswordEncryptor = mock(TenantPasswordEncryptionTask.class);