debian-mirror-gitlab/lib/gitlab/database/count.rb

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

64 lines
2.2 KiB
Ruby
Raw Normal View History

2019-02-15 15:39:39 +05:30
# frozen_string_literal: true
2018-10-15 14:42:47 +05:30
# For large tables, PostgreSQL can take a long time to count rows due to MVCC.
2019-02-15 15:39:39 +05:30
# We can optimize this by using various strategies for approximate counting.
#
# For example, we can use the reltuples count as described in https://wiki.postgresql.org/wiki/Slow_Counting.
#
# However, since statistics are not always up to date, we also implement a table sampling strategy
# that performs an exact count but only on a sample of the table. See TablesampleCountStrategy.
2018-10-15 14:42:47 +05:30
module Gitlab
module Database
module Count
CONNECTION_ERRORS =
if defined?(PG)
[
ActionView::Template::Error,
ActiveRecord::StatementInvalid,
PG::Error
].freeze
else
[
ActionView::Template::Error,
ActiveRecord::StatementInvalid
].freeze
end
2018-11-08 19:23:39 +05:30
# Takes in an array of models and returns a Hash for the approximate
2019-02-15 15:39:39 +05:30
# counts for them.
#
# Various count strategies can be specified that are executed in
# sequence until all tables have an approximate count attached
# or we run out of strategies.
#
# Note that not all strategies are available on all supported RDBMS.
2018-11-08 19:23:39 +05:30
#
# @param [Array]
# @return [Hash] of Model -> count mapping
2021-11-18 22:05:49 +05:30
def self.approximate_counts(models, strategies: [])
if strategies.empty?
# ExactCountStrategy is the only strategy working on read-only DBs, as others make
# use of tuple stats which use the primary DB to estimate tables size in a transaction.
strategies = if ::Gitlab::Database.read_write?
[TablesampleCountStrategy, ReltuplesCountStrategy, ExactCountStrategy]
else
[ExactCountStrategy]
end
end
2019-02-15 15:39:39 +05:30
strategies.each_with_object({}) do |strategy, counts_by_model|
2019-10-12 21:52:04 +05:30
models_with_missing_counts = models - counts_by_model.keys
2018-11-08 19:23:39 +05:30
2019-10-12 21:52:04 +05:30
break counts_by_model if models_with_missing_counts.empty?
2018-10-15 14:42:47 +05:30
2019-10-12 21:52:04 +05:30
counts = strategy.new(models_with_missing_counts).count
2018-10-15 14:42:47 +05:30
2019-10-12 21:52:04 +05:30
counts.each do |model, count|
counts_by_model[model] = count
2019-02-15 15:39:39 +05:30
end
2018-11-08 19:23:39 +05:30
end
2018-10-15 14:42:47 +05:30
end
end
end
end