From 03b1bcbb686f8e56fe6c6303adacdc01c5403790 Mon Sep 17 00:00:00 2001 From: Drew Blessing Date: Fri, 10 Nov 2017 14:39:00 -0600 Subject: Truncate tree to max 1,000 items and display notice to users Rendering ten thousands of tree items consumes a lot of server time and can cause timeouts in extreme cases. Realistically, displaying more than 1,000 files is probably not useful so truncate and show the user a notice instead. 'Find files' can be used to locate specific files beyond the 1,000 limit. --- app/assets/stylesheets/pages/tree.scss | 7 ++++- app/helpers/tree_helper.rb | 15 ++++++++-- .../tree/_truncated_notice_tree_row.html.haml | 7 +++++ changelogs/unreleased/tree_item_limit.yml | 5 ++++ spec/helpers/tree_helper_spec.rb | 32 ++++++++++++++++++++-- 5 files changed, 59 insertions(+), 7 deletions(-) create mode 100644 app/views/projects/tree/_truncated_notice_tree_row.html.haml create mode 100644 changelogs/unreleased/tree_item_limit.yml diff --git a/app/assets/stylesheets/pages/tree.scss b/app/assets/stylesheets/pages/tree.scss index 50f0ef4414a..65b334662c2 100644 --- a/app/assets/stylesheets/pages/tree.scss +++ b/app/assets/stylesheets/pages/tree.scss @@ -125,7 +125,7 @@ color: $white-normal; } - &:hover { + &:hover:not(.tree-truncated-warning) { td { background-color: $row-hover; border-top: 1px solid $row-hover-border; @@ -198,6 +198,11 @@ } } + .tree-truncated-warning { + color: $orange-600; + background-color: $orange-100; + } + .tree-time-ago { min-width: 135px; color: $gl-text-color-secondary; diff --git a/app/helpers/tree_helper.rb b/app/helpers/tree_helper.rb index c4ea0f5ac53..0e106e2c85d 100644 --- a/app/helpers/tree_helper.rb +++ b/app/helpers/tree_helper.rb @@ -1,14 +1,23 @@ module TreeHelper + FILE_LIMIT = 1_000 + # Sorts a repository's tree so that folders are before files and renders # their corresponding partials # - # contents - A Grit::Tree object for the current tree + # tree - A `Tree` object for the current tree def render_tree(tree) # Sort submodules and folders together by name ahead of files folders, files, submodules = tree.trees, tree.blobs, tree.submodules - tree = "" + tree = '' items = (folders + submodules).sort_by(&:name) + files - tree << render(partial: "projects/tree/tree_row", collection: items) if items.present? + + if items.size > FILE_LIMIT + tree << render(partial: 'projects/tree/truncated_notice_tree_row', + locals: { limit: FILE_LIMIT, total: items.size }) + items = items.take(FILE_LIMIT) + end + + tree << render(partial: 'projects/tree/tree_row', collection: items) if items.present? tree.html_safe end diff --git a/app/views/projects/tree/_truncated_notice_tree_row.html.haml b/app/views/projects/tree/_truncated_notice_tree_row.html.haml new file mode 100644 index 00000000000..693b641888b --- /dev/null +++ b/app/views/projects/tree/_truncated_notice_tree_row.html.haml @@ -0,0 +1,7 @@ +%tr.tree-truncated-warning + %td{ colspan: '3' } + = icon('exclamation-triangle fw') + %span + Too many items to show. To preserve performance only + %strong #{number_with_delimiter(limit)} of #{number_with_delimiter(total)} + items are displayed. diff --git a/changelogs/unreleased/tree_item_limit.yml b/changelogs/unreleased/tree_item_limit.yml new file mode 100644 index 00000000000..d95c5776075 --- /dev/null +++ b/changelogs/unreleased/tree_item_limit.yml @@ -0,0 +1,5 @@ +--- +title: Truncate tree to max 1,000 items and display notice to users +merge_request: +author: +type: performance diff --git a/spec/helpers/tree_helper_spec.rb b/spec/helpers/tree_helper_spec.rb index d7b66e6f078..c358ccae9c3 100644 --- a/spec/helpers/tree_helper_spec.rb +++ b/spec/helpers/tree_helper_spec.rb @@ -1,10 +1,36 @@ require 'spec_helper' describe TreeHelper do + let(:project) { create(:project, :repository) } + let(:repository) { project.repository } + let(:sha) { 'ce369011c189f62c815f5971d096b26759bab0d1' } + + describe '.render_tree' do + before do + @id = sha + @project = project + end + + it 'displays all entries without a warning' do + tree = repository.tree(sha, 'files') + + html = render_tree(tree) + + expect(html).not_to have_selector('.tree-truncated-warning') + end + + it 'truncates entries and adds a warning' do + stub_const('TreeHelper::FILE_LIMIT', 1) + tree = repository.tree(sha, 'files') + + html = render_tree(tree) + + expect(html).to have_selector('.tree-truncated-warning', count: 1) + expect(html).to have_selector('.tree-item-file-name', count: 1) + end + end + describe 'flatten_tree' do - let(:project) { create(:project, :repository) } - let(:repository) { project.repository } - let(:sha) { 'ce369011c189f62c815f5971d096b26759bab0d1' } let(:tree) { repository.tree(sha, 'files') } let(:root_path) { 'files' } let(:tree_item) { tree.entries.find { |entry| entry.path == path } } -- cgit v1.2.1