summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlejandro Rodríguez <alejorro70@gmail.com>2016-06-20 19:20:08 -0400
committerAlejandro Rodríguez <alejorro70@gmail.com>2016-06-20 19:20:08 -0400
commitffcdc39d6d6334f0b7be1f78ae9daac1257fc857 (patch)
tree4094ace66f09623fff752af1658e18891fa8e59a
parent5804b6a0a28a20b1977ab89e04b15b85475b49e0 (diff)
downloadgitlab-ce-18590-banzai-filter-relativelinkfilter-is-slow.tar.gz
Optimize Banzai::Filter::RelativeLinkFilter18590-banzai-filter-relativelinkfilter-is-slow
A lot of git operations were being repeated, for example, to build a url you would ask if the path was a Tree, which would call a recursive routine in Gitlab::Git::Tree#where, then ask if the path was a Blob, which would call a recursive routine at Gitlab::Git::Blob#find, making reference to the same git objects several times. Now we call Rugged::Tree#path, which allows us to determine the type of the path in one pass. Some other minor improvement added, like saving commonly used references instead of calculating them each time.
-rw-r--r--CHANGELOG3
-rw-r--r--app/models/commit.rb24
-rw-r--r--lib/banzai/filter/relative_link_filter.rb47
-rw-r--r--spec/lib/banzai/filter/relative_link_filter_spec.rb7
-rw-r--r--spec/models/commit_spec.rb12
5 files changed, 53 insertions, 40 deletions
diff --git a/CHANGELOG b/CHANGELOG
index 3c80936468b..301ab0d5d36 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,5 +1,8 @@
Please view this file on the master branch, on stable branches it's out of date.
+v 8.10.0 (unrelease)
+ - Performance improvements on RelativeLinkFilter
+
v 8.9.0 (unreleased)
- Fix error when CI job variables key specified but not defined
- Fix pipeline status when there are no builds in pipeline
diff --git a/app/models/commit.rb b/app/models/commit.rb
index d69d518fadd..5350856103d 100644
--- a/app/models/commit.rb
+++ b/app/models/commit.rb
@@ -271,6 +271,30 @@ class Commit
merged_merge_request ? 'merge request' : 'commit'
end
+ # Get the type of the given path
+ #
+ # path - String path to check
+ #
+ # Examples:
+ #
+ # path_type('doc/README.md') # => :blob
+ # path_type('doc/logo.png') # => :raw
+ # path_type('doc/api') # => :tree
+ # path_type('not/found') # => :nil
+ #
+ # Returns a symbol
+ def path_type(path)
+ entry = @raw.tree.path(path)
+ if entry[:type] == :blob
+ blob = Gitlab::Git::Blob.new(name: entry[:name])
+ blob.image? ? :raw : :blob
+ else
+ entry[:type]
+ end
+ rescue Rugged::TreeError
+ nil
+ end
+
private
def repo_changes
diff --git a/lib/banzai/filter/relative_link_filter.rb b/lib/banzai/filter/relative_link_filter.rb
index ea21c7b041c..52c9b82fe7c 100644
--- a/lib/banzai/filter/relative_link_filter.rb
+++ b/lib/banzai/filter/relative_link_filter.rb
@@ -14,6 +14,8 @@ module Banzai
def call
return doc unless linkable_files?
+ @path_types = {}
+
doc.search('a:not(.gfm)').each do |el|
process_link_attr el.attribute('href')
end
@@ -87,7 +89,7 @@ module Banzai
return path unless request_path
parts = request_path.split('/')
- parts.pop if path_type(request_path) != 'tree'
+ parts.pop if path_type(request_path) != :tree
while path.start_with?('../')
parts.pop
@@ -98,45 +100,20 @@ module Banzai
end
def file_exists?(path)
- return false if path.nil?
- repository.blob_at(current_sha, path).present? ||
- repository.tree(current_sha, path).entries.any?
+ path.present? && path_type(path)
end
- # Get the type of the given path
- #
- # path - String path to check
- #
- # Examples:
- #
- # path_type('doc/README.md') # => 'blob'
- # path_type('doc/logo.png') # => 'raw'
- # path_type('doc/api') # => 'tree'
- #
- # Returns a String
def path_type(path)
- unescaped_path = Addressable::URI.unescape(path)
-
- if tree?(unescaped_path)
- 'tree'
- elsif image?(unescaped_path)
- 'raw'
- else
- 'blob'
- end
- end
+ @path_types[path] ||= begin
+ unescaped_path = Addressable::URI.unescape(path)
- def tree?(path)
- repository.tree(current_sha, path).entries.any?
- end
-
- def image?(path)
- repository.blob_at(current_sha, path).try(:image?)
+ current_commit.path_type(unescaped_path)
+ end
end
- def current_sha
- context[:commit].try(:id) ||
- ref ? repository.commit(ref).try(:sha) : repository.head_commit.sha
+ def current_commit
+ @current_commit ||= context[:commit] ||
+ ref ? repository.commit(ref) : repository.head_commit
end
def relative_url_root
@@ -148,7 +125,7 @@ module Banzai
end
def repository
- context[:project].try(:repository)
+ @repository ||= context[:project].try(:repository)
end
end
end
diff --git a/spec/lib/banzai/filter/relative_link_filter_spec.rb b/spec/lib/banzai/filter/relative_link_filter_spec.rb
index 0e6685f0ffb..14f159df0ed 100644
--- a/spec/lib/banzai/filter/relative_link_filter_spec.rb
+++ b/spec/lib/banzai/filter/relative_link_filter_spec.rb
@@ -132,11 +132,8 @@ describe Banzai::Filter::RelativeLinkFilter, lib: true do
path = 'files/images/한글.png'
escaped = Addressable::URI.escape(path)
- # Stub these methods so the file doesn't actually need to be in the repo
- allow_any_instance_of(described_class).
- to receive(:file_exists?).and_return(true)
- allow_any_instance_of(described_class).
- to receive(:image?).with(path).and_return(true)
+ # Stub this method so the file doesn't actually need to be in the repo
+ allow_any_instance_of(described_class).to receive(:path_type).and_return(:raw)
doc = filter(image(escaped))
expect(doc.at_css('img')['src']).to match '/raw/'
diff --git a/spec/models/commit_spec.rb b/spec/models/commit_spec.rb
index beca8708c9d..f4744569df0 100644
--- a/spec/models/commit_spec.rb
+++ b/spec/models/commit_spec.rb
@@ -207,4 +207,16 @@ eos
expect(commit.participants).to include(note1.author, note2.author)
end
end
+
+ describe '#path_type' do
+ it 'returns the type of the object at the given path' do
+ expect(commit.path_type('files/html')).to be(:tree)
+ expect(commit.path_type('files/images/logo-black.png')).to be(:raw)
+ expect(commit.path_type('files/js/application.js')).to be(:blob)
+ end
+
+ it "returns nil if the path doesn't exists" do
+ expect(commit.path_type('this/path/doesnt/exist')).to be_nil
+ end
+ end
end