Commit 9d589b9f authored by GitLab Bot's avatar GitLab Bot

Automatic merge of gitlab-org/gitlab master

parents 77850f8a 94468c74
......@@ -12,16 +12,10 @@ module Packages
end
def execute
results = base.npm
.with_name(@package_name)
.installable
.last_of_each_version
unless Feature.enabled?(:npm_presenter_queries_tuning)
results = results.preload_files
end
results
base.npm
.with_name(@package_name)
.installable
.last_of_each_version
end
private
......
......@@ -13,34 +13,6 @@ module Packages
end
def versions
if queries_tuning?
new_versions
else
legacy_versions
end
end
def dist_tags
build_package_tags.tap { |t| t["latest"] ||= sorted_versions.last }
end
private
def legacy_versions
package_versions = {}
packages.each do |package|
package_file = package.package_files.last
next unless package_file
package_versions[package.version] = build_package_version(package, package_file)
end
package_versions
end
def new_versions
package_versions = {}
packages.each_batch do |relation|
......@@ -58,6 +30,12 @@ module Packages
package_versions
end
def dist_tags
build_package_tags.tap { |t| t["latest"] ||= sorted_versions.last }
end
private
def build_package_tags
package_tags.to_h { |tag| [tag.name, tag.package.version] }
end
......@@ -84,20 +62,9 @@ module Packages
def build_package_dependencies(package)
dependencies = Hash.new { |h, key| h[key] = {} }
if queries_tuning?
package.dependency_links.each do |dependency_link|
dependency = dependency_link.dependency
dependencies[dependency_link.dependency_type][dependency.name] = dependency.version_pattern
end
else
dependency_links = package.dependency_links
.with_dependency_type(%i[dependencies devDependencies bundleDependencies peerDependencies])
.includes_dependency
dependency_links.find_each do |dependency_link|
dependency = dependency_link.dependency
dependencies[dependency_link.dependency_type][dependency.name] = dependency.version_pattern
end
package.dependency_links.each do |dependency_link|
dependency = dependency_link.dependency
dependencies[dependency_link.dependency_type][dependency.name] = dependency.version_pattern
end
dependencies
......@@ -112,10 +79,6 @@ module Packages
Packages::Tag.for_packages(packages)
.preload_package
end
def queries_tuning?
Feature.enabled?(:npm_presenter_queries_tuning)
end
end
end
end
---
name: npm_presenter_queries_tuning
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/68275
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/338603
milestone: '14.2'
type: development
group: group::package
default_enabled: false
......@@ -68,20 +68,6 @@ RSpec.describe ::Packages::Npm::PackageFinder do
it { is_expected.to be_empty }
end
context 'with npm_presenter_queries_tuning disabled' do
before do
stub_feature_flags(npm_presenter_queries_tuning: false)
end
it_behaves_like 'finding packages by name'
context 'set to nil' do
let(:project) { nil }
it { is_expected.to be_empty }
end
end
end
context 'with a namespace' do
......@@ -94,20 +80,6 @@ RSpec.describe ::Packages::Npm::PackageFinder do
it { is_expected.to be_empty }
end
context 'with npm_presenter_queries_tuning disabled' do
before do
stub_feature_flags(npm_presenter_queries_tuning: false)
end
it_behaves_like 'accepting a namespace for', 'finding packages by name'
context 'set to nil' do
let_it_be(:namespace) { nil }
it { is_expected.to be_empty }
end
end
end
end
......@@ -137,24 +109,6 @@ RSpec.describe ::Packages::Npm::PackageFinder do
it_behaves_like 'accepting a namespace for', 'finding packages by version'
end
context 'with npm_presenter_queries_tuning disabled' do
before do
stub_feature_flags(npm_presenter_queries_tuning: false)
end
context 'with a project' do
let(:finder) { described_class.new(package_name, project: project) }
it_behaves_like 'finding packages by version'
end
context 'with a namespace' do
let(:finder) { described_class.new(package_name, namespace: namespace) }
it_behaves_like 'accepting a namespace for', 'finding packages by version'
end
end
end
describe '#last' do
......@@ -194,13 +148,5 @@ RSpec.describe ::Packages::Npm::PackageFinder do
end
it_behaves_like 'handling project or namespace parameter'
context 'with npm_presenter_queries_tuning disabled' do
before do
stub_feature_flags(npm_presenter_queries_tuning: false)
end
it_behaves_like 'handling project or namespace parameter'
end
end
end
......@@ -15,110 +15,86 @@ RSpec.describe ::Packages::Npm::PackagePresenter do
describe '#versions' do
subject { presenter.versions }
shared_examples 'returning packages versions' do |expect_n_plus_one: false|
context 'for packages without dependencies' do
it { is_expected.to be_a(Hash) }
it { expect(subject[package1.version].with_indifferent_access).to match_schema('public_api/v4/packages/npm_package_version') }
it { expect(subject[package2.version].with_indifferent_access).to match_schema('public_api/v4/packages/npm_package_version') }
::Packages::DependencyLink.dependency_types.keys.each do |dependency_type|
it { expect(subject.dig(package1.version, dependency_type)).to be nil }
it { expect(subject.dig(package2.version, dependency_type)).to be nil }
end
context 'for packages without dependencies' do
it { is_expected.to be_a(Hash) }
it { expect(subject[package1.version].with_indifferent_access).to match_schema('public_api/v4/packages/npm_package_version') }
it { expect(subject[package2.version].with_indifferent_access).to match_schema('public_api/v4/packages/npm_package_version') }
::Packages::DependencyLink.dependency_types.keys.each do |dependency_type|
it { expect(subject.dig(package1.version, dependency_type)).to be nil }
it { expect(subject.dig(package2.version, dependency_type)).to be nil }
end
it 'avoids N+1 database queries' do
check_n_plus_one(:versions, expect_it: expect_n_plus_one) do
create_list(:npm_package, 5, project: project, name: package_name)
end
it 'avoids N+1 database queries' do
check_n_plus_one(:versions) do
create_list(:npm_package, 5, project: project, name: package_name)
end
end
end
context 'for packages with dependencies' do
::Packages::DependencyLink.dependency_types.keys.each do |dependency_type|
let_it_be("package_dependency_link_for_#{dependency_type}") { create(:packages_dependency_link, package: package1, dependency_type: dependency_type) }
end
context 'for packages with dependencies' do
::Packages::DependencyLink.dependency_types.keys.each do |dependency_type|
let_it_be("package_dependency_link_for_#{dependency_type}") { create(:packages_dependency_link, package: package1, dependency_type: dependency_type) }
end
it { is_expected.to be_a(Hash) }
it { expect(subject[package1.version].with_indifferent_access).to match_schema('public_api/v4/packages/npm_package_version') }
it { expect(subject[package2.version].with_indifferent_access).to match_schema('public_api/v4/packages/npm_package_version') }
::Packages::DependencyLink.dependency_types.keys.each do |dependency_type|
it { expect(subject.dig(package1.version, dependency_type.to_s)).to be_any }
end
it { is_expected.to be_a(Hash) }
it { expect(subject[package1.version].with_indifferent_access).to match_schema('public_api/v4/packages/npm_package_version') }
it { expect(subject[package2.version].with_indifferent_access).to match_schema('public_api/v4/packages/npm_package_version') }
::Packages::DependencyLink.dependency_types.keys.each do |dependency_type|
it { expect(subject.dig(package1.version, dependency_type.to_s)).to be_any }
end
it 'avoids N+1 database queries' do
check_n_plus_one(:versions, expect_it: expect_n_plus_one) do
create_list(:npm_package, 5, project: project, name: package_name).each do |npm_package|
::Packages::DependencyLink.dependency_types.keys.each do |dependency_type|
create(:packages_dependency_link, package: npm_package, dependency_type: dependency_type)
end
it 'avoids N+1 database queries' do
check_n_plus_one(:versions) do
create_list(:npm_package, 5, project: project, name: package_name).each do |npm_package|
::Packages::DependencyLink.dependency_types.keys.each do |dependency_type|
create(:packages_dependency_link, package: npm_package, dependency_type: dependency_type)
end
end
end
end
end
it_behaves_like 'returning packages versions'
context 'with npm_presenter_queries_tuning disabled' do
before do
stub_feature_flags(npm_presenter_queries_tuning: false)
end
it_behaves_like 'returning packages versions', expect_n_plus_one: true
end
end
describe '#dist_tags' do
subject { presenter.dist_tags }
shared_examples 'returning packages tags' do
context 'for packages without tags' do
it { is_expected.to be_a(Hash) }
it { expect(subject["latest"]).to eq(latest_package.version) }
context 'for packages without tags' do
it { is_expected.to be_a(Hash) }
it { expect(subject["latest"]).to eq(latest_package.version) }
it 'avoids N+1 database queries' do
check_n_plus_one(:dist_tags) do
create_list(:npm_package, 5, project: project, name: package_name)
end
it 'avoids N+1 database queries' do
check_n_plus_one(:dist_tags) do
create_list(:npm_package, 5, project: project, name: package_name)
end
end
end
context 'for packages with tags' do
let_it_be(:package_tag1) { create(:packages_tag, package: package1, name: 'release_a') }
let_it_be(:package_tag2) { create(:packages_tag, package: package1, name: 'test_release') }
let_it_be(:package_tag3) { create(:packages_tag, package: package2, name: 'release_b') }
let_it_be(:package_tag4) { create(:packages_tag, package: latest_package, name: 'release_c') }
let_it_be(:package_tag5) { create(:packages_tag, package: latest_package, name: 'latest') }
it { is_expected.to be_a(Hash) }
it { expect(subject[package_tag1.name]).to eq(package1.version) }
it { expect(subject[package_tag2.name]).to eq(package1.version) }
it { expect(subject[package_tag3.name]).to eq(package2.version) }
it { expect(subject[package_tag4.name]).to eq(latest_package.version) }
it { expect(subject[package_tag5.name]).to eq(latest_package.version) }
it 'avoids N+1 database queries' do
check_n_plus_one(:dist_tags) do
create_list(:npm_package, 5, project: project, name: package_name).each_with_index do |npm_package, index|
create(:packages_tag, package: npm_package, name: "tag_#{index}")
end
context 'for packages with tags' do
let_it_be(:package_tag1) { create(:packages_tag, package: package1, name: 'release_a') }
let_it_be(:package_tag2) { create(:packages_tag, package: package1, name: 'test_release') }
let_it_be(:package_tag3) { create(:packages_tag, package: package2, name: 'release_b') }
let_it_be(:package_tag4) { create(:packages_tag, package: latest_package, name: 'release_c') }
let_it_be(:package_tag5) { create(:packages_tag, package: latest_package, name: 'latest') }
it { is_expected.to be_a(Hash) }
it { expect(subject[package_tag1.name]).to eq(package1.version) }
it { expect(subject[package_tag2.name]).to eq(package1.version) }
it { expect(subject[package_tag3.name]).to eq(package2.version) }
it { expect(subject[package_tag4.name]).to eq(latest_package.version) }
it { expect(subject[package_tag5.name]).to eq(latest_package.version) }
it 'avoids N+1 database queries' do
check_n_plus_one(:dist_tags) do
create_list(:npm_package, 5, project: project, name: package_name).each_with_index do |npm_package, index|
create(:packages_tag, package: npm_package, name: "tag_#{index}")
end
end
end
end
it_behaves_like 'returning packages tags'
context 'with npm_presenter_queries_tuning disabled' do
before do
stub_feature_flags(npm_presenter_queries_tuning: false)
end
it_behaves_like 'returning packages tags'
end
end
def check_n_plus_one(field, expect_it: false)
def check_n_plus_one(field)
pkgs = project.packages.npm.with_name(package_name).last_of_each_version.preload_files
control = ActiveRecord::QueryRecorder.new { described_class.new(package_name, pkgs).public_send(field) }
......@@ -126,10 +102,6 @@ RSpec.describe ::Packages::Npm::PackagePresenter do
pkgs = project.packages.npm.with_name(package_name).last_of_each_version.preload_files
if expect_it
expect { described_class.new(package_name, pkgs).public_send(field) }.to exceed_query_limit(control)
else
expect { described_class.new(package_name, pkgs).public_send(field) }.not_to exceed_query_limit(control)
end
expect { described_class.new(package_name, pkgs).public_send(field) }.not_to exceed_query_limit(control)
end
end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment