10 changed files with 181 additions and 266 deletions
@ -1,225 +0,0 @@ |
|||||
/** |
|
||||
* Copyright © 2016-2023 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.service.queue; |
|
||||
|
|
||||
import org.junit.Before; |
|
||||
import org.junit.Test; |
|
||||
import org.springframework.beans.factory.annotation.Autowired; |
|
||||
import org.springframework.boot.test.mock.mockito.SpyBean; |
|
||||
import org.thingsboard.server.actors.ActorSystemContext; |
|
||||
import org.thingsboard.server.common.data.DataConstants; |
|
||||
import org.thingsboard.server.common.data.Device; |
|
||||
import org.thingsboard.server.common.data.DeviceProfile; |
|
||||
import org.thingsboard.server.common.data.id.EntityId; |
|
||||
import org.thingsboard.server.common.data.id.TenantId; |
|
||||
import org.thingsboard.server.common.data.queue.ProcessingStrategy; |
|
||||
import org.thingsboard.server.common.data.queue.ProcessingStrategyType; |
|
||||
import org.thingsboard.server.common.data.queue.Queue; |
|
||||
import org.thingsboard.server.common.data.queue.SubmitStrategy; |
|
||||
import org.thingsboard.server.common.data.queue.SubmitStrategyType; |
|
||||
import org.thingsboard.server.common.data.tenant.profile.TenantProfileQueueConfiguration; |
|
||||
import org.thingsboard.server.common.msg.TbMsg; |
|
||||
import org.thingsboard.server.common.msg.queue.QueueToRuleEngineMsg; |
|
||||
import org.thingsboard.server.common.msg.queue.ServiceType; |
|
||||
import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; |
|
||||
import org.thingsboard.server.controller.AbstractControllerTest; |
|
||||
import org.thingsboard.server.dao.queue.QueueService; |
|
||||
import org.thingsboard.server.dao.service.DaoSqlTest; |
|
||||
import org.thingsboard.server.queue.discovery.PartitionService; |
|
||||
import org.thingsboard.server.service.entitiy.queue.TbQueueService; |
|
||||
import org.thingsboard.server.service.profile.TbDeviceProfileCache; |
|
||||
|
|
||||
import java.util.List; |
|
||||
import java.util.concurrent.TimeUnit; |
|
||||
import java.util.concurrent.atomic.AtomicReference; |
|
||||
import java.util.function.Consumer; |
|
||||
import java.util.function.Predicate; |
|
||||
|
|
||||
import static org.assertj.core.api.Assertions.assertThat; |
|
||||
import static org.awaitility.Awaitility.await; |
|
||||
import static org.mockito.ArgumentMatchers.argThat; |
|
||||
import static org.mockito.Mockito.timeout; |
|
||||
import static org.mockito.Mockito.verify; |
|
||||
|
|
||||
@DaoSqlTest |
|
||||
public class QueueServiceTest extends AbstractControllerTest { |
|
||||
|
|
||||
@SpyBean |
|
||||
private ActorSystemContext actorContext; |
|
||||
@Autowired |
|
||||
private TbQueueService tbQueueService; |
|
||||
@Autowired |
|
||||
private QueueService queueService; |
|
||||
@SpyBean |
|
||||
private PartitionService partitionService; |
|
||||
@Autowired |
|
||||
private TbDeviceProfileCache deviceProfileCache; |
|
||||
|
|
||||
@Before |
|
||||
public void beforeEach() { |
|
||||
Queue mainQueue = queueService.findQueueByTenantIdAndName(TenantId.SYS_TENANT_ID, DataConstants.MAIN_QUEUE_NAME); |
|
||||
if (mainQueue == null) { |
|
||||
mainQueue = new Queue(TenantId.SYS_TENANT_ID, getMainQueueConfig()); |
|
||||
tbQueueService.saveQueue(mainQueue); |
|
||||
} |
|
||||
|
|
||||
Queue hpQueue = queueService.findQueueByTenantIdAndName(TenantId.SYS_TENANT_ID, DataConstants.HP_QUEUE_NAME); |
|
||||
if (hpQueue == null) { |
|
||||
hpQueue = new Queue(TenantId.SYS_TENANT_ID, getHighPriorityQueueConfig()); |
|
||||
tbQueueService.saveQueue(hpQueue); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
@Test |
|
||||
public void testQueuesUpdateOnTenantProfileUpdate() throws Exception { |
|
||||
loginTenantAdmin(); |
|
||||
DeviceProfile hpQueueProfile = createDeviceProfile("HighPriority profile"); |
|
||||
hpQueueProfile.setDefaultQueueName(DataConstants.HP_QUEUE_NAME); |
|
||||
hpQueueProfile = doPost("/api/deviceProfile", hpQueueProfile, DeviceProfile.class); |
|
||||
Device hpQueueDevice = createDevice("HP", hpQueueProfile.getName(), "HP"); |
|
||||
deviceProfileCache.evict(tenantId, hpQueueProfile.getId()); |
|
||||
|
|
||||
DeviceProfile mainQueueProfile = createDeviceProfile("Main profile"); |
|
||||
mainQueueProfile.setDefaultQueueName(DataConstants.MAIN_QUEUE_NAME); |
|
||||
mainQueueProfile = doPost("/api/deviceProfile", mainQueueProfile, DeviceProfile.class); |
|
||||
Device mainQueueDevice = createDevice("Main", mainQueueProfile.getName(), "Main"); |
|
||||
|
|
||||
verifyUsedQueueAndMessage(DataConstants.HP_QUEUE_NAME, hpQueueDevice.getId(), DataConstants.ATTRIBUTES_UPDATED, () -> { |
|
||||
doPost("/api/plugins/telemetry/DEVICE/" + hpQueueDevice.getId() + "/attributes/SERVER_SCOPE", "{\"test\":123}", String.class); |
|
||||
}, usedTpi -> { |
|
||||
assertThat(usedTpi.getTenantId()).get().isEqualTo(TenantId.SYS_TENANT_ID); |
|
||||
}); |
|
||||
verifyUsedQueueAndMessage(DataConstants.MAIN_QUEUE_NAME, mainQueueDevice.getId(), DataConstants.ATTRIBUTES_UPDATED, () -> { |
|
||||
doPost("/api/plugins/telemetry/DEVICE/" + mainQueueDevice.getId() + "/attributes/SERVER_SCOPE", "{\"test\":123}", String.class); |
|
||||
}, usedTpi -> { |
|
||||
assertThat(usedTpi.getTenantId()).get().isEqualTo(TenantId.SYS_TENANT_ID); |
|
||||
}); |
|
||||
|
|
||||
updateDefaultTenantProfile(tenantProfile -> { |
|
||||
tenantProfile.setIsolatedTbRuleEngine(true); |
|
||||
tenantProfile.getProfileData().setQueueConfiguration(List.of( |
|
||||
getMainQueueConfig() |
|
||||
)); |
|
||||
}); |
|
||||
|
|
||||
verifyUsedQueueAndMessage(DataConstants.MAIN_QUEUE_NAME, mainQueueDevice.getId(), DataConstants.ATTRIBUTES_UPDATED, () -> { |
|
||||
doPost("/api/plugins/telemetry/DEVICE/" + mainQueueDevice.getId() + "/attributes/SERVER_SCOPE", "{\"test\":123}", String.class); |
|
||||
}, usedTpi -> { |
|
||||
assertThat(usedTpi.getTenantId()).get().isEqualTo(tenantId); |
|
||||
}); |
|
||||
verifyUsedQueueAndMessage(DataConstants.HP_QUEUE_NAME, hpQueueDevice.getId(), DataConstants.ATTRIBUTES_UPDATED, () -> { |
|
||||
doPost("/api/plugins/telemetry/DEVICE/" + hpQueueDevice.getId() + "/attributes/SERVER_SCOPE", "{\"test\":123}", String.class); |
|
||||
}, usedTpi -> { |
|
||||
assertThat(usedTpi.getTopic()).endsWith("main"); |
|
||||
assertThat(usedTpi.getTenantId()).get().isEqualTo(tenantId); |
|
||||
}); |
|
||||
|
|
||||
updateDefaultTenantProfile(tenantProfile -> { |
|
||||
tenantProfile.setIsolatedTbRuleEngine(true); |
|
||||
tenantProfile.getProfileData().setQueueConfiguration(List.of( |
|
||||
getMainQueueConfig(), getHighPriorityQueueConfig() |
|
||||
)); |
|
||||
}); |
|
||||
|
|
||||
verifyUsedQueueAndMessage(DataConstants.HP_QUEUE_NAME, hpQueueDevice.getId(), DataConstants.ATTRIBUTES_UPDATED, () -> { |
|
||||
doPost("/api/plugins/telemetry/DEVICE/" + hpQueueDevice.getId() + "/attributes/SERVER_SCOPE", "{\"test\":123}", String.class); |
|
||||
}, usedTpi -> { |
|
||||
assertThat(usedTpi.getTopic()).endsWith("hp"); |
|
||||
assertThat(usedTpi.getTenantId()).get().isEqualTo(tenantId); |
|
||||
}); |
|
||||
verifyUsedQueueAndMessage(DataConstants.MAIN_QUEUE_NAME, mainQueueDevice.getId(), DataConstants.ATTRIBUTES_UPDATED, () -> { |
|
||||
doPost("/api/plugins/telemetry/DEVICE/" + mainQueueDevice.getId() + "/attributes/SERVER_SCOPE", "{\"test\":123}", String.class); |
|
||||
}, usedTpi -> { |
|
||||
assertThat(usedTpi.getTenantId()).get().isEqualTo(tenantId); |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
private void verifyUsedQueueAndMessage(String queue, EntityId entityId, String msgType, Runnable action, Consumer<TopicPartitionInfo> tpiAssert) { |
|
||||
await().atMost(15, TimeUnit.SECONDS) |
|
||||
.untilAsserted(() -> { |
|
||||
TopicPartitionInfo tpi = partitionService.resolve(ServiceType.TB_RULE_ENGINE, queue, tenantId, entityId); |
|
||||
tpiAssert.accept(tpi); |
|
||||
}); |
|
||||
action.run(); |
|
||||
TbMsg tbMsg = awaitTbMsg(msg -> msg.getOriginator().equals(entityId) |
|
||||
&& msg.getType().equals(msgType), 10000); |
|
||||
assertThat(tbMsg.getQueueName()).isEqualTo(queue); |
|
||||
|
|
||||
TopicPartitionInfo tpi = partitionService.resolve(ServiceType.TB_RULE_ENGINE, queue, tenantId, entityId); |
|
||||
tpiAssert.accept(tpi); |
|
||||
} |
|
||||
|
|
||||
protected TbMsg awaitTbMsg(Predicate<TbMsg> predicate, int timeoutMillis) { |
|
||||
AtomicReference<TbMsg> tbMsgCaptor = new AtomicReference<>(); |
|
||||
verify(actorContext, timeout(timeoutMillis).atLeastOnce()).tell(argThat(actorMsg -> { |
|
||||
if (!(actorMsg instanceof QueueToRuleEngineMsg)) { |
|
||||
return false; |
|
||||
} |
|
||||
TbMsg tbMsg = ((QueueToRuleEngineMsg) actorMsg).getMsg(); |
|
||||
if (predicate.test(tbMsg)) { |
|
||||
tbMsgCaptor.set(tbMsg); |
|
||||
return true; |
|
||||
} |
|
||||
return false; |
|
||||
})); |
|
||||
return tbMsgCaptor.get(); |
|
||||
} |
|
||||
|
|
||||
private TenantProfileQueueConfiguration getHighPriorityQueueConfig() { |
|
||||
TenantProfileQueueConfiguration hpQueueConfig = new TenantProfileQueueConfiguration(); |
|
||||
hpQueueConfig.setName(DataConstants.HP_QUEUE_NAME); |
|
||||
hpQueueConfig.setTopic(DataConstants.HP_QUEUE_TOPIC); |
|
||||
hpQueueConfig.setPollInterval(25); |
|
||||
hpQueueConfig.setPartitions(10); |
|
||||
hpQueueConfig.setConsumerPerPartition(true); |
|
||||
hpQueueConfig.setPackProcessingTimeout(2000); |
|
||||
SubmitStrategy highPriorityQueueSubmitStrategy = new SubmitStrategy(); |
|
||||
highPriorityQueueSubmitStrategy.setType(SubmitStrategyType.BURST); |
|
||||
highPriorityQueueSubmitStrategy.setBatchSize(100); |
|
||||
hpQueueConfig.setSubmitStrategy(highPriorityQueueSubmitStrategy); |
|
||||
ProcessingStrategy highPriorityQueueProcessingStrategy = new ProcessingStrategy(); |
|
||||
highPriorityQueueProcessingStrategy.setType(ProcessingStrategyType.RETRY_FAILED_AND_TIMED_OUT); |
|
||||
highPriorityQueueProcessingStrategy.setRetries(0); |
|
||||
highPriorityQueueProcessingStrategy.setFailurePercentage(0); |
|
||||
highPriorityQueueProcessingStrategy.setPauseBetweenRetries(5); |
|
||||
highPriorityQueueProcessingStrategy.setMaxPauseBetweenRetries(5); |
|
||||
hpQueueConfig.setProcessingStrategy(highPriorityQueueProcessingStrategy); |
|
||||
return hpQueueConfig; |
|
||||
} |
|
||||
|
|
||||
private TenantProfileQueueConfiguration getMainQueueConfig() { |
|
||||
TenantProfileQueueConfiguration mainQueue = new TenantProfileQueueConfiguration(); |
|
||||
mainQueue.setName(DataConstants.MAIN_QUEUE_NAME); |
|
||||
mainQueue.setTopic(DataConstants.MAIN_QUEUE_TOPIC); |
|
||||
mainQueue.setPollInterval(25); |
|
||||
mainQueue.setPartitions(10); |
|
||||
mainQueue.setConsumerPerPartition(true); |
|
||||
mainQueue.setPackProcessingTimeout(2000); |
|
||||
SubmitStrategy mainQueueSubmitStrategy = new SubmitStrategy(); |
|
||||
mainQueueSubmitStrategy.setType(SubmitStrategyType.BURST); |
|
||||
mainQueueSubmitStrategy.setBatchSize(1000); |
|
||||
mainQueue.setSubmitStrategy(mainQueueSubmitStrategy); |
|
||||
ProcessingStrategy mainQueueProcessingStrategy = new ProcessingStrategy(); |
|
||||
mainQueueProcessingStrategy.setType(ProcessingStrategyType.SKIP_ALL_FAILURES); |
|
||||
mainQueueProcessingStrategy.setRetries(3); |
|
||||
mainQueueProcessingStrategy.setFailurePercentage(0); |
|
||||
mainQueueProcessingStrategy.setPauseBetweenRetries(3); |
|
||||
mainQueueProcessingStrategy.setMaxPauseBetweenRetries(3); |
|
||||
mainQueue.setProcessingStrategy(mainQueueProcessingStrategy); |
|
||||
return mainQueue; |
|
||||
} |
|
||||
|
|
||||
} |
|
||||
Loading…
Reference in new issue