diff --git a/lib/pact_broker/app.rb b/lib/pact_broker/app.rb index b5a17b6e0..2ada633d4 100644 --- a/lib/pact_broker/app.rb +++ b/lib/pact_broker/app.rb @@ -126,6 +126,7 @@ def any? PactBroker::DB.validate_connection_config if configuration.validate_database_connection_config PactBroker::DB.set_mysql_strict_mode_if_mysql PactBroker::DB.connection.extension(:pagination) + PactBroker::DB.connection.extension(:statement_timeout) Sequel.datetime_class = DateTime Sequel.database_timezone = :utc # Store all dates in UTC, assume any date without a TZ is UTC diff --git a/lib/pact_broker/configuration.rb b/lib/pact_broker/configuration.rb index a9fa828ad..e0f33c161 100644 --- a/lib/pact_broker/configuration.rb +++ b/lib/pact_broker/configuration.rb @@ -38,7 +38,8 @@ class Configuration :log_dir, :allow_missing_migration_files, :auto_migrate_db_data, - :use_rack_protection + :use_rack_protection, + :metrics_sql_statement_timeout ] attr_accessor :base_url, :log_dir, :database_connection, :auto_migrate_db, :auto_migrate_db_data, :allow_missing_migration_files, :example_data_seeder, :seed_example_data, :use_hal_browser, :html_pact_renderer, :use_rack_protection @@ -57,6 +58,7 @@ class Configuration attr_reader :api_error_reporters attr_reader :custom_logger attr_accessor :policy_builder, :policy_scope_builder, :base_resource_class_factory + attr_accessor :metrics_sql_statement_timeout alias_method :policy_finder=, :policy_builder= alias_method :policy_scope_finder=, :policy_scope_builder= @@ -126,6 +128,7 @@ def self.default_configuration PactBroker::Api::Resources::DefaultBaseResource } config.warning_error_class_names = ['Sequel::ForeignKeyConstraintViolation'] + config.metrics_sql_statement_timeout = 30 config end diff --git a/lib/pact_broker/metrics/service.rb b/lib/pact_broker/metrics/service.rb index b01e17cac..6e445fdb0 100644 --- a/lib/pact_broker/metrics/service.rb +++ b/lib/pact_broker/metrics/service.rb @@ -1,3 +1,4 @@ +require 'pact_broker/configuration' require 'pact_broker/pacts/pact_publication' require 'pact_broker/pacts/pact_version' require 'pact_broker/domain/pacticipant' @@ -66,7 +67,7 @@ def metrics count: PactBroker::Webhooks::Execution.count }, matrix: { - count: PactBroker::Matrix::Row.count + count: matrix_count } } end @@ -87,6 +88,16 @@ def verification_distribution order by 1" PactBroker::Pacts::PactPublication.db[query].all.each_with_object({}) { |row, hash| hash[row[:number_of_verifications]] = row[:pact_version_count] } end + + def matrix_count + begin + PactBroker::Matrix::Row.db.with_statement_timeout(PactBroker.configuration.metrics_sql_statement_timeout) do + PactBroker::Matrix::Row.count + end + rescue Sequel::DatabaseError => e + -1 + end + end end end end diff --git a/lib/sequel/extensions/statement_timeout.rb b/lib/sequel/extensions/statement_timeout.rb new file mode 100644 index 000000000..6d9aafd97 --- /dev/null +++ b/lib/sequel/extensions/statement_timeout.rb @@ -0,0 +1,22 @@ +module Sequel + module StatementTimeout + def with_statement_timeout(timeout_seconds = 20) + # Might not have postgres class loaded, use class name + if self.class.name == "Sequel::Postgres::Database" + # Don't want to use a transaction because this will often be a read and a transaction is unnecessary. + # Also, when using it for clean, want to control the transactions outside this. + current_statement_timeout = execute("show statement_timeout") { |r| r.first.values.first } + run("SET statement_timeout = '#{timeout_seconds}s'") + begin + yield + ensure + run("SET statement_timeout = '#{current_statement_timeout}'") + end + else + yield + end + end + end + + Database.register_extension(:statement_timeout){|db| db.extend(StatementTimeout) } +end