Merge pull request #5695 from thingsboard/feature/entity-alarms

[3.3.3] Alarm Query performance improvements
This commit is contained in:
Andrew Shvayka 2021-12-08 15:14:38 +02:00 committed by GitHub
commit e02fa93382
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 366 additions and 78 deletions

View File

@ -0,0 +1,29 @@
--
-- Copyright © 2016-2021 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.
--
CREATE TABLE IF NOT EXISTS entity_alarm (
tenant_id uuid NOT NULL,
entity_id uuid NOT NULL,
created_time bigint NOT NULL,
type varchar(255) NOT NULL,
customer_id uuid,
alarm_id uuid
CONSTRAINT entity_alarm_pkey PRIMARY KEY(entity_id, alarm_id),
CONSTRAINT fk_entity_alarm_id FOREIGN KEY (alarm_id) REFERENCES alarm(id) ON DELETE CASCADE
);
CREATE INDEX IF NOT EXISTS idx_alarm_tenant_status_created_time ON alarm(tenant_id, status, created_time DESC);
CREATE INDEX IF NOT EXISTS idx_entity_alarm_created_time ON entity_alarm(tenant_id, entity_id, created_time DESC);

View File

@ -210,6 +210,10 @@ public class ThingsboardInstallService {
log.info("Upgrading ThingsBoard from version 3.3.0 to 3.3.1 ...");
case "3.3.1":
log.info("Upgrading ThingsBoard from version 3.3.1 to 3.3.2 ...");
break;
case "3.3.2":
log.info("Upgrading ThingsBoard from version 3.3.2 to 3.3.3 ...");
databaseEntitiesUpgradeService.upgradeDatabase("3.3.2");
log.info("Updating system data...");
systemDataLoaderService.updateSystemWidgets();
break;

View File

@ -469,6 +469,28 @@ public class SqlDatabaseUpgradeService implements DatabaseEntitiesUpgradeService
log.error("Failed updating schema!!!", e);
}
break;
case "3.3.2":
try (Connection conn = DriverManager.getConnection(dbUrl, dbUserName, dbPassword)) {
log.info("Updating schema ...");
schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", "3.3.2", SCHEMA_UPDATE_SQL);
loadSql(schemaUpdateFile, conn);
try {
conn.createStatement().execute("insert into entity_alarm(tenant_id, entity_id, created_time, type, customer_id, alarm_id, status, severity)" +
" select tenant_id, originator_id, created_time, type, customer_id, id, status, severity from alarm;");
conn.createStatement().execute("insert into entity_alarm(tenant_id, entity_id, created_time, type, customer_id, alarm_id, status, severity)" +
" select a.tenant_id, r.from_id, created_time, type, customer_id, id, status, severity" +
" from alarm a inner join relation r on r.relation_type_group = 'ALARM' and r.relation_type = 'ANY' and a.id = r.to_id ON CONFLICT DO NOTHING;");
conn.createStatement().execute("delete from relation r where r.relation_type_group = 'ALARM';");
} catch (Exception e) {
log.error("Failed to update alarm relations!!!", e);
}
log.info("Updating schema settings...");
conn.createStatement().execute("UPDATE tb_schema_settings SET schema_version = 3003003;");
log.info("Schema updated.");
} catch (Exception e) {
log.error("Failed updating schema!!!", e);
}
break;
default:
throw new RuntimeException("Unable to upgrade SQL database, unsupported fromVersion: " + fromVersion);
}

View File

@ -0,0 +1,40 @@
/**
* Copyright © 2016-2021 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.common.data.alarm;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.thingsboard.server.common.data.HasTenantId;
import org.thingsboard.server.common.data.id.AlarmId;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class EntityAlarm implements HasTenantId {
private TenantId tenantId;
private EntityId entityId;
private long createdTime;
private String alarmType;
private CustomerId customerId;
private AlarmId alarmId;
}

View File

@ -18,7 +18,6 @@ package org.thingsboard.server.common.data.relation;
public enum RelationTypeGroup {
COMMON,
ALARM,
DASHBOARD,
RULE_CHAIN,
RULE_NODE,

View File

@ -21,6 +21,7 @@ import org.thingsboard.server.common.data.alarm.AlarmInfo;
import org.thingsboard.server.common.data.alarm.AlarmQuery;
import org.thingsboard.server.common.data.alarm.AlarmSeverity;
import org.thingsboard.server.common.data.alarm.AlarmStatus;
import org.thingsboard.server.common.data.alarm.EntityAlarm;
import org.thingsboard.server.common.data.id.AlarmId;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.EntityId;
@ -32,6 +33,7 @@ import org.thingsboard.server.common.data.query.AlarmDataQuery;
import org.thingsboard.server.dao.Dao;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import java.util.UUID;
@ -59,4 +61,7 @@ public interface AlarmDao extends Dao<Alarm> {
PageData<AlarmId> findAlarmsIdsByEndTsBeforeAndTenantId(Long time, TenantId tenantId, PageLink pageLink);
void createEntityAlarmRecord(EntityAlarm entityAlarm);
List<EntityAlarm> findEntityAlarmRecords(TenantId tenantId, AlarmId id);
}

View File

@ -34,6 +34,7 @@ import org.thingsboard.server.common.data.alarm.AlarmQuery;
import org.thingsboard.server.common.data.alarm.AlarmSearchStatus;
import org.thingsboard.server.common.data.alarm.AlarmSeverity;
import org.thingsboard.server.common.data.alarm.AlarmStatus;
import org.thingsboard.server.common.data.alarm.EntityAlarm;
import org.thingsboard.server.common.data.exception.ApiUsageLimitsExceededException;
import org.thingsboard.server.common.data.id.AlarmId;
import org.thingsboard.server.common.data.id.CustomerId;
@ -166,23 +167,24 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ
private AlarmOperationResult createAlarm(Alarm alarm) throws InterruptedException, ExecutionException {
log.debug("New Alarm : {}", alarm);
Alarm saved = alarmDao.save(alarm.getTenantId(), alarm);
List<EntityId> propagatedEntitiesList = createAlarmRelations(saved);
List<EntityId> propagatedEntitiesList = createEntityAlarmRecords(saved);
return new AlarmOperationResult(saved, true, true, propagatedEntitiesList);
}
private List<EntityId> createAlarmRelations(Alarm alarm) throws InterruptedException, ExecutionException {
private List<EntityId> createEntityAlarmRecords(Alarm alarm) throws InterruptedException, ExecutionException {
List<EntityId> propagatedEntitiesList;
if (alarm.isPropagate()) {
Set<EntityId> parentEntities = getParentEntities(alarm);
propagatedEntitiesList = new ArrayList<>(parentEntities.size() + 1);
for (EntityId parentId : parentEntities) {
propagatedEntitiesList.add(parentId);
createAlarmRelation(alarm.getTenantId(), parentId, alarm.getId());
createEntityAlarmRecord(alarm.getTenantId(), parentId, alarm);
}
propagatedEntitiesList.add(alarm.getOriginator());
} else {
propagatedEntitiesList = Collections.singletonList(alarm.getOriginator());
}
createEntityAlarmRecord(alarm.getTenantId(), alarm.getOriginator(), alarm);
return propagatedEntitiesList;
}
@ -221,7 +223,7 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ
List<EntityId> propagatedEntitiesList;
if (!oldPropagate && newPropagate) {
try {
propagatedEntitiesList = createAlarmRelations(result);
propagatedEntitiesList = createEntityAlarmRecords(result);
} catch (InterruptedException | ExecutionException e) {
log.warn("Failed to update alarm relations [{}]", result, e);
throw new RuntimeException(e);
@ -382,17 +384,20 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ
private Set<EntityId> getPropagationEntityIds(Alarm alarm) {
if (alarm.isPropagate()) {
List<EntityRelation> relations = relationService.findByTo(alarm.getTenantId(), alarm.getId(), RelationTypeGroup.ALARM);
Set<EntityId> propagationEntityIds = relations.stream().map(EntityRelation::getFrom).collect(Collectors.toSet());
propagationEntityIds.add(alarm.getOriginator());
return propagationEntityIds;
List<EntityAlarm> entityAlarms = alarmDao.findEntityAlarmRecords(alarm.getTenantId(), alarm.getId());
return entityAlarms.stream().map(EntityAlarm::getEntityId).collect(Collectors.toSet());
} else {
return Collections.singleton(alarm.getOriginator());
}
}
private void createAlarmRelation(TenantId tenantId, EntityId entityId, EntityId alarmId) {
createRelation(tenantId, new EntityRelation(entityId, alarmId, AlarmSearchStatus.ANY.name(), RelationTypeGroup.ALARM));
private void createEntityAlarmRecord(TenantId tenantId, EntityId entityId, Alarm alarm) {
EntityAlarm entityAlarm = new EntityAlarm(tenantId, entityId, alarm.getCreatedTime(), alarm.getType(), alarm.getCustomerId(), alarm.getId());
try {
alarmDao.createEntityAlarmRecord(entityAlarm);
} catch (Exception e) {
log.warn("[{}] Failed to create entity alarm record: {}", tenantId, entityAlarm, e);
}
}
private <T> ListenableFuture<T> getAndUpdate(TenantId tenantId, AlarmId alarmId, Function<Alarm, T> function) {
@ -402,7 +407,7 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ
}
private DataValidator<Alarm> alarmDataValidator =
new DataValidator<Alarm>() {
new DataValidator<>() {
@Override
protected void validateDataImpl(TenantId tenantId, Alarm alarm) {

View File

@ -44,7 +44,6 @@ import org.thingsboard.server.common.data.id.EntityViewId;
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.page.TimePageLink;
import org.thingsboard.server.common.data.relation.EntityRelation;
import org.thingsboard.server.common.data.relation.EntitySearchDirection;
import org.thingsboard.server.common.data.relation.RelationTypeGroup;

View File

@ -257,6 +257,7 @@ public class ModelConstants {
/**
* Cassandra alarm constants.
*/
public static final String ENTITY_ALARM_COLUMN_FAMILY_NAME = "entity_alarm";
public static final String ALARM_COLUMN_FAMILY_NAME = "alarm";
public static final String ALARM_TENANT_ID_PROPERTY = TENANT_ID_PROPERTY;
public static final String ALARM_CUSTOMER_ID_PROPERTY = CUSTOMER_ID_PROPERTY;

View File

@ -0,0 +1,42 @@
/**
* Copyright © 2016-2021 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.model.sql;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.thingsboard.server.common.data.alarm.EntityAlarm;
import javax.persistence.Transient;
import java.io.Serializable;
import java.util.UUID;
@NoArgsConstructor
@AllArgsConstructor
@Data
public class EntityAlarmCompositeKey implements Serializable {
@Transient
private static final long serialVersionUID = -245388185894468450L;
private UUID entityId;
private UUID alarmId;
public EntityAlarmCompositeKey(EntityAlarm entityAlarm) {
this.entityId = entityAlarm.getEntityId().getId();
this.alarmId = entityAlarm.getAlarmId().getId();
}
}

View File

@ -0,0 +1,99 @@
/**
* Copyright © 2016-2021 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.model.sql;
import lombok.Data;
import org.thingsboard.server.common.data.alarm.EntityAlarm;
import org.thingsboard.server.common.data.id.AlarmId;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.EntityIdFactory;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.dao.model.ToData;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.IdClass;
import javax.persistence.Table;
import java.util.UUID;
import static org.thingsboard.server.dao.model.ModelConstants.CREATED_TIME_PROPERTY;
import static org.thingsboard.server.dao.model.ModelConstants.CUSTOMER_ID_PROPERTY;
import static org.thingsboard.server.dao.model.ModelConstants.ENTITY_ALARM_COLUMN_FAMILY_NAME;
import static org.thingsboard.server.dao.model.ModelConstants.ENTITY_ID_COLUMN;
import static org.thingsboard.server.dao.model.ModelConstants.ENTITY_TYPE_COLUMN;
import static org.thingsboard.server.dao.model.ModelConstants.TENANT_ID_COLUMN;
@Data
@Entity
@Table(name = ENTITY_ALARM_COLUMN_FAMILY_NAME)
@IdClass(EntityAlarmCompositeKey.class)
public final class EntityAlarmEntity implements ToData<EntityAlarm> {
@Column(name = TENANT_ID_COLUMN, columnDefinition = "uuid")
private UUID tenantId;
@Column(name = ENTITY_TYPE_COLUMN)
private String entityType;
@Id
@Column(name = ENTITY_ID_COLUMN, columnDefinition = "uuid")
private UUID entityId;
@Id
@Column(name = "alarm_id", columnDefinition = "uuid")
private UUID alarmId;
@Column(name = CREATED_TIME_PROPERTY)
private long createdTime;
@Column(name = "alarm_type")
private String alarmType;
@Column(name = CUSTOMER_ID_PROPERTY, columnDefinition = "uuid")
private UUID customerId;
public EntityAlarmEntity() {
super();
}
public EntityAlarmEntity(EntityAlarm entityAlarm) {
tenantId = entityAlarm.getTenantId().getId();
entityId = entityAlarm.getEntityId().getId();
entityType = entityAlarm.getEntityId().getEntityType().name();
alarmId = entityAlarm.getAlarmId().getId();
alarmType = entityAlarm.getAlarmType();
createdTime = entityAlarm.getCreatedTime();
if (entityAlarm.getCustomerId() != null) {
customerId = entityAlarm.getCustomerId().getId();
}
}
@Override
public EntityAlarm toData() {
EntityAlarm result = new EntityAlarm();
result.setTenantId(new TenantId(tenantId));
result.setEntityId(EntityIdFactory.getByTypeAndUuid(entityType, entityId));
result.setAlarmId(new AlarmId(alarmId));
result.setAlarmType(alarmType);
result.setCreatedTime(createdTime);
if (customerId != null) {
result.setCustomerId(new CustomerId(customerId));
}
return result;
}
}

View File

@ -42,47 +42,28 @@ public interface AlarmRepository extends CrudRepository<AlarmEntity, UUID> {
Pageable pageable);
@Query(value = "SELECT new org.thingsboard.server.dao.model.sql.AlarmInfoEntity(a) FROM AlarmEntity a " +
"LEFT JOIN RelationEntity re ON a.id = re.toId " +
"AND re.relationTypeGroup = 'ALARM' " +
"AND re.toType = 'ALARM' " +
"AND re.fromId = :affectedEntityId " +
"AND re.fromType = :affectedEntityType " +
"LEFT JOIN EntityAlarmEntity ea ON a.id = ea.alarmId " +
"WHERE a.tenantId = :tenantId " +
"AND (a.originatorId = :affectedEntityId or re.fromId IS NOT NULL) " +
"AND (:startTime IS NULL OR a.createdTime >= :startTime) " +
"AND (:endTime IS NULL OR a.createdTime <= :endTime) " +
"AND ea.tenantId = :tenantId " +
"AND ea.entityId = :affectedEntityId " +
"AND ea.entityType = :affectedEntityType " +
"AND (:startTime IS NULL OR (a.createdTime >= :startTime AND ea.createdTime >= :startTime)) " +
"AND (:endTime IS NULL OR (a.createdTime <= :endTime AND ea.createdTime <= :endTime)) " +
"AND ((:alarmStatuses) IS NULL OR a.status in (:alarmStatuses)) " +
"AND (LOWER(a.type) LIKE LOWER(CONCAT('%', :searchText, '%')) " +
" OR LOWER(a.severity) LIKE LOWER(CONCAT('%', :searchText, '%')) " +
" OR LOWER(a.status) LIKE LOWER(CONCAT('%', :searchText, '%'))) "
,
countQuery = "" +
"SELECT count(a) + " + //alarms with relations only
" (SELECT count(a) FROM AlarmEntity a " + //alarms WITHOUT any relations
" LEFT JOIN RelationEntity re ON a.id = re.toId " +
" AND re.relationTypeGroup = 'ALARM' " +
" AND re.toType = 'ALARM' " +
" AND re.fromId = :affectedEntityId " +
" AND re.fromType = :affectedEntityType " +
" WHERE a.tenantId = :tenantId " +
" AND (a.originatorId = :affectedEntityId) " +
" AND (re.fromId IS NULL) " + //anti join
" AND (:startTime IS NULL OR a.createdTime >= :startTime) " +
" AND (:endTime IS NULL OR a.createdTime <= :endTime) " +
" AND ((:alarmStatuses) IS NULL OR a.status in (:alarmStatuses)) " +
" AND (LOWER(a.type) LIKE LOWER(CONCAT('%', :searchText, '%')) " +
" OR LOWER(a.severity) LIKE LOWER(CONCAT('%', :searchText, '%')) " +
" OR LOWER(a.status) LIKE LOWER(CONCAT('%', :searchText, '%'))) " +
" )" +
"SELECT count(a) " + //alarms with relations only
"FROM AlarmEntity a " +
"INNER JOIN RelationEntity re ON a.id = re.toId " +
"AND re.relationTypeGroup = 'ALARM' " +
"AND re.toType = 'ALARM' " +
"AND re.fromId = :affectedEntityId " +
"AND re.fromType = :affectedEntityType " +
"LEFT JOIN EntityAlarmEntity ea ON a.id = ea.alarmId " +
"WHERE a.tenantId = :tenantId " +
"AND (:startTime IS NULL OR a.createdTime >= :startTime) " +
"AND (:endTime IS NULL OR a.createdTime <= :endTime) " +
"AND ea.tenantId = :tenantId " +
"AND ea.entityId = :affectedEntityId " +
"AND ea.entityType = :affectedEntityType " +
"AND (:startTime IS NULL OR (a.createdTime >= :startTime AND ea.createdTime >= :startTime)) " +
"AND (:endTime IS NULL OR (a.createdTime <= :endTime AND ea.createdTime <= :endTime)) " +
"AND ((:alarmStatuses) IS NULL OR a.status in (:alarmStatuses)) " +
"AND (LOWER(a.type) LIKE LOWER(CONCAT('%', :searchText, '%')) " +
" OR LOWER(a.severity) LIKE LOWER(CONCAT('%', :searchText, '%')) " +
@ -149,13 +130,11 @@ public interface AlarmRepository extends CrudRepository<AlarmEntity, UUID> {
Pageable pageable);
@Query(value = "SELECT a.severity FROM AlarmEntity a " +
"LEFT JOIN RelationEntity re ON a.id = re.toId " +
"AND re.relationTypeGroup = 'ALARM' " +
"AND re.toType = 'ALARM' " +
"AND re.fromId = :affectedEntityId " +
"AND re.fromType = :affectedEntityType " +
"LEFT JOIN EntityAlarmEntity ea ON a.id = ea.alarmId " +
"WHERE a.tenantId = :tenantId " +
"AND (a.originatorId = :affectedEntityId or re.fromId IS NOT NULL) " +
"AND ea.tenantId = :tenantId " +
"AND ea.entityId = :affectedEntityId " +
"AND ea.entityType = :affectedEntityType " +
"AND ((:alarmStatuses) IS NULL OR a.status in (:alarmStatuses))")
Set<AlarmSeverity> findAlarmSeverities(@Param("tenantId") UUID tenantId,
@Param("affectedEntityId") UUID affectedEntityId,

View File

@ -0,0 +1,29 @@
/**
* Copyright © 2016-2021 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.alarm;
import org.springframework.data.repository.CrudRepository;
import org.thingsboard.server.dao.model.sql.EntityAlarmCompositeKey;
import org.thingsboard.server.dao.model.sql.EntityAlarmEntity;
import java.util.List;
import java.util.UUID;
public interface EntityAlarmRepository extends CrudRepository<EntityAlarmEntity, EntityAlarmCompositeKey> {
List<EntityAlarmEntity> findAllByAlarmId(UUID alarmId);
}

View File

@ -26,6 +26,7 @@ import org.thingsboard.server.common.data.alarm.AlarmInfo;
import org.thingsboard.server.common.data.alarm.AlarmQuery;
import org.thingsboard.server.common.data.alarm.AlarmSeverity;
import org.thingsboard.server.common.data.alarm.AlarmStatus;
import org.thingsboard.server.common.data.alarm.EntityAlarm;
import org.thingsboard.server.common.data.id.AlarmId;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.EntityId;
@ -37,6 +38,7 @@ import org.thingsboard.server.common.data.query.AlarmDataQuery;
import org.thingsboard.server.dao.DaoUtil;
import org.thingsboard.server.dao.alarm.AlarmDao;
import org.thingsboard.server.dao.model.sql.AlarmEntity;
import org.thingsboard.server.dao.model.sql.EntityAlarmEntity;
import org.thingsboard.server.dao.relation.RelationDao;
import org.thingsboard.server.dao.sql.JpaAbstractDao;
import org.thingsboard.server.dao.sql.query.AlarmQueryRepository;
@ -62,7 +64,7 @@ public class JpaAlarmDao extends JpaAbstractDao<AlarmEntity, Alarm> implements A
private AlarmQueryRepository alarmQueryRepository;
@Autowired
private RelationDao relationDao;
private EntityAlarmRepository entityAlarmRepository;
@Override
protected Class<AlarmEntity> getEntityClass() {
@ -169,4 +171,16 @@ public class JpaAlarmDao extends JpaAbstractDao<AlarmEntity, Alarm> implements A
return DaoUtil.pageToPageData(alarmRepository.findAlarmsIdsByEndTsBeforeAndTenantId(time, tenantId.getId(), DaoUtil.toPageable(pageLink)))
.mapData(AlarmId::new);
}
@Override
public void createEntityAlarmRecord(EntityAlarm entityAlarm) {
log.debug("Saving entity {}", entityAlarm);
entityAlarmRepository.save(new EntityAlarmEntity(entityAlarm));
}
@Override
public List<EntityAlarm> findEntityAlarmRecords(TenantId tenantId, AlarmId id) {
log.trace("[{}] Try to find entity alarm records using [{}]", tenantId, id);
return DaoUtil.convertDataList(entityAlarmRepository.findAllByAlarmId(id.getId()));
}
}

View File

@ -105,7 +105,7 @@ public class DefaultAlarmQueryRepository implements AlarmQueryRepository {
" a.propagate_relation_types as propagate_relation_types, " +
" a.type as type," + SELECT_ORIGINATOR_NAME + ", ";
private static final String JOIN_RELATIONS = "left join relation r on r.relation_type_group = 'ALARM' and r.relation_type = 'ANY' and a.id = r.to_id and r.from_id in (:entity_ids)";
private static final String JOIN_ENTITY_ALARMS = "inner join entity_alarm ea on a.id = ea.alarm_id";
protected final NamedParameterJdbcTemplate jdbcTemplate;
private final TransactionTemplate transactionTemplate;
@ -132,8 +132,8 @@ public class DefaultAlarmQueryRepository implements AlarmQueryRepository {
StringBuilder joinPart = new StringBuilder();
boolean addAnd = false;
if (pageLink.isSearchPropagatedAlarms()) {
selectPart.append(" CASE WHEN r.from_id IS NULL THEN a.originator_id ELSE r.from_id END as entity_id ");
fromPart.append(JOIN_RELATIONS);
selectPart.append(" ea.entity_id as entity_id ");
fromPart.append(JOIN_ENTITY_ALARMS);
wherePart.append(buildPermissionsQuery(tenantId, customerId, ctx));
addAnd = true;
} else {
@ -145,7 +145,7 @@ public class DefaultAlarmQueryRepository implements AlarmQueryRepository {
sortPart.append(alarmFieldColumnMap.getOrDefault(sortOrderKey, sortOrderKey))
.append(" ").append(sortOrder.getDirection().name());
if (pageLink.isSearchPropagatedAlarms()) {
wherePart.append(" and (a.originator_id in (:entity_ids) or r.from_id IS NOT NULL)");
wherePart.append(" and ea.entity_id in (:entity_ids)");
} else {
addAndIfNeeded(wherePart, addAnd);
addAnd = true;
@ -166,7 +166,7 @@ public class DefaultAlarmQueryRepository implements AlarmQueryRepository {
}
joinPart.append(" as e(id, priority)) e ");
if (pageLink.isSearchPropagatedAlarms()) {
joinPart.append("on (r.from_id IS NULL and a.originator_id = e.id) or (r.from_id IS NOT NULL and r.from_id = e.id)");
joinPart.append("on ea.entity_id = e.id");
} else {
joinPart.append("on a.originator_id = e.id");
}
@ -188,6 +188,9 @@ public class DefaultAlarmQueryRepository implements AlarmQueryRepository {
addAnd = true;
ctx.addLongParameter("startTime", startTs);
wherePart.append("a.created_time >= :startTime");
if (pageLink.isSearchPropagatedAlarms()) {
wherePart.append(" and ea.created_time >= :startTime");
}
}
if (endTs > 0) {
@ -195,6 +198,9 @@ public class DefaultAlarmQueryRepository implements AlarmQueryRepository {
addAnd = true;
ctx.addLongParameter("endTime", endTs);
wherePart.append("a.created_time <= :endTime");
if (pageLink.isSearchPropagatedAlarms()) {
wherePart.append(" and ea.created_time <= :endTime");
}
}
if (pageLink.getTypeList() != null && !pageLink.getTypeList().isEmpty()) {
@ -202,6 +208,9 @@ public class DefaultAlarmQueryRepository implements AlarmQueryRepository {
addAnd = true;
ctx.addStringListParameter("alarmTypes", pageLink.getTypeList());
wherePart.append("a.type in (:alarmTypes)");
if (pageLink.isSearchPropagatedAlarms()) {
wherePart.append(" and ea.alarm_type in (:alarmTypes)");
}
}
if (pageLink.getSeverityList() != null && !pageLink.getSeverityList().isEmpty()) {
@ -279,7 +288,7 @@ public class DefaultAlarmQueryRepository implements AlarmQueryRepository {
private String buildPermissionsQuery(TenantId tenantId, CustomerId customerId, QueryContext ctx) {
StringBuilder permissionsQuery = new StringBuilder();
ctx.addUuidParameter("permissions_tenant_id", tenantId.getId());
permissionsQuery.append(" a.tenant_id = :permissions_tenant_id ");
permissionsQuery.append(" a.tenant_id = :permissions_tenant_id and ea.tenant_id = :permissions_tenant_id ");
/*
No need to check the customer id, because we already use entity id list that passed security check when we were evaluating the data query.
*/

View File

@ -43,6 +43,18 @@ CREATE TABLE IF NOT EXISTS alarm (
type varchar(255)
);
CREATE TABLE IF NOT EXISTS entity_alarm (
tenant_id uuid NOT NULL,
entity_type varchar(32),
entity_id uuid NOT NULL,
created_time bigint NOT NULL,
alarm_type varchar(255) NOT NULL,
customer_id uuid,
alarm_id uuid,
CONSTRAINT entity_alarm_pkey PRIMARY KEY(entity_id, alarm_id),
CONSTRAINT fk_entity_alarm_id FOREIGN KEY (alarm_id) REFERENCES alarm(id) ON DELETE CASCADE
);
CREATE TABLE IF NOT EXISTS asset (
id uuid NOT NULL CONSTRAINT asset_pkey PRIMARY KEY,
created_time bigint NOT NULL,

View File

@ -20,8 +20,12 @@ CREATE INDEX IF NOT EXISTS idx_alarm_originator_created_time ON alarm(originator
CREATE INDEX IF NOT EXISTS idx_alarm_tenant_created_time ON alarm(tenant_id, created_time DESC);
CREATE INDEX IF NOT EXISTS idx_alarm_tenant_status_created_time ON alarm(tenant_id, status, created_time DESC);
CREATE INDEX IF NOT EXISTS idx_alarm_tenant_alarm_type_created_time ON alarm(tenant_id, type, created_time DESC);
CREATE INDEX IF NOT EXISTS idx_entity_alarm_created_time ON entity_alarm(tenant_id, entity_id, created_time DESC);
CREATE INDEX IF NOT EXISTS idx_relation_to_id ON relation(relation_type_group, to_type, to_id);
CREATE INDEX IF NOT EXISTS idx_relation_from_id ON relation(relation_type_group, from_type, from_id);

View File

@ -58,6 +58,18 @@ CREATE TABLE IF NOT EXISTS alarm (
type varchar(255)
);
CREATE TABLE IF NOT EXISTS entity_alarm (
tenant_id uuid NOT NULL,
entity_type varchar(32),
entity_id uuid NOT NULL,
created_time bigint NOT NULL,
alarm_type varchar(255) NOT NULL,
customer_id uuid,
alarm_id uuid,
CONSTRAINT entity_alarm_pkey PRIMARY KEY(entity_id, alarm_id),
CONSTRAINT fk_entity_alarm_id FOREIGN KEY (alarm_id) REFERENCES alarm(id) ON DELETE CASCADE
);
CREATE TABLE IF NOT EXISTS asset (
id uuid NOT NULL CONSTRAINT asset_pkey PRIMARY KEY,
created_time bigint NOT NULL,

View File

@ -612,16 +612,6 @@ public abstract class BaseAlarmServiceTest extends AbstractServiceTest {
Assert.assertEquals(1, alarms.getData().size());
Assert.assertEquals(created, alarms.getData().get(0));
List<EntityRelation> toAlarmRelations = relationService.findByTo(tenantId, created.getId(), RelationTypeGroup.ALARM);
Assert.assertEquals(1, toAlarmRelations.size());
List<EntityRelation> fromChildRelations = relationService.findByFrom(tenantId, childId, RelationTypeGroup.ALARM);
Assert.assertEquals(0, fromChildRelations.size());
List<EntityRelation> fromParentRelations = relationService.findByFrom(tenantId, parentId, RelationTypeGroup.ALARM);
Assert.assertEquals(1, fromParentRelations.size());
Assert.assertTrue("Alarm was not deleted when expected", alarmService.deleteAlarm(tenantId, created.getId()).isSuccessful());
Alarm fetched = alarmService.findAlarmByIdAsync(tenantId, created.getId()).get();
@ -647,14 +637,5 @@ public abstract class BaseAlarmServiceTest extends AbstractServiceTest {
Assert.assertNotNull(alarms.getData());
Assert.assertEquals(0, alarms.getData().size());
toAlarmRelations = relationService.findByTo(tenantId, created.getId(), RelationTypeGroup.ALARM);
Assert.assertEquals(0, toAlarmRelations.size());
fromChildRelations = relationService.findByFrom(tenantId, childId, RelationTypeGroup.ALARM);
Assert.assertEquals(0, fromChildRelations.size());
fromParentRelations = relationService.findByFrom(tenantId, childId, RelationTypeGroup.ALARM);
Assert.assertEquals(0, fromParentRelations.size());
}
}

View File

@ -1,4 +1,5 @@
DROP TABLE IF EXISTS admin_settings;
DROP TABLE IF EXISTS entity_alarm;
DROP TABLE IF EXISTS alarm;
DROP TABLE IF EXISTS asset;
DROP TABLE IF EXISTS audit_log;

View File

@ -1,4 +1,5 @@
DROP TABLE IF EXISTS admin_settings;
DROP TABLE IF EXISTS entity_alarm;
DROP TABLE IF EXISTS alarm;
DROP TABLE IF EXISTS asset;
DROP TABLE IF EXISTS audit_log;

View File

@ -1,4 +1,5 @@
DROP TABLE IF EXISTS admin_settings;
DROP TABLE IF EXISTS entity_alarm;
DROP TABLE IF EXISTS alarm;
DROP TABLE IF EXISTS asset;
DROP TABLE IF EXISTS audit_log;