From df2ef64e67b080a4bab29cff2c1596638247105d Mon Sep 17 00:00:00 2001 From: YevhenBondarenko Date: Tue, 4 Jul 2023 16:40:42 +0200 Subject: [PATCH] added alarm types cache --- .../server/controller/AlarmController.java | 4 +- .../DefaultAlarmSubscriptionService.java | 6 +-- .../src/main/resources/thingsboard.yml | 3 ++ .../server/dao/alarm/AlarmService.java | 2 +- .../server/common/data/CacheConstants.java | 1 + .../server/dao/alarm/AlarmDao.java | 2 +- .../dao/alarm/AlarmTypesCacheEvictEvent.java | 26 +++++++++++++ .../dao/alarm/AlarmTypesCaffeineCache.java | 36 ++++++++++++++++++ .../dao/alarm/AlarmTypesRedisCache.java | 38 +++++++++++++++++++ .../server/dao/alarm/BaseAlarmService.java | 35 ++++++++++++----- .../server/dao/sql/alarm/AlarmRepository.java | 2 +- .../server/dao/sql/alarm/JpaAlarmDao.java | 4 +- .../resources/application-test.properties | 3 ++ .../engine/api/RuleEngineAlarmService.java | 3 +- 14 files changed, 143 insertions(+), 22 deletions(-) create mode 100644 dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmTypesCacheEvictEvent.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmTypesCaffeineCache.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmTypesRedisCache.java diff --git a/application/src/main/java/org/thingsboard/server/controller/AlarmController.java b/application/src/main/java/org/thingsboard/server/controller/AlarmController.java index 9287ed515e..74b0075a17 100644 --- a/application/src/main/java/org/thingsboard/server/controller/AlarmController.java +++ b/application/src/main/java/org/thingsboard/server/controller/AlarmController.java @@ -15,7 +15,6 @@ */ package org.thingsboard.server.controller; -import com.google.common.util.concurrent.ListenableFuture; import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiParam; import lombok.RequiredArgsConstructor; @@ -506,8 +505,7 @@ public class AlarmController extends BaseController { @RequestMapping(value = "/alarm/types", method = RequestMethod.GET) @ResponseBody public List getAlarmTypes() throws ThingsboardException, ExecutionException, InterruptedException { - ListenableFuture> alarmTypes = alarmService.findAlarmTypesByTenantId(getTenantId()); - return checkNotNull(alarmTypes.get()); + return checkNotNull(alarmService.findAlarmTypesByTenantId(getTenantId())); } } diff --git a/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultAlarmSubscriptionService.java b/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultAlarmSubscriptionService.java index dd52403bcd..394e0598ae 100644 --- a/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultAlarmSubscriptionService.java +++ b/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultAlarmSubscriptionService.java @@ -45,15 +45,15 @@ import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.UserId; +import org.thingsboard.server.common.data.notification.rule.trigger.AlarmTrigger; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.query.AlarmData; import org.thingsboard.server.common.data.query.AlarmDataQuery; -import org.thingsboard.server.common.data.notification.rule.trigger.AlarmTrigger; +import org.thingsboard.server.common.msg.notification.NotificationRuleProcessor; import org.thingsboard.server.common.msg.queue.TbCallback; import org.thingsboard.server.common.stats.TbApiUsageReportClient; import org.thingsboard.server.dao.alarm.AlarmOperationResult; import org.thingsboard.server.dao.alarm.AlarmService; -import org.thingsboard.server.common.msg.notification.NotificationRuleProcessor; import org.thingsboard.server.service.apiusage.TbApiUsageStateService; import org.thingsboard.server.service.entitiy.alarm.TbAlarmCommentService; import org.thingsboard.server.service.subscription.TbSubscriptionUtils; @@ -238,7 +238,7 @@ public class DefaultAlarmSubscriptionService extends AbstractSubscriptionService } @Override - public ListenableFuture> findAlarmTypesByTenantId(TenantId tenantId) { + public List findAlarmTypesByTenantId(TenantId tenantId) { return alarmService.findAlarmTypesByTenantId(tenantId); } diff --git a/application/src/main/resources/thingsboard.yml b/application/src/main/resources/thingsboard.yml index 81654e3033..057013d7f2 100644 --- a/application/src/main/resources/thingsboard.yml +++ b/application/src/main/resources/thingsboard.yml @@ -497,6 +497,9 @@ cache: entityCount: timeToLiveInMinutes: "${CACHE_SPECS_ENTITY_COUNT_TTL:1440}" maxSize: "${CACHE_SPECS_ENTITY_COUNT_MAX_SIZE:100000}" + alarmTypes: + timeToLiveInMinutes: "${CACHE_SPECS_ALARM_TYPES_TTL:60}" + maxSize: "${CACHE_SPECS_ALARM_TYPES_MAX_SIZE:10000}" # deliberately placed outside 'specs' group above notificationRules: diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/alarm/AlarmService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/alarm/AlarmService.java index 89feae53b8..de0e451bcb 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/alarm/AlarmService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/alarm/AlarmService.java @@ -124,5 +124,5 @@ public interface AlarmService extends EntityDaoService { long countAlarmsByQuery(TenantId tenantId, CustomerId customerId, AlarmCountQuery query); - ListenableFuture> findAlarmTypesByTenantId(TenantId tenantId); + List findAlarmTypesByTenantId(TenantId tenantId); } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/CacheConstants.java b/common/data/src/main/java/org/thingsboard/server/common/data/CacheConstants.java index ff0032f435..dc59cef3e5 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/CacheConstants.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/CacheConstants.java @@ -44,4 +44,5 @@ public class CacheConstants { public static final String USER_SETTINGS_CACHE = "userSettings"; public static final String DASHBOARD_TITLES_CACHE = "dashboardTitles"; public static final String ENTITY_COUNT_CACHE = "entityCount"; + public static final String ALARM_TYPES_CACHE = "alarmTypes"; } diff --git a/dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmDao.java b/dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmDao.java index df680bada0..6c6d8cb27a 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmDao.java @@ -98,6 +98,6 @@ public interface AlarmDao extends Dao { long countAlarmsByQuery(TenantId tenantId, CustomerId customerId, AlarmCountQuery query); - ListenableFuture> findTenantAlarmTypesAsync(UUID tenantId); + List findTenantAlarmTypesAsync(UUID tenantId); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmTypesCacheEvictEvent.java b/dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmTypesCacheEvictEvent.java new file mode 100644 index 0000000000..985d0c8ad1 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmTypesCacheEvictEvent.java @@ -0,0 +1,26 @@ +/** + * Copyright © 2016-2023 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.alarm; + +import lombok.Data; +import lombok.RequiredArgsConstructor; +import org.thingsboard.server.common.data.id.TenantId; + +@Data +@RequiredArgsConstructor +class AlarmTypesCacheEvictEvent { + private final TenantId tenantId; +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmTypesCaffeineCache.java b/dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmTypesCaffeineCache.java new file mode 100644 index 0000000000..89b0d12b18 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmTypesCaffeineCache.java @@ -0,0 +1,36 @@ +/** + * Copyright © 2016-2023 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.alarm; + +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.cache.CacheManager; +import org.springframework.stereotype.Service; +import org.thingsboard.server.cache.CaffeineTbTransactionalCache; +import org.thingsboard.server.common.data.CacheConstants; +import org.thingsboard.server.common.data.EntitySubtype; +import org.thingsboard.server.common.data.id.TenantId; + +import java.util.ArrayList; + +@ConditionalOnProperty(prefix = "cache", value = "type", havingValue = "caffeine", matchIfMissing = true) +@Service("AlarmTypesCache") +public class AlarmTypesCaffeineCache extends CaffeineTbTransactionalCache> { + + public AlarmTypesCaffeineCache(CacheManager cacheManager) { + super(cacheManager, CacheConstants.ALARM_TYPES_CACHE); + } + +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmTypesRedisCache.java b/dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmTypesRedisCache.java new file mode 100644 index 0000000000..4b5b0b3d9e --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmTypesRedisCache.java @@ -0,0 +1,38 @@ +/** + * Copyright © 2016-2023 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.alarm; + +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.stereotype.Service; +import org.thingsboard.server.cache.CacheSpecsMap; +import org.thingsboard.server.cache.RedisTbTransactionalCache; +import org.thingsboard.server.cache.TBRedisCacheConfiguration; +import org.thingsboard.server.cache.TbFSTRedisSerializer; +import org.thingsboard.server.common.data.CacheConstants; +import org.thingsboard.server.common.data.EntitySubtype; +import org.thingsboard.server.common.data.id.TenantId; + +import java.util.ArrayList; + +@ConditionalOnProperty(prefix = "cache", value = "type", havingValue = "redis") +@Service("AlarmTypesCache") +public class AlarmTypesRedisCache extends RedisTbTransactionalCache> { + + public AlarmTypesRedisCache(TBRedisCacheConfiguration configuration, CacheSpecsMap cacheSpecsMap, RedisConnectionFactory connectionFactory) { + super(CacheConstants.ALARM_TYPES_CACHE, cacheSpecsMap, connectionFactory, configuration, new TbFSTRedisSerializer<>()); + } +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/alarm/BaseAlarmService.java b/dao/src/main/java/org/thingsboard/server/dao/alarm/BaseAlarmService.java index f40be0378d..eef476ae72 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/alarm/BaseAlarmService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/alarm/BaseAlarmService.java @@ -20,12 +20,14 @@ import com.fasterxml.jackson.databind.JsonNode; import com.google.common.base.Function; 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.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.event.TransactionalEventListener; import org.springframework.util.CollectionUtils; +import org.thingsboard.server.cache.TbTransactionalCache; import org.thingsboard.server.common.data.EntitySubtype; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.alarm.Alarm; @@ -56,7 +58,7 @@ import org.thingsboard.server.common.data.relation.EntityRelation; import org.thingsboard.server.common.data.relation.EntityRelationsQuery; import org.thingsboard.server.common.data.relation.EntitySearchDirection; import org.thingsboard.server.common.data.relation.RelationsSearchParameters; -import org.thingsboard.server.dao.entity.AbstractEntityService; +import org.thingsboard.server.dao.entity.AbstractCachedEntityService; import org.thingsboard.server.dao.entity.EntityService; import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.service.ConstraintValidator; @@ -81,7 +83,7 @@ import static org.thingsboard.server.dao.service.Validator.validateId; @Service("AlarmDaoService") @Slf4j @RequiredArgsConstructor -public class BaseAlarmService extends AbstractEntityService implements AlarmService { +public class BaseAlarmService extends AbstractCachedEntityService, AlarmTypesCacheEvictEvent> implements AlarmService { public static final String INCORRECT_TENANT_ID = "Incorrect tenantId "; @@ -90,6 +92,17 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ private final EntityService entityService; private final DataValidator alarmDataValidator; + @Autowired + protected TbTransactionalCache> alarmTypesCache; + + @TransactionalEventListener(classes = AlarmTypesCacheEvictEvent.class) + @Override + public void handleEvictEvent(AlarmTypesCacheEvictEvent event) { + TenantId tenantId = event.getTenantId(); + cache.evict(tenantId); + alarmTypesCache.evict(tenantId); + } + @Override public AlarmApiCallResult updateAlarm(AlarmUpdateRequest request) { validateAlarmRequest(request); @@ -115,6 +128,7 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ if (!result.isSuccessful() && !alarmCreationEnabled) { throw new ApiUsageLimitsExceededException("Alarms creation is disabled"); } + publishEvictEvent(new AlarmTypesCacheEvictEvent(request.getTenantId())); return withPropagated(result); } @@ -191,6 +205,7 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ } else { deleteEntityRelations(tenantId, alarm.getId()); alarmDao.removeById(tenantId, alarm.getUuidId()); + publishEvictEvent(new AlarmTypesCacheEvictEvent(tenantId)); return AlarmApiCallResult.builder().alarm(alarm).deleted(true).successful(true).build(); } } @@ -206,12 +221,14 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ AlarmOperationResult result = new AlarmOperationResult(alarm, true, new ArrayList<>(getPropagationEntityIds(alarm))); deleteEntityRelations(tenantId, alarm.getId()); alarmDao.removeById(tenantId, alarm.getUuidId()); + publishEvictEvent(new AlarmTypesCacheEvictEvent(tenantId)); return result; } private AlarmOperationResult createAlarm(Alarm alarm) throws InterruptedException, ExecutionException { log.debug("New Alarm : {}", alarm); Alarm saved = alarmDao.save(alarm.getTenantId(), alarm); + publishEvictEvent(new AlarmTypesCacheEvictEvent(alarm.getTenantId())); List propagatedEntitiesList = createEntityAlarmRecords(saved); return new AlarmOperationResult(saved, true, true, propagatedEntitiesList); } @@ -380,15 +397,13 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ } @Override - public ListenableFuture> findAlarmTypesByTenantId(TenantId tenantId) { + public List findAlarmTypesByTenantId(TenantId tenantId) { log.trace("Executing findAlarmTypesByTenantId, tenantId [{}]", tenantId); validateId(tenantId, INCORRECT_TENANT_ID + tenantId); - ListenableFuture> tenantAssetTypes = alarmDao.findTenantAlarmTypesAsync(tenantId.getId()); - return Futures.transform(tenantAssetTypes, - assetTypes -> { - assetTypes.sort(Comparator.comparing(EntitySubtype::getType)); - return assetTypes; - }, MoreExecutors.directExecutor()); + return cache.getAndPutInTransaction(tenantId, () -> + alarmDao.findTenantAlarmTypesAsync(tenantId.getId()).stream() + .sorted(Comparator.comparing(EntitySubtype::getType)) + .collect(Collectors.toCollection(ArrayList::new)), false); } private Alarm merge(Alarm existing, Alarm alarm) { diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/alarm/AlarmRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/alarm/AlarmRepository.java index 81d9068f11..473814a0f5 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/alarm/AlarmRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/alarm/AlarmRepository.java @@ -346,6 +346,6 @@ public interface AlarmRepository extends JpaRepository { @Query(value = "SELECT unassign_alarm(:t_id, :a_id, :a_ts)", nativeQuery = true) String unassignAlarm(@Param("t_id") UUID tenantId, @Param("a_id") UUID alarmId, @Param("a_ts") long unassignTime); - @Query("SELECT DISTINCT a.type FROM AlarmEntity a WHERE a.tenantId = :tenantId") + @Query(value = "SELECT DISTINCT a.type FROM alarm a WHERE a.tenant_id = :tenantId LIMIT 256", nativeQuery = true) List findTenantAlarmTypes(@Param("tenantId") UUID tenantId); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/alarm/JpaAlarmDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/alarm/JpaAlarmDao.java index d721996e0b..ec5734df84 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/alarm/JpaAlarmDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/alarm/JpaAlarmDao.java @@ -370,8 +370,8 @@ public class JpaAlarmDao extends JpaAbstractDao implements A } @Override - public ListenableFuture> findTenantAlarmTypesAsync(UUID tenantId) { - return service.submit(() -> convertTenantEntityTypesToDto(tenantId, EntityType.ALARM, alarmRepository.findTenantAlarmTypes(tenantId))); + public List findTenantAlarmTypesAsync(UUID tenantId) { + return convertTenantEntityTypesToDto(tenantId, EntityType.ALARM, alarmRepository.findTenantAlarmTypes(tenantId)); } private static String getPropagationTypes(AlarmPropagationInfo ap) { diff --git a/dao/src/test/resources/application-test.properties b/dao/src/test/resources/application-test.properties index d89211cb2f..e599cddb37 100644 --- a/dao/src/test/resources/application-test.properties +++ b/dao/src/test/resources/application-test.properties @@ -74,6 +74,9 @@ cache.specs.dashboardTitles.maxSize=10000 cache.specs.entityCount.timeToLiveInMinutes=1440 cache.specs.entityCount.maxSize=10000 +cache.specs.alarmTypes.timeToLiveInMinutes=60 +cache.specs.alarmTypes.maxSize=10000 + redis.connection.host=localhost redis.connection.port=6379 redis.connection.db=0 diff --git a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/RuleEngineAlarmService.java b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/RuleEngineAlarmService.java index ce069a92bb..5aa76726a7 100644 --- a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/RuleEngineAlarmService.java +++ b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/RuleEngineAlarmService.java @@ -56,6 +56,7 @@ public interface RuleEngineAlarmService { * Only one active alarm may exist for the pair {originatorId, alarmType} */ AlarmApiCallResult createAlarm(AlarmCreateOrUpdateActiveRequest request); + /** * Designed to update existing alarm. Accepts only part of the alarm fields. */ @@ -113,5 +114,5 @@ public interface RuleEngineAlarmService { PageData findAlarmDataByQueryForEntities(TenantId tenantId, AlarmDataQuery query, Collection orderedEntityIds); - ListenableFuture> findAlarmTypesByTenantId(TenantId tenantId); + List findAlarmTypesByTenantId(TenantId tenantId); }