go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/bisection/util/datastoreutil/analysis_result_queries.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 datastoreutil contains utility functions related to datastore entities 16 package datastoreutil 17 18 import ( 19 "context" 20 "fmt" 21 22 "go.chromium.org/luci/bisection/model" 23 pb "go.chromium.org/luci/bisection/proto/v1" 24 "go.chromium.org/luci/common/errors" 25 "go.chromium.org/luci/common/logging" 26 "go.chromium.org/luci/gae/service/datastore" 27 ) 28 29 // GetBuild returns the failed build in the datastore with the given Buildbucket ID 30 // Note: if the build is not found, this will return (nil, nil) 31 func GetBuild(c context.Context, bbid int64) (*model.LuciFailedBuild, error) { 32 build := &model.LuciFailedBuild{Id: bbid} 33 switch err := datastore.Get(c, build); { 34 case err == datastore.ErrNoSuchEntity: 35 return nil, nil 36 case err != nil: 37 return nil, err 38 } 39 40 return build, nil 41 } 42 43 // GetAnalysisForBuild returns the failure analysis associated with the given Buildbucket ID 44 // Note: if the build or its analysis is not found, this will return (nil, nil) 45 func GetAnalysisForBuild(c context.Context, bbid int64) (*model.CompileFailureAnalysis, error) { 46 buildModel, err := GetBuild(c, bbid) 47 if (err != nil) || (buildModel == nil) { 48 return nil, err 49 } 50 51 cfModel := &model.CompileFailure{ 52 Id: bbid, 53 Build: datastore.KeyForObj(c, buildModel), 54 } 55 switch err := datastore.Get(c, cfModel); { 56 case err == datastore.ErrNoSuchEntity: 57 return nil, nil 58 case err != nil: 59 return nil, err 60 default: 61 //continue 62 } 63 64 // If the compile failure was "merged" into another compile failure, 65 // use the merged one instead. 66 cfKey := datastore.KeyForObj(c, cfModel) 67 if cfModel.MergedFailureKey != nil { 68 cfKey = cfModel.MergedFailureKey 69 } 70 71 // Get the analysis for the compile failure 72 q := datastore.NewQuery("CompileFailureAnalysis").Eq("compile_failure", cfKey) 73 analyses := []*model.CompileFailureAnalysis{} 74 err = datastore.GetAll(c, q, &analyses) 75 if err != nil { 76 return nil, err 77 } 78 if len(analyses) == 0 { 79 return nil, nil 80 } 81 if len(analyses) > 1 { 82 logging.Warningf(c, "Found more than one analysis for build %d", bbid) 83 } 84 return analyses[0], nil 85 } 86 87 // GetHeuristicAnalysis returns the heuristic analysis associated with the given failure analysis 88 func GetHeuristicAnalysis(c context.Context, analysis *model.CompileFailureAnalysis) (*model.CompileHeuristicAnalysis, error) { 89 // Gets heuristic analysis results. 90 q := datastore.NewQuery("CompileHeuristicAnalysis").Ancestor(datastore.KeyForObj(c, analysis)) 91 heuristicAnalyses := []*model.CompileHeuristicAnalysis{} 92 err := datastore.GetAll(c, q, &heuristicAnalyses) 93 94 if err != nil { 95 return nil, err 96 } 97 98 if len(heuristicAnalyses) == 0 { 99 // No heuristic analysis 100 return nil, nil 101 } 102 103 if len(heuristicAnalyses) > 1 { 104 logging.Warningf(c, "Found multiple heuristic analysis for analysis %d", analysis.Id) 105 } 106 107 heuristicAnalysis := heuristicAnalyses[0] 108 return heuristicAnalysis, nil 109 } 110 111 // GetSuspectsForHeuristicAnalysis returns the heuristic suspects identified by the given heuristic analysis 112 func GetSuspectsForHeuristicAnalysis(c context.Context, heuristicAnalysis *model.CompileHeuristicAnalysis) ([]*model.Suspect, error) { 113 // Getting the suspects for heuristic analysis 114 suspects := []*model.Suspect{} 115 q := datastore.NewQuery("Suspect").Ancestor(datastore.KeyForObj(c, heuristicAnalysis)).Order("-score") 116 err := datastore.GetAll(c, q, &suspects) 117 if err != nil { 118 return nil, err 119 } 120 121 return suspects, nil 122 } 123 124 // GetSuspectForNthSectionAnalysis returns the heuristic suspects identified by the given heuristic analysis 125 func GetSuspectForNthSectionAnalysis(c context.Context, nthsectionAnalysis *model.CompileNthSectionAnalysis) (*model.Suspect, error) { 126 // Getting the suspects for nthsection analysis 127 suspects := []*model.Suspect{} 128 q := datastore.NewQuery("Suspect").Ancestor(datastore.KeyForObj(c, nthsectionAnalysis)) 129 err := datastore.GetAll(c, q, &suspects) 130 if err != nil { 131 return nil, err 132 } 133 if len(suspects) == 0 { 134 return nil, nil 135 } 136 if len(suspects) > 0 { 137 logging.Warningf(c, "nthsectionAnalysis has more than 1 suspect %d", len(suspects)) 138 } 139 return suspects[0], nil 140 } 141 142 // GetCompileFailureForAnalysisID gets CompileFailure for analysisID. 143 func GetCompileFailureForAnalysisID(c context.Context, analysisID int64) (*model.CompileFailure, error) { 144 cfa, err := GetCompileFailureAnalysis(c, analysisID) 145 if err != nil { 146 return nil, err 147 } 148 return GetCompileFailureForAnalysis(c, cfa) 149 } 150 151 // GetCompileFailureForAnalysis gets CompileFailure for analysis 152 func GetCompileFailureForAnalysis(c context.Context, cfa *model.CompileFailureAnalysis) (*model.CompileFailure, error) { 153 compileFailure := &model.CompileFailure{ 154 Id: cfa.CompileFailure.IntID(), 155 // We need to specify the parent here because this is a multi-part key. 156 Build: cfa.CompileFailure.Parent(), 157 } 158 err := datastore.Get(c, compileFailure) 159 if err != nil { 160 return nil, errors.Annotate(err, "getting compile failure for analysis %d", cfa.Id).Err() 161 } 162 return compileFailure, nil 163 } 164 165 // GetFailedBuildForAnalysis gets LuciFailedBuild for analysis. 166 func GetFailedBuildForAnalysis(c context.Context, cfa *model.CompileFailureAnalysis) (*model.LuciFailedBuild, error) { 167 cf, err := GetCompileFailureForAnalysis(c, cfa) 168 if err != nil { 169 return nil, errors.Annotate(err, "getting compile failure for analysis %d", cfa.Id).Err() 170 } 171 build := &model.LuciFailedBuild{Id: cf.Build.IntID()} 172 err = datastore.Get(c, build) 173 if err != nil { 174 return nil, errors.Annotate(err, "getting failed build for analysis %d", cfa.Id).Err() 175 } 176 return build, nil 177 } 178 179 // GetRerunsForRerunBuild returns all SingleRerun for a rerunBuild 180 func GetRerunsForRerunBuild(c context.Context, rerunBuild *model.CompileRerunBuild) ([]*model.SingleRerun, error) { 181 q := datastore.NewQuery("SingleRerun").Eq("rerun_build", datastore.KeyForObj(c, rerunBuild)).Order("start_time") 182 singleReruns := []*model.SingleRerun{} 183 err := datastore.GetAll(c, q, &singleReruns) 184 return singleReruns, errors.Annotate(err, "get reruns for rerun build %d", rerunBuild.Id).Err() 185 } 186 187 // GetLastRerunForRerunBuild returns the last SingleRerun for a rerunBuild (based on start_time) 188 func GetLastRerunForRerunBuild(c context.Context, rerunBuild *model.CompileRerunBuild) (*model.SingleRerun, error) { 189 reruns, err := GetRerunsForRerunBuild(c, rerunBuild) 190 if err != nil { 191 return nil, err 192 } 193 if len(reruns) == 0 { 194 return nil, fmt.Errorf("got no SingleRerun for build %d", rerunBuild.Id) 195 } 196 return reruns[len(reruns)-1], nil 197 } 198 199 // GetNthSectionAnalysis returns the nthsection analysis associated with the given failure analysis 200 func GetNthSectionAnalysis(c context.Context, analysis *model.CompileFailureAnalysis) (*model.CompileNthSectionAnalysis, error) { 201 q := datastore.NewQuery("CompileNthSectionAnalysis").Ancestor(datastore.KeyForObj(c, analysis)) 202 nthSectionAnalyses := []*model.CompileNthSectionAnalysis{} 203 err := datastore.GetAll(c, q, &nthSectionAnalyses) 204 205 if err != nil { 206 return nil, errors.Annotate(err, "couldn't get nthsection analysis for analysis %d", analysis.Id).Err() 207 } 208 209 if len(nthSectionAnalyses) == 0 { 210 return nil, nil 211 } 212 213 if len(nthSectionAnalyses) > 1 { 214 return nil, fmt.Errorf("found more than 1 nth section analysis for analysis %d", analysis.Id) 215 } 216 217 return nthSectionAnalyses[0], nil 218 } 219 220 // GetCompileFailureAnalysis gets compile failure analysis by its id 221 func GetCompileFailureAnalysis(c context.Context, analysisID int64) (*model.CompileFailureAnalysis, error) { 222 analysis := &model.CompileFailureAnalysis{ 223 Id: analysisID, 224 } 225 err := datastore.Get(c, analysis) 226 if err != nil { 227 return nil, errors.Annotate(err, "couldn't get CompileFailureAnalysis %d", analysis.Id).Err() 228 } 229 return analysis, err 230 } 231 232 // GetOtherSuspectsWithSameCL returns the list of Suspect(from different analyses) 233 // that has the same reviewURL as this suspect. 234 // It is meant to check if the same CL is the suspects for multiple failures. 235 func GetOtherSuspectsWithSameCL(c context.Context, suspect *model.Suspect) ([]*model.Suspect, error) { 236 suspects := []*model.Suspect{} 237 q := datastore.NewQuery("Suspect").Eq("review_url", suspect.ReviewUrl) 238 err := datastore.GetAll(c, q, &suspects) 239 if err != nil { 240 return nil, errors.Annotate(err, "failed GetSameSuspects").Err() 241 } 242 243 // Remove this suspect 244 for i, s := range suspects { 245 if s.Id == suspect.Id { 246 return append(suspects[:i], suspects[i+1:]...), nil 247 } 248 } 249 return suspects, nil 250 } 251 252 // GetLatestBuildFailureForBuilder returns the latest LuciFailedBuild model for a builderID 253 // If there is no build failure, return (nil, nil) 254 func GetLatestBuildFailureForBuilder(c context.Context, project string, bucket string, builder string) (*model.LuciFailedBuild, error) { 255 builds := []*model.LuciFailedBuild{} 256 q := datastore.NewQuery("LuciFailedBuild").Eq("project", project).Eq("bucket", bucket).Eq("builder", builder).Order("-end_time").Limit(1) 257 err := datastore.GetAll(c, q, &builds) 258 if err != nil { 259 return nil, errors.Annotate(err, "failed querying LuciFailedBuild").Err() 260 } 261 262 if len(builds) == 0 { 263 return nil, nil 264 } 265 return builds[0], nil 266 } 267 268 // GetLatestAnalysisForBuilder returns the latest CompileFailureAnalysis for a builderID 269 // If there is no analysis, return (nil, nil) 270 func GetLatestAnalysisForBuilder(c context.Context, project string, bucket string, builder string) (*model.CompileFailureAnalysis, error) { 271 build, err := GetLatestBuildFailureForBuilder(c, project, bucket, builder) 272 if err != nil { 273 return nil, errors.Annotate(err, "cannot GetLatestBuildFailureForBuilder").Err() 274 } 275 if build == nil { 276 return nil, nil 277 } 278 return GetAnalysisForBuild(c, build.Id) 279 } 280 281 // GetRerunsForAnalysis returns all reruns for an analysis 282 // The result is sorted by start_time 283 func GetRerunsForAnalysis(c context.Context, cfa *model.CompileFailureAnalysis) ([]*model.SingleRerun, error) { 284 q := datastore.NewQuery("SingleRerun").Eq("analysis", datastore.KeyForObj(c, cfa)).Order("start_time") 285 reruns := []*model.SingleRerun{} 286 err := datastore.GetAll(c, q, &reruns) 287 if err != nil { 288 return nil, errors.Annotate(err, "getting reruns for analysis %d", cfa.Id).Err() 289 } 290 return reruns, nil 291 } 292 293 func GetRerunsForNthSectionAnalysis(c context.Context, nsa *model.CompileNthSectionAnalysis) ([]*model.SingleRerun, error) { 294 q := datastore.NewQuery("SingleRerun").Eq("analysis", nsa.ParentAnalysis).Eq("rerun_type", model.RerunBuildType_NthSection) 295 reruns := []*model.SingleRerun{} 296 err := datastore.GetAll(c, q, &reruns) 297 if err != nil { 298 return nil, errors.Annotate(err, "getting reruns for analysis %d", nsa.ParentAnalysis.IntID()).Err() 299 } 300 return reruns, nil 301 } 302 303 // GetTestFailureAnalysis gets test failure analysis by its ID. 304 func GetTestFailureAnalysis(ctx context.Context, analysisID int64) (*model.TestFailureAnalysis, error) { 305 analysis := &model.TestFailureAnalysis{ 306 ID: analysisID, 307 } 308 err := datastore.Get(ctx, analysis) 309 if err != nil { 310 return nil, errors.Annotate(err, "get TestFailureAnalysis with id %d", analysis.ID).Err() 311 } 312 return analysis, err 313 } 314 315 // GetPrimaryTestFailure gets the primary TestFailure model for a TestFailureAnalysis. 316 func GetPrimaryTestFailure(ctx context.Context, analysis *model.TestFailureAnalysis) (*model.TestFailure, error) { 317 if analysis.TestFailure == nil { 318 return nil, errors.New("no TestFailure for analysis") 319 } 320 testFailure := &model.TestFailure{ 321 ID: analysis.TestFailure.IntID(), 322 } 323 err := datastore.Get(ctx, testFailure) 324 if err != nil { 325 return nil, errors.Annotate(err, "get TestFailure from datastore %d", analysis.TestFailure.IntID()).Err() 326 } 327 return testFailure, nil 328 } 329 330 // GetTestFailureBundle returns a TestFailureBundle for a TestFailureAnalysis. 331 func GetTestFailureBundle(ctx context.Context, tfa *model.TestFailureAnalysis) (*model.TestFailureBundle, error) { 332 return getTestFailureBundleWithAnalysisKey(ctx, datastore.KeyForObj(ctx, tfa)) 333 } 334 335 func getTestFailureBundleWithAnalysisKey(ctx context.Context, analysisKey *datastore.Key) (*model.TestFailureBundle, error) { 336 q := datastore.NewQuery("TestFailure").Eq("analysis_key", analysisKey) 337 tfs := []*model.TestFailure{} 338 err := datastore.GetAll(ctx, q, &tfs) 339 if err != nil { 340 return nil, errors.Annotate(err, "get test failures for analysis").Err() 341 } 342 if len(tfs) == 0 { 343 return nil, errors.New("no test failure for analysis") 344 } 345 bundle := &model.TestFailureBundle{} 346 err = bundle.Add(tfs) 347 if err != nil { 348 return nil, err 349 } 350 if bundle.Primary() == nil { 351 return nil, errors.New("no primary test failure for analysis") 352 } 353 return bundle, nil 354 } 355 356 // GetTestSingleRerun gets test single rerun by its ID. 357 func GetTestSingleRerun(ctx context.Context, rerunID int64) (*model.TestSingleRerun, error) { 358 rerun := &model.TestSingleRerun{ 359 ID: rerunID, 360 } 361 err := datastore.Get(ctx, rerun) 362 if err != nil { 363 return nil, errors.Annotate(err, "get test single rerun with id %d", rerunID).Err() 364 } 365 return rerun, err 366 } 367 368 // GetTestFailure gets test failure by its ID. 369 func GetTestFailure(ctx context.Context, failureID int64) (*model.TestFailure, error) { 370 failure := &model.TestFailure{ 371 ID: failureID, 372 } 373 err := datastore.Get(ctx, failure) 374 if err != nil { 375 return nil, errors.Annotate(err, "get TestFailure with id %d", failure.ID).Err() 376 } 377 return failure, err 378 } 379 380 // GetTestNthSectionAnalysis gets test nthsection analysis by its ID. 381 func GetTestNthSectionAnalysis(ctx context.Context, analysisID int64) (*model.TestNthSectionAnalysis, error) { 382 nsa := &model.TestNthSectionAnalysis{ 383 ID: analysisID, 384 } 385 err := datastore.Get(ctx, nsa) 386 if err != nil { 387 return nil, errors.Annotate(err, "get Nthsection analysis with id %d", nsa.ID).Err() 388 } 389 return nsa, err 390 } 391 392 // GetTestNthSectionForAnalysis gets test nthsection analysis for a test failure analysis. 393 // This may return nil if the nthsection analysis has not been created yet. 394 func GetTestNthSectionForAnalysis(ctx context.Context, tfa *model.TestFailureAnalysis) (*model.TestNthSectionAnalysis, error) { 395 q := datastore.NewQuery("TestNthSectionAnalysis").Eq("parent_analysis_key", datastore.KeyForObj(ctx, tfa)) 396 analyses := []*model.TestNthSectionAnalysis{} 397 err := datastore.GetAll(ctx, q, &analyses) 398 if err != nil { 399 return nil, errors.Annotate(err, "get all").Err() 400 } 401 if len(analyses) == 0 { 402 return nil, nil 403 } 404 if len(analyses) > 1 { 405 return nil, errors.Annotate(err, "found more than 1 nthsection analysis: %d", len(analyses)).Err() 406 } 407 return analyses[0], nil 408 } 409 410 // GetInProgressReruns returns the reruns which are in progress. 411 func GetInProgressReruns(ctx context.Context, tfa *model.TestFailureAnalysis) ([]*model.TestSingleRerun, error) { 412 q := datastore.NewQuery("TestSingleRerun"). 413 Eq("analysis_key", datastore.KeyForObj(ctx, tfa)). 414 Eq("status", pb.RerunStatus_RERUN_STATUS_IN_PROGRESS) 415 416 reruns := []*model.TestSingleRerun{} 417 err := datastore.GetAll(ctx, q, &reruns) 418 if err != nil { 419 return nil, errors.Annotate(err, "get test reruns").Err() 420 } 421 return reruns, nil 422 } 423 424 func GetVerificationRerunsForTestCulprit(ctx context.Context, culprit *model.Suspect) (culpritRerun *model.TestSingleRerun, parentRerun *model.TestSingleRerun, reterr error) { 425 var err error 426 if culprit.SuspectRerunBuild != nil { 427 culpritRerun, err = GetTestSingleRerun(ctx, culprit.SuspectRerunBuild.IntID()) 428 if err != nil { 429 return nil, nil, errors.Annotate(err, "get suspect rerun").Err() 430 } 431 } 432 if culprit.ParentRerunBuild != nil { 433 parentRerun, err = GetTestSingleRerun(ctx, culprit.ParentRerunBuild.IntID()) 434 if err != nil { 435 return nil, nil, errors.Annotate(err, "get parent rerun").Err() 436 } 437 } 438 return culpritRerun, parentRerun, nil 439 } 440 441 // GetTestNthSectionReruns returns the nthsection reruns. 442 // The reruns are ordered by create time. 443 func GetTestNthSectionReruns(ctx context.Context, nsa *model.TestNthSectionAnalysis) ([]*model.TestSingleRerun, error) { 444 q := datastore.NewQuery("TestSingleRerun").Eq("nthsection_analysis_key", datastore.KeyForObj(ctx, nsa)).Order("luci_build.create_time") 445 reruns := []*model.TestSingleRerun{} 446 err := datastore.GetAll(ctx, q, &reruns) 447 if err != nil { 448 return nil, errors.Annotate(err, "get test reruns").Err() 449 } 450 return reruns, nil 451 } 452 453 func GetProjectForCompileFailureAnalysisID(ctx context.Context, analysisID int64) (string, error) { 454 cfa, err := GetCompileFailureAnalysis(ctx, analysisID) 455 if err != nil { 456 return "", errors.Annotate(err, "get compile failure analysis").Err() 457 } 458 return GetProjectForCompileFailureAnalysis(ctx, cfa) 459 } 460 461 func GetProjectForCompileFailureAnalysis(ctx context.Context, cfa *model.CompileFailureAnalysis) (string, error) { 462 cf, err := GetCompileFailureForAnalysis(ctx, cfa) 463 if err != nil { 464 return "", errors.Annotate(err, "get compile failure for analysis").Err() 465 } 466 build, err := GetBuild(ctx, cf.Build.IntID()) 467 if err != nil { 468 return "", errors.Annotate(err, "get build").Err() 469 } 470 return build.Project, nil 471 }