diff --git a/src/backend/ci/core/common/common-pipeline-yaml/src/main/kotlin/com/tencent/devops/process/yaml/transfer/ModelTransfer.kt b/src/backend/ci/core/common/common-pipeline-yaml/src/main/kotlin/com/tencent/devops/process/yaml/transfer/ModelTransfer.kt index 740c584e649..936451c8db4 100644 --- a/src/backend/ci/core/common/common-pipeline-yaml/src/main/kotlin/com/tencent/devops/process/yaml/transfer/ModelTransfer.kt +++ b/src/backend/ci/core/common/common-pipeline-yaml/src/main/kotlin/com/tencent/devops/process/yaml/transfer/ModelTransfer.kt @@ -37,6 +37,7 @@ import com.tencent.devops.common.pipeline.pojo.setting.PipelineSetting import com.tencent.devops.common.pipeline.pojo.setting.Subscription import com.tencent.devops.common.pipeline.pojo.transfer.IfType import com.tencent.devops.common.pipeline.utils.PIPELINE_SETTING_CONCURRENCY_GROUP_DEFAULT +import com.tencent.devops.common.pipeline.utils.PIPELINE_SETTING_MAX_CON_QUEUE_SIZE_MAX import com.tencent.devops.process.yaml.pojo.YamlVersion import com.tencent.devops.process.yaml.transfer.VariableDefault.nullIfDefault import com.tencent.devops.process.yaml.transfer.aspect.PipelineTransferAspectWrapper @@ -96,6 +97,7 @@ class ModelTransfer @Autowired constructor( waitQueueTimeMinute = yaml.concurrency?.queueTimeoutMinutes ?: VariableDefault.DEFAULT_WAIT_QUEUE_TIME_MINUTE, maxQueueSize = yaml.concurrency?.queueLength ?: VariableDefault.DEFAULT_PIPELINE_SETTING_MAX_QUEUE_SIZE, + maxConRunningQueueSize = yaml.concurrency?.maxParallel ?: PIPELINE_SETTING_MAX_CON_QUEUE_SIZE_MAX, labels = yaml2Labels(yamlInput), pipelineAsCodeSettings = yamlInput.asCodeSettings, successSubscriptionList = yamlNotice2Setting( @@ -313,7 +315,18 @@ class ModelTransfer @Autowired constructor( queueLength = setting.maxQueueSize .nullIfDefault(VariableDefault.DEFAULT_PIPELINE_SETTING_MAX_QUEUE_SIZE), queueTimeoutMinutes = setting.waitQueueTimeMinute - .nullIfDefault(VariableDefault.DEFAULT_WAIT_QUEUE_TIME_MINUTE) + .nullIfDefault(VariableDefault.DEFAULT_WAIT_QUEUE_TIME_MINUTE), + maxParallel = null + ) + } + if (setting.runLockType == PipelineRunLockType.MULTIPLE) { + return Concurrency( + group = null, + cancelInProgress = null, + queueLength = null, + queueTimeoutMinutes = setting.waitQueueTimeMinute + .nullIfDefault(VariableDefault.DEFAULT_WAIT_QUEUE_TIME_MINUTE), + maxParallel = setting.maxConRunningQueueSize.nullIfDefault(PIPELINE_SETTING_MAX_CON_QUEUE_SIZE_MAX) ) } return null diff --git a/src/backend/ci/core/common/common-pipeline-yaml/src/main/kotlin/com/tencent/devops/process/yaml/v3/models/Concurrency.kt b/src/backend/ci/core/common/common-pipeline-yaml/src/main/kotlin/com/tencent/devops/process/yaml/v3/models/Concurrency.kt index 24ce8799d0e..7c16a9ba18e 100644 --- a/src/backend/ci/core/common/common-pipeline-yaml/src/main/kotlin/com/tencent/devops/process/yaml/v3/models/Concurrency.kt +++ b/src/backend/ci/core/common/common-pipeline-yaml/src/main/kotlin/com/tencent/devops/process/yaml/v3/models/Concurrency.kt @@ -40,5 +40,7 @@ data class Concurrency( @JsonProperty("queue-length") val queueLength: Int?, @JsonProperty("queue-timeout-minutes") - val queueTimeoutMinutes: Int? + val queueTimeoutMinutes: Int?, + @JsonProperty("max-parallel") + val maxParallel: Int? ) diff --git a/src/backend/ci/core/common/common-pipeline-yaml/src/main/resources/schema/V3_0/ci.json b/src/backend/ci/core/common/common-pipeline-yaml/src/main/resources/schema/V3_0/ci.json index 9b985d281e1..7d3d363d37f 100644 --- a/src/backend/ci/core/common/common-pipeline-yaml/src/main/resources/schema/V3_0/ci.json +++ b/src/backend/ci/core/common/common-pipeline-yaml/src/main/resources/schema/V3_0/ci.json @@ -1737,6 +1737,9 @@ "additionalProperties" : false, "required" : [ "jobs" ], "properties" : { + "enable" : { + "type" : "boolean" + }, "name" : { "type" : "string" }, @@ -1875,6 +1878,9 @@ "required" : [ "steps" ], "additionalProperties" : false, "properties" : { + "enable" : { + "type" : "boolean" + }, "name" : { "type" : "string" }, @@ -2110,6 +2116,11 @@ "type" : "integer", "minimum" : 1, "maximum" : 1440 + }, + "max-parallel" : { + "type" : "integer", + "minimum" : 1, + "maximum" : 200 } } }, @@ -2318,6 +2329,9 @@ "required" : [ "steps" ], "additionalProperties" : false, "properties" : { + "enable" : { + "type" : "boolean" + }, "name" : { "type" : "string" }, diff --git a/src/backend/ci/core/common/common-pipeline/src/main/kotlin/com/tencent/devops/common/pipeline/pojo/setting/PipelineSetting.kt b/src/backend/ci/core/common/common-pipeline/src/main/kotlin/com/tencent/devops/common/pipeline/pojo/setting/PipelineSetting.kt index 087448cd44d..0b20127f6a8 100644 --- a/src/backend/ci/core/common/common-pipeline/src/main/kotlin/com/tencent/devops/common/pipeline/pojo/setting/PipelineSetting.kt +++ b/src/backend/ci/core/common/common-pipeline/src/main/kotlin/com/tencent/devops/common/pipeline/pojo/setting/PipelineSetting.kt @@ -28,10 +28,8 @@ package com.tencent.devops.common.pipeline.pojo.setting import com.tencent.devops.common.api.pojo.PipelineAsCodeSettings -import com.tencent.devops.common.api.util.DateTimeUtil import com.tencent.devops.common.pipeline.utils.PIPELINE_RES_NUM_MIN import com.tencent.devops.common.pipeline.utils.PIPELINE_SETTING_CONCURRENCY_GROUP_DEFAULT -import com.tencent.devops.common.pipeline.utils.PIPELINE_SETTING_MAX_CON_QUEUE_SIZE_MAX import com.tencent.devops.common.pipeline.utils.PIPELINE_SETTING_MAX_QUEUE_SIZE_DEFAULT import com.tencent.devops.common.pipeline.utils.PIPELINE_SETTING_WAIT_QUEUE_TIME_MINUTE_DEFAULT import com.tencent.devops.common.web.annotation.BkField @@ -84,10 +82,10 @@ data class PipelineSetting( var concurrencyGroup: String? = PIPELINE_SETTING_CONCURRENCY_GROUP_DEFAULT, @get:Schema(title = "并发时,是否相同group取消正在执行的流水线", required = false) var concurrencyCancelInProgress: Boolean = false, + @get:Schema(title = "并发构建数量限制", required = false) + var maxConRunningQueueSize: Int? = null, // MULTIPLE类型时,并发构建数量限制 // 平台系统控制相关配置 —— 不作为生成版本的配置 - @get:Schema(title = "并发构建数量限制", required = false) - var maxConRunningQueueSize: Int? = PIPELINE_SETTING_MAX_CON_QUEUE_SIZE_MAX, // MULTIPLE类型时,并发构建数量限制 @get:Schema(title = "保存流水线编排的最大个数", required = false) val maxPipelineResNum: Int = PIPELINE_RES_NUM_MIN, // 保存流水线编排的最大个数 @get:Schema(title = "重试时清理引擎变量表", required = false) @@ -112,9 +110,7 @@ data class PipelineSetting( version = 1, desc = pipelineName, maxPipelineResNum = maxPipelineResNum ?: PIPELINE_RES_NUM_MIN, - waitQueueTimeMinute = DateTimeUtil.minuteToSecond( - PIPELINE_SETTING_WAIT_QUEUE_TIME_MINUTE_DEFAULT - ), + waitQueueTimeMinute = PIPELINE_SETTING_WAIT_QUEUE_TIME_MINUTE_DEFAULT, maxQueueSize = PIPELINE_SETTING_MAX_QUEUE_SIZE_DEFAULT, runLockType = PipelineRunLockType.MULTIPLE, successSubscription = null, diff --git a/src/backend/ci/core/common/common-pipeline/src/main/kotlin/com/tencent/devops/common/pipeline/utils/Constants.kt b/src/backend/ci/core/common/common-pipeline/src/main/kotlin/com/tencent/devops/common/pipeline/utils/Constants.kt index f07249df6ac..0821e7b5e46 100644 --- a/src/backend/ci/core/common/common-pipeline/src/main/kotlin/com/tencent/devops/common/pipeline/utils/Constants.kt +++ b/src/backend/ci/core/common/common-pipeline/src/main/kotlin/com/tencent/devops/common/pipeline/utils/Constants.kt @@ -40,7 +40,7 @@ const val PIPELINE_SETTING_MAX_CON_QUEUE_SIZE_MAX = 200 /** * 流水线设置-最大排队时间-默认值 单位:分钟 */ -const val PIPELINE_SETTING_WAIT_QUEUE_TIME_MINUTE_DEFAULT = 1 +const val PIPELINE_SETTING_WAIT_QUEUE_TIME_MINUTE_DEFAULT = 10 /** * 流水线设置-CONCURRENCY GROUP 并发组-默认值 diff --git a/src/backend/ci/core/openapi/biz-openapi/src/main/kotlin/com/tencent/devops/openapi/es/impl/ESServiceImpl.kt b/src/backend/ci/core/openapi/biz-openapi/src/main/kotlin/com/tencent/devops/openapi/es/impl/ESServiceImpl.kt index 74719d65fde..748356262f2 100644 --- a/src/backend/ci/core/openapi/biz-openapi/src/main/kotlin/com/tencent/devops/openapi/es/impl/ESServiceImpl.kt +++ b/src/backend/ci/core/openapi/biz-openapi/src/main/kotlin/com/tencent/devops/openapi/es/impl/ESServiceImpl.kt @@ -33,6 +33,7 @@ import com.tencent.devops.common.es.ESClient import com.tencent.devops.common.es.client.LogClient import com.tencent.devops.common.redis.RedisLock import com.tencent.devops.common.redis.RedisOperation +import com.tencent.devops.common.service.utils.RetryUtils import com.tencent.devops.openapi.es.ESIndexUtils import com.tencent.devops.openapi.es.ESMessage import com.tencent.devops.openapi.es.IESService @@ -122,27 +123,39 @@ class ESServiceImpl constructor( while (true) { val message = queue.take() ?: continue buf.add(message) - if (buf.size == BULK_BUFFER_SIZE) { - val currentEpoch = System.currentTimeMillis() - try { - prepareIndex() - if (doAddMultiLines(buf) == 0) { - throw ExecuteException( - "None of lines is inserted successfully to ES " - ) - } else { - buf.clear() + if (buf.size >= BULK_BUFFER_SIZE) { + RetryUtils.execute(action = object : RetryUtils.Action { + override fun execute() { + doAdd() } - } finally { - val elapse = System.currentTimeMillis() - currentEpoch - // #4265 当日志消息处理时间过长时打印消息内容 - if (elapse >= INDEX_STORAGE_WARN_MILLIS) logger.warn( - " addBatchLogEvent spent too much time($elapse)" - ) - } + + override fun fail(e: Throwable) { + logger.error("add to es failed", e) + } + }, retryTime = 6, retryPeriodMills = 10000) } } } + + private fun doAdd() { + val currentEpoch = System.currentTimeMillis() + try { + prepareIndex() + if (doAddMultiLines(buf) == 0) { + throw ExecuteException( + "None of lines is inserted successfully to ES " + ) + } else { + buf.clear() + } + } finally { + val elapse = System.currentTimeMillis() - currentEpoch + // #4265 当日志消息处理时间过长时打印消息内容 + if (elapse >= INDEX_STORAGE_WARN_MILLIS) logger.warn( + " addBatchLogEvent spent too much time($elapse)" + ) + } + } } private fun doAddMultiLines(logMessages: List): Int { @@ -269,7 +282,7 @@ class ESServiceImpl constructor( return try { logger.info( "[${createClient.clusterName}][$index]|createIndex|: shards[${createClient.shards}]" + - " replicas[${createClient.replicas}] shardsPerNode[${createClient.shardsPerNode}]" + " replicas[${createClient.replicas}] shardsPerNode[${createClient.shardsPerNode}]" ) val request = CreateIndexRequest(index) .settings( diff --git a/src/backend/ci/core/process/api-process/src/main/kotlin/com/tencent/devops/process/pojo/setting/PipelineSettingVersion.kt b/src/backend/ci/core/process/api-process/src/main/kotlin/com/tencent/devops/process/pojo/setting/PipelineSettingVersion.kt index 9a26ce2d2bb..efcdadabd78 100644 --- a/src/backend/ci/core/process/api-process/src/main/kotlin/com/tencent/devops/process/pojo/setting/PipelineSettingVersion.kt +++ b/src/backend/ci/core/process/api-process/src/main/kotlin/com/tencent/devops/process/pojo/setting/PipelineSettingVersion.kt @@ -71,7 +71,9 @@ data class PipelineSettingVersion( @get:Schema(title = "并发时,设定的group", required = false) var concurrencyGroup: String?, @get:Schema(title = "并发时,是否相同group取消正在执行的流水线", required = false) - var concurrencyCancelInProgress: Boolean? + var concurrencyCancelInProgress: Boolean?, + @get:Schema(title = "并发构建数量限制", required = false) + var maxConRunningQueueSize: Int? = null // MULTIPLE类型时,并发构建数量限制 ) { companion object { @@ -90,7 +92,8 @@ data class PipelineSettingVersion( maxQueueSize = setting.maxQueueSize, buildNumRule = setting.buildNumRule, concurrencyCancelInProgress = setting.concurrencyCancelInProgress, - concurrencyGroup = setting.concurrencyGroup + concurrencyGroup = setting.concurrencyGroup, + maxConRunningQueueSize = setting.maxConRunningQueueSize ) } } diff --git a/src/backend/ci/core/process/biz-base/src/main/kotlin/com/tencent/devops/process/dao/PipelineSettingDao.kt b/src/backend/ci/core/process/biz-base/src/main/kotlin/com/tencent/devops/process/dao/PipelineSettingDao.kt index 3cab8b6165d..a27fe282702 100644 --- a/src/backend/ci/core/process/biz-base/src/main/kotlin/com/tencent/devops/process/dao/PipelineSettingDao.kt +++ b/src/backend/ci/core/process/biz-base/src/main/kotlin/com/tencent/devops/process/dao/PipelineSettingDao.kt @@ -158,14 +158,11 @@ class PipelineSettingDao { .set(SUCCESS_SUBSCRIPTION, JsonUtil.toJson(successSubscriptionList, false)) .set(FAILURE_SUBSCRIPTION, JsonUtil.toJson(failSubscriptionList, false)) .set(VERSION, setting.version) + .set(MAX_CON_RUNNING_QUEUE_SIZE, setting.maxConRunningQueueSize) // pipelineAsCodeSettings 默认传空不更新 setting.pipelineAsCodeSettings?.let { self -> insert.set(PIPELINE_AS_CODE_SETTINGS, JsonUtil.toJson(self, false)) } - // maxConRunningQueueSize 默认传空不更新 - if (setting.maxConRunningQueueSize != null) { - insert.set(MAX_CON_RUNNING_QUEUE_SIZE, setting.maxConRunningQueueSize) - } return insert.execute() } } diff --git a/src/backend/ci/core/process/biz-base/src/main/kotlin/com/tencent/devops/process/dao/PipelineSettingVersionDao.kt b/src/backend/ci/core/process/biz-base/src/main/kotlin/com/tencent/devops/process/dao/PipelineSettingVersionDao.kt index 0debcb1618d..0991b83670a 100644 --- a/src/backend/ci/core/process/biz-base/src/main/kotlin/com/tencent/devops/process/dao/PipelineSettingVersionDao.kt +++ b/src/backend/ci/core/process/biz-base/src/main/kotlin/com/tencent/devops/process/dao/PipelineSettingVersionDao.kt @@ -76,7 +76,8 @@ class PipelineSettingVersionDao { SUCCESS_SUBSCRIPTION, FAILURE_SUBSCRIPTION, PIPELINE_AS_CODE_SETTINGS, - VERSION + VERSION, + MAX_CON_RUNNING_QUEUE_SIZE ).values( id, setting.projectId, @@ -98,7 +99,8 @@ class PipelineSettingVersionDao { setting.pipelineAsCodeSettings?.let { self -> JsonUtil.toJson(self, false) }, - version + version, + setting.maxConRunningQueueSize ?: -1 ).onDuplicateKeyUpdate() .set(NAME, setting.pipelineName) .set(DESC, setting.desc) @@ -111,6 +113,7 @@ class PipelineSettingVersionDao { .set(CONCURRENCY_CANCEL_IN_PROGRESS, setting.concurrencyCancelInProgress) .set(SUCCESS_SUBSCRIPTION, JsonUtil.toJson(successSubscriptionList, false)) .set(FAILURE_SUBSCRIPTION, JsonUtil.toJson(failSubscriptionList, false)) + .set(MAX_CON_RUNNING_QUEUE_SIZE, setting.maxConRunningQueueSize ?: -1) .execute() } } @@ -219,7 +222,8 @@ class PipelineSettingVersionDao { maxQueueSize = t.maxQueueSize, buildNumRule = t.buildNumRule, concurrencyCancelInProgress = t.concurrencyCancelInProgress, - concurrencyGroup = t.concurrencyGroup + concurrencyGroup = t.concurrencyGroup, + maxConRunningQueueSize = t.maxConRunningQueueSize ) } } diff --git a/src/backend/ci/core/process/biz-base/src/main/kotlin/com/tencent/devops/process/engine/dao/PipelineBuildDao.kt b/src/backend/ci/core/process/biz-base/src/main/kotlin/com/tencent/devops/process/engine/dao/PipelineBuildDao.kt index 019320cf948..faead107747 100644 --- a/src/backend/ci/core/process/biz-base/src/main/kotlin/com/tencent/devops/process/engine/dao/PipelineBuildDao.kt +++ b/src/backend/ci/core/process/biz-base/src/main/kotlin/com/tencent/devops/process/engine/dao/PipelineBuildDao.kt @@ -54,6 +54,9 @@ import com.tencent.devops.process.pojo.BuildStageStatus import com.tencent.devops.process.pojo.PipelineBuildMaterial import com.tencent.devops.process.pojo.app.StartBuildContext import com.tencent.devops.process.pojo.code.WebhookInfo +import java.sql.Timestamp +import java.time.LocalDateTime +import javax.ws.rs.core.Response import org.jooq.Condition import org.jooq.DSLContext import org.jooq.DatePart @@ -62,9 +65,6 @@ import org.jooq.RecordMapper import org.jooq.SelectConditionStep import org.jooq.impl.DSL import org.springframework.stereotype.Repository -import java.sql.Timestamp -import java.time.LocalDateTime -import javax.ws.rs.core.Response @Suppress("ALL") @Repository @@ -268,6 +268,29 @@ class PipelineBuildDao { } else normal } + fun countAllBuildWithStatus( + dslContext: DSLContext, + projectId: String, + pipelineId: String, + status: Set + ): Int { + val normal = with(T_PIPELINE_BUILD_HISTORY) { + val where = dslContext.selectCount().from(this) + .where(PROJECT_ID.eq(projectId)) + .and(PIPELINE_ID.eq(pipelineId)) + .and(STATUS.`in`(status.map { it.ordinal })) + where.fetchOne(0, Int::class.java)!! + } + val debug = with(T_PIPELINE_BUILD_HISTORY_DEBUG) { + val where = dslContext.selectCount().from(this) + .where(PROJECT_ID.eq(projectId)) + .and(PIPELINE_ID.eq(pipelineId)) + .and(STATUS.`in`(status.map { it.ordinal })) + where.fetchOne(0, Int::class.java)!! + } + return normal + debug + } + fun getBuildTasksByConcurrencyGroup( dslContext: DSLContext, projectId: String, diff --git a/src/backend/ci/core/process/biz-base/src/main/kotlin/com/tencent/devops/process/engine/interceptor/InterceptData.kt b/src/backend/ci/core/process/biz-base/src/main/kotlin/com/tencent/devops/process/engine/interceptor/InterceptData.kt index 0701a288709..6f3cca9c088 100644 --- a/src/backend/ci/core/process/biz-base/src/main/kotlin/com/tencent/devops/process/engine/interceptor/InterceptData.kt +++ b/src/backend/ci/core/process/biz-base/src/main/kotlin/com/tencent/devops/process/engine/interceptor/InterceptData.kt @@ -54,7 +54,7 @@ data class InterceptData( @get:Schema(title = "并发时,是否相同group取消正在执行的流水线", required = false) val concurrencyCancelInProgress: Boolean = false, @get:Schema(title = "并发构建数量限制", required = false) - val maxConRunningQueueSize: Int?, // MULTIPLE类型时,并发构建数量限制 + val maxConRunningQueueSize: Int, // MULTIPLE类型时,并发构建数量限制 @get:Schema(title = "是否为重试操作", required = false) val retry: Boolean? = false ) diff --git a/src/backend/ci/core/process/biz-base/src/main/kotlin/com/tencent/devops/process/engine/interceptor/QueueInterceptor.kt b/src/backend/ci/core/process/biz-base/src/main/kotlin/com/tencent/devops/process/engine/interceptor/QueueInterceptor.kt index b9dd0745d15..f61d44dc746 100644 --- a/src/backend/ci/core/process/biz-base/src/main/kotlin/com/tencent/devops/process/engine/interceptor/QueueInterceptor.kt +++ b/src/backend/ci/core/process/biz-base/src/main/kotlin/com/tencent/devops/process/engine/interceptor/QueueInterceptor.kt @@ -32,6 +32,7 @@ import com.tencent.devops.common.event.dispatcher.pipeline.PipelineEventDispatch import com.tencent.devops.common.log.utils.BuildLogPrinter import com.tencent.devops.common.pipeline.enums.BuildStatus import com.tencent.devops.common.pipeline.pojo.setting.PipelineRunLockType +import com.tencent.devops.common.pipeline.utils.PIPELINE_SETTING_MAX_CON_QUEUE_SIZE_MAX import com.tencent.devops.common.redis.RedisOperation import com.tencent.devops.common.web.utils.I18nUtil import com.tencent.devops.process.bean.PipelineUrlBean @@ -45,7 +46,7 @@ import com.tencent.devops.process.engine.pojo.event.PipelineBuildCancelEvent import com.tencent.devops.process.engine.service.PipelineRedisService import com.tencent.devops.process.engine.service.PipelineRuntimeExtService import com.tencent.devops.process.engine.service.PipelineRuntimeService -import com.tencent.devops.process.engine.service.PipelineTaskService +import kotlin.math.max import org.slf4j.LoggerFactory import org.springframework.beans.factory.annotation.Autowired import org.springframework.stereotype.Component @@ -61,7 +62,6 @@ class QueueInterceptor @Autowired constructor( private val pipelineRuntimeExtService: PipelineRuntimeExtService, private val pipelineEventDispatcher: PipelineEventDispatcher, private val buildLogPrinter: BuildLogPrinter, - private val pipelineTaskService: PipelineTaskService, private val redisOperation: RedisOperation, private val pipelineRedisService: PipelineRedisService, private val pipelineUrlBean: PipelineUrlBean @@ -86,6 +86,7 @@ class QueueInterceptor @Autowired constructor( language = I18nUtil.getDefaultLocaleLanguage() ) ) + runLockType == PipelineRunLockType.SINGLE || runLockType == PipelineRunLockType.SINGLE_LOCK -> checkRunLockWithSingleType( task = task, @@ -94,6 +95,7 @@ class QueueInterceptor @Autowired constructor( runningCount = buildSummaryRecord.runningCount, queueCount = buildSummaryRecord.queueCount ) + runLockType == PipelineRunLockType.GROUP_LOCK -> checkRunLockWithGroupType( task = task, @@ -101,14 +103,19 @@ class QueueInterceptor @Autowired constructor( latestStartUser = buildSummaryRecord.latestStartUser, runningCount = buildSummaryRecord.runningCount ) - task.maxConRunningQueueSize!! <= (buildSummaryRecord.queueCount + buildSummaryRecord.runningCount) -> + + (buildSummaryRecord.queueCount + buildSummaryRecord.runningCount) >= max( + PIPELINE_SETTING_MAX_CON_QUEUE_SIZE_MAX, + task.maxConRunningQueueSize + ) -> Response( status = ERROR_PIPELINE_QUEUE_FULL.toInt(), message = MessageUtil.getMessageByLocale( messageCode = BK_MAX_PARALLEL, language = I18nUtil.getDefaultLocaleLanguage() - ) + " ${task.maxConRunningQueueSize}" + ) + " ${max(PIPELINE_SETTING_MAX_CON_QUEUE_SIZE_MAX, task.maxConRunningQueueSize)}" ) + else -> Response(data = BuildStatus.RUNNING) } } @@ -130,6 +137,7 @@ class QueueInterceptor @Autowired constructor( // 设置了最大排队数量限制为0,但此时没有构建正在执行 task.maxQueueSize == 0 && runningCount == 0 && queueCount == 0 -> Response(data = BuildStatus.RUNNING) + task.maxQueueSize == 0 && (runningCount > 0 || queueCount > 0) -> Response( status = ERROR_PIPELINE_QUEUE_FULL.toInt(), @@ -138,6 +146,7 @@ class QueueInterceptor @Autowired constructor( language = I18nUtil.getDefaultLocaleLanguage() ) ) + queueCount >= task.maxQueueSize -> { if (groupName == null) { outQueueCancelBySingle( @@ -228,7 +237,8 @@ class QueueInterceptor @Autowired constructor( buildId = buildInfo.buildId, message = I18nUtil.getCodeLanMessage( messageCode = ProcessMessageCode.BK_BUILD_QUEUE_WAIT_FOR_CONCURRENCY, - params = arrayOf(groupName, + params = arrayOf( + groupName, "${task.buildId}" ) ), diff --git a/src/backend/ci/core/process/biz-base/src/main/kotlin/com/tencent/devops/process/engine/service/PipelineRepositoryService.kt b/src/backend/ci/core/process/biz-base/src/main/kotlin/com/tencent/devops/process/engine/service/PipelineRepositoryService.kt index 5b6ec8c4207..852d1073112 100644 --- a/src/backend/ci/core/process/biz-base/src/main/kotlin/com/tencent/devops/process/engine/service/PipelineRepositoryService.kt +++ b/src/backend/ci/core/process/biz-base/src/main/kotlin/com/tencent/devops/process/engine/service/PipelineRepositoryService.kt @@ -181,7 +181,9 @@ class PipelineRepositoryService constructor( ) } if (runLockType == PipelineRunLockType.SINGLE || - runLockType == PipelineRunLockType.SINGLE_LOCK || runLockType == PipelineRunLockType.GROUP_LOCK + runLockType == PipelineRunLockType.SINGLE_LOCK || + runLockType == PipelineRunLockType.GROUP_LOCK || + runLockType == PipelineRunLockType.MULTIPLE ) { if (waitQueueTimeMinute < PIPELINE_SETTING_WAIT_QUEUE_TIME_MINUTE_MIN || waitQueueTimeMinute > PIPELINE_SETTING_WAIT_QUEUE_TIME_MINUTE_MAX diff --git a/src/backend/ci/core/process/biz-base/src/main/kotlin/com/tencent/devops/process/engine/service/PipelineRuntimeExtService.kt b/src/backend/ci/core/process/biz-base/src/main/kotlin/com/tencent/devops/process/engine/service/PipelineRuntimeExtService.kt index 9620e99504f..f46ee1ba102 100644 --- a/src/backend/ci/core/process/biz-base/src/main/kotlin/com/tencent/devops/process/engine/service/PipelineRuntimeExtService.kt +++ b/src/backend/ci/core/process/biz-base/src/main/kotlin/com/tencent/devops/process/engine/service/PipelineRuntimeExtService.kt @@ -127,18 +127,12 @@ class PipelineRuntimeExtService @Autowired constructor( } fun existQueue(projectId: String, pipelineId: String, buildId: String, buildStatus: BuildStatus): Boolean { - val redisLock = PipelineNextQueueLock(redisOperation, pipelineId, buildId) - try { - redisLock.lock() - return pipelineBuildDao.updateStatus( - dslContext = dslContext, - projectId = projectId, - buildId = buildId, - oldBuildStatus = buildStatus, - newBuildStatus = BuildStatus.UNEXEC - ) - } finally { - redisLock.unlock() - } + return pipelineBuildDao.updateStatus( + dslContext = dslContext, + projectId = projectId, + buildId = buildId, + oldBuildStatus = buildStatus, + newBuildStatus = BuildStatus.UNEXEC + ) } } diff --git a/src/backend/ci/core/process/biz-base/src/main/kotlin/com/tencent/devops/process/engine/service/PipelineRuntimeService.kt b/src/backend/ci/core/process/biz-base/src/main/kotlin/com/tencent/devops/process/engine/service/PipelineRuntimeService.kt index 50f62b8e885..40d2deb46b2 100644 --- a/src/backend/ci/core/process/biz-base/src/main/kotlin/com/tencent/devops/process/engine/service/PipelineRuntimeService.kt +++ b/src/backend/ci/core/process/biz-base/src/main/kotlin/com/tencent/devops/process/engine/service/PipelineRuntimeService.kt @@ -137,15 +137,15 @@ import com.tencent.devops.process.utils.PIPELINE_NAME import com.tencent.devops.process.utils.PIPELINE_RETRY_COUNT import com.tencent.devops.process.utils.PIPELINE_START_TASK_ID import com.tencent.devops.process.utils.PipelineVarUtil +import java.time.LocalDateTime +import java.util.Date +import java.util.concurrent.TimeUnit import org.jooq.DSLContext import org.jooq.Result import org.jooq.impl.DSL import org.slf4j.LoggerFactory import org.springframework.beans.factory.annotation.Autowired import org.springframework.stereotype.Service -import java.time.LocalDateTime -import java.util.Date -import java.util.concurrent.TimeUnit /** * 流水线运行时相关的服务 @@ -227,6 +227,13 @@ class PipelineRuntimeService @Autowired constructor( return pipelineBuildDao.getBuildInfo(dslContext, projectId, pipelineId, buildId) } + fun getRunningBuildCount( + projectId: String, + pipelineId: String + ): Int { + return pipelineBuildDao.countAllBuildWithStatus(dslContext, projectId, pipelineId, setOf(BuildStatus.RUNNING)) + } + /** 根据状态信息获取并发组构建列表 * @return Pair( PIPELINE_ID , BUILD_ID ) */ @@ -535,6 +542,7 @@ class PipelineRuntimeService @Autowired constructor( } result.distinct() } + else -> emptyList() } return if (search.isNullOrBlank()) { diff --git a/src/backend/ci/core/process/biz-base/src/main/kotlin/com/tencent/devops/process/engine/service/PipelineSettingService.kt b/src/backend/ci/core/process/biz-base/src/main/kotlin/com/tencent/devops/process/engine/service/PipelineSettingService.kt index 55a9014563e..86f364ccda2 100644 --- a/src/backend/ci/core/process/biz-base/src/main/kotlin/com/tencent/devops/process/engine/service/PipelineSettingService.kt +++ b/src/backend/ci/core/process/biz-base/src/main/kotlin/com/tencent/devops/process/engine/service/PipelineSettingService.kt @@ -61,7 +61,8 @@ class PipelineSettingService @Autowired constructor( TimeUnit.HOURS.toMillis(1) } setting.runLockType == PipelineRunLockType.SINGLE || - setting.runLockType == PipelineRunLockType.GROUP_LOCK -> { + setting.runLockType == PipelineRunLockType.GROUP_LOCK || + setting.runLockType == PipelineRunLockType.MULTIPLE -> { TimeUnit.MINUTES.toMillis(setting.waitQueueTimeMinute.toLong()) } else -> { diff --git a/src/backend/ci/core/process/biz-base/src/main/kotlin/com/tencent/devops/process/service/pipeline/PipelineBuildService.kt b/src/backend/ci/core/process/biz-base/src/main/kotlin/com/tencent/devops/process/service/pipeline/PipelineBuildService.kt index 09f2735bac5..81966cae4b6 100644 --- a/src/backend/ci/core/process/biz-base/src/main/kotlin/com/tencent/devops/process/service/pipeline/PipelineBuildService.kt +++ b/src/backend/ci/core/process/biz-base/src/main/kotlin/com/tencent/devops/process/service/pipeline/PipelineBuildService.kt @@ -41,6 +41,7 @@ import com.tencent.devops.common.pipeline.enums.BuildFormPropertyType import com.tencent.devops.common.pipeline.enums.ChannelCode import com.tencent.devops.common.pipeline.enums.StartType import com.tencent.devops.common.pipeline.pojo.BuildParameters +import com.tencent.devops.common.pipeline.utils.PIPELINE_SETTING_MAX_CON_QUEUE_SIZE_MAX import com.tencent.devops.common.redis.concurrent.SimpleRateLimiter import com.tencent.devops.common.service.trace.TraceTag import com.tencent.devops.process.bean.PipelineUrlBean @@ -244,7 +245,7 @@ class PipelineBuildService( maxQueueSize = setting.maxQueueSize, concurrencyGroup = context.concurrencyGroup, concurrencyCancelInProgress = setting.concurrencyCancelInProgress, - maxConRunningQueueSize = setting.maxConRunningQueueSize, + maxConRunningQueueSize = setting.maxConRunningQueueSize ?: PIPELINE_SETTING_MAX_CON_QUEUE_SIZE_MAX, retry = pipelineParamMap[PIPELINE_RETRY_COUNT] != null ) ) diff --git a/src/backend/ci/core/process/biz-base/src/main/kotlin/com/tencent/devops/process/service/pipeline/PipelineSettingVersionService.kt b/src/backend/ci/core/process/biz-base/src/main/kotlin/com/tencent/devops/process/service/pipeline/PipelineSettingVersionService.kt index 9d2e7732924..bdc28ca5c69 100644 --- a/src/backend/ci/core/process/biz-base/src/main/kotlin/com/tencent/devops/process/service/pipeline/PipelineSettingVersionService.kt +++ b/src/backend/ci/core/process/biz-base/src/main/kotlin/com/tencent/devops/process/service/pipeline/PipelineSettingVersionService.kt @@ -128,6 +128,11 @@ class PipelineSettingVersionService @Autowired constructor( ?: settingInfo.concurrencyCancelInProgress settingInfo.waitQueueTimeMinute = ve.waitQueueTimeMinute ?: settingInfo.waitQueueTimeMinute settingInfo.maxQueueSize = ve.maxQueueSize ?: settingInfo.maxQueueSize + settingInfo.maxConRunningQueueSize = if (ve.maxConRunningQueueSize != -1) { + ve.maxConRunningQueueSize ?: settingInfo.maxConRunningQueueSize + } else { + null + } } // 来自前端的请求中,版本中的可能还不是正式生效的,如果和正式配置中有差异则重新获取名称 if (settingInfo.labels.isNotEmpty() && settingInfo.labels != labels && userId != null) { diff --git a/src/backend/ci/core/process/biz-engine/src/main/kotlin/com/tencent/devops/process/engine/control/BuildStartControl.kt b/src/backend/ci/core/process/biz-engine/src/main/kotlin/com/tencent/devops/process/engine/control/BuildStartControl.kt index 6a8bf6b22b1..89dc53f3fb6 100644 --- a/src/backend/ci/core/process/biz-engine/src/main/kotlin/com/tencent/devops/process/engine/control/BuildStartControl.kt +++ b/src/backend/ci/core/process/biz-engine/src/main/kotlin/com/tencent/devops/process/engine/control/BuildStartControl.kt @@ -53,6 +53,7 @@ import com.tencent.devops.common.pipeline.pojo.setting.PipelineRunLockType import com.tencent.devops.common.pipeline.pojo.setting.PipelineSetting import com.tencent.devops.common.pipeline.pojo.time.BuildRecordTimeCost import com.tencent.devops.common.pipeline.pojo.time.BuildTimestampType +import com.tencent.devops.common.pipeline.utils.PIPELINE_SETTING_MAX_CON_QUEUE_SIZE_MAX import com.tencent.devops.common.pipeline.utils.RepositoryConfigUtils import com.tencent.devops.common.redis.RedisOperation import com.tencent.devops.common.service.prometheus.BkTimed @@ -228,14 +229,24 @@ class BuildStartControl @Autowired constructor( return false } val setting = pipelineRepositoryService.getSetting(projectId, pipelineId) + + if (setting?.runLockType == PipelineRunLockType.MULTIPLE) { + canStart = checkRunningCountWithLimit( + buildInfo = buildInfo, + setting = setting, + executeCount = executeCount, + limitCount = setting.maxConRunningQueueSize ?: PIPELINE_SETTING_MAX_CON_QUEUE_SIZE_MAX + ) + } // #4074 LOCK 不会进入到这里,在启动API已经拦截 if (setting?.runLockType == PipelineRunLockType.SINGLE || setting?.runLockType == PipelineRunLockType.SINGLE_LOCK ) { - canStart = checkSingleType( + canStart = checkRunningCountWithLimit( buildInfo = buildInfo, setting = setting, - executeCount = executeCount + executeCount = executeCount, + limitCount = 1 ) } @@ -373,10 +384,11 @@ class BuildStartControl @Autowired constructor( return checkStart } - private fun PipelineBuildStartEvent.checkSingleType( + private fun PipelineBuildStartEvent.checkRunningCountWithLimit( buildInfo: BuildInfo, setting: PipelineSetting, - executeCount: Int + executeCount: Int, + limitCount: Int ): Boolean { // #4074 锁定当前构建是队列中第一个排队待执行的 var checkStart = true @@ -384,9 +396,9 @@ class BuildStartControl @Autowired constructor( checkStart = pipelineRuntimeExtService.queueCanPend2Start(projectId, pipelineId, buildId = buildId) } if (checkStart) { - val buildSummaryRecord = pipelineRuntimeService.getBuildSummaryRecord(projectId, pipelineId) + val runningCount = pipelineRuntimeService.getRunningBuildCount(projectId, pipelineId) - if (buildSummaryRecord!!.runningCount > 0) { + if (runningCount >= limitCount) { // 需要重新入队等待 pipelineRuntimeService.updateBuildInfoStatus2Queue( projectId = projectId, buildId = buildId, oldStatus = BuildStatus.QUEUE_CACHE, @@ -399,7 +411,7 @@ class BuildStartControl @Autowired constructor( buildLogPrinter.addLine( message = I18nUtil.getCodeLanMessage( messageCode = ProcessMessageCode.BK_BUILD_QUEUE_WAIT, - params = arrayOf(setting.runLockType.name, buildSummaryRecord.runningCount.toString()) + params = arrayOf(setting.runLockType.name, runningCount.toString()) ), buildId = buildId, tag = TAG, containerHashId = JOB_ID, executeCount = executeCount, jobId = null, stepId = TAG diff --git a/src/backend/ci/core/process/biz-process/src/main/kotlin/com/tencent/devops/process/api/op/OpPipelineSettingResourceImpl.kt b/src/backend/ci/core/process/biz-process/src/main/kotlin/com/tencent/devops/process/api/op/OpPipelineSettingResourceImpl.kt index 2f118578577..a517fa6f6ba 100644 --- a/src/backend/ci/core/process/biz-process/src/main/kotlin/com/tencent/devops/process/api/op/OpPipelineSettingResourceImpl.kt +++ b/src/backend/ci/core/process/biz-process/src/main/kotlin/com/tencent/devops/process/api/op/OpPipelineSettingResourceImpl.kt @@ -29,7 +29,6 @@ package com.tencent.devops.process.api.op import com.tencent.bk.audit.annotations.AuditEntry import com.tencent.devops.common.api.exception.ExecuteException -import com.tencent.devops.common.api.exception.InvalidParamException import com.tencent.devops.common.api.pojo.PipelineAsCodeSettings import com.tencent.devops.common.api.pojo.Result import com.tencent.devops.common.api.util.MessageUtil @@ -38,12 +37,9 @@ import com.tencent.devops.common.client.Client import com.tencent.devops.common.pipeline.pojo.setting.PipelineSetting import com.tencent.devops.common.web.RestResource import com.tencent.devops.common.web.utils.I18nUtil -import com.tencent.devops.process.constant.ProcessMessageCode.MAXIMUM_NUMBER_CONCURRENCY_ILLEGAL import com.tencent.devops.process.constant.ProcessMessageCode.PROJECT_NOT_EXIST import com.tencent.devops.process.dao.PipelineSettingDao import com.tencent.devops.process.service.pipeline.PipelineSettingFacadeService -import com.tencent.devops.process.utils.PIPELINE_SETTING_MAX_CON_QUEUE_SIZE_MAX -import com.tencent.devops.process.utils.PIPELINE_SETTING_MAX_QUEUE_SIZE_MIN import com.tencent.devops.project.api.op.OPProjectResource import com.tencent.devops.project.api.service.ServiceProjectResource import com.tencent.devops.project.pojo.ProjectProperties @@ -89,7 +85,6 @@ class OpPipelineSettingResourceImpl @Autowired constructor( pipelineId: String, maxConRunningQueueSize: Int ): Result { - checkMaxConRunningQueueSize(maxConRunningQueueSize) return Result( pipelineSettingFacadeService.updateMaxConRunningQueueSize( userId = userId, @@ -151,16 +146,4 @@ class OpPipelineSettingResourceImpl @Autowired constructor( ).data == true return Result(success) } - - private fun checkMaxConRunningQueueSize(maxConRunningQueueSize: Int) { - if (maxConRunningQueueSize <= PIPELINE_SETTING_MAX_QUEUE_SIZE_MIN || - maxConRunningQueueSize > PIPELINE_SETTING_MAX_CON_QUEUE_SIZE_MAX - ) { - throw InvalidParamException( - message = I18nUtil.getCodeLanMessage(MAXIMUM_NUMBER_CONCURRENCY_ILLEGAL), - errorCode = MAXIMUM_NUMBER_CONCURRENCY_ILLEGAL, - params = arrayOf("maxConRunningQueueSize") - ) - } - } } diff --git a/src/backend/ci/core/process/biz-process/src/main/kotlin/com/tencent/devops/process/api/template/UserPTemplateResourceImpl.kt b/src/backend/ci/core/process/biz-process/src/main/kotlin/com/tencent/devops/process/api/template/UserPTemplateResourceImpl.kt index 43b9cf5d11e..7980760a626 100644 --- a/src/backend/ci/core/process/biz-process/src/main/kotlin/com/tencent/devops/process/api/template/UserPTemplateResourceImpl.kt +++ b/src/backend/ci/core/process/biz-process/src/main/kotlin/com/tencent/devops/process/api/template/UserPTemplateResourceImpl.kt @@ -158,7 +158,9 @@ class UserPTemplateResourceImpl @Autowired constructor( setting: PipelineSetting ): Result { if (setting.runLockType == PipelineRunLockType.SINGLE || - setting.runLockType == PipelineRunLockType.SINGLE_LOCK + setting.runLockType == PipelineRunLockType.SINGLE_LOCK || + setting.runLockType == PipelineRunLockType.GROUP_LOCK || + setting.runLockType == PipelineRunLockType.MULTIPLE ) { if (setting.waitQueueTimeMinute < PIPELINE_SETTING_WAIT_QUEUE_TIME_MINUTE_MIN || setting.waitQueueTimeMinute > PIPELINE_SETTING_WAIT_QUEUE_TIME_MINUTE_MAX diff --git a/src/backend/ci/core/process/biz-process/src/main/kotlin/com/tencent/devops/process/service/builds/PipelineBuildFacadeService.kt b/src/backend/ci/core/process/biz-process/src/main/kotlin/com/tencent/devops/process/service/builds/PipelineBuildFacadeService.kt index 688205ba732..9e8ef292264 100644 --- a/src/backend/ci/core/process/biz-process/src/main/kotlin/com/tencent/devops/process/service/builds/PipelineBuildFacadeService.kt +++ b/src/backend/ci/core/process/biz-process/src/main/kotlin/com/tencent/devops/process/service/builds/PipelineBuildFacadeService.kt @@ -63,6 +63,7 @@ import com.tencent.devops.common.pipeline.pojo.element.atom.ManualReviewParamTyp import com.tencent.devops.common.pipeline.pojo.element.trigger.ManualTriggerElement import com.tencent.devops.common.pipeline.pojo.element.trigger.RemoteTriggerElement import com.tencent.devops.common.pipeline.utils.BuildStatusSwitcher +import com.tencent.devops.common.pipeline.utils.PIPELINE_SETTING_MAX_CON_QUEUE_SIZE_MAX import com.tencent.devops.common.redis.RedisOperation import com.tencent.devops.common.service.utils.CommonUtils import com.tencent.devops.common.service.utils.HomeHostUtil @@ -1194,7 +1195,7 @@ class PipelineBuildFacadeService( maxQueueSize = setting.maxQueueSize, concurrencyGroup = setting.concurrencyGroup, concurrencyCancelInProgress = setting.concurrencyCancelInProgress, - maxConRunningQueueSize = setting.maxConRunningQueueSize + maxConRunningQueueSize = setting.maxConRunningQueueSize ?: PIPELINE_SETTING_MAX_CON_QUEUE_SIZE_MAX ) ) diff --git a/support-files/sql/1001_ci_process_ddl_mysql.sql b/support-files/sql/1001_ci_process_ddl_mysql.sql index 47f92267d2a..ed97fe464ed 100644 --- a/support-files/sql/1001_ci_process_ddl_mysql.sql +++ b/support-files/sql/1001_ci_process_ddl_mysql.sql @@ -461,7 +461,7 @@ CREATE TABLE IF NOT EXISTS `T_PIPELINE_SETTING` ( `SUCCESS_WECHAT_GROUP_MARKDOWN_FLAG` bit(1) NOT NULL DEFAULT b'0' COMMENT '成功的企业微信群通知转为Markdown格式开关', `FAIL_WECHAT_GROUP_MARKDOWN_FLAG` bit(1) NOT NULL DEFAULT b'0' COMMENT '失败的企业微信群通知转为Markdown格式开关', `MAX_PIPELINE_RES_NUM` int(11) DEFAULT '500' COMMENT '保存流水线编排的最大个数', - `MAX_CON_RUNNING_QUEUE_SIZE` int(11) DEFAULT '50' COMMENT '并发构建数量限制', + `MAX_CON_RUNNING_QUEUE_SIZE` int(11) DEFAULT NULL COMMENT '并发构建数量限制,为null时表示取系统默认值', `BUILD_NUM_RULE` varchar(512) DEFAULT NULL COMMENT '构建号生成规则', `CONCURRENCY_GROUP` varchar(255) DEFAULT NULL COMMENT '并发时,设定的group', `CONCURRENCY_CANCEL_IN_PROGRESS` bit(1) DEFAULT b'0' COMMENT '并发时,是否相同group取消正在执行的流水线', @@ -954,6 +954,7 @@ CREATE TABLE IF NOT EXISTS `T_PIPELINE_SETTING_VERSION` ( `FAIL_CONTENT` longtext, `SUCCESS_WECHAT_GROUP_MARKDOWN_FLAG` bit(1) NOT NULL DEFAULT b'0', `FAIL_WECHAT_GROUP_MARKDOWN_FLAG` bit(1) DEFAULT b'0', + `MAX_CON_RUNNING_QUEUE_SIZE` int(11) DEFAULT NULL COMMENT '并发构建数量限制,值为-1时表示取系统默认值。', PRIMARY KEY (`ID`), UNIQUE KEY `UNI_INX_TPSV_PROJECT_PIPELINE_VERSION` (`PROJECT_ID`,`PIPELINE_ID`,`VERSION`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='流水线基础配置版本表'; diff --git a/support-files/sql/2004_v3.x/2030_ci_process-update_v3.0_mysql.sql b/support-files/sql/2004_v3.x/2030_ci_process-update_v3.0_mysql.sql index ef2a76f50f1..e7718f93bcb 100644 --- a/support-files/sql/2004_v3.x/2030_ci_process-update_v3.0_mysql.sql +++ b/support-files/sql/2004_v3.x/2030_ci_process-update_v3.0_mysql.sql @@ -77,6 +77,15 @@ BEGIN ALTER TABLE T_PIPELINE_RESOURCE_VERSION ADD COLUMN RELEASE_TIME TIMESTAMP NULL COMMENT '发布时间'; END IF; + + IF NOT EXISTS(SELECT 1 + FROM information_schema.COLUMNS + WHERE TABLE_SCHEMA = db + AND TABLE_NAME = 'T_PIPELINE_SETTING_VERSION' + AND COLUMN_NAME = 'MAX_CON_RUNNING_QUEUE_SIZE') THEN + ALTER TABLE T_PIPELINE_SETTING_VERSION + ADD COLUMN `MAX_CON_RUNNING_QUEUE_SIZE` int(11) DEFAULT NULL COMMENT '并发构建数量限制,值为-1时表示取系统默认值。'; + END IF; COMMIT;