Backend: Parent-child pipeline - `commit#status` is returning nil
Description
A project that has a parent-child pipeline will return an incorrect commit status
Impact
In ProjectPipelineStatus
, we use project.commit.status
to query the commit status if there's no cache entry
Details.
- A Project has a only parent-child pipeline. e.g https://staging.gitlab.com/gitlab-qa-sandbox-group/qa-test-2020-07-26-20-38-46-f609810422ec8edc/pipeline-independent-relationship-dc1d1d3b35899bd1/-/pipelines/12804829
-
project.commit.status
for that project
[ gstg ] production> project.commit.status
=> nil
# status is delegated to app/models/commit_with_pipeline.rb which is latest_pipeline(ref)&.status
# latest_pipeline is nil
[ gstg ] production> project.commit.latest_pipeline
=> nil
# latest_pipeline is latest_pipeline_for_project(ref, project)
[ gstg ] production> project.commit.latest_pipeline_for_project(nil, project)
=> nil
# latest_pipeline_for_project is pipeline_project.ci_pipelines.latest_pipeline_per_commit(id, ref)[id]
[ gstg ] production> project.ci_pipelines.latest_pipeline_per_commit(commit.id, ref)
=> {}
-
latest_pipeline_per_commit
, does the following query, it is returning nothing:
SELECT "ci_pipelines".* FROM "ci_pipelines" LEFT OUTER JOIN "ci_pipelines" "ci_pipelines_2" ON "ci_pipelines"."sha" = "ci_pipelines_2"."sha" AND "ci_pipelines"."project_id" = "ci_pipelines_2"."project_id" AND "ci_pipelines"."id" < "ci_pipelines_2"."id" WHERE "ci_pipelines"."project_id" = 4758426 AND ("ci_pipelines"."config_source" IN (1, 2, 4, 5) OR "ci_pipelines"."config_source" IS NULL) AND "ci_pipelines"."sha" = '63ce735d98d6692d82f02d4499981c7e977471d5' AND "ci_pipelines_2"."id" IS NULL /*application:web,controller:projects,action:starred,correlation_id:teivjWERVs2*/
- But if we run the same query with
config_source
clause we get the12804830
pipeline (bridge_source
)
[ gstg ] production> rows= ActiveRecord::Base.connection.execute %q{SELECT "ci_pipelines".* FROM "ci_pipelines" LEFT OUTER JOIN "ci_pipelines" "ci_pipelines_2" ON "ci_pipelines"."sha" = "ci_pipelines_2"."sha" AND "ci_pipelines"."project_id" = "ci_pipelines_2"."project_id" AND "ci_pipelines"."id" < "ci_pipelines_2"."id" WHERE "ci_pipelines"."project_id" = 4758426 AND "ci_pipelines"."sha" = '63ce735d98d6692d82f02d4499981c7e977471d5' AND "ci_pipelines_2"."id" IS NULL}
=> #<PG::Result:0x00007fa934695458 status=PGRES_TUPLES_OK ntuples=1 nfields=29 cmd_tuples=1>
[ gstg ] production> rows.first
=> {"id"=>12804830, "ref"=>"master", "sha"=>"63ce735d98d6692d82f02d4499981c7e977471d5", "before_sha"=>"0000000000000000000000000000000000000000", "created_at"=>2020-07-26 20:46:41 UTC, "updated_at"=>2020-07-26 20:46:46 UTC, "tag"=>false, "yaml_errors"=>nil, "committed_at"=>nil, "project_id"=>4758426, "status"=>"failed", "started_at"=>2020-07-26 20:46:45 UTC, "finished_at"=>2020-07-26 20:46:46 UTC, "duration"=>1, "user_id"=>1614863, "lock_version"=>3, "auto_canceled_by_id"=>nil, "pipeline_schedule_id"=>nil, "source"=>12, "protected"=>true, "config_source"=>6, "failure_reason"=>nil, "iid"=>2, "merge_request_id"=>nil, "source_sha"=>nil, "target_sha"=>nil, "external_pull_request_id"=>nil, "ci_ref_id"=>16124, "locked"=>0}
- I think the issue is that the query thinks that the
bridge_source
is later so it "shadows" therepository_source
pipeline:
[ gstg ] production> Ci::PipelineEnums.ci_config_sources
=> {:unknown_source=>nil, :repository_source=>1, :auto_devops_source=>2, :remote_source=>4, :external_project_source=>5}
[ gstg ] production> ::Ci::PipelineEnums.config_sources
=> {:unknown_source=>nil, :repository_source=>1, :auto_devops_source=>2, :webide_source=>3, :remote_source=>4, :external_project_source=>5, :bridge_source=>6, :parameter_source=>7}
[ gstg ] production> pipeline = Ci::Pipeline.find 12804829
=> #<Ci::Pipeline id: 12804829, ref: "master", sha: "63ce735d98d6692d82f02d4499981c7e977471d5", before_sha: "0000000000000000000000000000000000000000", created_at: "2020-07-26 20:46:41", updated_at: "2020-07-26 20:46:43", tag: false, yaml_errors: nil, committed_at: nil, project_id: 4758426, status: "success", started_at: "2020-07-26 20:46:41", finished_at: "2020-07-26 20:46:43", duration: 1, user_id: 1614863, lock_version: 3, auto_canceled_by_id: nil, pipeline_schedule_id: nil, source: "push", protected: true, config_source: "repository_source", failure_reason: nil, iid: 1, merge_request_id: nil, source_sha: nil, target_sha: nil, external_pull_request_id: nil, ci_ref_id: 16124, locked: "unlocked">
[ gstg ] production> pipeline.config_source
=> "repository_source"
[ gstg ] production> pipeline = Ci::Pipeline.find 12804830
=> #<Ci::Pipeline id: 12804830, ref: "master", sha: "63ce735d98d6692d82f02d4499981c7e977471d5", before_sha: "0000000000000000000000000000000000000000", created_at: "2020-07-26 20:46:41", updated_at: "2020-07-26 20:46:46", tag: false, yaml_errors: nil, committed_at: nil, project_id: 4758426, status: "failed", started_at: "2020-07-26 20:46:45", finished_at: "2020-07-26 20:46:46", duration: 1, user_id: 1614863, lock_version: 3, auto_canceled_by_id: nil, pipeline_schedule_id: nil, source: "parent_pipeline", protected: true, config_source: "bridge_source", failure_reason: nil, iid: 2, merge_request_id: nil, source_sha: nil, target_sha: nil, external_pull_request_id: nil, ci_ref_id: 16124, locked: "unlocked">
ps. contrast with a project that does not use parent-child pipelines (https://staging.gitlab.com/hello-staging/minimal-ruby-app)
[ gstg ] production> project = Project.find 4349298
=> #<Project id:4349298 hello-staging/minimal-ruby-app>>
[ gstg ] production> project.commit.status
=> "success"
User Impact
Incorrect commit status being returned in parent-child pipeline
Edited by Mark Nuzzo