Commit c5fb4682 authored by Andreas Brandl's avatar Andreas Brandl

Flexible approximate counts with fallback strategies.

parent b6a530c9
...@@ -25,21 +25,17 @@ module Gitlab ...@@ -25,21 +25,17 @@ module Gitlab
# #
# @param [Array] # @param [Array]
# @return [Hash] of Model -> count mapping # @return [Hash] of Model -> count mapping
def self.approximate_counts(models) def self.approximate_counts(models, strategies = [ReltuplesCountStrategy, ExactCountStrategy])
counts_by_model = {} strategies.each_with_object({}) do |strategy, counts_by_model|
if strategy.enabled?
if Gitlab::Database.postgresql? models_with_missing_counts = models - counts_by_model.keys
#counts_by_model = ReltuplesCountStrategy.new(models).count counts = strategy.new(models_with_missing_counts).count
counts_by_model = reltuples_from_recently_updated(models)
end counts.each do |model, count|
counts_by_model[model] = count
missing_models = models - counts_by_model.keys end
end
ExactCountStrategy.new(missing_models).count.each do |model, count|
counts_by_model[model] = count
end end
counts_by_model
end end
# Returns a hash of the table names that have recently updated tuples. # Returns a hash of the table names that have recently updated tuples.
...@@ -61,6 +57,10 @@ module Gitlab ...@@ -61,6 +57,10 @@ module Gitlab
data[model] = model.count data[model] = model.count
end end
end end
def self.enabled?
true
end
end end
class ReltuplesCountStrategy class ReltuplesCountStrategy
...@@ -92,6 +92,10 @@ module Gitlab ...@@ -92,6 +92,10 @@ module Gitlab
{} {}
end end
def self.enabled?
Gitlab::Database.postgresql?
end
private private
def table_names def table_names
......
...@@ -23,10 +23,14 @@ describe Gitlab::Database::Count do ...@@ -23,10 +23,14 @@ describe Gitlab::Database::Count do
end end
context 'with PostgreSQL', :postgresql do context 'with PostgreSQL', :postgresql do
let(:reltuples_strategy) { double('reltuples_strategy', count: {}) }
before do
allow(Gitlab::Database::Count::ReltuplesCountStrategy).to receive(:new).with(models).and_return(reltuples_strategy)
end
describe 'when reltuples have not been updated' do describe 'when reltuples have not been updated' do
it 'counts all models the normal way' do it 'counts all models the normal way' do
expect(described_class).to receive(:reltuples_from_recently_updated).with(models).and_return({})
expect(Project).to receive(:count).and_call_original expect(Project).to receive(:count).and_call_original
expect(Identity).to receive(:count).and_call_original expect(Identity).to receive(:count).and_call_original
expect(described_class.approximate_counts(models)).to eq({ Project => 3, Identity => 1 }) expect(described_class.approximate_counts(models)).to eq({ Project => 3, Identity => 1 })
...@@ -45,7 +49,7 @@ describe Gitlab::Database::Count do ...@@ -45,7 +49,7 @@ describe Gitlab::Database::Count do
describe 'when some reltuples have been updated' do describe 'when some reltuples have been updated' do
it 'counts projects in the fast way' do it 'counts projects in the fast way' do
expect(described_class).to receive(:reltuples_from_recently_updated).with(models).and_return({ Project => 3 }) expect(reltuples_strategy).to receive(:count).and_return({ Project => 3 })
expect(Project).not_to receive(:count).and_call_original expect(Project).not_to receive(:count).and_call_original
expect(Identity).to receive(:count).and_call_original expect(Identity).to receive(:count).and_call_original
...@@ -53,13 +57,16 @@ describe Gitlab::Database::Count do ...@@ -53,13 +57,16 @@ describe Gitlab::Database::Count do
end end
end end
# TODO: This covers two parts: reltuple strategy itself and the fallback
# TODO: Add spec that covers strategy details for reltuple strategy
describe 'when all reltuples have been updated' do describe 'when all reltuples have been updated' do
before do #before do
ActiveRecord::Base.connection.execute('ANALYZE projects') #ActiveRecord::Base.connection.execute('ANALYZE projects')
ActiveRecord::Base.connection.execute('ANALYZE identities') #ActiveRecord::Base.connection.execute('ANALYZE identities')
end #end
it 'counts models with the standard way' do it 'counts models with the standard way' do
allow(reltuples_strategy).to receive(:count).and_return({ Project => 3, Identity => 1 })
expect(Project).not_to receive(:count) expect(Project).not_to receive(:count)
expect(Identity).not_to receive(:count) expect(Identity).not_to receive(:count)
......
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