Commit 07bdcac0fe2b8b5165cd7823fa53deaba26ea45a

Authored by Andrii Shvaika
1 parent 2ccce3b6

Refactoring of Rule Engine API

Showing 18 changed files with 362 additions and 90 deletions
... ... @@ -5,7 +5,7 @@
5 5 * you may not use this file except in compliance with the License.
6 6 * You may obtain a copy of the License at
7 7 *
8   - * http://www.apache.org/licenses/LICENSE-2.0
  8 + * http://www.apache.org/licenses/LICENSE-2.0
9 9 *
10 10 * Unless required by applicable law or agreed to in writing, software
11 11 * distributed under the License is distributed on an "AS IS" BASIS,
... ... @@ -63,8 +63,13 @@ import org.thingsboard.server.dao.rule.RuleChainService;
63 63 import org.thingsboard.server.dao.tenant.TenantService;
64 64 import org.thingsboard.server.dao.timeseries.TimeseriesService;
65 65 import org.thingsboard.server.dao.user.UserService;
  66 +import org.thingsboard.server.gen.transport.TransportProtos;
  67 +import org.thingsboard.server.queue.TbQueueProducer;
  68 +import org.thingsboard.server.queue.common.TbProtoQueueMsg;
66 69 import org.thingsboard.server.queue.discovery.PartitionService;
  70 +import org.thingsboard.server.queue.discovery.ServiceType;
67 71 import org.thingsboard.server.queue.discovery.TbServiceInfoProvider;
  72 +import org.thingsboard.server.queue.discovery.TopicPartitionInfo;
68 73 import org.thingsboard.server.queue.provider.TbRuleEngineQueueProvider;
69 74 import org.thingsboard.server.service.component.ComponentDiscoveryService;
70 75 import org.thingsboard.server.service.encoding.DataDecodingEncodingService;
... ... @@ -154,7 +159,6 @@ public class ActorSystemContext {
154 159 private RuleChainService ruleChainService;
155 160
156 161 @Autowired
157   - @Getter
158 162 private PartitionService partitionService;
159 163
160 164 @Autowired
... ... @@ -402,6 +406,17 @@ public class ActorSystemContext {
402 406 return mapper.createObjectNode().put("server", serviceId).put("method", method).put("error", body);
403 407 }
404 408
  409 + public TbQueueProducer<TbProtoQueueMsg<TransportProtos.ToCoreMsg>> getTbCoreMsgProducer() {
  410 + return ruleEngineQueueProvider.getTbCoreMsgProducer();
  411 + }
  412 +
  413 + public TbQueueProducer<TbProtoQueueMsg<TransportProtos.ToRuleEngineMsg>> getRuleEngineMsgProducer() {
  414 + return ruleEngineQueueProvider.getRuleEngineMsgProducer();
  415 + }
  416 +
  417 + public TopicPartitionInfo resolve(ServiceType serviceType, TenantId tenantId, EntityId entityId) {
  418 + return partitionService.resolve(serviceType, tenantId, entityId);
  419 + }
405 420
406 421 public String getServerAddress() {
407 422 return serviceInfoProvider.getServiceId();
... ...
... ... @@ -5,7 +5,7 @@
5 5 * you may not use this file except in compliance with the License.
6 6 * You may obtain a copy of the License at
7 7 *
8   - * http://www.apache.org/licenses/LICENSE-2.0
  8 + * http://www.apache.org/licenses/LICENSE-2.0
9 9 *
10 10 * Unless required by applicable law or agreed to in writing, software
11 11 * distributed under the License is distributed on an "AS IS" BASIS,
... ... @@ -65,7 +65,10 @@ import org.thingsboard.server.dao.rule.RuleChainService;
65 65 import org.thingsboard.server.dao.tenant.TenantService;
66 66 import org.thingsboard.server.dao.timeseries.TimeseriesService;
67 67 import org.thingsboard.server.dao.user.UserService;
  68 +import org.thingsboard.server.gen.transport.TransportProtos;
  69 +import org.thingsboard.server.queue.common.TbProtoQueueMsg;
68 70 import org.thingsboard.server.queue.discovery.ServiceType;
  71 +import org.thingsboard.server.queue.discovery.TopicPartitionInfo;
69 72 import org.thingsboard.server.service.script.RuleNodeJsScriptEngine;
70 73 import scala.concurrent.duration.Duration;
71 74
... ... @@ -119,7 +122,7 @@ class DefaultTbContext implements TbContext {
119 122
120 123 @Override
121 124 public boolean isLocalEntity(EntityId entityId) {
122   - return mainCtx.getPartitionService().resolve(ServiceType.TB_RULE_ENGINE, getTenantId(), entityId).isMyPartition();
  125 + return mainCtx.resolve(ServiceType.TB_RULE_ENGINE, getTenantId(), entityId).isMyPartition();
123 126 }
124 127
125 128 private void scheduleMsgWithDelay(Object msg, long delayInMs, ActorRef target) {
... ... @@ -151,8 +154,13 @@ class DefaultTbContext implements TbContext {
151 154 }
152 155
153 156 @Override
154   - public void sendTbMsgToRuleEngine(TbMsg msg) {
155   - mainCtx.getActorService().onMsg(new SendToClusterMsg(msg.getOriginator(), new QueueToRuleEngineMsg(getTenantId(), msg)));
  157 + public void sendTbMsgToRuleEngine(TbMsg tbMsg) {
  158 + TenantId tenantId = getTenantId();
  159 + TopicPartitionInfo tpi = mainCtx.resolve(ServiceType.TB_RULE_ENGINE, tenantId, tbMsg.getOriginator());
  160 + TransportProtos.ToRuleEngineMsg msg = TransportProtos.ToRuleEngineMsg.newBuilder().setTbMsg(TbMsg.toByteString(tbMsg))
  161 + .setTenantIdMSB(tenantId.getId().getMostSignificantBits())
  162 + .setTenantIdLSB(tenantId.getId().getLeastSignificantBits()).build();
  163 + mainCtx.getRuleEngineMsgProducer().send(tpi, new TbProtoQueueMsg<>(tbMsg.getId(), msg), null);
156 164 }
157 165
158 166 public TbMsg customerCreatedMsg(Customer customer, RuleNodeId ruleNodeId) {
... ...
... ... @@ -5,7 +5,7 @@
5 5 * you may not use this file except in compliance with the License.
6 6 * You may obtain a copy of the License at
7 7 *
8   - * http://www.apache.org/licenses/LICENSE-2.0
  8 + * http://www.apache.org/licenses/LICENSE-2.0
9 9 *
10 10 * Unless required by applicable law or agreed to in writing, software
11 11 * distributed under the License is distributed on an "AS IS" BASIS,
... ... @@ -68,7 +68,6 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh
68 68 private final Map<RuleNodeId, RuleNodeCtx> nodeActors;
69 69 private final Map<RuleNodeId, List<RuleNodeRelation>> nodeRoutes;
70 70 private final RuleChainService service;
71   - private final PartitionService partitionService;
72 71 private final TbQueueProducer<TbProtoQueueMsg<ToRuleEngineMsg>> producer;
73 72
74 73 private RuleNodeId firstId;
... ... @@ -83,7 +82,6 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh
83 82 this.nodeActors = new HashMap<>();
84 83 this.nodeRoutes = new HashMap<>();
85 84 this.service = systemContext.getRuleChainService();
86   - this.partitionService = systemContext.getPartitionService();
87 85 this.producer = systemContext.getRuleEngineQueueProvider().getRuleEngineMsgProducer();
88 86 }
89 87
... ... @@ -234,7 +232,7 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh
234 232 try {
235 233 checkActive();
236 234 EntityId entityId = msg.getOriginator();
237   - TopicPartitionInfo tpi = partitionService.resolve(ServiceType.TB_RULE_ENGINE, tenantId, entityId);
  235 + TopicPartitionInfo tpi = systemContext.resolve(ServiceType.TB_RULE_ENGINE, tenantId, entityId);
238 236 RuleNodeId originatorNodeId = envelope.getOriginator();
239 237 List<RuleNodeRelation> relations = nodeRoutes.get(originatorNodeId).stream()
240 238 .filter(r -> contains(envelope.getRelationTypes(), r.getType()))
... ... @@ -242,9 +240,9 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh
242 240 int relationsCount = relations.size();
243 241 if (relationsCount == 0) {
244 242 log.trace("[{}][{}][{}] No outbound relations to process", tenantId, entityId, msg.getId());
245   - //TODO 2.5: Maybe let's check that the output relation is not a Failure?
246 243 if (envelope.getRelationTypes().contains(TbRelationTypes.FAILURE)) {
247 244 log.debug("[{}] Failure during message processing by Rule Node [{}]. Enable and see debug events for more info", entityId, envelope.getOriginator().getId());
  245 + //TODO 2.5: Introduce our own RuleEngineFailureException to track what is wrong
248 246 msg.getCallback().onFailure(new RuntimeException("Failure during message processing by Rule Node [" + envelope.getOriginator().getId().toString() + "]"));
249 247 } else {
250 248 msg.getCallback().onSuccess();
... ... @@ -297,7 +295,7 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh
297 295 ToRuleEngineMsg toQueueMsg = ToRuleEngineMsg.newBuilder()
298 296 .setTenantIdMSB(tenantId.getId().getMostSignificantBits())
299 297 .setTenantIdLSB(tenantId.getId().getLeastSignificantBits())
300   - .setTbMsg(ByteString.copyFrom(TbMsg.toByteArray(newMsg)))
  298 + .setTbMsg(TbMsg.toByteString(newMsg))
301 299 .build();
302 300 producer.send(tpi, new TbProtoQueueMsg<>(newMsg.getId(), toQueueMsg), callbackWrapper);
303 301 }
... ...
... ... @@ -93,6 +93,12 @@ import org.thingsboard.server.dao.user.UserService;
93 93 import org.thingsboard.server.dao.widget.WidgetTypeService;
94 94 import org.thingsboard.server.dao.widget.WidgetsBundleService;
95 95 import org.thingsboard.server.exception.ThingsboardErrorResponseHandler;
  96 +import org.thingsboard.server.gen.transport.TransportProtos;
  97 +import org.thingsboard.server.queue.common.TbProtoQueueMsg;
  98 +import org.thingsboard.server.queue.discovery.PartitionService;
  99 +import org.thingsboard.server.queue.discovery.ServiceType;
  100 +import org.thingsboard.server.queue.discovery.TopicPartitionInfo;
  101 +import org.thingsboard.server.queue.provider.TbCoreQueueProvider;
96 102 import org.thingsboard.server.service.component.ComponentDiscoveryService;
97 103 import org.thingsboard.server.service.security.model.SecurityUser;
98 104 import org.thingsboard.server.service.security.permission.AccessControlService;
... ... @@ -185,6 +191,12 @@ public abstract class BaseController {
185 191 @Autowired
186 192 protected ClaimDevicesService claimDevicesService;
187 193
  194 + @Autowired
  195 + protected PartitionService partitionService;
  196 +
  197 + @Autowired
  198 + protected TbCoreQueueProvider coreQueueProvider;
  199 +
188 200 @Value("${server.log_controller_error_stack_trace}")
189 201 @Getter
190 202 private boolean logControllerErrorStackTrace;
... ... @@ -662,7 +674,12 @@ public abstract class BaseController {
662 674 TbMsg tbMsg = new TbMsg(UUIDs.timeBased(), msgType, entityId, metaData, TbMsgDataType.JSON
663 675 , json.writeValueAsString(entityNode)
664 676 , null, null, null);
665   - actorService.onMsg(new SendToClusterMsg(entityId, new QueueToRuleEngineMsg(user.getTenantId(), tbMsg)));
  677 + TopicPartitionInfo tpi = partitionService.resolve(ServiceType.TB_RULE_ENGINE, user.getTenantId(), entityId);
  678 + TransportProtos.ToRuleEngineMsg msg = TransportProtos.ToRuleEngineMsg.newBuilder()
  679 + .setTenantIdMSB(user.getTenantId().getId().getMostSignificantBits())
  680 + .setTenantIdLSB(user.getTenantId().getId().getLeastSignificantBits())
  681 + .setTbMsg(TbMsg.toByteString(tbMsg)).build();
  682 + coreQueueProvider.getRuleEngineMsgProducer().send(tpi, new TbProtoQueueMsg<>(tbMsg.getId(), msg), null);
666 683 } catch (Exception e) {
667 684 log.warn("[{}] Failed to push entity action to rule engine: {}", entityId, actionType, e);
668 685 }
... ...
... ... @@ -5,7 +5,7 @@
5 5 * you may not use this file except in compliance with the License.
6 6 * You may obtain a copy of the License at
7 7 *
8   - * http://www.apache.org/licenses/LICENSE-2.0
  8 + * http://www.apache.org/licenses/LICENSE-2.0
9 9 *
10 10 * Unless required by applicable law or agreed to in writing, software
11 11 * distributed under the License is distributed on an "AS IS" BASIS,
... ... @@ -45,6 +45,7 @@ import javax.annotation.PostConstruct;
45 45 import javax.annotation.PreDestroy;
46 46 import java.util.List;
47 47 import java.util.UUID;
  48 +import java.util.concurrent.ConcurrentHashMap;
48 49 import java.util.concurrent.ConcurrentMap;
49 50 import java.util.concurrent.CountDownLatch;
50 51 import java.util.concurrent.ExecutorService;
... ... @@ -104,11 +105,13 @@ public class DefaultTbCoreConsumerService implements TbCoreConsumerService {
104 105 if (msgs.isEmpty()) {
105 106 continue;
106 107 }
107   - ConcurrentMap<UUID, TbProtoQueueMsg<ToCoreMsg>> ackMap = msgs.stream().collect(
  108 + ConcurrentMap<UUID, TbProtoQueueMsg<ToCoreMsg>> pendingMap = msgs.stream().collect(
108 109 Collectors.toConcurrentMap(s -> UUID.randomUUID(), Function.identity()));
  110 + ConcurrentMap<UUID, TbProtoQueueMsg<ToCoreMsg>> successMap = new ConcurrentHashMap<>();
  111 + ConcurrentMap<UUID, TbProtoQueueMsg<ToCoreMsg>> failedMap = new ConcurrentHashMap<>();
109 112 CountDownLatch processingTimeoutLatch = new CountDownLatch(1);
110   - ackMap.forEach((id, msg) -> {
111   - TbMsgCallback callback = new MsgPackCallback<>(id, processingTimeoutLatch, ackMap);
  113 + pendingMap.forEach((id, msg) -> {
  114 + TbMsgCallback callback = new MsgPackCallback<>(id, processingTimeoutLatch, pendingMap, successMap, failedMap);
112 115 try {
113 116 ToCoreMsg toCoreMsg = msg.getValue();
114 117 if (toCoreMsg.hasToDeviceActorMsg()) {
... ... @@ -130,7 +133,8 @@ public class DefaultTbCoreConsumerService implements TbCoreConsumerService {
130 133 }
131 134 });
132 135 if (!processingTimeoutLatch.await(packProcessingTimeout, TimeUnit.MILLISECONDS)) {
133   - ackMap.forEach((id, msg) -> log.warn("[{}] Timeout to process message: {}", id, msg.getValue()));
  136 + pendingMap.forEach((id, msg) -> log.warn("[{}] Timeout to process message: {}", id, msg.getValue()));
  137 + failedMap.forEach((id, msg) -> log.warn("[{}] Failed to process message: {}", id, msg.getValue()));
134 138 }
135 139 consumer.commit();
136 140 } catch (Exception e) {
... ... @@ -182,7 +186,7 @@ public class DefaultTbCoreConsumerService implements TbCoreConsumerService {
182 186 subscriptionManagerService.cancelSubscription(closeProto.getSessionId(), closeProto.getSubscriptionId(), callback);
183 187 } else if (msg.hasTsUpdate()) {
184 188 TransportProtos.TbTimeSeriesUpdateProto proto = msg.getTsUpdate();
185   - subscriptionManagerService.onTimeseriesDataUpdate(
  189 + subscriptionManagerService.onTimeSeriesUpdate(
186 190 new TenantId(new UUID(proto.getTenantIdMSB(), proto.getTenantIdLSB())),
187 191 TbSubscriptionUtils.toEntityId(proto.getEntityType(), proto.getEntityIdMSB(), proto.getEntityIdLSB()),
188 192 TbSubscriptionUtils.toTsKvEntityList(proto.getDataList()), callback);
... ...
... ... @@ -5,7 +5,7 @@
5 5 * you may not use this file except in compliance with the License.
6 6 * You may obtain a copy of the License at
7 7 *
8   - * http://www.apache.org/licenses/LICENSE-2.0
  8 + * http://www.apache.org/licenses/LICENSE-2.0
9 9 *
10 10 * Unless required by applicable law or agreed to in writing, software
11 11 * distributed under the License is distributed on an "AS IS" BASIS,
... ... @@ -36,11 +36,16 @@ import org.thingsboard.server.queue.discovery.PartitionChangeEvent;
36 36 import org.thingsboard.server.queue.discovery.ServiceType;
37 37 import org.thingsboard.server.gen.transport.TransportProtos;
38 38 import org.thingsboard.server.queue.provider.TbRuleEngineQueueProvider;
  39 +import org.thingsboard.server.service.queue.processing.TbRuleEngineProcessingDecision;
  40 +import org.thingsboard.server.service.queue.processing.TbRuleEngineProcessingResult;
  41 +import org.thingsboard.server.service.queue.processing.TbRuleEngineProcessingStrategy;
  42 +import org.thingsboard.server.service.queue.processing.TbRuleEngineProcessingStrategyFactory;
39 43
40 44 import javax.annotation.PostConstruct;
41 45 import javax.annotation.PreDestroy;
42 46 import java.util.List;
43 47 import java.util.UUID;
  48 +import java.util.concurrent.ConcurrentHashMap;
44 49 import java.util.concurrent.ConcurrentMap;
45 50 import java.util.concurrent.CountDownLatch;
46 51 import java.util.concurrent.ExecutorService;
... ... @@ -64,10 +69,12 @@ public class DefaultTbRuleEngineConsumerService implements TbRuleEngineConsumerS
64 69 private final ActorSystemContext actorContext;
65 70 private final TbQueueConsumer<TbProtoQueueMsg<TransportProtos.ToRuleEngineMsg>> consumer;
66 71 private final TbCoreConsumerStats stats = new TbCoreConsumerStats();
  72 + private final TbRuleEngineProcessingStrategyFactory factory;
67 73 private volatile ExecutorService mainConsumerExecutor;
68 74 private volatile boolean stopped = false;
69 75
70   - public DefaultTbRuleEngineConsumerService(TbRuleEngineQueueProvider tbRuleEngineQueueProvider, ActorSystemContext actorContext) {
  76 + public DefaultTbRuleEngineConsumerService(TbRuleEngineProcessingStrategyFactory factory, TbRuleEngineQueueProvider tbRuleEngineQueueProvider, ActorSystemContext actorContext) {
  77 + this.factory = factory;
71 78 this.consumer = tbRuleEngineQueueProvider.getToRuleEngineMsgConsumer();
72 79 this.actorContext = actorContext;
73 80 }
... ... @@ -75,6 +82,7 @@ public class DefaultTbRuleEngineConsumerService implements TbRuleEngineConsumerS
75 82 @PostConstruct
76 83 public void init() {
77 84 this.mainConsumerExecutor = Executors.newSingleThreadExecutor(ThingsBoardThreadFactory.forName("tb-core-consumer"));
  85 + this.factory.newInstance();
78 86 }
79 87
80 88 @Override
... ... @@ -94,29 +102,46 @@ public class DefaultTbRuleEngineConsumerService implements TbRuleEngineConsumerS
94 102 if (msgs.isEmpty()) {
95 103 continue;
96 104 }
97   - ConcurrentMap<UUID, TbProtoQueueMsg<TransportProtos.ToRuleEngineMsg>> ackMap = msgs.stream().collect(
98   - Collectors.toConcurrentMap(s -> UUID.randomUUID(), Function.identity()));
99   - CountDownLatch processingTimeoutLatch = new CountDownLatch(1);
100   - ackMap.forEach((id, msg) -> {
101   - TbMsgCallback callback = new MsgPackCallback<>(id, processingTimeoutLatch, ackMap);
102   - try {
103   - TransportProtos.ToRuleEngineMsg toRuleEngineMsg = msg.getValue();
104   - TenantId tenantId = new TenantId(new UUID(toRuleEngineMsg.getTenantIdMSB(), toRuleEngineMsg.getTenantIdLSB()));
105   - if (toRuleEngineMsg.getTbMsg() != null && !toRuleEngineMsg.getTbMsg().isEmpty()) {
106   - forwardToRuleEngineActor(tenantId, toRuleEngineMsg.getTbMsg(), callback);
107   - } else {
108   - callback.onSuccess();
  105 + TbRuleEngineProcessingStrategy strategy = factory.newInstance();
  106 + TbRuleEngineProcessingDecision decision = null;
  107 + boolean firstAttempt = true;
  108 + while (!stopped && (firstAttempt || !decision.isCommit())) {
  109 + ConcurrentMap<UUID, TbProtoQueueMsg<TransportProtos.ToRuleEngineMsg>> allMap;
  110 + if (firstAttempt) {
  111 + allMap = msgs.stream().collect(
  112 + Collectors.toConcurrentMap(s -> UUID.randomUUID(), Function.identity()));
  113 + firstAttempt = false;
  114 + } else {
  115 + allMap = decision.getReprocessMap();
  116 + }
  117 + ConcurrentMap<UUID, TbProtoQueueMsg<TransportProtos.ToRuleEngineMsg>> successMap = new ConcurrentHashMap<>();
  118 + ConcurrentMap<UUID, TbProtoQueueMsg<TransportProtos.ToRuleEngineMsg>> failedMap = new ConcurrentHashMap<>();
  119 +
  120 + CountDownLatch processingTimeoutLatch = new CountDownLatch(1);
  121 + allMap.forEach((id, msg) -> {
  122 + TbMsgCallback callback = new MsgPackCallback<>(id, processingTimeoutLatch, allMap, successMap, failedMap);
  123 + try {
  124 + TransportProtos.ToRuleEngineMsg toRuleEngineMsg = msg.getValue();
  125 + TenantId tenantId = new TenantId(new UUID(toRuleEngineMsg.getTenantIdMSB(), toRuleEngineMsg.getTenantIdLSB()));
  126 + if (toRuleEngineMsg.getTbMsg() != null && !toRuleEngineMsg.getTbMsg().isEmpty()) {
  127 + forwardToRuleEngineActor(tenantId, toRuleEngineMsg.getTbMsg(), callback);
  128 + } else {
  129 + callback.onSuccess();
  130 + }
  131 + } catch (Throwable e) {
  132 + callback.onFailure(e);
109 133 }
110   - } catch (Throwable e) {
111   - callback.onFailure(e);
  134 + });
  135 +
  136 + boolean timeout = false;
  137 + if (!processingTimeoutLatch.await(packProcessingTimeout, TimeUnit.MILLISECONDS)) {
  138 + timeout = true;
112 139 }
113   - });
114   - if (!processingTimeoutLatch.await(packProcessingTimeout, TimeUnit.MILLISECONDS)) {
115   - ackMap.forEach((id, msg) -> log.warn("[{}] Timeout to process message: {}", id, msg.getValue()));
  140 + decision = strategy.analyze(new TbRuleEngineProcessingResult(timeout, allMap, successMap, failedMap));
116 141 }
117 142 consumer.commit();
118 143 } catch (Exception e) {
119   - log.warn("Failed to obtain messages from queue.", e);
  144 + log.warn("Failed to process messages from queue.", e);
120 145 try {
121 146 Thread.sleep(pollDuration);
122 147 } catch (InterruptedException e2) {
... ...
... ... @@ -5,7 +5,7 @@
5 5 * you may not use this file except in compliance with the License.
6 6 * You may obtain a copy of the License at
7 7 *
8   - * http://www.apache.org/licenses/LICENSE-2.0
  8 + * http://www.apache.org/licenses/LICENSE-2.0
9 9 *
10 10 * Unless required by applicable law or agreed to in writing, software
11 11 * distributed under the License is distributed on an "AS IS" BASIS,
... ... @@ -20,25 +20,37 @@ import org.thingsboard.server.common.msg.queue.TbMsgCallback;
20 20 import org.thingsboard.server.queue.common.TbProtoQueueMsg;
21 21
22 22 import java.util.UUID;
  23 +import java.util.concurrent.ConcurrentHashMap;
23 24 import java.util.concurrent.ConcurrentMap;
24 25 import java.util.concurrent.CountDownLatch;
25 26
26 27 @Slf4j
27   -public class MsgPackCallback<T extends com.google.protobuf.GeneratedMessageV3> implements TbMsgCallback {
  28 +public class MsgPackCallback<T> implements TbMsgCallback {
28 29 private final CountDownLatch processingTimeoutLatch;
29   - private final ConcurrentMap<UUID, TbProtoQueueMsg<T>> ackMap;
  30 + private final ConcurrentMap<UUID, T> ackMap;
  31 + private final ConcurrentMap<UUID, T> successMap;
  32 + private final ConcurrentMap<UUID, T> failedMap;
30 33 private final UUID id;
31 34
32   - public MsgPackCallback(UUID id, CountDownLatch processingTimeoutLatch, ConcurrentMap<UUID, TbProtoQueueMsg<T>> ackMap) {
  35 + public MsgPackCallback(UUID id, CountDownLatch processingTimeoutLatch,
  36 + ConcurrentMap<UUID, T> ackMap,
  37 + ConcurrentMap<UUID, T> successMap,
  38 + ConcurrentMap<UUID, T> failedMap) {
33 39 this.id = id;
34 40 this.processingTimeoutLatch = processingTimeoutLatch;
35 41 this.ackMap = ackMap;
  42 + this.successMap = successMap;
  43 + this.failedMap = failedMap;
36 44 }
37 45
38 46 @Override
39 47 public void onSuccess() {
40 48 log.trace("[{}] ON SUCCESS", id);
41   - if (ackMap.remove(id) != null && ackMap.isEmpty()) {
  49 + T msg = ackMap.remove(id);
  50 + if (msg != null) {
  51 + successMap.put(id, msg);
  52 + }
  53 + if (msg != null && ackMap.isEmpty()) {
42 54 processingTimeoutLatch.countDown();
43 55 }
44 56 }
... ... @@ -46,8 +58,10 @@ public class MsgPackCallback<T extends com.google.protobuf.GeneratedMessageV3> i
46 58 @Override
47 59 public void onFailure(Throwable t) {
48 60 log.trace("[{}] ON FAILURE", id);
49   - TbProtoQueueMsg<T> message = ackMap.remove(id);
50   - log.warn("Failed to process message: {}", message.getValue(), t);
  61 + T msg = ackMap.remove(id);
  62 + if (msg != null) {
  63 + failedMap.put(id, msg);
  64 + }
51 65 if (ackMap.isEmpty()) {
52 66 processingTimeoutLatch.countDown();
53 67 }
... ...
  1 +package org.thingsboard.server.service.queue.processing;
  2 +
  3 +import lombok.Data;
  4 +import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineMsg;
  5 +import org.thingsboard.server.queue.common.TbProtoQueueMsg;
  6 +
  7 +import java.util.UUID;
  8 +import java.util.concurrent.ConcurrentMap;
  9 +
  10 +@Data
  11 +public class TbRuleEngineProcessingDecision {
  12 +
  13 + private final boolean commit;
  14 + private final ConcurrentMap<UUID, TbProtoQueueMsg<ToRuleEngineMsg>> reprocessMap;
  15 +
  16 +}
... ...
  1 +package org.thingsboard.server.service.queue.processing;
  2 +
  3 +import lombok.Getter;
  4 +import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineMsg;
  5 +import org.thingsboard.server.queue.common.TbProtoQueueMsg;
  6 +
  7 +import java.util.UUID;
  8 +import java.util.concurrent.ConcurrentMap;
  9 +
  10 +public class TbRuleEngineProcessingResult {
  11 +
  12 + @Getter
  13 + private boolean success;
  14 + @Getter
  15 + private boolean timeout;
  16 + @Getter
  17 + private ConcurrentMap<UUID, TbProtoQueueMsg<ToRuleEngineMsg>> pendingMap;
  18 + @Getter
  19 + private ConcurrentMap<UUID, TbProtoQueueMsg<ToRuleEngineMsg>> successMap;
  20 + @Getter
  21 + private ConcurrentMap<UUID, TbProtoQueueMsg<ToRuleEngineMsg>> failureMap;
  22 +
  23 + public TbRuleEngineProcessingResult(boolean timeout,
  24 + ConcurrentMap<UUID, TbProtoQueueMsg<ToRuleEngineMsg>> pendingMap,
  25 + ConcurrentMap<UUID, TbProtoQueueMsg<ToRuleEngineMsg>> successMap,
  26 + ConcurrentMap<UUID, TbProtoQueueMsg<ToRuleEngineMsg>> failureMap) {
  27 + this.timeout = timeout;
  28 + this.pendingMap = pendingMap;
  29 + this.successMap = successMap;
  30 + this.failureMap = failureMap;
  31 + this.success = !timeout && pendingMap.isEmpty() && failureMap.isEmpty();
  32 + }
  33 +}
... ...
  1 +package org.thingsboard.server.service.queue.processing;
  2 +
  3 +public interface TbRuleEngineProcessingStrategy {
  4 +
  5 + TbRuleEngineProcessingDecision analyze(TbRuleEngineProcessingResult result);
  6 +
  7 +}
... ...
  1 +package org.thingsboard.server.service.queue.processing;
  2 +
  3 +import lombok.extern.slf4j.Slf4j;
  4 +import org.springframework.beans.factory.annotation.Value;
  5 +import org.springframework.stereotype.Component;
  6 +import org.thingsboard.server.gen.transport.TransportProtos;
  7 +import org.thingsboard.server.queue.common.TbProtoQueueMsg;
  8 +
  9 +import java.util.UUID;
  10 +import java.util.concurrent.ConcurrentHashMap;
  11 +import java.util.concurrent.ConcurrentMap;
  12 +import java.util.concurrent.TimeUnit;
  13 +
  14 +@Component
  15 +@Slf4j
  16 +public class TbRuleEngineProcessingStrategyFactory {
  17 +
  18 + @Value("${queue.rule_engine.strategy.type}")
  19 + private String strategyType;
  20 + @Value("${queue.rule_engine.strategy.retries:3}")
  21 + private int maxRetries;
  22 + @Value("${queue.rule_engine.strategy.failure_percentage:0}")
  23 + private double maxAllowedFailurePercentage;
  24 + @Value("${queue.rule_engine.strategy.pause_between_retries:3}")
  25 + private long pauseBetweenRetries;
  26 +
  27 +
  28 + public TbRuleEngineProcessingStrategy newInstance() {
  29 + switch (strategyType) {
  30 + case "SKIP_ALL":
  31 + return new SkipStrategy();
  32 + case "RETRY_ALL":
  33 + return new RetryStrategy(true, true, true, maxRetries, maxAllowedFailurePercentage, pauseBetweenRetries);
  34 + case "RETRY_FAILED":
  35 + return new RetryStrategy(false, true, false, maxRetries, maxAllowedFailurePercentage, pauseBetweenRetries);
  36 + case "RETRY_TIMED_OUT":
  37 + return new RetryStrategy(false, false, true, maxRetries, maxAllowedFailurePercentage, pauseBetweenRetries);
  38 + case "RETRY_FAILED_AND_TIMED_OUT":
  39 + return new RetryStrategy(false, true, true, maxRetries, maxAllowedFailurePercentage, pauseBetweenRetries);
  40 + default:
  41 + throw new RuntimeException("TbRuleEngineProcessingStrategy with type " + strategyType + " is not supported!");
  42 + }
  43 + }
  44 +
  45 + private static class RetryStrategy implements TbRuleEngineProcessingStrategy {
  46 + private final boolean retrySuccessful;
  47 + private final boolean retryFailed;
  48 + private final boolean retryTimeout;
  49 + private final int maxRetries;
  50 + private final double maxAllowedFailurePercentage;
  51 + private final long pauseBetweenRetries;
  52 +
  53 + private int initialTotalCount;
  54 + private int retryCount;
  55 +
  56 + public RetryStrategy(boolean retrySuccessful, boolean retryFailed, boolean retryTimeout, int maxRetries, double maxAllowedFailurePercentage, long pauseBetweenRetries) {
  57 + this.retrySuccessful = retrySuccessful;
  58 + this.retryFailed = retryFailed;
  59 + this.retryTimeout = retryTimeout;
  60 + this.maxRetries = maxRetries;
  61 + this.maxAllowedFailurePercentage = maxAllowedFailurePercentage;
  62 + this.pauseBetweenRetries = pauseBetweenRetries;
  63 + }
  64 +
  65 + @Override
  66 + public TbRuleEngineProcessingDecision analyze(TbRuleEngineProcessingResult result) {
  67 + if (result.isSuccess()) {
  68 + return new TbRuleEngineProcessingDecision(true, null);
  69 + } else {
  70 + if (retryCount == 0) {
  71 + initialTotalCount = result.getPendingMap().size() + result.getFailureMap().size() + result.getSuccessMap().size();
  72 + }
  73 + retryCount++;
  74 + double failedCount = result.getFailureMap().size() + result.getPendingMap().size();
  75 + if (maxRetries > 0 && retryCount > maxRetries) {
  76 + log.info("Skip reprocess of the rule engine pack due to max retries");
  77 + return new TbRuleEngineProcessingDecision(true, null);
  78 + } else if (maxAllowedFailurePercentage > 0 && (failedCount / initialTotalCount) > maxAllowedFailurePercentage) {
  79 + log.info("Skip reprocess of the rule engine pack due to max allowed failure percentage");
  80 + return new TbRuleEngineProcessingDecision(true, null);
  81 + } else {
  82 + ConcurrentMap<UUID, TbProtoQueueMsg<TransportProtos.ToRuleEngineMsg>> toReprocess = new ConcurrentHashMap<>(initialTotalCount);
  83 + if (retryFailed) {
  84 + result.getFailureMap().forEach(toReprocess::put);
  85 + }
  86 + if (retryTimeout) {
  87 + result.getPendingMap().forEach(toReprocess::put);
  88 + }
  89 + if (retrySuccessful) {
  90 + result.getSuccessMap().forEach(toReprocess::put);
  91 + }
  92 + log.info("Going to reprocess {} messages", toReprocess.size());
  93 + //TODO: 2.5 Log most popular rule nodes by error count;
  94 + if (log.isTraceEnabled()) {
  95 + toReprocess.forEach((id, msg) -> log.trace("Going to reprocess [{}]: {}", id, msg.getValue()));
  96 + }
  97 + if (pauseBetweenRetries > 0) {
  98 + try {
  99 + Thread.sleep(TimeUnit.SECONDS.toMillis(pauseBetweenRetries));
  100 + } catch (InterruptedException e) {
  101 + throw new RuntimeException(e);
  102 + }
  103 + }
  104 + return new TbRuleEngineProcessingDecision(false, toReprocess);
  105 + }
  106 + }
  107 + }
  108 + }
  109 +
  110 + private static class SkipStrategy implements TbRuleEngineProcessingStrategy {
  111 +
  112 + @Override
  113 + public TbRuleEngineProcessingDecision analyze(TbRuleEngineProcessingResult result) {
  114 + log.info("Skip reprocess of the rule engine pack");
  115 + return new TbRuleEngineProcessingDecision(true, null);
  116 + }
  117 + }
  118 +}
... ...
... ... @@ -5,7 +5,7 @@
5 5 * you may not use this file except in compliance with the License.
6 6 * You may obtain a copy of the License at
7 7 *
8   - * http://www.apache.org/licenses/LICENSE-2.0
  8 + * http://www.apache.org/licenses/LICENSE-2.0
9 9 *
10 10 * Unless required by applicable law or agreed to in writing, software
11 11 * distributed under the License is distributed on an "AS IS" BASIS,
... ... @@ -32,6 +32,7 @@ import org.springframework.stereotype.Service;
32 32 import org.springframework.util.StringUtils;
33 33 import org.thingsboard.common.util.ThingsBoardThreadFactory;
34 34 import org.thingsboard.server.actors.service.ActorService;
  35 +import org.thingsboard.server.queue.TbQueueCallback;
35 36 import org.thingsboard.server.queue.common.TbProtoQueueMsg;
36 37 import org.thingsboard.server.common.data.DataConstants;
37 38 import org.thingsboard.server.common.data.Device;
... ... @@ -502,7 +503,12 @@ public class DefaultDeviceStateService implements DeviceStateService {
502 503 TbMsg tbMsg = new TbMsg(UUIDs.timeBased(), msgType, stateData.getDeviceId(), stateData.getMetaData().copy(), TbMsgDataType.JSON
503 504 , json.writeValueAsString(state)
504 505 , null, null, null);
505   - actorService.onMsg(new SendToClusterMsg(stateData.getDeviceId(), new QueueToRuleEngineMsg(stateData.getTenantId(), tbMsg)));
  506 + TopicPartitionInfo tpi = partitionService.resolve(ServiceType.TB_RULE_ENGINE, stateData.getTenantId(), stateData.getDeviceId());
  507 + TransportProtos.ToRuleEngineMsg msg = TransportProtos.ToRuleEngineMsg.newBuilder()
  508 + .setTenantIdMSB(stateData.getTenantId().getId().getMostSignificantBits())
  509 + .setTenantIdLSB(stateData.getTenantId().getId().getLeastSignificantBits())
  510 + .setTbMsg(TbMsg.toByteString(tbMsg)).build();
  511 + queueProvider.getRuleEngineMsgProducer().send(tpi, new TbProtoQueueMsg<>(tbMsg.getId(), msg), null);
506 512 } catch (Exception e) {
507 513 log.warn("[{}] Failed to push inactivity alarm: {}", stateData.getDeviceId(), state, e);
508 514 }
... ...
... ... @@ -187,7 +187,7 @@ public class DefaultSubscriptionManagerService implements SubscriptionManagerSer
187 187 }
188 188
189 189 @Override
190   - public void onTimeseriesDataUpdate(TenantId tenantId, EntityId entityId, List<TsKvEntry> ts, TbMsgCallback callback) {
  190 + public void onTimeSeriesUpdate(TenantId tenantId, EntityId entityId, List<TsKvEntry> ts, TbMsgCallback callback) {
191 191 onLocalSubUpdate(entityId,
192 192 s -> {
193 193 if (TbSubscriptionType.TIMESERIES.equals(s.getType())) {
... ...
... ... @@ -31,7 +31,8 @@ public interface SubscriptionManagerService extends ApplicationListener<Partitio
31 31
32 32 void cancelSubscription(String sessionId, int subscriptionId, TbMsgCallback callback);
33 33
34   - void onTimeseriesDataUpdate(TenantId tenantId, EntityId entityId, List<TsKvEntry> ts, TbMsgCallback callback);
  34 + void onTimeSeriesUpdate(TenantId tenantId, EntityId entityId, List<TsKvEntry> ts, TbMsgCallback callback);
35 35
36 36 void onAttributesUpdate(TenantId tenantId, EntityId entityId, String scope, List<AttributeKvEntry> attributes, TbMsgCallback callback);
  37 +
37 38 }
... ...
... ... @@ -168,7 +168,7 @@ public class DefaultTelemetrySubscriptionService implements TelemetrySubscriptio
168 168 private void onTimeSeriesUpdate(TenantId tenantId, EntityId entityId, List<TsKvEntry> ts) {
169 169 TopicPartitionInfo tpi = partitionService.resolve(ServiceType.TB_CORE, tenantId, entityId);
170 170 if (currentPartitions.contains(tpi)) {
171   - subscriptionManagerService.onTimeseriesDataUpdate(tenantId, entityId, ts, TbMsgCallback.EMPTY);
  171 + subscriptionManagerService.onTimeSeriesUpdate(tenantId, entityId, ts, TbMsgCallback.EMPTY);
172 172 } else {
173 173 TransportProtos.ToCoreMsg toCoreMsg = TbSubscriptionUtils.toTimeseriesUpdateProto(tenantId, entityId, ts);
174 174 toCoreProducer.send(tpi, new TbProtoQueueMsg<>(entityId.getId(), toCoreMsg), null);
... ...
... ... @@ -181,27 +181,27 @@ cassandra:
181 181
182 182 # SQL configuration parameters
183 183 sql:
184   - # Specify batch size for persisting attribute updates
185   - attributes:
186   - batch_size: "${SQL_ATTRIBUTES_BATCH_SIZE:10000}"
187   - batch_max_delay: "${SQL_ATTRIBUTES_BATCH_MAX_DELAY_MS:100}"
188   - stats_print_interval_ms: "${SQL_ATTRIBUTES_BATCH_STATS_PRINT_MS:10000}"
189   - ts:
190   - batch_size: "${SQL_TS_BATCH_SIZE:10000}"
191   - batch_max_delay: "${SQL_TS_BATCH_MAX_DELAY_MS:100}"
192   - stats_print_interval_ms: "${SQL_TS_BATCH_STATS_PRINT_MS:10000}"
193   - ts_latest:
194   - batch_size: "${SQL_TS_LATEST_BATCH_SIZE:10000}"
195   - batch_max_delay: "${SQL_TS_LATEST_BATCH_MAX_DELAY_MS:100}"
196   - stats_print_interval_ms: "${SQL_TS_LATEST_BATCH_STATS_PRINT_MS:10000}"
197   - # Specify whether to remove null characters from strValue of attributes and timeseries before insert
198   - remove_null_chars: "${SQL_REMOVE_NULL_CHARS:true}"
199   - postgres:
200   - # Specify partitioning size for timestamp key-value storage. Example: DAYS, MONTHS, YEARS, INDEFINITE.
201   - ts_key_value_partitioning: "${SQL_POSTGRES_TS_KV_PARTITIONING:MONTHS}"
202   - timescale:
203   - # Specify Interval size for new data chunks storage.
204   - chunk_time_interval: "${SQL_TIMESCALE_CHUNK_TIME_INTERVAL:604800000}"
  184 + # Specify batch size for persisting attribute updates
  185 + attributes:
  186 + batch_size: "${SQL_ATTRIBUTES_BATCH_SIZE:10000}"
  187 + batch_max_delay: "${SQL_ATTRIBUTES_BATCH_MAX_DELAY_MS:100}"
  188 + stats_print_interval_ms: "${SQL_ATTRIBUTES_BATCH_STATS_PRINT_MS:10000}"
  189 + ts:
  190 + batch_size: "${SQL_TS_BATCH_SIZE:10000}"
  191 + batch_max_delay: "${SQL_TS_BATCH_MAX_DELAY_MS:100}"
  192 + stats_print_interval_ms: "${SQL_TS_BATCH_STATS_PRINT_MS:10000}"
  193 + ts_latest:
  194 + batch_size: "${SQL_TS_LATEST_BATCH_SIZE:10000}"
  195 + batch_max_delay: "${SQL_TS_LATEST_BATCH_MAX_DELAY_MS:100}"
  196 + stats_print_interval_ms: "${SQL_TS_LATEST_BATCH_STATS_PRINT_MS:10000}"
  197 + # Specify whether to remove null characters from strValue of attributes and timeseries before insert
  198 + remove_null_chars: "${SQL_REMOVE_NULL_CHARS:true}"
  199 + postgres:
  200 + # Specify partitioning size for timestamp key-value storage. Example: DAYS, MONTHS, YEARS, INDEFINITE.
  201 + ts_key_value_partitioning: "${SQL_POSTGRES_TS_KV_PARTITIONING:MONTHS}"
  202 + timescale:
  203 + # Specify Interval size for new data chunks storage.
  204 + chunk_time_interval: "${SQL_TIMESCALE_CHUNK_TIME_INTERVAL:604800000}"
205 205
206 206 # Actor system parameters
207 207 actors:
... ... @@ -330,19 +330,19 @@ updates:
330 330
331 331 # spring CORS configuration
332 332 spring.mvc.cors:
333   - mappings:
334   - # Intercept path
335   - "[/api/**]":
336   - #Comma-separated list of origins to allow. '*' allows all origins. When not set,CORS support is disabled.
337   - allowed-origins: "*"
338   - #Comma-separated list of methods to allow. '*' allows all methods.
339   - allowed-methods: "*"
340   - #Comma-separated list of headers to allow in a request. '*' allows all headers.
341   - allowed-headers: "*"
342   - #How long, in seconds, the response from a pre-flight request can be cached by clients.
343   - max-age: "1800"
344   - #Set whether credentials are supported. When not set, credentials are not supported.
345   - allow-credentials: "true"
  333 + mappings:
  334 + # Intercept path
  335 + "[/api/**]":
  336 + #Comma-separated list of origins to allow. '*' allows all origins. When not set,CORS support is disabled.
  337 + allowed-origins: "*"
  338 + #Comma-separated list of methods to allow. '*' allows all methods.
  339 + allowed-methods: "*"
  340 + #Comma-separated list of headers to allow in a request. '*' allows all headers.
  341 + allowed-headers: "*"
  342 + #How long, in seconds, the response from a pre-flight request can be cached by clients.
  343 + max-age: "1800"
  344 + #Set whether credentials are supported. When not set, credentials are not supported.
  345 + allow-credentials: "true"
346 346
347 347 # spring serve gzip compressed static resources
348 348 spring.resources.chain:
... ... @@ -551,6 +551,12 @@ queue:
551 551 poll_interval: "${TB_QUEUE_RULE_ENGINE_POLL_INTERVAL_MS:25}"
552 552 partitions: "${TB_QUEUE_RULE_ENGINE_PARTITIONS:10}"
553 553 pack_processing_timeout: "${TB_QUEUE_RULE_ENGINE_PACK_PROCESSING_TIMEOUT_MS:60000}"
  554 + strategy:
  555 + type: "${TB_QUEUE_RULE_ENGINE_STRATEGY_TYPE:RETRY_FAILED_AND_TIMED_OUT}" # SKIP_ALL_FAILURES, RETRY_ALL, RETRY_FAILED, RETRY_TIMED_OUT, RETRY_FAILED_AND_TIMED_OUT
  556 + # For RETRY_ALL, RETRY_FAILED, RETRY_TIMED_OUT, RETRY_FAILED_AND_TIMED_OUT
  557 + retries: "${TB_QUEUE_RULE_ENGINE_STRATEGY_RETRIES:3}" # Number of retries, 0 is unlimited
  558 + failure_percentage: "${TB_QUEUE_RULE_ENGINE_STRATEGY_FAILURE_PERCENTAGE:0}" # Skip retry if failures or timeouts are less then X percentage of messages;
  559 + pause_between_retries: "${TB_QUEUE_RULE_ENGINE_STRATEGY_RETRY_PAUSE:3}"# Time in seconds to wait in consumer thread before retries;
554 560 stats:
555 561 enabled: "${TB_QUEUE_RULE_ENGINE_STATS_ENABLED:false}"
556 562 print_interval_ms: "${TB_QUEUE_RULE_ENGINE_STATS_PRINT_INTERVAL_MS:10000}"
... ...
... ... @@ -5,7 +5,7 @@
5 5 * you may not use this file except in compliance with the License.
6 6 * You may obtain a copy of the License at
7 7 *
8   - * http://www.apache.org/licenses/LICENSE-2.0
  8 + * http://www.apache.org/licenses/LICENSE-2.0
9 9 *
10 10 * Unless required by applicable law or agreed to in writing, software
11 11 * distributed under the License is distributed on an "AS IS" BASIS,
... ... @@ -15,6 +15,7 @@
15 15 */
16 16 package org.thingsboard.server.common.msg;
17 17
  18 +import com.google.protobuf.ByteString;
18 19 import com.google.protobuf.InvalidProtocolBufferException;
19 20 import lombok.AllArgsConstructor;
20 21 import lombok.Builder;
... ... @@ -73,7 +74,10 @@ public final class TbMsg implements Serializable {
73 74 log.warn("[{}] Created message with empty callback: {}", originator, type);
74 75 this.callback = TbMsgCallback.EMPTY;
75 76 }
  77 + }
76 78
  79 + public static ByteString toByteString(TbMsg msg) {
  80 + return ByteString.copyFrom(toByteArray(msg));
77 81 }
78 82
79 83 public static byte[] toByteArray(TbMsg msg) {
... ...
... ... @@ -5,7 +5,7 @@
5 5 * you may not use this file except in compliance with the License.
6 6 * You may obtain a copy of the License at
7 7 *
8   - * http://www.apache.org/licenses/LICENSE-2.0
  8 + * http://www.apache.org/licenses/LICENSE-2.0
9 9 *
10 10 * Unless required by applicable law or agreed to in writing, software
11 11 * distributed under the License is distributed on an "AS IS" BASIS,
... ... @@ -244,7 +244,7 @@ public class DefaultTransportService implements TransportService {
244 244 metaData.putValue("ts", tsKv.getTs() + "");
245 245 JsonObject json = JsonUtils.getJsonObject(tsKv.getKvList());
246 246 TbMsg tbMsg = new TbMsg(UUID.randomUUID(), SessionMsgType.POST_TELEMETRY_REQUEST.name(),
247   - deviceId, metaData, TbMsgDataType.JSON, gson.toJson(json), null, null, null);
  247 + deviceId, metaData, TbMsgDataType.JSON, gson.toJson(json), null, null, TbMsgCallback.EMPTY);
248 248 sendToRuleEngine(tenantId, tbMsg, packCallback);
249 249 }
250 250 }
... ... @@ -261,7 +261,7 @@ public class DefaultTransportService implements TransportService {
261 261 metaData.putValue("deviceName", sessionInfo.getDeviceName());
262 262 metaData.putValue("deviceType", sessionInfo.getDeviceType());
263 263 TbMsg tbMsg = new TbMsg(UUID.randomUUID(), SessionMsgType.POST_ATTRIBUTES_REQUEST.name(), deviceId, metaData,
264   - TbMsgDataType.JSON, gson.toJson(json), null, null, null);
  264 + TbMsgDataType.JSON, gson.toJson(json), null, null, TbMsgCallback.EMPTY);
265 265 sendToRuleEngine(tenantId, tbMsg, new TransportTbQueueCallback(callback));
266 266 }
267 267 }
... ... @@ -488,7 +488,7 @@ public class DefaultTransportService implements TransportService {
488 488
489 489 protected void sendToRuleEngine(TenantId tenantId, TbMsg tbMsg, TbQueueCallback callback) {
490 490 TopicPartitionInfo tpi = partitionService.resolve(ServiceType.TB_RULE_ENGINE, tenantId, tbMsg.getOriginator());
491   - ToRuleEngineMsg msg = ToRuleEngineMsg.newBuilder().setTbMsg(ByteString.copyFrom(TbMsg.toByteArray(tbMsg)))
  491 + ToRuleEngineMsg msg = ToRuleEngineMsg.newBuilder().setTbMsg(TbMsg.toByteString(tbMsg))
492 492 .setTenantIdMSB(tenantId.getId().getMostSignificantBits())
493 493 .setTenantIdLSB(tenantId.getId().getLeastSignificantBits()).build();
494 494 ruleEngineMsgProducer.send(tpi, new TbProtoQueueMsg<>(tbMsg.getId(), msg), callback);
... ...