From b82bceceb4142e31167293871a304d2f63cbda89 Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Thu, 7 Mar 2024 17:01:33 +0200 Subject: [PATCH] Housekeeper: tests and refactoring --- .../DefaultHousekeeperService.java | 20 +- .../EntitiesDeletionTaskProcessor.java | 7 +- .../EntityAlarmsDeletionTaskProcessor.java | 2 + .../DefaultExportableEntitiesService.java | 3 - .../housekeeper/HousekeeperServiceTest.java | 303 ++++++++++++++++++ .../entity/DefaultEntityServiceRegistry.java | 4 +- .../dao/housekeeper/CleanUpService.java | 22 +- .../dao/housekeeper/HousekeeperService.java | 9 +- .../server/dao/rule/BaseRuleChainService.java | 8 +- 9 files changed, 344 insertions(+), 34 deletions(-) create mode 100644 application/src/test/java/org/thingsboard/server/service/housekeeper/HousekeeperServiceTest.java diff --git a/application/src/main/java/org/thingsboard/server/service/housekeeper/DefaultHousekeeperService.java b/application/src/main/java/org/thingsboard/server/service/housekeeper/DefaultHousekeeperService.java index 56123af64d..63e8bb6a75 100644 --- a/application/src/main/java/org/thingsboard/server/service/housekeeper/DefaultHousekeeperService.java +++ b/application/src/main/java/org/thingsboard/server/service/housekeeper/DefaultHousekeeperService.java @@ -42,7 +42,6 @@ import org.thingsboard.server.service.housekeeper.stats.HousekeeperStatsService; import javax.annotation.PreDestroy; import java.util.List; import java.util.Map; -import java.util.UUID; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @@ -58,11 +57,12 @@ public class DefaultHousekeeperService implements HousekeeperService { private final Map> taskProcessors; - private final TbQueueConsumer> consumer; - private final TbQueueProducer> producer; private final HousekeeperReprocessingService reprocessingService; private final HousekeeperStatsService statsService; private final NotificationRuleProcessor notificationRuleProcessor; + private final TbQueueConsumer> consumer; + private final TbQueueProducer> producer; + private final TopicPartitionInfo submitTpi; private final ExecutorService consumerExecutor = Executors.newSingleThreadExecutor(ThingsBoardThreadFactory.forName("housekeeper-consumer")); private final ExecutorService executor = Executors.newSingleThreadExecutor(ThingsBoardThreadFactory.forName("housekeeper-task-processor")); @@ -80,11 +80,12 @@ public class DefaultHousekeeperService implements HousekeeperService { HousekeeperStatsService statsService, NotificationRuleProcessor notificationRuleProcessor, @Lazy List> taskProcessors) { - this.consumer = queueFactory.createHousekeeperMsgConsumer(); - this.producer = producerProvider.getHousekeeperMsgProducer(); this.reprocessingService = reprocessingService; this.statsService = statsService; this.notificationRuleProcessor = notificationRuleProcessor; + this.consumer = queueFactory.createHousekeeperMsgConsumer(); + this.producer = producerProvider.getHousekeeperMsgProducer(); + this.submitTpi = TopicPartitionInfo.builder().topic(producer.getDefaultTopic()).build(); this.taskProcessors = taskProcessors.stream().collect(Collectors.toMap(HousekeeperTaskProcessor::getTaskType, p -> p)); } @@ -169,10 +170,13 @@ public class DefaultHousekeeperService implements HousekeeperService { } @Override - public void submitTask(UUID key, HousekeeperTask task) { + public void submitTask(HousekeeperTask task) { log.trace("[{}][{}][{}] Submitting task: {}", task.getTenantId(), task.getEntityId().getEntityType(), task.getEntityId(), task.getTaskType()); - TopicPartitionInfo tpi = TopicPartitionInfo.builder().topic(producer.getDefaultTopic()).build(); - producer.send(tpi, new TbProtoQueueMsg<>(key, ToHousekeeperServiceMsg.newBuilder() + /* + * using msg key as entity id so that msgs related to certain entity are pushed to same partition, + * e.g. on tenant deletion (entity id is tenant id), we need to clean up tenant entities in certain order + * */ + producer.send(submitTpi, new TbProtoQueueMsg<>(task.getEntityId().getId(), ToHousekeeperServiceMsg.newBuilder() .setTask(HousekeeperTaskProto.newBuilder() .setValue(JacksonUtil.toString(task)) .setTs(task.getTs()) diff --git a/application/src/main/java/org/thingsboard/server/service/housekeeper/processor/EntitiesDeletionTaskProcessor.java b/application/src/main/java/org/thingsboard/server/service/housekeeper/processor/EntitiesDeletionTaskProcessor.java index 8028a76199..a16a5f09bd 100644 --- a/application/src/main/java/org/thingsboard/server/service/housekeeper/processor/EntitiesDeletionTaskProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/housekeeper/processor/EntitiesDeletionTaskProcessor.java @@ -17,10 +17,10 @@ package org.thingsboard.server.service.housekeeper.processor; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Component; -import org.thingsboard.server.dao.entity.EntityDaoService; -import org.thingsboard.server.dao.entity.EntityServiceRegistry; import org.thingsboard.server.common.data.housekeeper.EntitiesDeletionHousekeeperTask; import org.thingsboard.server.common.data.housekeeper.HousekeeperTaskType; +import org.thingsboard.server.dao.entity.EntityDaoService; +import org.thingsboard.server.dao.entity.EntityServiceRegistry; @Component @RequiredArgsConstructor @@ -31,9 +31,6 @@ public class EntitiesDeletionTaskProcessor implements HousekeeperTaskProcessor void removeById(TenantId tenantId, I id) { EntityType entityType = id.getEntityType(); EntityDaoService entityService = entityServiceRegistry.getServiceByEntityType(entityType); - if (entityService == null) { - throw new IllegalArgumentException("Unsupported entity type " + entityType); - } entityService.deleteEntity(tenantId, id); } diff --git a/application/src/test/java/org/thingsboard/server/service/housekeeper/HousekeeperServiceTest.java b/application/src/test/java/org/thingsboard/server/service/housekeeper/HousekeeperServiceTest.java new file mode 100644 index 0000000000..b132cf34fd --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/service/housekeeper/HousekeeperServiceTest.java @@ -0,0 +1,303 @@ +/** + * Copyright © 2016-2024 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.service.housekeeper; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.TextNode; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.mock.mockito.SpyBean; +import org.springframework.test.context.TestPropertySource; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.rule.engine.metadata.TbGetAttributesNode; +import org.thingsboard.rule.engine.metadata.TbGetAttributesNodeConfiguration; +import org.thingsboard.server.common.data.DataConstants; +import org.thingsboard.server.common.data.Device; +import org.thingsboard.server.common.data.EventInfo; +import org.thingsboard.server.common.data.alarm.Alarm; +import org.thingsboard.server.common.data.alarm.AlarmSeverity; +import org.thingsboard.server.common.data.event.EventType; +import org.thingsboard.server.common.data.event.LifecycleEvent; +import org.thingsboard.server.common.data.housekeeper.HousekeeperTaskType; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.id.RuleChainId; +import org.thingsboard.server.common.data.id.RuleNodeId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.id.UserId; +import org.thingsboard.server.common.data.kv.AttributeKvEntry; +import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry; +import org.thingsboard.server.common.data.kv.BaseReadTsKvQuery; +import org.thingsboard.server.common.data.kv.BasicTsKvEntry; +import org.thingsboard.server.common.data.kv.StringDataEntry; +import org.thingsboard.server.common.data.kv.TsKvEntry; +import org.thingsboard.server.common.data.msg.TbNodeConnectionType; +import org.thingsboard.server.common.data.page.PageLink; +import org.thingsboard.server.common.data.page.TimePageLink; +import org.thingsboard.server.common.data.rule.RuleChain; +import org.thingsboard.server.common.data.rule.RuleChainMetaData; +import org.thingsboard.server.common.data.rule.RuleChainType; +import org.thingsboard.server.common.data.rule.RuleNode; +import org.thingsboard.server.controller.AbstractControllerTest; +import org.thingsboard.server.dao.alarm.AlarmService; +import org.thingsboard.server.dao.attributes.AttributesService; +import org.thingsboard.server.dao.event.EventService; +import org.thingsboard.server.dao.housekeeper.HousekeeperService; +import org.thingsboard.server.dao.rule.RuleChainService; +import org.thingsboard.server.dao.service.DaoSqlTest; +import org.thingsboard.server.dao.timeseries.TimeseriesService; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Optional; +import java.util.concurrent.TimeUnit; +import java.util.function.Function; +import java.util.stream.Collectors; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.awaitility.Awaitility.await; +import static org.mockito.ArgumentMatchers.argThat; +import static org.mockito.Mockito.verify; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@DaoSqlTest +@TestPropertySource(properties = { + "transport.http.enabled=true" +}) +public class HousekeeperServiceTest extends AbstractControllerTest { + + @SpyBean + private HousekeeperService housekeeperService; + @Autowired + private EventService eventService; + @Autowired + private TimeseriesService timeseriesService; + @Autowired + private AttributesService attributesService; + @Autowired + private RuleChainService ruleChainService; + @Autowired + private AlarmService alarmService; + + private TenantId tenantId; + + private static final String TELEMETRY_KEY = "telemetry1"; + private static final String ATTRIBUTE_KEY = "_attribute1"; + private static final String KV_VALUE = "ewfewfwef"; + + @Before + public void setUp() throws Exception { + loginTenantAdmin(); + this.tenantId = super.tenantId; + } + + @After + public void tearDown() throws Exception { + } + + @Test + public void whenDeviceIsDeleted_thenCleanUpRelatedData() throws Exception { + Device device = createDevice("test", "test"); + createRelatedData(device.getId()); + + doDelete("/api/device/" + device.getId()).andExpect(status().isOk()); + + await().atMost(10, TimeUnit.SECONDS).untilAsserted(() -> { + verifyNoRelatedData(device.getId()); + }); + } + + @Test + public void whenRuleChainIsDeleted_thenCleanUpRelatedData() throws Exception { + RuleChainMetaData ruleChainMetaData = createRuleChain(); + RuleChainId ruleChainId = ruleChainMetaData.getRuleChainId(); + RuleNodeId ruleNode1Id = ruleChainMetaData.getNodes().get(0).getId(); + RuleNodeId ruleNode2Id = ruleChainMetaData.getNodes().get(1).getId(); + createRelatedData(ruleChainId); + createRelatedData(ruleNode1Id); + createRelatedData(ruleNode2Id); + + doDelete("/api/ruleChain/" + ruleChainId).andExpect(status().isOk()); + + await().atMost(10, TimeUnit.SECONDS).untilAsserted(() -> { + verifyNoRelatedData(ruleNode1Id); + verifyNoRelatedData(ruleNode2Id); + verifyNoRelatedData(ruleChainId); + }); + } + + @Test + public void whenUserIsDeleted_thenCleanUpRelatedData() throws Exception { + Device device = createDevice("test", "test"); + UserId userId = customerUserId; + createRelatedData(userId); + Alarm alarm = Alarm.builder() + .type("test") + .tenantId(tenantId) + .originator(device.getId()) + .severity(AlarmSeverity.MAJOR) + .build(); + alarm = doPost("/api/alarm", alarm, Alarm.class); + alarm = doPost("/api/alarm/" + alarm.getId() + "/assign/" + userId, "", Alarm.class); + assertThat(alarm.getAssigneeId()).isEqualTo(userId); + assertThat(alarmService.findAlarmIdsByAssigneeId(tenantId, userId, new PageLink(100)).getData()).isNotEmpty(); + + doDelete("/api/user/" + userId).andExpect(status().isOk()); + + await().atMost(10, TimeUnit.SECONDS).untilAsserted(() -> { + verifyNoRelatedData(userId); + }); + assertThat(alarmService.findAlarmById(tenantId, alarm.getId()).getAssigneeId()).isNull(); + } + + @Test + public void whenTenantIsDeleted_thenDeleteAllEntitiesAndCleanUpRelatedData() throws Exception { + loginDifferentTenant(); + tenantId = differentTenantId; + + createRelatedData(tenantId); + + Device device = createDevice("test", "test"); + createRelatedData(device.getId()); + + RuleChainMetaData ruleChainMetaData = createRuleChain(); + RuleChainId ruleChainId = ruleChainMetaData.getRuleChainId(); + RuleNodeId ruleNode1Id = ruleChainMetaData.getNodes().get(0).getId(); + RuleNodeId ruleNode2Id = ruleChainMetaData.getNodes().get(1).getId(); + createRelatedData(ruleChainId); + createRelatedData(ruleNode1Id); + createRelatedData(ruleNode2Id); + + UserId userId = savedDifferentTenantUser.getId(); + createRelatedData(userId); + + loginSysAdmin(); + deleteDifferentTenant(); + + await().atMost(30, TimeUnit.SECONDS).untilAsserted(() -> { + verifyNoRelatedData(device.getId()); + verifyNoRelatedData(ruleNode1Id); + verifyNoRelatedData(ruleNode2Id); + verifyNoRelatedData(ruleChainId); + verifyNoRelatedData(userId); + verifyNoRelatedData(tenantId); + }); + } + + private void createRelatedData(EntityId entityId) throws Exception { + createTelemetry(entityId); + for (String scope : List.of(DataConstants.SERVER_SCOPE, DataConstants.SHARED_SCOPE, DataConstants.CLIENT_SCOPE)) { + createAttribute(entityId, scope, scope + ATTRIBUTE_KEY); + } + createEvent(entityId); + } + + private void verifyNoRelatedData(EntityId entityId) throws Exception { + List expectedTaskTypes = List.of(HousekeeperTaskType.DELETE_TELEMETRY, HousekeeperTaskType.DELETE_ATTRIBUTES, HousekeeperTaskType.DELETE_EVENTS, HousekeeperTaskType.DELETE_ENTITY_ALARMS); + for (HousekeeperTaskType taskType : expectedTaskTypes) { + verify(housekeeperService).submitTask(argThat(task -> task.getTaskType() == taskType && task.getEntityId().equals(entityId))); + } + + assertThat(getLatestTelemetry(entityId)).isNull(); + assertThat(getTimeseriesHistory(entityId)).isEmpty(); + for (String scope : List.of(DataConstants.SERVER_SCOPE, DataConstants.SHARED_SCOPE, DataConstants.CLIENT_SCOPE)) { + assertThat(getAttribute(entityId, scope, scope + ATTRIBUTE_KEY)).isNull(); + } + assertThat(getEvents(entityId)).isEmpty(); + } + + private void createAttribute(EntityId entityId, String scope, String key) throws Exception { + attributesService.save(tenantId, entityId, scope, new BaseAttributeKvEntry(System.currentTimeMillis(), new StringDataEntry(key, KV_VALUE))).get(); + } + + private void createTelemetry(EntityId entityId) throws Exception { + timeseriesService.save(tenantId, entityId, new BasicTsKvEntry(System.currentTimeMillis(), new StringDataEntry(TELEMETRY_KEY, KV_VALUE))).get(); + } + + private void createEvent(EntityId entityId) { + LifecycleEvent event = LifecycleEvent.builder() + .tenantId(tenantId) + .entityId(entityId.getId()) + .serviceId("test") + .lcEventType("test") + .success(true) + .build(); + eventService.saveAsync(event); + await().atMost(10, TimeUnit.SECONDS) + .until(() -> !getEvents(entityId).isEmpty()); + + } + + private TsKvEntry getLatestTelemetry(EntityId entityId) throws Exception { + return timeseriesService.findLatest(tenantId, entityId, HousekeeperServiceTest.TELEMETRY_KEY).get().orElse(null); + } + + private List getTimeseriesHistory(EntityId entityId) throws Exception { + return timeseriesService.findAll(tenantId, entityId, List.of(new BaseReadTsKvQuery(HousekeeperServiceTest.TELEMETRY_KEY, 0, System.currentTimeMillis(), 10, "DESC"))).get(); + } + + private AttributeKvEntry getAttribute(EntityId entityId, String scope, String key) throws Exception { + return attributesService.find(tenantId, entityId, scope, key).get().orElse(null); + } + + private List getEvents(EntityId entityId) { + return eventService.findEvents(tenantId, entityId, EventType.LC_EVENT, new TimePageLink(100)).getData() + .stream().filter(event -> Optional.ofNullable(event.getBody()).map(body -> body.get("event")) + .map(JsonNode::asText).orElse("").equals("test")) + .collect(Collectors.toList()); + } + + private RuleChainMetaData createRuleChain() { + RuleChain ruleChain = new RuleChain(); + ruleChain.setTenantId(tenantId); + ruleChain.setName("Test"); + ruleChain.setType(RuleChainType.CORE); + ruleChain.setDebugMode(true); + ruleChain.setConfiguration(JacksonUtil.newObjectNode().set("a", new TextNode("b"))); + ruleChain = ruleChainService.saveRuleChain(ruleChain); + RuleChainId ruleChainId = ruleChain.getId(); + + RuleChainMetaData metaData = new RuleChainMetaData(); + metaData.setRuleChainId(ruleChainId); + + RuleNode ruleNode1 = new RuleNode(); + ruleNode1.setName("Simple Rule Node 1"); + ruleNode1.setType(org.thingsboard.rule.engine.metadata.TbGetAttributesNode.class.getName()); + ruleNode1.setConfigurationVersion(TbGetAttributesNode.class.getAnnotation(org.thingsboard.rule.engine.api.RuleNode.class).version()); + ruleNode1.setDebugMode(true); + TbGetAttributesNodeConfiguration configuration1 = new TbGetAttributesNodeConfiguration(); + configuration1.setServerAttributeNames(Collections.singletonList("serverAttributeKey1")); + ruleNode1.setConfiguration(JacksonUtil.valueToTree(configuration1)); + + RuleNode ruleNode2 = new RuleNode(); + ruleNode2.setName("Simple Rule Node 2"); + ruleNode2.setType(org.thingsboard.rule.engine.metadata.TbGetAttributesNode.class.getName()); + ruleNode2.setConfigurationVersion(TbGetAttributesNode.class.getAnnotation(org.thingsboard.rule.engine.api.RuleNode.class).version()); + ruleNode2.setDebugMode(true); + TbGetAttributesNodeConfiguration configuration2 = new TbGetAttributesNodeConfiguration(); + configuration2.setServerAttributeNames(Collections.singletonList("serverAttributeKey2")); + ruleNode2.setConfiguration(JacksonUtil.valueToTree(configuration2)); + + metaData.setNodes(Arrays.asList(ruleNode1, ruleNode2)); + metaData.setFirstNodeIndex(0); + metaData.addConnectionInfo(0, 1, TbNodeConnectionType.SUCCESS); + ruleChainService.saveRuleChainMetaData(tenantId, metaData, Function.identity()); + return ruleChainService.loadRuleChainMetaData(tenantId, ruleChainId); + } + +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/entity/DefaultEntityServiceRegistry.java b/dao/src/main/java/org/thingsboard/server/dao/entity/DefaultEntityServiceRegistry.java index 6e6233b1b2..40064fbbde 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/entity/DefaultEntityServiceRegistry.java +++ b/dao/src/main/java/org/thingsboard/server/dao/entity/DefaultEntityServiceRegistry.java @@ -27,6 +27,7 @@ import org.thingsboard.server.common.data.EntityType; import java.util.HashMap; import java.util.Map; +import java.util.Optional; @Service @RequiredArgsConstructor @@ -52,7 +53,8 @@ public class DefaultEntityServiceRegistry implements EntityServiceRegistry { @Override public EntityDaoService getServiceByEntityType(EntityType entityType) { - return entityDaoServicesMap.get(entityType); + return Optional.ofNullable(entityDaoServicesMap.get(entityType)) + .orElseThrow(() -> new IllegalArgumentException("Unsupported entity type " + entityType)); } } diff --git a/dao/src/main/java/org/thingsboard/server/dao/housekeeper/CleanUpService.java b/dao/src/main/java/org/thingsboard/server/dao/housekeeper/CleanUpService.java index b81b6dc5e6..13a1e1cb09 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/housekeeper/CleanUpService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/housekeeper/CleanUpService.java @@ -17,6 +17,8 @@ package org.thingsboard.server.dao.housekeeper; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Component; import org.springframework.transaction.event.TransactionalEventListener; import org.thingsboard.server.common.data.EntityType; @@ -24,11 +26,12 @@ import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.housekeeper.HousekeeperTask; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.dao.entity.EntityDaoService; +import org.thingsboard.server.dao.entity.EntityServiceRegistry; import org.thingsboard.server.dao.eventsourcing.DeleteEntityEvent; import org.thingsboard.server.dao.relation.RelationService; import java.util.Optional; -import java.util.UUID; @Component @RequiredArgsConstructor @@ -37,12 +40,15 @@ public class CleanUpService { private final Optional housekeeperService; private final RelationService relationService; + @Autowired + @Lazy + private EntityServiceRegistry entityServiceRegistry; @TransactionalEventListener(fallbackExecution = true) public void handleEntityDeletionEvent(DeleteEntityEvent event) { TenantId tenantId = event.getTenantId(); EntityId entityId = event.getEntityId(); - log.trace("[{}] Handling entity deletion event: {}", tenantId, event); + log.trace("[{}][{}][{}] Handling entity deletion event", tenantId, entityId.getEntityType(), entityId.getId()); cleanUpRelatedData(tenantId, entityId); if (entityId.getEntityType() == EntityType.USER) { housekeeperService.ifPresent(housekeeperService -> { @@ -52,6 +58,7 @@ public class CleanUpService { } public void cleanUpRelatedData(TenantId tenantId, EntityId entityId) { + log.trace("[{}][{}][{}] Cleaning up related data", tenantId, entityId.getEntityType(), entityId.getId()); // todo: skipped entities list relationService.deleteEntityRelations(tenantId, entityId); housekeeperService.ifPresent(housekeeperService -> { @@ -63,11 +70,14 @@ public class CleanUpService { } public void removeTenantEntities(TenantId tenantId, EntityType... entityTypes) { - UUID tasksKey = UUID.randomUUID(); // so that all tasks are pushed to single partition to be processed synchronously - // todo: just use tenantId as key in the impl - housekeeperService.ifPresent(housekeeperService -> { + housekeeperService.ifPresentOrElse(housekeeperService -> { + for (EntityType entityType : entityTypes) { + housekeeperService.submitTask(HousekeeperTask.deleteEntities(tenantId, entityType)); + } + }, () -> { for (EntityType entityType : entityTypes) { - housekeeperService.submitTask(tasksKey, HousekeeperTask.deleteEntities(tenantId, entityType)); + EntityDaoService entityService = entityServiceRegistry.getServiceByEntityType(entityType); + entityService.deleteByTenantId(tenantId); } }); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/housekeeper/HousekeeperService.java b/dao/src/main/java/org/thingsboard/server/dao/housekeeper/HousekeeperService.java index 1bbbb25df1..e3c0800f69 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/housekeeper/HousekeeperService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/housekeeper/HousekeeperService.java @@ -17,15 +17,8 @@ package org.thingsboard.server.dao.housekeeper; import org.thingsboard.server.common.data.housekeeper.HousekeeperTask; -import java.util.UUID; - public interface HousekeeperService { - default void submitTask(HousekeeperTask task) { - submitTask(task.getEntityId().getId(), task); - } - - // tasks with the same key will be pushed to the same partition and thus processed synchronously - void submitTask(UUID key, HousekeeperTask task); + void submitTask(HousekeeperTask task); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/rule/BaseRuleChainService.java b/dao/src/main/java/org/thingsboard/server/dao/rule/BaseRuleChainService.java index 92d4ea1baf..18f3d58ba7 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/rule/BaseRuleChainService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/rule/BaseRuleChainService.java @@ -438,12 +438,14 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC checkRuleNodesAndDelete(tenantId, ruleChainId); } + @Transactional @Override public void deleteRuleChainsByTenantId(TenantId tenantId) { Validator.validateId(tenantId, "Incorrect tenant id for delete rule chains request."); tenantRuleChainsRemover.removeEntities(tenantId, tenantId); } + @Transactional @Override public void deleteByTenantId(TenantId tenantId) { deleteRuleChainsByTenantId(tenantId); @@ -776,10 +778,10 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC private void deleteRuleNodes(TenantId tenantId, List ruleNodes) { List ruleNodeIds = ruleNodes.stream().map(RuleNode::getId).collect(Collectors.toList()); - for (var node : ruleNodes) { - cleanUpService.cleanUpRelatedData(tenantId, node.getId()); - } ruleNodeDao.deleteByIdIn(ruleNodeIds); + for (var nodeId : ruleNodeIds) { + cleanUpService.cleanUpRelatedData(tenantId, nodeId); + } } @Override