refactor(测试计划): 优化同一个测试计划并发执行偶发Block Waiting情况
Signed-off-by: fit2-zhao <yong.zhao@fit2cloud.com>
This commit is contained in:
parent
9628430583
commit
d315868c0c
|
@ -26,6 +26,7 @@ import io.metersphere.dto.RunModeConfigDTO;
|
|||
import io.metersphere.environment.service.BaseEnvGroupProjectService;
|
||||
import io.metersphere.environment.service.BaseEnvironmentService;
|
||||
import io.metersphere.service.ApiExecutionQueueService;
|
||||
import io.metersphere.service.RedisTemplateService;
|
||||
import io.metersphere.service.ServiceUtils;
|
||||
import io.metersphere.service.definition.ApiCaseResultService;
|
||||
import io.metersphere.service.scenario.ApiScenarioReportStructureService;
|
||||
|
@ -68,7 +69,8 @@ public class ApiCaseExecuteService {
|
|||
private BaseEnvironmentService baseEnvironmentService;
|
||||
@Resource
|
||||
private ExtTestPlanApiCaseMapper extTestPlanApiCaseMapper;
|
||||
|
||||
@Resource
|
||||
private RedisTemplateService redisTemplateService;
|
||||
/**
|
||||
* 测试计划case执行
|
||||
*
|
||||
|
@ -131,6 +133,9 @@ public class ApiCaseExecuteService {
|
|||
ApiDefinitionExecResultWithBLOBs report = ApiDefinitionExecResultUtil.addResult(request, runModeConfigDTO, testPlanApiCase, status, testCase, resourcePoolId);
|
||||
executeQueue.put(testPlanApiCase.getId(), report);
|
||||
responseDTOS.add(new MsExecResponseDTO(testPlanApiCase.getId(), report.getId(), request.getTriggerMode()));
|
||||
// 执行中资源锁住,防止重复更新造成LOCK WAIT
|
||||
redisTemplateService.lock(testPlanApiCase.getId());
|
||||
|
||||
LoggerUtil.info("预生成测试用例结果报告:" + report.getName(), report.getId());
|
||||
}
|
||||
apiCaseResultService.batchSave(executeQueue);
|
||||
|
|
|
@ -35,6 +35,7 @@ import io.metersphere.environment.service.BaseEnvGroupProjectService;
|
|||
import io.metersphere.i18n.Translator;
|
||||
import io.metersphere.plugin.core.MsTestElement;
|
||||
import io.metersphere.service.ApiExecutionQueueService;
|
||||
import io.metersphere.service.RedisTemplateService;
|
||||
import io.metersphere.service.ServiceUtils;
|
||||
import io.metersphere.service.SystemParameterService;
|
||||
import io.metersphere.service.definition.TcpApiParamService;
|
||||
|
@ -91,6 +92,8 @@ public class ApiScenarioExecuteService {
|
|||
private ExtTestPlanScenarioCaseMapper extTestPlanScenarioCaseMapper;
|
||||
@Resource
|
||||
private SystemParameterService systemParameterService;
|
||||
@Resource
|
||||
private RedisTemplateService redisTemplateService;
|
||||
|
||||
public List<MsExecResponseDTO> run(RunScenarioRequest request) {
|
||||
if (LoggerUtil.getLogger().isDebugEnabled()) {
|
||||
|
@ -343,6 +346,8 @@ public class ApiScenarioExecuteService {
|
|||
if (!StringUtils.equals(request.getConfig().getReportType(), RunModeConstants.SET_REPORT.toString())) {
|
||||
apiScenarioReportStructureService.save(scenario, report.getId(), request.getConfig() != null ? request.getConfig().getReportType() : null);
|
||||
}
|
||||
// 执行中资源锁住,防止重复更新造成LOCK WAIT
|
||||
redisTemplateService.lock(planApiScenario.getId());
|
||||
// 重置报告ID
|
||||
reportId = UUID.randomUUID().toString();
|
||||
}
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
package io.metersphere.commons.enums;
|
||||
|
||||
public enum LockEnum {
|
||||
LOCK, WAITING
|
||||
}
|
|
@ -1,13 +1,21 @@
|
|||
package io.metersphere.service;
|
||||
|
||||
import io.metersphere.api.jmeter.utils.JmxFileUtil;
|
||||
import io.metersphere.commons.enums.LockEnum;
|
||||
import io.metersphere.commons.utils.LogUtil;
|
||||
import io.metersphere.utils.LoggerUtil;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.apache.commons.lang3.ObjectUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@Service
|
||||
public class RedisTemplateService {
|
||||
private static final String PRX = "TEST_PLAN_";
|
||||
public static final long TIME_OUT = 60;
|
||||
@Resource
|
||||
private RedisTemplate<String, Object> redisTemplate;
|
||||
|
||||
|
@ -46,4 +54,33 @@ public class RedisTemplateService {
|
|||
public void delFilePath(String reportId) {
|
||||
delete(JmxFileUtil.getExecuteFileKeyInRedis(reportId));
|
||||
}
|
||||
|
||||
/**
|
||||
* 加锁
|
||||
*/
|
||||
public boolean lock(String key) {
|
||||
return redisTemplate.opsForValue().setIfAbsent(StringUtils.join(PRX, key), LockEnum.LOCK.name());
|
||||
}
|
||||
|
||||
public boolean has(String key) {
|
||||
try {
|
||||
Object value = redisTemplate.opsForValue().get(StringUtils.join(PRX, key));
|
||||
if (ObjectUtils.isNotEmpty(value)) {
|
||||
if (StringUtils.equals(LockEnum.LOCK.name(), String.valueOf(value))) {
|
||||
// 设置一分钟超时
|
||||
redisTemplate.opsForValue().setIfPresent(StringUtils.join(PRX, key),
|
||||
LockEnum.WAITING.name(), TIME_OUT, TimeUnit.SECONDS);
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
redisTemplate.opsForValue().setIfAbsent(StringUtils.join(PRX, key),
|
||||
LockEnum.WAITING.name(), TIME_OUT, TimeUnit.SECONDS);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
LogUtil.error(e);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,18 +2,25 @@ package io.metersphere.service;
|
|||
|
||||
import io.metersphere.api.exec.queue.PoolExecBlockingQueueUtil;
|
||||
import io.metersphere.api.jmeter.ApiLocalRunner;
|
||||
import io.metersphere.api.jmeter.utils.JmxFileUtil;
|
||||
import io.metersphere.commons.utils.BeanUtils;
|
||||
import io.metersphere.commons.utils.CommonBeanFactory;
|
||||
import io.metersphere.commons.utils.FixedCapacityUtil;
|
||||
import io.metersphere.dto.JmeterRunRequestDTO;
|
||||
import io.metersphere.dto.ResultDTO;
|
||||
import io.metersphere.utils.LoggerUtil;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
public class RemakeReportService {
|
||||
@Resource
|
||||
@Lazy
|
||||
private ApiExecutionQueueService queueService;
|
||||
@Resource
|
||||
@Lazy
|
||||
private TestResultService testResultService;
|
||||
|
||||
public void queueNext(JmeterRunRequestDTO request, String errorMsg) {
|
||||
try {
|
||||
ResultDTO dto = new ResultDTO();
|
||||
|
@ -25,11 +32,12 @@ public class RemakeReportService {
|
|||
PoolExecBlockingQueueUtil.offer(dto.getReportId());
|
||||
LoggerUtil.error("执行异常处理:" + errorMsg, request.getReportId());
|
||||
if (StringUtils.isNotEmpty(dto.getQueueId())) {
|
||||
CommonBeanFactory.getBean(ApiExecutionQueueService.class).queueNext(dto);
|
||||
queueService.queueNext(dto);
|
||||
}
|
||||
// 更新测试计划报告
|
||||
LoggerUtil.info("Check Processing Test Plan report status.queueId:" + dto.getQueueId() + ",runMode:" + dto.getRunMode() + ",testId:" + dto.getTestId(), dto.getReportId());
|
||||
CommonBeanFactory.getBean(ApiExecutionQueueService.class).checkTestPlanCaseTestEnd(dto.getTestId(), dto.getRunMode(), dto.getTestPlanReportId());
|
||||
LoggerUtil.info("Check Processing Test Plan report status.queueId:"
|
||||
+ dto.getQueueId() + ",runMode:" + dto.getRunMode() + ",testId:" + dto.getTestId(), dto.getReportId());
|
||||
queueService.checkTestPlanCaseTestEnd(dto.getTestId(), dto.getRunMode(), dto.getTestPlanReportId());
|
||||
} catch (Exception e) {
|
||||
ApiLocalRunner.clearCache(request.getReportId());
|
||||
LoggerUtil.error("回退报告异常", request.getReportId(), e);
|
||||
|
@ -43,7 +51,7 @@ public class RemakeReportService {
|
|||
dto.setTestId(request.getTestId());
|
||||
String consoleMsg = FixedCapacityUtil.getJmeterLogger(dto.getReportId(), true);
|
||||
dto.setConsole(consoleMsg + StringUtils.LF + errorMsg);
|
||||
CommonBeanFactory.getBean(TestResultService.class).testEnded(dto);
|
||||
testResultService.testEnded(dto);
|
||||
}
|
||||
|
||||
public void testEnded(JmeterRunRequestDTO request, String errorMsg) {
|
||||
|
|
|
@ -196,7 +196,9 @@ public class TestResultService {
|
|||
}
|
||||
if (StringUtils.equals(dto.getRunMode(), ApiRunMode.SCENARIO_PLAN.name())) {
|
||||
return apiScenarioReportService.updatePlanCase(dto);
|
||||
} else if (StringUtils.equalsAny(dto.getRunMode(), ApiRunMode.SCHEDULE_SCENARIO_PLAN.name(), ApiRunMode.JENKINS_SCENARIO_PLAN.name())) {
|
||||
} else if (StringUtils.equalsAny(dto.getRunMode(),
|
||||
ApiRunMode.SCHEDULE_SCENARIO_PLAN.name(),
|
||||
ApiRunMode.JENKINS_SCENARIO_PLAN.name())) {
|
||||
return apiScenarioReportService.updateSchedulePlanCase(dto);
|
||||
} else {
|
||||
return this.editReport(dto);
|
||||
|
|
|
@ -20,6 +20,7 @@ import io.metersphere.dto.RequestResult;
|
|||
import io.metersphere.dto.ResultDTO;
|
||||
import io.metersphere.notice.sender.NoticeModel;
|
||||
import io.metersphere.notice.service.NoticeSendService;
|
||||
import io.metersphere.service.RedisTemplateService;
|
||||
import io.metersphere.service.ServiceUtils;
|
||||
import io.metersphere.utils.LoggerUtil;
|
||||
import jakarta.annotation.Resource;
|
||||
|
@ -66,6 +67,8 @@ public class ApiDefinitionExecResultService {
|
|||
private ExtApiTestCaseMapper extApiTestCaseMapper;
|
||||
@Resource
|
||||
private ExtTestPlanApiCaseMapper extTestPlanApiCaseMapper;
|
||||
@Resource
|
||||
private RedisTemplateService redisTemplateService;
|
||||
|
||||
/**
|
||||
* API/CASE 重试结果保留一条
|
||||
|
@ -100,7 +103,7 @@ public class ApiDefinitionExecResultService {
|
|||
User user = getUser(dto, result);
|
||||
//如果是测试计划用例,更新接口用例的上次执行结果
|
||||
TestPlanApiCase testPlanApiCase = testPlanApiCaseMapper.selectByPrimaryKey(dto.getTestId());
|
||||
if (testPlanApiCase != null) {
|
||||
if (testPlanApiCase != null && !redisTemplateService.has(dto.getTestId())) {
|
||||
ApiTestCaseWithBLOBs apiTestCase = apiTestCaseMapper.selectByPrimaryKey(testPlanApiCase.getApiCaseId());
|
||||
if (apiTestCase != null) {
|
||||
apiTestCase.setLastResultId(dto.getReportId());
|
||||
|
@ -219,21 +222,27 @@ public class ApiDefinitionExecResultService {
|
|||
}
|
||||
|
||||
public void setExecResult(String id, String status, Long time) {
|
||||
TestPlanApiCase apiCase = new TestPlanApiCase();
|
||||
apiCase.setId(id);
|
||||
apiCase.setStatus(status);
|
||||
apiCase.setUpdateTime(time);
|
||||
testPlanApiCaseMapper.updateByPrimaryKeySelective(apiCase);
|
||||
if (!redisTemplateService.has(id)) {
|
||||
TestPlanApiCase apiCase = new TestPlanApiCase();
|
||||
apiCase.setId(id);
|
||||
apiCase.setStatus(status);
|
||||
apiCase.setUpdateTime(time);
|
||||
testPlanApiCaseMapper.updateByPrimaryKeySelective(apiCase);
|
||||
}
|
||||
}
|
||||
|
||||
public void editStatus(ApiDefinitionExecResult saveResult, String type, String status, Long time, String reportId, String testId) {
|
||||
String name = testId;
|
||||
String version = StringUtils.EMPTY;
|
||||
String projectId = StringUtils.EMPTY;
|
||||
if (StringUtils.equalsAnyIgnoreCase(type, ApiRunMode.API_PLAN.name(), ApiRunMode.SCHEDULE_API_PLAN.name(), ApiRunMode.JENKINS_API_PLAN.name(), ApiRunMode.MANUAL_PLAN.name())) {
|
||||
if (StringUtils.equalsAnyIgnoreCase(type,
|
||||
ApiRunMode.API_PLAN.name(),
|
||||
ApiRunMode.SCHEDULE_API_PLAN.name(),
|
||||
ApiRunMode.JENKINS_API_PLAN.name(),
|
||||
ApiRunMode.MANUAL_PLAN.name())) {
|
||||
TestPlanApiCase testPlanApiCase = testPlanApiCaseMapper.selectByPrimaryKey(testId);
|
||||
ApiTestCaseWithBLOBs caseWithBLOBs = null;
|
||||
if (testPlanApiCase != null) {
|
||||
if (testPlanApiCase != null && !redisTemplateService.has(testId)) {
|
||||
this.setExecResult(testId, status, time);
|
||||
caseWithBLOBs = apiTestCaseMapper.selectByPrimaryKey(testPlanApiCase.getApiCaseId());
|
||||
testPlanApiCase.setStatus(status);
|
||||
|
@ -255,7 +264,7 @@ public class ApiDefinitionExecResultService {
|
|||
projectId = apiDefinition.getProjectId();
|
||||
} else {
|
||||
ApiTestCaseWithBLOBs caseWithBLOBs = apiTestCaseMapper.selectByPrimaryKey(testId);
|
||||
if (caseWithBLOBs != null) {
|
||||
if (caseWithBLOBs != null && !redisTemplateService.has(testId)) {
|
||||
// 更新用例最后执行结果
|
||||
caseWithBLOBs.setLastResultId(reportId);
|
||||
caseWithBLOBs.setStatus(status);
|
||||
|
@ -281,18 +290,23 @@ public class ApiDefinitionExecResultService {
|
|||
public void batchEditStatus(String type, String status, String reportId, String testId,
|
||||
TestPlanApiCaseMapper batchTestPlanApiCaseMapper,
|
||||
ApiTestCaseMapper batchApiTestCaseMapper) {
|
||||
if (StringUtils.equalsAnyIgnoreCase(type, ApiRunMode.API_PLAN.name(), ApiRunMode.SCHEDULE_API_PLAN.name(),
|
||||
ApiRunMode.JENKINS_API_PLAN.name(), ApiRunMode.MANUAL_PLAN.name())) {
|
||||
TestPlanApiCase apiCase = new TestPlanApiCase();
|
||||
apiCase.setId(testId);
|
||||
apiCase.setStatus(status);
|
||||
apiCase.setUpdateTime(System.currentTimeMillis());
|
||||
batchTestPlanApiCaseMapper.updateByPrimaryKeySelective(apiCase);
|
||||
if (StringUtils.equalsAnyIgnoreCase(type,
|
||||
ApiRunMode.API_PLAN.name(),
|
||||
ApiRunMode.SCHEDULE_API_PLAN.name(),
|
||||
ApiRunMode.JENKINS_API_PLAN.name(),
|
||||
ApiRunMode.MANUAL_PLAN.name())) {
|
||||
if (!redisTemplateService.has(testId)) {
|
||||
TestPlanApiCase apiCase = new TestPlanApiCase();
|
||||
apiCase.setId(testId);
|
||||
apiCase.setStatus(status);
|
||||
apiCase.setUpdateTime(System.currentTimeMillis());
|
||||
batchTestPlanApiCaseMapper.updateByPrimaryKeySelective(apiCase);
|
||||
|
||||
TestCaseReviewApiCase reviewApiCase = new TestCaseReviewApiCase();
|
||||
reviewApiCase.setId(testId);
|
||||
reviewApiCase.setStatus(status);
|
||||
reviewApiCase.setUpdateTime(System.currentTimeMillis());
|
||||
TestCaseReviewApiCase reviewApiCase = new TestCaseReviewApiCase();
|
||||
reviewApiCase.setId(testId);
|
||||
reviewApiCase.setStatus(status);
|
||||
reviewApiCase.setUpdateTime(System.currentTimeMillis());
|
||||
}
|
||||
} else {
|
||||
// 更新用例最后执行结果
|
||||
ApiTestCaseWithBLOBs caseWithBLOBs = new ApiTestCaseWithBLOBs();
|
||||
|
@ -324,7 +338,7 @@ public class ApiDefinitionExecResultService {
|
|||
}
|
||||
if (StringUtils.equalsAny(dto.getRunMode(), ApiRunMode.SCHEDULE_API_PLAN.name(), ApiRunMode.JENKINS_API_PLAN.name())) {
|
||||
TestPlanApiCase apiCase = testPlanApiCaseMapper.selectByPrimaryKey(dto.getTestId());
|
||||
if (apiCase != null) {
|
||||
if (apiCase != null && !redisTemplateService.has(dto.getTestId())) {
|
||||
String projectId = extTestPlanApiCaseMapper.selectProjectId(apiCase.getId());
|
||||
ApiDefinition apiDefinition = extApiTestCaseMapper.selectApiBasicInfoByCaseId(apiCase.getId());
|
||||
String version = apiDefinition == null ? "" : apiDefinition.getVersionId();
|
||||
|
|
|
@ -7,6 +7,7 @@ import io.metersphere.api.dto.automation.ExecuteType;
|
|||
import io.metersphere.api.dto.automation.RunScenarioRequest;
|
||||
import io.metersphere.api.dto.datacount.ApiDataCountResult;
|
||||
import io.metersphere.api.dto.definition.RunDefinitionRequest;
|
||||
import io.metersphere.service.*;
|
||||
import io.metersphere.utils.ReportStatusUtil;
|
||||
import io.metersphere.base.domain.*;
|
||||
import io.metersphere.base.mapper.*;
|
||||
|
@ -30,10 +31,6 @@ import io.metersphere.log.vo.OperatingLogDetails;
|
|||
import io.metersphere.log.vo.api.ModuleReference;
|
||||
import io.metersphere.notice.sender.NoticeModel;
|
||||
import io.metersphere.notice.service.NoticeSendService;
|
||||
import io.metersphere.service.BaseShareInfoService;
|
||||
import io.metersphere.service.BaseUserService;
|
||||
import io.metersphere.service.ServiceUtils;
|
||||
import io.metersphere.service.SystemParameterService;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.apache.commons.beanutils.BeanMap;
|
||||
import org.apache.commons.collections4.CollectionUtils;
|
||||
|
@ -89,6 +86,8 @@ public class ApiScenarioReportService {
|
|||
private TestPlanApiScenarioMapper testPlanApiScenarioMapper;
|
||||
@Resource
|
||||
BaseShareInfoService baseShareInfoService;
|
||||
@Resource
|
||||
private RedisTemplateService redisTemplateService;
|
||||
|
||||
public void saveResult(ResultDTO dto) {
|
||||
// 报告详情内容
|
||||
|
@ -305,6 +304,10 @@ public class ApiScenarioReportService {
|
|||
public ApiScenarioReport updatePlanCase(ResultDTO dto) {
|
||||
ResultVO resultVO = ReportStatusUtil.computedProcess(dto);
|
||||
ApiScenarioReport report = editReport(dto.getReportType(), dto.getReportId(), resultVO.getStatus(), dto.getRunMode());
|
||||
// 当前资源正在执行中
|
||||
if (redisTemplateService.has(dto.getTestId())) {
|
||||
return report;
|
||||
}
|
||||
TestPlanApiScenario testPlanApiScenario = testPlanApiScenarioMapper.selectByPrimaryKey(dto.getTestId());
|
||||
if (testPlanApiScenario != null) {
|
||||
if (report != null) {
|
||||
|
@ -341,6 +344,10 @@ public class ApiScenarioReportService {
|
|||
|
||||
ResultVO resultVO = ReportStatusUtil.computedProcess(dto);
|
||||
ApiScenarioReport report = editReport(dto.getReportType(), dto.getReportId(), resultVO.getStatus(), dto.getRunMode());
|
||||
// 当前资源正在执行中
|
||||
if (redisTemplateService.has(dto.getTestId())) {
|
||||
return report;
|
||||
}
|
||||
if (report != null) {
|
||||
if (StringUtils.isNotEmpty(dto.getTestPlanReportId()) && !testPlanReportIdList.contains(dto.getTestPlanReportId())) {
|
||||
testPlanReportIdList.add(dto.getTestPlanReportId());
|
||||
|
|
Loading…
Reference in New Issue