go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/bisection/compilefailureanalysis/statusupdater/status_updater.go (about) 1 // Copyright 2022 The LUCI Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 // Package statusupdater updates analysis status based on the data in datastore. 16 package statusupdater 17 18 import ( 19 "context" 20 21 "go.chromium.org/luci/bisection/model" 22 pb "go.chromium.org/luci/bisection/proto/v1" 23 "go.chromium.org/luci/bisection/util/datastoreutil" 24 25 "go.chromium.org/luci/common/clock" 26 "go.chromium.org/luci/common/errors" 27 "go.chromium.org/luci/gae/service/datastore" 28 ) 29 30 func UpdateAnalysisStatus(c context.Context, cfa *model.CompileFailureAnalysis) error { 31 // If there are confirmed culprit 32 if len(cfa.VerifiedCulprits) > 0 { 33 return UpdateStatus(c, cfa, pb.AnalysisStatus_FOUND, pb.AnalysisRunStatus_ENDED) 34 } 35 36 // Fetch heuristic and nthsection analysis 37 ha, err := datastoreutil.GetHeuristicAnalysis(c, cfa) 38 if err != nil { 39 return errors.Annotate(err, "couldn't fetch heuristic analysis of analysis %d", cfa.Id).Err() 40 } 41 42 nsa, err := datastoreutil.GetNthSectionAnalysis(c, cfa) 43 if err != nil { 44 return errors.Annotate(err, "couldn't fetch nthsection analysis of analysis %d", cfa.Id).Err() 45 } 46 47 haveUnfinishedReruns, err := analysisStillHaveUnfinishedReruns(c, cfa) 48 if err != nil { 49 return errors.Annotate(err, "couldn't decide if analysis %d has unfinished rerun", cfa.Id).Err() 50 } 51 havePendingVerificationSuspect, err := analysisStillHasSuspectWaitingToBeVerified(c, cfa) 52 if err != nil { 53 return errors.Annotate(err, "couldn't decide if analysis %d has suspect pending verification", cfa.Id).Err() 54 } 55 56 // No nth-section run. Just consider the heuristic analysis. 57 if nsa == nil || nsa.Status == pb.AnalysisStatus_ERROR { 58 if ha == nil || ha.Status == pb.AnalysisStatus_ERROR { 59 return UpdateStatus(c, cfa, pb.AnalysisStatus_ERROR, pb.AnalysisRunStatus_ENDED) 60 } 61 if ha.Status != pb.AnalysisStatus_SUSPECTFOUND { 62 return UpdateStatus(c, cfa, ha.Status, ha.RunStatus) 63 } 64 // Heuristic found suspect. So analysis could be in progress or ended 65 // depend on if there is any rerun in progress 66 if haveUnfinishedReruns || havePendingVerificationSuspect { 67 return UpdateStatus(c, cfa, pb.AnalysisStatus_SUSPECTFOUND, pb.AnalysisRunStatus_STARTED) 68 } else { 69 return UpdateStatus(c, cfa, pb.AnalysisStatus_SUSPECTFOUND, pb.AnalysisRunStatus_ENDED) 70 } 71 } 72 73 // No heuristic analysis (for some reasons). Just consider nth section 74 if ha == nil || ha.Status == pb.AnalysisStatus_ERROR { 75 if nsa == nil || nsa.Status == pb.AnalysisStatus_ERROR { 76 return UpdateStatus(c, cfa, pb.AnalysisStatus_ERROR, pb.AnalysisRunStatus_ENDED) 77 } 78 79 if nsa.Status != pb.AnalysisStatus_SUSPECTFOUND { 80 return UpdateStatus(c, cfa, nsa.Status, nsa.RunStatus) 81 } 82 // nsa found suspect. So analysis could be in progress or ended 83 // depend on if there is any rerun in progress 84 if haveUnfinishedReruns || havePendingVerificationSuspect { 85 return UpdateStatus(c, cfa, pb.AnalysisStatus_SUSPECTFOUND, pb.AnalysisRunStatus_STARTED) 86 } else { 87 return UpdateStatus(c, cfa, pb.AnalysisStatus_SUSPECTFOUND, pb.AnalysisRunStatus_ENDED) 88 } 89 } 90 91 // Both heuristic and nthsection analysis present 92 gotSuspect := (ha.Status == pb.AnalysisStatus_SUSPECTFOUND || nsa.Status == pb.AnalysisStatus_SUSPECTFOUND) 93 if gotSuspect { 94 inProgress := (ha.Status == pb.AnalysisStatus_RUNNING || nsa.Status == pb.AnalysisStatus_RUNNING) 95 if haveUnfinishedReruns || havePendingVerificationSuspect || inProgress { 96 return UpdateStatus(c, cfa, pb.AnalysisStatus_SUSPECTFOUND, pb.AnalysisRunStatus_STARTED) 97 } else { 98 return UpdateStatus(c, cfa, pb.AnalysisStatus_SUSPECTFOUND, pb.AnalysisRunStatus_ENDED) 99 } 100 } 101 102 // No suspect -> either in progress or notfound 103 if ha.Status == pb.AnalysisStatus_NOTFOUND && nsa.Status == pb.AnalysisStatus_NOTFOUND { 104 return UpdateStatus(c, cfa, pb.AnalysisStatus_NOTFOUND, pb.AnalysisRunStatus_ENDED) 105 } 106 return UpdateStatus(c, cfa, pb.AnalysisStatus_RUNNING, pb.AnalysisRunStatus_STARTED) 107 } 108 109 func UpdateStatus(c context.Context, cfa *model.CompileFailureAnalysis, status pb.AnalysisStatus, runStatus pb.AnalysisRunStatus) error { 110 return datastore.RunInTransaction(c, func(c context.Context) error { 111 e := datastore.Get(c, cfa) 112 if e != nil { 113 return e 114 } 115 116 // If the run has ended or canceled, we don't want to do anything 117 if cfa.RunStatus == pb.AnalysisRunStatus_ENDED || cfa.RunStatus == pb.AnalysisRunStatus_CANCELED { 118 return nil 119 } 120 121 // All the same, no need to update 122 if cfa.RunStatus == runStatus && cfa.Status == status { 123 return nil 124 } 125 126 cfa.Status = status 127 cfa.RunStatus = runStatus 128 if runStatus == pb.AnalysisRunStatus_ENDED || runStatus == pb.AnalysisRunStatus_CANCELED { 129 cfa.EndTime = clock.Now(c) 130 } 131 return datastore.Put(c, cfa) 132 }, nil) 133 } 134 135 func UpdateNthSectionStatus(c context.Context, nsa *model.CompileNthSectionAnalysis, status pb.AnalysisStatus, runStatus pb.AnalysisRunStatus) error { 136 return datastore.RunInTransaction(c, func(c context.Context) error { 137 e := datastore.Get(c, nsa) 138 if e != nil { 139 return e 140 } 141 142 // If the run has ended or canceled, we don't want to do anything 143 if nsa.RunStatus == pb.AnalysisRunStatus_ENDED || nsa.RunStatus == pb.AnalysisRunStatus_CANCELED { 144 return nil 145 } 146 147 // All the same, no need to update 148 if nsa.RunStatus == runStatus && nsa.Status == status { 149 return nil 150 } 151 152 nsa.Status = status 153 nsa.RunStatus = runStatus 154 if runStatus == pb.AnalysisRunStatus_ENDED || runStatus == pb.AnalysisRunStatus_CANCELED { 155 nsa.EndTime = clock.Now(c) 156 } 157 return datastore.Put(c, nsa) 158 }, nil) 159 } 160 161 func analysisStillHaveUnfinishedReruns(c context.Context, cfa *model.CompileFailureAnalysis) (bool, error) { 162 reruns, err := datastoreutil.GetRerunsForAnalysis(c, cfa) 163 if err != nil { 164 return false, err 165 } 166 for _, rerun := range reruns { 167 if rerun.Status == pb.RerunStatus_RERUN_STATUS_IN_PROGRESS { 168 return true, nil 169 } 170 } 171 return false, nil 172 } 173 174 func analysisStillHasSuspectWaitingToBeVerified(c context.Context, cfa *model.CompileFailureAnalysis) (bool, error) { 175 suspects, err := datastoreutil.FetchSuspectsForAnalysis(c, cfa) 176 if err != nil { 177 return false, errors.Annotate(err, "fetchSuspectsForAnalysis").Err() 178 } 179 for _, suspect := range suspects { 180 if suspect.VerificationStatus == model.SuspectVerificationStatus_VerificationScheduled { 181 return true, nil 182 } 183 } 184 return false, nil 185 }