pg_insights 0.3.2 → 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/app/assets/javascripts/pg_insights/application.js +91 -21
- data/app/assets/javascripts/pg_insights/plan_performance.js +53 -0
- data/app/assets/javascripts/pg_insights/query_comparison.js +1129 -0
- data/app/assets/javascripts/pg_insights/results/view_toggles.js +26 -5
- data/app/assets/javascripts/pg_insights/results.js +231 -1
- data/app/assets/stylesheets/pg_insights/analysis.css +2628 -0
- data/app/assets/stylesheets/pg_insights/application.css +51 -1
- data/app/assets/stylesheets/pg_insights/results.css +12 -1
- data/app/controllers/pg_insights/insights_controller.rb +486 -9
- data/app/helpers/pg_insights/application_helper.rb +339 -0
- data/app/helpers/pg_insights/insights_helper.rb +567 -0
- data/app/jobs/pg_insights/query_analysis_job.rb +142 -0
- data/app/models/pg_insights/query_execution.rb +198 -0
- data/app/services/pg_insights/query_analysis_service.rb +269 -0
- data/app/views/layouts/pg_insights/application.html.erb +2 -0
- data/app/views/pg_insights/insights/_compare_view.html.erb +264 -0
- data/app/views/pg_insights/insights/_empty_state.html.erb +9 -0
- data/app/views/pg_insights/insights/_execution_table_view.html.erb +86 -0
- data/app/views/pg_insights/insights/_history_bar.html.erb +33 -0
- data/app/views/pg_insights/insights/_perf_view.html.erb +244 -0
- data/app/views/pg_insights/insights/_plan_nodes.html.erb +12 -0
- data/app/views/pg_insights/insights/_plan_tree.html.erb +30 -0
- data/app/views/pg_insights/insights/_plan_tree_modern.html.erb +12 -0
- data/app/views/pg_insights/insights/_plan_view.html.erb +159 -0
- data/app/views/pg_insights/insights/_query_panel.html.erb +3 -2
- data/app/views/pg_insights/insights/_result.html.erb +19 -4
- data/app/views/pg_insights/insights/_results_info.html.erb +33 -9
- data/app/views/pg_insights/insights/_results_info_empty.html.erb +10 -0
- data/app/views/pg_insights/insights/_results_panel.html.erb +7 -9
- data/app/views/pg_insights/insights/_results_table.html.erb +0 -5
- data/app/views/pg_insights/insights/_visual_view.html.erb +212 -0
- data/app/views/pg_insights/insights/index.html.erb +4 -1
- data/app/views/pg_insights/timeline/compare.html.erb +3 -3
- data/config/routes.rb +6 -0
- data/lib/generators/pg_insights/install_generator.rb +20 -14
- data/lib/generators/pg_insights/templates/db/migrate/create_pg_insights_query_executions.rb +45 -0
- data/lib/pg_insights/version.rb +1 -1
- data/lib/pg_insights.rb +30 -2
- metadata +20 -2
@@ -16,6 +16,7 @@ module PgInsights
|
|
16
16
|
def copy_migrations
|
17
17
|
copy_queries_migration
|
18
18
|
copy_health_check_results_migration
|
19
|
+
copy_query_executions_migration
|
19
20
|
end
|
20
21
|
|
21
22
|
def create_initializer
|
@@ -53,7 +54,7 @@ module PgInsights
|
|
53
54
|
puts ""
|
54
55
|
puts "Features available:"
|
55
56
|
puts "• Health Dashboard: '/pg_insights/health' - Monitor database health"
|
56
|
-
puts "• Query Runner: '/pg_insights' - Run SQL queries with charts"
|
57
|
+
puts "• Query Runner: '/pg_insights' - Run SQL queries with charts & EXPLAIN ANALYZE"
|
57
58
|
puts "• Timeline: '/pg_insights/timeline' - Track parameter changes over time"
|
58
59
|
puts ""
|
59
60
|
puts "Useful commands:"
|
@@ -98,22 +99,17 @@ module PgInsights
|
|
98
99
|
config.background_job_queue = :pg_insights_health
|
99
100
|
|
100
101
|
# === Cache and Timeout Settings ===
|
101
|
-
# How long to cache health check results before considering them stale
|
102
|
-
# Stale results will trigger background refresh when accessed
|
103
|
-
#
|
104
|
-
# Default: 5.minutes
|
105
102
|
config.health_cache_expiry = 5.minutes
|
106
103
|
|
107
|
-
#
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
config.health_check_timeout = 10.seconds
|
104
|
+
# === Query Execution Timeouts ===
|
105
|
+
config.query_execution_timeout = 30.seconds
|
106
|
+
config.query_analysis_timeout = 60.seconds
|
107
|
+
config.health_check_timeout = 20.seconds
|
112
108
|
|
113
|
-
#
|
114
|
-
#
|
115
|
-
#
|
116
|
-
config.
|
109
|
+
# === Heavy Analytics Environment ===
|
110
|
+
# config.query_execution_timeout = 120.seconds
|
111
|
+
# config.query_analysis_timeout = 180.seconds
|
112
|
+
# config.health_check_timeout = 60.seconds
|
117
113
|
|
118
114
|
# === Timeline & Snapshot Settings ===
|
119
115
|
#
|
@@ -217,6 +213,16 @@ module PgInsights
|
|
217
213
|
end
|
218
214
|
end
|
219
215
|
|
216
|
+
def copy_query_executions_migration
|
217
|
+
if migration_exists?("create_pg_insights_query_executions")
|
218
|
+
say_status("skipped", "Migration 'create_pg_insights_query_executions' already exists", :yellow)
|
219
|
+
else
|
220
|
+
puts "Copying PgInsights query executions migration..."
|
221
|
+
migration_template "db/migrate/create_pg_insights_query_executions.rb",
|
222
|
+
"db/migrate/create_pg_insights_query_executions.rb"
|
223
|
+
end
|
224
|
+
end
|
225
|
+
|
220
226
|
def migration_exists?(migration_name)
|
221
227
|
migrate_path = File.join(destination_root, "db", "migrate")
|
222
228
|
return false unless File.directory?(migrate_path)
|
@@ -0,0 +1,45 @@
|
|
1
|
+
class CreatePgInsightsQueryExecutions < ActiveRecord::Migration[7.0]
|
2
|
+
def change
|
3
|
+
create_table :pg_insights_query_executions do |t|
|
4
|
+
t.references :query, null: true, foreign_key: { to_table: :pg_insights_queries }
|
5
|
+
t.text :sql_text, null: false
|
6
|
+
t.string :execution_type, null: false, default: "execute" # 'execute', 'analyze', 'both'
|
7
|
+
t.string :status, null: false, default: "pending" # 'pending', 'running', 'completed', 'failed'
|
8
|
+
|
9
|
+
# Execution Results
|
10
|
+
t.json :result_data, null: true # Query results when execution_type includes 'execute'
|
11
|
+
t.integer :result_rows_count, null: true
|
12
|
+
t.integer :result_columns_count, null: true
|
13
|
+
|
14
|
+
# Analysis Results
|
15
|
+
t.json :execution_plan, null: true # EXPLAIN ANALYZE output
|
16
|
+
t.text :plan_summary, null: true # Human-readable summary
|
17
|
+
|
18
|
+
# Performance Metrics
|
19
|
+
t.decimal :planning_time_ms, precision: 10, scale: 3, null: true
|
20
|
+
t.decimal :execution_time_ms, precision: 10, scale: 3, null: true
|
21
|
+
t.decimal :total_time_ms, precision: 10, scale: 3, null: true
|
22
|
+
t.decimal :query_cost, precision: 15, scale: 3, null: true
|
23
|
+
|
24
|
+
# Analysis Metadata
|
25
|
+
t.json :performance_insights, null: true # Optimization suggestions
|
26
|
+
t.json :execution_stats, null: true # Buffer usage, cache hits, etc.
|
27
|
+
|
28
|
+
# Error handling
|
29
|
+
t.text :error_message, null: true
|
30
|
+
t.text :error_detail, null: true
|
31
|
+
|
32
|
+
# Audit fields
|
33
|
+
t.timestamp :started_at, null: true
|
34
|
+
t.timestamp :completed_at, null: true
|
35
|
+
t.decimal :duration_ms, precision: 10, scale: 3, null: true
|
36
|
+
|
37
|
+
t.timestamps
|
38
|
+
end
|
39
|
+
|
40
|
+
add_index :pg_insights_query_executions, :execution_type
|
41
|
+
add_index :pg_insights_query_executions, :status
|
42
|
+
add_index :pg_insights_query_executions, :created_at
|
43
|
+
add_index :pg_insights_query_executions, [ :query_id, :created_at ]
|
44
|
+
end
|
45
|
+
end
|
data/lib/pg_insights/version.rb
CHANGED
data/lib/pg_insights.rb
CHANGED
@@ -5,8 +5,10 @@ module PgInsights
|
|
5
5
|
mattr_accessor :enable_background_jobs, default: true
|
6
6
|
mattr_accessor :health_cache_expiry, default: 5.minutes
|
7
7
|
mattr_accessor :background_job_queue, default: :pg_insights_health
|
8
|
-
|
9
|
-
mattr_accessor :
|
8
|
+
|
9
|
+
mattr_accessor :query_execution_timeout, default: 30.seconds
|
10
|
+
mattr_accessor :query_analysis_timeout, default: 60.seconds
|
11
|
+
mattr_accessor :health_check_timeout, default: 20.seconds
|
10
12
|
|
11
13
|
mattr_accessor :enable_snapshots, default: true
|
12
14
|
mattr_accessor :snapshot_frequency, default: 1.day
|
@@ -17,6 +19,32 @@ module PgInsights
|
|
17
19
|
yield self
|
18
20
|
end
|
19
21
|
|
22
|
+
def self.query_execution_timeout_ms
|
23
|
+
validate_timeout(query_execution_timeout, "query_execution_timeout")
|
24
|
+
(query_execution_timeout.to_f * 1000).to_i
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.query_analysis_timeout_ms
|
28
|
+
validate_timeout(query_analysis_timeout, "query_analysis_timeout")
|
29
|
+
(query_analysis_timeout.to_f * 1000).to_i
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.health_check_timeout_ms
|
33
|
+
validate_timeout(health_check_timeout, "health_check_timeout")
|
34
|
+
(health_check_timeout.to_f * 1000).to_i
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def self.validate_timeout(timeout, setting_name)
|
40
|
+
unless timeout.respond_to?(:to_f) && timeout.to_f > 0
|
41
|
+
raise ArgumentError, "#{setting_name} must be a positive number (seconds). Got: #{timeout.inspect}"
|
42
|
+
end
|
43
|
+
if timeout.to_f > 300
|
44
|
+
Rails.logger.warn "PgInsights: #{setting_name} is set to #{timeout.to_f}s, which is very high. Consider optimizing queries instead."
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
20
48
|
def self.background_jobs_available?
|
21
49
|
return false unless enable_background_jobs
|
22
50
|
return false unless defined?(ActiveJob::Base)
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: pg_insights
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Mezbah Alam
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2025-
|
11
|
+
date: 2025-08-04 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -51,10 +51,13 @@ files:
|
|
51
51
|
- Rakefile
|
52
52
|
- app/assets/javascripts/pg_insights/application.js
|
53
53
|
- app/assets/javascripts/pg_insights/health.js
|
54
|
+
- app/assets/javascripts/pg_insights/plan_performance.js
|
55
|
+
- app/assets/javascripts/pg_insights/query_comparison.js
|
54
56
|
- app/assets/javascripts/pg_insights/results.js
|
55
57
|
- app/assets/javascripts/pg_insights/results/chart_renderer.js
|
56
58
|
- app/assets/javascripts/pg_insights/results/table_manager.js
|
57
59
|
- app/assets/javascripts/pg_insights/results/view_toggles.js
|
60
|
+
- app/assets/stylesheets/pg_insights/analysis.css
|
58
61
|
- app/assets/stylesheets/pg_insights/application.css
|
59
62
|
- app/assets/stylesheets/pg_insights/health.css
|
60
63
|
- app/assets/stylesheets/pg_insights/results.css
|
@@ -69,25 +72,39 @@ files:
|
|
69
72
|
- app/jobs/pg_insights/database_snapshot_job.rb
|
70
73
|
- app/jobs/pg_insights/health_check_job.rb
|
71
74
|
- app/jobs/pg_insights/health_check_scheduler_job.rb
|
75
|
+
- app/jobs/pg_insights/query_analysis_job.rb
|
72
76
|
- app/jobs/pg_insights/recurring_health_checks_job.rb
|
73
77
|
- app/models/pg_insights/application_record.rb
|
74
78
|
- app/models/pg_insights/health_check_result.rb
|
75
79
|
- app/models/pg_insights/query.rb
|
80
|
+
- app/models/pg_insights/query_execution.rb
|
76
81
|
- app/services/pg_insights/health_check_service.rb
|
77
82
|
- app/services/pg_insights/insight_query_service.rb
|
83
|
+
- app/services/pg_insights/query_analysis_service.rb
|
78
84
|
- app/views/layouts/pg_insights/application.html.erb
|
79
85
|
- app/views/pg_insights/health/index.html.erb
|
80
86
|
- app/views/pg_insights/insights/_chart_view.html.erb
|
81
87
|
- app/views/pg_insights/insights/_column_panel.html.erb
|
88
|
+
- app/views/pg_insights/insights/_compare_view.html.erb
|
89
|
+
- app/views/pg_insights/insights/_empty_state.html.erb
|
90
|
+
- app/views/pg_insights/insights/_execution_table_view.html.erb
|
91
|
+
- app/views/pg_insights/insights/_history_bar.html.erb
|
92
|
+
- app/views/pg_insights/insights/_perf_view.html.erb
|
93
|
+
- app/views/pg_insights/insights/_plan_nodes.html.erb
|
94
|
+
- app/views/pg_insights/insights/_plan_tree.html.erb
|
95
|
+
- app/views/pg_insights/insights/_plan_tree_modern.html.erb
|
96
|
+
- app/views/pg_insights/insights/_plan_view.html.erb
|
82
97
|
- app/views/pg_insights/insights/_query_examples.html.erb
|
83
98
|
- app/views/pg_insights/insights/_query_panel.html.erb
|
84
99
|
- app/views/pg_insights/insights/_result.html.erb
|
85
100
|
- app/views/pg_insights/insights/_results_info.html.erb
|
101
|
+
- app/views/pg_insights/insights/_results_info_empty.html.erb
|
86
102
|
- app/views/pg_insights/insights/_results_panel.html.erb
|
87
103
|
- app/views/pg_insights/insights/_results_table.html.erb
|
88
104
|
- app/views/pg_insights/insights/_stats_view.html.erb
|
89
105
|
- app/views/pg_insights/insights/_table_controls.html.erb
|
90
106
|
- app/views/pg_insights/insights/_table_view.html.erb
|
107
|
+
- app/views/pg_insights/insights/_visual_view.html.erb
|
91
108
|
- app/views/pg_insights/insights/index.html.erb
|
92
109
|
- app/views/pg_insights/timeline/compare.html.erb
|
93
110
|
- app/views/pg_insights/timeline/index.html.erb
|
@@ -98,6 +115,7 @@ files:
|
|
98
115
|
- lib/generators/pg_insights/install_generator.rb
|
99
116
|
- lib/generators/pg_insights/templates/db/migrate/create_pg_insights_health_check_results.rb
|
100
117
|
- lib/generators/pg_insights/templates/db/migrate/create_pg_insights_queries.rb
|
118
|
+
- lib/generators/pg_insights/templates/db/migrate/create_pg_insights_query_executions.rb
|
101
119
|
- lib/pg_insights.rb
|
102
120
|
- lib/pg_insights/engine.rb
|
103
121
|
- lib/pg_insights/version.rb
|