Skip to content

Fix inconsistent memory association for ProjectNamespace / Project

What does this MR do and why?

Currently the inverse_of relationship is not used due to some technical debt on Project that needs to be resolved. It results in a loaded Project and its ProjectNamespace having inconsistent objects loaded in memory. This MR adds the inverse_of relationship so that they are more performant and consistent 👍

See https://github.com/rails/rails/issues/8125 and https://www.viget.com/articles/exploring-the-inverse-of-option-on-rails-model-associations/ for some context, and !89311 (closed) for prior art 🙂

Screenshots or screen recordings

There should be no detectable user-facing changes

How to set up and validate locally

This was initially detected in specs with following code:

p = create(:project)

p.project_namespace.project == nil # this returns true

However loading the project does not result in the same issue:

p = Project.find(project.id)
p.project_namespace.project == nil # this returns false

This can also be reproduced from a rails console through Projects::CreateService

[1] pry(main)> group = Group.first
[2] pry(main)> params = {
[2] pry(main)*   namespace_id: group.id,
[2] pry(main)*   name: 'test1'.titleize,
[2] pry(main)*   path: 'test1',
[2] pry(main)*   description: FFaker::Lorem.sentence,
[2] pry(main)*   visibility_level: Gitlab::VisibilityLevel::PRIVATE,
[2] pry(main)*   skip_disk_validation: true
[2] pry(main)* }
=> {:namespace_id=>22, :name=>"Test1", :path=>"test1", :description=>"Omnis distinctio omnis nemo sequi atque voluptate non.", :visibility_level=>0, :skip_disk_validation=>true}
[3] pry(main)> project = ::Projects::CreateService.new(User.first, params).execute
[4] pry(main)> project.project_namespace.project
=> nil

With the change:

[156] pry(main)> group = Group.first
[157] pry(main)> params = { namespace_id: group.id, name: 'name15', path: 'name15', visibility_level: Gitlab::VisibilityLevel::PRIVATE }
=> {:namespace_id=>22, :name=>"name15", :path=>"name15", :visibility_level=>0}
[158] pry(main)> project = ::Projects::CreateService.new(User.first, params).execute
[159] pry(main)> project.project_namespace.association(:mirror_project).loaded?
=> true
[160] pry(main)> project.project_namespace.association(:project).loaded?
=> false
[161] pry(main)> project.project_namespace.project
=> nil
[162] pry(main)> project.project_namespace.mirror_project
=> #<Project id: gitlab-org/name15>>

MR acceptance checklist

This checklist encourages us to confirm any changes have been analyzed to reduce risks in quality, performance, reliability, security, and maintainability.

Related to #364277 (closed)

Edited by charlie ablett

Merge request reports