Skip to content

Commit

Permalink
New feature to track query blockers.
Browse files Browse the repository at this point in the history
  • Loading branch information
jeffdoering committed May 3, 2019
1 parent adb7dc5 commit 090def6
Show file tree
Hide file tree
Showing 31 changed files with 983 additions and 108 deletions.
10 changes: 9 additions & 1 deletion guides/Linux.md
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,7 @@ CREATE TABLE "pghero_connection_stats" (
"username" text,
"captured_at" timestamp
);
CREATE INDEX "pghero_connection_stats" ("database", "captured_at");
CREATE INDEX ON "pghero_connection_stats" ("database", "captured_at");
```

Schedule the task below to run once a day.
Expand All @@ -234,6 +234,14 @@ Schedule the task below to run once a day.
sudo pghero run rake pghero:capture_connection_stats
```

## Historical Query Blockers

To track query blockers over time, create a table to store them.

TODO
```sql
CREATE TABLE ...
```

## System Stats

Expand Down
6 changes: 6 additions & 0 deletions guides/Rails.md
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,12 @@ PgHero.drop_user("ganondorf")

## Upgrading

### 2.x.y

New features

Sample blocker queries - TODO

### 2.0.0

New features
Expand Down
39 changes: 39 additions & 0 deletions lib/generators/pghero/blocker_sample_sessions_generator.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# frozen_string_literal: true

# taken from https://github.com/collectiveidea/audited/blob/master/lib/generators/audited/install_generator.rb
require 'rails/generators'
require 'rails/generators/migration'
require 'active_record'
require 'rails/generators/active_record'

module Pghero
module Generators
class BlockerSampleSessionsGenerator < Rails::Generators::Base
include Rails::Generators::Migration

source_root File.expand_path('../templates', __FILE__)

# Implement the required interface for Rails::Generators::Migration.
def self.next_migration_number(dirname) #:nodoc:
next_migration_number = current_migration_number(dirname) + 1
if ::ActiveRecord::Base.timestamped_migrations
[Time.now.utc.strftime("%Y%m%d%H%M%S"), "%.14d" % next_migration_number].max
else
'%.3d' % next_migration_number
end
end

def copy_migration
migration_template 'blocker_sample_sessions.rb',
'db/migrate/create_pghero_blocker_sample_sessions.rb',
migration_version: migration_version
end

def migration_version
return unless ActiveRecord::VERSION::MAJOR >= 5

"[#{ActiveRecord::VERSION::MAJOR}.#{ActiveRecord::VERSION::MINOR}]"
end
end
end
end
39 changes: 39 additions & 0 deletions lib/generators/pghero/blocker_samples_generator.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# frozen_string_literal: true

# taken from https://github.com/collectiveidea/audited/blob/master/lib/generators/audited/install_generator.rb
require 'rails/generators'
require 'rails/generators/migration'
require 'active_record'
require 'rails/generators/active_record'

module Pghero
module Generators
class BlockerSamplesGenerator < Rails::Generators::Base
include Rails::Generators::Migration

source_root File.expand_path('../templates', __FILE__)

# Implement the required interface for Rails::Generators::Migration.
def self.next_migration_number(dirname) #:nodoc:
next_migration_number = current_migration_number(dirname) + 1
if ::ActiveRecord::Base.timestamped_migrations
[Time.now.utc.strftime("%Y%m%d%H%M%S"), "%.14d" % next_migration_number].max
else
'%.3d' % next_migration_number
end
end

def copy_migration
migration_template 'blocker_samples.rb',
'db/migrate/create_pghero_blocker_samples.rb',
migration_version: migration_version
end

def migration_version
return unless ActiveRecord::VERSION::MAJOR >= 5

"[#{ActiveRecord::VERSION::MAJOR}.#{ActiveRecord::VERSION::MINOR}]"
end
end
end
end
31 changes: 31 additions & 0 deletions lib/generators/pghero/templates/blocker_sample_sessions.rb.tt
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
class <%= migration_class_name %> < ActiveRecord::Migration<%= migration_version %>
def change
create_table :pghero_blocker_sample_sessions do |t|
t.integer :blocker_sample_id, limit: 8
t.integer :pid
t.text :user
t.text :source
t.inet :client_addr
t.text :client_hostname
t.integer :client_port
t.timestamp :backend_start
t.timestamp :xact_start
t.timestamp :query_start
t.timestamp :state_change
t.text :wait_event_type
t.text :wait_event
t.text :state
t.integer :backend_xid, limit: 8
t.integer :backend_xmin, limit: 8
t.text :query
t.integer :query_hash, limit: 8
t.text :backend_type
t.integer :blocked_by, array: true
t.integer :blocking, array: true
end

add_foreign_key :pghero_blocker_sample_sessions, :pghero_blocker_samples, column: :blocker_sample_id

add_index :pghero_blocker_sample_sessions, :blocker_sample_id
end
end
13 changes: 13 additions & 0 deletions lib/generators/pghero/templates/blocker_samples.rb.tt
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
class <%= migration_class_name %> < ActiveRecord::Migration<%= migration_version %>
def change
create_table :pghero_blocker_samples do |t|
t.text :database
t.timestamp :captured_at
t.integer :txid_xmin, limit: 8
t.integer :txid_xmax, limit: 8
t.integer :txid_xip, limit: 8, array: true
end

add_index :pghero_blocker_samples, [:database, :captured_at]
end
end
26 changes: 22 additions & 4 deletions lib/pghero.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# frozen_string_literal: true

# dependencies
require "active_support"

Expand All @@ -9,8 +11,11 @@
require "pghero/methods/kill"
require "pghero/methods/maintenance"
require "pghero/methods/queries"
require "pghero/methods/query_blockers"
require "pghero/methods/query_blockers_history"
require "pghero/methods/query_stats"
require "pghero/methods/replication"
require "pghero/methods/repository"
require "pghero/methods/sequences"
require "pghero/methods/settings"
require "pghero/methods/space"
Expand All @@ -19,13 +24,14 @@
require "pghero/methods/tables"
require "pghero/methods/users"

require "pghero/base_database"
require "pghero/database"
require "pghero/engine" if defined?(Rails)
require "pghero/pg_const"
require "pghero/repository"
require "pghero/version"

module PgHero
autoload :Connection, "pghero/connection"
autoload :QueryStats, "pghero/query_stats"

class Error < StandardError; end
class NotEnabled < Error; end
Expand Down Expand Up @@ -55,7 +61,7 @@ class << self
:query_stats_available?, :query_stats_enabled?, :query_stats_extension_enabled?, :query_stats_readable?,
:rds_stats, :read_iops_stats, :region, :relation_sizes, :replica?, :replication_lag, :replication_lag_stats,
:reset_query_stats, :reset_stats, :running_queries, :secret_access_key, :sequence_danger, :sequences, :settings,
:slow_queries, :space_growth, :ssl_used?, :stats_connection, :suggested_indexes, :suggested_indexes_by_query,
:slow_queries, :space_growth, :ssl_used?, :suggested_indexes, :suggested_indexes_by_query,
:suggested_indexes_enabled?, :system_stats_enabled?, :table_caching, :table_hit_rate, :table_stats,
:total_connections, :transaction_id_danger, :unused_indexes, :unused_tables, :write_iops_stats

Expand Down Expand Up @@ -100,9 +106,10 @@ def config

def databases
@databases ||= begin
repository = PgHero::Repository.new
Hash[
config["databases"].map do |id, c|
[id.to_sym, PgHero::Database.new(id, c)]
[id.to_sym, PgHero::Database.new(id, c, repository)]
end
]
end
Expand All @@ -115,6 +122,7 @@ def primary_database
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
Expand All @@ -134,9 +142,19 @@ def capture_connection_stats(verbose: false)
end
end

def capture_query_blockers(verbose: false)
each_database do |database|
next unless database.capture_query_blockers?

puts "Capturing query blockers for #{database.id}..." if verbose
database.capture_query_blockers
end
end

def analyze_all(**options)
each_database do |database|
next if database.replica?

database.analyze_tables(**options)
end
end
Expand Down
15 changes: 15 additions & 0 deletions lib/pghero/base_database.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# frozen_string_literal: true

module PgHero
class BaseDatabase

include Methods::Basic

# Subclasses must define connection_model returning and ActiveRecord model
# for connection management

def connection
connection_model.connection
end
end
end
5 changes: 0 additions & 5 deletions lib/pghero/connection.rb

This file was deleted.

24 changes: 18 additions & 6 deletions lib/pghero/database.rb
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
# frozen_string_literal: true

require "active_record"

module PgHero
class Database
include Methods::Basic
class Database < BaseDatabase

include Methods::Connections
include Methods::Explain
include Methods::Indexes
include Methods::Kill
include Methods::Maintenance
include Methods::Queries
include Methods::QueryBlockers
include Methods::QueryStats
include Methods::Replication
include Methods::Sequences
Expand All @@ -19,17 +24,18 @@ class Database

attr_reader :id, :config

def initialize(id, config)
def initialize(id, config, repository)
@id = id
@config = config || {}
@repository = repository
end

def name
@name ||= @config["name"] || id.titleize
@name ||= config["name"] || id.titleize
end

def db_instance_identifier
@db_instance_identifier ||= @config["db_instance_identifier"]
@db_instance_identifier ||= config["db_instance_identifier"]
end

def capture_query_stats?
Expand Down Expand Up @@ -66,10 +72,12 @@ def index_bloat_bytes

private

attr_reader :repository

def connection_model
@connection_model ||= begin
url = config["url"]
Class.new(PgHero::Connection) do
Class.new(Connection) do
def self.name
"PgHero::Connection::Database#{object_id}"
end
Expand All @@ -83,5 +91,9 @@ def self.name
end
end
end

class Connection < ActiveRecord::Base
self.abstract_class = true
end
end
end
Loading

0 comments on commit 090def6

Please sign in to comment.