module PgHero

Constants

MUTEX
VERSION

Attributes

cache_hit_rate_threshold[RW]
config_path[RW]
env[RW]
explain_timeout_sec[RW]
filter_data[RW]
long_running_query_sec[RW]
show_migrations[RW]
slow_query_calls[RW]
slow_query_ms[RW]
total_connections_threshold[RW]

Public Class Methods

analyze_all(**options) click to toggle source
# File lib/pghero.rb, line 211
def analyze_all(**options)
  each_database do |database|
    next if database.replica?
    database.analyze_tables(**options)
  end
end
autoindex_all(create: false, verbose: true) click to toggle source
# File lib/pghero.rb, line 218
def autoindex_all(create: false, verbose: true)
  each_database do |database|
    puts "Autoindexing #{database.id}..." if verbose
    database.autoindex(create: create)
  end
end
capture_query_stats(verbose: false) click to toggle source
# File lib/pghero.rb, line 196
def capture_query_stats(verbose: false)
  each_database do |database|
    next unless database.capture_query_stats?
    puts "Capturing query stats for #{database.id}..." if verbose
    database.capture_query_stats(raise_errors: true)
  end
end
capture_space_stats(verbose: false) click to toggle source
# File lib/pghero.rb, line 204
def capture_space_stats(verbose: false)
  each_database do |database|
    puts "Capturing space stats for #{database.id}..." if verbose
    database.capture_space_stats
  end
end
clean_query_stats(before: nil) click to toggle source

delete previous stats go database by database to use an index stats for old databases are not cleaned up since we can’t use an index

# File lib/pghero.rb, line 232
def clean_query_stats(before: nil)
  each_database do |database|
    database.clean_query_stats(before: before)
  end
end
clean_space_stats(before: nil) click to toggle source
# File lib/pghero.rb, line 238
def clean_space_stats(before: nil)
  each_database do |database|
    database.clean_space_stats(before: before)
  end
end
config() click to toggle source
# File lib/pghero.rb, line 110
def config
  @config ||= file_config || default_config
end
connection_config(model) click to toggle source

private

# File lib/pghero.rb, line 245
def connection_config(model)
  model.connection_db_config.configuration_hash
end
databases() click to toggle source

ensure we only have one copy of databases so there’s only one connection pool per database

# File lib/pghero.rb, line 178
def databases
  unless defined?(@databases)
    # only use mutex on initialization
    MUTEX.synchronize do
      # return if another process initialized while we were waiting
      return @databases if defined?(@databases)

      @databases = config["databases"].map { |id, c| [id.to_sym, Database.new(id, c)] }.to_h
    end
  end

  @databases
end
default_config() click to toggle source

private

# File lib/pghero.rb, line 143
def default_config
  databases = {}

  unless ENV["PGHERO_DATABASE_URL"]
    ActiveRecord::Base.configurations.configs_for(env_name: env, include_replicas_key => true).each do |db|
      databases[db.send(spec_name_key)] = {"spec" => db.send(spec_name_key)}
    end
  end

  if databases.empty?
    databases["primary"] = {
      "url" => ENV["PGHERO_DATABASE_URL"] || default_connection_config
    }
  end

  if databases.size == 1
    databases.values.first.merge!(
      "aws_db_instance_identifier" => ENV["PGHERO_DB_INSTANCE_IDENTIFIER"],
      "gcp_database_id" => ENV["PGHERO_GCP_DATABASE_ID"],
      "azure_resource_id" => ENV["PGHERO_AZURE_RESOURCE_ID"]
    )
  end

  {
    "databases" => databases
  }
end
default_connection_config() click to toggle source

private

# File lib/pghero.rb, line 172
def default_connection_config
  connection_config(ActiveRecord::Base) if ActiveRecord::VERSION::STRING.to_f < 7.1
end
explain_enabled?() click to toggle source

private

# File lib/pghero.rb, line 97
def explain_enabled?
  explain_mode.nil? || explain_mode == true || explain_mode == "analyze"
end
explain_mode() click to toggle source

private

# File lib/pghero.rb, line 102
def explain_mode
  @config["explain"]
end
file_config() click to toggle source

private

# File lib/pghero.rb, line 115
def file_config
  unless defined?(@file_config)
    require "erb"
    require "yaml"

    path = config_path

    config_file_exists = File.exist?(path)

    config = YAML.safe_load(ERB.new(File.read(path)).result, aliases: true) if config_file_exists
    config ||= {}

    @file_config =
      if config[env]
        config[env]
      elsif config["databases"] # preferred format
        config
      elsif config_file_exists
        raise "Invalid config file"
      else
        nil
      end
  end

  @file_config
end
include_replicas_key() click to toggle source

private Rails 7.0 deprecates ‘include_replicas` for `include_hidden`

# File lib/pghero.rb, line 256
def include_replicas_key
  ActiveRecord::VERSION::MAJOR >= 7 ? :include_hidden : :include_replicas
end
password() click to toggle source

use method instead of attr_accessor to ensure this works if variable set after PgHero is loaded

# File lib/pghero.rb, line 87
def password
  @password ||= config["password"] || ENV["PGHERO_PASSWORD"]
end
pretty_size(value) click to toggle source
# File lib/pghero.rb, line 225
def pretty_size(value)
  ActiveSupport::NumberHelper.number_to_human_size(value, precision: 3)
end
primary_database() click to toggle source
# File lib/pghero.rb, line 192
def primary_database
  databases.values.first
end
spec_name_key() click to toggle source

private

# File lib/pghero.rb, line 250
def spec_name_key
  :name
end
stats_database_url() click to toggle source

config pattern for github.com/ankane/pghero/issues/424

# File lib/pghero.rb, line 92
def stats_database_url
  @stats_database_url ||= (file_config || {})["stats_database_url"] || ENV["PGHERO_STATS_DATABASE_URL"]
end
time_zone() click to toggle source
# File lib/pghero.rb, line 75
def time_zone
  @time_zone || Time.zone
end
time_zone=(time_zone) click to toggle source
# File lib/pghero.rb, line 71
def time_zone=(time_zone)
  @time_zone = time_zone.is_a?(ActiveSupport::TimeZone) ? time_zone : ActiveSupport::TimeZone[time_zone.to_s]
end
username() click to toggle source

use method instead of attr_accessor to ensure this works if variable set after PgHero is loaded

# File lib/pghero.rb, line 81
def username
  @username ||= config["username"] || ENV["PGHERO_USERNAME"]
end
visualize_url() click to toggle source
# File lib/pghero.rb, line 106
def visualize_url
  @visualize_url ||= config["visualize_url"] || ENV["PGHERO_VISUALIZE_URL"] || "https://tatiyants.com/pev/#/plans/new"
end

Private Class Methods

each_database() { |database| ... } click to toggle source
# File lib/pghero.rb, line 262
def each_database
  first_error = nil

  databases.each do |_, database|
    begin
      yield database
    rescue => e
      puts "#{e.class.name}: #{e.message}"
      puts
      first_error ||= e
    end
  end

  raise first_error if first_error

  true
end