From 57bde0ce65caf2cbbb6a57f21435639cdaa06225 Mon Sep 17 00:00:00 2001 From: Yorick Peterse <yorickpeterse@gmail.com> Date: Thu, 24 Mar 2016 15:53:38 +0100 Subject: [PATCH] Cache Banzai projects/objects using RequestStore This was originally suggested by @ayufan and modified to be a bit cleaner and use RequestStore instead of a regular Hash. By caching the output of the two methods involved the number of queries is reduced significantly. For example, for an issue with 200 notes (of which 100 reference a number of merge requests) this cuts down the amount of queries from around 6300 to around 3300. --- Gemfile | 2 +- Gemfile.lock | 4 +- .../filter/abstract_reference_filter.rb | 71 +++++++++++++++++-- 3 files changed, 70 insertions(+), 7 deletions(-) diff --git a/Gemfile b/Gemfile index 006e53e0c100c..6327227282ae0 100644 --- a/Gemfile +++ b/Gemfile @@ -214,7 +214,7 @@ gem 'jquery-rails', '~> 4.0.0' gem 'jquery-scrollto-rails', '~> 1.4.3' gem 'jquery-ui-rails', '~> 5.0.0' gem 'raphael-rails', '~> 2.1.2' -gem 'request_store', '~> 1.2.0' +gem 'request_store', '~> 1.3.0' gem 'select2-rails', '~> 3.5.9' gem 'virtus', '~> 1.0.1' gem 'net-ssh', '~> 3.0.1' diff --git a/Gemfile.lock b/Gemfile.lock index bd41cc84198e1..229089f431d13 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -652,7 +652,7 @@ GEM redis-store (~> 1.1.0) redis-store (1.1.7) redis (>= 2.2) - request_store (1.2.1) + request_store (1.3.0) rerun (0.11.0) listen (~> 3.0) responders (2.1.1) @@ -1011,7 +1011,7 @@ DEPENDENCIES redcarpet (~> 3.3.3) redis-namespace redis-rails (~> 4.0.0) - request_store (~> 1.2.0) + request_store (~> 1.3.0) rerun (~> 0.11.0) responders (~> 2.0) rouge (~> 1.10.1) diff --git a/lib/banzai/filter/abstract_reference_filter.rb b/lib/banzai/filter/abstract_reference_filter.rb index 34c38913474d1..41fd4be76acf9 100644 --- a/lib/banzai/filter/abstract_reference_filter.rb +++ b/lib/banzai/filter/abstract_reference_filter.rb @@ -62,11 +62,53 @@ def find_object(project, id) # Example: project.merge_requests.find end + def find_object_cached(project, id) + if RequestStore.active? + cache = find_objects_cache[object_class][project.id] + + if cache.key?(id) + cache[id] + else + cache[id] = find_object(project, id) + end + else + find_object(project, id) + end + end + + def project_from_ref_cache(ref) + if RequestStore.active? + cache = project_refs_cache + + if cache.key?(ref) + cache[ref] + else + cache[ref] = project_from_ref(ref) + end + else + project_from_ref(ref) + end + end + def url_for_object(object, project) # Implement in child class # Example: project_merge_request_url end + def url_for_object_cached(object, project) + if RequestStore.active? + cache = url_for_object_cache[object_class][project.id] + + if cache.key?(object) + cache[object] + else + cache[object] = url_for_object(object, project) + end + else + url_for_object(object, project) + end + end + def call if object_class.reference_pattern # `#123` @@ -109,9 +151,9 @@ def call # have `gfm` and `gfm-OBJECT_NAME` class names attached for styling. def object_link_filter(text, pattern, link_text: nil) references_in(text, pattern) do |match, id, project_ref, matches| - project = project_from_ref(project_ref) + project = project_from_ref_cache(project_ref) - if project && object = find_object(project, id) + if project && object = find_object_cached(project, id) title = object_link_title(object) klass = reference_class(object_sym) @@ -121,8 +163,11 @@ def object_link_filter(text, pattern, link_text: nil) object_sym => object.id ) - url = matches[:url] if matches.names.include?("url") - url ||= url_for_object(object, project) + if matches.names.include?("url") && matches[:url] + url = matches[:url] + else + url = url_for_object_cached(object, project) + end text = link_text || object_link_text(object, matches) @@ -157,6 +202,24 @@ def object_link_text(object, matches) text end + + private + + def project_refs_cache + RequestStore[:banzai_project_refs] ||= {} + end + + def find_objects_cache + RequestStore[:banzai_find_objects_cache] ||= Hash.new do |hash, key| + hash[key] = Hash.new { |h, k| h[k] = {} } + end + end + + def url_for_object_cache + RequestStore[:banzai_url_for_object] ||= Hash.new do |hash, key| + hash[key] = Hash.new { |h, k| h[k] = {} } + end + end end end end -- GitLab