fix(测试计划): 修复测试计划中单用例调试结果偶发收不到问题
--bug=1026377 --user=赵勇 【测试计划】接口测试用例执行,禁用项目管理-接口执行资源池后,执行接口用例,local无结果,k8s资源池正常输出 https://www.tapd.cn/55049933/s/1373857 Signed-off-by: fit2-zhao <yong.zhao@fit2cloud.com>
This commit is contained in:
parent
c0c8ba5aab
commit
77490c32a1
|
@ -71,6 +71,7 @@ public class ApiCaseExecuteService {
|
|||
private ExtTestPlanApiCaseMapper extTestPlanApiCaseMapper;
|
||||
@Resource
|
||||
private RedisTemplateService redisTemplateService;
|
||||
|
||||
/**
|
||||
* 测试计划case执行
|
||||
*
|
||||
|
@ -134,7 +135,7 @@ public class ApiCaseExecuteService {
|
|||
executeQueue.put(testPlanApiCase.getId(), report);
|
||||
responseDTOS.add(new MsExecResponseDTO(testPlanApiCase.getId(), report.getId(), request.getTriggerMode()));
|
||||
// 执行中资源锁住,防止重复更新造成LOCK WAIT
|
||||
redisTemplateService.lock(testPlanApiCase.getId());
|
||||
redisTemplateService.lock(testPlanApiCase.getId(), report.getId());
|
||||
|
||||
LoggerUtil.info("预生成测试用例结果报告:" + report.getName(), report.getId());
|
||||
}
|
||||
|
|
|
@ -161,7 +161,7 @@ public class ApiScenarioExecuteService {
|
|||
DBTestQueue executionQueue = apiExecutionQueueService.add(
|
||||
executeQueue, request.getConfig().getResourcePoolId(),
|
||||
ApiRunMode.SCENARIO.name(), planReportId, reportType,
|
||||
request.getRunMode(), request.getConfig());
|
||||
request.getRunMode(), request.getConfig());
|
||||
|
||||
// 预生成报告
|
||||
if (!request.isRerun() && !GenerateHashTreeUtil.isSetReport(request.getConfig())) {
|
||||
|
@ -347,7 +347,7 @@ public class ApiScenarioExecuteService {
|
|||
apiScenarioReportStructureService.save(scenario, report.getId(), request.getConfig() != null ? request.getConfig().getReportType() : null);
|
||||
}
|
||||
// 执行中资源锁住,防止重复更新造成LOCK WAIT
|
||||
redisTemplateService.lock(planApiScenario.getId());
|
||||
redisTemplateService.lock(planApiScenario.getId(), report.getId());
|
||||
// 重置报告ID
|
||||
reportId = UUID.randomUUID().toString();
|
||||
}
|
||||
|
|
|
@ -1,15 +1,18 @@
|
|||
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.data.redis.core.script.DefaultRedisScript;
|
||||
import org.springframework.data.redis.core.script.RedisScript;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@Service
|
||||
|
@ -58,29 +61,42 @@ public class RedisTemplateService {
|
|||
/**
|
||||
* 加锁
|
||||
*/
|
||||
public boolean lock(String key) {
|
||||
return redisTemplate.opsForValue().setIfAbsent(StringUtils.join(PRX, key), LockEnum.LOCK.name());
|
||||
public boolean lock(String key, String value) {
|
||||
boolean hasReport = redisTemplate.opsForValue().setIfAbsent(
|
||||
StringUtils.join(PRX, key),
|
||||
value,
|
||||
TIME_OUT,
|
||||
TimeUnit.MINUTES);
|
||||
if (!hasReport) {
|
||||
redisTemplate.opsForValue().setIfPresent(
|
||||
StringUtils.join(PRX, key),
|
||||
value,
|
||||
TIME_OUT,
|
||||
TimeUnit.MINUTES);
|
||||
}
|
||||
return hasReport;
|
||||
}
|
||||
|
||||
public boolean has(String key) {
|
||||
public boolean has(String key, String reportId) {
|
||||
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;
|
||||
return ObjectUtils.isNotEmpty(value) && StringUtils.equals(reportId, String.valueOf(value));
|
||||
} catch (Exception e) {
|
||||
LogUtil.error(e);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 解锁
|
||||
*/
|
||||
public boolean unlock(String key, String value) {
|
||||
String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
|
||||
RedisScript<Long> redisScript = new DefaultRedisScript<>(script, Long.class);
|
||||
Long result = redisTemplate.execute(redisScript, Collections.singletonList(StringUtils.join(PRX, key)), value);
|
||||
if (Objects.equals(1L, result)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,4 +21,11 @@ public class ApiCaseResultService {
|
|||
resultMapper.sqlInsert(new LinkedList<>(executeQueue.values()));
|
||||
}
|
||||
}
|
||||
|
||||
@Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
|
||||
public void batchSave(ApiDefinitionExecResultWithBLOBs result) {
|
||||
if (result != null) {
|
||||
resultMapper.sqlInsert(new LinkedList<>(){{this.add(result);}});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -103,12 +103,13 @@ public class ApiDefinitionExecResultService {
|
|||
User user = getUser(dto, result);
|
||||
//如果是测试计划用例,更新接口用例的上次执行结果
|
||||
TestPlanApiCase testPlanApiCase = testPlanApiCaseMapper.selectByPrimaryKey(dto.getTestId());
|
||||
if (testPlanApiCase != null && !redisTemplateService.has(dto.getTestId())) {
|
||||
if (testPlanApiCase != null && redisTemplateService.has(dto.getTestId(), dto.getReportId())) {
|
||||
ApiTestCaseWithBLOBs apiTestCase = apiTestCaseMapper.selectByPrimaryKey(testPlanApiCase.getApiCaseId());
|
||||
if (apiTestCase != null) {
|
||||
apiTestCase.setLastResultId(dto.getReportId());
|
||||
apiTestCaseMapper.updateByPrimaryKeySelective(apiTestCase);
|
||||
}
|
||||
redisTemplateService.unlock(dto.getTestId(), dto.getReportId());
|
||||
}
|
||||
// 发送通知
|
||||
LoggerUtil.info("执行结果【 " + result.getName() + " 】入库存储完成");
|
||||
|
@ -223,13 +224,11 @@ public class ApiDefinitionExecResultService {
|
|||
}
|
||||
|
||||
public void setExecResult(String id, String status, Long time) {
|
||||
if (!redisTemplateService.has(id)) {
|
||||
TestPlanApiCase apiCase = new TestPlanApiCase();
|
||||
apiCase.setId(id);
|
||||
apiCase.setStatus(status);
|
||||
apiCase.setUpdateTime(time);
|
||||
testPlanApiCaseMapper.updateByPrimaryKeySelective(apiCase);
|
||||
}
|
||||
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) {
|
||||
|
@ -243,7 +242,7 @@ public class ApiDefinitionExecResultService {
|
|||
ApiRunMode.MANUAL_PLAN.name())) {
|
||||
TestPlanApiCase testPlanApiCase = testPlanApiCaseMapper.selectByPrimaryKey(testId);
|
||||
ApiTestCaseWithBLOBs caseWithBLOBs = null;
|
||||
if (testPlanApiCase != null && !redisTemplateService.has(testId)) {
|
||||
if (testPlanApiCase != null && redisTemplateService.has(testId, saveResult.getId())) {
|
||||
this.setExecResult(testId, status, time);
|
||||
caseWithBLOBs = apiTestCaseMapper.selectByPrimaryKey(testPlanApiCase.getApiCaseId());
|
||||
testPlanApiCase.setStatus(status);
|
||||
|
@ -252,6 +251,7 @@ public class ApiDefinitionExecResultService {
|
|||
if (LoggerUtil.getLogger().isDebugEnabled()) {
|
||||
LoggerUtil.debug("更新测试计划用例【 " + testPlanApiCase.getId() + " 】");
|
||||
}
|
||||
redisTemplateService.unlock(testId, saveResult.getId());
|
||||
}
|
||||
if (caseWithBLOBs != null) {
|
||||
name = caseWithBLOBs.getName();
|
||||
|
@ -265,7 +265,7 @@ public class ApiDefinitionExecResultService {
|
|||
projectId = apiDefinition.getProjectId();
|
||||
} else {
|
||||
ApiTestCaseWithBLOBs caseWithBLOBs = apiTestCaseMapper.selectByPrimaryKey(testId);
|
||||
if (caseWithBLOBs != null && !redisTemplateService.has(testId)) {
|
||||
if (caseWithBLOBs != null && redisTemplateService.has(testId, saveResult.getId())) {
|
||||
// 更新用例最后执行结果
|
||||
caseWithBLOBs.setLastResultId(reportId);
|
||||
caseWithBLOBs.setStatus(status);
|
||||
|
@ -278,6 +278,7 @@ public class ApiDefinitionExecResultService {
|
|||
name = caseWithBLOBs.getName();
|
||||
version = caseWithBLOBs.getVersionId();
|
||||
projectId = caseWithBLOBs.getProjectId();
|
||||
redisTemplateService.unlock(testId, saveResult.getId());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -296,7 +297,7 @@ public class ApiDefinitionExecResultService {
|
|||
ApiRunMode.SCHEDULE_API_PLAN.name(),
|
||||
ApiRunMode.JENKINS_API_PLAN.name(),
|
||||
ApiRunMode.MANUAL_PLAN.name())) {
|
||||
if (!redisTemplateService.has(testId)) {
|
||||
if (redisTemplateService.has(testId, reportId)) {
|
||||
TestPlanApiCase apiCase = new TestPlanApiCase();
|
||||
apiCase.setId(testId);
|
||||
apiCase.setStatus(status);
|
||||
|
@ -307,6 +308,7 @@ public class ApiDefinitionExecResultService {
|
|||
reviewApiCase.setId(testId);
|
||||
reviewApiCase.setStatus(status);
|
||||
reviewApiCase.setUpdateTime(System.currentTimeMillis());
|
||||
redisTemplateService.unlock(testId, reportId);
|
||||
}
|
||||
} else {
|
||||
// 更新用例最后执行结果
|
||||
|
@ -339,7 +341,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 && !redisTemplateService.has(dto.getTestId())) {
|
||||
if (apiCase != null && redisTemplateService.has(dto.getTestId(), dto.getReportId())) {
|
||||
String projectId = extTestPlanApiCaseMapper.selectProjectId(apiCase.getId());
|
||||
ApiDefinition apiDefinition = extApiTestCaseMapper.selectApiBasicInfoByCaseId(apiCase.getId());
|
||||
String version = apiDefinition == null ? "" : apiDefinition.getVersionId();
|
||||
|
@ -353,6 +355,7 @@ public class ApiDefinitionExecResultService {
|
|||
apiTestCase.setLastResultId(dto.getReportId());
|
||||
apiTestCaseMapper.updateByPrimaryKeySelective(apiTestCase);
|
||||
}
|
||||
redisTemplateService.unlock(dto.getTestId(), dto.getReportId());
|
||||
}
|
||||
} else {
|
||||
this.setExecResult(dto.getTestId(), reportResult.getStatus(), item.getStartTime());
|
||||
|
|
|
@ -167,6 +167,8 @@ public class ApiDefinitionService {
|
|||
private BaseQuotaService baseQuotaService;
|
||||
@Resource
|
||||
private BaseEnvGroupProjectService environmentGroupProjectService;
|
||||
@Resource
|
||||
private ApiCaseResultService apiCaseResultService;
|
||||
|
||||
|
||||
private static final String COPY = "Copy";
|
||||
|
@ -2018,7 +2020,8 @@ public class ApiDefinitionService {
|
|||
result.setEnvConfig(JSON.toJSONString(runModeConfigDTO));
|
||||
}
|
||||
result.setActuator(request.getConfig().getResourcePoolId());
|
||||
apiDefinitionExecResultMapper.insert(result);
|
||||
apiCaseResultService.batchSave(result);
|
||||
|
||||
}
|
||||
if (request.isEditCaseRequest() && CollectionUtils.isNotEmpty(request.getTestElement().getHashTree()) && CollectionUtils.isNotEmpty(request.getTestElement().getHashTree().get(0).getHashTree())) {
|
||||
ApiTestCaseWithBLOBs record = new ApiTestCaseWithBLOBs();
|
||||
|
|
|
@ -34,11 +34,9 @@ import io.metersphere.log.vo.OperatingLogDetails;
|
|||
import io.metersphere.request.OrderRequest;
|
||||
import io.metersphere.request.ResetOrderRequest;
|
||||
import io.metersphere.service.BaseProjectService;
|
||||
import io.metersphere.service.RedisTemplateService;
|
||||
import io.metersphere.service.ServiceUtils;
|
||||
import io.metersphere.service.definition.ApiDefinitionExecResultService;
|
||||
import io.metersphere.service.definition.ApiDefinitionService;
|
||||
import io.metersphere.service.definition.ApiModuleService;
|
||||
import io.metersphere.service.definition.ApiTestCaseService;
|
||||
import io.metersphere.service.definition.*;
|
||||
import io.metersphere.service.plan.remote.TestPlanService;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.apache.commons.collections.MapUtils;
|
||||
|
@ -96,6 +94,10 @@ public class TestPlanApiCaseService {
|
|||
private JMeterService jMeterService;
|
||||
@Resource
|
||||
private ApiScenarioReportMapper apiScenarioReportMapper;
|
||||
@Resource
|
||||
private ApiCaseResultService apiCaseResultService;
|
||||
@Resource
|
||||
private RedisTemplateService redisTemplateService;
|
||||
|
||||
public List<TestPlanApiCaseDTO> list(ApiTestCaseRequest request) {
|
||||
request.setProjectId(null);
|
||||
|
@ -783,7 +785,6 @@ public class TestPlanApiCaseService {
|
|||
if (apiCase == null) {
|
||||
MSException.throwException("用例已经被删除");
|
||||
}
|
||||
|
||||
String reportName = apiCase.getName();
|
||||
ApiDefinitionExecResultWithBLOBs result = ApiDefinitionExecResultUtil.add(testId, ApiReportStatus.RUNNING.name(), reportId, Objects.requireNonNull(SessionUtils.getUser()).getId());
|
||||
result.setName(reportName);
|
||||
|
@ -799,7 +800,7 @@ public class TestPlanApiCaseService {
|
|||
result.setEnvConfig(JSON.toJSONString(runModeConfigDTO));
|
||||
}
|
||||
result.setActuator(runModeConfigDTO.getResourcePoolId());
|
||||
apiDefinitionExecResultMapper.insert(result);
|
||||
apiCaseResultService.batchSave(result);
|
||||
apiCase.setId(testId);
|
||||
|
||||
RunCaseRequest request = new RunCaseRequest();
|
||||
|
@ -812,6 +813,8 @@ public class TestPlanApiCaseService {
|
|||
request.setTestPlanId(testPlanApiCase.getTestPlanId());
|
||||
Map<String, Object> extendedParameters = new HashMap<>();
|
||||
extendedParameters.put(ExtendedParameter.SYNC_STATUS, true);
|
||||
|
||||
redisTemplateService.lock(testId, reportId);
|
||||
apiExecuteService.exec(request, extendedParameters);
|
||||
}
|
||||
|
||||
|
|
|
@ -305,7 +305,7 @@ public class ApiScenarioReportService {
|
|||
ResultVO resultVO = ReportStatusUtil.computedProcess(dto);
|
||||
ApiScenarioReport report = editReport(dto.getReportType(), dto.getReportId(), resultVO.getStatus(), dto.getRunMode());
|
||||
// 当前资源正在执行中
|
||||
if (redisTemplateService.has(dto.getTestId())) {
|
||||
if (!redisTemplateService.has(dto.getTestId(), dto.getReportId())) {
|
||||
return report;
|
||||
}
|
||||
TestPlanApiScenario testPlanApiScenario = testPlanApiScenarioMapper.selectByPrimaryKey(dto.getTestId());
|
||||
|
@ -335,6 +335,7 @@ public class ApiScenarioReportService {
|
|||
scenario.setExecuteTimes(executeTimes + 1);
|
||||
apiScenarioMapper.updateByPrimaryKey(scenario);
|
||||
}
|
||||
redisTemplateService.unlock(dto.getTestId(), dto.getReportId());
|
||||
}
|
||||
return report;
|
||||
}
|
||||
|
@ -345,7 +346,7 @@ public class ApiScenarioReportService {
|
|||
ResultVO resultVO = ReportStatusUtil.computedProcess(dto);
|
||||
ApiScenarioReport report = editReport(dto.getReportType(), dto.getReportId(), resultVO.getStatus(), dto.getRunMode());
|
||||
// 当前资源正在执行中
|
||||
if (redisTemplateService.has(dto.getTestId())) {
|
||||
if (!redisTemplateService.has(dto.getTestId(), dto.getReportId())) {
|
||||
return report;
|
||||
}
|
||||
if (report != null) {
|
||||
|
@ -375,6 +376,7 @@ public class ApiScenarioReportService {
|
|||
scenario.setExecuteTimes(executeTimes + 1);
|
||||
apiScenarioMapper.updateByPrimaryKey(scenario);
|
||||
}
|
||||
redisTemplateService.unlock(dto.getTestId(), dto.getReportId());
|
||||
}
|
||||
}
|
||||
return report;
|
||||
|
|
Loading…
Reference in New Issue