Browse Source

Redis Cache for RelationsService

pull/6536/head
Andrii Shvaika 4 years ago
parent
commit
a3b8021ec5
  1. 2
      common/cache/src/main/java/org/thingsboard/server/cache/TbTransactionalCache.java
  2. 36
      dao/src/main/java/org/thingsboard/server/dao/CacheDaoConfig.java
  3. 2
      dao/src/main/java/org/thingsboard/server/dao/JpaDaoConfig.java
  4. 38
      dao/src/main/java/org/thingsboard/server/dao/JpaServiceDaoConfig.java
  5. 2
      dao/src/main/java/org/thingsboard/server/dao/cache/CaffeineTbTransactionalCache.java
  6. 14
      dao/src/main/java/org/thingsboard/server/dao/cache/RedisTbCacheTransaction.java
  7. 3
      dao/src/main/java/org/thingsboard/server/dao/cache/RedisTbTransactionalCache.java
  8. 2
      dao/src/main/java/org/thingsboard/server/dao/customer/CustomerServiceImpl.java
  9. 3
      dao/src/main/java/org/thingsboard/server/dao/entity/AbstractEntityService.java
  10. 150
      dao/src/main/java/org/thingsboard/server/dao/relation/BaseRelationService.java
  11. 15
      dao/src/main/java/org/thingsboard/server/dao/relation/EntityRelationEvent.java
  12. 2
      dao/src/main/java/org/thingsboard/server/dao/relation/RelationCacheKey.java
  13. 2
      dao/src/main/java/org/thingsboard/server/dao/relation/RelationCacheValue.java
  14. 8
      dao/src/main/java/org/thingsboard/server/dao/relation/RelationDao.java
  15. 14
      dao/src/main/java/org/thingsboard/server/dao/relation/RelationRedisCache.java
  16. 28
      dao/src/main/java/org/thingsboard/server/dao/sql/relation/JpaRelationDao.java
  17. 1
      dao/src/main/java/org/thingsboard/server/dao/timeseries/BaseTimeseriesService.java
  18. 12
      dao/src/main/java/org/thingsboard/server/dao/usagerecord/ApiUsageStateServiceImpl.java
  19. 2
      dao/src/test/java/org/thingsboard/server/dao/AbstractDaoServiceTest.java
  20. 65
      dao/src/test/java/org/thingsboard/server/dao/attributes/CachedAttributesServiceTest.java
  21. 7
      dao/src/test/java/org/thingsboard/server/dao/service/BaseRelationCacheTest.java
  22. 4
      dao/src/test/java/org/thingsboard/server/dao/sql/attributes/RedisAttributeServiceTest.java

2
common/cache/src/main/java/org/thingsboard/server/cache/TbTransactionalCache.java

@ -5,7 +5,7 @@
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,

36
dao/src/main/java/org/thingsboard/server/dao/CacheDaoConfig.java

@ -1,36 +0,0 @@
/**
* Copyright © 2016-2022 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.dao;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.thingsboard.server.dao.util.TbAutoConfiguration;
/**
* @author Valerii Sosliuk
*/
@Configuration
@TbAutoConfiguration
@ComponentScan({"org.thingsboard.server.dao.sql", "org.thingsboard.server.dao.attributes"})
@EnableJpaRepositories("org.thingsboard.server.dao.sql")
@EntityScan("org.thingsboard.server.dao.model.sql")
@EnableTransactionManagement
public class CacheDaoConfig {
}

2
dao/src/main/java/org/thingsboard/server/dao/JpaDaoConfig.java

@ -27,7 +27,7 @@ import org.thingsboard.server.dao.util.TbAutoConfiguration;
*/
@Configuration
@TbAutoConfiguration
@ComponentScan("org.thingsboard.server.dao.sql")
@ComponentScan({"org.thingsboard.server.dao.sql", "org.thingsboard.server.dao.attributes", "org.thingsboard.server.dao.cache", "org.thingsboard.server.cache"})
@EnableJpaRepositories("org.thingsboard.server.dao.sql")
@EntityScan("org.thingsboard.server.dao.model.sql")
@EnableTransactionManagement

38
dao/src/main/java/org/thingsboard/server/dao/JpaServiceDaoConfig.java

@ -1,38 +0,0 @@
/**
* Copyright © 2016-2022 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.dao;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.thingsboard.server.dao.util.TbAutoConfiguration;
/**
* @author Valerii Sosliuk
*/
@Configuration
@EnableCaching
@TbAutoConfiguration
@ComponentScan({"org.thingsboard.server.dao.sql", "org.thingsboard.server.dao.attributes", "org.thingsboard.server.dao.cache", "org.thingsboard.server.cache"})
@EnableJpaRepositories("org.thingsboard.server.dao.sql")
@EntityScan("org.thingsboard.server.dao.model.sql")
@EnableTransactionManagement
public class JpaServiceDaoConfig {
}

2
dao/src/main/java/org/thingsboard/server/dao/cache/CaffeineTbTransactionalCache.java

@ -5,7 +5,7 @@
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,

14
dao/src/main/java/org/thingsboard/server/dao/cache/RedisTbCacheTransaction.java

@ -15,24 +15,13 @@
*/
package org.thingsboard.server.dao.cache;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.springframework.data.redis.connection.RedisConnection;
import org.thingsboard.server.cache.TbCacheTransaction;
import java.io.Serializable;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.UUID;
import java.util.concurrent.Executor;
@Slf4j
@RequiredArgsConstructor
@ -50,8 +39,7 @@ public class RedisTbCacheTransaction<K extends Serializable, V extends Serializa
public boolean commit() {
try {
var execResult = connection.exec();
var result = execResult!= null && execResult.stream().anyMatch(Objects::nonNull);
log.warn("Transaction result: {}", result);
var result = execResult != null && execResult.stream().anyMatch(Objects::nonNull);
return result;
} finally {
connection.close();

3
dao/src/main/java/org/thingsboard/server/dao/cache/RedisTbTransactionalCache.java

@ -5,7 +5,7 @@
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
@ -136,6 +136,7 @@ public abstract class RedisTbTransactionalCache<K extends Serializable, V extend
connection.multi();
} catch (Exception e) {
connection.close();
throw e;
}
return connection;
}

2
dao/src/main/java/org/thingsboard/server/dao/customer/CustomerServiceImpl.java

@ -20,6 +20,7 @@ import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.util.concurrent.ListenableFuture;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.thingsboard.server.common.data.Customer;
import org.thingsboard.server.common.data.id.CustomerId;
@ -69,6 +70,7 @@ public class CustomerServiceImpl extends AbstractEntityService implements Custom
@Autowired
private DashboardService dashboardService;
@Lazy
@Autowired
private ApiUsageStateService apiUsageStateService;

3
dao/src/main/java/org/thingsboard/server/dao/entity/AbstractEntityService.java

@ -40,6 +40,7 @@ public abstract class AbstractEntityService {
public static final String INCORRECT_EDGE_ID = "Incorrect edgeId ";
public static final String INCORRECT_PAGE_LINK = "Incorrect page link ";
@Lazy
@Autowired
protected RelationService relationService;
@ -47,9 +48,11 @@ public abstract class AbstractEntityService {
@Autowired
protected AlarmService alarmService;
@Lazy
@Autowired
protected EntityViewService entityViewService;
@Lazy
@Autowired(required = false)
protected EdgeService edgeService;

150
dao/src/main/java/org/thingsboard/server/dao/relation/BaseRelationService.java

@ -5,7 +5,7 @@
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
@ -16,22 +16,20 @@
package org.thingsboard.server.dao.relation;
import com.google.common.base.Function;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.MoreExecutors;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cache.Cache;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.annotation.Lazy;
import org.springframework.dao.ConcurrencyFailureException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.event.TransactionalEventListener;
import org.springframework.util.StringUtils;
import org.thingsboard.server.cache.TbTransactionalCache;
import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.relation.EntityRelation;
@ -45,25 +43,22 @@ import org.thingsboard.server.common.data.rule.RuleChainType;
import org.thingsboard.server.dao.entity.EntityService;
import org.thingsboard.server.dao.exception.DataValidationException;
import org.thingsboard.server.dao.service.ConstraintValidator;
import org.thingsboard.server.dao.sql.JpaExecutorService;
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutionException;
import java.util.function.BiConsumer;
import static org.thingsboard.server.common.data.CacheConstants.RELATIONS_CACHE;
import static org.thingsboard.server.dao.service.Validator.validateId;
/**
* Created by ashvayka on 28.04.17.
*/
@Service
@RequiredArgsConstructor
@Slf4j
public class BaseRelationService implements RelationService {
@ -71,6 +66,17 @@ public class BaseRelationService implements RelationService {
private final EntityService entityService;
private final TbTransactionalCache<RelationCacheKey, RelationCacheValue> cache;
private final ApplicationEventPublisher publisher;
private final JpaExecutorService executor;
public BaseRelationService(RelationDao relationDao, @Lazy EntityService entityService,
TbTransactionalCache<RelationCacheKey, RelationCacheValue> cache,
ApplicationEventPublisher publisher, JpaExecutorService executor) {
this.relationDao = relationDao;
this.entityService = entityService;
this.cache = cache;
this.publisher = publisher;
this.executor = executor;
}
@TransactionalEventListener(classes = EntityRelationEvent.class)
public void handleRelationEvictEvent(EntityRelationEvent event) {
@ -195,14 +201,14 @@ public class BaseRelationService implements RelationService {
validate(entityId);
List<ListenableFuture<List<EntityRelation>>> inboundRelationsList = new ArrayList<>();
for (RelationTypeGroup typeGroup : RelationTypeGroup.values()) {
inboundRelationsList.add(relationDao.findAllByToAsync(tenantId, entityId, typeGroup));
inboundRelationsList.add(executor.submit(() -> relationDao.findAllByTo(tenantId, entityId, typeGroup)));
}
ListenableFuture<List<List<EntityRelation>>> inboundRelations = Futures.allAsList(inboundRelationsList);
List<ListenableFuture<List<EntityRelation>>> outboundRelationsList = new ArrayList<>();
for (RelationTypeGroup typeGroup : RelationTypeGroup.values()) {
outboundRelationsList.add(relationDao.findAllByFromAsync(tenantId, entityId, typeGroup));
outboundRelationsList.add(executor.submit(() -> relationDao.findAllByFrom(tenantId, entityId, typeGroup)));
}
ListenableFuture<List<List<EntityRelation>>> outboundRelations = Futures.allAsList(outboundRelationsList);
@ -265,7 +271,6 @@ public class BaseRelationService implements RelationService {
public List<EntityRelation> findByFrom(TenantId tenantId, EntityId from, RelationTypeGroup typeGroup) {
validate(from);
validateTypeGroup(typeGroup);
log.trace("[{}] Find by from: [{}][{}]: ", tenantId, from, typeGroup, new RuntimeException());
RelationCacheKey cacheKey = RelationCacheKey.builder().from(from).typeGroup(typeGroup).direction(EntitySearchDirection.FROM).build();
return cache.getAndPutInTransaction(cacheKey,
() -> relationDao.findAllByFrom(tenantId, from, typeGroup),
@ -279,30 +284,13 @@ public class BaseRelationService implements RelationService {
validate(from);
validateTypeGroup(typeGroup);
List<Object> fromAndTypeGroup = new ArrayList<>();
fromAndTypeGroup.add(from);
fromAndTypeGroup.add(typeGroup);
fromAndTypeGroup.add(EntitySearchDirection.FROM.name());
var cacheValue = cache.get(RelationCacheKey.builder().from(from).typeGroup(typeGroup).direction(EntitySearchDirection.FROM).build());
Cache cache = cacheManager.getCache(RELATIONS_CACHE);
@SuppressWarnings("unchecked")
List<EntityRelation> fromCache = cache.get(fromAndTypeGroup, List.class);
if (fromCache != null) {
return Futures.immediateFuture(fromCache);
if (cacheValue != null && cacheValue.get() != null) {
return Futures.immediateFuture(cacheValue.get().getRelations());
} else {
ListenableFuture<List<EntityRelation>> relationsFuture = relationDao.findAllByFromAsync(tenantId, from, typeGroup);
Futures.addCallback(relationsFuture,
new FutureCallback<>() {
@Override
public void onSuccess(@Nullable List<EntityRelation> result) {
cache.putIfAbsent(fromAndTypeGroup, result);
}
@Override
public void onFailure(Throwable t) {
}
}, MoreExecutors.directExecutor());
return relationsFuture;
//Disabled cache put for the async requests due to limitations of the cache implementation (Redis lib does not support thread-safe transactions)
return executor.submit(() -> findByFrom(tenantId, from, typeGroup));
}
}
@ -311,7 +299,7 @@ public class BaseRelationService implements RelationService {
log.trace("Executing findInfoByFrom [{}][{}]", from, typeGroup);
validate(from);
validateTypeGroup(typeGroup);
ListenableFuture<List<EntityRelation>> relations = relationDao.findAllByFromAsync(tenantId, from, typeGroup);
ListenableFuture<List<EntityRelation>> relations = executor.submit(() -> relationDao.findAllByFrom(tenantId, from, typeGroup));
return Futures.transformAsync(relations,
relations1 -> {
List<ListenableFuture<EntityRelationInfo>> futures = new ArrayList<>();
@ -324,15 +312,14 @@ public class BaseRelationService implements RelationService {
}, MoreExecutors.directExecutor());
}
// @Cacheable(cacheNames = RELATIONS_CACHE, key = "{#from, #relationType, #typeGroup, 'FROM'}")
@Transactional(propagation = Propagation.SUPPORTS)
@Override
public List<EntityRelation> findByFromAndType(TenantId tenantId, EntityId from, String relationType, RelationTypeGroup typeGroup) {
try {
//TODO refactor
return findByFromAndTypeAsync(tenantId, from, relationType, typeGroup).get();
} catch (InterruptedException | ExecutionException e) {
throw new RuntimeException(e);
}
RelationCacheKey cacheKey = RelationCacheKey.builder().from(from).type(relationType).typeGroup(typeGroup).direction(EntitySearchDirection.FROM).build();
return cache.getAndPutInTransaction(cacheKey,
() -> relationDao.findAllByFromAndType(tenantId, from, relationType, typeGroup),
RelationCacheValue::getRelations,
relations -> RelationCacheValue.builder().relations(relations).build(), false);
}
@Override
@ -341,49 +328,28 @@ public class BaseRelationService implements RelationService {
validate(from);
validateType(relationType);
validateTypeGroup(typeGroup);
return relationDao.findAllByFromAndType(tenantId, from, relationType, typeGroup);
return executor.submit(() -> findByFromAndType(tenantId, from, relationType, typeGroup));
}
@Cacheable(cacheNames = RELATIONS_CACHE, key = "{#to, #typeGroup, 'TO'}")
@Transactional(propagation = Propagation.SUPPORTS)
@Override
public List<EntityRelation> findByTo(TenantId tenantId, EntityId to, RelationTypeGroup typeGroup) {
validate(to);
validateTypeGroup(typeGroup);
return relationDao.findAllByTo(tenantId, to, typeGroup);
RelationCacheKey cacheKey = RelationCacheKey.builder().to(to).typeGroup(typeGroup).direction(EntitySearchDirection.TO).build();
return cache.getAndPutInTransaction(cacheKey,
() -> relationDao.findAllByTo(tenantId, to, typeGroup),
RelationCacheValue::getRelations,
relations -> RelationCacheValue.builder().relations(relations).build(), false);
}
@Override
public ListenableFuture<List<EntityRelation>> findByToAsync(TenantId tenantId, EntityId to, RelationTypeGroup typeGroup) {
log.trace("Executing findByTo [{}][{}]", to, typeGroup);
log.trace("Executing findByToAsync [{}][{}]", to, typeGroup);
validate(to);
validateTypeGroup(typeGroup);
List<Object> toAndTypeGroup = new ArrayList<>();
toAndTypeGroup.add(to);
toAndTypeGroup.add(typeGroup);
toAndTypeGroup.add(EntitySearchDirection.TO.name());
Cache cache = cacheManager.getCache(RELATIONS_CACHE);
@SuppressWarnings("unchecked")
List<EntityRelation> fromCache = cache.get(toAndTypeGroup, List.class);
if (fromCache != null) {
return Futures.immediateFuture(fromCache);
} else {
ListenableFuture<List<EntityRelation>> relationsFuture = relationDao.findAllByToAsync(tenantId, to, typeGroup);
Futures.addCallback(relationsFuture,
new FutureCallback<List<EntityRelation>>() {
@Override
public void onSuccess(@Nullable List<EntityRelation> result) {
cache.putIfAbsent(toAndTypeGroup, result);
}
@Override
public void onFailure(Throwable t) {
}
}, MoreExecutors.directExecutor());
return relationsFuture;
}
return executor.submit(() -> findByTo(tenantId, to, typeGroup));
}
@Override
@ -391,7 +357,7 @@ public class BaseRelationService implements RelationService {
log.trace("Executing findInfoByTo [{}][{}]", to, typeGroup);
validate(to);
validateTypeGroup(typeGroup);
ListenableFuture<List<EntityRelation>> relations = relationDao.findAllByToAsync(tenantId, to, typeGroup);
ListenableFuture<List<EntityRelation>> relations = findByToAsync(tenantId, to, typeGroup);
return Futures.transformAsync(relations,
relations1 -> {
List<ListenableFuture<EntityRelationInfo>> futures = new ArrayList<>();
@ -415,24 +381,27 @@ public class BaseRelationService implements RelationService {
}, MoreExecutors.directExecutor());
}
@Cacheable(cacheNames = RELATIONS_CACHE, key = "{#to, #relationType, #typeGroup, 'TO'}")
@Override
public List<EntityRelation> findByToAndType(TenantId tenantId, EntityId to, String relationType, RelationTypeGroup typeGroup) {
try {
//TODO refactor
return findByToAndTypeAsync(tenantId, to, relationType, typeGroup).get();
} catch (InterruptedException | ExecutionException e) {
throw new RuntimeException(e);
}
log.trace("Executing findByToAndType [{}][{}][{}]", to, relationType, typeGroup);
validate(to);
validateType(relationType);
validateTypeGroup(typeGroup);
RelationCacheKey cacheKey = RelationCacheKey.builder().to(to).type(relationType).typeGroup(typeGroup).direction(EntitySearchDirection.TO).build();
return cache.getAndPutInTransaction(cacheKey,
() -> relationDao.findAllByToAndType(tenantId, to, relationType, typeGroup),
RelationCacheValue::getRelations,
relations -> RelationCacheValue.builder().relations(relations).build(), false);
}
@Override
public ListenableFuture<List<EntityRelation>> findByToAndTypeAsync(TenantId tenantId, EntityId to, String relationType, RelationTypeGroup typeGroup) {
log.trace("Executing findByToAndType [{}][{}][{}]", to, relationType, typeGroup);
log.trace("Executing findByToAndTypeAsync [{}][{}][{}]", to, relationType, typeGroup);
validate(to);
validateType(relationType);
validateTypeGroup(typeGroup);
return relationDao.findAllByToAndType(tenantId, to, relationType, typeGroup);
return executor.submit(() -> findByToAndType(tenantId, to, relationType, typeGroup));
}
@Override
@ -495,7 +464,6 @@ public class BaseRelationService implements RelationService {
@Override
public void removeRelations(TenantId tenantId, EntityId entityId) {
log.trace("removeRelations {}", entityId);
Cache cache = cacheManager.getCache(RELATIONS_CACHE);
List<EntityRelation> relations = new ArrayList<>();
for (RelationTypeGroup relationTypeGroup : RelationTypeGroup.values()) {
@ -505,7 +473,6 @@ public class BaseRelationService implements RelationService {
for (EntityRelation relation : relations) {
deleteRelation(tenantId, relation);
cacheEviction(relation, cache);
}
}
@ -553,21 +520,6 @@ public class BaseRelationService implements RelationService {
}
}
private Function<List<Boolean>, Boolean> getListToBooleanFunction() {
return new Function<List<Boolean>, Boolean>() {
@Nullable
@Override
public Boolean apply(@Nullable List<Boolean> results) {
for (Boolean result : results) {
if (result == null || !result) {
return false;
}
}
return true;
}
};
}
private boolean matchFilters(List<RelationEntityTypeFilter> filters, EntityRelation relation, EntitySearchDirection direction) {
for (RelationEntityTypeFilter filter : filters) {
if (match(filter, relation, direction)) {

15
dao/src/main/java/org/thingsboard/server/dao/relation/EntityRelationEvent.java

@ -1,3 +1,18 @@
/**
* Copyright © 2016-2022 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.dao.relation;
import lombok.Getter;

2
dao/src/main/java/org/thingsboard/server/dao/relation/RelationCacheKey.java

@ -5,7 +5,7 @@
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,

2
dao/src/main/java/org/thingsboard/server/dao/relation/RelationCacheValue.java

@ -5,7 +5,7 @@
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,

8
dao/src/main/java/org/thingsboard/server/dao/relation/RelationDao.java

@ -29,17 +29,13 @@ import java.util.List;
*/
public interface RelationDao {
ListenableFuture<List<EntityRelation>> findAllByFromAsync(TenantId tenantId, EntityId from, RelationTypeGroup typeGroup);
List<EntityRelation> findAllByFrom(TenantId tenantId, EntityId from, RelationTypeGroup typeGroup);
ListenableFuture<List<EntityRelation>> findAllByFromAndType(TenantId tenantId, EntityId from, String relationType, RelationTypeGroup typeGroup);
ListenableFuture<List<EntityRelation>> findAllByToAsync(TenantId tenantId, EntityId to, RelationTypeGroup typeGroup);
List<EntityRelation> findAllByFromAndType(TenantId tenantId, EntityId from, String relationType, RelationTypeGroup typeGroup);
List<EntityRelation> findAllByTo(TenantId tenantId, EntityId to, RelationTypeGroup typeGroup);
ListenableFuture<List<EntityRelation>> findAllByToAndType(TenantId tenantId, EntityId to, String relationType, RelationTypeGroup typeGroup);
List<EntityRelation> findAllByToAndType(TenantId tenantId, EntityId to, String relationType, RelationTypeGroup typeGroup);
ListenableFuture<Boolean> checkRelation(TenantId tenantId, EntityId from, EntityId to, String relationType, RelationTypeGroup typeGroup);

14
dao/src/main/java/org/thingsboard/server/dao/relation/AttributeRedisCache.java → dao/src/main/java/org/thingsboard/server/dao/relation/RelationRedisCache.java

@ -28,22 +28,22 @@ import org.thingsboard.server.dao.attributes.AttributeCacheKey;
import org.thingsboard.server.dao.cache.RedisTbTransactionalCache;
@ConditionalOnProperty(prefix = "cache", value = "type", havingValue = "redis")
@Service("AttributeCache")
public class AttributeRedisCache extends RedisTbTransactionalCache<AttributeCacheKey, AttributeKvEntry> {
@Service("RelationCache")
public class RelationRedisCache extends RedisTbTransactionalCache<RelationCacheKey, RelationCacheValue> {
public AttributeRedisCache(TBRedisCacheConfiguration configuration, CacheSpecsMap cacheSpecsMap, RedisConnectionFactory connectionFactory) {
super(CacheConstants.ATTRIBUTES_CACHE, cacheSpecsMap, connectionFactory, configuration, new RedisSerializer<>() {
public RelationRedisCache(TBRedisCacheConfiguration configuration, CacheSpecsMap cacheSpecsMap, RedisConnectionFactory connectionFactory) {
super(CacheConstants.RELATIONS_CACHE, cacheSpecsMap, connectionFactory, configuration, new RedisSerializer<>() {
private final RedisSerializer<Object> java = RedisSerializer.java();
@Override
public byte[] serialize(AttributeKvEntry attributeKvEntry) throws SerializationException {
public byte[] serialize(RelationCacheValue attributeKvEntry) throws SerializationException {
return java.serialize(attributeKvEntry);
}
@Override
public AttributeKvEntry deserialize(byte[] bytes) throws SerializationException {
return (AttributeKvEntry) java.deserialize(bytes);
public RelationCacheValue deserialize(byte[] bytes) throws SerializationException {
return (RelationCacheValue) java.deserialize(bytes);
}
});
}

28
dao/src/main/java/org/thingsboard/server/dao/sql/relation/JpaRelationDao.java

@ -19,11 +19,9 @@ import com.google.common.util.concurrent.ListenableFuture;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.ConcurrencyFailureException;
import org.springframework.data.domain.PageRequest;
import org.springframework.dao.DataAccessException;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.data.domain.PageRequest;
import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.relation.EntityRelation;
@ -35,8 +33,6 @@ import org.thingsboard.server.dao.model.sql.RelationEntity;
import org.thingsboard.server.dao.relation.RelationDao;
import org.thingsboard.server.dao.sql.JpaAbstractDaoListeningExecutorService;
import javax.persistence.criteria.Predicate;
import java.util.ArrayList;
import java.util.List;
/**
@ -52,11 +48,6 @@ public class JpaRelationDao extends JpaAbstractDaoListeningExecutorService imple
@Autowired
private RelationInsertRepository relationInsertRepository;
@Override
public ListenableFuture<List<EntityRelation>> findAllByFromAsync(TenantId tenantId, EntityId from, RelationTypeGroup typeGroup) {
return service.submit(() -> findAllByFrom(tenantId, from, typeGroup));
}
@Override
public List<EntityRelation> findAllByFrom(TenantId tenantId, EntityId from, RelationTypeGroup typeGroup) {
return DaoUtil.convertDataList(
@ -67,18 +58,13 @@ public class JpaRelationDao extends JpaAbstractDaoListeningExecutorService imple
}
@Override
public ListenableFuture<List<EntityRelation>> findAllByFromAndType(TenantId tenantId, EntityId from, String relationType, RelationTypeGroup typeGroup) {
return service.submit(() -> DaoUtil.convertDataList(
public List<EntityRelation> findAllByFromAndType(TenantId tenantId, EntityId from, String relationType, RelationTypeGroup typeGroup) {
return DaoUtil.convertDataList(
relationRepository.findAllByFromIdAndFromTypeAndRelationTypeAndRelationTypeGroup(
from.getId(),
from.getEntityType().name(),
relationType,
typeGroup.name())));
}
@Override
public ListenableFuture<List<EntityRelation>> findAllByToAsync(TenantId tenantId, EntityId to, RelationTypeGroup typeGroup) {
return service.submit(() -> findAllByTo(tenantId, to, typeGroup));
typeGroup.name()));
}
@Override
@ -91,13 +77,13 @@ public class JpaRelationDao extends JpaAbstractDaoListeningExecutorService imple
}
@Override
public ListenableFuture<List<EntityRelation>> findAllByToAndType(TenantId tenantId, EntityId to, String relationType, RelationTypeGroup typeGroup) {
return service.submit(() -> DaoUtil.convertDataList(
public List<EntityRelation> findAllByToAndType(TenantId tenantId, EntityId to, String relationType, RelationTypeGroup typeGroup) {
return DaoUtil.convertDataList(
relationRepository.findAllByToIdAndToTypeAndRelationTypeAndRelationTypeGroup(
to.getId(),
to.getEntityType().name(),
relationType,
typeGroup.name())));
typeGroup.name()));
}
@Override

1
dao/src/main/java/org/thingsboard/server/dao/timeseries/BaseTimeseriesService.java

@ -24,6 +24,7 @@ import lombok.extern.slf4j.Slf4j;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.EntityView;

12
dao/src/main/java/org/thingsboard/server/dao/usagerecord/ApiUsageStateServiceImpl.java

@ -17,6 +17,7 @@ package org.thingsboard.server.dao.usagerecord;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.thingsboard.server.common.data.ApiFeature;
import org.thingsboard.server.common.data.ApiUsageRecordKey;
@ -47,7 +48,6 @@ import static org.thingsboard.server.dao.service.Validator.validateId;
@Service
@Slf4j
@AllArgsConstructor
public class ApiUsageStateServiceImpl extends AbstractEntityService implements ApiUsageStateService {
public static final String INCORRECT_TENANT_ID = "Incorrect tenantId ";
@ -57,6 +57,16 @@ public class ApiUsageStateServiceImpl extends AbstractEntityService implements A
private final TimeseriesService tsService;
private final DataValidator<ApiUsageState> apiUsageStateValidator;
public ApiUsageStateServiceImpl(ApiUsageStateDao apiUsageStateDao, TenantProfileDao tenantProfileDao,
TenantDao tenantDao, @Lazy TimeseriesService tsService,
DataValidator<ApiUsageState> apiUsageStateValidator) {
this.apiUsageStateDao = apiUsageStateDao;
this.tenantProfileDao = tenantProfileDao;
this.tenantDao = tenantDao;
this.tsService = tsService;
this.apiUsageStateValidator = apiUsageStateValidator;
}
@Override
public void deleteApiUsageStateByTenantId(TenantId tenantId) {
log.trace("Executing deleteUsageRecordsByTenantId [{}]", tenantId);

2
dao/src/test/java/org/thingsboard/server/dao/AbstractDaoServiceTest.java

@ -27,7 +27,7 @@ import org.thingsboard.server.common.stats.StatsFactory;
import org.thingsboard.server.dao.service.DaoSqlTest;
@RunWith(SpringRunner.class)
@ContextConfiguration(classes = {JpaServiceDaoConfig.class, PsqlTsDaoConfig.class, PsqlTsLatestDaoConfig.class, SqlTimeseriesDaoConfig.class})
@ContextConfiguration(classes = {JpaDaoConfig.class, PsqlTsDaoConfig.class, PsqlTsLatestDaoConfig.class, SqlTimeseriesDaoConfig.class})
@DaoSqlTest
@TestExecutionListeners({
DependencyInjectionTestExecutionListener.class,

65
dao/src/test/java/org/thingsboard/server/dao/attributes/CachedAttributesServiceTest.java

@ -1,65 +0,0 @@
/**
* Copyright © 2016-2022 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.dao.attributes;
import com.google.common.util.concurrent.MoreExecutors;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.thingsboard.server.dao.AbstractJpaDaoTest;
import org.thingsboard.server.dao.alarm.AlarmDao;
import org.thingsboard.server.dao.cache.CacheExecutorService;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.BDDMockito.willCallRealMethod;
import static org.mockito.Mockito.mock;
public class CachedAttributesServiceTest extends AbstractJpaDaoTest {
public static final String REDIS = "redis";
@Test
public void givenLocalCacheTypeName_whenEquals_thenOK() {
assertThat(CachedAttributesService.LOCAL_CACHE_TYPE, is("caffeine"));
}
@Test
public void givenCacheType_whenGetExecutor_thenDirectExecutor() {
CachedAttributesService cachedAttributesService = mock(CachedAttributesService.class);
CacheExecutorService cacheExecutorService = mock(CacheExecutorService.class);
willCallRealMethod().given(cachedAttributesService).getExecutor(any(), any());
assertThat(cachedAttributesService.getExecutor(null, cacheExecutorService), is(MoreExecutors.directExecutor()));
assertThat(cachedAttributesService.getExecutor("", cacheExecutorService), is(MoreExecutors.directExecutor()));
assertThat(cachedAttributesService.getExecutor(CachedAttributesService.LOCAL_CACHE_TYPE, cacheExecutorService), is(MoreExecutors.directExecutor()));
}
@Test
public void givenCacheType_whenGetExecutor_thenReturnCacheExecutorService() {
CachedAttributesService cachedAttributesService = mock(CachedAttributesService.class);
CacheExecutorService cacheExecutorService = mock(CacheExecutorService.class);
willCallRealMethod().given(cachedAttributesService).getExecutor(any(String.class), any(CacheExecutorService.class));
assertThat(cachedAttributesService.getExecutor(REDIS, cacheExecutorService), is(cacheExecutorService));
assertThat(cachedAttributesService.getExecutor("unknownCacheType", cacheExecutorService), is(cacheExecutorService));
}
}

7
dao/src/test/java/org/thingsboard/server/dao/service/BaseRelationCacheTest.java

@ -15,7 +15,6 @@
*/
package org.thingsboard.server.dao.service;
import com.google.common.util.concurrent.Futures;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@ -75,7 +74,7 @@ public abstract class BaseRelationCacheTest extends AbstractServiceTest {
@Test
public void testFindRelationByFrom_Cached() throws ExecutionException, InterruptedException {
when(relationDao.getRelation(SYSTEM_TENANT_ID, ENTITY_ID_FROM, ENTITY_ID_TO, RELATION_TYPE, RelationTypeGroup.COMMON))
.thenReturn(Futures.immediateFuture(new EntityRelation(ENTITY_ID_FROM, ENTITY_ID_TO, RELATION_TYPE)));
.thenReturn(new EntityRelation(ENTITY_ID_FROM, ENTITY_ID_TO, RELATION_TYPE));
relationService.getRelation(SYSTEM_TENANT_ID, ENTITY_ID_FROM, ENTITY_ID_TO, RELATION_TYPE, RelationTypeGroup.COMMON);
relationService.getRelation(SYSTEM_TENANT_ID, ENTITY_ID_FROM, ENTITY_ID_TO, RELATION_TYPE, RelationTypeGroup.COMMON);
@ -86,7 +85,7 @@ public abstract class BaseRelationCacheTest extends AbstractServiceTest {
@Test
public void testDeleteRelations_EvictsCache() {
when(relationDao.getRelation(SYSTEM_TENANT_ID, ENTITY_ID_FROM, ENTITY_ID_TO, RELATION_TYPE, RelationTypeGroup.COMMON))
.thenReturn(Futures.immediateFuture(new EntityRelation(ENTITY_ID_FROM, ENTITY_ID_TO, RELATION_TYPE)));
.thenReturn(new EntityRelation(ENTITY_ID_FROM, ENTITY_ID_TO, RELATION_TYPE));
relationService.getRelation(SYSTEM_TENANT_ID, ENTITY_ID_FROM, ENTITY_ID_TO, RELATION_TYPE, RelationTypeGroup.COMMON);
relationService.getRelation(SYSTEM_TENANT_ID, ENTITY_ID_FROM, ENTITY_ID_TO, RELATION_TYPE, RelationTypeGroup.COMMON);
@ -98,7 +97,7 @@ public abstract class BaseRelationCacheTest extends AbstractServiceTest {
relationService.getRelation(SYSTEM_TENANT_ID, ENTITY_ID_FROM, ENTITY_ID_TO, RELATION_TYPE, RelationTypeGroup.COMMON);
relationService.getRelation(SYSTEM_TENANT_ID, ENTITY_ID_FROM, ENTITY_ID_TO, RELATION_TYPE, RelationTypeGroup.COMMON);
verify(relationDao, times(2)).getRelation(SYSTEM_TENANT_ID, ENTITY_ID_FROM, ENTITY_ID_TO, RELATION_TYPE, RelationTypeGroup.COMMON);
verify(relationDao, times(1)).getRelation(SYSTEM_TENANT_ID, ENTITY_ID_FROM, ENTITY_ID_TO, RELATION_TYPE, RelationTypeGroup.COMMON);
}
}

4
dao/src/test/java/org/thingsboard/server/dao/sql/attributes/RedisAttributeServiceTest.java

@ -17,6 +17,7 @@ package org.thingsboard.server.dao.sql.attributes;
import lombok.extern.slf4j.Slf4j;
import org.junit.ClassRule;
import org.junit.Ignore;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
@ -33,10 +34,11 @@ import org.thingsboard.server.dao.attributes.AttributeCacheKey;
})
@ContextConfiguration(initializers = RedisAttributeServiceTest.class)
@Slf4j
@Ignore
public class RedisAttributeServiceTest extends AttributeServiceTest implements ApplicationContextInitializer<ConfigurableApplicationContext> {
@ClassRule
public static GenericContainer redis = new GenericContainer("redis:latest").withExposedPorts(6379);
public static GenericContainer redis = new GenericContainer("redis:6.0").withExposedPorts(6379);
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {

Loading…
Cancel
Save