Preload member_role associations and add inverse has_many to MemberRole
## Context | | | |---|---| | **Phase** | 2 of 6 | | **Parallel with** | https://gitlab.com/gitlab-org/gitlab/-/work_items/594877+ <br> https://gitlab.com/gitlab-org/gitlab/-/work_items/594878+ | | **Blocked by** | https://gitlab.com/gitlab-org/gitlab/-/work_items/594874+ | | **Unblocks** | https://gitlab.com/gitlab-org/gitlab/-/work_items/594880+ <br> https://gitlab.com/gitlab-org/gitlab/-/work_items/594881+ <br> https://gitlab.com/gitlab-org/gitlab/-/work_items/594882+ <br> https://gitlab.com/gitlab-org/gitlab/-/work_items/594883+ <br> https://gitlab.com/gitlab-org/gitlab/-/work_items/594884+ | ## Summary Add preloading of `member_role` associations to prevent N+1 queries when listing protected branches, and add inverse `has_many` associations on `MemberRole` for completeness. ## Background When rendering protected branch lists (e.g. in the API index response or branch rules UI), each access level row's `humanize` method (and any display of the custom role name) will trigger a query for `member_role` if not preloaded. This issue ensures association preloading is in place before the feature is enabled. ## Relevant files - `ee/app/models/concerns/ee/protected_branch.rb` — contains `preload_access_levels` scope override - `ee/app/models/members/member_role.rb` — the `MemberRole` model ## Changes required ### Update `ee/app/models/concerns/ee/protected_branch.rb` The existing `preload_access_levels` EE override currently adds `:unprotect_access_levels`. Extend it to also preload `member_role` on all three access level types: ```ruby override :preload_access_levels scope :preload_access_levels, -> do preload( merge_access_levels: [:user, :group, :member_role], push_access_levels: [:user, :group, :deploy_key, :member_role], unprotect_access_levels: [:user, :group, :member_role] ) end ``` ### Update `ee/app/models/members/member_role.rb` Add inverse associations: ```ruby has_many :protected_branch_merge_access_levels, class_name: 'ProtectedBranch::MergeAccessLevel', foreign_key: :member_role_id, inverse_of: :member_role has_many :protected_branch_push_access_levels, class_name: 'ProtectedBranch::PushAccessLevel', foreign_key: :member_role_id, inverse_of: :member_role has_many :protected_branch_unprotect_access_levels, class_name: 'ProtectedBranch::UnprotectAccessLevel', foreign_key: :member_role_id, inverse_of: :member_role ``` ## Testing - Use `QueryRecorder` / `exceed_query_limit` to verify no N+1 when fetching a list of protected branches that each have a `member_role`-type access level - Verify `MemberRole` inverse associations load correctly ## Dependencies - Issue 1 (DB migration) — `member_role_id` column must exist - Issue 3 (`EE::ProtectedBranchAccess` concern) — `belongs_to :member_role` must be defined first ## Labels
issue