Support cache eviction in microservice mode (#2503)

When running in microservice mode, updating configuration properties via
the management component does not automatically update caches in other
services.

This change introduces support for distributed cache eviction using
Spring Cloud Bus. It listens for remote tenant configuration events and
evicts the relevant cache key upon receipt.

Signed-off-by: Birk Blechschmidt <birk.blechschmidt@liebherr.com>
This commit is contained in:
Birk Blechschmidt
2025-07-11 08:16:37 +02:00
committed by GitHub
parent c5efce4ad8
commit f249544f5d

View File

@@ -30,6 +30,9 @@ import java.util.stream.Collectors;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.hawkbit.im.authentication.SpPermission;
import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
import org.eclipse.hawkbit.repository.event.remote.RemoteTenantAwareEvent;
import org.eclipse.hawkbit.repository.event.remote.TenantConfigurationDeletedEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.RemoteEntityEvent;
import org.eclipse.hawkbit.repository.exception.InsufficientPermissionException;
import org.eclipse.hawkbit.repository.exception.TenantConfigurationValidatorException;
import org.eclipse.hawkbit.repository.exception.TenantConfigurationValueChangeNotAllowedException;
@@ -49,11 +52,14 @@ import org.eclipse.hawkbit.tenancy.configuration.DurationHelper;
import org.eclipse.hawkbit.tenancy.configuration.PollingTime;
import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties;
import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cloud.bus.ServiceMatcher;
import org.springframework.context.ApplicationContext;
import org.springframework.context.event.EventListener;
import org.springframework.core.convert.support.ConfigurableConversionService;
import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.dao.ConcurrencyFailureException;
@@ -79,6 +85,8 @@ public class JpaTenantConfigurationManagement implements TenantConfigurationMana
private final CacheManager cacheManager;
private final AfterTransactionCommitExecutor afterCommitExecutor;
private ServiceMatcher serviceMatcher;
public JpaTenantConfigurationManagement(
final TenantConfigurationRepository tenantConfigurationRepository,
final TenantConfigurationProperties tenantConfigurationProperties,
@@ -91,6 +99,11 @@ public class JpaTenantConfigurationManagement implements TenantConfigurationMana
this.applicationContext = applicationContext;
}
@Autowired(required = false)
public void setServiceMatcher(final ServiceMatcher serviceMatcher) {
this.serviceMatcher = serviceMatcher;
}
@Override
@CacheEvict(value = "tenantConfiguration", key = "#configurationKeyName")
@Transactional
@@ -162,6 +175,36 @@ public class JpaTenantConfigurationManagement implements TenantConfigurationMana
return CONVERSION_SERVICE.convert(key.getDefaultValue(), propertyType);
}
/**
* Ensures that cache eviction takes place in microservice mode in case of deletions.
*
* @param event The event indicating that a configuration value has been deleted.
*/
@EventListener
public void onTenantConfigurationDeletedEvent(final TenantConfigurationDeletedEvent event) {
if (!shouldProcessRemoteTenantAwareEvent(event)) {
return;
}
evictCacheEntryByKeyIfPresent(event.getConfigKey());
}
/**
* Ensures that cache eviction takes place in microservice mode in case of creation or update events.
*
* @param event The event indicating that a configuration value has been created or updated.
*/
@EventListener
public void onTenantConfigurationRemoteEntityEvent(final RemoteEntityEvent<TenantConfiguration> event) {
if (!shouldProcessRemoteTenantAwareEvent(event)) {
return;
}
event.getEntity().ifPresent(tenantConfiguration -> {
evictCacheEntryByKeyIfPresent(tenantConfiguration.getKey());
});
}
@Override
public Function<Target, PollStatus> pollStatusResolver() {
final PollingTime pollingTime = new PollingTime(
@@ -339,4 +382,15 @@ public class JpaTenantConfigurationManagement implements TenantConfigurationMana
}
}
}
}
private void evictCacheEntryByKeyIfPresent(final String key) {
final Cache cache = cacheManager.getCache("tenantConfiguration");
if (cache != null) {
cache.evictIfPresent(key);
}
}
private boolean shouldProcessRemoteTenantAwareEvent(final RemoteTenantAwareEvent event) {
return serviceMatcher == null || !serviceMatcher.isFromSelf(event) && serviceMatcher.isForSelf(event);
}
}