Commit d8602b42 authored by Kerri Miller's avatar Kerri Miller Committed by Shinya Maeda

Check for default_max_patch_size changes

If the value is updated by an admin, we may _want_ to display the
highlighted lines on a page, but will be unable to deliver them as the
cached version might have been set as empty ([]) when the file
PREVIOUSLY was considered "too large". When one of these situations is
detected - the file is unreadable from the cache, and would've violated
the default value but NOT violate the current value, go ahead and
manually update the cache with it.
parent 4a8e4ea5
...@@ -20,10 +20,23 @@ module Gitlab ...@@ -20,10 +20,23 @@ module Gitlab
# - Assigns DiffFile#highlighted_diff_lines for cached files # - Assigns DiffFile#highlighted_diff_lines for cached files
# #
def decorate(diff_file) def decorate(diff_file)
if content = read_file(diff_file) content = read_file(diff_file)
diff_file.highlighted_diff_lines = content.map do |line|
Gitlab::Diff::Line.safe_init_from_hash(line) return [] unless content
end
if content.empty? && recache_due_to_size?(diff_file)
# If the file is missing from the cache and there's reason to believe
# it is uncached due to a size issue around changing the values for
# max patch size, manually populate the hash and then set the value.
#
new_cache_content = {}
new_cache_content[diff_file.file_path] = diff_file.highlighted_diff_lines.map(&:to_hash)
write_to_redis_hash(new_cache_content)
set_highlighted_diff_lines(diff_file, read_file(diff_file))
else
set_highlighted_diff_lines(diff_file, content)
end end
end end
...@@ -58,6 +71,28 @@ module Gitlab ...@@ -58,6 +71,28 @@ module Gitlab
private private
def set_highlighted_diff_lines(diff_file, content)
diff_file.highlighted_diff_lines = content.map do |line|
Gitlab::Diff::Line.safe_init_from_hash(line)
end
end
def recache_due_to_size?(diff_file)
diff_file_class = diff_file.diff.class
current_patch_safe_limit_bytes = diff_file_class.patch_safe_limit_bytes
default_patch_safe_limit_bytes = diff_file_class.patch_safe_limit_bytes(diff_file_class::DEFAULT_MAX_PATCH_BYTES)
# If the diff is >= than the default limit, but less than the current
# limit, it is likely uncached due to having hit the default limit,
# making it eligible for recalculating.
#
diff_file.diff.diff_bytesize.between?(
default_patch_safe_limit_bytes,
current_patch_safe_limit_bytes
)
end
def cacheable_files def cacheable_files
strong_memoize(:cacheable_files) do strong_memoize(:cacheable_files) do
diff_files.select { |file| cacheable?(file) && read_file(file).nil? } diff_files.select { |file| cacheable?(file) && read_file(file).nil? }
......
...@@ -120,8 +120,8 @@ module Gitlab ...@@ -120,8 +120,8 @@ module Gitlab
# default. # default.
# #
# Patches surpassing this limit should still be persisted in the database. # Patches surpassing this limit should still be persisted in the database.
def patch_safe_limit_bytes def patch_safe_limit_bytes(limit = patch_hard_limit_bytes)
patch_hard_limit_bytes / 10 limit / 10
end end
# Returns the limit for a single diff file (patch). # Returns the limit for a single diff file (patch).
...@@ -174,9 +174,13 @@ module Gitlab ...@@ -174,9 +174,13 @@ module Gitlab
@line_count ||= Util.count_lines(@diff) @line_count ||= Util.count_lines(@diff)
end end
def diff_bytesize
@diff_bytesize ||= @diff.bytesize
end
def too_large? def too_large?
if @too_large.nil? if @too_large.nil?
@too_large = @diff.bytesize >= self.class.patch_hard_limit_bytes @too_large = diff_bytesize >= self.class.patch_hard_limit_bytes
else else
@too_large @too_large
end end
...@@ -194,7 +198,7 @@ module Gitlab ...@@ -194,7 +198,7 @@ module Gitlab
def collapsed? def collapsed?
return @collapsed if defined?(@collapsed) return @collapsed if defined?(@collapsed)
@collapsed = !expanded && @diff.bytesize >= self.class.patch_safe_limit_bytes @collapsed = !expanded && diff_bytesize >= self.class.patch_safe_limit_bytes
end end
def collapse! def collapse!
......
...@@ -43,7 +43,8 @@ RSpec.describe Gitlab::Diff::HighlightCache, :clean_gitlab_redis_cache do ...@@ -43,7 +43,8 @@ RSpec.describe Gitlab::Diff::HighlightCache, :clean_gitlab_redis_cache do
describe '#decorate' do describe '#decorate' do
# Manually creates a Diff::File object to avoid triggering the cache on # Manually creates a Diff::File object to avoid triggering the cache on
# the FileCollection::MergeRequestDiff # the FileCollection::MergeRequestDiff
#
let(:diff_file) do let(:diff_file) do
diffs = merge_request.diffs diffs = merge_request.diffs
raw_diff = diffs.diffable.raw_diffs(diffs.diff_options.merge(paths: ['CHANGELOG'])).first raw_diff = diffs.diffable.raw_diffs(diffs.diff_options.merge(paths: ['CHANGELOG'])).first
...@@ -73,6 +74,37 @@ RSpec.describe Gitlab::Diff::HighlightCache, :clean_gitlab_redis_cache do ...@@ -73,6 +74,37 @@ RSpec.describe Gitlab::Diff::HighlightCache, :clean_gitlab_redis_cache do
expect(rich_texts).to all(be_html_safe) expect(rich_texts).to all(be_html_safe)
end end
context "when diff_file is uncached due to default_max_patch_bytes change" do
before do
expect(cache).to receive(:read_file).at_least(:once).and_return([])
# Stub out the application's default and current patch size limits. We
# want them to be different, and the diff file to be sized between
# the 2 values.
#
diff_file_size_kb = (diff_file.diff.diff.bytesize * 10)
stub_const("#{diff_file.diff.class}::DEFAULT_MAX_PATCH_BYTES", diff_file_size_kb - 1 )
expect(diff_file.diff.class).to receive(:patch_safe_limit_bytes).and_return(diff_file_size_kb + 1)
expect(diff_file.diff.class)
.to receive(:patch_safe_limit_bytes)
.with(diff_file.diff.class::DEFAULT_MAX_PATCH_BYTES)
.and_call_original
end
it "manually writes highlighted lines to the cache" do
expect(cache).to receive(:write_to_redis_hash).and_call_original
cache.decorate(diff_file)
end
it "assigns highlighted diff lines to the DiffFile" do
expect(diff_file.highlighted_diff_lines.size).to be > 5
cache.decorate(diff_file)
end
end
end end
shared_examples 'caches missing entries' do shared_examples 'caches missing entries' do
......
...@@ -284,13 +284,21 @@ EOT ...@@ -284,13 +284,21 @@ EOT
end end
describe '#line_count' do describe '#line_count' do
it 'returns the correct number of lines' do let(:diff) { described_class.new(gitaly_diff) }
diff = described_class.new(gitaly_diff)
it 'returns the correct number of lines' do
expect(diff.line_count).to eq(7) expect(diff.line_count).to eq(7)
end end
end end
describe "#diff_bytesize" do
let(:diff) { described_class.new(gitaly_diff) }
it "returns the size of the diff in bytes" do
expect(diff.diff_bytesize).to eq(diff.diff.bytesize)
end
end
describe '#too_large?' do describe '#too_large?' do
it 'returns true for a diff that is too large' do it 'returns true for a diff that is too large' do
diff = described_class.new(diff: 'a' * 204800) diff = described_class.new(diff: 'a' * 204800)
......
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