Skip to content
Snippets Groups Projects
Commit 95464320 authored by Yorick Peterse's avatar Yorick Peterse
Browse files

Revamp finding projects by namespaces

By using a JOIN we can remove the need for using 2 separate queries to
find a project by its namespace. Combined with an index (only needed for
PostgreSQL) this reduces the query time from ~245 ms (~520 ms for the
first call) down to roughly 10 ms (~15 ms for the first call).
parent 1190d0ab
No related branches found
No related tags found
No related merge requests found
Loading
Loading
@@ -40,6 +40,7 @@ class Project < ActiveRecord::Base
include Referable
include Sortable
include AfterCommitQueue
include CaseSensitivity
 
extend Gitlab::ConfigHelper
extend Enumerize
Loading
Loading
@@ -235,13 +236,18 @@ class Project < ActiveRecord::Base
end
 
def find_with_namespace(id)
return nil unless id.include?('/')
id = id.split('/')
namespace = Namespace.by_path(id.first)
return nil unless namespace
where(namespace_id: namespace.id).where("LOWER(projects.path) = :path", path: id.second.downcase).first
namespace_path, project_path = id.split('/')
return nil if !namespace_path || !project_path
# Use of unscoped ensures we're not secretly adding any ORDER BYs, which
# have a negative impact on performance (and aren't needed for this
# query).
unscoped.
joins(:namespace).
case_insensitive_where('namespaces.path' => namespace_path).
case_insensitive_where('projects.path' => project_path).
take
end
 
def visibility_levels
Loading
Loading
class NamespacesProjectsPathLowerIndexes < ActiveRecord::Migration
disable_ddl_transaction!
def up
return unless Gitlab::Database.postgresql?
execute 'CREATE INDEX CONCURRENTLY index_on_namespaces_lower_path ON namespaces (LOWER(path));'
execute 'CREATE INDEX CONCURRENTLY index_on_projects_lower_path ON projects (LOWER(path));'
end
def down
return unless Gitlab::Database.postgresql?
remove_index :namespaces, name: :index_on_namespaces_lower_path
remove_index :projects, name: :index_on_projects_lower_path
end
end
Loading
Loading
@@ -11,7 +11,7 @@
#
# It's strongly recommended that you check this file into your version control system.
 
ActiveRecord::Schema.define(version: 20151005162154) do
ActiveRecord::Schema.define(version: 20151007120511) do
 
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
Loading
Loading
require 'spec_helper'
describe Project, benchmark: true do
describe '.find_with_namespace' do
let(:group) { create(:group, name: 'sisinmaru') }
let(:project) { create(:project, name: 'maru', namespace: group) }
describe 'using a capitalized namespace' do
benchmark_subject { described_class.find_with_namespace('sisinmaru/MARU') }
it { is_expected.to iterate_per_second(600) }
end
describe 'using a lowercased namespace' do
benchmark_subject { described_class.find_with_namespace('sisinmaru/maru') }
it { is_expected.to iterate_per_second(600) }
end
end
end
Loading
Loading
@@ -32,7 +32,7 @@ describe CaseSensitivity do
with(%q{LOWER("foo"."bar") = LOWER(:value)}, value: 'bar').
and_return(criteria)
 
expect(model.case_insensitive_where('foo.bar': 'bar')).to eq(criteria)
expect(model.case_insensitive_where(:'foo.bar' => 'bar')).to eq(criteria)
end
end
 
Loading
Loading
@@ -57,7 +57,7 @@ describe CaseSensitivity do
with(%q{LOWER("bar") = LOWER(:value)}, value: 'baz').
and_return(final)
 
got = model.case_insensitive_where(foo: 'bar', bar: 'baz')
got = model.case_insensitive_where(:foo => 'bar', :bar => 'baz')
 
expect(got).to eq(final)
end
Loading
Loading
@@ -82,7 +82,8 @@ describe CaseSensitivity do
with(%q{LOWER("foo"."baz") = LOWER(:value)}, value: 'baz').
and_return(final)
 
got = model.case_insensitive_where('foo.bar': 'bar', 'foo.baz': 'baz')
got = model.case_insensitive_where(:'foo.bar' => 'bar',
:'foo.baz' => 'baz')
 
expect(got).to eq(final)
end
Loading
Loading
@@ -116,7 +117,8 @@ describe CaseSensitivity do
with(%q{LOWER(`foo`.`bar`) = LOWER(:value)}, value: 'bar').
and_return(criteria)
 
expect(model.case_insensitive_where('foo.bar': 'bar')).to eq(criteria)
expect(model.case_insensitive_where(:'foo.bar' => 'bar')).
to eq(criteria)
end
end
 
Loading
Loading
@@ -166,7 +168,8 @@ describe CaseSensitivity do
with(%q{LOWER(`foo`.`baz`) = LOWER(:value)}, value: 'baz').
and_return(final)
 
got = model.case_insensitive_where('foo.bar': 'bar', 'foo.baz': 'baz')
got = model.case_insensitive_where(:'foo.bar' => 'bar',
:'foo.baz' => 'baz')
 
expect(got).to eq(final)
end
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