Commit 80ebc43a authored by Oswaldo Ferreira's avatar Oswaldo Ferreira

Fix excessive Redis calls for new HSET diffs cache

We were doing multiple hmget calls when reading
the cache. This neutralizes the problem by using
a memoized cache.
parent 740dd848
...@@ -3,6 +3,8 @@ ...@@ -3,6 +3,8 @@
module Gitlab module Gitlab
module Diff module Diff
class HighlightCache class HighlightCache
include Gitlab::Utils::StrongMemoize
EXPIRATION = 1.week EXPIRATION = 1.week
VERSION = 1 VERSION = 1
...@@ -30,12 +32,11 @@ module Gitlab ...@@ -30,12 +32,11 @@ module Gitlab
# IO generated by N+1's (1 writing for each highlighted line or file). # IO generated by N+1's (1 writing for each highlighted line or file).
# #
def write_if_empty def write_if_empty
return if uncached_files.empty? return if cacheable_files.empty?
new_cache_content = {} new_cache_content = {}
uncached_files.each do |diff_file|
next unless cacheable?(diff_file)
cacheable_files.each do |diff_file|
new_cache_content[diff_file.file_path] = diff_file.highlighted_diff_lines.map(&:to_hash) new_cache_content[diff_file.file_path] = diff_file.highlighted_diff_lines.map(&:to_hash)
end end
...@@ -49,7 +50,9 @@ module Gitlab ...@@ -49,7 +50,9 @@ module Gitlab
end end
def key def key
@redis_key ||= ['highlighted-diff-files', diffable.cache_key, VERSION, diff_options].join(":") strong_memoize(:redis_key) do
['highlighted-diff-files', diffable.cache_key, VERSION, diff_options].join(":")
end
end end
private private
...@@ -60,13 +63,17 @@ module Gitlab ...@@ -60,13 +63,17 @@ module Gitlab
# See https://gitlab.com/gitlab-org/gitlab/issues/38008 # See https://gitlab.com/gitlab-org/gitlab/issues/38008
# #
def deprecated_cache def deprecated_cache
@deprecated_cache ||= Gitlab::Diff::DeprecatedHighlightCache.new(@diff_collection) strong_memoize(:deprecated_cache) do
Gitlab::Diff::DeprecatedHighlightCache.new(@diff_collection)
end
end end
def uncached_files def cacheable_files
strong_memoize(:cacheable_files) do
diff_files = @diff_collection.diff_files diff_files = @diff_collection.diff_files
diff_files.select { |file| read_cache[file.file_path].nil? } diff_files.select { |file| cacheable?(file) && read_file(file).nil? }
end
end end
# Given a hash of: # Given a hash of:
...@@ -95,13 +102,20 @@ module Gitlab ...@@ -95,13 +102,20 @@ module Gitlab
end end
end end
# Subsequent read_file calls would need the latest cache.
#
clear_memoization(:cached_content)
clear_memoization(:cacheable_files)
# Clean up any deprecated hash entries # Clean up any deprecated hash entries
# #
deprecated_cache.clear deprecated_cache.clear
end end
def file_paths def file_paths
@file_paths ||= @diff_collection.diffs.collect(&:file_path) strong_memoize(:file_paths) do
@diff_collection.diffs.collect(&:file_path)
end
end end
def read_file(diff_file) def read_file(diff_file)
...@@ -109,7 +123,7 @@ module Gitlab ...@@ -109,7 +123,7 @@ module Gitlab
end end
def cached_content def cached_content
@cached_content ||= read_cache strong_memoize(:cached_content) { read_cache }
end end
def read_cache def read_cache
......
...@@ -72,13 +72,22 @@ describe Gitlab::Diff::HighlightCache, :clean_gitlab_redis_cache do ...@@ -72,13 +72,22 @@ describe Gitlab::Diff::HighlightCache, :clean_gitlab_redis_cache do
describe '#write_if_empty' do describe '#write_if_empty' do
it 'filters the key/value list of entries to be caches for each invocation' do it 'filters the key/value list of entries to be caches for each invocation' do
paths = merge_request.diffs.diff_files.select(&:text?).map(&:file_path)
expect(cache).to receive(:write_to_redis_hash) expect(cache).to receive(:write_to_redis_hash)
.once.with(hash_including(".gitignore")).and_call_original .with(hash_including(*paths))
expect(cache).to receive(:write_to_redis_hash).once.with({}).and_call_original .once
.and_call_original
2.times { cache.write_if_empty } 2.times { cache.write_if_empty }
end end
it 'reads from cache once' do
expect(cache).to receive(:read_cache).once.and_call_original
cache.write_if_empty
end
context 'different diff_collections for the same diffable' do context 'different diff_collections for the same diffable' do
before do before do
cache.write_if_empty cache.write_if_empty
......
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