Make save edge event method async
This commit is contained in:
parent
00de756e03
commit
f8687cb983
@ -35,6 +35,7 @@ import org.thingsboard.server.actors.TbActorCtx;
|
||||
import org.thingsboard.server.actors.shared.AbstractContextAwareMsgProcessor;
|
||||
import org.thingsboard.server.common.data.DataConstants;
|
||||
import org.thingsboard.server.common.data.Device;
|
||||
import org.thingsboard.server.common.data.EdgeUtils;
|
||||
import org.thingsboard.server.common.data.StringUtils;
|
||||
import org.thingsboard.server.common.data.edge.EdgeEvent;
|
||||
import org.thingsboard.server.common.data.edge.EdgeEventActionType;
|
||||
@ -97,7 +98,6 @@ import javax.annotation.Nullable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.ConcurrentModificationException;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
@ -811,12 +811,6 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
|
||||
}
|
||||
|
||||
private void saveRpcRequestToEdgeQueue(ToDeviceRpcRequest msg, Integer requestId) {
|
||||
EdgeEvent edgeEvent = new EdgeEvent();
|
||||
edgeEvent.setTenantId(tenantId);
|
||||
edgeEvent.setAction(EdgeEventActionType.RPC_CALL);
|
||||
edgeEvent.setEntityId(deviceId.getId());
|
||||
edgeEvent.setType(EdgeEventType.DEVICE);
|
||||
|
||||
ObjectNode body = mapper.createObjectNode();
|
||||
body.put("requestId", requestId);
|
||||
body.put("requestUUID", msg.getId().toString());
|
||||
@ -824,13 +818,23 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
|
||||
body.put("expirationTime", msg.getExpirationTime());
|
||||
body.put("method", msg.getBody().getMethod());
|
||||
body.put("params", msg.getBody().getParams());
|
||||
edgeEvent.setBody(body);
|
||||
|
||||
edgeEvent.setEdgeId(edgeId);
|
||||
systemContext.getEdgeEventService().save(edgeEvent);
|
||||
EdgeEvent edgeEvent = EdgeUtils.constructEdgeEvent(tenantId, edgeId, EdgeEventType.DEVICE, EdgeEventActionType.RPC_CALL, deviceId, body);
|
||||
|
||||
Futures.addCallback(systemContext.getEdgeEventService().saveAsync(edgeEvent), new FutureCallback<>() {
|
||||
@Override
|
||||
public void onSuccess(Void unused) {
|
||||
systemContext.getClusterService().onEdgeEventUpdate(tenantId, edgeId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Throwable t) {
|
||||
String errMsg = String.format("Failed to save edge event. msg [%s], edge event [%s]", msg, edgeEvent);
|
||||
log.warn(errMsg, t);
|
||||
}
|
||||
}, systemContext.getDbCallbackExecutor());
|
||||
}
|
||||
|
||||
private List<TsKvProto> toTsKvProtos(@Nullable List<AttributeKvEntry> result) {
|
||||
List<TsKvProto> clientAttributes;
|
||||
if (result == null || result.isEmpty()) {
|
||||
|
||||
@ -16,11 +16,15 @@
|
||||
package org.thingsboard.server.service.edge;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.google.common.util.concurrent.FutureCallback;
|
||||
import com.google.common.util.concurrent.Futures;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.thingsboard.common.util.ThingsBoardThreadFactory;
|
||||
import org.thingsboard.server.cluster.TbClusterService;
|
||||
import org.thingsboard.server.common.data.EdgeUtils;
|
||||
import org.thingsboard.server.common.data.edge.Edge;
|
||||
import org.thingsboard.server.common.data.edge.EdgeEvent;
|
||||
import org.thingsboard.server.common.data.edge.EdgeEventActionType;
|
||||
@ -76,17 +80,17 @@ public class DefaultEdgeNotificationService implements EdgeNotificationService {
|
||||
@Autowired
|
||||
private CustomerEdgeProcessor customerProcessor;
|
||||
|
||||
private ExecutorService tsCallBackExecutor;
|
||||
private ExecutorService dbCallBackExecutor;
|
||||
|
||||
@PostConstruct
|
||||
public void initExecutor() {
|
||||
tsCallBackExecutor = Executors.newSingleThreadExecutor(ThingsBoardThreadFactory.forName("edge-notifications"));
|
||||
dbCallBackExecutor = Executors.newSingleThreadExecutor(ThingsBoardThreadFactory.forName("edge-notifications"));
|
||||
}
|
||||
|
||||
@PreDestroy
|
||||
public void shutdownExecutor() {
|
||||
if (tsCallBackExecutor != null) {
|
||||
tsCallBackExecutor.shutdownNow();
|
||||
if (dbCallBackExecutor != null) {
|
||||
dbCallBackExecutor.shutdownNow();
|
||||
}
|
||||
}
|
||||
|
||||
@ -107,19 +111,22 @@ public class DefaultEdgeNotificationService implements EdgeNotificationService {
|
||||
log.debug("Pushing edge event to edge queue. tenantId [{}], edgeId [{}], type [{}], action[{}], entityId [{}], body [{}]",
|
||||
tenantId, edgeId, type, action, entityId, body);
|
||||
|
||||
EdgeEvent edgeEvent = new EdgeEvent();
|
||||
edgeEvent.setEdgeId(edgeId);
|
||||
edgeEvent.setTenantId(tenantId);
|
||||
edgeEvent.setType(type);
|
||||
edgeEvent.setAction(action);
|
||||
if (entityId != null) {
|
||||
edgeEvent.setEntityId(entityId.getId());
|
||||
}
|
||||
edgeEvent.setBody(body);
|
||||
edgeEventService.save(edgeEvent);
|
||||
EdgeEvent edgeEvent = EdgeUtils.constructEdgeEvent(tenantId, edgeId, type, action, entityId, body);
|
||||
|
||||
Futures.addCallback(edgeEventService.saveAsync(edgeEvent), new FutureCallback<>() {
|
||||
@Override
|
||||
public void onSuccess(@Nullable Void unused) {
|
||||
clusterService.onEdgeEventUpdate(tenantId, edgeId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Throwable t) {
|
||||
String errMsg = String.format("Failed to save edge event. edge event [%s]", edgeEvent);
|
||||
log.warn(errMsg, t);
|
||||
}
|
||||
}, dbCallBackExecutor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void pushNotificationToEdge(TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg, TbCallback callback) {
|
||||
log.debug("Pushing notification to edge {}", edgeNotificationMsg);
|
||||
|
||||
@ -17,10 +17,14 @@ package org.thingsboard.server.service.edge.rpc.processor;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.google.common.util.concurrent.FutureCallback;
|
||||
import com.google.common.util.concurrent.Futures;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.thingsboard.server.cluster.TbClusterService;
|
||||
import org.thingsboard.server.common.data.Device;
|
||||
import org.thingsboard.server.common.data.EdgeUtils;
|
||||
import org.thingsboard.server.common.data.HasCustomerId;
|
||||
import org.thingsboard.server.common.data.edge.Edge;
|
||||
import org.thingsboard.server.common.data.edge.EdgeEvent;
|
||||
@ -189,19 +193,22 @@ public abstract class BaseEdgeProcessor {
|
||||
"action [{}], entityId [{}], body [{}]",
|
||||
tenantId, edgeId, type, action, entityId, body);
|
||||
|
||||
EdgeEvent edgeEvent = new EdgeEvent();
|
||||
edgeEvent.setTenantId(tenantId);
|
||||
edgeEvent.setEdgeId(edgeId);
|
||||
edgeEvent.setType(type);
|
||||
edgeEvent.setAction(action);
|
||||
if (entityId != null) {
|
||||
edgeEvent.setEntityId(entityId.getId());
|
||||
}
|
||||
edgeEvent.setBody(body);
|
||||
edgeEventService.save(edgeEvent);
|
||||
EdgeEvent edgeEvent = EdgeUtils.constructEdgeEvent(tenantId, edgeId, type, action, entityId, body);
|
||||
|
||||
Futures.addCallback(edgeEventService.saveAsync(edgeEvent), new FutureCallback<>() {
|
||||
@Override
|
||||
public void onSuccess(@Nullable Void unused) {
|
||||
tbClusterService.onEdgeEventUpdate(tenantId, edgeId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Throwable t) {
|
||||
String errMsg = String.format("Failed to save edge event. edge event [%s]", edgeEvent);
|
||||
log.warn(errMsg, t);
|
||||
}
|
||||
}, dbCallbackExecutorService);
|
||||
}
|
||||
|
||||
protected CustomerId getCustomerIdIfEdgeAssignedToCustomer(HasCustomerId hasCustomerIdEntity, Edge edge) {
|
||||
if (!edge.getCustomerId().isNullUid() && edge.getCustomerId().equals(hasCustomerIdEntity.getCustomerId())) {
|
||||
return edge.getCustomerId();
|
||||
|
||||
@ -405,8 +405,18 @@ public class DefaultEdgeRequestsService implements EdgeRequestsService {
|
||||
|
||||
EdgeEvent edgeEvent = EdgeUtils.constructEdgeEvent(tenantId, edgeId, type, action, entityId, body);
|
||||
|
||||
edgeEventService.save(edgeEvent);
|
||||
Futures.addCallback(edgeEventService.saveAsync(edgeEvent), new FutureCallback<>() {
|
||||
@Override
|
||||
public void onSuccess(@Nullable Void unused) {
|
||||
tbClusterService.onEdgeEventUpdate(tenantId, edgeId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Throwable t) {
|
||||
String errMsg = String.format("Failed to save edge event. edge event [%s]", edgeEvent);
|
||||
log.warn(errMsg, t);
|
||||
}
|
||||
}, dbCallbackExecutorService);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -976,7 +976,7 @@ abstract public class BaseEdgeTest extends AbstractControllerTest {
|
||||
String timeseriesData = "{\"data\":{\"temperature\":25},\"ts\":" + System.currentTimeMillis() + "}";
|
||||
JsonNode timeseriesEntityData = mapper.readTree(timeseriesData);
|
||||
EdgeEvent edgeEvent = constructEdgeEvent(tenantId, edge.getId(), EdgeEventActionType.TIMESERIES_UPDATED, device.getId().getId(), EdgeEventType.DEVICE, timeseriesEntityData);
|
||||
edgeEventService.save(edgeEvent);
|
||||
edgeEventService.saveAsync(edgeEvent).get();
|
||||
clusterService.onEdgeEventUpdate(tenantId, edge.getId());
|
||||
Assert.assertTrue(edgeImitator.waitForMessages());
|
||||
|
||||
@ -1007,12 +1007,12 @@ abstract public class BaseEdgeTest extends AbstractControllerTest {
|
||||
testAttributesDeleteMsg(device);
|
||||
}
|
||||
|
||||
private void testAttributesUpdatedMsg(Device device) throws JsonProcessingException, InterruptedException {
|
||||
private void testAttributesUpdatedMsg(Device device) throws Exception {
|
||||
String attributesData = "{\"scope\":\"SERVER_SCOPE\",\"kv\":{\"key1\":\"value1\"}}";
|
||||
JsonNode attributesEntityData = mapper.readTree(attributesData);
|
||||
EdgeEvent edgeEvent1 = constructEdgeEvent(tenantId, edge.getId(), EdgeEventActionType.ATTRIBUTES_UPDATED, device.getId().getId(), EdgeEventType.DEVICE, attributesEntityData);
|
||||
edgeImitator.expectMessageAmount(1);
|
||||
edgeEventService.save(edgeEvent1);
|
||||
edgeEventService.saveAsync(edgeEvent1).get();
|
||||
clusterService.onEdgeEventUpdate(tenantId, edge.getId());
|
||||
Assert.assertTrue(edgeImitator.waitForMessages());
|
||||
|
||||
@ -1032,12 +1032,12 @@ abstract public class BaseEdgeTest extends AbstractControllerTest {
|
||||
Assert.assertEquals("value1", keyValueProto.getStringV());
|
||||
}
|
||||
|
||||
private void testPostAttributesMsg(Device device) throws JsonProcessingException, InterruptedException {
|
||||
private void testPostAttributesMsg(Device device) throws Exception {
|
||||
String postAttributesData = "{\"scope\":\"SERVER_SCOPE\",\"kv\":{\"key2\":\"value2\"}}";
|
||||
JsonNode postAttributesEntityData = mapper.readTree(postAttributesData);
|
||||
EdgeEvent edgeEvent = constructEdgeEvent(tenantId, edge.getId(), EdgeEventActionType.POST_ATTRIBUTES, device.getId().getId(), EdgeEventType.DEVICE, postAttributesEntityData);
|
||||
edgeImitator.expectMessageAmount(1);
|
||||
edgeEventService.save(edgeEvent);
|
||||
edgeEventService.saveAsync(edgeEvent).get();
|
||||
clusterService.onEdgeEventUpdate(tenantId, edge.getId());
|
||||
Assert.assertTrue(edgeImitator.waitForMessages());
|
||||
|
||||
@ -1057,12 +1057,12 @@ abstract public class BaseEdgeTest extends AbstractControllerTest {
|
||||
Assert.assertEquals("value2", keyValueProto.getStringV());
|
||||
}
|
||||
|
||||
private void testAttributesDeleteMsg(Device device) throws JsonProcessingException, InterruptedException {
|
||||
private void testAttributesDeleteMsg(Device device) throws Exception {
|
||||
String deleteAttributesData = "{\"scope\":\"SERVER_SCOPE\",\"keys\":[\"key1\",\"key2\"]}";
|
||||
JsonNode deleteAttributesEntityData = mapper.readTree(deleteAttributesData);
|
||||
EdgeEvent edgeEvent = constructEdgeEvent(tenantId, edge.getId(), EdgeEventActionType.ATTRIBUTES_DELETED, device.getId().getId(), EdgeEventType.DEVICE, deleteAttributesEntityData);
|
||||
edgeImitator.expectMessageAmount(1);
|
||||
edgeEventService.save(edgeEvent);
|
||||
edgeEventService.saveAsync(edgeEvent).get();
|
||||
clusterService.onEdgeEventUpdate(tenantId, edge.getId());
|
||||
Assert.assertTrue(edgeImitator.waitForMessages());
|
||||
|
||||
@ -1097,7 +1097,7 @@ abstract public class BaseEdgeTest extends AbstractControllerTest {
|
||||
|
||||
EdgeEvent edgeEvent = constructEdgeEvent(tenantId, edge.getId(), EdgeEventActionType.RPC_CALL, device.getId().getId(), EdgeEventType.DEVICE, body);
|
||||
edgeImitator.expectMessageAmount(1);
|
||||
edgeEventService.save(edgeEvent);
|
||||
edgeEventService.saveAsync(edgeEvent).get();
|
||||
clusterService.onEdgeEventUpdate(tenantId, edge.getId());
|
||||
Assert.assertTrue(edgeImitator.waitForMessages());
|
||||
|
||||
@ -1122,7 +1122,7 @@ abstract public class BaseEdgeTest extends AbstractControllerTest {
|
||||
JsonNode timeseriesEntityData = mapper.readTree(timeseriesData);
|
||||
EdgeEvent edgeEvent = constructEdgeEvent(tenantId, edge.getId(), EdgeEventActionType.TIMESERIES_UPDATED,
|
||||
device.getId().getId(), EdgeEventType.DEVICE, timeseriesEntityData);
|
||||
edgeEventService.save(edgeEvent);
|
||||
edgeEventService.saveAsync(edgeEvent).get();
|
||||
clusterService.onEdgeEventUpdate(tenantId, edge.getId());
|
||||
}
|
||||
|
||||
|
||||
@ -15,6 +15,7 @@
|
||||
*/
|
||||
package org.thingsboard.server.dao.edge;
|
||||
|
||||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
import org.thingsboard.server.common.data.edge.EdgeEvent;
|
||||
import org.thingsboard.server.common.data.id.EdgeId;
|
||||
import org.thingsboard.server.common.data.id.TenantId;
|
||||
@ -23,7 +24,7 @@ import org.thingsboard.server.common.data.page.TimePageLink;
|
||||
|
||||
public interface EdgeEventService {
|
||||
|
||||
EdgeEvent save(EdgeEvent edgeEvent);
|
||||
ListenableFuture<Void> saveAsync(EdgeEvent edgeEvent);
|
||||
|
||||
PageData<EdgeEvent> findEdgeEvents(TenantId tenantId, EdgeId edgeId, TimePageLink pageLink, boolean withTsUpdate);
|
||||
|
||||
|
||||
@ -15,6 +15,7 @@
|
||||
*/
|
||||
package org.thingsboard.server.dao.edge;
|
||||
|
||||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
@ -36,9 +37,9 @@ public class BaseEdgeEventService implements EdgeEventService {
|
||||
private DataValidator<EdgeEvent> edgeEventValidator;
|
||||
|
||||
@Override
|
||||
public EdgeEvent save(EdgeEvent edgeEvent) {
|
||||
public ListenableFuture<Void> saveAsync(EdgeEvent edgeEvent) {
|
||||
edgeEventValidator.validate(edgeEvent, EdgeEvent::getTenantId);
|
||||
return edgeEventDao.save(edgeEvent);
|
||||
return edgeEventDao.saveAsync(edgeEvent);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@ -35,7 +35,7 @@ public interface EdgeEventDao extends Dao<EdgeEvent> {
|
||||
* @param edgeEvent the event object
|
||||
* @return saved edge event object future
|
||||
*/
|
||||
EdgeEvent save(EdgeEvent edgeEvent);
|
||||
ListenableFuture<Void> saveAsync(EdgeEvent edgeEvent);
|
||||
|
||||
|
||||
/**
|
||||
|
||||
@ -0,0 +1,79 @@
|
||||
/**
|
||||
* 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.sql.edge;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.jdbc.core.BatchPreparedStatementSetter;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.stereotype.Repository;
|
||||
import org.springframework.transaction.TransactionStatus;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
|
||||
import org.springframework.transaction.support.TransactionTemplate;
|
||||
import org.thingsboard.server.dao.model.sql.EdgeEventEntity;
|
||||
import org.thingsboard.server.dao.util.PsqlDao;
|
||||
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.SQLException;
|
||||
import java.util.List;
|
||||
|
||||
@PsqlDao
|
||||
@Repository
|
||||
@Transactional
|
||||
public class EdgeEventInsertRepository {
|
||||
|
||||
private static final String INSERT =
|
||||
"INSERT INTO edge_event (id, created_time, edge_id, edge_event_type, edge_event_uid, entity_id, edge_event_action, body, tenant_id, ts) " +
|
||||
"VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) " +
|
||||
"ON CONFLICT DO NOTHING;";
|
||||
|
||||
@Autowired
|
||||
protected JdbcTemplate jdbcTemplate;
|
||||
|
||||
@Autowired
|
||||
private TransactionTemplate transactionTemplate;
|
||||
|
||||
protected void save(List<EdgeEventEntity> entities) {
|
||||
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
|
||||
@Override
|
||||
protected void doInTransactionWithoutResult(TransactionStatus status) {
|
||||
jdbcTemplate.batchUpdate(INSERT, new BatchPreparedStatementSetter() {
|
||||
@Override
|
||||
public void setValues(PreparedStatement ps, int i) throws SQLException {
|
||||
EdgeEventEntity edgeEvent = entities.get(i);
|
||||
ps.setObject(1, edgeEvent.getId());
|
||||
ps.setLong(2, edgeEvent.getCreatedTime());
|
||||
ps.setObject(3, edgeEvent.getEdgeId());
|
||||
ps.setString(4, edgeEvent.getEdgeEventType().name());
|
||||
ps.setString(5, edgeEvent.getEdgeEventUid());
|
||||
ps.setObject(6, edgeEvent.getEntityId());
|
||||
ps.setString(7, edgeEvent.getEdgeEventAction().name());
|
||||
ps.setString(8, edgeEvent.getEntityBody() != null
|
||||
? edgeEvent.getEntityBody().toString()
|
||||
: null);
|
||||
ps.setObject(9, edgeEvent.getTenantId());
|
||||
ps.setLong(10, edgeEvent.getTs());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBatchSize() {
|
||||
return entities.size();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -16,9 +16,11 @@
|
||||
package org.thingsboard.server.dao.sql.edge;
|
||||
|
||||
import com.datastax.oss.driver.api.core.uuid.Uuids;
|
||||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.thingsboard.server.common.data.edge.EdgeEvent;
|
||||
@ -26,15 +28,22 @@ import org.thingsboard.server.common.data.id.EdgeEventId;
|
||||
import org.thingsboard.server.common.data.id.EdgeId;
|
||||
import org.thingsboard.server.common.data.page.PageData;
|
||||
import org.thingsboard.server.common.data.page.TimePageLink;
|
||||
import org.thingsboard.server.common.stats.StatsFactory;
|
||||
import org.thingsboard.server.dao.DaoUtil;
|
||||
import org.thingsboard.server.dao.edge.EdgeEventDao;
|
||||
import org.thingsboard.server.dao.model.sql.EdgeEventEntity;
|
||||
import org.thingsboard.server.dao.sql.JpaAbstractSearchTextDao;
|
||||
import org.thingsboard.server.dao.sql.ScheduledLogExecutorComponent;
|
||||
import org.thingsboard.server.dao.sql.TbSqlBlockingQueueParams;
|
||||
import org.thingsboard.server.dao.sql.TbSqlBlockingQueueWrapper;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import javax.annotation.PreDestroy;
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Comparator;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
@ -43,6 +52,7 @@ import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
import java.util.function.Function;
|
||||
|
||||
import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID;
|
||||
|
||||
@ -52,11 +62,32 @@ public class JpaBaseEdgeEventDao extends JpaAbstractSearchTextDao<EdgeEventEntit
|
||||
|
||||
private final UUID systemTenantId = NULL_UUID;
|
||||
|
||||
private final ConcurrentMap<EdgeId, Lock> readWriteLocks = new ConcurrentHashMap<>();
|
||||
@Autowired
|
||||
ScheduledLogExecutorComponent logExecutor;
|
||||
|
||||
@Autowired
|
||||
private StatsFactory statsFactory;
|
||||
|
||||
@Value("${sql.edge_events.batch_size:10000}")
|
||||
private int batchSize;
|
||||
|
||||
@Value("${sql.edge_events.batch_max_delay:100}")
|
||||
private long maxDelay;
|
||||
|
||||
@Value("${sql.edge_events.stats_print_interval_ms:10000}")
|
||||
private long statsPrintIntervalMs;
|
||||
|
||||
@Value("${sql.edge_events.batch_threads:3}")
|
||||
private int batchThreads;
|
||||
|
||||
private TbSqlBlockingQueueWrapper<EdgeEventEntity> queue;
|
||||
|
||||
@Autowired
|
||||
private EdgeEventRepository edgeEventRepository;
|
||||
|
||||
@Autowired
|
||||
private EdgeEventInsertRepository edgeEventInsertRepository;
|
||||
|
||||
@Override
|
||||
protected Class<EdgeEventEntity> getEntityClass() {
|
||||
return EdgeEventEntity.class;
|
||||
@ -67,11 +98,38 @@ public class JpaBaseEdgeEventDao extends JpaAbstractSearchTextDao<EdgeEventEntit
|
||||
return edgeEventRepository;
|
||||
}
|
||||
|
||||
@PostConstruct
|
||||
private void init() {
|
||||
TbSqlBlockingQueueParams params = TbSqlBlockingQueueParams.builder()
|
||||
.logName("Edge Events")
|
||||
.batchSize(batchSize)
|
||||
.maxDelay(maxDelay)
|
||||
.statsPrintIntervalMs(statsPrintIntervalMs)
|
||||
.statsNamePrefix("edge.events")
|
||||
.batchSortEnabled(true)
|
||||
.build();
|
||||
Function<EdgeEventEntity, Integer> hashcodeFunction = entity -> {
|
||||
if (entity.getEntityId() != null) {
|
||||
return entity.getEntityId().hashCode();
|
||||
} else {
|
||||
return NULL_UUID.hashCode();
|
||||
}
|
||||
};
|
||||
queue = new TbSqlBlockingQueueWrapper<>(params, hashcodeFunction, batchThreads, statsFactory);
|
||||
queue.init(logExecutor, v -> edgeEventInsertRepository.save(v),
|
||||
Comparator.comparing(EdgeEventEntity::getTs)
|
||||
);
|
||||
}
|
||||
|
||||
@PreDestroy
|
||||
private void destroy() {
|
||||
if (queue != null) {
|
||||
queue.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public EdgeEvent save(EdgeEvent edgeEvent) {
|
||||
final Lock readWriteLock = readWriteLocks.computeIfAbsent(edgeEvent.getEdgeId(), id -> new ReentrantLock());
|
||||
readWriteLock.lock();
|
||||
try {
|
||||
public ListenableFuture<Void> saveAsync(EdgeEvent edgeEvent) {
|
||||
log.debug("Save edge event [{}] ", edgeEvent);
|
||||
if (edgeEvent.getId() == null) {
|
||||
UUID timeBased = Uuids.timeBased();
|
||||
@ -88,17 +146,29 @@ public class JpaBaseEdgeEventDao extends JpaAbstractSearchTextDao<EdgeEventEntit
|
||||
if (StringUtils.isEmpty(edgeEvent.getUid())) {
|
||||
edgeEvent.setUid(edgeEvent.getId().toString());
|
||||
}
|
||||
return save(new EdgeEventEntity(edgeEvent)).orElse(null);
|
||||
} finally {
|
||||
readWriteLock.unlock();
|
||||
return save(new EdgeEventEntity(edgeEvent));
|
||||
}
|
||||
|
||||
private ListenableFuture<Void> save(EdgeEventEntity entity) {
|
||||
log.debug("Save edge event [{}] ", entity);
|
||||
if (entity.getTenantId() == null) {
|
||||
log.trace("Save system edge event with predefined id {}", systemTenantId);
|
||||
entity.setTenantId(systemTenantId);
|
||||
}
|
||||
if (entity.getUuid() == null) {
|
||||
entity.setUuid(Uuids.timeBased());
|
||||
}
|
||||
|
||||
return addToQueue(entity);
|
||||
}
|
||||
|
||||
private ListenableFuture<Void> addToQueue(EdgeEventEntity entity) {
|
||||
return queue.add(entity);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public PageData<EdgeEvent> findEdgeEvents(UUID tenantId, EdgeId edgeId, TimePageLink pageLink, boolean withTsUpdate) {
|
||||
final Lock readWriteLock = readWriteLocks.computeIfAbsent(edgeId, id -> new ReentrantLock());
|
||||
readWriteLock.lock();
|
||||
try {
|
||||
if (withTsUpdate) {
|
||||
return DaoUtil.toPageData(
|
||||
edgeEventRepository
|
||||
@ -121,21 +191,6 @@ public class JpaBaseEdgeEventDao extends JpaAbstractSearchTextDao<EdgeEventEntit
|
||||
DaoUtil.toPageable(pageLink)));
|
||||
|
||||
}
|
||||
} finally {
|
||||
readWriteLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public Optional<EdgeEvent> save(EdgeEventEntity entity) {
|
||||
log.debug("Save edge event [{}] ", entity);
|
||||
if (entity.getTenantId() == null) {
|
||||
log.trace("Save system edge event with predefined id {}", systemTenantId);
|
||||
entity.setTenantId(systemTenantId);
|
||||
}
|
||||
if (entity.getUuid() == null) {
|
||||
entity.setUuid(Uuids.timeBased());
|
||||
}
|
||||
return Optional.of(DaoUtil.getData(edgeEventRepository.save(entity)));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@ -16,6 +16,8 @@
|
||||
package org.thingsboard.server.dao.service;
|
||||
|
||||
import com.datastax.oss.driver.api.core.uuid.Uuids;
|
||||
import com.google.common.util.concurrent.Futures;
|
||||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.thingsboard.server.common.data.edge.EdgeEvent;
|
||||
@ -34,6 +36,8 @@ import java.io.IOException;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.Month;
|
||||
import java.time.ZoneOffset;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public abstract class BaseEdgeEventServiceTest extends AbstractServiceTest {
|
||||
|
||||
@ -41,8 +45,14 @@ public abstract class BaseEdgeEventServiceTest extends AbstractServiceTest {
|
||||
public void saveEdgeEvent() throws Exception {
|
||||
EdgeId edgeId = new EdgeId(Uuids.timeBased());
|
||||
DeviceId deviceId = new DeviceId(Uuids.timeBased());
|
||||
EdgeEvent edgeEvent = generateEdgeEvent(null, edgeId, deviceId, EdgeEventActionType.ADDED);
|
||||
EdgeEvent saved = edgeEventService.save(edgeEvent);
|
||||
TenantId tenantId = new TenantId(Uuids.timeBased());
|
||||
EdgeEvent edgeEvent = generateEdgeEvent(tenantId, edgeId, deviceId, EdgeEventActionType.ADDED);
|
||||
edgeEventService.saveAsync(edgeEvent).get();
|
||||
|
||||
PageData<EdgeEvent> edgeEvents = edgeEventService.findEdgeEvents(tenantId, edgeId, new TimePageLink(1), false);
|
||||
Assert.assertFalse(edgeEvents.getData().isEmpty());
|
||||
|
||||
EdgeEvent saved = edgeEvents.getData().get(0);
|
||||
Assert.assertEquals(saved.getTenantId(), edgeEvent.getTenantId());
|
||||
Assert.assertEquals(saved.getEdgeId(), edgeEvent.getEdgeId());
|
||||
Assert.assertEquals(saved.getEntityId(), edgeEvent.getEntityId());
|
||||
@ -77,27 +87,31 @@ public abstract class BaseEdgeEventServiceTest extends AbstractServiceTest {
|
||||
EdgeId edgeId = new EdgeId(Uuids.timeBased());
|
||||
DeviceId deviceId = new DeviceId(Uuids.timeBased());
|
||||
TenantId tenantId = TenantId.fromUUID(Uuids.timeBased());
|
||||
saveEdgeEventWithProvidedTime(timeBeforeStartTime, edgeId, deviceId, tenantId);
|
||||
EdgeEvent savedEdgeEvent = saveEdgeEventWithProvidedTime(eventTime, edgeId, deviceId, tenantId);
|
||||
EdgeEvent savedEdgeEvent2 = saveEdgeEventWithProvidedTime(eventTime + 1, edgeId, deviceId, tenantId);
|
||||
EdgeEvent savedEdgeEvent3 = saveEdgeEventWithProvidedTime(eventTime + 2, edgeId, deviceId, tenantId);
|
||||
saveEdgeEventWithProvidedTime(timeAfterEndTime, edgeId, deviceId, tenantId);
|
||||
|
||||
List<ListenableFuture<Void>> futures = new ArrayList<>();
|
||||
futures.add(saveEdgeEventWithProvidedTime(timeBeforeStartTime, edgeId, deviceId, tenantId));
|
||||
futures.add(saveEdgeEventWithProvidedTime(eventTime, edgeId, deviceId, tenantId));
|
||||
futures.add(saveEdgeEventWithProvidedTime(eventTime + 1, edgeId, deviceId, tenantId));
|
||||
futures.add(saveEdgeEventWithProvidedTime(eventTime + 2, edgeId, deviceId, tenantId));
|
||||
futures.add(saveEdgeEventWithProvidedTime(timeAfterEndTime, edgeId, deviceId, tenantId));
|
||||
|
||||
Futures.allAsList(futures).get();
|
||||
|
||||
TimePageLink pageLink = new TimePageLink(2, 0, "", new SortOrder("createdTime", SortOrder.Direction.DESC), startTime, endTime);
|
||||
PageData<EdgeEvent> edgeEvents = edgeEventService.findEdgeEvents(tenantId, edgeId, pageLink, true);
|
||||
|
||||
Assert.assertNotNull(edgeEvents.getData());
|
||||
Assert.assertTrue(edgeEvents.getData().size() == 2);
|
||||
Assert.assertTrue(edgeEvents.getData().get(0).getUuidId().equals(savedEdgeEvent3.getUuidId()));
|
||||
Assert.assertTrue(edgeEvents.getData().get(1).getUuidId().equals(savedEdgeEvent2.getUuidId()));
|
||||
Assert.assertEquals(2, edgeEvents.getData().size());
|
||||
Assert.assertEquals(Uuids.startOf(eventTime + 2), edgeEvents.getData().get(0).getUuidId());
|
||||
Assert.assertEquals(Uuids.startOf(eventTime + 1), edgeEvents.getData().get(1).getUuidId());
|
||||
Assert.assertTrue(edgeEvents.hasNext());
|
||||
Assert.assertNotNull(pageLink.nextPageLink());
|
||||
|
||||
edgeEvents = edgeEventService.findEdgeEvents(tenantId, edgeId, pageLink.nextPageLink(), true);
|
||||
|
||||
Assert.assertNotNull(edgeEvents.getData());
|
||||
Assert.assertTrue(edgeEvents.getData().size() == 1);
|
||||
Assert.assertTrue(edgeEvents.getData().get(0).getUuidId().equals(savedEdgeEvent.getUuidId()));
|
||||
Assert.assertEquals(1, edgeEvents.getData().size());
|
||||
Assert.assertEquals(Uuids.startOf(eventTime), edgeEvents.getData().get(0).getUuidId());
|
||||
Assert.assertFalse(edgeEvents.hasNext());
|
||||
}
|
||||
|
||||
@ -109,7 +123,7 @@ public abstract class BaseEdgeEventServiceTest extends AbstractServiceTest {
|
||||
TimePageLink pageLink = new TimePageLink(1, 0, null, new SortOrder("createdTime", SortOrder.Direction.ASC));
|
||||
|
||||
EdgeEvent edgeEventWithTsUpdate = generateEdgeEvent(tenantId, edgeId, deviceId, EdgeEventActionType.TIMESERIES_UPDATED);
|
||||
edgeEventService.save(edgeEventWithTsUpdate);
|
||||
edgeEventService.saveAsync(edgeEventWithTsUpdate).get();
|
||||
|
||||
PageData<EdgeEvent> allEdgeEvents = edgeEventService.findEdgeEvents(tenantId, edgeId, pageLink, true);
|
||||
PageData<EdgeEvent> edgeEventsWithoutTsUpdate = edgeEventService.findEdgeEvents(tenantId, edgeId, pageLink, false);
|
||||
@ -121,9 +135,9 @@ public abstract class BaseEdgeEventServiceTest extends AbstractServiceTest {
|
||||
Assert.assertTrue(edgeEventsWithoutTsUpdate.getData().isEmpty());
|
||||
}
|
||||
|
||||
private EdgeEvent saveEdgeEventWithProvidedTime(long time, EdgeId edgeId, EntityId entityId, TenantId tenantId) throws Exception {
|
||||
private ListenableFuture<Void> saveEdgeEventWithProvidedTime(long time, EdgeId edgeId, EntityId entityId, TenantId tenantId) throws Exception {
|
||||
EdgeEvent edgeEvent = generateEdgeEvent(tenantId, edgeId, entityId, EdgeEventActionType.ADDED);
|
||||
edgeEvent.setId(new EdgeEventId(Uuids.startOf(time)));
|
||||
return edgeEventService.save(edgeEvent);
|
||||
return edgeEventService.saveAsync(edgeEvent);
|
||||
}
|
||||
}
|
||||
@ -80,10 +80,6 @@ public abstract class AbstractTbMsgPushNode<T extends BaseTbMsgPushNodeConfigura
|
||||
if (DataConstants.ALARM.equals(msgType)) {
|
||||
return buildEvent(ctx.getTenantId(), EdgeEventActionType.ADDED, getUUIDFromMsgData(msg), getAlarmEventType(), null);
|
||||
} else {
|
||||
U eventTypeByEntityType = getEventTypeByEntityType(msg.getOriginator().getEntityType());
|
||||
if (eventTypeByEntityType == null) {
|
||||
return null;
|
||||
}
|
||||
EdgeEventActionType actionType = getEdgeEventActionTypeByMsgType(msgType);
|
||||
Map<String, Object> entityBody = new HashMap<>();
|
||||
Map<String, String> metadata = msg.getMetaData().getData();
|
||||
@ -107,7 +103,11 @@ public abstract class AbstractTbMsgPushNode<T extends BaseTbMsgPushNodeConfigura
|
||||
entityBody.put("ts", msg.getMetaDataTs());
|
||||
break;
|
||||
}
|
||||
return buildEvent(ctx.getTenantId(), actionType, msg.getOriginator().getId(), eventTypeByEntityType, JacksonUtil.valueToTree(entityBody));
|
||||
return buildEvent(ctx.getTenantId(),
|
||||
actionType,
|
||||
msg.getOriginator().getId(),
|
||||
getEventTypeByEntityType(msg.getOriginator().getEntityType()),
|
||||
JacksonUtil.valueToTree(entityBody));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -16,7 +16,11 @@
|
||||
package org.thingsboard.rule.engine.edge;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.google.common.util.concurrent.FutureCallback;
|
||||
import com.google.common.util.concurrent.Futures;
|
||||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.thingsboard.rule.engine.api.RuleNode;
|
||||
import org.thingsboard.rule.engine.api.TbContext;
|
||||
import org.thingsboard.server.common.data.DataConstants;
|
||||
@ -33,6 +37,8 @@ import org.thingsboard.server.common.data.plugin.ComponentType;
|
||||
import org.thingsboard.server.common.data.rule.RuleChainType;
|
||||
import org.thingsboard.server.common.msg.TbMsg;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
import static org.thingsboard.rule.engine.api.TbRelationTypes.SUCCESS;
|
||||
@ -107,29 +113,33 @@ public class TbMsgPushToEdgeNode extends AbstractTbMsgPushNode<TbMsgPushToEdgeNo
|
||||
|
||||
@Override
|
||||
protected void processMsg(TbContext ctx, TbMsg msg) {
|
||||
try {
|
||||
if (EntityType.EDGE.equals(msg.getOriginator().getEntityType())) {
|
||||
EdgeEvent edgeEvent = buildEvent(msg, ctx);
|
||||
if (edgeEvent != null) {
|
||||
EdgeId edgeId = new EdgeId(msg.getOriginator().getId());
|
||||
notifyEdge(ctx, msg, edgeEvent, edgeId);
|
||||
} else {
|
||||
tellFailure(ctx, msg);
|
||||
ListenableFuture<Void> future = notifyEdge(ctx, edgeEvent, edgeId);
|
||||
FutureCallback<Void> futureCallback = new FutureCallback<>() {
|
||||
@Override
|
||||
public void onSuccess(@Nullable Void result) {
|
||||
ctx.tellSuccess(msg);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Throwable t) {
|
||||
ctx.tellFailure(msg, t);
|
||||
}
|
||||
};
|
||||
Futures.addCallback(future, futureCallback, ctx.getDbCallbackExecutor());
|
||||
} else {
|
||||
PageLink pageLink = new PageLink(DEFAULT_PAGE_SIZE);
|
||||
PageData<EdgeId> pageData;
|
||||
boolean edgeNotified = false;
|
||||
List<ListenableFuture<Void>> futures = new ArrayList<>();
|
||||
do {
|
||||
pageData = ctx.getEdgeService().findRelatedEdgeIdsByEntityId(ctx.getTenantId(), msg.getOriginator(), pageLink);
|
||||
if (pageData != null && pageData.getData() != null && !pageData.getData().isEmpty()) {
|
||||
for (EdgeId edgeId : pageData.getData()) {
|
||||
EdgeEvent edgeEvent = buildEvent(msg, ctx);
|
||||
if (edgeEvent != null) {
|
||||
notifyEdge(ctx, msg, edgeEvent, edgeId);
|
||||
edgeNotified = true;
|
||||
} else {
|
||||
tellFailure(ctx, msg);
|
||||
}
|
||||
futures.add(notifyEdge(ctx, edgeEvent, edgeId));
|
||||
}
|
||||
if (pageData.hasNext()) {
|
||||
pageLink = pageLink.nextPageLink();
|
||||
@ -137,24 +147,36 @@ public class TbMsgPushToEdgeNode extends AbstractTbMsgPushNode<TbMsgPushToEdgeNo
|
||||
}
|
||||
} while (pageData != null && pageData.hasNext());
|
||||
|
||||
if (!edgeNotified) {
|
||||
if (futures.isEmpty()) {
|
||||
// ack in case no edges are related to provided entity
|
||||
ctx.ack(msg);
|
||||
} else {
|
||||
Futures.addCallback(Futures.allAsList(futures), new FutureCallback<>() {
|
||||
@Override
|
||||
public void onSuccess(@Nullable List<Void> voids) {
|
||||
ctx.tellSuccess(msg);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Throwable t) {
|
||||
ctx.tellFailure(msg, t);
|
||||
}
|
||||
}, ctx.getDbCallbackExecutor());
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("Failed to build edge event", e);
|
||||
ctx.tellFailure(msg, e);
|
||||
}
|
||||
}
|
||||
|
||||
private void tellFailure(TbContext ctx, TbMsg msg) {
|
||||
String errMsg = String.format("Edge event type is null. Entity Type %s", msg.getOriginator().getEntityType());
|
||||
log.warn(errMsg);
|
||||
ctx.tellFailure(msg, new RuntimeException(errMsg));
|
||||
}
|
||||
|
||||
private void notifyEdge(TbContext ctx, TbMsg msg, EdgeEvent edgeEvent, EdgeId edgeId) {
|
||||
private ListenableFuture<Void> notifyEdge(TbContext ctx, EdgeEvent edgeEvent, EdgeId edgeId) {
|
||||
edgeEvent.setEdgeId(edgeId);
|
||||
ctx.getEdgeEventService().save(edgeEvent);
|
||||
ctx.tellNext(msg, SUCCESS);
|
||||
ListenableFuture<Void> future = ctx.getEdgeEventService().saveAsync(edgeEvent);
|
||||
return Futures.transform(future, result -> {
|
||||
ctx.onEdgeEventUpdate(ctx.getTenantId(), edgeId);
|
||||
return null;
|
||||
}, ctx.getDbCallbackExecutor());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user