Browse Source

Implemented isolated rule engine queue consumers

pull/6134/head
YevhenBondarenko 4 years ago
parent
commit
75e2b60eb8
  1. 30
      application/src/main/java/org/thingsboard/server/actors/app/AppActor.java
  2. 2
      application/src/main/java/org/thingsboard/server/actors/service/DefaultActorService.java
  3. 24
      application/src/main/java/org/thingsboard/server/actors/tenant/TenantActor.java
  4. 101
      application/src/main/java/org/thingsboard/server/service/queue/DefaultTbClusterService.java
  5. 79
      application/src/main/java/org/thingsboard/server/service/queue/DefaultTbRuleEngineConsumerService.java
  6. 1
      application/src/main/resources/thingsboard.yml
  7. 8
      application/src/test/java/org/thingsboard/server/service/cluster/routing/HashPartitionServiceTest.java
  8. 4
      common/cluster-api/src/main/java/org/thingsboard/server/queue/TbQueueClusterService.java
  9. 4
      common/cluster-api/src/main/proto/queue.proto
  10. 4
      common/dao-api/src/main/java/org/thingsboard/server/dao/queue/QueueService.java
  11. 13
      common/data/src/main/java/org/thingsboard/server/common/data/queue/Queue.java
  12. 5
      common/data/src/main/java/org/thingsboard/server/common/data/tenant/profile/TenantProfileData.java
  13. 32
      common/data/src/main/java/org/thingsboard/server/common/data/tenant/profile/TenantProfileQueueConfiguration.java
  14. 2
      common/message/src/main/java/org/thingsboard/server/common/msg/queue/PartitionChangeMsg.java
  15. 14
      common/message/src/main/java/org/thingsboard/server/common/msg/queue/ServiceQueueKey.java
  16. 20
      common/queue/src/main/java/org/thingsboard/server/queue/discovery/DefaultTbServiceInfoProvider.java
  17. 161
      common/queue/src/main/java/org/thingsboard/server/queue/discovery/HashPartitionService.java
  18. 56
      common/queue/src/main/java/org/thingsboard/server/queue/discovery/QueueKey.java
  19. 9
      common/queue/src/main/java/org/thingsboard/server/queue/discovery/QueueRoutingInfo.java
  20. 5
      common/queue/src/main/java/org/thingsboard/server/queue/discovery/TbServiceInfoProvider.java
  21. 44
      common/queue/src/main/java/org/thingsboard/server/queue/discovery/TopicPartitionInfoKey.java
  22. 10
      common/queue/src/main/java/org/thingsboard/server/queue/discovery/event/PartitionChangeEvent.java
  23. 29
      dao/src/main/java/org/thingsboard/server/dao/queue/BaseQueueService.java
  24. 91
      dao/src/main/java/org/thingsboard/server/dao/tenant/TenantServiceImpl.java
  25. 1
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbMsgCountNode.java
  26. 1
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/debug/TbMsgGeneratorNode.java
  27. 1
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/delay/TbMsgDelayNode.java
  28. 4
      ui-ngx/src/app/modules/home/components/import-export/import-export.service.ts
  29. 62
      ui-ngx/src/app/modules/home/components/profile/tenant-profile.component.html
  30. 13
      ui-ngx/src/app/modules/home/components/profile/tenant-profile.component.ts
  31. 1
      ui-ngx/src/app/shared/models/tenant.model.ts
  32. 3
      ui-ngx/src/assets/locale/locale.constant-en_US.json

30
application/src/main/java/org/thingsboard/server/actors/app/AppActor.java

@ -30,8 +30,6 @@ import org.thingsboard.server.actors.service.DefaultActorService;
import org.thingsboard.server.actors.tenant.TenantActor;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.TenantProfile;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.PageDataIterable;
import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
@ -45,24 +43,20 @@ import org.thingsboard.server.common.msg.queue.RuleEngineException;
import org.thingsboard.server.common.msg.queue.ServiceType;
import org.thingsboard.server.dao.model.ModelConstants;
import org.thingsboard.server.dao.tenant.TenantService;
import org.thingsboard.server.dao.tenant.TbTenantProfileCache;
import org.thingsboard.server.service.transport.msg.TransportToDeviceActorMsgWrapper;
import java.util.HashSet;
import java.util.Optional;
import java.util.Set;
@Slf4j
public class AppActor extends ContextAwareActor {
private final TbTenantProfileCache tenantProfileCache;
private final TenantService tenantService;
private final Set<TenantId> deletedTenants;
private volatile boolean ruleChainsInitialized;
private AppActor(ActorSystemContext systemContext) {
super(systemContext);
this.tenantProfileCache = systemContext.getTenantProfileCache();
this.tenantService = systemContext.getTenantService();
this.deletedTenants = new HashSet<>();
}
@ -125,28 +119,12 @@ public class AppActor extends ContextAwareActor {
private void initTenantActors() {
log.info("Starting main system actor.");
try {
// This Service may be started for specific tenant only.
Optional<TenantId> isolatedTenantId = systemContext.getServiceInfoProvider().getIsolatedTenant();
if (isolatedTenantId.isPresent()) {
Tenant tenant = systemContext.getTenantService().findTenantById(isolatedTenantId.get());
if (tenant != null) {
log.debug("[{}] Creating tenant actor", tenant.getId());
getOrCreateTenantActor(tenant.getId());
log.debug("Tenant actor created.");
} else {
log.error("[{}] Tenant with such ID does not exist", isolatedTenantId.get());
}
} else if (systemContext.isTenantComponentsInitEnabled()) {
if (systemContext.isTenantComponentsInitEnabled()) {
PageDataIterable<Tenant> tenantIterator = new PageDataIterable<>(tenantService::findTenants, ENTITY_PACK_LIMIT);
boolean isRuleEngine = systemContext.getServiceInfoProvider().isService(ServiceType.TB_RULE_ENGINE);
boolean isCore = systemContext.getServiceInfoProvider().isService(ServiceType.TB_CORE);
for (Tenant tenant : tenantIterator) {
TenantProfile tenantProfile = tenantProfileCache.get(tenant.getTenantProfileId());
if (isCore || (isRuleEngine && !tenantProfile.isIsolatedTbRuleEngine())) {
log.debug("[{}] Creating tenant actor", tenant.getId());
getOrCreateTenantActor(tenant.getId());
log.debug("[{}] Tenant actor created.", tenant.getId());
}
log.debug("[{}] Creating tenant actor", tenant.getId());
getOrCreateTenantActor(tenant.getId());
log.debug("[{}] Tenant actor created.", tenant.getId());
}
}
log.info("Main system actor started.");

2
application/src/main/java/org/thingsboard/server/actors/service/DefaultActorService.java

@ -123,7 +123,7 @@ public class DefaultActorService extends TbApplicationEventListener<PartitionCha
@Override
protected void onTbApplicationEvent(PartitionChangeEvent event) {
log.info("Received partition change event.");
this.appActor.tellWithHighPriority(new PartitionChangeMsg(event.getServiceQueueKey(), event.getPartitions()));
this.appActor.tellWithHighPriority(new PartitionChangeMsg(event.getQueueKey().getType(), event.getPartitions()));
}
@PreDestroy

24
application/src/main/java/org/thingsboard/server/actors/tenant/TenantActor.java

@ -26,16 +26,12 @@ import org.thingsboard.server.actors.TbActorRef;
import org.thingsboard.server.actors.TbEntityActorId;
import org.thingsboard.server.actors.TbEntityTypeActorIdPredicate;
import org.thingsboard.server.actors.device.DeviceActorCreator;
import org.thingsboard.server.actors.device.SessionTimeoutCheckMsg;
import org.thingsboard.server.actors.ruleChain.RuleChainInputMsg;
import org.thingsboard.server.actors.ruleChain.RuleChainManagerActor;
import org.thingsboard.server.actors.ruleChain.RuleChainOutputMsg;
import org.thingsboard.server.actors.service.ContextBasedCreator;
import org.thingsboard.server.actors.service.DefaultActorService;
import org.thingsboard.server.common.data.ApiUsageState;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.TenantProfile;
import org.thingsboard.server.common.data.edge.Edge;
import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.EdgeId;
@ -60,7 +56,6 @@ import org.thingsboard.server.service.edge.rpc.EdgeRpcService;
import org.thingsboard.server.service.transport.msg.TransportToDeviceActorMsgWrapper;
import java.util.List;
import java.util.Optional;
@Slf4j
public class TenantActor extends RuleChainManagerActor {
@ -87,24 +82,15 @@ public class TenantActor extends RuleChainManagerActor {
} else {
apiUsageState = new ApiUsageState(systemContext.getApiUsageStateService().getApiUsageState(tenant.getId()));
// This Service may be started for specific tenant only.
Optional<TenantId> isolatedTenantId = systemContext.getServiceInfoProvider().getIsolatedTenant();
TenantProfile tenantProfile = systemContext.getTenantProfileCache().get(tenant.getTenantProfileId());
isCore = systemContext.getServiceInfoProvider().isService(ServiceType.TB_CORE);
isRuleEngineForCurrentTenant = systemContext.getServiceInfoProvider().isService(ServiceType.TB_RULE_ENGINE);
if (isRuleEngineForCurrentTenant) {
try {
if (isolatedTenantId.map(id -> id.equals(tenantId)).orElseGet(() -> !tenantProfile.isIsolatedTbRuleEngine())) {
if (apiUsageState.isReExecEnabled()) {
log.info("[{}] Going to init rule chains", tenantId);
initRuleChains();
} else {
log.info("[{}] Skip init of the rule chains due to API limits", tenantId);
}
if (apiUsageState.isReExecEnabled()) {
log.info("[{}] Going to init rule chains", tenantId);
initRuleChains();
} else {
isRuleEngineForCurrentTenant = false;
log.info("[{}] Skip init of the rule chains due to API limits", tenantId);
}
} catch (Exception e) {
cantFindTenant = true;
@ -138,7 +124,7 @@ public class TenantActor extends RuleChainManagerActor {
switch (msg.getMsgType()) {
case PARTITION_CHANGE_MSG:
PartitionChangeMsg partitionChangeMsg = (PartitionChangeMsg) msg;
ServiceType serviceType = partitionChangeMsg.getServiceQueueKey().getServiceType();
ServiceType serviceType = partitionChangeMsg.getServiceType();
if (ServiceType.TB_RULE_ENGINE.equals(serviceType)) {
//To Rule Chain Actors
broadcast(msg);

101
application/src/main/java/org/thingsboard/server/service/queue/DefaultTbClusterService.java

@ -26,32 +26,33 @@ import org.springframework.stereotype.Service;
import org.thingsboard.rule.engine.api.msg.DeviceEdgeUpdateMsg;
import org.thingsboard.rule.engine.api.msg.DeviceNameOrTypeUpdateMsg;
import org.thingsboard.server.cluster.TbClusterService;
import org.thingsboard.server.common.data.EdgeUtils;
import org.thingsboard.server.common.data.edge.EdgeEventActionType;
import org.thingsboard.server.common.data.edge.EdgeEventType;
import org.thingsboard.server.common.data.id.QueueId;
import org.thingsboard.server.common.data.queue.Queue;
import org.thingsboard.server.common.msg.ToDeviceActorNotificationMsg;
import org.thingsboard.server.common.data.ApiUsageState;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.DeviceProfile;
import org.thingsboard.server.common.data.EdgeUtils;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.HasName;
import org.thingsboard.server.common.data.TbResource;
import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.TenantProfile;
import org.thingsboard.server.common.data.edge.EdgeEventActionType;
import org.thingsboard.server.common.data.edge.EdgeEventType;
import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.DeviceProfileId;
import org.thingsboard.server.common.data.id.EdgeId;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.QueueId;
import org.thingsboard.server.common.data.id.RuleChainId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
import org.thingsboard.server.common.data.queue.Queue;
import org.thingsboard.server.common.msg.TbMsg;
import org.thingsboard.server.common.msg.ToDeviceActorNotificationMsg;
import org.thingsboard.server.common.msg.edge.EdgeEventUpdateMsg;
import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg;
import org.thingsboard.server.common.msg.queue.ServiceType;
import org.thingsboard.server.common.msg.queue.TopicPartitionInfo;
import org.thingsboard.server.common.msg.rpc.FromDeviceRpcResponse;
import org.thingsboard.server.common.transport.util.DataDecodingEncodingService;
import org.thingsboard.server.gen.transport.TransportProtos;
import org.thingsboard.server.gen.transport.TransportProtos.FromDeviceRPCResponseProto;
@ -70,7 +71,6 @@ import org.thingsboard.server.queue.provider.TbQueueProducerProvider;
import org.thingsboard.server.service.gateway_device.GatewayNotificationsService;
import org.thingsboard.server.service.ota.OtaPackageStateService;
import org.thingsboard.server.service.profile.TbDeviceProfileCache;
import org.thingsboard.server.common.msg.rpc.FromDeviceRpcResponse;
import java.util.HashSet;
import java.util.Set;
@ -490,7 +490,7 @@ public class DefaultTbClusterService implements TbClusterService {
}
@Override
public void onQueueChange(Queue queue, TbQueueCallback callback) {
public void onQueueChange(Queue queue) {
log.trace("[{}][{}] Processing queue change [{}] event", queue.getTenantId(), queue.getId(), queue.getName());
TransportProtos.QueueUpdateMsg queueUpdateMsg = TransportProtos.QueueUpdateMsg.newBuilder()
@ -503,38 +503,14 @@ public class DefaultTbClusterService implements TbClusterService {
.setPartitions(queue.getPartitions())
.build();
if ("Main".equals(queue.getName())) {
Set<TransportProtos.ServiceInfo> tbTransportServices = partitionService.getAllServices(ServiceType.TB_TRANSPORT);
ToTransportMsg transportMsg = ToTransportMsg.newBuilder().setQueueUpdateMsg(queueUpdateMsg).build();
for (TransportProtos.ServiceInfo transportService : tbTransportServices) {
TopicPartitionInfo tpi = notificationsTopicService.getNotificationsTopic(ServiceType.TB_TRANSPORT, transportService.getServiceId());
producerProvider.getTransportNotificationsMsgProducer().send(tpi, new TbProtoQueueMsg<>(UUID.randomUUID(), transportMsg), callback);
toTransportNfs.incrementAndGet();
}
Set<TransportProtos.ServiceInfo> tbCoreServices = partitionService.getAllServices(ServiceType.TB_CORE);
ToCoreNotificationMsg coreMsg = ToCoreNotificationMsg.newBuilder().setQueueUpdateMsg(queueUpdateMsg).build();
for (TransportProtos.ServiceInfo coreService : tbCoreServices) {
TopicPartitionInfo tpi = notificationsTopicService.getNotificationsTopic(ServiceType.TB_CORE, coreService.getServiceId());
producerProvider.getTbCoreNotificationsMsgProducer().send(tpi, new TbProtoQueueMsg<>(UUID.randomUUID(), coreMsg), callback);
toCoreNfs.incrementAndGet();
}
} else {
Set<TransportProtos.ServiceInfo> tbRuleEngineServices = partitionService.getAllServices(ServiceType.TB_RULE_ENGINE);
ToRuleEngineNotificationMsg ruleEngineMsg = ToRuleEngineNotificationMsg.newBuilder().setQueueUpdateMsg(queueUpdateMsg).build();
for (TransportProtos.ServiceInfo ruleEngineService : tbRuleEngineServices) {
TenantId tenantId = new TenantId(new UUID(ruleEngineService.getTenantIdMSB(), ruleEngineService.getTenantIdLSB()));
if (tenantId.equals(queue.getTenantId())) {
TopicPartitionInfo tpi = notificationsTopicService.getNotificationsTopic(ServiceType.TB_RULE_ENGINE, ruleEngineService.getServiceId());
producerProvider.getRuleEngineNotificationsMsgProducer().send(tpi, new TbProtoQueueMsg<>(UUID.randomUUID(), ruleEngineMsg), callback);
toRuleEngineNfs.incrementAndGet();
}
}
}
ToTransportMsg transportMsg = ToTransportMsg.newBuilder().setQueueUpdateMsg(queueUpdateMsg).build();
ToCoreNotificationMsg coreMsg = ToCoreNotificationMsg.newBuilder().setQueueUpdateMsg(queueUpdateMsg).build();
ToRuleEngineNotificationMsg ruleEngineMsg = ToRuleEngineNotificationMsg.newBuilder().setQueueUpdateMsg(queueUpdateMsg).build();
doSendQueueNotifications(transportMsg, coreMsg, ruleEngineMsg);
}
@Override
public void onQueueDelete(Queue queue, TbQueueCallback callback) {
public void onQueueDelete(Queue queue) {
log.trace("[{}][{}] Processing queue delete [{}] event", queue.getTenantId(), queue.getId(), queue.getName());
TransportProtos.QueueDeleteMsg queueDeleteMsg = TransportProtos.QueueDeleteMsg.newBuilder()
@ -545,33 +521,32 @@ public class DefaultTbClusterService implements TbClusterService {
.setQueueName(queue.getName())
.build();
if ("Main".equals(queue.getName())) {
Set<TransportProtos.ServiceInfo> tbTransportServices = partitionService.getAllServices(ServiceType.TB_TRANSPORT);
ToTransportMsg transportMsg = ToTransportMsg.newBuilder().setQueueDeleteMsg(queueDeleteMsg).build();
for (TransportProtos.ServiceInfo transportService : tbTransportServices) {
TopicPartitionInfo tpi = notificationsTopicService.getNotificationsTopic(ServiceType.TB_TRANSPORT, transportService.getServiceId());
producerProvider.getTransportNotificationsMsgProducer().send(tpi, new TbProtoQueueMsg<>(UUID.randomUUID(), transportMsg), callback);
toTransportNfs.incrementAndGet();
}
ToTransportMsg transportMsg = ToTransportMsg.newBuilder().setQueueDeleteMsg(queueDeleteMsg).build();
ToCoreNotificationMsg coreMsg = ToCoreNotificationMsg.newBuilder().setQueueDeleteMsg(queueDeleteMsg).build();
ToRuleEngineNotificationMsg ruleEngineMsg = ToRuleEngineNotificationMsg.newBuilder().setQueueDeleteMsg(queueDeleteMsg).build();
doSendQueueNotifications(transportMsg, coreMsg, ruleEngineMsg);
}
Set<TransportProtos.ServiceInfo> tbCoreServices = partitionService.getAllServices(ServiceType.TB_CORE);
ToCoreNotificationMsg coreMsg = ToCoreNotificationMsg.newBuilder().setQueueDeleteMsg(queueDeleteMsg).build();
for (TransportProtos.ServiceInfo coreService : tbCoreServices) {
TopicPartitionInfo tpi = notificationsTopicService.getNotificationsTopic(ServiceType.TB_CORE, coreService.getServiceId());
producerProvider.getTbCoreNotificationsMsgProducer().send(tpi, new TbProtoQueueMsg<>(UUID.randomUUID(), coreMsg), callback);
toCoreNfs.incrementAndGet();
}
} else {
Set<TransportProtos.ServiceInfo> tbRuleEngineServices = partitionService.getAllServices(ServiceType.TB_RULE_ENGINE);
ToRuleEngineNotificationMsg ruleEngineMsg = ToRuleEngineNotificationMsg.newBuilder().setQueueDeleteMsg(queueDeleteMsg).build();
for (TransportProtos.ServiceInfo ruleEngineService : tbRuleEngineServices) {
TenantId tenantId = new TenantId(new UUID(ruleEngineService.getTenantIdMSB(), ruleEngineService.getTenantIdLSB()));
if (tenantId.equals(queue.getTenantId())) {
TopicPartitionInfo tpi = notificationsTopicService.getNotificationsTopic(ServiceType.TB_RULE_ENGINE, ruleEngineService.getServiceId());
producerProvider.getRuleEngineNotificationsMsgProducer().send(tpi, new TbProtoQueueMsg<>(UUID.randomUUID(), ruleEngineMsg), callback);
toRuleEngineNfs.incrementAndGet();
}
}
private void doSendQueueNotifications(ToTransportMsg transportMsg, ToCoreNotificationMsg coreMsg, ToRuleEngineNotificationMsg ruleEngineMsg) {
Set<TransportProtos.ServiceInfo> tbTransportServices = partitionService.getAllServices(ServiceType.TB_TRANSPORT);
for (TransportProtos.ServiceInfo transportService : tbTransportServices) {
TopicPartitionInfo tpi = notificationsTopicService.getNotificationsTopic(ServiceType.TB_TRANSPORT, transportService.getServiceId());
producerProvider.getTransportNotificationsMsgProducer().send(tpi, new TbProtoQueueMsg<>(UUID.randomUUID(), transportMsg), null);
toTransportNfs.incrementAndGet();
}
Set<TransportProtos.ServiceInfo> tbCoreServices = partitionService.getAllServices(ServiceType.TB_CORE);
for (TransportProtos.ServiceInfo coreService : tbCoreServices) {
TopicPartitionInfo tpi = notificationsTopicService.getNotificationsTopic(ServiceType.TB_CORE, coreService.getServiceId());
producerProvider.getTbCoreNotificationsMsgProducer().send(tpi, new TbProtoQueueMsg<>(UUID.randomUUID(), coreMsg), null);
toCoreNfs.incrementAndGet();
}
Set<TransportProtos.ServiceInfo> tbRuleEngineServices = partitionService.getAllServices(ServiceType.TB_RULE_ENGINE);
for (TransportProtos.ServiceInfo ruleEngineService : tbRuleEngineServices) {
TopicPartitionInfo tpi = notificationsTopicService.getNotificationsTopic(ServiceType.TB_RULE_ENGINE, ruleEngineService.getServiceId());
producerProvider.getRuleEngineNotificationsMsgProducer().send(tpi, new TbProtoQueueMsg<>(UUID.randomUUID(), ruleEngineMsg), null);
toRuleEngineNfs.incrementAndGet();
}
}
}

79
application/src/main/java/org/thingsboard/server/service/queue/DefaultTbRuleEngineConsumerService.java

@ -30,7 +30,6 @@ import org.thingsboard.server.common.msg.TbMsg;
import org.thingsboard.server.common.msg.queue.QueueToRuleEngineMsg;
import org.thingsboard.server.common.msg.queue.RuleEngineException;
import org.thingsboard.server.common.msg.queue.RuleNodeInfo;
import org.thingsboard.server.common.msg.queue.ServiceQueue;
import org.thingsboard.server.common.msg.queue.ServiceType;
import org.thingsboard.server.common.msg.queue.TbCallback;
import org.thingsboard.server.common.msg.queue.TbMsgCallback;
@ -46,6 +45,7 @@ import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineNotifica
import org.thingsboard.server.queue.TbQueueConsumer;
import org.thingsboard.server.queue.common.TbProtoQueueMsg;
import org.thingsboard.server.queue.discovery.PartitionService;
import org.thingsboard.server.queue.discovery.QueueKey;
import org.thingsboard.server.queue.discovery.TbServiceInfoProvider;
import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent;
import org.thingsboard.server.queue.provider.TbRuleEngineQueueFactory;
@ -105,11 +105,11 @@ public class DefaultTbRuleEngineConsumerService extends AbstractConsumerService<
private final PartitionService partitionService;
private final TbServiceInfoProvider serviceInfoProvider;
private final QueueService queueService;
private final TenantId tenantId;
private final ConcurrentMap<String, TbQueueConsumer<TbProtoQueueMsg<ToRuleEngineMsg>>> consumers = new ConcurrentHashMap<>();
private final ConcurrentMap<String, Queue> consumerConfigurations = new ConcurrentHashMap<>();
private final ConcurrentMap<String, TbRuleEngineConsumerStats> consumerStats = new ConcurrentHashMap<>();
private final ConcurrentMap<String, TbTopicWithConsumerPerPartition> topicsConsumerPerPartition = new ConcurrentHashMap<>();
// private final TenantId tenantId;
private final ConcurrentMap<QueueKey, TbQueueConsumer<TbProtoQueueMsg<ToRuleEngineMsg>>> consumers = new ConcurrentHashMap<>();
private final ConcurrentMap<QueueKey, Queue> consumerConfigurations = new ConcurrentHashMap<>();
private final ConcurrentMap<QueueKey, TbRuleEngineConsumerStats> consumerStats = new ConcurrentHashMap<>();
private final ConcurrentMap<QueueKey, TbTopicWithConsumerPerPartition> topicsConsumerPerPartition = new ConcurrentHashMap<>();
final ExecutorService submitExecutor = Executors.newSingleThreadExecutor(ThingsBoardThreadFactory.forName("tb-rule-engine-consumer-submit"));
final ScheduledExecutorService repartitionExecutor = Executors.newScheduledThreadPool(1, ThingsBoardThreadFactory.forName("tb-rule-engine-consumer-repartition"));
@ -135,25 +135,26 @@ public class DefaultTbRuleEngineConsumerService extends AbstractConsumerService<
this.partitionService = partitionService;
this.serviceInfoProvider = serviceInfoProvider;
this.queueService = queueService;
this.tenantId = actorContext.getServiceInfoProvider().getIsolatedTenant().orElse(TenantId.SYS_TENANT_ID);
// this.tenantId = actorContext.getServiceInfoProvider().getIsolatedTenant().orElse(TenantId.SYS_TENANT_ID);
}
@PostConstruct
public void init() {
super.init("tb-rule-engine-consumer", "tb-rule-engine-notifications-consumer");
List<Queue> queues = queueService.findQueuesByTenantId(tenantId);
List<Queue> queues = queueService.findAllQueues();
for (Queue configuration : queues) {
initConsumer(configuration);
}
}
private void initConsumer(Queue configuration) {
consumerConfigurations.putIfAbsent(configuration.getName(), configuration);
consumerStats.putIfAbsent(configuration.getName(), new TbRuleEngineConsumerStats(configuration.getName(), statsFactory));
QueueKey queueKey = new QueueKey(ServiceType.TB_RULE_ENGINE, configuration);
consumerConfigurations.putIfAbsent(queueKey, configuration);
consumerStats.putIfAbsent(queueKey, new TbRuleEngineConsumerStats(configuration.getName(), statsFactory));
if (!configuration.isConsumerPerPartition()) {
consumers.computeIfAbsent(configuration.getName(), queueName -> tbRuleEngineQueueFactory.createToRuleEngineMsgConsumer(configuration));
consumers.computeIfAbsent(queueKey, queueName -> tbRuleEngineQueueFactory.createToRuleEngineMsgConsumer(configuration));
} else {
topicsConsumerPerPartition.computeIfAbsent(configuration.getName(), TbTopicWithConsumerPerPartition::new);
topicsConsumerPerPartition.computeIfAbsent(queueKey, k -> new TbTopicWithConsumerPerPartition(k.getQueueName()));
}
}
@ -167,31 +168,31 @@ public class DefaultTbRuleEngineConsumerService extends AbstractConsumerService<
@Override
protected void onTbApplicationEvent(PartitionChangeEvent event) {
if (event.getServiceType().equals(getServiceType())) {
ServiceQueue serviceQueue = event.getServiceQueueKey().getServiceQueue();
log.info("[{}] Subscribing to partitions: {}", serviceQueue.getQueue(), event.getPartitions());
if (!consumerConfigurations.get(serviceQueue.getQueue()).isConsumerPerPartition()) {
consumers.get(serviceQueue.getQueue()).subscribe(event.getPartitions());
String serviceQueue = event.getQueueKey().getQueueName();
log.info("[{}] Subscribing to partitions: {}", serviceQueue, event.getPartitions());
if (!consumerConfigurations.get(event.getQueueKey()).isConsumerPerPartition()) {
consumers.get(event.getQueueKey()).subscribe(event.getPartitions());
} else {
log.info("[{}] Subscribing consumer per partition: {}", serviceQueue.getQueue(), event.getPartitions());
subscribeConsumerPerPartition(serviceQueue.getQueue(), event.getPartitions());
log.info("[{}] Subscribing consumer per partition: {}", serviceQueue, event.getPartitions());
subscribeConsumerPerPartition(event.getQueueKey(), event.getPartitions());
}
}
}
void subscribeConsumerPerPartition(String queue, Set<TopicPartitionInfo> partitions) {
void subscribeConsumerPerPartition(QueueKey queue, Set<TopicPartitionInfo> partitions) {
topicsConsumerPerPartition.get(queue).getSubscribeQueue().add(partitions);
scheduleTopicRepartition(queue);
}
private void scheduleTopicRepartition(String queue) {
private void scheduleTopicRepartition(QueueKey queue) {
repartitionExecutor.schedule(() -> repartitionTopicWithConsumerPerPartition(queue), 1, TimeUnit.SECONDS);
}
void repartitionTopicWithConsumerPerPartition(final String queueName) {
void repartitionTopicWithConsumerPerPartition(final QueueKey queueKey) {
if (stopped) {
return;
}
TbTopicWithConsumerPerPartition tbTopicWithConsumerPerPartition = topicsConsumerPerPartition.get(queueName);
TbTopicWithConsumerPerPartition tbTopicWithConsumerPerPartition = topicsConsumerPerPartition.get(queueKey);
java.util.Queue<Set<TopicPartitionInfo>> subscribeQueue = tbTopicWithConsumerPerPartition.getSubscribeQueue();
if (subscribeQueue.isEmpty()) {
return;
@ -216,15 +217,15 @@ public class DefaultTbRuleEngineConsumerService extends AbstractConsumerService<
log.info("calculated removedPartitions {}", removedPartitions);
removedPartitions.forEach((tpi) -> {
removeConsumerForTopicByTpi(queueName, consumers, tpi);
removeConsumerForTopicByTpi(queueKey.getQueueName(), consumers, tpi);
});
addedPartitions.forEach((tpi) -> {
log.info("[{}] Adding consumer for topic: {}", queueName, tpi);
Queue configuration = consumerConfigurations.get(queueName);
log.info("[{}] Adding consumer for topic: {}", queueKey, tpi);
Queue configuration = consumerConfigurations.get(queueKey);
TbQueueConsumer<TbProtoQueueMsg<ToRuleEngineMsg>> consumer = tbRuleEngineQueueFactory.createToRuleEngineMsgConsumer(configuration);
consumers.put(tpi, consumer);
launchConsumer(consumer, consumerConfigurations.get(queueName), consumerStats.get(queueName), "" + queueName + "-" + tpi.getPartition().orElse(-999999));
launchConsumer(consumer, consumerConfigurations.get(queueKey), consumerStats.get(queueKey), "" + queueKey + "-" + tpi.getPartition().orElse(-999999));
consumer.subscribe(Collections.singleton(tpi));
});
@ -232,7 +233,7 @@ public class DefaultTbRuleEngineConsumerService extends AbstractConsumerService<
tbTopicWithConsumerPerPartition.getLock().unlock();
}
} else {
scheduleTopicRepartition(queueName); //reschedule later
scheduleTopicRepartition(queueKey); //reschedule later
}
}
@ -245,7 +246,7 @@ public class DefaultTbRuleEngineConsumerService extends AbstractConsumerService<
@Override
protected void launchMainConsumers() {
consumers.forEach((queue, consumer) -> launchConsumer(consumer, consumerConfigurations.get(queue), consumerStats.get(queue), queue));
consumers.forEach((queue, consumer) -> launchConsumer(consumer, consumerConfigurations.get(queue), consumerStats.get(queue), queue.getQueueName()));
}
@Override
@ -408,11 +409,14 @@ public class DefaultTbRuleEngineConsumerService extends AbstractConsumerService<
private void updateQueue(TransportProtos.QueueUpdateMsg queueUpdateMsg) {
String queueName = queueUpdateMsg.getQueueName();
Queue queue = queueService.findQueueByTenantIdAndName(tenantId, queueName);
Queue oldQueue = consumerConfigurations.remove(queueName);
TenantId tenantId = new TenantId(new UUID(queueUpdateMsg.getTenantIdMSB(), queueUpdateMsg.getTenantIdLSB()));
QueueId queueId = new QueueId(new UUID(queueUpdateMsg.getQueueIdMSB(), queueUpdateMsg.getQueueIdLSB()));
QueueKey queueKey = new QueueKey(ServiceType.TB_RULE_ENGINE, queueUpdateMsg.getQueueName(), tenantId);
Queue queue = queueService.findQueueById(tenantId, queueId);
Queue oldQueue = consumerConfigurations.remove(queueKey);
if (oldQueue != null) {
if (oldQueue.isConsumerPerPartition()) {
TbTopicWithConsumerPerPartition consumerPerPartition = topicsConsumerPerPartition.remove(queueName);
TbTopicWithConsumerPerPartition consumerPerPartition = topicsConsumerPerPartition.remove(queueKey);
ReentrantLock lock = consumerPerPartition.getLock();
try {
lock.lock();
@ -421,7 +425,7 @@ public class DefaultTbRuleEngineConsumerService extends AbstractConsumerService<
lock.unlock();
}
} else {
TbQueueConsumer<TbProtoQueueMsg<ToRuleEngineMsg>> consumer = consumers.remove(queueName);
TbQueueConsumer<TbProtoQueueMsg<ToRuleEngineMsg>> consumer = consumers.remove(queueKey);
consumer.unsubscribe();
}
}
@ -429,7 +433,7 @@ public class DefaultTbRuleEngineConsumerService extends AbstractConsumerService<
initConsumer(queue);
if (!queue.isConsumerPerPartition()) {
launchConsumer(consumers.get(queueName), consumerConfigurations.get(queueName), consumerStats.get(queueName), queueName);
launchConsumer(consumers.get(queueKey), consumerConfigurations.get(queueKey), consumerStats.get(queueKey), queueName);
}
partitionService.updateQueue(queueUpdateMsg);
@ -437,16 +441,19 @@ public class DefaultTbRuleEngineConsumerService extends AbstractConsumerService<
}
private void deleteQueue(TransportProtos.QueueDeleteMsg queueDeleteMsg) {
Queue queue = consumerConfigurations.remove(queueDeleteMsg.getQueueName());
TenantId tenantId = new TenantId(new UUID(queueDeleteMsg.getTenantIdMSB(), queueDeleteMsg.getTenantIdLSB()));
QueueKey queueKey = new QueueKey(ServiceType.TB_RULE_ENGINE, queueDeleteMsg.getQueueName(), tenantId);
Queue queue = consumerConfigurations.remove(queueKey);
if (queue != null) {
if (queue.isConsumerPerPartition()) {
TbTopicWithConsumerPerPartition tbTopicWithConsumerPerPartition = topicsConsumerPerPartition.remove(queueDeleteMsg.getQueueName());
TbTopicWithConsumerPerPartition tbTopicWithConsumerPerPartition = topicsConsumerPerPartition.remove(queueKey);
if (tbTopicWithConsumerPerPartition != null) {
tbTopicWithConsumerPerPartition.getConsumers().values().forEach(TbQueueConsumer::unsubscribe);
tbTopicWithConsumerPerPartition.getConsumers().clear();
}
} else {
TbQueueConsumer<TbProtoQueueMsg<ToRuleEngineMsg>> consumer = consumers.remove(queueDeleteMsg.getQueueName());
TbQueueConsumer<TbProtoQueueMsg<ToRuleEngineMsg>> consumer = consumers.remove(queueKey);
if (consumer != null) {
consumer.unsubscribe();
}

1
application/src/main/resources/thingsboard.yml

@ -1094,7 +1094,6 @@ service:
type: "${TB_SERVICE_TYPE:monolith}" # monolith or tb-core or tb-rule-engine
# Unique id for this service (autogenerated if empty)
id: "${TB_SERVICE_ID:}"
tenant_id: "${TB_SERVICE_TENANT_ID:}" # empty or specific tenant id.
metrics:
# Enable/disable actuator metrics.

8
application/src/test/java/org/thingsboard/server/service/cluster/routing/HashPartitionServiceTest.java

@ -74,8 +74,8 @@ public class HashPartitionServiceTest {
ReflectionTestUtils.setField(clusterRoutingService, "hashFunctionName", hashFunctionName);
TransportProtos.ServiceInfo currentServer = TransportProtos.ServiceInfo.newBuilder()
.setServiceId("tb-core-0")
.setTenantIdMSB(TenantId.NULL_UUID.getMostSignificantBits())
.setTenantIdLSB(TenantId.NULL_UUID.getLeastSignificantBits())
// .setTenantIdMSB(TenantId.NULL_UUID.getMostSignificantBits())
// .setTenantIdLSB(TenantId.NULL_UUID.getLeastSignificantBits())
.addAllServiceTypes(Collections.singletonList(ServiceType.TB_CORE.name()))
.build();
// when(queueService.resolve(Mockito.any(), Mockito.anyString())).thenAnswer(i -> i.getArguments()[1]);
@ -84,8 +84,8 @@ public class HashPartitionServiceTest {
for (int i = 1; i < SERVER_COUNT; i++) {
otherServers.add(TransportProtos.ServiceInfo.newBuilder()
.setServiceId("tb-rule-" + i)
.setTenantIdMSB(TenantId.NULL_UUID.getMostSignificantBits())
.setTenantIdLSB(TenantId.NULL_UUID.getLeastSignificantBits())
// .setTenantIdMSB(TenantId.NULL_UUID.getMostSignificantBits())
// .setTenantIdLSB(TenantId.NULL_UUID.getLeastSignificantBits())
.addAllServiceTypes(Collections.singletonList(ServiceType.TB_CORE.name()))
.build());
}

4
common/cluster-api/src/main/java/org/thingsboard/server/queue/TbQueueClusterService.java

@ -18,7 +18,7 @@ package org.thingsboard.server.queue;
import org.thingsboard.server.common.data.queue.Queue;
public interface TbQueueClusterService {
void onQueueChange(Queue queue, TbQueueCallback callback);
void onQueueChange(Queue queue);
void onQueueDelete(Queue queue, TbQueueCallback callback);
void onQueueDelete(Queue queue);
}

4
common/cluster-api/src/main/proto/queue.proto

@ -32,8 +32,8 @@ option java_outer_classname = "TransportProtos";
message ServiceInfo {
string serviceId = 1;
repeated string serviceTypes = 2;
int64 tenantIdMSB = 3;
int64 tenantIdLSB = 4;
// int64 tenantIdMSB = 3;
// int64 tenantIdLSB = 4;
// repeated QueueInfo ruleEngineQueues = 5;
repeated string transports = 6;
}

4
common/dao-api/src/main/java/org/thingsboard/server/dao/queue/QueueService.java

@ -30,12 +30,12 @@ public interface QueueService {
void deleteQueue(TenantId tenantId, QueueId queueId);
void deleteQueueByQueueName(TenantId tenantId, String queueName);
List<Queue> findQueuesByTenantId(TenantId tenantId);
PageData<Queue> findQueuesByTenantId(TenantId tenantId, PageLink pageLink);
List<Queue> findAllMainQueues();
List<Queue> findAllQueues();
Queue findQueueById(TenantId tenantId, QueueId queueId);

13
common/data/src/main/java/org/thingsboard/server/common/data/queue/Queue.java

@ -21,6 +21,7 @@ import org.thingsboard.server.common.data.HasName;
import org.thingsboard.server.common.data.HasTenantId;
import org.thingsboard.server.common.data.id.QueueId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.tenant.profile.TenantProfileQueueConfiguration;
@Data
public class Queue extends BaseData<QueueId> implements HasName, HasTenantId {
@ -40,4 +41,16 @@ public class Queue extends BaseData<QueueId> implements HasName, HasTenantId {
public Queue(QueueId id) {
super(id);
}
public Queue(TenantId tenantId, TenantProfileQueueConfiguration queueConfiguration) {
this.tenantId = tenantId;
this.name = queueConfiguration.getName();
this.topic = queueConfiguration.getTopic();
this.pollInterval = queueConfiguration.getPollInterval();
this.partitions = queueConfiguration.getPartitions();
this.consumerPerPartition = queueConfiguration.isConsumerPerPartition();
this.packProcessingTimeout = queueConfiguration.getPackProcessingTimeout();
this.submitStrategy = queueConfiguration.getSubmitStrategy();
this.processingStrategy = queueConfiguration.getProcessingStrategy();
}
}

5
common/data/src/main/java/org/thingsboard/server/common/data/tenant/profile/TenantProfileData.java

@ -19,6 +19,8 @@ import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.List;
@ApiModel
@Data
public class TenantProfileData {
@ -26,4 +28,7 @@ public class TenantProfileData {
@ApiModelProperty(position = 1, value = "Complex JSON object that contains profile settings: max devices, max assets, rate limits, etc.")
private TenantProfileConfiguration configuration;
@ApiModelProperty(position = 2, value = "JSON array of queue configuration per tenant profile")
private List<TenantProfileQueueConfiguration> queueConfiguration;
}

32
common/data/src/main/java/org/thingsboard/server/common/data/tenant/profile/TenantProfileQueueConfiguration.java

@ -0,0 +1,32 @@
/**
* Copyright © 2016-2022 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.common.data.tenant.profile;
import lombok.Data;
import org.thingsboard.server.common.data.queue.ProcessingStrategy;
import org.thingsboard.server.common.data.queue.SubmitStrategy;
@Data
public class TenantProfileQueueConfiguration {
private String name;
private String topic;
private int pollInterval;
private int partitions;
private boolean consumerPerPartition;
private long packProcessingTimeout;
private SubmitStrategy submitStrategy;
private ProcessingStrategy processingStrategy;
}

2
common/message/src/main/java/org/thingsboard/server/common/msg/queue/PartitionChangeMsg.java

@ -29,7 +29,7 @@ import java.util.Set;
public final class PartitionChangeMsg implements TbActorMsg {
@Getter
private final ServiceQueueKey serviceQueueKey;
private final ServiceType serviceType;
@Getter
private final Set<TopicPartitionInfo> partitions;

14
common/message/src/main/java/org/thingsboard/server/common/msg/queue/ServiceQueueKey.java

@ -17,7 +17,6 @@ package org.thingsboard.server.common.msg.queue;
import lombok.Getter;
import lombok.ToString;
import org.thingsboard.server.common.data.id.TenantId;
import java.util.Objects;
@ -26,12 +25,8 @@ public class ServiceQueueKey {
@Getter
private final ServiceQueue serviceQueue;
@Getter
private final TenantId tenantId;
public ServiceQueueKey(ServiceQueue serviceQueue, TenantId tenantId) {
public ServiceQueueKey(ServiceQueue serviceQueue) {
this.serviceQueue = serviceQueue;
this.tenantId = tenantId;
}
@Override
@ -39,16 +34,15 @@ public class ServiceQueueKey {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ServiceQueueKey that = (ServiceQueueKey) o;
return serviceQueue.equals(that.serviceQueue) &&
Objects.equals(tenantId, that.tenantId);
return serviceQueue.equals(that.serviceQueue);
}
@Override
public int hashCode() {
return Objects.hash(serviceQueue, tenantId);
return Objects.hash(serviceQueue);
}
public ServiceType getServiceType() {
return serviceQueue.getType();
}
}
}

20
common/queue/src/main/java/org/thingsboard/server/queue/discovery/DefaultTbServiceInfoProvider.java

@ -23,10 +23,8 @@ import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.thingsboard.server.common.data.TbTransportService;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.msg.queue.ServiceType;
import org.thingsboard.server.gen.transport.TransportProtos.ServiceInfo;
import org.thingsboard.server.queue.settings.TbQueueRuleEngineSettings;
import org.thingsboard.server.queue.util.AfterContextReady;
import javax.annotation.PostConstruct;
@ -36,8 +34,6 @@ import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
import java.util.stream.Collectors;
@Component
@ -56,14 +52,11 @@ public class DefaultTbServiceInfoProvider implements TbServiceInfoProvider {
@Value("${service.tenant_id:}")
private String tenantIdStr;
@Autowired(required = false)
private TbQueueRuleEngineSettings ruleEngineSettings;
@Autowired
private ApplicationContext applicationContext;
private List<ServiceType> serviceTypes;
private ServiceInfo serviceInfo;
private TenantId isolatedTenant;
@PostConstruct
public void init() {
@ -83,15 +76,6 @@ public class DefaultTbServiceInfoProvider implements TbServiceInfoProvider {
ServiceInfo.Builder builder = ServiceInfo.newBuilder()
.setServiceId(serviceId)
.addAllServiceTypes(serviceTypes.stream().map(ServiceType::name).collect(Collectors.toList()));
UUID tenantId;
if (!StringUtils.isEmpty(tenantIdStr)) {
tenantId = UUID.fromString(tenantIdStr);
isolatedTenant = TenantId.fromUUID(tenantId);
} else {
tenantId = TenantId.NULL_UUID;
}
builder.setTenantIdMSB(tenantId.getMostSignificantBits());
builder.setTenantIdLSB(tenantId.getLeastSignificantBits());
serviceInfo = builder.build();
}
@ -119,8 +103,4 @@ public class DefaultTbServiceInfoProvider implements TbServiceInfoProvider {
return serviceTypes.contains(serviceType);
}
@Override
public Optional<TenantId> getIsolatedTenant() {
return Optional.ofNullable(isolatedTenant);
}
}

161
common/queue/src/main/java/org/thingsboard/server/queue/discovery/HashPartitionService.java

@ -63,13 +63,15 @@ public class HashPartitionService implements PartitionService {
private final TbServiceInfoProvider serviceInfoProvider;
private final TenantRoutingInfoService tenantRoutingInfoService;
private final QueueRoutingInfoService queueRoutingInfoService;
private final ConcurrentMap<QueueId, String> queueNames = new ConcurrentHashMap<>();
private final ConcurrentMap<TenantId, ConcurrentMap<ServiceQueue, String>> partitionTopicsMap = new ConcurrentHashMap<>();
private final ConcurrentMap<TenantId, ConcurrentMap<ServiceQueue, Integer>> partitionSizesMap = new ConcurrentHashMap<>();
private final ConcurrentMap<TenantId, TenantRoutingInfo> tenantRoutingInfoMap = new ConcurrentHashMap<>();
private ConcurrentMap<ServiceQueueKey, List<Integer>> myPartitions = new ConcurrentHashMap<>();
private ConcurrentMap<TopicPartitionInfoKey, TopicPartitionInfo> tpiCache = new ConcurrentHashMap<>();
private final ConcurrentMap<QueueId, QueueRoutingInfo> queuesById = new ConcurrentHashMap<>();
private ConcurrentMap<QueueKey, List<Integer>> myPartitions = new ConcurrentHashMap<>();
private final ConcurrentMap<QueueKey, String> partitionTopicsMap = new ConcurrentHashMap<>();
private final ConcurrentMap<QueueKey, Integer> partitionSizesMap = new ConcurrentHashMap<>();
private final ConcurrentMap<TenantId, TenantRoutingInfo> tenantRoutingInfoMap = new ConcurrentHashMap<>();
private Map<String, List<ServiceInfo>> tbTransportServicesByType = new HashMap<>();
private List<ServiceInfo> currentOtherServices;
@ -93,8 +95,9 @@ public class HashPartitionService implements PartitionService {
}
private void partitionsInit() {
addPartitionSizeToMap(TenantId.SYS_TENANT_ID, new ServiceQueue(ServiceType.TB_CORE), corePartitions);
addPartitionTopicToMap(TenantId.SYS_TENANT_ID, new ServiceQueue(ServiceType.TB_CORE), coreTopic);
QueueKey coreKey = new QueueKey(ServiceType.TB_CORE);
partitionSizesMap.put(coreKey, corePartitions);
partitionTopicsMap.put(coreKey, coreTopic);
List<QueueRoutingInfo> queueRoutingInfoList;
@ -127,69 +130,59 @@ public class HashPartitionService implements PartitionService {
}
queueRoutingInfoList.forEach(queue -> {
addPartitionTopicToMap(queue.getTenantId(), new ServiceQueue(ServiceType.TB_RULE_ENGINE, queue.getQueueName()), queue.getQueueTopic());
addPartitionSizeToMap(queue.getTenantId(), new ServiceQueue(ServiceType.TB_RULE_ENGINE, queue.getQueueName()), queue.getPartitions());
queueNames.put(queue.getQueueId(), queue.getQueueName());
QueueKey queueKey = new QueueKey(ServiceType.TB_RULE_ENGINE, queue);
partitionTopicsMap.put(queueKey, queue.getQueueTopic());
partitionSizesMap.put(queueKey, queue.getPartitions());
queuesById.put(queue.getQueueId(), queue);
});
}
@Override
public void updateQueue(TransportProtos.QueueUpdateMsg queueUpdateMsg) {
TenantId tenantId = new TenantId(new UUID(queueUpdateMsg.getTenantIdMSB(), queueUpdateMsg.getTenantIdLSB()));
addPartitionTopicToMap(tenantId, new ServiceQueue(ServiceType.TB_RULE_ENGINE, queueUpdateMsg.getQueueName()), queueUpdateMsg.getQueueTopic());
addPartitionSizeToMap(tenantId, new ServiceQueue(ServiceType.TB_RULE_ENGINE, queueUpdateMsg.getQueueName()), queueUpdateMsg.getPartitions());
queueNames.putIfAbsent(new QueueId(new UUID(queueUpdateMsg.getQueueIdMSB(), queueUpdateMsg.getQueueIdLSB())), queueUpdateMsg.getQueueName());
tpiCache.clear();
QueueKey queueKey = new QueueKey(ServiceType.TB_RULE_ENGINE, queueUpdateMsg.getQueueName(), tenantId);
partitionTopicsMap.put(queueKey, queueUpdateMsg.getQueueTopic());
partitionSizesMap.put(queueKey, queueUpdateMsg.getPartitions());
QueueRoutingInfo queue = new QueueRoutingInfo(queueUpdateMsg);
queuesById.put(queue.getQueueId(), queue);
}
@Override
public void removeQueue(TransportProtos.QueueDeleteMsg queueDeleteMsg) {
TenantId tenantId = new TenantId(new UUID(queueDeleteMsg.getTenantIdMSB(), queueDeleteMsg.getTenantIdLSB()));
ServiceQueue serviceQueue = new ServiceQueue(ServiceType.TB_RULE_ENGINE, queueDeleteMsg.getQueueName());
partitionTopicsMap.get(tenantId).remove(serviceQueue);
partitionSizesMap.get(tenantId).remove(serviceQueue);
myPartitions.remove(new ServiceQueueKey(serviceQueue, tenantId));
queueNames.remove(new QueueId(new UUID(queueDeleteMsg.getQueueIdMSB(), queueDeleteMsg.getQueueIdLSB())));
tpiCache.clear();
}
private void addPartitionSizeToMap(TenantId tenantId, ServiceQueue serviceQueue, int partitions) {
partitionSizesMap.computeIfAbsent(tenantId, id -> new ConcurrentHashMap<>()).put(serviceQueue, partitions);
}
private void addPartitionTopicToMap(TenantId tenantId, ServiceQueue serviceQueue, String topic) {
partitionTopicsMap.computeIfAbsent(tenantId, id -> new ConcurrentHashMap<>()).put(serviceQueue, topic);
QueueKey queueKey = new QueueKey(ServiceType.TB_RULE_ENGINE, queueDeleteMsg.getQueueName(), tenantId);
partitionTopicsMap.remove(queueKey);
partitionSizesMap.remove(queueKey);
queuesById.remove(new QueueId(new UUID(queueDeleteMsg.getQueueIdMSB(), queueDeleteMsg.getQueueIdLSB())));
myPartitions.remove(queueKey);
}
@Override
public TopicPartitionInfo resolve(ServiceType serviceType, TenantId tenantId, EntityId entityId) {
return resolve(new ServiceQueue(serviceType), tenantId, entityId);
return resolve(serviceType, null, tenantId, entityId);
}
@Override
public TopicPartitionInfo resolve(ServiceType serviceType, QueueId queueId, TenantId tenantId, EntityId entityId) {
String queueName;
QueueKey queueKey;
if (queueId == null) {
queueName = ServiceQueue.MAIN;
queueKey = isIsolated(serviceType, tenantId) ? new QueueKey(serviceType, tenantId) : new QueueKey(serviceType);
} else {
queueName = queueNames.get(queueId);
queueKey = new QueueKey(serviceType, queuesById.get(queueId));
}
ServiceQueue serviceQueue = new ServiceQueue(serviceType, queueName);
return resolve(serviceQueue, tenantId, entityId);
return resolve(queueKey, entityId);
}
private TopicPartitionInfo resolve(ServiceQueue serviceQueue, TenantId tenantId, EntityId entityId) {
private TopicPartitionInfo resolve(QueueKey queueKey, EntityId entityId) {
int hash = hashFunction.newHasher()
.putLong(entityId.getId().getMostSignificantBits())
.putLong(entityId.getId().getLeastSignificantBits()).hash().asInt();
boolean isolatedTenant = isIsolated(serviceQueue.getType(), tenantId);
Integer partitionSize = partitionSizesMap.get(isolatedTenant ? tenantId : TenantId.SYS_TENANT_ID).get(serviceQueue);
Integer partitionSize = partitionSizesMap.get(queueKey);
int partition = Math.abs(hash % partitionSize);
TopicPartitionInfoKey cacheKey = new TopicPartitionInfoKey(serviceQueue, isolatedTenant ? tenantId : null, partition);
return tpiCache.computeIfAbsent(cacheKey, key -> buildTopicPartitionInfo(serviceQueue, tenantId, partition));
return buildTopicPartitionInfo(queueKey, partition);
}
@Override
@ -197,43 +190,39 @@ public class HashPartitionService implements PartitionService {
tbTransportServicesByType.clear();
logServiceInfo(currentService);
otherServices.forEach(this::logServiceInfo);
Map<ServiceQueueKey, List<ServiceInfo>> queueServicesMap = new HashMap<>();
Map<QueueKey, List<ServiceInfo>> queueServicesMap = new HashMap<>();
addNode(queueServicesMap, currentService);
for (ServiceInfo other : otherServices) {
addNode(queueServicesMap, other);
}
queueServicesMap.values().forEach(list -> list.sort(Comparator.comparing(ServiceInfo::getServiceId)));
ConcurrentMap<ServiceQueueKey, List<Integer>> oldPartitions = myPartitions;
TenantId myIsolatedOrSystemTenantId = getSystemOrIsolatedTenantId(currentService);
ConcurrentMap<QueueKey, List<Integer>> oldPartitions = myPartitions;
myPartitions = new ConcurrentHashMap<>();
partitionSizesMap.get(myIsolatedOrSystemTenantId).forEach((serviceQueue, size) -> {
ServiceQueueKey myServiceQueueKey = new ServiceQueueKey(serviceQueue, myIsolatedOrSystemTenantId);
partitionSizesMap.forEach((queueKey, size) -> {
for (int i = 0; i < size; i++) {
ServiceInfo serviceInfo = resolveByPartitionIdx(queueServicesMap.get(myServiceQueueKey), i);
ServiceInfo serviceInfo = resolveByPartitionIdx(queueServicesMap.get(queueKey), i);
if (currentService.equals(serviceInfo)) {
ServiceQueueKey serviceQueueKey = new ServiceQueueKey(serviceQueue, getSystemOrIsolatedTenantId(serviceInfo));
myPartitions.computeIfAbsent(serviceQueueKey, key -> new ArrayList<>()).add(i);
myPartitions.computeIfAbsent(queueKey, key -> new ArrayList<>()).add(i);
}
}
});
tpiCache.clear();
oldPartitions.forEach((serviceQueueKey, partitions) -> {
if (!myPartitions.containsKey(serviceQueueKey)) {
log.info("[{}] NO MORE PARTITIONS FOR CURRENT KEY", serviceQueueKey);
applicationEventPublisher.publishEvent(new PartitionChangeEvent(this, serviceQueueKey, Collections.emptySet()));
oldPartitions.forEach((queueKey, partitions) -> {
if (!myPartitions.containsKey(queueKey)) {
log.info("[{}] NO MORE PARTITIONS FOR CURRENT KEY", queueKey);
applicationEventPublisher.publishEvent(new PartitionChangeEvent(this, queueKey, Collections.emptySet()));
}
});
myPartitions.forEach((serviceQueueKey, partitions) -> {
if (!partitions.equals(oldPartitions.get(serviceQueueKey))) {
log.info("[{}] NEW PARTITIONS: {}", serviceQueueKey, partitions);
myPartitions.forEach((queueKey, partitions) -> {
if (!partitions.equals(oldPartitions.get(queueKey))) {
log.info("[{}] NEW PARTITIONS: {}", queueKey, partitions);
Set<TopicPartitionInfo> tpiList = partitions.stream()
.map(partition -> buildTopicPartitionInfo(serviceQueueKey, partition))
.map(partition -> buildTopicPartitionInfo(queueKey, partition))
.collect(Collectors.toSet());
applicationEventPublisher.publishEvent(new PartitionChangeEvent(this, serviceQueueKey, tpiList));
applicationEventPublisher.publishEvent(new PartitionChangeEvent(this, queueKey, tpiList));
}
});
@ -313,7 +302,7 @@ public class HashPartitionService implements PartitionService {
// currentMap.computeIfAbsent(serviceQueueKey, key -> new ArrayList<>()).add(serviceInfo);
// }
} else {
ServiceQueueKey serviceQueueKey = new ServiceQueueKey(new ServiceQueue(serviceType), getSystemOrIsolatedTenantId(serviceInfo));
ServiceQueueKey serviceQueueKey = new ServiceQueueKey(new ServiceQueue(serviceType));
currentMap.computeIfAbsent(serviceQueueKey, key -> new ArrayList<>()).add(serviceInfo);
}
}
@ -321,24 +310,13 @@ public class HashPartitionService implements PartitionService {
return currentMap;
}
private TopicPartitionInfo buildTopicPartitionInfo(ServiceQueueKey serviceQueueKey, int partition) {
return buildTopicPartitionInfo(serviceQueueKey.getServiceQueue(), serviceQueueKey.getTenantId(), partition);
}
private TopicPartitionInfo buildTopicPartitionInfo(ServiceQueue serviceQueue, TenantId tenantId, int partition) {
boolean isolatedTenant = isIsolated(serviceQueue.getType(), tenantId);
private TopicPartitionInfo buildTopicPartitionInfo(QueueKey queueKey, int partition) {
TopicPartitionInfo.TopicPartitionInfoBuilder tpi = TopicPartitionInfo.builder();
tpi.topic(partitionTopicsMap.get(isolatedTenant ? tenantId : TenantId.SYS_TENANT_ID).get(serviceQueue));
tpi.topic(partitionTopicsMap.get(queueKey));
tpi.partition(partition);
ServiceQueueKey myPartitionsSearchKey;
if (isolatedTenant) {
tpi.tenantId(tenantId);
myPartitionsSearchKey = new ServiceQueueKey(serviceQueue, tenantId);
} else {
myPartitionsSearchKey = new ServiceQueueKey(serviceQueue, TenantId.SYS_TENANT_ID);
}
List<Integer> partitions = myPartitions.get(myPartitionsSearchKey);
tpi.tenantId(queueKey.getTenantId());
List<Integer> partitions = myPartitions.get(queueKey);
if (partitions != null) {
tpi.myPartition(partitions.contains(partition));
} else {
@ -374,39 +352,24 @@ public class HashPartitionService implements PartitionService {
}
}
private TenantId getSystemIsolatedTenantId(ServiceType serviceType, TenantId tenantId) {
return isIsolated(serviceType, tenantId) ? tenantId : TenantId.SYS_TENANT_ID;
}
private void logServiceInfo(TransportProtos.ServiceInfo server) {
TenantId tenantId = getSystemOrIsolatedTenantId(server);
if (tenantId.isNullUid()) {
log.info("[{}] Found common server: [{}]", server.getServiceId(), server.getServiceTypesList());
} else {
log.info("[{}][{}] Found specific server: [{}]", server.getServiceId(), tenantId, server.getServiceTypesList());
}
log.info("[{}] Found common server: [{}]", server.getServiceId(), server.getServiceTypesList());
}
private TenantId getSystemOrIsolatedTenantId(TransportProtos.ServiceInfo serviceInfo) {
return TenantId.fromUUID(new UUID(serviceInfo.getTenantIdMSB(), serviceInfo.getTenantIdLSB()));
}
private void addNode(Map<ServiceQueueKey, List<ServiceInfo>> queueServiceList, ServiceInfo instance) {
TenantId tenantId = getSystemOrIsolatedTenantId(instance);
private void addNode(Map<QueueKey, List<ServiceInfo>> queueServiceList, ServiceInfo instance) {
for (String serviceTypeStr : instance.getServiceTypesList()) {
ServiceType serviceType = ServiceType.valueOf(serviceTypeStr.toUpperCase());
if (ServiceType.TB_RULE_ENGINE.equals(serviceType)) {
partitionTopicsMap.get(tenantId).forEach((serviceQueue, topic) -> {
if (serviceQueue.getType().equals(ServiceType.TB_RULE_ENGINE)) {
ServiceQueueKey serviceQueueKey = new ServiceQueueKey(serviceQueue, tenantId);
queueServiceList.computeIfAbsent(serviceQueueKey, key -> new ArrayList<>()).add(instance);
partitionTopicsMap.keySet().forEach(key -> {
if (key.getType().equals(ServiceType.TB_RULE_ENGINE)) {
queueServiceList.computeIfAbsent(key, k -> new ArrayList<>()).add(instance);
}
});
} else {
ServiceQueueKey serviceQueueKey = new ServiceQueueKey(new ServiceQueue(serviceType), tenantId);
queueServiceList.computeIfAbsent(serviceQueueKey, key -> new ArrayList<>()).add(instance);
} else if (ServiceType.TB_CORE.equals(serviceType)) {
queueServiceList.computeIfAbsent(new QueueKey(serviceType), key -> new ArrayList<>()).add(instance);
}
}
for (String transportType : instance.getTransportsList()) {
tbTransportServicesByType.computeIfAbsent(transportType, t -> new ArrayList<>()).add(instance);
}

56
common/queue/src/main/java/org/thingsboard/server/queue/discovery/QueueKey.java

@ -0,0 +1,56 @@
/**
* Copyright © 2016-2022 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.queue.discovery;
import lombok.AllArgsConstructor;
import lombok.Data;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.queue.Queue;
import org.thingsboard.server.common.msg.queue.ServiceType;
@Data
@AllArgsConstructor
public class QueueKey {
private static final String MAIN = "Main";
private final ServiceType type;
private final String queueName;
private final TenantId tenantId;
public QueueKey(ServiceType type, Queue queue) {
this.type = type;
this.queueName = queue.getName();
this.tenantId = queue.getTenantId();
}
public QueueKey(ServiceType type, QueueRoutingInfo queueRoutingInfo) {
this.type = type;
this.queueName = queueRoutingInfo.getQueueName();
this.tenantId = queueRoutingInfo.getTenantId();
}
public QueueKey(ServiceType type, TenantId tenantId) {
this.type = type;
this.queueName = MAIN;
this.tenantId = tenantId != null ? tenantId : TenantId.SYS_TENANT_ID;
}
public QueueKey(ServiceType type) {
this.type = type;
this.queueName = MAIN;
this.tenantId = TenantId.SYS_TENANT_ID;
}
}

9
common/queue/src/main/java/org/thingsboard/server/queue/discovery/QueueRoutingInfo.java

@ -20,6 +20,7 @@ import org.thingsboard.server.common.data.id.QueueId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.queue.Queue;
import org.thingsboard.server.gen.transport.TransportProtos.GetQueueRoutingInfoResponseMsg;
import org.thingsboard.server.gen.transport.TransportProtos.QueueUpdateMsg;
import java.util.UUID;
@ -48,4 +49,12 @@ public class QueueRoutingInfo {
this.queueTopic = routingInfo.getQueueTopic();
this.partitions = routingInfo.getPartitions();
}
public QueueRoutingInfo(QueueUpdateMsg queueUpdateMsg) {
this.tenantId = new TenantId(new UUID(queueUpdateMsg.getTenantIdMSB(), queueUpdateMsg.getQueueIdLSB()));
this.queueId = new QueueId(new UUID(queueUpdateMsg.getQueueIdMSB(), queueUpdateMsg.getQueueIdLSB()));
this.queueName = queueUpdateMsg.getQueueName();
this.queueTopic = queueUpdateMsg.getQueueTopic();
this.partitions = queueUpdateMsg.getPartitions();
}
}

5
common/queue/src/main/java/org/thingsboard/server/queue/discovery/TbServiceInfoProvider.java

@ -15,12 +15,9 @@
*/
package org.thingsboard.server.queue.discovery;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.msg.queue.ServiceType;
import org.thingsboard.server.gen.transport.TransportProtos.ServiceInfo;
import java.util.Optional;
public interface TbServiceInfoProvider {
String getServiceId();
@ -31,6 +28,4 @@ public interface TbServiceInfoProvider {
boolean isService(ServiceType serviceType);
Optional<TenantId> getIsolatedTenant();
}

44
common/queue/src/main/java/org/thingsboard/server/queue/discovery/TopicPartitionInfoKey.java

@ -1,44 +0,0 @@
/**
* Copyright © 2016-2022 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.queue.discovery;
import lombok.AllArgsConstructor;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.msg.queue.ServiceQueue;
import java.util.Objects;
@AllArgsConstructor
public class TopicPartitionInfoKey {
private ServiceQueue serviceQueue;
private TenantId isolatedTenantId;
private int partition;
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
TopicPartitionInfoKey that = (TopicPartitionInfoKey) o;
return partition == that.partition &&
serviceQueue.equals(that.serviceQueue) &&
Objects.equals(isolatedTenantId, that.isolatedTenantId);
}
@Override
public int hashCode() {
return Objects.hash(serviceQueue, isolatedTenantId, partition);
}
}

10
common/queue/src/main/java/org/thingsboard/server/queue/discovery/event/PartitionChangeEvent.java

@ -17,9 +17,9 @@ package org.thingsboard.server.queue.discovery.event;
import lombok.Getter;
import lombok.ToString;
import org.thingsboard.server.common.msg.queue.ServiceQueueKey;
import org.thingsboard.server.common.msg.queue.ServiceType;
import org.thingsboard.server.common.msg.queue.TopicPartitionInfo;
import org.thingsboard.server.queue.discovery.QueueKey;
import java.util.Set;
@ -29,17 +29,17 @@ public class PartitionChangeEvent extends TbApplicationEvent {
private static final long serialVersionUID = -8731788167026510559L;
@Getter
private final ServiceQueueKey serviceQueueKey;
private final QueueKey queueKey;
@Getter
private final Set<TopicPartitionInfo> partitions;
public PartitionChangeEvent(Object source, ServiceQueueKey serviceQueueKey, Set<TopicPartitionInfo> partitions) {
public PartitionChangeEvent(Object source, QueueKey queueKey, Set<TopicPartitionInfo> partitions) {
super(source);
this.serviceQueueKey = serviceQueueKey;
this.queueKey = queueKey;
this.partitions = partitions;
}
public ServiceType getServiceType() {
return serviceQueueKey.getServiceQueue().getType();
return queueKey.getType();
}
}

29
dao/src/main/java/org/thingsboard/server/dao/queue/BaseQueueService.java

@ -87,7 +87,7 @@ public class BaseQueueService extends AbstractEntityService implements QueueServ
}
if (queueClusterService != null) {
queueClusterService.onQueueChange(createdQueue, null);
queueClusterService.onQueueChange(createdQueue);
}
return createdQueue;
@ -107,12 +107,12 @@ public class BaseQueueService extends AbstractEntityService implements QueueServ
tbQueueAdmin.createTopicIfNotExists(new TopicPartitionInfo(queue.getTopic(), queue.getTenantId(), i, false).getFullTopicName());
}
if (queueClusterService != null) {
queueClusterService.onQueueChange(updatedQueue, null);
queueClusterService.onQueueChange(updatedQueue);
}
} else {
log.info("Removed [{}] partitions from [{}] queue", oldPartitions - currentPartitions, queue.getName());
if (queueClusterService != null) {
queueClusterService.onQueueChange(updatedQueue, null);
queueClusterService.onQueueChange(updatedQueue);
}
await();
for (int i = currentPartitions; i < oldPartitions; i++) {
@ -120,7 +120,7 @@ public class BaseQueueService extends AbstractEntityService implements QueueServ
}
}
} else if (!oldQueue.equals(queue) && queueClusterService != null) {
queueClusterService.onQueueChange(updatedQueue, null);
queueClusterService.onQueueChange(updatedQueue);
}
return updatedQueue;
@ -130,12 +130,23 @@ public class BaseQueueService extends AbstractEntityService implements QueueServ
public void deleteQueue(TenantId tenantId, QueueId queueId) {
log.trace("Executing deleteQueue, queueId: [{}]", queueId);
Queue queue = findQueueById(tenantId, queueId);
doDelete(tenantId, queue);
}
@Override
public void deleteQueueByQueueName(TenantId tenantId, String queueName) {
log.trace("Executing deleteQueueByQueueName, name: [{}]", queueName);
Queue queue = findQueueByTenantIdAndName(tenantId, queueName);
doDelete(tenantId, queue);
}
private void doDelete(TenantId tenantId, Queue queue) {
if (queueClusterService != null) {
queueClusterService.onQueueDelete(queue, null);
queueClusterService.onQueueDelete(queue);
await();
}
// queueStatsService.deleteQueueStatsByQueueId(tenantId, queueId);
boolean result = queueDao.removeById(tenantId, queueId.getId());
boolean result = queueDao.removeById(tenantId, queue.getUuidId());
if (result && tbQueueAdmin != null) {
for (int i = 0; i < queue.getPartitions(); i++) {
String fullTopicName = new TopicPartitionInfo(queue.getTopic(), queue.getTenantId(), i, false).getFullTopicName();
@ -167,12 +178,6 @@ public class BaseQueueService extends AbstractEntityService implements QueueServ
return queueDao.findQueuesByTenantId(getSystemOrIsolatedTenantId(tenantId), pageLink);
}
@Override
public List<Queue> findAllMainQueues() {
log.trace("Executing findAllMainQueues");
return queueDao.findAllMainQueues();
}
@Override
public List<Queue> findAllQueues() {
log.trace("Executing findAllQueues");

91
dao/src/main/java/org/thingsboard/server/dao/tenant/TenantServiceImpl.java

@ -24,10 +24,11 @@ import org.springframework.stereotype.Service;
import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.TenantInfo;
import org.thingsboard.server.common.data.TenantProfile;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.queue.Queue;
import org.thingsboard.server.common.data.tenant.profile.TenantProfileQueueConfiguration;
import org.thingsboard.server.dao.asset.AssetService;
import org.thingsboard.server.dao.customer.CustomerService;
import org.thingsboard.server.dao.dashboard.DashboardService;
@ -48,6 +49,12 @@ import org.thingsboard.server.dao.usagerecord.ApiUsageStateService;
import org.thingsboard.server.dao.user.UserService;
import org.thingsboard.server.dao.widget.WidgetsBundleService;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import static org.thingsboard.server.dao.service.Validator.validateId;
@Service
@ -138,18 +145,90 @@ public class TenantServiceImpl extends AbstractEntityService implements TenantSe
tenant.setTenantProfileId(tenantProfile.getId());
}
tenantValidator.validate(tenant, Tenant::getId);
Tenant oldTenant = tenant.getId() != null ? tenantDao.findById(tenant.getId(), tenant.getUuidId()) : null;
Tenant savedTenant = tenantDao.save(tenant.getId(), tenant);
if (tenant.getId() == null) {
deviceProfileService.createDefaultDeviceProfile(savedTenant.getId());
apiUsageStateService.createDefaultApiUsageState(savedTenant.getId(), null);
TenantProfile tenantProfile = tenantProfileService.findTenantProfileById(TenantId.SYS_TENANT_ID, savedTenant.getTenantProfileId());
if(tenantProfile.isIsolatedTbRuleEngine()) {
queueService.createDefaultMainQueue(tenantProfile, savedTenant.getTenantId());
}
}
updateQueuesForTenant(oldTenant, savedTenant);
return savedTenant;
}
private void updateQueuesForTenant(Tenant oldTenant, Tenant newTenant) {
TenantProfile oldTenantProfile = oldTenant != null ? tenantProfileService.findTenantProfileById(TenantId.SYS_TENANT_ID, oldTenant.getTenantProfileId()) : null;
TenantProfile newTenantProfile = tenantProfileService.findTenantProfileById(TenantId.SYS_TENANT_ID, newTenant.getTenantProfileId());
TenantId tenantId = newTenant.getId();
boolean oldIsolated = oldTenantProfile != null && oldTenantProfile.isIsolatedTbRuleEngine();
boolean newIsolated = newTenantProfile.isIsolatedTbRuleEngine();
if (!oldIsolated && !newIsolated) {
return;
}
if (newTenantProfile.equals(oldTenantProfile)) {
return;
}
Map<String, TenantProfileQueueConfiguration> oldQueues;
Map<String, TenantProfileQueueConfiguration> newQueues;
if (oldIsolated) {
oldQueues = oldTenantProfile.getProfileData().getQueueConfiguration().stream()
.collect(Collectors.toMap(TenantProfileQueueConfiguration::getName, q -> q));
} else {
oldQueues = Collections.emptyMap();
}
if (newIsolated) {
newQueues = newTenantProfile.getProfileData().getQueueConfiguration().stream()
.collect(Collectors.toMap(TenantProfileQueueConfiguration::getName, q -> q));
} else {
newQueues = Collections.emptyMap();
}
List<String> toRemove = new ArrayList<>();
List<String> toCreate = new ArrayList<>();
List<String> toUpdate = new ArrayList<>();
for (String oldQueue : oldQueues.keySet()) {
if (!newQueues.containsKey(oldQueue)) {
toRemove.add(oldQueue);
}
}
for (String newQueue : newQueues.keySet()) {
if (oldQueues.containsKey(newQueue)) {
toUpdate.add(newQueue);
} else {
toCreate.add(newQueue);
}
}
toRemove.forEach(q -> queueService.deleteQueueByQueueName(tenantId, q));
toCreate.forEach(key -> queueService.saveQueue(new Queue(tenantId, newQueues.get(key))));
toUpdate.forEach(key -> {
Queue queueToUpdate = new Queue(tenantId, newQueues.get(key));
Queue foundQueue = queueService.findQueueByTenantIdAndName(tenantId, key);
queueToUpdate.setId(foundQueue.getId());
queueToUpdate.setCreatedTime(foundQueue.getCreatedTime());
if (queueToUpdate.equals(foundQueue)) {
//Queue not changed
} else {
queueService.saveQueue(queueToUpdate);
}
});
}
@Override
public void deleteTenant(TenantId tenantId) {
log.trace("Executing deleteTenant [{}]", tenantId);
@ -218,7 +297,7 @@ public class TenantServiceImpl extends AbstractEntityService implements TenantSe
private void validateTenantProfile(TenantId tenantId, Tenant tenant) {
TenantProfile tenantProfileById = tenantProfileService.findTenantProfileById(tenantId, tenant.getTenantProfileId());
if (!zkEnabled && (tenantProfileById.isIsolatedTbCore() || tenantProfileById.isIsolatedTbRuleEngine())) {
throw new DataValidationException("Can't use isolated tenant profiles in monolith setup!");
// throw new DataValidationException("Can't use isolated tenant profiles in monolith setup!");
}
}
};

1
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbMsgCountNode.java

@ -27,7 +27,6 @@ import org.thingsboard.rule.engine.api.util.TbNodeUtils;
import org.thingsboard.server.common.data.plugin.ComponentType;
import org.thingsboard.server.common.msg.TbMsg;
import org.thingsboard.server.common.msg.TbMsgMetaData;
import org.thingsboard.server.common.msg.queue.ServiceQueue;
import org.thingsboard.server.common.msg.session.SessionMsgType;
import java.util.UUID;

1
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/debug/TbMsgGeneratorNode.java

@ -34,7 +34,6 @@ import org.thingsboard.server.common.data.plugin.ComponentType;
import org.thingsboard.server.common.msg.TbMsg;
import org.thingsboard.server.common.msg.TbMsgMetaData;
import org.thingsboard.server.common.msg.queue.PartitionChangeMsg;
import org.thingsboard.server.common.msg.queue.ServiceQueue;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

1
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/delay/TbMsgDelayNode.java

@ -26,7 +26,6 @@ import org.thingsboard.rule.engine.api.util.TbNodeUtils;
import org.thingsboard.server.common.data.plugin.ComponentType;
import org.thingsboard.server.common.msg.TbMsg;
import org.thingsboard.server.common.msg.TbMsgMetaData;
import org.thingsboard.server.common.msg.queue.ServiceQueue;
import java.util.HashMap;
import java.util.Map;

4
ui-ngx/src/app/modules/home/components/import-export/import-export.service.ts

@ -567,7 +567,9 @@ export class ImportExportService {
return isDefined(tenantProfile.name)
&& isDefined(tenantProfile.profileData)
&& isDefined(tenantProfile.isolatedTbCore)
&& isDefined(tenantProfile.isolatedTbRuleEngine);
&& isDefined(tenantProfile.isolatedTbRuleEngine)
&& isDefined(tenantProfile.maxNumberOfQueues)
&& isDefined(tenantProfile.maxNumberOfPartitionsPerQueue);
}
private sumObject(obj1: any, obj2: any): any {

62
ui-ngx/src/app/modules/home/components/profile/tenant-profile.component.html

@ -67,36 +67,38 @@
<div>{{ 'tenant.isolated-tb-rule-engine' | translate }}</div>
<div class="tb-hint">{{'tenant.isolated-tb-rule-engine-details' | translate}}</div>
</mat-checkbox>
<mat-form-field class="mat-block" *ngIf="showQueueParams()">
<mat-label translate>tenant.max-number-of-queues</mat-label>
<input matInput
type="number"
step="1"
min="1"
required
formControlName="maxNumberOfQueues"/>
<mat-error *ngIf="entityForm.get('maxNumberOfQueues').hasError('required')">
{{ 'tenant.max-number-of-queues-required' | translate }}
</mat-error>
<mat-error *ngIf="entityForm.get('maxNumberOfQueues').hasError('min')">
{{ 'tenant.max-number-of-queues-min-length' | translate }}
</mat-error>
</mat-form-field>
<mat-form-field class="mat-block" *ngIf="showQueueParams()">
<mat-label translate>tenant.max-number-of-partitions-per-queue</mat-label>
<input matInput
type="number"
step="1"
min="1"
required
formControlName="maxNumberOfPartitionsPerQueue"/>
<mat-error *ngIf="entityForm.get('maxNumberOfPartitionsPerQueue').hasError('required')">
{{ 'tenant.max-number-of-partitions-per-queue-required' | translate }}
</mat-error>
<mat-error *ngIf="entityForm.get('maxNumberOfPartitionsPerQueue').hasError('min')">
{{ 'tenant.max-number-of-partitions-per-queue-min-length' | translate }}
</mat-error>
</mat-form-field>
<div *ngIf="entityForm.get('isolatedTbRuleEngine').value">
<mat-form-field class="mat-block">
<mat-label translate>tenant.max-number-of-queues</mat-label>
<input matInput
type="number"
step="1"
min="1"
required
formControlName="maxNumberOfQueues"/>
<mat-error *ngIf="entityForm.get('maxNumberOfQueues').hasError('required')">
{{ 'tenant.max-number-of-queues-required' | translate }}
</mat-error>
<mat-error *ngIf="entityForm.get('maxNumberOfQueues').hasError('min')">
{{ 'tenant.max-number-of-queues-min-length' | translate }}
</mat-error>
</mat-form-field>
<mat-form-field class="mat-block">
<mat-label translate>tenant.max-number-of-partitions-per-queue</mat-label>
<input matInput
type="number"
step="1"
min="1"
required
formControlName="maxNumberOfPartitionsPerQueue"/>
<mat-error *ngIf="entityForm.get('maxNumberOfPartitionsPerQueue').hasError('required')">
{{ 'tenant.max-number-of-partitions-per-queue-required' | translate }}
</mat-error>
<mat-error *ngIf="entityForm.get('maxNumberOfPartitionsPerQueue').hasError('min')">
{{ 'tenant.max-number-of-partitions-per-queue-min-length' | translate }}
</mat-error>
</mat-form-field>
</div>
</div>
<tb-tenant-profile-data
formControlName="profileData"

13
ui-ngx/src/app/modules/home/components/profile/tenant-profile.component.ts

@ -48,6 +48,11 @@ export class TenantProfileComponent extends EntityComponent<TenantProfile> {
super(store, fb, entityValue, entitiesTableConfigValue, cd);
}
ngOnInit() {
this.showQueueParams();
this.entityForm.get('isolatedTbRuleEngine').valueChanges.subscribe(() => this.showQueueParams());
}
hideDelete() {
if (this.entitiesTableConfig) {
return !this.entitiesTableConfig.deleteEnabled(this.entity);
@ -87,11 +92,11 @@ export class TenantProfileComponent extends EntityComponent<TenantProfile> {
showQueueParams(): boolean {
let isolatedTbRuleEngine: boolean = this.entityForm.get('isolatedTbRuleEngine').value;
if (isolatedTbRuleEngine) {
this.entityForm.controls['maxNumberOfQueues'].enable();
this.entityForm.controls['maxNumberOfPartitionsPerQueue'].enable();
this.entityForm.get('maxNumberOfQueues').enable();
this.entityForm.get('maxNumberOfPartitionsPerQueue').enable();
} else {
this.entityForm.controls['maxNumberOfQueues'].disable();
this.entityForm.controls['maxNumberOfPartitionsPerQueue'].disable();
this.entityForm.get('maxNumberOfQueues').disable();
this.entityForm.get('maxNumberOfPartitionsPerQueue').disable();
}
return isolatedTbRuleEngine;
}

1
ui-ngx/src/app/shared/models/tenant.model.ts

@ -98,6 +98,7 @@ export function createTenantProfileConfiguration(type: TenantProfileType): Tenan
export interface TenantProfileData {
configuration: TenantProfileConfiguration;
queueConfiguration?: any;
}
export interface TenantProfile extends BaseData<TenantProfileId> {

3
ui-ngx/src/assets/locale/locale.constant-en_US.json

@ -259,7 +259,8 @@
"queues": "Queues",
"queue-partitions": "Partitions",
"queue-submit-strategy": "Submit strategy",
"queue-processing-strategy": "Processing strategy"
"queue-processing-strategy": "Processing strategy",
"queue-configuration": "Queue configuration"
},
"alarm": {
"alarm": "Alarm",

Loading…
Cancel
Save