Commit 5a9fa254 authored by Nick Thomas's avatar Nick Thomas

Merge branch 'ajk-229664' into 'master'

Fix performance of save designs service spec

See merge request gitlab-org/gitlab!39739
parents 5adf8f0a 10a0d747
...@@ -101,7 +101,7 @@ module DesignManagement ...@@ -101,7 +101,7 @@ module DesignManagement
scope :current, -> { visible_at_version(nil) } scope :current, -> { visible_at_version(nil) }
def self.relative_positioning_query_base(design) def self.relative_positioning_query_base(design)
on_issue(design.issue_id) default_scoped.on_issue(design.issue_id)
end end
def self.relative_positioning_parent_column def self.relative_positioning_parent_column
......
...@@ -5,9 +5,9 @@ RSpec.describe DesignManagement::SaveDesignsService do ...@@ -5,9 +5,9 @@ RSpec.describe DesignManagement::SaveDesignsService do
include DesignManagementTestHelpers include DesignManagementTestHelpers
include ConcurrentHelpers include ConcurrentHelpers
let_it_be(:developer) { create(:user) } let_it_be_with_reload(:issue) { create(:issue) }
let_it_be(:developer) { create(:user, developer_projects: [issue.project]) }
let(:project) { issue.project } let(:project) { issue.project }
let(:issue) { create(:issue) }
let(:user) { developer } let(:user) { developer }
let(:files) { [rails_sample] } let(:files) { [rails_sample] }
let(:design_repository) { ::Gitlab::GlRepository::DESIGN.repository_resolver.call(project) } let(:design_repository) { ::Gitlab::GlRepository::DESIGN.repository_resolver.call(project) }
...@@ -19,8 +19,20 @@ RSpec.describe DesignManagement::SaveDesignsService do ...@@ -19,8 +19,20 @@ RSpec.describe DesignManagement::SaveDesignsService do
fixture_file_upload("spec/fixtures/#{filename}") fixture_file_upload("spec/fixtures/#{filename}")
end end
def commit_count
design_repository.expire_statistics_caches
design_repository.expire_root_ref_cache
design_repository.commit_count
end
before do before do
project.add_developer(developer) if issue.design_collection.repository.exists?
issue.design_collection.repository.expire_all_method_caches
issue.design_collection.repository.raw.delete_all_refs_except([Gitlab::Git::BLANK_SHA])
end
allow(::DesignManagement::NewVersionWorker)
.to receive(:perform_async).with(Integer).and_return(nil)
end end
def run_service(files_to_upload = nil) def run_service(files_to_upload = nil)
...@@ -83,24 +95,20 @@ RSpec.describe DesignManagement::SaveDesignsService do ...@@ -83,24 +95,20 @@ RSpec.describe DesignManagement::SaveDesignsService do
design_repository.exists? design_repository.exists?
end end
it 'creates a design repository when it did not exist' do it 'is ensured when the service runs' do
expect { run_service }.to change { repository_exists }.from(false).to(true) run_service
expect(repository_exists).to be true
end end
end end
it 'updates the creation count' do it 'creates a commit, an event in the activity stream and updates the creation count' do
counter = Gitlab::UsageDataCounters::DesignsCounter counter = Gitlab::UsageDataCounters::DesignsCounter
expect { run_service }.to change { counter.read(:create) }.by(1)
end
it 'creates an event in the activity stream' do
expect { run_service } expect { run_service }
.to change { Event.count }.by(1) .to change { Event.count }.by(1)
.and change { Event.for_design.created_action.count }.by(1) .and change { Event.for_design.created_action.count }.by(1)
end .and change { counter.read(:create) }.by(1)
it 'creates a commit in the repository' do
run_service
expect(design_repository.commit).to have_attributes( expect(design_repository.commit).to have_attributes(
author: user, author: user,
...@@ -109,36 +117,27 @@ RSpec.describe DesignManagement::SaveDesignsService do ...@@ -109,36 +117,27 @@ RSpec.describe DesignManagement::SaveDesignsService do
end end
it 'can run the same command in parallel' do it 'can run the same command in parallel' do
blocks = Array.new(10).map do parellism = 4
unique_files = %w(rails_sample.jpg dk.png)
.map { |name| RenameableUpload.unique_file(name) }
-> { run_service(unique_files) } blocks = Array.new(parellism).map do
end unique_files = [RenameableUpload.unique_file('rails_sample.jpg')]
expect { run_parallel(blocks) }.to change(DesignManagement::Version, :count).by(10) -> { run_service(unique_files) }
end end
it 'causes diff_refs not to be nil' do expect { run_parallel(blocks) }.to change(DesignManagement::Version, :count).by(parellism)
expect(response).to include(
designs: all(have_attributes(diff_refs: be_present))
)
end end
it 'creates a design & a version for the filename if it did not exist' do describe 'the response' do
expect(issue.designs.size).to eq(0) it 'includes designs with the expected properties' do
updated_designs = response[:designs] updated_designs = response[:designs]
expect(updated_designs).to all(have_attributes(diff_refs: be_present))
expect(updated_designs.size).to eq(1) expect(updated_designs.size).to eq(1)
expect(updated_designs.first.versions.size).to eq(1) expect(updated_designs.first.versions.size).to eq(1)
end
it 'saves the user as the author' do
updated_designs = response[:designs]
expect(updated_designs.first.versions.first.author).to eq(user) expect(updated_designs.first.versions.first.author).to eq(user)
end end
end
describe 'saving the file to LFS' do describe 'saving the file to LFS' do
before do before do
...@@ -147,14 +146,10 @@ RSpec.describe DesignManagement::SaveDesignsService do ...@@ -147,14 +146,10 @@ RSpec.describe DesignManagement::SaveDesignsService do
end end
end end
it 'saves the design to LFS' do it 'saves the design to LFS and saves the repository_type of the LfsObjectsProject as design' do
expect { run_service }.to change { LfsObject.count }.by(1) expect { run_service }
end .to change { LfsObject.count }.by(1)
.and change { project.lfs_objects_projects.count }.from(0).to(1)
it 'saves the repository_type of the LfsObjectsProject as design' do
expect do
run_service
end.to change { project.lfs_objects_projects.count }.from(0).to(1)
expect(project.lfs_objects_projects.first.repository_type).to eq('design') expect(project.lfs_objects_projects.first.repository_type).to eq('design')
end end
...@@ -202,12 +197,10 @@ RSpec.describe DesignManagement::SaveDesignsService do ...@@ -202,12 +197,10 @@ RSpec.describe DesignManagement::SaveDesignsService do
run_service run_service
end end
it 'does not create a new version' do it 'does not create a new version, and returns the design in `skipped_designs`' do
expect { run_service }.not_to change { issue.design_versions.count } response = nil
end
it 'returns the design in `skipped_designs` instead of `designs`' do expect { response = run_service }.not_to change { issue.design_versions.count }
response = run_service
expect(response[:designs]).to be_empty expect(response[:designs]).to be_empty
expect(response[:skipped_designs].size).to eq(1) expect(response[:skipped_designs].size).to eq(1)
...@@ -223,35 +216,20 @@ RSpec.describe DesignManagement::SaveDesignsService do ...@@ -223,35 +216,20 @@ RSpec.describe DesignManagement::SaveDesignsService do
touch_files([files.first]) touch_files([files.first])
end end
it 'counts one creation and one update' do it 'has the correct side-effects' do
counter = Gitlab::UsageDataCounters::DesignsCounter counter = Gitlab::UsageDataCounters::DesignsCounter
expect { run_service }
.to change { counter.read(:create) }.by(1)
.and change { counter.read(:update) }.by(1)
end
it 'creates the correct activity stream events' do expect(::DesignManagement::NewVersionWorker)
.to receive(:perform_async).once.with(Integer).and_return(nil)
expect { run_service } expect { run_service }
.to change { Event.count }.by(2) .to change { Event.count }.by(2)
.and change { Event.for_design.count }.by(2) .and change { Event.for_design.count }.by(2)
.and change { Event.created_action.count }.by(1) .and change { Event.created_action.count }.by(1)
.and change { Event.updated_action.count }.by(1) .and change { Event.updated_action.count }.by(1)
end .and change { counter.read(:create) }.by(1)
.and change { counter.read(:update) }.by(1)
it 'creates a single commit' do .and change { commit_count }.by(1)
commit_count = -> do
design_repository.expire_all_method_caches
design_repository.commit_count
end
expect { run_service }.to change { commit_count.call }.by(1)
end
it 'enqueues just one new version worker' do
expect(::DesignManagement::NewVersionWorker)
.to receive(:perform_async).once.with(Integer)
run_service
end end
end end
...@@ -262,45 +240,28 @@ RSpec.describe DesignManagement::SaveDesignsService do ...@@ -262,45 +240,28 @@ RSpec.describe DesignManagement::SaveDesignsService do
expect(response).to include(designs: have_attributes(size: 2), status: :success) expect(response).to include(designs: have_attributes(size: 2), status: :success)
end end
it 'creates 2 designs with a single version' do it 'has the correct side-effects', :request_store do
expect { run_service }.to change { issue.designs.count }.from(0).to(2)
expect(DesignManagement::Version.for_designs(issue.designs).size).to eq(1)
end
it 'increments the creation count by 2' do
counter = Gitlab::UsageDataCounters::DesignsCounter counter = Gitlab::UsageDataCounters::DesignsCounter
expect { run_service }.to change { counter.read(:create) }.by 2
end
it 'enqueues a new version worker' do
expect(::DesignManagement::NewVersionWorker)
.to receive(:perform_async).once.with(Integer)
run_service
end
it 'creates a single commit' do
commit_count = -> do
design_repository.expire_all_method_caches
design_repository.commit_count
end
expect { run_service }.to change { commit_count.call }.by(1)
end
it 'only does 5 gitaly calls', :request_store, :sidekiq_might_not_need_inline do
allow(::DesignManagement::NewVersionWorker).to receive(:perform_async).with(Integer)
service = described_class.new(project, user, issue: issue, files: files) service = described_class.new(project, user, issue: issue, files: files)
# Some unrelated calls that are usually cached or happen only once # Some unrelated calls that are usually cached or happen only once
service.__send__(:repository).create_if_not_exists # We expect:
service.__send__(:repository).has_visible_content? # - An exists?
# - a check for existing blobs
# - default branch
# - an after_commit callback on LfsObjectsProject
design_repository.create_if_not_exists
design_repository.has_visible_content?
request_count = -> { Gitlab::GitalyClient.get_request_count } expect(::DesignManagement::NewVersionWorker)
.to receive(:perform_async).once.with(Integer).and_return(nil)
# An exists?, a check for existing blobs, default branch, an after_commit expect { service.execute }
# callback on LfsObjectsProject .to change { issue.designs.count }.from(0).to(2)
expect { service.execute }.to change(&request_count).by(4) .and change { DesignManagement::Version.count }.by(1)
.and change { counter.read(:create) }.by(2)
.and change { Gitlab::GitalyClient.get_request_count }.by(3)
.and change { commit_count }.by(1)
end end
context 'when uploading too many files' do context 'when uploading too many files' do
...@@ -313,7 +274,7 @@ RSpec.describe DesignManagement::SaveDesignsService do ...@@ -313,7 +274,7 @@ RSpec.describe DesignManagement::SaveDesignsService do
end end
context 'when the user is not allowed to upload designs' do context 'when the user is not allowed to upload designs' do
let(:user) { create(:user) } let(:user) { build_stubbed(:user) }
it_behaves_like 'a service error' it_behaves_like 'a service error'
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