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 | +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); | ... | ... |