Skip to content
Snippets Groups Projects
Commit ff26ea81 authored by Francisco Javier López's avatar Francisco Javier López Committed by Douwe Maan
Browse files

Resolve "Performance issues when loading large number of wiki pages"

parent 88d29775
No related branches found
No related tags found
No related merge requests found
Loading
Loading
@@ -74,7 +74,11 @@ class Projects::WikisController < Projects::ApplicationController
def history
@page = @project_wiki.find_page(params[:id])
 
unless @page
if @page
@page_versions = Kaminari.paginate_array(@page.versions(page: params[:page]),
total_count: @page.count_versions)
.page(params[:page])
else
redirect_to(
project_wiki_path(@project, :home),
notice: "Page not found"
Loading
Loading
@@ -101,7 +105,7 @@ class Projects::WikisController < Projects::ApplicationController
 
# Call #wiki to make sure the Wiki Repo is initialized
@project_wiki.wiki
@sidebar_wiki_entries = WikiPage.group_by_directory(@project_wiki.pages.first(15))
@sidebar_wiki_entries = WikiPage.group_by_directory(@project_wiki.pages(limit: 15))
rescue ProjectWiki::CouldNotCreateWikiError
flash[:notice] = "Could not create Wiki Repository at this time. Please try again later."
redirect_to project_path(@project)
Loading
Loading
Loading
Loading
@@ -76,8 +76,8 @@ class ProjectWiki
 
# Returns an Array of Gitlab WikiPage instances or an
# empty Array if this Wiki has no pages.
def pages
wiki.pages.map { |page| WikiPage.new(self, page, true) }
def pages(limit: nil)
wiki.pages(limit: limit).map { |page| WikiPage.new(self, page, true) }
end
 
# Finds a page within the repository based on a tile
Loading
Loading
Loading
Loading
@@ -127,19 +127,24 @@ class WikiPage
@version ||= @page.version
end
 
# Returns an array of Gitlab Commit instances.
def versions
def versions(options = {})
return [] unless persisted?
 
wiki.wiki.page_versions(@page.path)
wiki.wiki.page_versions(@page.path, options)
end
 
def commit
versions.first
def count_versions
return [] unless persisted?
wiki.wiki.count_page_versions(@page.path)
end
def last_version
@last_version ||= versions(limit: 1).first
end
 
def last_commit_sha
commit&.sha
last_version&.sha
end
 
# Returns the Date that this latest version was
Loading
Loading
@@ -151,7 +156,7 @@ class WikiPage
# Returns boolean True or False if this instance
# is an old version of the page.
def historical?
@page.historical? && versions.first.sha != version.sha
@page.historical? && last_version.sha != version.sha
end
 
# Returns boolean True or False if this instance
Loading
Loading
Loading
Loading
@@ -2,4 +2,4 @@
= link_to wiki_page.title, project_wiki_path(@project, wiki_page)
%small (#{wiki_page.format})
.pull-right
%small= (s_("Last edited %{date}") % { date: time_ago_with_tooltip(wiki_page.commit.authored_date) }).html_safe
%small= (s_("Last edited %{date}") % { date: time_ago_with_tooltip(wiki_page.last_version.authored_date) }).html_safe
Loading
Loading
@@ -21,7 +21,7 @@
%th= _("Last updated")
%th= _("Format")
%tbody
- @page.versions.each_with_index do |version, index|
- @page_versions.each_with_index do |version, index|
- commit = version
%tr
%td
Loading
Loading
@@ -37,5 +37,6 @@
%td
%strong
= version.format
= paginate @page_versions, theme: 'gitlab'
 
= render 'sidebar'
Loading
Loading
@@ -11,8 +11,8 @@
.nav-text
%h2.wiki-page-title= @page.title.capitalize
%span.wiki-last-edit-by
= (_("Last edited by %{name}") % { name: "<strong>#{@page.commit.author_name}</strong>" }).html_safe
#{time_ago_with_tooltip(@page.commit.authored_date)}
= (_("Last edited by %{name}") % { name: "<strong>#{@page.last_version.author_name}</strong>" }).html_safe
#{time_ago_with_tooltip(@page.last_version.authored_date)}
 
.nav-controls
= render 'main_links'
Loading
Loading
---
title: Performance issues when loading large number of wiki pages
merge_request: 15276
author:
type: performance
Loading
Loading
@@ -10,4 +10,30 @@ module Gollum
index.send(name, *args)
end
end
class Wiki
def pages(treeish = nil, limit: nil)
tree_list((treeish || @ref), limit: limit)
end
def tree_list(ref, limit: nil)
if (sha = @access.ref_to_sha(ref))
commit = @access.commit(sha)
tree_map_for(sha).inject([]) do |list, entry|
next list unless @page_class.valid_page_name?(entry.name)
list << entry.page(self, commit)
break list if limit && list.size >= limit
list
end
else
[]
end
end
end
end
Rails.application.configure do
config.after_initialize do
Gollum::Page.per_page = Kaminari.config.default_per_page
end
end
Loading
Loading
@@ -58,12 +58,12 @@ module Gitlab
end
end
 
def pages
@repository.gitaly_migrate(:wiki_get_all_pages) do |is_enabled|
def pages(limit: nil)
@repository.gitaly_migrate(:wiki_get_all_pages, status: Gitlab::GitalyClient::MigrationStatus::DISABLED) do |is_enabled|
if is_enabled
gitaly_get_all_pages
else
gollum_get_all_pages
gollum_get_all_pages(limit: limit)
end
end
end
Loading
Loading
@@ -88,14 +88,23 @@ module Gitlab
end
end
 
def page_versions(page_path)
# options:
# :page - The Integer page number.
# :per_page - The number of items per page.
# :limit - Total number of items to return.
def page_versions(page_path, options = {})
current_page = gollum_page_by_path(page_path)
current_page.versions.map do |gollum_git_commit|
gollum_page = gollum_wiki.page(current_page.title, gollum_git_commit.id)
new_version(gollum_page, gollum_git_commit.id)
commits_from_page(current_page, options).map do |gitlab_git_commit|
gollum_page = gollum_wiki.page(current_page.title, gitlab_git_commit.id)
Gitlab::Git::WikiPageVersion.new(gitlab_git_commit, gollum_page&.format)
end
end
 
def count_page_versions(page_path)
@repository.count_commits(ref: 'HEAD', path: page_path)
end
def preview_slug(title, format)
# Adapted from gollum gem (Gollum::Wiki#preview_page) to avoid
# using Rugged through a Gollum::Wiki instance
Loading
Loading
@@ -110,6 +119,22 @@ module Gitlab
 
private
 
# options:
# :page - The Integer page number.
# :per_page - The number of items per page.
# :limit - Total number of items to return.
def commits_from_page(gollum_page, options = {})
unless options[:limit]
options[:offset] = ([1, options.delete(:page).to_i].max - 1) * Gollum::Page.per_page
options[:limit] = (options.delete(:per_page) || Gollum::Page.per_page).to_i
end
@repository.log(ref: gollum_page.last_version.id,
path: gollum_page.path,
limit: options[:limit],
offset: options[:offset])
end
def gollum_wiki
@gollum_wiki ||= Gollum::Wiki.new(@repository.path)
end
Loading
Loading
@@ -126,8 +151,17 @@ module Gitlab
end
 
def new_version(gollum_page, commit_id)
commit = Gitlab::Git::Commit.find(@repository, commit_id)
Gitlab::Git::WikiPageVersion.new(commit, gollum_page&.format)
Gitlab::Git::WikiPageVersion.new(version(commit_id), gollum_page&.format)
end
def version(commit_id)
commit_find_proc = -> { Gitlab::Git::Commit.find(@repository, commit_id) }
if RequestStore.active?
RequestStore.fetch([:wiki_version_commit, commit_id]) { commit_find_proc.call }
else
commit_find_proc.call
end
end
 
def assert_type!(object, klass)
Loading
Loading
@@ -185,8 +219,8 @@ module Gitlab
Gitlab::Git::WikiFile.new(gollum_file)
end
 
def gollum_get_all_pages
gollum_wiki.pages.map { |gollum_page| new_page(gollum_page) }
def gollum_get_all_pages(limit: nil)
gollum_wiki.pages(limit: limit).map { |gollum_page| new_page(gollum_page) }
end
 
def gitaly_write_page(name, format, content, commit_details)
Loading
Loading
Loading
Loading
@@ -373,7 +373,7 @@ describe WikiPage do
end
 
it 'returns commit sha' do
expect(@page.last_commit_sha).to eq @page.commit.sha
expect(@page.last_commit_sha).to eq @page.last_version.sha
end
 
it 'is changed after page updated' do
Loading
Loading
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment