Showing
18 changed files
with
362 additions
and
90 deletions
@@ -5,7 +5,7 @@ | @@ -5,7 +5,7 @@ | ||
5 | * you may not use this file except in compliance with the License. | 5 | * you may not use this file except in compliance with the License. |
6 | * You may obtain a copy of the License at | 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 | * Unless required by applicable law or agreed to in writing, software | 10 | * Unless required by applicable law or agreed to in writing, software |
11 | * distributed under the License is distributed on an "AS IS" BASIS, | 11 | * distributed under the License is distributed on an "AS IS" BASIS, |
@@ -63,8 +63,13 @@ import org.thingsboard.server.dao.rule.RuleChainService; | @@ -63,8 +63,13 @@ import org.thingsboard.server.dao.rule.RuleChainService; | ||
63 | import org.thingsboard.server.dao.tenant.TenantService; | 63 | import org.thingsboard.server.dao.tenant.TenantService; |
64 | import org.thingsboard.server.dao.timeseries.TimeseriesService; | 64 | import org.thingsboard.server.dao.timeseries.TimeseriesService; |
65 | import org.thingsboard.server.dao.user.UserService; | 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 | import org.thingsboard.server.queue.discovery.PartitionService; | 69 | import org.thingsboard.server.queue.discovery.PartitionService; |
70 | +import org.thingsboard.server.queue.discovery.ServiceType; | ||
67 | import org.thingsboard.server.queue.discovery.TbServiceInfoProvider; | 71 | import org.thingsboard.server.queue.discovery.TbServiceInfoProvider; |
72 | +import org.thingsboard.server.queue.discovery.TopicPartitionInfo; | ||
68 | import org.thingsboard.server.queue.provider.TbRuleEngineQueueProvider; | 73 | import org.thingsboard.server.queue.provider.TbRuleEngineQueueProvider; |
69 | import org.thingsboard.server.service.component.ComponentDiscoveryService; | 74 | import org.thingsboard.server.service.component.ComponentDiscoveryService; |
70 | import org.thingsboard.server.service.encoding.DataDecodingEncodingService; | 75 | import org.thingsboard.server.service.encoding.DataDecodingEncodingService; |
@@ -154,7 +159,6 @@ public class ActorSystemContext { | @@ -154,7 +159,6 @@ public class ActorSystemContext { | ||
154 | private RuleChainService ruleChainService; | 159 | private RuleChainService ruleChainService; |
155 | 160 | ||
156 | @Autowired | 161 | @Autowired |
157 | - @Getter | ||
158 | private PartitionService partitionService; | 162 | private PartitionService partitionService; |
159 | 163 | ||
160 | @Autowired | 164 | @Autowired |
@@ -402,6 +406,17 @@ public class ActorSystemContext { | @@ -402,6 +406,17 @@ public class ActorSystemContext { | ||
402 | return mapper.createObjectNode().put("server", serviceId).put("method", method).put("error", body); | 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 | public String getServerAddress() { | 421 | public String getServerAddress() { |
407 | return serviceInfoProvider.getServiceId(); | 422 | return serviceInfoProvider.getServiceId(); |
@@ -5,7 +5,7 @@ | @@ -5,7 +5,7 @@ | ||
5 | * you may not use this file except in compliance with the License. | 5 | * you may not use this file except in compliance with the License. |
6 | * You may obtain a copy of the License at | 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 | * Unless required by applicable law or agreed to in writing, software | 10 | * Unless required by applicable law or agreed to in writing, software |
11 | * distributed under the License is distributed on an "AS IS" BASIS, | 11 | * distributed under the License is distributed on an "AS IS" BASIS, |
@@ -65,7 +65,10 @@ import org.thingsboard.server.dao.rule.RuleChainService; | @@ -65,7 +65,10 @@ import org.thingsboard.server.dao.rule.RuleChainService; | ||
65 | import org.thingsboard.server.dao.tenant.TenantService; | 65 | import org.thingsboard.server.dao.tenant.TenantService; |
66 | import org.thingsboard.server.dao.timeseries.TimeseriesService; | 66 | import org.thingsboard.server.dao.timeseries.TimeseriesService; |
67 | import org.thingsboard.server.dao.user.UserService; | 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 | import org.thingsboard.server.queue.discovery.ServiceType; | 70 | import org.thingsboard.server.queue.discovery.ServiceType; |
71 | +import org.thingsboard.server.queue.discovery.TopicPartitionInfo; | ||
69 | import org.thingsboard.server.service.script.RuleNodeJsScriptEngine; | 72 | import org.thingsboard.server.service.script.RuleNodeJsScriptEngine; |
70 | import scala.concurrent.duration.Duration; | 73 | import scala.concurrent.duration.Duration; |
71 | 74 | ||
@@ -119,7 +122,7 @@ class DefaultTbContext implements TbContext { | @@ -119,7 +122,7 @@ class DefaultTbContext implements TbContext { | ||
119 | 122 | ||
120 | @Override | 123 | @Override |
121 | public boolean isLocalEntity(EntityId entityId) { | 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 | private void scheduleMsgWithDelay(Object msg, long delayInMs, ActorRef target) { | 128 | private void scheduleMsgWithDelay(Object msg, long delayInMs, ActorRef target) { |
@@ -151,8 +154,13 @@ class DefaultTbContext implements TbContext { | @@ -151,8 +154,13 @@ class DefaultTbContext implements TbContext { | ||
151 | } | 154 | } |
152 | 155 | ||
153 | @Override | 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 | public TbMsg customerCreatedMsg(Customer customer, RuleNodeId ruleNodeId) { | 166 | public TbMsg customerCreatedMsg(Customer customer, RuleNodeId ruleNodeId) { |
@@ -5,7 +5,7 @@ | @@ -5,7 +5,7 @@ | ||
5 | * you may not use this file except in compliance with the License. | 5 | * you may not use this file except in compliance with the License. |
6 | * You may obtain a copy of the License at | 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 | * Unless required by applicable law or agreed to in writing, software | 10 | * Unless required by applicable law or agreed to in writing, software |
11 | * distributed under the License is distributed on an "AS IS" BASIS, | 11 | * distributed under the License is distributed on an "AS IS" BASIS, |
@@ -68,7 +68,6 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh | @@ -68,7 +68,6 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh | ||
68 | private final Map<RuleNodeId, RuleNodeCtx> nodeActors; | 68 | private final Map<RuleNodeId, RuleNodeCtx> nodeActors; |
69 | private final Map<RuleNodeId, List<RuleNodeRelation>> nodeRoutes; | 69 | private final Map<RuleNodeId, List<RuleNodeRelation>> nodeRoutes; |
70 | private final RuleChainService service; | 70 | private final RuleChainService service; |
71 | - private final PartitionService partitionService; | ||
72 | private final TbQueueProducer<TbProtoQueueMsg<ToRuleEngineMsg>> producer; | 71 | private final TbQueueProducer<TbProtoQueueMsg<ToRuleEngineMsg>> producer; |
73 | 72 | ||
74 | private RuleNodeId firstId; | 73 | private RuleNodeId firstId; |
@@ -83,7 +82,6 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh | @@ -83,7 +82,6 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh | ||
83 | this.nodeActors = new HashMap<>(); | 82 | this.nodeActors = new HashMap<>(); |
84 | this.nodeRoutes = new HashMap<>(); | 83 | this.nodeRoutes = new HashMap<>(); |
85 | this.service = systemContext.getRuleChainService(); | 84 | this.service = systemContext.getRuleChainService(); |
86 | - this.partitionService = systemContext.getPartitionService(); | ||
87 | this.producer = systemContext.getRuleEngineQueueProvider().getRuleEngineMsgProducer(); | 85 | this.producer = systemContext.getRuleEngineQueueProvider().getRuleEngineMsgProducer(); |
88 | } | 86 | } |
89 | 87 | ||
@@ -234,7 +232,7 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh | @@ -234,7 +232,7 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh | ||
234 | try { | 232 | try { |
235 | checkActive(); | 233 | checkActive(); |
236 | EntityId entityId = msg.getOriginator(); | 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 | RuleNodeId originatorNodeId = envelope.getOriginator(); | 236 | RuleNodeId originatorNodeId = envelope.getOriginator(); |
239 | List<RuleNodeRelation> relations = nodeRoutes.get(originatorNodeId).stream() | 237 | List<RuleNodeRelation> relations = nodeRoutes.get(originatorNodeId).stream() |
240 | .filter(r -> contains(envelope.getRelationTypes(), r.getType())) | 238 | .filter(r -> contains(envelope.getRelationTypes(), r.getType())) |
@@ -242,9 +240,9 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh | @@ -242,9 +240,9 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh | ||
242 | int relationsCount = relations.size(); | 240 | int relationsCount = relations.size(); |
243 | if (relationsCount == 0) { | 241 | if (relationsCount == 0) { |
244 | log.trace("[{}][{}][{}] No outbound relations to process", tenantId, entityId, msg.getId()); | 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 | if (envelope.getRelationTypes().contains(TbRelationTypes.FAILURE)) { | 243 | if (envelope.getRelationTypes().contains(TbRelationTypes.FAILURE)) { |
247 | log.debug("[{}] Failure during message processing by Rule Node [{}]. Enable and see debug events for more info", entityId, envelope.getOriginator().getId()); | 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 | msg.getCallback().onFailure(new RuntimeException("Failure during message processing by Rule Node [" + envelope.getOriginator().getId().toString() + "]")); | 246 | msg.getCallback().onFailure(new RuntimeException("Failure during message processing by Rule Node [" + envelope.getOriginator().getId().toString() + "]")); |
249 | } else { | 247 | } else { |
250 | msg.getCallback().onSuccess(); | 248 | msg.getCallback().onSuccess(); |
@@ -297,7 +295,7 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh | @@ -297,7 +295,7 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh | ||
297 | ToRuleEngineMsg toQueueMsg = ToRuleEngineMsg.newBuilder() | 295 | ToRuleEngineMsg toQueueMsg = ToRuleEngineMsg.newBuilder() |
298 | .setTenantIdMSB(tenantId.getId().getMostSignificantBits()) | 296 | .setTenantIdMSB(tenantId.getId().getMostSignificantBits()) |
299 | .setTenantIdLSB(tenantId.getId().getLeastSignificantBits()) | 297 | .setTenantIdLSB(tenantId.getId().getLeastSignificantBits()) |
300 | - .setTbMsg(ByteString.copyFrom(TbMsg.toByteArray(newMsg))) | 298 | + .setTbMsg(TbMsg.toByteString(newMsg)) |
301 | .build(); | 299 | .build(); |
302 | producer.send(tpi, new TbProtoQueueMsg<>(newMsg.getId(), toQueueMsg), callbackWrapper); | 300 | producer.send(tpi, new TbProtoQueueMsg<>(newMsg.getId(), toQueueMsg), callbackWrapper); |
303 | } | 301 | } |
@@ -93,6 +93,12 @@ import org.thingsboard.server.dao.user.UserService; | @@ -93,6 +93,12 @@ import org.thingsboard.server.dao.user.UserService; | ||
93 | import org.thingsboard.server.dao.widget.WidgetTypeService; | 93 | import org.thingsboard.server.dao.widget.WidgetTypeService; |
94 | import org.thingsboard.server.dao.widget.WidgetsBundleService; | 94 | import org.thingsboard.server.dao.widget.WidgetsBundleService; |
95 | import org.thingsboard.server.exception.ThingsboardErrorResponseHandler; | 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 | import org.thingsboard.server.service.component.ComponentDiscoveryService; | 102 | import org.thingsboard.server.service.component.ComponentDiscoveryService; |
97 | import org.thingsboard.server.service.security.model.SecurityUser; | 103 | import org.thingsboard.server.service.security.model.SecurityUser; |
98 | import org.thingsboard.server.service.security.permission.AccessControlService; | 104 | import org.thingsboard.server.service.security.permission.AccessControlService; |
@@ -185,6 +191,12 @@ public abstract class BaseController { | @@ -185,6 +191,12 @@ public abstract class BaseController { | ||
185 | @Autowired | 191 | @Autowired |
186 | protected ClaimDevicesService claimDevicesService; | 192 | protected ClaimDevicesService claimDevicesService; |
187 | 193 | ||
194 | + @Autowired | ||
195 | + protected PartitionService partitionService; | ||
196 | + | ||
197 | + @Autowired | ||
198 | + protected TbCoreQueueProvider coreQueueProvider; | ||
199 | + | ||
188 | @Value("${server.log_controller_error_stack_trace}") | 200 | @Value("${server.log_controller_error_stack_trace}") |
189 | @Getter | 201 | @Getter |
190 | private boolean logControllerErrorStackTrace; | 202 | private boolean logControllerErrorStackTrace; |
@@ -662,7 +674,12 @@ public abstract class BaseController { | @@ -662,7 +674,12 @@ public abstract class BaseController { | ||
662 | TbMsg tbMsg = new TbMsg(UUIDs.timeBased(), msgType, entityId, metaData, TbMsgDataType.JSON | 674 | TbMsg tbMsg = new TbMsg(UUIDs.timeBased(), msgType, entityId, metaData, TbMsgDataType.JSON |
663 | , json.writeValueAsString(entityNode) | 675 | , json.writeValueAsString(entityNode) |
664 | , null, null, null); | 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 | } catch (Exception e) { | 683 | } catch (Exception e) { |
667 | log.warn("[{}] Failed to push entity action to rule engine: {}", entityId, actionType, e); | 684 | log.warn("[{}] Failed to push entity action to rule engine: {}", entityId, actionType, e); |
668 | } | 685 | } |
@@ -5,7 +5,7 @@ | @@ -5,7 +5,7 @@ | ||
5 | * you may not use this file except in compliance with the License. | 5 | * you may not use this file except in compliance with the License. |
6 | * You may obtain a copy of the License at | 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 | * Unless required by applicable law or agreed to in writing, software | 10 | * Unless required by applicable law or agreed to in writing, software |
11 | * distributed under the License is distributed on an "AS IS" BASIS, | 11 | * distributed under the License is distributed on an "AS IS" BASIS, |
@@ -45,6 +45,7 @@ import javax.annotation.PostConstruct; | @@ -45,6 +45,7 @@ import javax.annotation.PostConstruct; | ||
45 | import javax.annotation.PreDestroy; | 45 | import javax.annotation.PreDestroy; |
46 | import java.util.List; | 46 | import java.util.List; |
47 | import java.util.UUID; | 47 | import java.util.UUID; |
48 | +import java.util.concurrent.ConcurrentHashMap; | ||
48 | import java.util.concurrent.ConcurrentMap; | 49 | import java.util.concurrent.ConcurrentMap; |
49 | import java.util.concurrent.CountDownLatch; | 50 | import java.util.concurrent.CountDownLatch; |
50 | import java.util.concurrent.ExecutorService; | 51 | import java.util.concurrent.ExecutorService; |
@@ -104,11 +105,13 @@ public class DefaultTbCoreConsumerService implements TbCoreConsumerService { | @@ -104,11 +105,13 @@ public class DefaultTbCoreConsumerService implements TbCoreConsumerService { | ||
104 | if (msgs.isEmpty()) { | 105 | if (msgs.isEmpty()) { |
105 | continue; | 106 | continue; |
106 | } | 107 | } |
107 | - ConcurrentMap<UUID, TbProtoQueueMsg<ToCoreMsg>> ackMap = msgs.stream().collect( | 108 | + ConcurrentMap<UUID, TbProtoQueueMsg<ToCoreMsg>> pendingMap = msgs.stream().collect( |
108 | Collectors.toConcurrentMap(s -> UUID.randomUUID(), Function.identity())); | 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 | CountDownLatch processingTimeoutLatch = new CountDownLatch(1); | 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 | try { | 115 | try { |
113 | ToCoreMsg toCoreMsg = msg.getValue(); | 116 | ToCoreMsg toCoreMsg = msg.getValue(); |
114 | if (toCoreMsg.hasToDeviceActorMsg()) { | 117 | if (toCoreMsg.hasToDeviceActorMsg()) { |
@@ -130,7 +133,8 @@ public class DefaultTbCoreConsumerService implements TbCoreConsumerService { | @@ -130,7 +133,8 @@ public class DefaultTbCoreConsumerService implements TbCoreConsumerService { | ||
130 | } | 133 | } |
131 | }); | 134 | }); |
132 | if (!processingTimeoutLatch.await(packProcessingTimeout, TimeUnit.MILLISECONDS)) { | 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 | consumer.commit(); | 139 | consumer.commit(); |
136 | } catch (Exception e) { | 140 | } catch (Exception e) { |
@@ -182,7 +186,7 @@ public class DefaultTbCoreConsumerService implements TbCoreConsumerService { | @@ -182,7 +186,7 @@ public class DefaultTbCoreConsumerService implements TbCoreConsumerService { | ||
182 | subscriptionManagerService.cancelSubscription(closeProto.getSessionId(), closeProto.getSubscriptionId(), callback); | 186 | subscriptionManagerService.cancelSubscription(closeProto.getSessionId(), closeProto.getSubscriptionId(), callback); |
183 | } else if (msg.hasTsUpdate()) { | 187 | } else if (msg.hasTsUpdate()) { |
184 | TransportProtos.TbTimeSeriesUpdateProto proto = msg.getTsUpdate(); | 188 | TransportProtos.TbTimeSeriesUpdateProto proto = msg.getTsUpdate(); |
185 | - subscriptionManagerService.onTimeseriesDataUpdate( | 189 | + subscriptionManagerService.onTimeSeriesUpdate( |
186 | new TenantId(new UUID(proto.getTenantIdMSB(), proto.getTenantIdLSB())), | 190 | new TenantId(new UUID(proto.getTenantIdMSB(), proto.getTenantIdLSB())), |
187 | TbSubscriptionUtils.toEntityId(proto.getEntityType(), proto.getEntityIdMSB(), proto.getEntityIdLSB()), | 191 | TbSubscriptionUtils.toEntityId(proto.getEntityType(), proto.getEntityIdMSB(), proto.getEntityIdLSB()), |
188 | TbSubscriptionUtils.toTsKvEntityList(proto.getDataList()), callback); | 192 | TbSubscriptionUtils.toTsKvEntityList(proto.getDataList()), callback); |
@@ -5,7 +5,7 @@ | @@ -5,7 +5,7 @@ | ||
5 | * you may not use this file except in compliance with the License. | 5 | * you may not use this file except in compliance with the License. |
6 | * You may obtain a copy of the License at | 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 | * Unless required by applicable law or agreed to in writing, software | 10 | * Unless required by applicable law or agreed to in writing, software |
11 | * distributed under the License is distributed on an "AS IS" BASIS, | 11 | * distributed under the License is distributed on an "AS IS" BASIS, |
@@ -36,11 +36,16 @@ import org.thingsboard.server.queue.discovery.PartitionChangeEvent; | @@ -36,11 +36,16 @@ import org.thingsboard.server.queue.discovery.PartitionChangeEvent; | ||
36 | import org.thingsboard.server.queue.discovery.ServiceType; | 36 | import org.thingsboard.server.queue.discovery.ServiceType; |
37 | import org.thingsboard.server.gen.transport.TransportProtos; | 37 | import org.thingsboard.server.gen.transport.TransportProtos; |
38 | import org.thingsboard.server.queue.provider.TbRuleEngineQueueProvider; | 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 | import javax.annotation.PostConstruct; | 44 | import javax.annotation.PostConstruct; |
41 | import javax.annotation.PreDestroy; | 45 | import javax.annotation.PreDestroy; |
42 | import java.util.List; | 46 | import java.util.List; |
43 | import java.util.UUID; | 47 | import java.util.UUID; |
48 | +import java.util.concurrent.ConcurrentHashMap; | ||
44 | import java.util.concurrent.ConcurrentMap; | 49 | import java.util.concurrent.ConcurrentMap; |
45 | import java.util.concurrent.CountDownLatch; | 50 | import java.util.concurrent.CountDownLatch; |
46 | import java.util.concurrent.ExecutorService; | 51 | import java.util.concurrent.ExecutorService; |
@@ -64,10 +69,12 @@ public class DefaultTbRuleEngineConsumerService implements TbRuleEngineConsumerS | @@ -64,10 +69,12 @@ public class DefaultTbRuleEngineConsumerService implements TbRuleEngineConsumerS | ||
64 | private final ActorSystemContext actorContext; | 69 | private final ActorSystemContext actorContext; |
65 | private final TbQueueConsumer<TbProtoQueueMsg<TransportProtos.ToRuleEngineMsg>> consumer; | 70 | private final TbQueueConsumer<TbProtoQueueMsg<TransportProtos.ToRuleEngineMsg>> consumer; |
66 | private final TbCoreConsumerStats stats = new TbCoreConsumerStats(); | 71 | private final TbCoreConsumerStats stats = new TbCoreConsumerStats(); |
72 | + private final TbRuleEngineProcessingStrategyFactory factory; | ||
67 | private volatile ExecutorService mainConsumerExecutor; | 73 | private volatile ExecutorService mainConsumerExecutor; |
68 | private volatile boolean stopped = false; | 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 | this.consumer = tbRuleEngineQueueProvider.getToRuleEngineMsgConsumer(); | 78 | this.consumer = tbRuleEngineQueueProvider.getToRuleEngineMsgConsumer(); |
72 | this.actorContext = actorContext; | 79 | this.actorContext = actorContext; |
73 | } | 80 | } |
@@ -75,6 +82,7 @@ public class DefaultTbRuleEngineConsumerService implements TbRuleEngineConsumerS | @@ -75,6 +82,7 @@ public class DefaultTbRuleEngineConsumerService implements TbRuleEngineConsumerS | ||
75 | @PostConstruct | 82 | @PostConstruct |
76 | public void init() { | 83 | public void init() { |
77 | this.mainConsumerExecutor = Executors.newSingleThreadExecutor(ThingsBoardThreadFactory.forName("tb-core-consumer")); | 84 | this.mainConsumerExecutor = Executors.newSingleThreadExecutor(ThingsBoardThreadFactory.forName("tb-core-consumer")); |
85 | + this.factory.newInstance(); | ||
78 | } | 86 | } |
79 | 87 | ||
80 | @Override | 88 | @Override |
@@ -94,29 +102,46 @@ public class DefaultTbRuleEngineConsumerService implements TbRuleEngineConsumerS | @@ -94,29 +102,46 @@ public class DefaultTbRuleEngineConsumerService implements TbRuleEngineConsumerS | ||
94 | if (msgs.isEmpty()) { | 102 | if (msgs.isEmpty()) { |
95 | continue; | 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 | consumer.commit(); | 142 | consumer.commit(); |
118 | } catch (Exception e) { | 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 | try { | 145 | try { |
121 | Thread.sleep(pollDuration); | 146 | Thread.sleep(pollDuration); |
122 | } catch (InterruptedException e2) { | 147 | } catch (InterruptedException e2) { |
@@ -5,7 +5,7 @@ | @@ -5,7 +5,7 @@ | ||
5 | * you may not use this file except in compliance with the License. | 5 | * you may not use this file except in compliance with the License. |
6 | * You may obtain a copy of the License at | 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 | * Unless required by applicable law or agreed to in writing, software | 10 | * Unless required by applicable law or agreed to in writing, software |
11 | * distributed under the License is distributed on an "AS IS" BASIS, | 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,25 +20,37 @@ import org.thingsboard.server.common.msg.queue.TbMsgCallback; | ||
20 | import org.thingsboard.server.queue.common.TbProtoQueueMsg; | 20 | import org.thingsboard.server.queue.common.TbProtoQueueMsg; |
21 | 21 | ||
22 | import java.util.UUID; | 22 | import java.util.UUID; |
23 | +import java.util.concurrent.ConcurrentHashMap; | ||
23 | import java.util.concurrent.ConcurrentMap; | 24 | import java.util.concurrent.ConcurrentMap; |
24 | import java.util.concurrent.CountDownLatch; | 25 | import java.util.concurrent.CountDownLatch; |
25 | 26 | ||
26 | @Slf4j | 27 | @Slf4j |
27 | -public class MsgPackCallback<T extends com.google.protobuf.GeneratedMessageV3> implements TbMsgCallback { | 28 | +public class MsgPackCallback<T> implements TbMsgCallback { |
28 | private final CountDownLatch processingTimeoutLatch; | 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 | private final UUID id; | 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 | this.id = id; | 39 | this.id = id; |
34 | this.processingTimeoutLatch = processingTimeoutLatch; | 40 | this.processingTimeoutLatch = processingTimeoutLatch; |
35 | this.ackMap = ackMap; | 41 | this.ackMap = ackMap; |
42 | + this.successMap = successMap; | ||
43 | + this.failedMap = failedMap; | ||
36 | } | 44 | } |
37 | 45 | ||
38 | @Override | 46 | @Override |
39 | public void onSuccess() { | 47 | public void onSuccess() { |
40 | log.trace("[{}] ON SUCCESS", id); | 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 | processingTimeoutLatch.countDown(); | 54 | processingTimeoutLatch.countDown(); |
43 | } | 55 | } |
44 | } | 56 | } |
@@ -46,8 +58,10 @@ public class MsgPackCallback<T extends com.google.protobuf.GeneratedMessageV3> i | @@ -46,8 +58,10 @@ public class MsgPackCallback<T extends com.google.protobuf.GeneratedMessageV3> i | ||
46 | @Override | 58 | @Override |
47 | public void onFailure(Throwable t) { | 59 | public void onFailure(Throwable t) { |
48 | log.trace("[{}] ON FAILURE", id); | 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 | if (ackMap.isEmpty()) { | 65 | if (ackMap.isEmpty()) { |
52 | processingTimeoutLatch.countDown(); | 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 | +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,7 +5,7 @@ | ||
5 | * you may not use this file except in compliance with the License. | 5 | * you may not use this file except in compliance with the License. |
6 | * You may obtain a copy of the License at | 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 | * Unless required by applicable law or agreed to in writing, software | 10 | * Unless required by applicable law or agreed to in writing, software |
11 | * distributed under the License is distributed on an "AS IS" BASIS, | 11 | * distributed under the License is distributed on an "AS IS" BASIS, |
@@ -32,6 +32,7 @@ import org.springframework.stereotype.Service; | @@ -32,6 +32,7 @@ import org.springframework.stereotype.Service; | ||
32 | import org.springframework.util.StringUtils; | 32 | import org.springframework.util.StringUtils; |
33 | import org.thingsboard.common.util.ThingsBoardThreadFactory; | 33 | import org.thingsboard.common.util.ThingsBoardThreadFactory; |
34 | import org.thingsboard.server.actors.service.ActorService; | 34 | import org.thingsboard.server.actors.service.ActorService; |
35 | +import org.thingsboard.server.queue.TbQueueCallback; | ||
35 | import org.thingsboard.server.queue.common.TbProtoQueueMsg; | 36 | import org.thingsboard.server.queue.common.TbProtoQueueMsg; |
36 | import org.thingsboard.server.common.data.DataConstants; | 37 | import org.thingsboard.server.common.data.DataConstants; |
37 | import org.thingsboard.server.common.data.Device; | 38 | import org.thingsboard.server.common.data.Device; |
@@ -502,7 +503,12 @@ public class DefaultDeviceStateService implements DeviceStateService { | @@ -502,7 +503,12 @@ public class DefaultDeviceStateService implements DeviceStateService { | ||
502 | TbMsg tbMsg = new TbMsg(UUIDs.timeBased(), msgType, stateData.getDeviceId(), stateData.getMetaData().copy(), TbMsgDataType.JSON | 503 | TbMsg tbMsg = new TbMsg(UUIDs.timeBased(), msgType, stateData.getDeviceId(), stateData.getMetaData().copy(), TbMsgDataType.JSON |
503 | , json.writeValueAsString(state) | 504 | , json.writeValueAsString(state) |
504 | , null, null, null); | 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 | } catch (Exception e) { | 512 | } catch (Exception e) { |
507 | log.warn("[{}] Failed to push inactivity alarm: {}", stateData.getDeviceId(), state, e); | 513 | log.warn("[{}] Failed to push inactivity alarm: {}", stateData.getDeviceId(), state, e); |
508 | } | 514 | } |
@@ -187,7 +187,7 @@ public class DefaultSubscriptionManagerService implements SubscriptionManagerSer | @@ -187,7 +187,7 @@ public class DefaultSubscriptionManagerService implements SubscriptionManagerSer | ||
187 | } | 187 | } |
188 | 188 | ||
189 | @Override | 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 | onLocalSubUpdate(entityId, | 191 | onLocalSubUpdate(entityId, |
192 | s -> { | 192 | s -> { |
193 | if (TbSubscriptionType.TIMESERIES.equals(s.getType())) { | 193 | if (TbSubscriptionType.TIMESERIES.equals(s.getType())) { |
@@ -31,7 +31,8 @@ public interface SubscriptionManagerService extends ApplicationListener<Partitio | @@ -31,7 +31,8 @@ public interface SubscriptionManagerService extends ApplicationListener<Partitio | ||
31 | 31 | ||
32 | void cancelSubscription(String sessionId, int subscriptionId, TbMsgCallback callback); | 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 | void onAttributesUpdate(TenantId tenantId, EntityId entityId, String scope, List<AttributeKvEntry> attributes, TbMsgCallback callback); | 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,7 +168,7 @@ public class DefaultTelemetrySubscriptionService implements TelemetrySubscriptio | ||
168 | private void onTimeSeriesUpdate(TenantId tenantId, EntityId entityId, List<TsKvEntry> ts) { | 168 | private void onTimeSeriesUpdate(TenantId tenantId, EntityId entityId, List<TsKvEntry> ts) { |
169 | TopicPartitionInfo tpi = partitionService.resolve(ServiceType.TB_CORE, tenantId, entityId); | 169 | TopicPartitionInfo tpi = partitionService.resolve(ServiceType.TB_CORE, tenantId, entityId); |
170 | if (currentPartitions.contains(tpi)) { | 170 | if (currentPartitions.contains(tpi)) { |
171 | - subscriptionManagerService.onTimeseriesDataUpdate(tenantId, entityId, ts, TbMsgCallback.EMPTY); | 171 | + subscriptionManagerService.onTimeSeriesUpdate(tenantId, entityId, ts, TbMsgCallback.EMPTY); |
172 | } else { | 172 | } else { |
173 | TransportProtos.ToCoreMsg toCoreMsg = TbSubscriptionUtils.toTimeseriesUpdateProto(tenantId, entityId, ts); | 173 | TransportProtos.ToCoreMsg toCoreMsg = TbSubscriptionUtils.toTimeseriesUpdateProto(tenantId, entityId, ts); |
174 | toCoreProducer.send(tpi, new TbProtoQueueMsg<>(entityId.getId(), toCoreMsg), null); | 174 | toCoreProducer.send(tpi, new TbProtoQueueMsg<>(entityId.getId(), toCoreMsg), null); |
@@ -181,27 +181,27 @@ cassandra: | @@ -181,27 +181,27 @@ cassandra: | ||
181 | 181 | ||
182 | # SQL configuration parameters | 182 | # SQL configuration parameters |
183 | sql: | 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 | # Actor system parameters | 206 | # Actor system parameters |
207 | actors: | 207 | actors: |
@@ -330,19 +330,19 @@ updates: | @@ -330,19 +330,19 @@ updates: | ||
330 | 330 | ||
331 | # spring CORS configuration | 331 | # spring CORS configuration |
332 | spring.mvc.cors: | 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 | # spring serve gzip compressed static resources | 347 | # spring serve gzip compressed static resources |
348 | spring.resources.chain: | 348 | spring.resources.chain: |
@@ -551,6 +551,12 @@ queue: | @@ -551,6 +551,12 @@ queue: | ||
551 | poll_interval: "${TB_QUEUE_RULE_ENGINE_POLL_INTERVAL_MS:25}" | 551 | poll_interval: "${TB_QUEUE_RULE_ENGINE_POLL_INTERVAL_MS:25}" |
552 | partitions: "${TB_QUEUE_RULE_ENGINE_PARTITIONS:10}" | 552 | partitions: "${TB_QUEUE_RULE_ENGINE_PARTITIONS:10}" |
553 | pack_processing_timeout: "${TB_QUEUE_RULE_ENGINE_PACK_PROCESSING_TIMEOUT_MS:60000}" | 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 | stats: | 560 | stats: |
555 | enabled: "${TB_QUEUE_RULE_ENGINE_STATS_ENABLED:false}" | 561 | enabled: "${TB_QUEUE_RULE_ENGINE_STATS_ENABLED:false}" |
556 | print_interval_ms: "${TB_QUEUE_RULE_ENGINE_STATS_PRINT_INTERVAL_MS:10000}" | 562 | print_interval_ms: "${TB_QUEUE_RULE_ENGINE_STATS_PRINT_INTERVAL_MS:10000}" |
@@ -5,7 +5,7 @@ | @@ -5,7 +5,7 @@ | ||
5 | * you may not use this file except in compliance with the License. | 5 | * you may not use this file except in compliance with the License. |
6 | * You may obtain a copy of the License at | 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 | * Unless required by applicable law or agreed to in writing, software | 10 | * Unless required by applicable law or agreed to in writing, software |
11 | * distributed under the License is distributed on an "AS IS" BASIS, | 11 | * distributed under the License is distributed on an "AS IS" BASIS, |
@@ -15,6 +15,7 @@ | @@ -15,6 +15,7 @@ | ||
15 | */ | 15 | */ |
16 | package org.thingsboard.server.common.msg; | 16 | package org.thingsboard.server.common.msg; |
17 | 17 | ||
18 | +import com.google.protobuf.ByteString; | ||
18 | import com.google.protobuf.InvalidProtocolBufferException; | 19 | import com.google.protobuf.InvalidProtocolBufferException; |
19 | import lombok.AllArgsConstructor; | 20 | import lombok.AllArgsConstructor; |
20 | import lombok.Builder; | 21 | import lombok.Builder; |
@@ -73,7 +74,10 @@ public final class TbMsg implements Serializable { | @@ -73,7 +74,10 @@ public final class TbMsg implements Serializable { | ||
73 | log.warn("[{}] Created message with empty callback: {}", originator, type); | 74 | log.warn("[{}] Created message with empty callback: {}", originator, type); |
74 | this.callback = TbMsgCallback.EMPTY; | 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 | public static byte[] toByteArray(TbMsg msg) { | 83 | public static byte[] toByteArray(TbMsg msg) { |
@@ -5,7 +5,7 @@ | @@ -5,7 +5,7 @@ | ||
5 | * you may not use this file except in compliance with the License. | 5 | * you may not use this file except in compliance with the License. |
6 | * You may obtain a copy of the License at | 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 | * Unless required by applicable law or agreed to in writing, software | 10 | * Unless required by applicable law or agreed to in writing, software |
11 | * distributed under the License is distributed on an "AS IS" BASIS, | 11 | * distributed under the License is distributed on an "AS IS" BASIS, |
@@ -244,7 +244,7 @@ public class DefaultTransportService implements TransportService { | @@ -244,7 +244,7 @@ public class DefaultTransportService implements TransportService { | ||
244 | metaData.putValue("ts", tsKv.getTs() + ""); | 244 | metaData.putValue("ts", tsKv.getTs() + ""); |
245 | JsonObject json = JsonUtils.getJsonObject(tsKv.getKvList()); | 245 | JsonObject json = JsonUtils.getJsonObject(tsKv.getKvList()); |
246 | TbMsg tbMsg = new TbMsg(UUID.randomUUID(), SessionMsgType.POST_TELEMETRY_REQUEST.name(), | 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 | sendToRuleEngine(tenantId, tbMsg, packCallback); | 248 | sendToRuleEngine(tenantId, tbMsg, packCallback); |
249 | } | 249 | } |
250 | } | 250 | } |
@@ -261,7 +261,7 @@ public class DefaultTransportService implements TransportService { | @@ -261,7 +261,7 @@ public class DefaultTransportService implements TransportService { | ||
261 | metaData.putValue("deviceName", sessionInfo.getDeviceName()); | 261 | metaData.putValue("deviceName", sessionInfo.getDeviceName()); |
262 | metaData.putValue("deviceType", sessionInfo.getDeviceType()); | 262 | metaData.putValue("deviceType", sessionInfo.getDeviceType()); |
263 | TbMsg tbMsg = new TbMsg(UUID.randomUUID(), SessionMsgType.POST_ATTRIBUTES_REQUEST.name(), deviceId, metaData, | 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 | sendToRuleEngine(tenantId, tbMsg, new TransportTbQueueCallback(callback)); | 265 | sendToRuleEngine(tenantId, tbMsg, new TransportTbQueueCallback(callback)); |
266 | } | 266 | } |
267 | } | 267 | } |
@@ -488,7 +488,7 @@ public class DefaultTransportService implements TransportService { | @@ -488,7 +488,7 @@ public class DefaultTransportService implements TransportService { | ||
488 | 488 | ||
489 | protected void sendToRuleEngine(TenantId tenantId, TbMsg tbMsg, TbQueueCallback callback) { | 489 | protected void sendToRuleEngine(TenantId tenantId, TbMsg tbMsg, TbQueueCallback callback) { |
490 | TopicPartitionInfo tpi = partitionService.resolve(ServiceType.TB_RULE_ENGINE, tenantId, tbMsg.getOriginator()); | 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 | .setTenantIdMSB(tenantId.getId().getMostSignificantBits()) | 492 | .setTenantIdMSB(tenantId.getId().getMostSignificantBits()) |
493 | .setTenantIdLSB(tenantId.getId().getLeastSignificantBits()).build(); | 493 | .setTenantIdLSB(tenantId.getId().getLeastSignificantBits()).build(); |
494 | ruleEngineMsgProducer.send(tpi, new TbProtoQueueMsg<>(tbMsg.getId(), msg), callback); | 494 | ruleEngineMsgProducer.send(tpi, new TbProtoQueueMsg<>(tbMsg.getId(), msg), callback); |