Follow-up from "Fix remaining N+1 queries in EnvironmentSerializer"
The following discussion from !82746 (merged) should be addressed:
-
@shinya.maeda started a discussion: (+1 comment) While having this preloader is a nice improvement, it has a duplicate logic with the current SSOT method. This might gives us hard time to keep synchronizing these logic in the long run, for example, project-level protected environment could also use
tier
for the matching.I wonder if we can use the SSOT method in this preloader as well. Something like:
diff --git a/ee/app/models/preloaders/environments/protected_environment_preloader.rb b/ee/app/models/preloaders/environments/protected_environment_preloader.rb index a1da48bbdb8..61b36e5c67b 100644 --- a/ee/app/models/preloaders/environments/protected_environment_preloader.rb +++ b/ee/app/models/preloaders/environments/protected_environment_preloader.rb @@ -16,15 +16,14 @@ def initialize(environments) def execute return if environments.empty? - project = environments.first.project - project_id = project.id - group_ids = project.ancestors_upto_ids + associated_protected_environments = ProtectedEnvironment + .for_environments(environments).preload(:deploy_access_levels) - names = environments.map(&:name) - tiers = environments.map(&:tier) + project_protected_environments = associated_protected_environments + .select(&:project_level?).index_by(&:name) - project_protected_environments = ProtectedEnvironment.preload(:deploy_access_levels).where(project_id: project_id, name: names).index_by(&:name) - group_protected_environments = ProtectedEnvironment.preload(:deploy_access_levels).where(group_id: group_ids, name: tiers).index_by(&:name) + group_protected_environments = associated_protected_environments + .select(&:group_level?).index_by(&:name) environments.each do |environment| protected_environments ||= [] diff --git a/ee/app/models/protected_environment.rb b/ee/app/models/protected_environment.rb index 33146642fa8..9fc52ea03f4 100644 --- a/ee/app/models/protected_environment.rb +++ b/ee/app/models/protected_environment.rb @@ -37,17 +37,28 @@ def deploy_access_levels_by_group(group) .where(group: group) end - def for_environment(environment) + def for_environment_with_cache(environment) raise ArgumentError unless environment.is_a?(::Environment) key = "protected_environment:for_environment:#{environment.id}" - ::Gitlab::SafeRequestStore.fetch(key) do - from_union([ - where(project: environment.project_id, name: environment.name), - where(group: environment.project.ancestors_upto_ids, name: environment.tier) - ]) + ::Gitlab::SafeRequestStore.fetch(key) { for_environments([environment]) } + end + + def for_environments(environments) + if environments.map(&:project_id).uniq.size > 1 + raise ArgumentError, 'Environments must be in the same project' end + + project_id = environments.project_id + group_ids = environments.first.project.ancestors_upto_ids + names = environments.map(&:name) + tiers = environments.map(&:tier) + + from_union([ + where(project: project_id, name: names), + where(group: group_ids, name: tiers) + ]) end end
I do not strongly feel that this refactoring should be placed in this MR, so feel free to move on as-is. However, we'd like to catch this up at some point.