mirror of
https://github.com/go-gitea/gitea.git
synced 2026-09-08 14:03:24 +09:00
feat: Add max-parallel Support for Gitea Actions (#36357)
Add support for `strategy.max-parallel` on Gitea Actions matrix jobs.
**How it works**
Jobs over the limit are inserted as `Blocked` instead of `Waiting`, so
runners never see them. When a job finishes, the job-status resolver
promotes one `Blocked` job per freed slot, in job order. Slots are
counted per `JobID` and scoped by reusable-workflow caller. A
`Cancelling` job still owns its runner, so it keeps its slot.
The cap is applied wherever a job can become `Waiting`: initial insert,
rerun, approval, and resolver promotion.
Best effort, not a hard invariant: two concurrent emitter passes can
each promote into the last slot, overshooting by one. It does not
compound, since every later pass recounts.
**Parsing**
Any YAML number, cast to an int as GitHub does (`1.5` → 1). `0` or
negative means unlimited. Expressions (`${{ ... }}`) are not evaluated
yet and fall back to unlimited.
**Migration**
Adds the `max_parallel` column on `action_run_job`. No index or
constraint changes.
**Compatibility**
Existing rows default to `0`, so behaviour is unchanged. No runner
changes needed: the runner protocol is untouched, and since the server
splits the matrix each runner still receives a single job.
Closes https://github.com/go-gitea/gitea/issues/35561
Signed-off-by: Pascal Zimmermann <pascal.zimmermann@theiotstudio.com>
Signed-off-by: ZPascal <pascal.zimmermann@theiotstudio.com>
Signed-off-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: Zettat123 <zettat123@gmail.com>
This commit is contained in:
@@ -240,6 +240,7 @@ func execRerunPlan(ctx context.Context, plan *rerunPlan) (*actions_model.ActionR
|
||||
|
||||
// templateIDToNewID maps each template-attempt job's DB ID to its newly-inserted clone's DB ID
|
||||
templateIDToNewID := make(map[int64]int64, len(plan.templateJobs))
|
||||
slots := maxParallelSlots{}
|
||||
|
||||
for _, templateJob := range plan.templateJobs {
|
||||
// descendants of a reset reusable caller are not cloned at all, the caller will re-insert them
|
||||
@@ -276,7 +277,8 @@ func execRerunPlan(ctx context.Context, plan *rerunPlan) (*actions_model.ActionR
|
||||
newJob.CallPayload = ""
|
||||
}
|
||||
|
||||
if newJob.RawConcurrency != "" && !shouldBlockJob {
|
||||
// A slot-starved job must not cancel its group peers.
|
||||
if newJob.RawConcurrency != "" && !shouldBlockJob && slots.available(newJob) {
|
||||
if err := EvaluateJobConcurrencyFillModel(ctx, plan.run, newAttempt, newJob, vars, nil); err != nil {
|
||||
return fmt.Errorf("evaluate job concurrency: %w", err)
|
||||
}
|
||||
@@ -287,6 +289,7 @@ func execRerunPlan(ctx context.Context, plan *rerunPlan) (*actions_model.ActionR
|
||||
cancelledConcurrencyJobs = append(cancelledConcurrencyJobs, jobsToCancel...)
|
||||
}
|
||||
|
||||
applyMaxParallel(newJob, slots)
|
||||
newJobsToRerun = append(newJobsToRerun, newJob)
|
||||
} else {
|
||||
newJob.TaskID = 0
|
||||
@@ -522,6 +525,7 @@ func cloneRunJobForAttempt(templateJob *actions_model.ActionRunJob, attempt *act
|
||||
ConcurrencyGroup: templateJob.ConcurrencyGroup,
|
||||
ConcurrencyCancel: templateJob.ConcurrencyCancel,
|
||||
TokenPermissions: templateJob.TokenPermissions,
|
||||
MaxParallel: templateJob.MaxParallel,
|
||||
|
||||
// reusable workflow fields
|
||||
IsReusableCaller: templateJob.IsReusableCaller,
|
||||
|
||||
Reference in New Issue
Block a user