From e658f9603c99ca6a8ef4c0292b2cdab2916fb09e Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Tue, 13 Aug 2019 09:04:30 -0700 Subject: Only expire tag cache once per push Previously each tag in a push would invoke the Gitaly `FindAllTags` RPC since the tag cache would be invalidated with every tag. We can eliminate those extraneous calls by expiring the tag cache once in `PostReceive` and taking advantage of the cached tags. Relates to https://gitlab.com/gitlab-org/gitlab-ce/issues/65795 --- app/models/repository.rb | 14 ++++--- app/workers/post_receive.rb | 2 + .../sh-only-flush-tags-once-per-push.yml | 5 +++ lib/gitlab/git_post_receive.rb | 6 +++ spec/lib/gitlab/git_post_receive_spec.rb | 43 ++++++++++++++++++++++ spec/models/repository_spec.rb | 13 ++++++- spec/services/git/tag_push_service_spec.rb | 4 +- spec/workers/post_receive_spec.rb | 30 +++++++++++++-- 8 files changed, 105 insertions(+), 12 deletions(-) create mode 100644 changelogs/unreleased/sh-only-flush-tags-once-per-push.yml diff --git a/app/models/repository.rb b/app/models/repository.rb index 58abfaef801..9d45a12fa6e 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -418,25 +418,29 @@ class Repository end # Runs code before pushing (= creating or removing) a tag. + # + # Note that this doesn't expire the tags. You may need to call + # expire_caches_for_tags or expire_tags_cache. def before_push_tag + repository_event(:push_tag) + end + + def expire_caches_for_tags expire_statistics_caches expire_emptiness_caches expire_tags_cache - - repository_event(:push_tag) end # Runs code before removing a tag. def before_remove_tag - expire_tags_cache - expire_statistics_caches + expire_caches_for_tags repository_event(:remove_tag) end # Runs code after removing a tag. def after_remove_tag - expire_tags_cache + expire_caches_for_tags end # Runs code after the HEAD of a repository is changed. diff --git a/app/workers/post_receive.rb b/app/workers/post_receive.rb index d8dfbc0faf7..675fa1a73ca 100644 --- a/app/workers/post_receive.rb +++ b/app/workers/post_receive.rb @@ -44,6 +44,8 @@ class PostReceive # Expire the branches cache so we have updated data for this push post_received.project.repository.expire_branches_cache if post_received.includes_branches? + # We only need to expire tags once per push + post_received.project.repository.expire_caches_for_tags if post_received.includes_tags? post_received.enum_for(:changes_refs).with_index do |(oldrev, newrev, ref), index| service_klass = diff --git a/changelogs/unreleased/sh-only-flush-tags-once-per-push.yml b/changelogs/unreleased/sh-only-flush-tags-once-per-push.yml new file mode 100644 index 00000000000..502fc22ebbd --- /dev/null +++ b/changelogs/unreleased/sh-only-flush-tags-once-per-push.yml @@ -0,0 +1,5 @@ +--- +title: Only expire tag cache once per push +merge_request: 31641 +author: +type: performance diff --git a/lib/gitlab/git_post_receive.rb b/lib/gitlab/git_post_receive.rb index 6c7f23a673c..24d752b8a4b 100644 --- a/lib/gitlab/git_post_receive.rb +++ b/lib/gitlab/git_post_receive.rb @@ -33,6 +33,12 @@ module Gitlab end end + def includes_tags? + enum_for(:changes_refs).any? do |_oldrev, _newrev, ref| + Gitlab::Git.tag_ref?(ref) + end + end + private def deserialize_changes(changes) diff --git a/spec/lib/gitlab/git_post_receive_spec.rb b/spec/lib/gitlab/git_post_receive_spec.rb index 1911e954df9..4c20d945585 100644 --- a/spec/lib/gitlab/git_post_receive_spec.rb +++ b/spec/lib/gitlab/git_post_receive_spec.rb @@ -49,4 +49,47 @@ describe ::Gitlab::GitPostReceive do end end end + + describe '#includes_tags?' do + context 'with no tags' do + let(:changes) do + <<~EOF + 654321 210987 refs/notags/tag1 + 654322 210986 refs/heads/test1 + 654323 210985 refs/merge-requests/mr1 + EOF + end + + it 'returns false' do + expect(subject.includes_tags?).to be_falsey + end + end + + context 'with tags' do + let(:changes) do + <<~EOF + 654322 210986 refs/heads/test1 + 654321 210987 refs/tags/tag1 + 654323 210985 refs/merge-requests/mr1 + EOF + end + + it 'returns true' do + expect(subject.includes_tags?).to be_truthy + end + end + + context 'with malformed changes' do + let(:changes) do + <<~EOF + ref/tags/1 a + sometag refs/tags/2 + EOF + end + + it 'returns false' do + expect(subject.includes_tags?).to be_falsey + end + end + end end diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb index fa243876632..e68de2e73a8 100644 --- a/spec/models/repository_spec.rb +++ b/spec/models/repository_spec.rb @@ -1744,12 +1744,23 @@ describe Repository do end end - describe '#before_push_tag' do + describe '#expires_caches_for_tags' do it 'flushes the cache' do expect(repository).to receive(:expire_statistics_caches) expect(repository).to receive(:expire_emptiness_caches) expect(repository).to receive(:expire_tags_cache) + repository.expire_caches_for_tags + end + end + + describe '#before_push_tag' do + it 'logs an event' do + expect(repository).not_to receive(:expire_statistics_caches) + expect(repository).not_to receive(:expire_emptiness_caches) + expect(repository).not_to receive(:expire_tags_cache) + expect(repository).to receive(:repository_event).with(:push_tag) + repository.before_push_tag end end diff --git a/spec/services/git/tag_push_service_spec.rb b/spec/services/git/tag_push_service_spec.rb index 418952b52da..7e008637182 100644 --- a/spec/services/git/tag_push_service_spec.rb +++ b/spec/services/git/tag_push_service_spec.rb @@ -26,8 +26,8 @@ describe Git::TagPushService do subject end - it 'flushes the tags cache' do - expect(project.repository).to receive(:expire_tags_cache) + it 'does not flush the tags cache' do + expect(project.repository).not_to receive(:expire_tags_cache) subject end diff --git a/spec/workers/post_receive_spec.rb b/spec/workers/post_receive_spec.rb index 081d95d4d79..c586ee3006e 100644 --- a/spec/workers/post_receive_spec.rb +++ b/spec/workers/post_receive_spec.rb @@ -85,8 +85,20 @@ describe PostReceive do end end - context 'tags' do - let(:changes) { '123456 789012 refs/tags/tag' } + context "tags" do + let(:changes) do + <<~EOF + 654321 210987 refs/tags/tag1 + 654322 210986 refs/tags/tag2 + 654323 210985 refs/tags/tag3 + 654324 210984 refs/tags/tag4 + 654325 210983 refs/tags/tag5 + EOF + end + + before do + expect(Gitlab::GlRepository).to receive(:parse).and_return([project, Gitlab::GlRepository::PROJECT]) + end it 'does not expire branches cache' do expect(project.repository).not_to receive(:expire_branches_cache) @@ -94,8 +106,18 @@ describe PostReceive do described_class.new.perform(gl_repository, key_id, base64_changes) end - it 'calls Git::TagPushService' do - expect_next_instance_of(Git::TagPushService) do |service| + it "only invalidates tags once" do + expect(project.repository).to receive(:repository_event).exactly(5).times.with(:push_tag).and_call_original + expect(project.repository).to receive(:expire_caches_for_tags).once.and_call_original + expect(project.repository).to receive(:expire_tags_cache).once.and_call_original + + described_class.new.perform(gl_repository, key_id, base64_changes) + end + + it "calls Git::TagPushService" do + expect(Git::BranchPushService).not_to receive(:new) + + expect_any_instance_of(Git::TagPushService) do |service| expect(service).to receive(:execute).and_return(true) end -- cgit v1.2.1