@@ -114,32 +114,41 @@ func UpdateRunJob(ctx context.Context, job *ActionRunJob, cond builder.Cond, col
114114 if affected != 0 && util .SliceContains (cols , "status" ) && job .Status .IsWaiting () {
115115 // if the status of job changes to waiting again, increase tasks version.
116116 if err := IncreaseTaskVersion (ctx , job .OwnerID , job .RepoID ); err != nil {
117- return affected , err
117+ return 0 , err
118118 }
119119 }
120120
121121 if job .RunID == 0 {
122122 var err error
123123 if job , err = GetRunJobByID (ctx , job .ID ); err != nil {
124- return affected , err
124+ return 0 , err
125125 }
126126 }
127127
128- jobs , err := GetRunJobsByRunID (ctx , job .RunID )
129- if err != nil {
130- return affected , err
128+ {
129+ // Other goroutines may aggregate the status of the run and update it too.
130+ // So we need load the run and its jobs before updating the run.
131+ run , err := GetRunByID (ctx , job .RunID )
132+ if err != nil {
133+ return 0 , err
134+ }
135+ jobs , err := GetRunJobsByRunID (ctx , job .RunID )
136+ if err != nil {
137+ return 0 , err
138+ }
139+ run .Status = aggregateJobStatus (jobs )
140+ if run .Started .IsZero () && run .Status .IsRunning () {
141+ run .Started = timeutil .TimeStampNow ()
142+ }
143+ if run .Stopped .IsZero () && run .Status .IsDone () {
144+ run .Stopped = timeutil .TimeStampNow ()
145+ }
146+ if err := UpdateRun (ctx , run , "status" , "started" , "stopped" ); err != nil {
147+ return 0 , fmt .Errorf ("update run %d: %w" , run .ID , err )
148+ }
131149 }
132150
133- runStatus := aggregateJobStatus (jobs )
134-
135- run := & ActionRun {
136- ID : job .RunID ,
137- Status : runStatus ,
138- }
139- if runStatus .IsDone () {
140- run .Stopped = timeutil .TimeStampNow ()
141- }
142- return affected , UpdateRun (ctx , run )
151+ return affected , nil
143152}
144153
145154func aggregateJobStatus (jobs []* ActionRunJob ) Status {
0 commit comments