Add cache for SoftwareModule, DistributionSet and Target types (#2784)
Signed-off-by: Avgustin Marinov <Avgustin.Marinov@bosch.com>
This commit is contained in:
@@ -15,12 +15,13 @@ import lombok.NoArgsConstructor;
|
||||
import org.eclipse.hawkbit.repository.event.entity.EntityDeletedEvent;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSetType;
|
||||
import org.eclipse.hawkbit.repository.model.TenantAwareBaseEntity;
|
||||
import org.eclipse.hawkbit.tenancy.TenantAwareCacheManager.CacheEvictEvent;
|
||||
|
||||
/**
|
||||
* Defines the remote event of deleting a {@link DistributionSetType}.
|
||||
*/
|
||||
@NoArgsConstructor // for serialization libs like jackson
|
||||
public class DistributionSetTypeDeletedEvent extends RemoteIdEvent implements EntityDeletedEvent {
|
||||
public class DistributionSetTypeDeletedEvent extends RemoteIdEvent implements EntityDeletedEvent, CacheEvictEvent {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@@ -15,12 +15,13 @@ import lombok.NoArgsConstructor;
|
||||
import org.eclipse.hawkbit.repository.event.entity.EntityDeletedEvent;
|
||||
import org.eclipse.hawkbit.repository.model.SoftwareModuleType;
|
||||
import org.eclipse.hawkbit.repository.model.TenantAwareBaseEntity;
|
||||
import org.eclipse.hawkbit.tenancy.TenantAwareCacheManager.CacheEvictEvent;
|
||||
|
||||
/**
|
||||
* Defines the remote event of deleting a {@link SoftwareModuleType}.
|
||||
*/
|
||||
@NoArgsConstructor // for serialization libs like jackson
|
||||
public class SoftwareModuleTypeDeletedEvent extends RemoteIdEvent implements EntityDeletedEvent {
|
||||
public class SoftwareModuleTypeDeletedEvent extends RemoteIdEvent implements EntityDeletedEvent, CacheEvictEvent {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@@ -15,12 +15,13 @@ import lombok.NoArgsConstructor;
|
||||
import org.eclipse.hawkbit.repository.event.entity.EntityDeletedEvent;
|
||||
import org.eclipse.hawkbit.repository.model.TargetType;
|
||||
import org.eclipse.hawkbit.repository.model.TenantAwareBaseEntity;
|
||||
import org.eclipse.hawkbit.tenancy.TenantAwareCacheManager.CacheEvictEvent;
|
||||
|
||||
/**
|
||||
* Defines the remote event of deleting a {@link TargetType}.
|
||||
*/
|
||||
@NoArgsConstructor // for serialization libs like jackson
|
||||
public class TargetTypeDeletedEvent extends RemoteIdEvent implements EntityDeletedEvent {
|
||||
public class TargetTypeDeletedEvent extends RemoteIdEvent implements EntityDeletedEvent, CacheEvictEvent {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@@ -14,12 +14,13 @@ import java.io.Serial;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.eclipse.hawkbit.repository.event.entity.EntityCreatedEvent;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSetType;
|
||||
import org.eclipse.hawkbit.tenancy.TenantAwareCacheManager.CacheEvictEvent;
|
||||
|
||||
/**
|
||||
* Defines the remote event of creating a new {@link DistributionSetType}.
|
||||
*/
|
||||
@NoArgsConstructor // for serialization libs like jackson
|
||||
public class DistributionSetTypeCreatedEvent extends RemoteEntityEvent<DistributionSetType> implements EntityCreatedEvent {
|
||||
public class DistributionSetTypeCreatedEvent extends RemoteEntityEvent<DistributionSetType> implements EntityCreatedEvent, CacheEvictEvent {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@@ -15,12 +15,13 @@ import lombok.NoArgsConstructor;
|
||||
import org.eclipse.hawkbit.repository.event.entity.EntityUpdatedEvent;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSetType;
|
||||
import org.eclipse.hawkbit.repository.model.SoftwareModuleType;
|
||||
import org.eclipse.hawkbit.tenancy.TenantAwareCacheManager.CacheEvictEvent;
|
||||
|
||||
/**
|
||||
* Defines the remote event for updating a {@link SoftwareModuleType}.
|
||||
*/
|
||||
@NoArgsConstructor // for serialization libs like jackson
|
||||
public class DistributionSetTypeUpdatedEvent extends RemoteEntityEvent<DistributionSetType> implements EntityUpdatedEvent {
|
||||
public class DistributionSetTypeUpdatedEvent extends RemoteEntityEvent<DistributionSetType> implements EntityUpdatedEvent, CacheEvictEvent {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@@ -14,12 +14,13 @@ import java.io.Serial;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.eclipse.hawkbit.repository.event.entity.EntityUpdatedEvent;
|
||||
import org.eclipse.hawkbit.repository.model.SoftwareModuleType;
|
||||
import org.eclipse.hawkbit.tenancy.TenantAwareCacheManager.CacheEvictEvent;
|
||||
|
||||
/**
|
||||
* Defines the remote event for updating a {@link SoftwareModuleType}.
|
||||
*/
|
||||
@NoArgsConstructor // for serialization libs like jackson
|
||||
public class SoftwareModuleTypeUpdatedEvent extends RemoteEntityEvent<SoftwareModuleType> implements EntityUpdatedEvent {
|
||||
public class SoftwareModuleTypeUpdatedEvent extends RemoteEntityEvent<SoftwareModuleType> implements EntityUpdatedEvent, CacheEvictEvent {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@@ -14,12 +14,13 @@ import java.io.Serial;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.eclipse.hawkbit.repository.event.entity.EntityUpdatedEvent;
|
||||
import org.eclipse.hawkbit.repository.model.SoftwareModule;
|
||||
import org.eclipse.hawkbit.tenancy.TenantAwareCacheManager.CacheEvictEvent;
|
||||
|
||||
/**
|
||||
* Defines the remote event for updating a {@link SoftwareModule}.
|
||||
*/
|
||||
@NoArgsConstructor // for serialization libs like jackson
|
||||
public class SoftwareModuleUpdatedEvent extends RemoteEntityEvent<SoftwareModule> implements EntityUpdatedEvent {
|
||||
public class SoftwareModuleUpdatedEvent extends RemoteEntityEvent<SoftwareModule> implements EntityUpdatedEvent, CacheEvictEvent {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@@ -14,12 +14,13 @@ import java.io.Serial;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.eclipse.hawkbit.repository.event.entity.EntityCreatedEvent;
|
||||
import org.eclipse.hawkbit.repository.model.TargetType;
|
||||
import org.eclipse.hawkbit.tenancy.TenantAwareCacheManager.CacheEvictEvent;
|
||||
|
||||
/**
|
||||
* Defines the remote event of creating a new {@link TargetType}.
|
||||
*/
|
||||
@NoArgsConstructor // for serialization libs like jackson
|
||||
public class TargetTypeCreatedEvent extends RemoteEntityEvent<TargetType> implements EntityCreatedEvent {
|
||||
public class TargetTypeCreatedEvent extends RemoteEntityEvent<TargetType> implements EntityCreatedEvent, CacheEvictEvent {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@@ -14,12 +14,13 @@ import java.io.Serial;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.eclipse.hawkbit.repository.event.entity.EntityUpdatedEvent;
|
||||
import org.eclipse.hawkbit.repository.model.TargetType;
|
||||
import org.eclipse.hawkbit.tenancy.TenantAwareCacheManager.CacheEvictEvent;
|
||||
|
||||
/**
|
||||
* Defines the remote event for updating a {@link TargetType}.
|
||||
*/
|
||||
@NoArgsConstructor // for serialization libs like jackson
|
||||
public class TargetTypeUpdatedEvent extends RemoteEntityEvent<TargetType> implements EntityUpdatedEvent {
|
||||
public class TargetTypeUpdatedEvent extends RemoteEntityEvent<TargetType> implements EntityUpdatedEvent, CacheEvictEvent {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@@ -134,7 +134,6 @@ abstract class AbstractJpaRepositoryManagement<T extends AbstractJpaBaseEntity,
|
||||
@Transactional
|
||||
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = TX_RT_MAX, backoff = @Backoff(delay = TX_RT_DELAY))
|
||||
public T create(final C create) {
|
||||
getCache().ifPresent(cache -> afterCommit(cache::clear));
|
||||
return jpaRepository.save(AccessController.Operation.CREATE, jpaEntity(create));
|
||||
}
|
||||
|
||||
@@ -142,46 +141,35 @@ abstract class AbstractJpaRepositoryManagement<T extends AbstractJpaBaseEntity,
|
||||
@Transactional
|
||||
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = TX_RT_MAX, backoff = @Backoff(delay = TX_RT_DELAY))
|
||||
public List<T> create(final Collection<C> create) {
|
||||
getCache().ifPresent(cache -> afterCommit(cache::clear));
|
||||
return jpaRepository.saveAll(AccessController.Operation.CREATE, create.stream().map(this::jpaEntity).toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public T get(final long id) {
|
||||
final Cache cache = getCache().orElse(null);
|
||||
if (cache == null) {
|
||||
return jpaRepository.getById(id);
|
||||
} else {
|
||||
final T cached = (T) cache.get(id);
|
||||
if (cached == null) {
|
||||
final T entity = jpaRepository.getById(id);
|
||||
if (entity != null) { // should not be null - but throw exception - but for sure check
|
||||
cache.put(id, entity);
|
||||
try {
|
||||
return cache.get(id, () -> jpaRepository.getById(id));
|
||||
} catch (final Cache.ValueRetrievalException e) {
|
||||
if (e.getCause() instanceof EntityNotFoundException enf) {
|
||||
throw enf;
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
return entity;
|
||||
} else {
|
||||
return cached;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public Optional<T> find(final long id) {
|
||||
final Cache cache = getCache().orElse(null);
|
||||
if (cache == null) {
|
||||
return jpaRepository.findById(id);
|
||||
} else {
|
||||
final T cached = (T) cache.get(id);
|
||||
if (cached == null) {
|
||||
final Optional<T> entity = jpaRepository.findById(id);
|
||||
// should not be null - but throw exception - but for sure check
|
||||
entity.ifPresent(t -> cache.put(id, t));
|
||||
return entity;
|
||||
} else {
|
||||
return Optional.of(cached);
|
||||
}
|
||||
// we cache only value - not optionals
|
||||
return Optional.ofNullable(cache.get(id, () -> jpaRepository.findById(id).orElse(null)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -226,7 +214,6 @@ abstract class AbstractJpaRepositoryManagement<T extends AbstractJpaBaseEntity,
|
||||
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = TX_RT_MAX, backoff = @Backoff(delay = TX_RT_DELAY))
|
||||
@SuppressWarnings("java:S1066") // javaS1066 - better readable that way
|
||||
public T update(final U update) {
|
||||
getCache().ifPresent(cache -> afterCommit(() -> cache.evict(update.getId())));
|
||||
final T entity = getValid(update.getId());
|
||||
// update getId has no setter in target JPA entity but shall have getter and the value shall be the same
|
||||
// otherwise the Utils will throw an exception that there is no counterpart setter for getId
|
||||
@@ -264,7 +251,6 @@ abstract class AbstractJpaRepositoryManagement<T extends AbstractJpaBaseEntity,
|
||||
if (toSave.isEmpty()) {
|
||||
return toUpdate;
|
||||
} else {
|
||||
getCache().ifPresent(cache -> afterCommit(() -> toSave.forEach(updated -> cache.evict(updated.getId()))));
|
||||
final List<T> savedEntities = jpaRepository.saveAll(toSave);
|
||||
final Map<Long, T> result = new HashMap<>(toUpdate);
|
||||
for (final T savedEntity : savedEntities) {
|
||||
@@ -278,7 +264,6 @@ abstract class AbstractJpaRepositoryManagement<T extends AbstractJpaBaseEntity,
|
||||
@Transactional
|
||||
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = TX_RT_MAX, backoff = @Backoff(delay = TX_RT_DELAY))
|
||||
public void delete(final long id) {
|
||||
getCache().ifPresent(cache -> afterCommit(() -> cache.evict(id)));
|
||||
delete0(List.of(id));
|
||||
}
|
||||
|
||||
@@ -286,7 +271,6 @@ abstract class AbstractJpaRepositoryManagement<T extends AbstractJpaBaseEntity,
|
||||
@Transactional
|
||||
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = TX_RT_MAX, backoff = @Backoff(delay = TX_RT_DELAY))
|
||||
public void delete(final Collection<Long> ids) {
|
||||
getCache().ifPresent(cache -> afterCommit(() -> ids.forEach(cache::evict)));
|
||||
delete0(ids);
|
||||
}
|
||||
|
||||
|
||||
@@ -36,7 +36,9 @@ import org.eclipse.hawkbit.repository.jpa.specifications.DistributionSetTypeSpec
|
||||
import org.eclipse.hawkbit.repository.jpa.utils.QuotaHelper;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSetType;
|
||||
import org.eclipse.hawkbit.repository.model.SoftwareModuleType;
|
||||
import org.eclipse.hawkbit.tenancy.TenantAwareCacheManager;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBooleanProperty;
|
||||
import org.springframework.cache.Cache;
|
||||
import org.springframework.dao.ConcurrencyFailureException;
|
||||
import org.springframework.retry.annotation.Backoff;
|
||||
import org.springframework.retry.annotation.Retryable;
|
||||
@@ -89,6 +91,11 @@ public class JpaDistributionSetTypeManagement
|
||||
super.delete0(ids);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Optional<Cache> getCache() {
|
||||
return Optional.of(TenantAwareCacheManager.getInstance().getCache(JpaDistributionSetType.class.getSimpleName()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<JpaDistributionSetType> findByKey(final String key) {
|
||||
return jpaRepository.findOne(DistributionSetTypeSpecification.byKey(key));
|
||||
|
||||
@@ -20,7 +20,9 @@ import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModuleType;
|
||||
import org.eclipse.hawkbit.repository.jpa.repository.DistributionSetTypeRepository;
|
||||
import org.eclipse.hawkbit.repository.jpa.repository.SoftwareModuleRepository;
|
||||
import org.eclipse.hawkbit.repository.jpa.repository.SoftwareModuleTypeRepository;
|
||||
import org.eclipse.hawkbit.tenancy.TenantAwareCacheManager;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBooleanProperty;
|
||||
import org.springframework.cache.Cache;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
@@ -42,6 +44,11 @@ public class JpaSoftwareModuleTypeManagement
|
||||
this.softwareModuleRepository = softwareModuleRepository;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Optional<Cache> getCache() {
|
||||
return Optional.of(TenantAwareCacheManager.getInstance().getCache(JpaSoftwareModuleType.class.getSimpleName()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<JpaSoftwareModuleType> findByKey(final String key) {
|
||||
return jpaRepository.findByKey(key);
|
||||
|
||||
@@ -31,7 +31,9 @@ import org.eclipse.hawkbit.repository.jpa.specifications.TargetTypeSpecification
|
||||
import org.eclipse.hawkbit.repository.jpa.utils.QuotaHelper;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSetType;
|
||||
import org.eclipse.hawkbit.repository.model.TargetType;
|
||||
import org.eclipse.hawkbit.tenancy.TenantAwareCacheManager;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBooleanProperty;
|
||||
import org.springframework.cache.Cache;
|
||||
import org.springframework.cache.annotation.Cacheable;
|
||||
import org.springframework.dao.ConcurrencyFailureException;
|
||||
import org.springframework.retry.annotation.Backoff;
|
||||
@@ -78,6 +80,12 @@ public class JpaTargetTypeManagement
|
||||
super.delete0(ids);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Optional<Cache> getCache() {
|
||||
return Optional.of(TenantAwareCacheManager.getInstance().getCache(JpaTargetType.class.getSimpleName()));
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
@Cacheable(value = CACHE_TARGET_TYPE_NAME, key = "#key")
|
||||
public Optional<TargetType> findByKey(final String key) {
|
||||
|
||||
@@ -36,7 +36,6 @@ import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSetType;
|
||||
import org.eclipse.hawkbit.repository.model.NamedEntity;
|
||||
import org.eclipse.hawkbit.repository.model.NamedVersionedEntity;
|
||||
import org.eclipse.hawkbit.repository.model.SoftwareModule;
|
||||
import org.eclipse.hawkbit.repository.test.matcher.Expect;
|
||||
import org.eclipse.hawkbit.repository.test.matcher.ExpectEvents;
|
||||
import org.junit.jupiter.api.Test;
|
||||
@@ -72,7 +71,8 @@ class DistributionSetTypeManagementTest extends AbstractRepositoryManagementTest
|
||||
|
||||
// remove OS
|
||||
distributionSetTypeManagement.unassignSoftwareModuleType(updatableType.getId(), osType.getId());
|
||||
assertThat(distributionSetTypeManagement.findByKey(USED_BY_DS_TYPE_KEY).orElseThrow().getMandatoryModuleTypes()).containsOnly(runtimeType);
|
||||
assertThat(distributionSetTypeManagement.findByKey(USED_BY_DS_TYPE_KEY).orElseThrow().getMandatoryModuleTypes()).containsOnly(
|
||||
runtimeType);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -12,16 +12,12 @@ package org.eclipse.hawkbit.repository.jpa.management;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import jakarta.validation.ConstraintViolationException;
|
||||
|
||||
import org.assertj.core.api.Assertions;
|
||||
import org.eclipse.hawkbit.repository.SoftwareModuleManagement;
|
||||
import org.eclipse.hawkbit.repository.SoftwareModuleTypeManagement;
|
||||
import org.eclipse.hawkbit.repository.SoftwareModuleTypeManagement.Create;
|
||||
import org.eclipse.hawkbit.repository.SoftwareModuleTypeManagement.Update;
|
||||
import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModuleType;
|
||||
import org.eclipse.hawkbit.repository.model.SoftwareModuleType;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
@@ -74,7 +74,7 @@ class TargetTypeManagementTest extends AbstractRepositoryManagementTest<TargetTy
|
||||
@ExpectEvents({ @Expect(type = TargetTypeUpdatedEvent.class) })
|
||||
void failIfReferNotExistingEntity() {
|
||||
verifyThrownExceptionBy(() -> targetTypeManagement.delete(NOT_EXIST_IDL), "TargetType");
|
||||
verifyThrownExceptionBy(() -> targetTypeManagement.update(Update.builder().id(NOT_EXIST_IDL).build()),"TargetType");
|
||||
verifyThrownExceptionBy(() -> targetTypeManagement.update(Update.builder().id(NOT_EXIST_IDL).build()), "TargetType");
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -27,13 +27,11 @@ import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.T
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.context.EnvironmentAware;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.test.context.TestPropertySource;
|
||||
|
||||
/**
|
||||
* Feature: Component Tests - Repository<br/>
|
||||
* Story: Tenant Configuration Management
|
||||
*/
|
||||
@TestPropertySource(properties = {"hawkbit.cache.JpaTenantConfiguration.spec=maximumSize=0"}) // disable cache, its async processed
|
||||
class TenantConfigurationManagementTest extends AbstractJpaIntegrationTest implements EnvironmentAware {
|
||||
|
||||
private Environment env;
|
||||
|
||||
@@ -23,7 +23,13 @@ spring.jpa.properties.eclipselink.logging.parameters=true
|
||||
# Enforce persistence of target polls for test predictability.
|
||||
hawkbit.server.repository.eagerPollPersistence=true
|
||||
|
||||
# disables Rollout and RoulloutGroup status cache for tests
|
||||
# disables tenant config cache for tests
|
||||
hawkbit.cache.JpaTenantConfiguration.spec=maximumSize=0
|
||||
# disables type cache for tests
|
||||
hawkbit.cache.JpaSoftwareModuleType.spec=maximumSize=0
|
||||
hawkbit.cache.JpaDistributionSetType.spec=maximumSize=0
|
||||
hawkbit.cache.JpaTargetType.spec=maximumSize=0
|
||||
# disables Rollout and RolloutGroup status cache for tests
|
||||
hawkbit.cache.RolloutStatus.spec=maximumSize=0
|
||||
hawkbit.cache.RolloutGroupStatus.spec=maximumSize=0
|
||||
|
||||
|
||||
Reference in New Issue
Block a user