From bc1e758c743a25a03da8d5d86e38e6887ecfe673 Mon Sep 17 00:00:00 2001
From: Arnold Galovics
Date: Sun, 12 Mar 2023 19:14:00 +0100
Subject: [PATCH] FINERACT-1724: Making global configuration caching
transaction bound
---
.../cache/CacheApiConstants.java | 2 +-
.../command/UpdateCacheCommandHandler.java | 6 +-
.../RuntimeDelegatingCacheManager.java | 84 ++++++++-----------
.../domain/ConfigurationDomainServiceJpa.java | 33 ++------
.../GlobalConfigurationRepositoryWrapper.java | 9 ++
.../core/config/FineractProperties.java | 7 ++
.../core/config/{ => cache}/CacheConfig.java | 19 ++++-
.../SpecifiedCacheSupportingCacheManager.java | 82 ++++++++++++++++++
.../cache/TransactionBoundCacheManager.java | 52 ++++++++++++
.../jpa/DiagnosticsEntityManager.java | 61 ++++++++++++++
.../jpa/StatementLoggingCustomizer.java | 37 ++++++++
.../StatementLoggingCustomizerCondition.java | 30 +++++++
.../ExtendedJpaTransactionManager.java | 22 ++++-
.../TransactionLifecycleCallback.java | 28 +++++++
.../jobs/ScheduledJobRunnerConfig.java | 6 +-
.../src/main/resources/application.properties | 2 +
.../resources/application-test.properties | 2 +
17 files changed, 400 insertions(+), 82 deletions(-)
rename fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/config/{ => cache}/CacheConfig.java (78%)
create mode 100644 fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/config/cache/SpecifiedCacheSupportingCacheManager.java
create mode 100644 fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/config/cache/TransactionBoundCacheManager.java
create mode 100644 fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/diagnostics/jpa/DiagnosticsEntityManager.java
create mode 100644 fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/diagnostics/jpa/StatementLoggingCustomizer.java
create mode 100644 fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/diagnostics/jpa/StatementLoggingCustomizerCondition.java
create mode 100644 fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/persistence/TransactionLifecycleCallback.java
diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/cache/CacheApiConstants.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/cache/CacheApiConstants.java
index 24e2468ccc0..a56acf7868e 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/cache/CacheApiConstants.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/cache/CacheApiConstants.java
@@ -25,6 +25,6 @@ private CacheApiConstants() {
}
public static final String RESOURCE_NAME = "CACHE";
- public static final String cacheTypeParameter = "cacheType";
+ public static final String CACHE_TYPE_PARAMETER = "cacheType";
}
diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/cache/command/UpdateCacheCommandHandler.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/cache/command/UpdateCacheCommandHandler.java
index 26858cd7fa4..99d90afd2a3 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/cache/command/UpdateCacheCommandHandler.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/cache/command/UpdateCacheCommandHandler.java
@@ -48,7 +48,7 @@
public class UpdateCacheCommandHandler implements NewCommandSourceHandler {
private final CacheWritePlatformService cacheService;
- private static final Set REQUEST_DATA_PARAMETERS = new HashSet<>(Arrays.asList(CacheApiConstants.cacheTypeParameter));
+ private static final Set REQUEST_DATA_PARAMETERS = new HashSet<>(Arrays.asList(CacheApiConstants.CACHE_TYPE_PARAMETER));
@Autowired
public UpdateCacheCommandHandler(final CacheWritePlatformService cacheService) {
@@ -72,8 +72,8 @@ public CommandProcessingResult processCommand(final JsonCommand command) {
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors)
.resource(CacheApiConstants.RESOURCE_NAME.toLowerCase());
- final int cacheTypeEnum = command.integerValueSansLocaleOfParameterNamed(CacheApiConstants.cacheTypeParameter);
- baseDataValidator.reset().parameter(CacheApiConstants.cacheTypeParameter).value(Integer.valueOf(cacheTypeEnum)).notNull()
+ final int cacheTypeEnum = command.integerValueSansLocaleOfParameterNamed(CacheApiConstants.CACHE_TYPE_PARAMETER);
+ baseDataValidator.reset().parameter(CacheApiConstants.CACHE_TYPE_PARAMETER).value(Integer.valueOf(cacheTypeEnum)).notNull()
.isOneOfTheseValues(Integer.valueOf(1), Integer.valueOf(2), Integer.valueOf(3));
if (!dataValidationErrors.isEmpty()) {
diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/cache/service/RuntimeDelegatingCacheManager.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/cache/service/RuntimeDelegatingCacheManager.java
index 74225c10389..a15ae0130b2 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/cache/service/RuntimeDelegatingCacheManager.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/cache/service/RuntimeDelegatingCacheManager.java
@@ -22,17 +22,17 @@
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
import org.apache.fineract.infrastructure.cache.CacheApiConstants;
import org.apache.fineract.infrastructure.cache.CacheEnumerations;
import org.apache.fineract.infrastructure.cache.data.CacheData;
import org.apache.fineract.infrastructure.cache.domain.CacheType;
import org.apache.fineract.infrastructure.core.data.EnumOptionData;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
-import org.springframework.cache.jcache.JCacheCacheManager;
import org.springframework.cache.support.NoOpCacheManager;
import org.springframework.stereotype.Component;
@@ -43,49 +43,43 @@
* database on startup and allow user to switch implementation through UI/API
*/
@Component(value = "runtimeDelegatingCacheManager")
-public class RuntimeDelegatingCacheManager implements CacheManager {
-
- private static final Logger LOG = LoggerFactory.getLogger(RuntimeDelegatingCacheManager.class);
-
- private final CacheManager cacheManager;
- private final CacheManager noOpCacheManager = new NoOpCacheManager();
+@RequiredArgsConstructor
+@Slf4j
+public class RuntimeDelegatingCacheManager implements CacheManager, InitializingBean {
+
+ @Qualifier("ehCacheManager")
+ private final CacheManager ehCacheManager;
+ @Qualifier("defaultCacheManager")
+ private final CacheManager defaultCacheManager;
private CacheManager currentCacheManager;
- @Autowired
- public RuntimeDelegatingCacheManager(final JCacheCacheManager cacheManager) {
- this.cacheManager = cacheManager;
- this.currentCacheManager = this.noOpCacheManager;
+ @Override
+ public void afterPropertiesSet() throws Exception {
+ currentCacheManager = defaultCacheManager;
}
@Override
public Cache getCache(final String name) {
- return this.currentCacheManager.getCache(name);
+ return currentCacheManager.getCache(name);
}
@Override
public Collection getCacheNames() {
- return this.currentCacheManager.getCacheNames();
+ return currentCacheManager.getCacheNames();
}
public Collection retrieveAll() {
- final boolean noCacheEnabled = this.currentCacheManager instanceof NoOpCacheManager;
- final boolean ehcacheEnabled = this.currentCacheManager instanceof JCacheCacheManager;
-
- // final boolean distributedCacheEnabled = false;
+ final boolean noCacheEnabled = currentCacheManager == defaultCacheManager;
+ final boolean ehCacheEnabled = currentCacheManager == ehCacheManager;
final EnumOptionData noCacheType = CacheEnumerations.cacheType(CacheType.NO_CACHE);
final EnumOptionData singleNodeCacheType = CacheEnumerations.cacheType(CacheType.SINGLE_NODE);
- // final EnumOptionData multiNodeCacheType =
- // CacheEnumerations.cacheType(CacheType.MULTI_NODE);
final CacheData noCache = CacheData.instance(noCacheType, noCacheEnabled);
- final CacheData singleNodeCache = CacheData.instance(singleNodeCacheType, ehcacheEnabled);
- // final CacheData distributedCache =
- // CacheData.instance(multiNodeCacheType, distributedCacheEnabled);
+ final CacheData singleNodeCache = CacheData.instance(singleNodeCacheType, ehCacheEnabled);
- final Collection caches = Arrays.asList(noCache, singleNodeCache);
- return caches;
+ return Arrays.asList(noCache, singleNodeCache);
}
public Map switchToCache(final boolean ehcacheEnabled, final CacheType toCacheType) {
@@ -93,42 +87,38 @@ public Map switchToCache(final boolean ehcacheEnabled, final Cac
final Map changes = new HashMap<>();
final boolean noCacheEnabled = !ehcacheEnabled;
- final boolean distributedCacheEnabled = !ehcacheEnabled;
switch (toCacheType) {
- case INVALID:
- break;
- case NO_CACHE:
+ case INVALID -> {
+ log.warn("Invalid cache type used");
+ }
+ case NO_CACHE -> {
if (!noCacheEnabled) {
- changes.put(CacheApiConstants.cacheTypeParameter, toCacheType.getValue());
+ changes.put(CacheApiConstants.CACHE_TYPE_PARAMETER, toCacheType.getValue());
}
- this.currentCacheManager = this.noOpCacheManager;
- break;
- case SINGLE_NODE:
+ currentCacheManager = defaultCacheManager;
+ }
+ case SINGLE_NODE -> {
if (!ehcacheEnabled) {
- changes.put(CacheApiConstants.cacheTypeParameter, toCacheType.getValue());
+ changes.put(CacheApiConstants.CACHE_TYPE_PARAMETER, toCacheType.getValue());
clearEhCache();
}
- this.currentCacheManager = this.cacheManager;
+ currentCacheManager = ehCacheManager;
- if (this.currentCacheManager.getCacheNames().size() == 0) {
- LOG.error("No caches configured for activated CacheManager {}", this.currentCacheManager);
- }
- break;
- case MULTI_NODE:
- if (!distributedCacheEnabled) {
- changes.put(CacheApiConstants.cacheTypeParameter, toCacheType.getValue());
+ if (currentCacheManager.getCacheNames().size() == 0) {
+ log.error("No caches configured for activated CacheManager {}", currentCacheManager);
}
- break;
+ }
+ case MULTI_NODE -> throw new UnsupportedOperationException("Multi node cache is not supported");
}
return changes;
}
private void clearEhCache() {
- Iterable cacheNames = cacheManager.getCacheNames();
+ Iterable cacheNames = ehCacheManager.getCacheNames();
for (String cacheName : cacheNames) {
- cacheManager.getCache(cacheName).clear();
+ ehCacheManager.getCache(cacheName).clear();
}
}
}
diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/domain/ConfigurationDomainServiceJpa.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/domain/ConfigurationDomainServiceJpa.java
index 5088b3c3828..a278c1c5ca2 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/domain/ConfigurationDomainServiceJpa.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/domain/ConfigurationDomainServiceJpa.java
@@ -19,23 +19,22 @@
package org.apache.fineract.infrastructure.configuration.domain;
import java.time.LocalDate;
-import java.util.HashMap;
-import java.util.Map;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.fineract.infrastructure.cache.domain.CacheType;
import org.apache.fineract.infrastructure.cache.domain.PlatformCache;
import org.apache.fineract.infrastructure.cache.domain.PlatformCacheRepository;
import org.apache.fineract.infrastructure.configuration.data.GlobalConfigurationPropertyData;
-import org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil;
import org.apache.fineract.useradministration.domain.Permission;
import org.apache.fineract.useradministration.domain.PermissionRepository;
import org.apache.fineract.useradministration.exception.PermissionNotFoundException;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
+@Slf4j
@Service
+@RequiredArgsConstructor
public class ConfigurationDomainServiceJpa implements ConfigurationDomainService {
public static final String ENABLE_BUSINESS_DATE = "enable_business_date";
@@ -55,15 +54,6 @@ public class ConfigurationDomainServiceJpa implements ConfigurationDomainService
private final PermissionRepository permissionRepository;
private final GlobalConfigurationRepositoryWrapper globalConfigurationRepository;
private final PlatformCacheRepository cacheTypeRepository;
- private static Map configurations = new HashMap<>();
-
- @Autowired
- public ConfigurationDomainServiceJpa(final PermissionRepository permissionRepository,
- final GlobalConfigurationRepositoryWrapper globalConfigurationRepository, final PlatformCacheRepository cacheTypeRepository) {
- this.permissionRepository = permissionRepository;
- this.globalConfigurationRepository = globalConfigurationRepository;
- this.cacheTypeRepository = cacheTypeRepository;
- }
@Override
public boolean isMakerCheckerEnabledForTask(final String taskPermissionCode) {
@@ -321,9 +311,7 @@ public Long getDailyTPTLimit() {
@Override
public void removeGlobalConfigurationPropertyDataFromCache(final String propertyName) {
- String identifier = ThreadLocalContextUtil.getTenant().getTenantIdentifier();
- String key = identifier + "_" + propertyName;
- configurations.remove(key);
+ globalConfigurationRepository.removeFromCache(propertyName);
}
@Override
@@ -389,15 +377,8 @@ public Long retrieveRelaxingDaysConfigForPivotDate() {
return property.getValue();
}
- @Cacheable(value = "configByName", key = "T(org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil).getTenant().getTenantIdentifier().concat(#propertyName)")
- public GlobalConfigurationPropertyData getGlobalConfigurationPropertyData(final String propertyName) {
- String identifier = ThreadLocalContextUtil.getTenant().getTenantIdentifier();
- String key = identifier + "_" + propertyName;
- if (!configurations.containsKey(key)) {
- GlobalConfigurationProperty configuration = this.globalConfigurationRepository.findOneByNameWithNotFoundDetection(propertyName);
- configurations.put(key, configuration.toData());
- }
- return configurations.get(key);
+ private GlobalConfigurationPropertyData getGlobalConfigurationPropertyData(final String propertyName) {
+ return globalConfigurationRepository.findOneByNameWithNotFoundDetection(propertyName).toData();
}
@Override
diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/domain/GlobalConfigurationRepositoryWrapper.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/domain/GlobalConfigurationRepositoryWrapper.java
index 7276bcd2276..3d91820f72d 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/domain/GlobalConfigurationRepositoryWrapper.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/domain/GlobalConfigurationRepositoryWrapper.java
@@ -18,8 +18,11 @@
*/
package org.apache.fineract.infrastructure.configuration.domain;
+import lombok.extern.slf4j.Slf4j;
import org.apache.fineract.infrastructure.configuration.exception.GlobalConfigurationPropertyNotFoundException;
import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.cache.annotation.CacheEvict;
+import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
/**
@@ -28,6 +31,7 @@
*
*/
@Service
+@Slf4j
public class GlobalConfigurationRepositoryWrapper {
private final GlobalConfigurationRepository repository;
@@ -37,6 +41,7 @@ public GlobalConfigurationRepositoryWrapper(final GlobalConfigurationRepository
this.repository = repository;
}
+ @Cacheable(value = "configByName", key = "T(org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil).getTenant().getTenantIdentifier().concat(#propertyName)")
public GlobalConfigurationProperty findOneByNameWithNotFoundDetection(final String propertyName) {
final GlobalConfigurationProperty property = this.repository.findOneByName(propertyName);
if (property == null) {
@@ -61,4 +66,8 @@ public void delete(final GlobalConfigurationProperty globalConfigurationProperty
this.repository.delete(globalConfigurationProperty);
}
+ @CacheEvict(value = "configByName", key = "T(org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil).getTenant().getTenantIdentifier().concat(#propertyName)")
+ public void removeFromCache(String propertyName) {
+ log.debug("Cache entry evicted {}", propertyName);
+ }
}
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 c9c88fb6120..2e5fc9d2a55 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
@@ -53,6 +53,7 @@ public class FineractProperties {
private FineractJobProperties job;
private FineractTemplateProperties template;
+ private FineractJpaProperties jpa;
@Getter
@Setter
@@ -248,4 +249,10 @@ public static class FineractTemplateProperties {
private List regexWhitelist;
}
+ @Getter
+ @Setter
+ public static class FineractJpaProperties {
+
+ private boolean statementLoggingEnabled;
+ }
}
diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/config/CacheConfig.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/config/cache/CacheConfig.java
similarity index 78%
rename from fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/config/CacheConfig.java
rename to fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/config/cache/CacheConfig.java
index e6117fa68c5..e4202ae3479 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/config/CacheConfig.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/config/cache/CacheConfig.java
@@ -17,7 +17,7 @@
* under the License.
*/
-package org.apache.fineract.infrastructure.core.config;
+package org.apache.fineract.infrastructure.core.config.cache;
import java.time.Duration;
import javax.cache.CacheManager;
@@ -28,20 +28,32 @@
import org.ehcache.config.builders.ResourcePoolsBuilder;
import org.ehcache.jsr107.Eh107Configuration;
import org.springframework.cache.jcache.JCacheCacheManager;
+import org.springframework.cache.support.NoOpCacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class CacheConfig {
+ public static final String CONFIG_BY_NAME_CACHE_NAME = "configByName";
+
+ @Bean
+ public TransactionBoundCacheManager defaultCacheManager(JCacheCacheManager ehCacheManager) {
+ SpecifiedCacheSupportingCacheManager cacheManager = new SpecifiedCacheSupportingCacheManager();
+ cacheManager.setNoOpCacheManager(new NoOpCacheManager());
+ cacheManager.setDelegateCacheManager(ehCacheManager);
+ cacheManager.setSupportedCaches(CONFIG_BY_NAME_CACHE_NAME);
+ return new TransactionBoundCacheManager(cacheManager);
+ }
+
@Bean
public JCacheCacheManager ehCacheManager() {
JCacheCacheManager jCacheCacheManager = new JCacheCacheManager();
- jCacheCacheManager.setCacheManager(getCustomCacheManager());
+ jCacheCacheManager.setCacheManager(getInternalEhCacheManager());
return jCacheCacheManager;
}
- private CacheManager getCustomCacheManager() {
+ private CacheManager getInternalEhCacheManager() {
CachingProvider provider = Caching.getCachingProvider();
CacheManager cacheManager = provider.getCacheManager();
@@ -61,6 +73,7 @@ private CacheManager getCustomCacheManager() {
cacheManager.createCache("codes", defaultTemplate);
cacheManager.createCache("hooks", defaultTemplate);
cacheManager.createCache("tfConfig", defaultTemplate);
+ cacheManager.createCache(CONFIG_BY_NAME_CACHE_NAME, defaultTemplate);
javax.cache.configuration.Configuration