pg_reports 0.3.1 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +36 -0
- data/app/controllers/pg_reports/dashboard_controller.rb +59 -4
- data/app/views/layouts/pg_reports/application.html.erb +1 -1
- data/app/views/pg_reports/dashboard/_show_modals.html.erb +8 -1
- data/app/views/pg_reports/dashboard/_show_scripts.html.erb +56 -18
- data/app/views/pg_reports/dashboard/_show_styles.html.erb +122 -1
- data/app/views/pg_reports/dashboard/show.html.erb +89 -47
- data/config/locales/en.yml +13 -0
- data/config/locales/ru.yml +13 -0
- data/config/locales/uk.yml +13 -0
- data/lib/pg_reports/dashboard/reports_registry.rb +14 -0
- data/lib/pg_reports/definitions/connections/active_connections.yml +23 -0
- data/lib/pg_reports/definitions/connections/blocking_queries.yml +20 -0
- data/lib/pg_reports/definitions/connections/connection_stats.yml +18 -0
- data/lib/pg_reports/definitions/connections/idle_connections.yml +21 -0
- data/lib/pg_reports/definitions/connections/locks.yml +22 -0
- data/lib/pg_reports/definitions/connections/long_running_queries.yml +43 -0
- data/lib/pg_reports/definitions/indexes/bloated_indexes.yml +43 -0
- data/lib/pg_reports/definitions/indexes/duplicate_indexes.yml +19 -0
- data/lib/pg_reports/definitions/indexes/index_sizes.yml +29 -0
- data/lib/pg_reports/definitions/indexes/index_usage.yml +27 -0
- data/lib/pg_reports/definitions/indexes/invalid_indexes.yml +19 -0
- data/lib/pg_reports/definitions/indexes/missing_indexes.yml +27 -0
- data/lib/pg_reports/definitions/indexes/unused_indexes.yml +41 -0
- data/lib/pg_reports/definitions/queries/all_queries.yml +35 -0
- data/lib/pg_reports/definitions/queries/expensive_queries.yml +43 -0
- data/lib/pg_reports/definitions/queries/heavy_queries.yml +49 -0
- data/lib/pg_reports/definitions/queries/low_cache_hit_queries.yml +47 -0
- data/lib/pg_reports/definitions/queries/missing_index_queries.yml +31 -0
- data/lib/pg_reports/definitions/queries/slow_queries.yml +48 -0
- data/lib/pg_reports/definitions/system/activity_overview.yml +17 -0
- data/lib/pg_reports/definitions/system/cache_stats.yml +18 -0
- data/lib/pg_reports/definitions/system/database_sizes.yml +18 -0
- data/lib/pg_reports/definitions/system/extensions.yml +19 -0
- data/lib/pg_reports/definitions/system/settings.yml +20 -0
- data/lib/pg_reports/definitions/tables/bloated_tables.yml +43 -0
- data/lib/pg_reports/definitions/tables/cache_hit_ratios.yml +26 -0
- data/lib/pg_reports/definitions/tables/recently_modified.yml +27 -0
- data/lib/pg_reports/definitions/tables/row_counts.yml +29 -0
- data/lib/pg_reports/definitions/tables/seq_scans.yml +31 -0
- data/lib/pg_reports/definitions/tables/table_sizes.yml +31 -0
- data/lib/pg_reports/definitions/tables/vacuum_needed.yml +39 -0
- data/lib/pg_reports/filter.rb +58 -0
- data/lib/pg_reports/module_generator.rb +44 -0
- data/lib/pg_reports/modules/connections.rb +8 -73
- data/lib/pg_reports/modules/indexes.rb +9 -94
- data/lib/pg_reports/modules/queries.rb +9 -100
- data/lib/pg_reports/modules/schema_analysis.rb +156 -0
- data/lib/pg_reports/modules/system.rb +7 -59
- data/lib/pg_reports/modules/tables.rb +9 -96
- data/lib/pg_reports/report_definition.rb +161 -0
- data/lib/pg_reports/report_loader.rb +38 -0
- data/lib/pg_reports/sql/schema_analysis/unique_indexes.sql +35 -0
- data/lib/pg_reports/version.rb +1 -1
- data/lib/pg_reports.rb +24 -0
- metadata +38 -1
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# Cache hit ratio for the entire database
|
|
2
|
+
# Simple report showing cache statistics
|
|
3
|
+
|
|
4
|
+
report:
|
|
5
|
+
name: cache_stats
|
|
6
|
+
module: system
|
|
7
|
+
description: "Database-wide cache hit statistics"
|
|
8
|
+
|
|
9
|
+
sql:
|
|
10
|
+
category: system
|
|
11
|
+
file: cache_stats
|
|
12
|
+
|
|
13
|
+
title: "Database Cache Statistics"
|
|
14
|
+
|
|
15
|
+
columns:
|
|
16
|
+
- database
|
|
17
|
+
- heap_hit_ratio
|
|
18
|
+
- index_hit_ratio
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# Database sizes
|
|
2
|
+
# Simple report showing all database sizes
|
|
3
|
+
|
|
4
|
+
report:
|
|
5
|
+
name: database_sizes
|
|
6
|
+
module: system
|
|
7
|
+
description: "Sizes of all databases"
|
|
8
|
+
|
|
9
|
+
sql:
|
|
10
|
+
category: system
|
|
11
|
+
file: database_sizes
|
|
12
|
+
|
|
13
|
+
title: "Database Sizes"
|
|
14
|
+
|
|
15
|
+
columns:
|
|
16
|
+
- database
|
|
17
|
+
- size_mb
|
|
18
|
+
- size_pretty
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# Extension information
|
|
2
|
+
# Simple report showing installed PostgreSQL extensions
|
|
3
|
+
|
|
4
|
+
report:
|
|
5
|
+
name: extensions
|
|
6
|
+
module: system
|
|
7
|
+
description: "Installed PostgreSQL extensions"
|
|
8
|
+
|
|
9
|
+
sql:
|
|
10
|
+
category: system
|
|
11
|
+
file: extensions
|
|
12
|
+
|
|
13
|
+
title: "Installed Extensions"
|
|
14
|
+
|
|
15
|
+
columns:
|
|
16
|
+
- name
|
|
17
|
+
- version
|
|
18
|
+
- schema
|
|
19
|
+
- description
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# PostgreSQL settings
|
|
2
|
+
# Simple report showing important configuration settings
|
|
3
|
+
|
|
4
|
+
report:
|
|
5
|
+
name: settings
|
|
6
|
+
module: system
|
|
7
|
+
description: "Important PostgreSQL configuration settings"
|
|
8
|
+
|
|
9
|
+
sql:
|
|
10
|
+
category: system
|
|
11
|
+
file: settings
|
|
12
|
+
|
|
13
|
+
title: "PostgreSQL Settings"
|
|
14
|
+
|
|
15
|
+
columns:
|
|
16
|
+
- name
|
|
17
|
+
- setting
|
|
18
|
+
- unit
|
|
19
|
+
- category
|
|
20
|
+
- description
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# Bloated tables - tables with high dead tuple ratio
|
|
2
|
+
# Report with threshold filtering and title interpolation
|
|
3
|
+
|
|
4
|
+
report:
|
|
5
|
+
name: bloated_tables
|
|
6
|
+
module: tables
|
|
7
|
+
description: "Tables with high dead tuple ratio"
|
|
8
|
+
|
|
9
|
+
sql:
|
|
10
|
+
category: tables
|
|
11
|
+
file: bloated_tables
|
|
12
|
+
|
|
13
|
+
title: "Bloated Tables (bloat >= ${threshold}%)"
|
|
14
|
+
title_vars:
|
|
15
|
+
threshold:
|
|
16
|
+
source: config
|
|
17
|
+
key: bloat_threshold_percent
|
|
18
|
+
|
|
19
|
+
columns:
|
|
20
|
+
- schema
|
|
21
|
+
- table_name
|
|
22
|
+
- live_rows
|
|
23
|
+
- dead_rows
|
|
24
|
+
- bloat_percent
|
|
25
|
+
- table_size_mb
|
|
26
|
+
|
|
27
|
+
parameters:
|
|
28
|
+
limit:
|
|
29
|
+
type: integer
|
|
30
|
+
default: 20
|
|
31
|
+
description: "Maximum number of results"
|
|
32
|
+
|
|
33
|
+
filters:
|
|
34
|
+
- field: bloat_percent
|
|
35
|
+
operator: gte
|
|
36
|
+
value:
|
|
37
|
+
source: config
|
|
38
|
+
key: bloat_threshold_percent
|
|
39
|
+
cast: float
|
|
40
|
+
|
|
41
|
+
problem_explanations:
|
|
42
|
+
dead_tuple_ratio: many_dead_tuples
|
|
43
|
+
n_dead_tup: many_dead_tuples
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# Table cache hit ratios
|
|
2
|
+
# Simple report with limit parameter
|
|
3
|
+
|
|
4
|
+
report:
|
|
5
|
+
name: cache_hit_ratios
|
|
6
|
+
module: tables
|
|
7
|
+
description: "Table cache hit ratios showing buffer hits vs reads"
|
|
8
|
+
|
|
9
|
+
sql:
|
|
10
|
+
category: tables
|
|
11
|
+
file: cache_hit_ratios
|
|
12
|
+
|
|
13
|
+
title: "Table Cache Hit Ratios"
|
|
14
|
+
|
|
15
|
+
columns:
|
|
16
|
+
- schema
|
|
17
|
+
- table_name
|
|
18
|
+
- heap_blks_read
|
|
19
|
+
- heap_blks_hit
|
|
20
|
+
- cache_hit_ratio
|
|
21
|
+
|
|
22
|
+
parameters:
|
|
23
|
+
limit:
|
|
24
|
+
type: integer
|
|
25
|
+
default: 50
|
|
26
|
+
description: "Maximum number of results"
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# Recently modified tables
|
|
2
|
+
# Simple report with limit parameter
|
|
3
|
+
|
|
4
|
+
report:
|
|
5
|
+
name: recently_modified
|
|
6
|
+
module: tables
|
|
7
|
+
description: "Tables with recent modifications (inserts, updates, deletes)"
|
|
8
|
+
|
|
9
|
+
sql:
|
|
10
|
+
category: tables
|
|
11
|
+
file: recently_modified
|
|
12
|
+
|
|
13
|
+
title: "Recently Modified Tables"
|
|
14
|
+
|
|
15
|
+
columns:
|
|
16
|
+
- schema
|
|
17
|
+
- table_name
|
|
18
|
+
- n_tup_ins
|
|
19
|
+
- n_tup_upd
|
|
20
|
+
- n_tup_del
|
|
21
|
+
- last_analyze
|
|
22
|
+
|
|
23
|
+
parameters:
|
|
24
|
+
limit:
|
|
25
|
+
type: integer
|
|
26
|
+
default: 20
|
|
27
|
+
description: "Maximum number of results"
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# Table row counts
|
|
2
|
+
# Report with title interpolation using limit parameter
|
|
3
|
+
|
|
4
|
+
report:
|
|
5
|
+
name: row_counts
|
|
6
|
+
module: tables
|
|
7
|
+
description: "Table row counts sorted by count"
|
|
8
|
+
|
|
9
|
+
sql:
|
|
10
|
+
category: tables
|
|
11
|
+
file: row_counts
|
|
12
|
+
|
|
13
|
+
title: "Table Row Counts (top ${limit})"
|
|
14
|
+
title_vars:
|
|
15
|
+
limit:
|
|
16
|
+
source: param
|
|
17
|
+
key: limit
|
|
18
|
+
|
|
19
|
+
columns:
|
|
20
|
+
- schema
|
|
21
|
+
- table_name
|
|
22
|
+
- row_count
|
|
23
|
+
- table_size_mb
|
|
24
|
+
|
|
25
|
+
parameters:
|
|
26
|
+
limit:
|
|
27
|
+
type: integer
|
|
28
|
+
default: 50
|
|
29
|
+
description: "Maximum number of results"
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# Sequential scan statistics
|
|
2
|
+
# Report with title interpolation using limit parameter
|
|
3
|
+
|
|
4
|
+
report:
|
|
5
|
+
name: seq_scans
|
|
6
|
+
module: tables
|
|
7
|
+
description: "Sequential scan statistics for tables"
|
|
8
|
+
|
|
9
|
+
sql:
|
|
10
|
+
category: tables
|
|
11
|
+
file: seq_scans
|
|
12
|
+
|
|
13
|
+
title: "Sequential Scans (top ${limit})"
|
|
14
|
+
title_vars:
|
|
15
|
+
limit:
|
|
16
|
+
source: param
|
|
17
|
+
key: limit
|
|
18
|
+
|
|
19
|
+
columns:
|
|
20
|
+
- schema
|
|
21
|
+
- table_name
|
|
22
|
+
- seq_scan
|
|
23
|
+
- seq_tup_read
|
|
24
|
+
- idx_scan
|
|
25
|
+
- rows_per_seq_scan
|
|
26
|
+
|
|
27
|
+
parameters:
|
|
28
|
+
limit:
|
|
29
|
+
type: integer
|
|
30
|
+
default: 20
|
|
31
|
+
description: "Maximum number of results"
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# Table sizes including indexes
|
|
2
|
+
# Report with title interpolation using limit parameter
|
|
3
|
+
|
|
4
|
+
report:
|
|
5
|
+
name: table_sizes
|
|
6
|
+
module: tables
|
|
7
|
+
description: "Table sizes including indexes"
|
|
8
|
+
|
|
9
|
+
sql:
|
|
10
|
+
category: tables
|
|
11
|
+
file: table_sizes
|
|
12
|
+
|
|
13
|
+
title: "Table Sizes (top ${limit})"
|
|
14
|
+
title_vars:
|
|
15
|
+
limit:
|
|
16
|
+
source: param
|
|
17
|
+
key: limit
|
|
18
|
+
|
|
19
|
+
columns:
|
|
20
|
+
- schema
|
|
21
|
+
- table_name
|
|
22
|
+
- table_size_mb
|
|
23
|
+
- index_size_mb
|
|
24
|
+
- total_size_mb
|
|
25
|
+
- row_count
|
|
26
|
+
|
|
27
|
+
parameters:
|
|
28
|
+
limit:
|
|
29
|
+
type: integer
|
|
30
|
+
default: 50
|
|
31
|
+
description: "Maximum number of results"
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# Tables needing vacuum - high dead rows count
|
|
2
|
+
# Report with threshold filtering and title interpolation
|
|
3
|
+
|
|
4
|
+
report:
|
|
5
|
+
name: vacuum_needed
|
|
6
|
+
module: tables
|
|
7
|
+
description: "Tables with high dead rows count needing vacuum"
|
|
8
|
+
|
|
9
|
+
sql:
|
|
10
|
+
category: tables
|
|
11
|
+
file: vacuum_needed
|
|
12
|
+
|
|
13
|
+
title: "Tables Needing Vacuum (dead rows >= ${threshold})"
|
|
14
|
+
title_vars:
|
|
15
|
+
threshold:
|
|
16
|
+
source: config
|
|
17
|
+
key: dead_rows_threshold
|
|
18
|
+
|
|
19
|
+
columns:
|
|
20
|
+
- schema
|
|
21
|
+
- table_name
|
|
22
|
+
- n_live_tup
|
|
23
|
+
- n_dead_tup
|
|
24
|
+
- last_vacuum
|
|
25
|
+
- last_autovacuum
|
|
26
|
+
|
|
27
|
+
parameters:
|
|
28
|
+
limit:
|
|
29
|
+
type: integer
|
|
30
|
+
default: 20
|
|
31
|
+
description: "Maximum number of results"
|
|
32
|
+
|
|
33
|
+
filters:
|
|
34
|
+
- field: n_dead_tup
|
|
35
|
+
operator: gte
|
|
36
|
+
value:
|
|
37
|
+
source: config
|
|
38
|
+
key: dead_rows_threshold
|
|
39
|
+
cast: integer
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module PgReports
|
|
4
|
+
# Applies filtering logic to report data based on YAML configuration
|
|
5
|
+
class Filter
|
|
6
|
+
OPERATORS = {
|
|
7
|
+
"eq" => ->(a, b) { a == b },
|
|
8
|
+
"ne" => ->(a, b) { a != b },
|
|
9
|
+
"lt" => ->(a, b) { a < b },
|
|
10
|
+
"lte" => ->(a, b) { a <= b },
|
|
11
|
+
"gt" => ->(a, b) { a > b },
|
|
12
|
+
"gte" => ->(a, b) { a >= b }
|
|
13
|
+
}.freeze
|
|
14
|
+
|
|
15
|
+
def initialize(config)
|
|
16
|
+
@field = config["field"]
|
|
17
|
+
@operator = config["operator"]
|
|
18
|
+
@value_config = config["value"]
|
|
19
|
+
@cast = config["cast"] || "string"
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def apply(data, params)
|
|
23
|
+
threshold = resolve_value(params)
|
|
24
|
+
operator_fn = OPERATORS[@operator]
|
|
25
|
+
|
|
26
|
+
raise ArgumentError, "Unknown operator: #{@operator}" unless operator_fn
|
|
27
|
+
|
|
28
|
+
data.select do |row|
|
|
29
|
+
field_value = cast_value(row[@field], @cast)
|
|
30
|
+
operator_fn.call(field_value, threshold)
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
private
|
|
35
|
+
|
|
36
|
+
def resolve_value(params)
|
|
37
|
+
value = case @value_config["source"]
|
|
38
|
+
when "config"
|
|
39
|
+
PgReports.config.public_send(@value_config["key"])
|
|
40
|
+
when "param"
|
|
41
|
+
params[@value_config["key"].to_sym]
|
|
42
|
+
else
|
|
43
|
+
raise ArgumentError, "Unknown value source: #{@value_config["source"]}"
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
cast_value(value, @cast)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def cast_value(value, type)
|
|
50
|
+
case type
|
|
51
|
+
when "integer" then value.to_i
|
|
52
|
+
when "float" then value.to_f
|
|
53
|
+
when "string" then value.to_s
|
|
54
|
+
else value
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module PgReports
|
|
4
|
+
# Generates module methods dynamically from YAML report definitions
|
|
5
|
+
class ModuleGenerator
|
|
6
|
+
def self.generate!
|
|
7
|
+
ReportLoader.load_all.each do |module_name, reports|
|
|
8
|
+
module_class = get_module(module_name)
|
|
9
|
+
next unless module_class
|
|
10
|
+
|
|
11
|
+
reports.each do |report_name, definition|
|
|
12
|
+
define_report_method(module_class, report_name, definition)
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
private
|
|
18
|
+
|
|
19
|
+
def self.get_module(module_name)
|
|
20
|
+
PgReports::Modules.const_get(module_name.capitalize)
|
|
21
|
+
rescue NameError
|
|
22
|
+
# Module doesn't exist, skip it
|
|
23
|
+
# We don't auto-create modules to avoid conflicts
|
|
24
|
+
nil
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def self.define_report_method(module_class, report_name, definition)
|
|
28
|
+
params_config = definition.config["parameters"] || {}
|
|
29
|
+
|
|
30
|
+
# Extract default parameter values
|
|
31
|
+
defaults = params_config.transform_values { |v| v["default"] }
|
|
32
|
+
|
|
33
|
+
# Define the method on the module
|
|
34
|
+
# We capture the definition in a local variable to avoid closure issues
|
|
35
|
+
captured_definition = definition
|
|
36
|
+
captured_defaults = defaults
|
|
37
|
+
|
|
38
|
+
module_class.define_singleton_method(report_name) do |**params|
|
|
39
|
+
merged_params = captured_defaults.merge(params)
|
|
40
|
+
captured_definition.generate_report(**merged_params)
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
@@ -3,82 +3,17 @@
|
|
|
3
3
|
module PgReports
|
|
4
4
|
module Modules
|
|
5
5
|
# Connection and lock analysis module
|
|
6
|
+
# Most report methods are generated from YAML definitions in lib/pg_reports/definitions/connections/
|
|
6
7
|
module Connections
|
|
7
8
|
extend self
|
|
8
9
|
|
|
9
|
-
#
|
|
10
|
-
#
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
data: data,
|
|
17
|
-
columns: %w[pid database username application state query_start state_change query]
|
|
18
|
-
)
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
# Connection statistics by state
|
|
22
|
-
# @return [Report] Report with connection counts by state
|
|
23
|
-
def connection_stats
|
|
24
|
-
data = executor.execute_from_file(:connections, :connection_stats)
|
|
25
|
-
|
|
26
|
-
Report.new(
|
|
27
|
-
title: "Connection Statistics",
|
|
28
|
-
data: data,
|
|
29
|
-
columns: %w[database state count]
|
|
30
|
-
)
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
# Long running queries
|
|
34
|
-
# @return [Report] Report with long running queries
|
|
35
|
-
def long_running_queries(min_duration_seconds: 60)
|
|
36
|
-
data = executor.execute_from_file(:connections, :long_running_queries)
|
|
37
|
-
|
|
38
|
-
filtered = data.select { |row| row["duration_seconds"].to_f >= min_duration_seconds }
|
|
39
|
-
|
|
40
|
-
Report.new(
|
|
41
|
-
title: "Long Running Queries (>= #{min_duration_seconds}s)",
|
|
42
|
-
data: filtered,
|
|
43
|
-
columns: %w[pid database username duration_seconds state query]
|
|
44
|
-
)
|
|
45
|
-
end
|
|
46
|
-
|
|
47
|
-
# Blocking queries - queries that are blocking others
|
|
48
|
-
# @return [Report] Report with blocking queries
|
|
49
|
-
def blocking_queries
|
|
50
|
-
data = executor.execute_from_file(:connections, :blocking_queries)
|
|
51
|
-
|
|
52
|
-
Report.new(
|
|
53
|
-
title: "Blocking Queries",
|
|
54
|
-
data: data,
|
|
55
|
-
columns: %w[blocked_pid blocking_pid blocked_query blocking_query blocked_duration]
|
|
56
|
-
)
|
|
57
|
-
end
|
|
58
|
-
|
|
59
|
-
# Lock statistics
|
|
60
|
-
# @return [Report] Report with lock statistics
|
|
61
|
-
def locks
|
|
62
|
-
data = executor.execute_from_file(:connections, :locks)
|
|
63
|
-
|
|
64
|
-
Report.new(
|
|
65
|
-
title: "Current Locks",
|
|
66
|
-
data: data,
|
|
67
|
-
columns: %w[pid database relation locktype mode granted waiting]
|
|
68
|
-
)
|
|
69
|
-
end
|
|
70
|
-
|
|
71
|
-
# Idle connections
|
|
72
|
-
# @return [Report] Report with idle connections
|
|
73
|
-
def idle_connections
|
|
74
|
-
data = executor.execute_from_file(:connections, :idle_connections)
|
|
75
|
-
|
|
76
|
-
Report.new(
|
|
77
|
-
title: "Idle Connections",
|
|
78
|
-
data: data,
|
|
79
|
-
columns: %w[pid database username application idle_duration state_change]
|
|
80
|
-
)
|
|
81
|
-
end
|
|
10
|
+
# The following methods are auto-generated from YAML:
|
|
11
|
+
# - active_connections
|
|
12
|
+
# - connection_stats
|
|
13
|
+
# - long_running_queries(min_duration_seconds: 60)
|
|
14
|
+
# - blocking_queries
|
|
15
|
+
# - locks
|
|
16
|
+
# - idle_connections
|
|
82
17
|
|
|
83
18
|
# Kill a specific backend process
|
|
84
19
|
# @param pid [Integer] Process ID to terminate
|
|
@@ -3,103 +3,18 @@
|
|
|
3
3
|
module PgReports
|
|
4
4
|
module Modules
|
|
5
5
|
# Index analysis module
|
|
6
|
+
# All report methods are generated from YAML definitions in lib/pg_reports/definitions/indexes/
|
|
6
7
|
module Indexes
|
|
7
8
|
extend self
|
|
8
9
|
|
|
9
|
-
#
|
|
10
|
-
#
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
Report.new(
|
|
19
|
-
title: "Unused Indexes (scans <= #{threshold})",
|
|
20
|
-
data: filtered,
|
|
21
|
-
columns: %w[schema table_name index_name idx_scan index_size_mb]
|
|
22
|
-
)
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
# Duplicate indexes - indexes that may be redundant
|
|
26
|
-
# @return [Report] Report with duplicate indexes
|
|
27
|
-
def duplicate_indexes
|
|
28
|
-
data = executor.execute_from_file(:indexes, :duplicate_indexes)
|
|
29
|
-
|
|
30
|
-
Report.new(
|
|
31
|
-
title: "Duplicate Indexes",
|
|
32
|
-
data: data,
|
|
33
|
-
columns: %w[table_name index_name duplicate_of index_size_mb]
|
|
34
|
-
)
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
# Invalid indexes - indexes that are not valid (e.g., failed to build)
|
|
38
|
-
# @return [Report] Report with invalid indexes
|
|
39
|
-
def invalid_indexes
|
|
40
|
-
data = executor.execute_from_file(:indexes, :invalid_indexes)
|
|
41
|
-
|
|
42
|
-
Report.new(
|
|
43
|
-
title: "Invalid Indexes",
|
|
44
|
-
data: data,
|
|
45
|
-
columns: %w[schema table_name index_name index_definition]
|
|
46
|
-
)
|
|
47
|
-
end
|
|
48
|
-
|
|
49
|
-
# Missing indexes - tables with high sequential scans
|
|
50
|
-
# @return [Report] Report suggesting missing indexes
|
|
51
|
-
def missing_indexes(limit: 20)
|
|
52
|
-
data = executor.execute_from_file(:indexes, :missing_indexes)
|
|
53
|
-
.first(limit)
|
|
54
|
-
|
|
55
|
-
Report.new(
|
|
56
|
-
title: "Tables Potentially Missing Indexes",
|
|
57
|
-
data: data,
|
|
58
|
-
columns: %w[schema table_name seq_scan seq_tup_read idx_scan table_size_mb]
|
|
59
|
-
)
|
|
60
|
-
end
|
|
61
|
-
|
|
62
|
-
# Index usage statistics
|
|
63
|
-
# @return [Report] Report with index usage statistics
|
|
64
|
-
def index_usage(limit: 50)
|
|
65
|
-
data = executor.execute_from_file(:indexes, :index_usage)
|
|
66
|
-
.first(limit)
|
|
67
|
-
|
|
68
|
-
Report.new(
|
|
69
|
-
title: "Index Usage Statistics",
|
|
70
|
-
data: data,
|
|
71
|
-
columns: %w[schema table_name index_name idx_scan idx_tup_read index_size_mb]
|
|
72
|
-
)
|
|
73
|
-
end
|
|
74
|
-
|
|
75
|
-
# Bloated indexes - indexes with high bloat
|
|
76
|
-
# @return [Report] Report with bloated indexes
|
|
77
|
-
def bloated_indexes(limit: 20)
|
|
78
|
-
data = executor.execute_from_file(:indexes, :bloated_indexes)
|
|
79
|
-
threshold = PgReports.config.bloat_threshold_percent
|
|
80
|
-
|
|
81
|
-
filtered = data.select { |row| row["bloat_percent"].to_f >= threshold }
|
|
82
|
-
.first(limit)
|
|
83
|
-
|
|
84
|
-
Report.new(
|
|
85
|
-
title: "Bloated Indexes (bloat >= #{threshold}%)",
|
|
86
|
-
data: filtered,
|
|
87
|
-
columns: %w[schema table_name index_name index_size_mb bloat_size_mb bloat_percent]
|
|
88
|
-
)
|
|
89
|
-
end
|
|
90
|
-
|
|
91
|
-
# Index sizes
|
|
92
|
-
# @return [Report] Report with index sizes
|
|
93
|
-
def index_sizes(limit: 50)
|
|
94
|
-
data = executor.execute_from_file(:indexes, :index_sizes)
|
|
95
|
-
.first(limit)
|
|
96
|
-
|
|
97
|
-
Report.new(
|
|
98
|
-
title: "Index Sizes (top #{limit})",
|
|
99
|
-
data: data,
|
|
100
|
-
columns: %w[schema table_name index_name index_size_mb]
|
|
101
|
-
)
|
|
102
|
-
end
|
|
10
|
+
# The following methods are auto-generated from YAML:
|
|
11
|
+
# - unused_indexes(limit: 50)
|
|
12
|
+
# - duplicate_indexes
|
|
13
|
+
# - invalid_indexes
|
|
14
|
+
# - missing_indexes(limit: 20)
|
|
15
|
+
# - index_usage(limit: 50)
|
|
16
|
+
# - bloated_indexes(limit: 20)
|
|
17
|
+
# - index_sizes(limit: 50)
|
|
103
18
|
|
|
104
19
|
private
|
|
105
20
|
|