Commit f6eb1edc authored by Stan Hu's avatar Stan Hu

Skip LFS updates in mirrors if repository has not changed

The GetAllLFSPointers RPC is expensive and was being called for every
mirror. If the repository has not changed, we can assume that the LFS
pointers also did not change. We can skip calling this RPC if there are
no updates.

Since we don't actually get a return code if branches or tags were
updated, we use the repository checksum as a cheaper mechanism (which
XORs the ref contents) to determine whether we run the LFS scan.

Closes https://gitlab.com/gitlab-org/gitlab/issues/38256
parent 262bcdd6
---
title: Skip updating LFS objects in mirror updates if repository has not changed
merge_request: 21744
author:
type: performance
...@@ -24,12 +24,17 @@ module Projects ...@@ -24,12 +24,17 @@ module Projects
return error("The mirror user is not allowed to push code to all branches on this project.") return error("The mirror user is not allowed to push code to all branches on this project.")
end end
checksum_before = project.repository.checksum
update_tags do update_tags do
project.fetch_mirror(forced: true) project.fetch_mirror(forced: true)
end end
update_branches update_branches
update_lfs_objects
# Updating LFS objects is expensive since it requires scanning for blobs with pointers.
# Let's skip this if the repository hasn't changed.
update_lfs_objects if project.repository.checksum != checksum_before
success success
rescue Gitlab::Shell::Error, Gitlab::Git::BaseError, UpdateError => e rescue Gitlab::Shell::Error, Gitlab::Git::BaseError, UpdateError => e
......
...@@ -325,60 +325,74 @@ describe Projects::UpdateMirrorService do ...@@ -325,60 +325,74 @@ describe Projects::UpdateMirrorService do
end end
end end
context 'updating Lfs objects' do context 'updating LFS objects' do
before do context 'when repository does not change' do
stub_fetch_mirror(project) before do
end allow(project).to receive(:lfs_enabled?).and_return(true)
end
context 'when Lfs is disabled in the project' do it 'does not attempt to update LFS objects' do
it 'does not update Lfs objects' do expect(Projects::LfsPointers::LfsImportService).not_to receive(:new)
allow(project).to receive(:lfs_enabled?).and_return(false)
expect(Projects::LfsPointers::LfsObjectDownloadListService).not_to receive(:new)
service.execute service.execute
end end
end end
context 'when Lfs is enabled in the project' do context 'when repository changes' do
before do before do
allow(project).to receive(:lfs_enabled?).and_return(true) stub_fetch_mirror(project)
end end
it 'updates Lfs objects' do context 'when Lfs is disabled in the project' do
expect(Projects::LfsPointers::LfsImportService).to receive(:new).and_call_original it 'does not update LFS objects' do
expect_any_instance_of(Projects::LfsPointers::LfsObjectDownloadListService).to receive(:execute).and_return({}) allow(project).to receive(:lfs_enabled?).and_return(false)
expect(Projects::LfsPointers::LfsObjectDownloadListService).not_to receive(:new)
service.execute service.execute
end
end end
context 'when Lfs import fails' do context 'when Lfs is enabled in the project' do
let(:error_message) { 'error_message' }
before do before do
expect_any_instance_of(Projects::LfsPointers::LfsImportService).to receive(:execute).and_return(status: :error, message: error_message) allow(project).to receive(:lfs_enabled?).and_return(true)
end end
# Uncomment once https://gitlab.com/gitlab-org/gitlab-foss/issues/61834 is closed it 'updates LFS objects' do
# it 'fails mirror operation' do expect(Projects::LfsPointers::LfsImportService).to receive(:new).and_call_original
# expect_any_instance_of(Projects::LfsPointers::LfsImportService).to receive(:execute).and_return(status: :error, message: 'error message') expect_any_instance_of(Projects::LfsPointers::LfsObjectDownloadListService).to receive(:execute).and_return({})
# result = subject.execute service.execute
end
context 'when Lfs import fails' do
let(:error_message) { 'error_message' }
# expect(result[:status]).to eq :error before do
# expect(result[:message]).to eq 'error message' expect_any_instance_of(Projects::LfsPointers::LfsImportService).to receive(:execute).and_return(status: :error, message: error_message)
# end end
# Remove once https://gitlab.com/gitlab-org/gitlab-foss/issues/61834 is closed # Uncomment once https://gitlab.com/gitlab-org/gitlab-foss/issues/61834 is closed
it 'does not fail mirror operation' do # it 'fails mirror operation' do
result = subject.execute # expect_any_instance_of(Projects::LfsPointers::LfsImportService).to receive(:execute).and_return(status: :error, message: 'error message')
expect(result[:status]).to eq :success # result = subject.execute
end
it 'logs the error' do # expect(result[:status]).to eq :error
expect_any_instance_of(Gitlab::UpdateMirrorServiceJsonLogger).to receive(:error).with(hash_including(error_message: error_message)) # expect(result[:message]).to eq 'error message'
# end
subject.execute # Remove once https://gitlab.com/gitlab-org/gitlab-foss/issues/61834 is closed
it 'does not fail mirror operation' do
result = subject.execute
expect(result[:status]).to eq :success
end
it 'logs the error' do
expect_any_instance_of(Gitlab::UpdateMirrorServiceJsonLogger).to receive(:error).with(hash_including(error_message: error_message))
subject.execute
end
end end
end end
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