Fix rule node query performance

This commit is contained in:
Andrii Shvaika 2023-09-12 12:17:54 +03:00
parent 3f10ba464e
commit e369218a1f
4 changed files with 169 additions and 8 deletions

View File

@ -30,7 +30,7 @@ import java.util.List;
*/
public interface RuleNodeDao extends Dao<RuleNode> {
List<RuleNode> findRuleNodesByTenantIdAndType(TenantId tenantId, String type, String search);
List<RuleNode> findRuleNodesByTenantIdAndType(TenantId tenantId, String type, String configurationSearch);
PageData<RuleNode> findAllRuleNodesByType(String type, PageLink pageLink);

View File

@ -56,8 +56,8 @@ public class JpaRuleNodeDao extends JpaAbstractDao<RuleNodeEntity, RuleNode> imp
}
@Override
public List<RuleNode> findRuleNodesByTenantIdAndType(TenantId tenantId, String type, String search) {
return DaoUtil.convertDataList(ruleNodeRepository.findRuleNodesByTenantIdAndType(tenantId.getId(), type, search));
public List<RuleNode> findRuleNodesByTenantIdAndType(TenantId tenantId, String type, String configurationSearch) {
return DaoUtil.convertDataList(ruleNodeRepository.findRuleNodesByTenantIdAndType(tenantId.getId(), type, configurationSearch));
}
@Override

View File

@ -29,19 +29,22 @@ import java.util.UUID;
public interface RuleNodeRepository extends JpaRepository<RuleNodeEntity, UUID> {
@Query("SELECT r FROM RuleNodeEntity r WHERE r.ruleChainId in " +
"(select id from RuleChainEntity rc WHERE rc.tenantId = :tenantId) " +
"AND r.type = :ruleType AND LOWER(r.configuration) LIKE LOWER(CONCAT('%', :searchText, '%')) ")
@Query(nativeQuery = true, value = "SELECT * FROM rule_node r WHERE r.rule_chain_id in " +
"(select id from rule_chain rc WHERE rc.tenant_id = :tenantId) AND r.type = :ruleType " +
" AND (:searchText IS NULL OR r.configuration ILIKE CONCAT('%', :searchText, '%'))")
List<RuleNodeEntity> findRuleNodesByTenantIdAndType(@Param("tenantId") UUID tenantId,
@Param("ruleType") String ruleType,
@Param("searchText") String searchText);
@Query("SELECT r FROM RuleNodeEntity r WHERE r.type = :ruleType AND LOWER(r.configuration) LIKE LOWER(CONCAT('%', :searchText, '%')) ")
@Query(nativeQuery = true, value = "SELECT * FROM rule_node r WHERE r.type = :ruleType " +
" AND (:searchText IS NULL OR r.configuration ILIKE CONCAT('%', :searchText, '%'))")
Page<RuleNodeEntity> findAllRuleNodesByType(@Param("ruleType") String ruleType,
@Param("searchText") String searchText,
Pageable pageable);
@Query("SELECT r FROM RuleNodeEntity r WHERE r.type = :ruleType AND r.configurationVersion < :version AND LOWER(r.configuration) LIKE LOWER(CONCAT('%', :searchText, '%')) ")
@Query(nativeQuery = true, value = "SELECT * FROM rule_node r WHERE r.type = :ruleType " +
" AND configuration_version < :version " +
" AND (:searchText IS NULL OR r.configuration ILIKE CONCAT('%', :searchText, '%'))")
Page<RuleNodeEntity> findAllRuleNodesByTypeAndVersionLessThan(@Param("ruleType") String ruleType,
@Param("version") int version,
@Param("searchText") String searchText,

View File

@ -0,0 +1,158 @@
/**
* 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.sql.rule;
import com.datastax.oss.driver.api.core.uuid.Uuids;
import com.google.common.util.concurrent.ListeningExecutorService;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.thingsboard.common.util.JacksonUtil;
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.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.rule.RuleChain;
import org.thingsboard.server.common.data.rule.RuleNode;
import org.thingsboard.server.dao.AbstractJpaDaoTest;
import org.thingsboard.server.dao.rule.RuleChainDao;
import org.thingsboard.server.dao.rule.RuleNodeDao;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
public class JpaRuleNodeDaoTest extends AbstractJpaDaoTest {
public static final int COUNT = 40;
public static final String PREFIX_FOR_RULE_NODE_NAME = "SEARCH_TEXT_";
List<UUID> ruleNodeIds;
TenantId tenantId1;
TenantId tenantId2;
RuleChainId ruleChainId1;
RuleChainId ruleChainId2;
@Autowired
private RuleChainDao ruleChainDao;
@Autowired
private RuleNodeDao ruleNodeDao;
ListeningExecutorService executor;
@Before
public void setUp() {
tenantId1 = TenantId.fromUUID(Uuids.timeBased());
ruleChainId1 = new RuleChainId(UUID.randomUUID());
tenantId2 = TenantId.fromUUID(Uuids.timeBased());
ruleChainId2 = new RuleChainId(UUID.randomUUID());
ruleNodeIds = createRuleNodes(tenantId1, tenantId2, ruleChainId1, ruleChainId2, COUNT);
}
@After
public void tearDown() throws Exception {
ruleNodeDao.removeAllByIds(ruleNodeIds);
if (executor != null) {
executor.shutdownNow();
}
}
@Test
public void testSaveRuleName0x00_thenSomeDatabaseException() {
RuleNode ruleNode = getRuleNode(ruleChainId1, "T", "\u0000");
assertThatThrownBy(() -> ruleNodeIds.add(ruleNodeDao.save(tenantId1, ruleNode).getUuidId()));
}
@Test
public void testFindRuleNodesByTenantIdAndType() {
List<RuleNode> ruleNodes1 = ruleNodeDao.findRuleNodesByTenantIdAndType(tenantId1, "A", PREFIX_FOR_RULE_NODE_NAME);
assertEquals(20, ruleNodes1.size());
List<RuleNode> ruleNodes2 = ruleNodeDao.findRuleNodesByTenantIdAndType(tenantId2, "B", PREFIX_FOR_RULE_NODE_NAME);
assertEquals(20, ruleNodes2.size());
ruleNodes1 = ruleNodeDao.findRuleNodesByTenantIdAndType(tenantId1, "A", null);
assertEquals(20, ruleNodes1.size());
ruleNodes2 = ruleNodeDao.findRuleNodesByTenantIdAndType(tenantId2, "B", null);
assertEquals(20, ruleNodes2.size());
}
@Test
public void testFindRuleNodesByType() {
PageData<RuleNode> ruleNodes = ruleNodeDao.findAllRuleNodesByType( "A", new PageLink(10, 0, PREFIX_FOR_RULE_NODE_NAME));
assertEquals(20, ruleNodes.getTotalElements());
assertEquals(2, ruleNodes.getTotalPages());
assertEquals(10, ruleNodes.getData().size());
ruleNodes = ruleNodeDao.findAllRuleNodesByType( "A", new PageLink(10, 0));
assertEquals(20, ruleNodes.getTotalElements());
assertEquals(2, ruleNodes.getTotalPages());
assertEquals(10, ruleNodes.getData().size());
}
@Test
public void testFindRuleNodesByTypeAndVersionLessThan() {
PageData<RuleNode> ruleNodes = ruleNodeDao.findAllRuleNodesByTypeAndVersionLessThan( "A", 1, new PageLink(10, 0, PREFIX_FOR_RULE_NODE_NAME));
assertEquals(20, ruleNodes.getTotalElements());
assertEquals(2, ruleNodes.getTotalPages());
assertEquals(10, ruleNodes.getData().size());
ruleNodes = ruleNodeDao.findAllRuleNodesByTypeAndVersionLessThan( "A", 1, new PageLink(10, 0));
assertEquals(20, ruleNodes.getTotalElements());
assertEquals(2, ruleNodes.getTotalPages());
assertEquals(10, ruleNodes.getData().size());
}
private List<UUID> createRuleNodes(TenantId tenantId1, TenantId tenantId2, RuleChainId ruleChainId1, RuleChainId ruleChainId2, int count) {
var chain1 = new RuleChain(ruleChainId1);
chain1.setTenantId(tenantId1);
chain1.setName(ruleChainId1.toString());
ruleChainDao.save(tenantId1, chain1);
var chain2 = new RuleChain(ruleChainId2);
chain2.setTenantId(tenantId2);
chain2.setName(ruleChainId2.toString());
ruleChainDao.save(tenantId2, chain2);
List<UUID> savedRuleNodeIds = new ArrayList<>();
for (int i = 0; i < count / 2; i++) {
savedRuleNodeIds.add(ruleNodeDao.save(tenantId1, getRuleNode(ruleChainId1, "A", Integer.toString(i))).getUuidId());
savedRuleNodeIds.add(ruleNodeDao.save(tenantId2, getRuleNode(ruleChainId2, "B", Integer.toString(i + count / 2))).getUuidId());
}
return savedRuleNodeIds;
}
private RuleNode getRuleNode(RuleChainId ruleChainId, String type, String nameSuffix) {
return getRuleNode(ruleChainId, Uuids.timeBased(), type, nameSuffix);
}
private RuleNode getRuleNode(RuleChainId ruleChainId, UUID ruleNodeId, String type, String nameSuffix) {
RuleNode ruleNode = new RuleNode();
ruleNode.setId(new RuleNodeId(ruleNodeId));
ruleNode.setRuleChainId(ruleChainId);
ruleNode.setName(nameSuffix);
ruleNode.setType(type);
ruleNode.setConfiguration(JacksonUtil.newObjectNode().put("searchHint", PREFIX_FOR_RULE_NODE_NAME + nameSuffix));
ruleNode.setConfigurationVersion(0);
return ruleNode;
}
}