diff options
author | Yorick Peterse <yorickpeterse@gmail.com> | 2018-05-16 17:31:48 +0000 |
---|---|---|
committer | Yorick Peterse <yorickpeterse@gmail.com> | 2018-05-16 17:31:48 +0000 |
commit | f3d60d7e733eb67b3d41581eee513719cf51cc31 (patch) | |
tree | 8baf15c14f70636c6a646ae157116a2fb30d78eb /lib | |
parent | 9e41ed61ea70532578416e01c228ff7a81abc192 (diff) | |
parent | e38938b332ca751dfc5e784f242d620016e8ca43 (diff) | |
download | gitlab-ce-f3d60d7e733eb67b3d41581eee513719cf51cc31.tar.gz |
Merge branch 'sh-fast-admin-counts' into 'master'
Fix Error 500 viewing admin page due to statement timeouts
Closes #46255
See merge request gitlab-org/gitlab-ce!18982
Diffstat (limited to 'lib')
-rw-r--r-- | lib/gitlab/database/count.rb | 48 |
1 files changed, 48 insertions, 0 deletions
diff --git a/lib/gitlab/database/count.rb b/lib/gitlab/database/count.rb new file mode 100644 index 00000000000..3374203960e --- /dev/null +++ b/lib/gitlab/database/count.rb @@ -0,0 +1,48 @@ +# For large tables, PostgreSQL can take a long time to count rows due to MVCC. +# We can optimize this by using the reltuples count as described in https://wiki.postgresql.org/wiki/Slow_Counting. +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 + + def self.approximate_count(model) + return model.count unless Gitlab::Database.postgresql? + + execute_estimate_if_updated_recently(model) || model.count + end + + def self.execute_estimate_if_updated_recently(model) + ActiveRecord::Base.connection.select_value(postgresql_estimate_query(model)).to_i if reltuples_updated_recently?(model) + rescue *CONNECTION_ERRORS + end + + def self.reltuples_updated_recently?(model) + time = "to_timestamp(#{1.hour.ago.to_i})" + query = <<~SQL + SELECT 1 FROM pg_stat_user_tables WHERE relname = '#{model.table_name}' AND + (last_vacuum > #{time} OR last_autovacuum > #{time} OR last_analyze > #{time} OR last_autoanalyze > #{time}) + SQL + + ActiveRecord::Base.connection.select_all(query).count > 0 + rescue *CONNECTION_ERRORS + false + end + + def self.postgresql_estimate_query(model) + "SELECT reltuples::bigint AS estimate FROM pg_class where relname = '#{model.table_name}'" + end + end + end +end |