summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNick Thomas <nick@gitlab.com>2018-10-12 14:42:35 +0100
committerNick Thomas <nick@gitlab.com>2018-10-17 16:24:36 +0100
commit0669127a328af171850afb01deef2a07eb89a6a6 (patch)
tree8a25b2e738eb131f89b21ce1cb93442ae89b6299
parente347170cc59dfa1e48de451f7c48ccb65d3e581a (diff)
downloadgitlab-ce-0669127a328af171850afb01deef2a07eb89a6a6.tar.gz
Use cached readme blobs where appropriate
GitLab keeps a cache of the rendered HTML for a repository's README as stored in the HEAD branch. However, it was not used in all circumstances. In particular, the new blob viewer framework bypassed this cache entirely. This MR ensures a ::ReadmeBlob is returned instead of a ::Blob when asking a repository for an individual blob, if the commit and path match the readme for HEAD. This makes the cached HTML available to consumers, including the blob viewer. The ReadmeBlob is a simple delegator to the Blob, so should be compatible in all cases. Adding the rendered_markdown method is the only additional behaviour it contains.
-rw-r--r--app/models/repository.rb19
-rw-r--r--app/models/tree.rb51
-rw-r--r--changelogs/unreleased/49591-use-cached-readme-blobs.yml5
-rw-r--r--spec/models/project_spec.rb14
-rw-r--r--spec/models/repository_spec.rb28
5 files changed, 79 insertions, 38 deletions
diff --git a/app/models/repository.rb b/app/models/repository.rb
index 6ce480c32c4..37a1dd64052 100644
--- a/app/models/repository.rb
+++ b/app/models/repository.rb
@@ -487,7 +487,20 @@ class Repository
end
def blob_at(sha, path)
- Blob.decorate(raw_repository.blob_at(sha, path), project)
+ blob = Blob.decorate(raw_repository.blob_at(sha, path), project)
+
+ # Don't attempt to return a special result if there is no blob at all
+ return unless blob
+
+ # Don't attempt to return a special result unless we're looking at HEAD
+ return blob unless head_commit&.sha == sha
+
+ case path
+ when head_tree&.readme_path
+ ReadmeBlob.new(blob, self)
+ else
+ blob
+ end
rescue Gitlab::Git::Repository::NoRepository
nil
end
@@ -569,9 +582,7 @@ class Repository
cache_method :merge_request_template_names, fallback: []
def readme
- if readme = tree(:head)&.readme
- ReadmeBlob.new(readme, self)
- end
+ head_tree&.readme
end
def rendered_readme
diff --git a/app/models/tree.rb b/app/models/tree.rb
index 3641c33254c..cd385872171 100644
--- a/app/models/tree.rb
+++ b/app/models/tree.rb
@@ -2,6 +2,7 @@
class Tree
include Gitlab::MarkupHelper
+ include Gitlab::Utils::StrongMemoize
attr_accessor :repository, :sha, :path, :entries
@@ -16,32 +17,36 @@ class Tree
@entries = Gitlab::Git::Tree.where(git_repo, @sha, @path, recursive)
end
- def readme
- return @readme if defined?(@readme)
-
- available_readmes = blobs.select do |blob|
- Gitlab::FileDetector.type_of(blob.name) == :readme
- end
-
- previewable_readmes = available_readmes.select do |blob|
- previewable?(blob.name)
- end
-
- plain_readmes = available_readmes.select do |blob|
- plain?(blob.name)
+ def readme_path
+ strong_memoize(:readme_path) do
+ available_readmes = blobs.select do |blob|
+ Gitlab::FileDetector.type_of(blob.name) == :readme
+ end
+
+ previewable_readmes = available_readmes.select do |blob|
+ previewable?(blob.name)
+ end
+
+ plain_readmes = available_readmes.select do |blob|
+ plain?(blob.name)
+ end
+
+ # Prioritize previewable over plain readmes
+ entry = previewable_readmes.first || plain_readmes.first
+ next nil unless entry
+
+ if path == '/'
+ entry.name
+ else
+ File.join(path, entry.name)
+ end
end
+ end
- # Prioritize previewable over plain readmes
- readme_tree = previewable_readmes.first || plain_readmes.first
-
- # Return if we can't preview any of them
- if readme_tree.nil?
- return @readme = nil
+ def readme
+ strong_memoize(:readme) do
+ repository.blob_at(sha, readme_path) if readme_path
end
-
- readme_path = path == '/' ? readme_tree.name : File.join(path, readme_tree.name)
-
- @readme = repository.blob_at(sha, readme_path)
end
def trees
diff --git a/changelogs/unreleased/49591-use-cached-readme-blobs.yml b/changelogs/unreleased/49591-use-cached-readme-blobs.yml
new file mode 100644
index 00000000000..59098d2733a
--- /dev/null
+++ b/changelogs/unreleased/49591-use-cached-readme-blobs.yml
@@ -0,0 +1,5 @@
+---
+title: Use cached readme contents when available
+merge_request: 22325
+author:
+type: performance
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index a807c336165..88f70a88210 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -527,28 +527,28 @@ describe Project do
end
describe "#readme_url" do
- let(:project) { create(:project, :repository, path: "somewhere") }
-
context 'with a non-existing repository' do
- it 'returns nil' do
- allow(project.repository).to receive(:tree).with(:head).and_return(nil)
+ let(:project) { create(:project) }
+ it 'returns nil' do
expect(project.readme_url).to be_nil
end
end
context 'with an existing repository' do
context 'when no README exists' do
- it 'returns nil' do
- allow_any_instance_of(Tree).to receive(:readme).and_return(nil)
+ let(:project) { create(:project, :empty_repo) }
+ it 'returns nil' do
expect(project.readme_url).to be_nil
end
end
context 'when a README exists' do
+ let(:project) { create(:project, :repository) }
+
it 'returns the README' do
- expect(project.readme_url).to eql("#{Gitlab.config.gitlab.url}/#{project.namespace.full_path}/somewhere/blob/master/README.md")
+ expect(project.readme_url).to eq("#{project.web_url}/blob/master/README.md")
end
end
end
diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb
index fb16a321e4b..799a60ac62f 100644
--- a/spec/models/repository_spec.rb
+++ b/spec/models/repository_spec.rb
@@ -461,6 +461,24 @@ describe Repository do
it { is_expected.to be_nil }
end
+
+ context 'regular blob' do
+ subject { repository.blob_at(repository.head_commit.sha, '.gitignore') }
+
+ it { is_expected.to be_an_instance_of(::Blob) }
+ end
+
+ context 'readme blob on HEAD' do
+ subject { repository.blob_at(repository.head_commit.sha, 'README.md') }
+
+ it { is_expected.to be_an_instance_of(::ReadmeBlob) }
+ end
+
+ context 'readme blob not on HEAD' do
+ subject { repository.blob_at(repository.find_branch('feature').target, 'README.md') }
+
+ it { is_expected.to be_an_instance_of(::Blob) }
+ end
end
describe '#merged_to_root_ref?' do
@@ -1922,23 +1940,25 @@ describe Repository do
describe '#readme', :use_clean_rails_memory_store_caching do
context 'with a non-existing repository' do
- it 'returns nil' do
- allow(repository).to receive(:tree).with(:head).and_return(nil)
+ let(:project) { create(:project) }
+ it 'returns nil' do
expect(repository.readme).to be_nil
end
end
context 'with an existing repository' do
context 'when no README exists' do
- it 'returns nil' do
- allow_any_instance_of(Tree).to receive(:readme).and_return(nil)
+ let(:project) { create(:project, :empty_repo) }
+ it 'returns nil' do
expect(repository.readme).to be_nil
end
end
context 'when a README exists' do
+ let(:project) { create(:project, :repository) }
+
it 'returns the README' do
expect(repository.readme).to be_an_instance_of(ReadmeBlob)
end