pg_reports 0.6.0 → 0.6.1

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.
Files changed (30) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +29 -0
  3. data/README.md +122 -377
  4. data/app/controllers/pg_reports/dashboard_controller.rb +21 -21
  5. data/app/views/layouts/pg_reports/application.html.erb +65 -8
  6. data/app/views/pg_reports/dashboard/_show_modals.html.erb +22 -22
  7. data/app/views/pg_reports/dashboard/_show_scripts.html.erb +55 -57
  8. data/app/views/pg_reports/dashboard/_show_styles.html.erb +18 -0
  9. data/app/views/pg_reports/dashboard/index.html.erb +109 -106
  10. data/app/views/pg_reports/dashboard/show.html.erb +26 -26
  11. data/config/locales/en.yml +488 -0
  12. data/config/locales/ru.yml +481 -0
  13. data/config/locales/uk.yml +481 -0
  14. data/lib/pg_reports/compatibility.rb +3 -3
  15. data/lib/pg_reports/dashboard/reports_registry.rb +83 -12
  16. data/lib/pg_reports/definitions/schema_analysis/always_null_columns.yml +31 -0
  17. data/lib/pg_reports/definitions/schema_analysis/unused_columns.yml +32 -0
  18. data/lib/pg_reports/definitions/tables/unused_tables.yml +30 -0
  19. data/lib/pg_reports/definitions/tables/update_hotspots.yml +32 -0
  20. data/lib/pg_reports/module_generator.rb +2 -1
  21. data/lib/pg_reports/modules/schema_analysis.rb +261 -2
  22. data/lib/pg_reports/modules/system.rb +3 -3
  23. data/lib/pg_reports/query_monitor.rb +2 -6
  24. data/lib/pg_reports/report_definition.rb +20 -24
  25. data/lib/pg_reports/sql/schema_analysis/always_null_columns.sql +25 -0
  26. data/lib/pg_reports/sql/schema_analysis/unused_columns.sql +36 -0
  27. data/lib/pg_reports/sql/tables/unused_tables.sql +19 -0
  28. data/lib/pg_reports/sql/tables/update_hotspots.sql +26 -0
  29. data/lib/pg_reports/version.rb +1 -1
  30. metadata +9 -1
data/README.md CHANGED
@@ -29,161 +29,119 @@ A comprehensive PostgreSQL monitoring and analysis library for Rails application
29
29
 
30
30
  ## Installation
31
31
 
32
- Add to your Gemfile:
33
-
34
32
  ```ruby
33
+ # Gemfile
35
34
  gem "pg_reports"
36
-
37
- # Optional: for Telegram support
38
- gem "telegram-bot-ruby"
35
+ gem "telegram-bot-ruby" # optional, for Telegram delivery
39
36
  ```
40
37
 
41
- Run:
42
-
43
38
  ```bash
44
39
  bundle install
45
40
  ```
46
41
 
47
- ## Quick Start
48
-
49
- ### Mount the Dashboard
50
-
51
- Add to your `config/routes.rb`:
42
+ Mount the dashboard:
52
43
 
53
44
  ```ruby
45
+ # config/routes.rb
54
46
  Rails.application.routes.draw do
55
- # Mount in development only (recommended)
56
47
  if Rails.env.development?
57
48
  mount PgReports::Engine, at: "/pg_reports"
58
49
  end
59
50
 
60
- # Or with authentication
61
- authenticate :user, ->(u) { u.admin? } do
62
- mount PgReports::Engine, at: "/pg_reports"
63
- end
51
+ # Or with authentication:
52
+ # authenticate :user, ->(u) { u.admin? } do
53
+ # mount PgReports::Engine, at: "/pg_reports"
54
+ # end
64
55
  end
65
56
  ```
66
57
 
67
- Visit `http://localhost:3000/pg_reports` to access the dashboard.
58
+ Visit `http://localhost:3000/pg_reports`.
59
+
60
+ For query analysis, also enable `pg_stat_statements` — see [setup](#pg_stat_statements-setup) below.
68
61
 
69
- ### Use in Console or Code
62
+ ## Usage
70
63
 
71
64
  ```ruby
72
- # Get slow queries
65
+ # In console or code
73
66
  PgReports.slow_queries.display
67
+ PgReports.unused_indexes.each { |row| puts row["index_name"] }
74
68
 
75
- # Get unused indexes
76
- report = PgReports.unused_indexes
77
- report.each { |row| puts row["index_name"] }
69
+ # Export
70
+ report = PgReports.expensive_queries
71
+ report.to_text
72
+ report.to_csv
73
+ report.to_a
78
74
 
79
- # Export to different formats
80
- report.to_text # Plain text
81
- report.to_csv # CSV
82
- report.to_a # Array of hashes
83
-
84
- # Send to Telegram
85
- PgReports.expensive_queries.send_to_telegram
86
-
87
- # Health report
88
- PgReports.health_report.display
75
+ # Telegram
76
+ PgReports.slow_queries.send_to_telegram
89
77
  ```
90
78
 
91
- ## Configuration
79
+ **[Full list of reports →](docs/reports.md)**
92
80
 
93
- Create an initializer `config/initializers/pg_reports.rb`:
81
+ ## Configuration
94
82
 
95
83
  ```ruby
84
+ # config/initializers/pg_reports.rb
96
85
  PgReports.configure do |config|
97
86
  # Telegram (optional)
98
87
  config.telegram_bot_token = ENV["PG_REPORTS_TELEGRAM_TOKEN"]
99
- config.telegram_chat_id = ENV["PG_REPORTS_TELEGRAM_CHAT_ID"]
88
+ config.telegram_chat_id = ENV["PG_REPORTS_TELEGRAM_CHAT_ID"]
100
89
 
101
- # Query thresholds
102
- config.slow_query_threshold_ms = 100 # Queries slower than this
103
- config.heavy_query_threshold_calls = 1000 # Queries with more calls
104
- config.expensive_query_threshold_ms = 10000 # Total time threshold
90
+ # Thresholds
91
+ config.slow_query_threshold_ms = 100
92
+ config.heavy_query_threshold_calls = 1000
93
+ config.expensive_query_threshold_ms = 10_000
94
+ config.unused_index_threshold_scans = 50
95
+ config.bloat_threshold_percent = 20
96
+ config.dead_rows_threshold = 10_000
105
97
 
106
- # Index thresholds
107
- config.unused_index_threshold_scans = 50 # Index with fewer scans
98
+ # Output
99
+ config.max_query_length = 200
108
100
 
109
- # Table thresholds
110
- config.bloat_threshold_percent = 20 # Tables with more bloat
111
- config.dead_rows_threshold = 10000 # Dead rows needing vacuum
112
-
113
- # Output settings
114
- config.max_query_length = 200 # Truncate queries in text output
115
-
116
- # Dashboard authentication (optional)
117
- config.dashboard_auth = -> {
101
+ # Auth (optional)
102
+ config.dashboard_auth = -> {
118
103
  authenticate_or_request_with_http_basic do |user, pass|
119
- user == "admin" && pass == "secret"
104
+ user == ENV["PG_REPORTS_USER"] && pass == ENV["PG_REPORTS_PASSWORD"]
120
105
  end
121
106
  }
122
107
 
123
- # External fonts (Google Fonts)
124
- # Default: false (no external requests)
125
- config.load_external_fonts = ENV["PG_REPORTS_LOAD_EXTERNAL_FONTS"] == "true"
126
- # or simply:
127
- # config.load_external_fonts = true
128
-
108
+ # Google Fonts (default: false — no external requests)
109
+ config.load_external_fonts = false
129
110
  end
130
111
  ```
131
112
 
132
- ### Query Execution Security
133
-
134
- ⚠️ **Security Warning**: By default, the dashboard **does not allow** executing raw SQL queries via "Execute Query" and "EXPLAIN ANALYZE" buttons. This prevents accidental or malicious query execution in production environments.
113
+ <details>
114
+ <summary><strong>Locale (EN / RU / UK)</strong></summary>
135
115
 
136
- To enable query execution (only in secure environments):
137
-
138
- ```ruby
139
- PgReports.configure do |config|
140
- # Enable query execution from dashboard (default: false)
141
- config.allow_raw_query_execution = true
142
- end
143
- ```
116
+ PgReports follows your application's `I18n.locale`. Set it the way you set it for the rest of the app — there's no PgReports-specific knob. The dashboard supports `en`, `ru`, and `uk` out of the box.
144
117
 
145
- Or via environment variable:
118
+ </details>
146
119
 
147
- ```bash
148
- export PG_REPORTS_ALLOW_RAW_QUERY_EXECUTION=true
149
- ```
120
+ <details>
121
+ <summary><strong>Raw query execution (EXPLAIN ANALYZE / Execute Query)</strong></summary>
150
122
 
151
- **Recommended setup** (only enable in development/staging):
123
+ ⚠️ Disabled by default. The dashboard's "Execute Query" and "EXPLAIN ANALYZE" buttons require this opt-in.
152
124
 
153
125
  ```ruby
154
- # config/initializers/pg_reports.rb
155
126
  PgReports.configure do |config|
156
- # Only allow query execution in development/staging
157
127
  config.allow_raw_query_execution = Rails.env.development? || Rails.env.staging?
158
-
159
- # Combine with authentication for additional security
160
- config.dashboard_auth = -> {
161
- authenticate_or_request_with_http_basic do |user, pass|
162
- user == ENV["PG_REPORTS_USER"] && pass == ENV["PG_REPORTS_PASSWORD"]
163
- end
164
- }
165
128
  end
166
129
  ```
167
130
 
168
- When disabled:
169
- - API endpoints `/execute_query` and `/explain_analyze` return 403 Forbidden
170
- - UI buttons are disabled with explanation tooltips
171
- - Existing safety measures (SELECT/SHOW only, automatic LIMIT) still apply when enabled
131
+ </details>
172
132
 
173
- ## Query Source Tracking
133
+ <details>
134
+ <summary><strong>Query source tracking (Marginalia / Rails query logs)</strong></summary>
174
135
 
175
- PgReports automatically parses query annotations to show **where queries originated**. Works with:
136
+ PgReports parses query annotations to show **where queries originated**.
176
137
 
177
- ### Marginalia (recommended)
178
-
179
- If you use [marginalia](https://github.com/basecamp/marginalia), PgReports will automatically parse and display controller/action info in the **source** column.
138
+ [Marginalia](https://github.com/basecamp/marginalia):
180
139
 
181
140
  ```ruby
182
- # Gemfile
183
- gem 'marginalia'
141
+ gem "marginalia"
184
142
  ```
185
143
 
186
- ### Rails 7+ Query Logs
144
+ Rails 7+ query logs:
187
145
 
188
146
  ```ruby
189
147
  # config/application.rb
@@ -191,105 +149,25 @@ config.active_record.query_log_tags_enabled = true
191
149
  config.active_record.query_log_tags = [:controller, :action]
192
150
  ```
193
151
 
194
- ## Available Reports
195
-
196
- ### Queries (requires pg_stat_statements)
197
-
198
- | Method | Description |
199
- |--------|-------------|
200
- | `slow_queries` | Queries with high mean execution time |
201
- | `heavy_queries` | Most frequently called queries |
202
- | `expensive_queries` | Queries consuming most total time |
203
- | `missing_index_queries` | Queries potentially missing indexes |
204
- | `low_cache_hit_queries` | Queries with poor cache utilization |
205
- | `temp_file_queries` | 🆕 Queries spilling to disk via temporary files |
206
- | `all_queries` | All query statistics |
207
- | `reset_statistics!` | Reset pg_stat_statements data |
208
-
209
- ### Indexes
210
-
211
- | Method | Description |
212
- |--------|-------------|
213
- | `unused_indexes` | Indexes rarely or never scanned |
214
- | `duplicate_indexes` | Redundant indexes |
215
- | `invalid_indexes` | Indexes that failed to build |
216
- | `missing_indexes` | Tables potentially missing indexes |
217
- | `inefficient_indexes` | 🆕 Indexes with high read-to-fetch ratio (misaligned column order) |
218
- | `fk_without_indexes` | 🆕 Foreign keys missing indexes on child table |
219
- | `index_correlation` | 🆕 Low physical correlation causing random I/O |
220
- | `index_usage` | Index scan statistics |
221
- | `bloated_indexes` | Indexes with high bloat |
222
- | `index_sizes` | Index disk usage |
223
-
224
- ### Tables
225
-
226
- | Method | Description |
227
- |--------|-------------|
228
- | `table_sizes` | Table disk usage |
229
- | `bloated_tables` | Tables with high dead tuple ratio |
230
- | `vacuum_needed` | Tables needing vacuum |
231
- | `row_counts` | Table row counts |
232
- | `cache_hit_ratios` | Table cache statistics |
233
- | `seq_scans` | Tables with high sequential scans |
234
- | `tables_without_pk` | 🆕 Tables missing primary keys |
235
- | `recently_modified` | Tables with recent activity |
236
-
237
- ### Connections
238
-
239
- | Method | Description |
240
- |--------|-------------|
241
- | `active_connections` | Current database connections |
242
- | `connection_stats` | Connection statistics by state |
243
- | `long_running_queries` | Queries running for extended period |
244
- | `blocking_queries` | Queries blocking others |
245
- | `locks` | Current database locks |
246
- | `idle_connections` | Idle connections |
247
- | `pool_usage` | Connection pool utilization analysis |
248
- | `pool_wait_times` | Resource wait time analysis |
249
- | `pool_saturation` | Pool health warnings with recommendations |
250
- | `connection_churn` | Connection lifecycle and churn rate analysis |
251
- | `kill_connection(pid)` | Terminate a backend process |
252
- | `cancel_query(pid)` | Cancel a running query |
253
-
254
- ### System
255
-
256
- | Method | Description |
257
- |--------|-------------|
258
- | `database_sizes` | Size of all databases |
259
- | `settings` | PostgreSQL configuration |
260
- | `extensions` | Installed extensions |
261
- | `activity_overview` | Current activity summary |
262
- | `wraparound_risk` | 🆕 Transaction ID wraparound proximity |
263
- | `checkpoint_stats` | 🆕 Checkpoint and bgwriter statistics (PG 12–18+) |
264
- | `cache_stats` | Database cache statistics |
265
- | `pg_stat_statements_available?` | Check if extension is ready |
266
- | `enable_pg_stat_statements!` | Create the extension |
267
-
268
- ## pg_stat_statements Setup
269
-
270
- For query analysis, you need to enable `pg_stat_statements`:
152
+ Either form is auto-detected; controller/action and file:line appear in the **source** column on report rows.
153
+
154
+ </details>
155
+
156
+ ## pg_stat_statements setup
271
157
 
272
158
  1. Edit `postgresql.conf`:
273
159
  ```
274
160
  shared_preload_libraries = 'pg_stat_statements'
275
161
  pg_stat_statements.track = all
276
162
  ```
163
+ 2. Restart PostgreSQL: `sudo systemctl restart postgresql`
164
+ 3. Create the extension (via dashboard button or `PgReports.enable_pg_stat_statements!`).
277
165
 
278
- 2. Restart PostgreSQL:
279
- ```bash
280
- sudo systemctl restart postgresql
281
- ```
166
+ > PgReports does **not** require the `pg_read_all_settings` role — extension availability is detected directly. Works with CloudnativePG, managed databases, and other restricted environments.
282
167
 
283
- 3. Create extension (via dashboard or console):
284
- ```ruby
285
- PgReports.enable_pg_stat_statements!
286
- ```
168
+ ## Report object
287
169
 
288
- > **Note**: PgReports does **not** require the `pg_read_all_settings` role. It detects `pg_stat_statements` availability by directly querying the extension, making it compatible with CloudnativePG, managed databases, and other environments with restricted permissions.
289
-
290
- ## Report Object
291
-
292
- Every method returns a `PgReports::Report` object:
170
+ Every method returns a `PgReports::Report`:
293
171
 
294
172
  ```ruby
295
173
  report = PgReports.slow_queries
@@ -319,247 +197,114 @@ report.map { |row| row["query"] }
319
197
  report.select { |row| row["calls"] > 100 }
320
198
  ```
321
199
 
322
- ## Web Dashboard
323
-
324
- The dashboard provides:
325
-
326
- - 📊 Overview of all report categories with descriptions
327
- - ⚡ One-click report execution
328
- - 🔍 Filter parameters - adjust thresholds and limits on the fly
329
- - 🔍 Expandable rows for full query text
330
- - 📋 Copy query to clipboard
331
- - 📥 Download in multiple formats (TXT, CSV, JSON)
332
- - 📨 Send to Telegram
333
- - 🔧 pg_stat_statements management
334
- - 🔄 Sortable columns - click headers to sort ascending/descending
335
- - 📌 Save records for comparison - track before/after optimization results
336
- - 📊 EXPLAIN ANALYZE - run query plans directly from the dashboard
337
- - 🗑️ Migration generator - create Rails migrations to drop unused indexes
338
- - 🔗 IDE integration - click source locations to open in your IDE
339
-
340
- ### IDE Integration
200
+ ## Dashboard features
341
201
 
342
- Click on source locations in reports to open the file directly in your IDE. Supported IDEs:
202
+ The dashboard provides one-click execution, sortable columns, expandable rows, filter parameters, multi-format export, Telegram delivery, and pg_stat_statements management.
343
203
 
344
- - **VS Code (WSL)** - for Windows Subsystem for Linux
345
- - **VS Code** - direct path for native Linux/macOS
346
- - **RubyMine**
347
- - **IntelliJ IDEA**
348
- - **Cursor (WSL)** - for Windows Subsystem for Linux
349
- - **Cursor**
204
+ <details>
205
+ <summary><strong>EXPLAIN ANALYZE query plan analyzer</strong></summary>
350
206
 
351
- Use the ⚙️ button to set your default IDE and skip the selection menu.
207
+ Expand a row with a query, click **📊 EXPLAIN ANALYZE**. Shows:
352
208
 
353
- ### Filter Parameters
209
+ - **Status indicator** (🟢🟡🔴) — overall query health
210
+ - **Key metrics** — planning/execution time, cost, rows
211
+ - **Detected problems** — sequential scans on large tables, high-cost ops, sorts spilling to disk, slow sorts (>1s), inaccurate row estimates (>10× off), slow execution
212
+ - **Recommendations** for each issue
213
+ - **Color-coded plan** — node types tinted by performance impact (green: efficient, blue: normal, yellow: potential issue)
214
+ - **Line annotations** highlighting problems on specific plan lines
354
215
 
355
- Each report page includes a collapsible "Параметры фильтрации" (Filter Parameters) section where you can:
216
+ Queries from `pg_stat_statements` with parameter placeholders (`$1`, `$2`) prompt for parameter values before analysis.
356
217
 
357
- 1. **Adjust thresholds** - Override default thresholds (e.g., slow query threshold, unused index scans)
358
- 2. **Change limits** - Set the maximum number of results to display
359
- 3. **Real-time refresh** - Reports automatically refresh when you change parameters
218
+ Requires `config.allow_raw_query_execution = true`.
360
219
 
361
- Parameters show their current configured values and allow you to experiment with different thresholds without changing your configuration file.
220
+ </details>
362
221
 
363
- ### Save Records for Comparison
222
+ <details>
223
+ <summary><strong>SQL Query Monitor — real-time query capture</strong></summary>
364
224
 
365
- When optimizing queries, you can save records to compare before/after results:
225
+ Live capture of all SQL executed by your Rails app. Click **▶ Start Monitoring**, run any operation, watch the queries appear with:
366
226
 
367
- 1. Expand a row and click "📌 Save for Comparison"
368
- 2. Saved records appear above the results table
369
- 3. Click saved records to expand and see all details
370
- 4. Clear all or remove individual saved records
227
+ - SQL with syntax highlighting
228
+ - Duration (color-coded: 🟢 <10ms, 🟡 <100ms, 🔴 >100ms)
229
+ - Source location with click-to-IDE
230
+ - Timestamp
371
231
 
372
- Records are stored in browser localStorage per report type.
373
-
374
- ### EXPLAIN ANALYZE
375
-
376
- The advanced query analyzer provides intelligent problem detection and recommendations:
377
-
378
- 1. Expand a row with a query
379
- 2. Click "📊 EXPLAIN ANALYZE"
380
- 3. View the color-coded execution plan with:
381
- - **🟢🟡🔴 Status indicator** - Overall query health assessment
382
- - **📈 Key metrics** - Planning/Execution time, Cost, Rows
383
- - **⚠️ Detected problems** - Sequential scans, high costs, slow sorts, estimation errors
384
- - **💡 Recommendations** - Actionable advice for each issue
385
- - **🎨 Colored plan** - Node types color-coded by performance impact:
386
- - 🟢 Green: Efficient operations (Index Scan, Hash Join)
387
- - 🔵 Blue: Normal operations (Bitmap Scan, HashAggregate)
388
- - 🟡 Yellow: Potential issues (Seq Scan, Sort, Materialize)
389
- - **Line-by-line annotations** - Problems highlighted on specific plan lines
390
-
391
- **Problem Detection:**
392
- - Sequential scans on large tables (> 1000 rows)
393
- - High-cost operations (> 10,000 cost units)
394
- - Sorts spilling to disk
395
- - Slow sort operations (> 1s)
396
- - Inaccurate row estimates (> 10x off)
397
- - Slow execution/planning times
398
-
399
- > Note: Queries with parameter placeholders ($1, $2) from pg_stat_statements require parameter input before analysis.
400
-
401
- ### SQL Query Monitoring
402
-
403
- Monitor all SQL queries executed in your Rails application in real-time:
404
-
405
- 1. Visit the dashboard at `/pg_reports`
406
- 2. Click **"▶ Start Monitoring"** button in the SQL Query Monitor panel
407
- 3. Execute operations in your application (web requests, console commands, background jobs)
408
- 4. View captured queries in the dashboard with:
409
- - **SQL text** - Formatted with syntax highlighting
410
- - **Execution duration** - Color-coded: 🟢 green (< 10ms), 🟡 yellow (< 100ms), 🔴 red (> 100ms)
411
- - **Source location** - File:line with click-to-open in IDE
412
- - **Timestamp** - When the query was executed
413
- 5. Click **"⏹ Stop Monitoring"** when done
414
-
415
- **Features:**
416
- - Uses Rails' built-in **ActiveSupport::Notifications** (`sql.active_record` events)
417
- - Global monitoring session (shared by all dashboard users)
418
- - Automatically filters internal queries (SCHEMA, CACHE, pg_reports' own queries)
419
- - Keeps last N queries in memory (configurable, default 100)
420
- - 2-second auto-refresh while monitoring is active
421
- - Session-based tracking with unique IDs
422
- - Logged to `log/pg_reports.log` in JSON Lines format
423
-
424
- **Configuration:**
232
+ Built on `ActiveSupport::Notifications` (`sql.active_record`). Filters internal queries (SCHEMA / CACHE / pg_reports' own). Logged to `log/pg_reports.log` (JSON Lines). Configurable buffer size and backtrace filter:
425
233
 
426
234
  ```ruby
427
235
  PgReports.configure do |config|
428
- # Query monitoring
429
236
  config.query_monitor_log_file = Rails.root.join("log", "custom_monitor.log")
430
- config.query_monitor_max_queries = 200 # Keep last 200 queries (default: 100)
431
-
432
- # Custom backtrace filtering to show only application code
433
- config.query_monitor_backtrace_filter = ->(location) {
434
- !location.path.match?(%r{/(gems|ruby|railties)/})
435
- }
237
+ config.query_monitor_max_queries = 200
238
+ config.query_monitor_backtrace_filter = ->(loc) { !loc.path.match?(%r{/(gems|ruby|railties)/}) }
436
239
  end
437
240
  ```
438
241
 
439
- **Log Format:**
242
+ Use cases: debugging N+1, identifying slow queries during feature development, tracking down unexpected queries, teaching ActiveRecord behavior.
440
243
 
441
- The log file uses JSON Lines format (one JSON object per line):
244
+ </details>
442
245
 
443
- ```json
444
- {"type":"session_start","session_id":"550e8400-e29b-41d4-a716-446655440000","timestamp":"2024-02-07T10:30:00Z"}
445
- {"type":"query","session_id":"550e8400-e29b-41d4-a716-446655440000","sql":"SELECT * FROM users WHERE id = 1","duration_ms":2.34,"name":"User Load","source_location":{"file":"app/controllers/users_controller.rb","line":15,"method":"show"},"timestamp":"2024-02-07T10:30:01Z"}
446
- {"type":"session_end","session_id":"550e8400-e29b-41d4-a716-446655440000","timestamp":"2024-02-07T10:35:00Z"}
447
- ```
448
-
449
- **Use Cases:**
450
- - 🐛 Debug N+1 query problems during development
451
- - 🐌 Identify slow queries in real-time
452
- - 🔍 Track down source of unexpected queries
453
- - 📊 Monitor query patterns during feature development
454
- - 📚 Teaching tool for understanding ActiveRecord behavior
455
-
456
- ### Migration Generator
457
-
458
- For unused or invalid indexes, generate Rails migrations:
459
-
460
- 1. Go to Indexes → Unused Indexes
461
- 2. Expand a row and click "🗑️ Generate Migration"
462
- 3. Copy the code or create the file directly
463
- 4. The file opens automatically in your configured IDE
464
-
465
- ### Connection Pool Analytics
246
+ <details>
247
+ <summary><strong>Connection pool analytics</strong></summary>
466
248
 
467
- Monitor your connection pool health with specialized reports:
249
+ Four specialized reports under the **Connections** category:
468
250
 
469
- **Pool Usage** - Real-time utilization metrics:
470
- - Total, active, idle connections per database
471
- - Pool utilization percentage with 🟢🟡🔴 indicators
472
- - Idle in transaction connections (resource waste)
473
- - Available connection capacity
474
-
475
- **Wait Times** - Identify resource bottlenecks:
476
- - Queries waiting for locks, I/O, or network
477
- - Wait event types (ClientRead, Lock, IO)
478
- - Wait duration with severity thresholds
479
- - Helps diagnose contention issues
480
-
481
- **Pool Saturation** - Health warnings with actionable recommendations:
482
- - Overall pool metrics with status indicators
483
- - Automatic severity assessment (Normal/Elevated/Warning/Critical)
484
- - Context-aware recommendations for each metric
485
- - Detects approaching exhaustion, high idle transactions
486
-
487
- **Connection Churn** - Lifecycle analysis:
488
- - Connection age distribution by application
489
- - Short-lived connection detection (< 10 seconds)
490
- - Churn rate percentage calculation
491
- - Identifies missing/misconfigured connection pooling
251
+ - **Pool Usage** total/active/idle per database, utilization %, idle-in-transaction count, available capacity
252
+ - **Wait Times** queries waiting on locks/IO/network with wait event types and severity
253
+ - **Pool Saturation** auto-classified (Normal / Elevated / Warning / Critical) with context-aware recommendations
254
+ - **Connection Churn** age distribution by application, short-lived (<10s) detection, churn-rate calculation, missing-pooling diagnosis
492
255
 
493
256
  ```ruby
494
- # Console usage
495
257
  PgReports.pool_usage.display
496
258
  PgReports.pool_saturation.display
497
259
  PgReports.connection_churn.display
498
260
  ```
499
261
 
500
- ### Authentication
262
+ </details>
501
263
 
502
- ```ruby
503
- PgReports.configure do |config|
504
- # HTTP Basic Auth
505
- config.dashboard_auth = -> {
506
- authenticate_or_request_with_http_basic do |user, pass|
507
- user == ENV["ADMIN_USER"] && pass == ENV["ADMIN_PASS"]
508
- end
509
- }
264
+ <details>
265
+ <summary><strong>IDE integration & migration generator</strong></summary>
510
266
 
511
- # Or use Devise
512
- config.dashboard_auth = -> {
513
- redirect_to main_app.root_path unless current_user&.admin?
514
- }
515
- end
516
- ```
267
+ Click any source location (file:line) in a report to open it in your IDE. Supported: VS Code, VS Code (WSL), RubyMine, IntelliJ IDEA, Cursor, Cursor (WSL). Use the ⚙️ button to set your default and skip the menu.
517
268
 
518
- ### External Fonts
269
+ For unused or invalid indexes, the dashboard generates a Rails migration: expand the row → **🗑️ Generate Migration** → copy the code or create the file directly (opens in your default IDE).
519
270
 
520
- By default, PgReports does **not** load external fonts.
271
+ </details>
521
272
 
522
- ```ruby
523
- PgReports.configure do |config|
524
- # Enable loading Google Fonts (optional)
525
- config.load_external_fonts = true
526
- end
527
- ```
273
+ <details>
274
+ <summary><strong>Save records for comparison</strong></summary>
528
275
 
529
- ## Telegram Integration
276
+ When optimizing queries, click **📌 Save for Comparison** on any expanded row. Saved records persist in browser localStorage per report type and appear above the results table for before/after comparison.
530
277
 
531
- 1. Create a bot via [@BotFather](https://t.me/BotFather)
532
- 2. Get your chat ID (add [@userinfobot](https://t.me/userinfobot) to get it)
533
- 3. Configure:
278
+ </details>
279
+
280
+ <details>
281
+ <summary><strong>AI prompt export</strong></summary>
282
+
283
+ The Export dropdown includes **Copy Prompt** (visible on actionable reports). It assembles a ready-to-paste prompt with problem description, fix instructions, and the actual report data — formatted for Claude Code, Cursor, Codex, or any code-aware AI assistant.
284
+
285
+ </details>
286
+
287
+ ## Telegram
534
288
 
535
289
  ```ruby
536
290
  PgReports.configure do |config|
537
291
  config.telegram_bot_token = "123456:ABC-DEF..."
538
- config.telegram_chat_id = "-1001234567890"
292
+ config.telegram_chat_id = "-1001234567890"
539
293
  end
540
- ```
541
-
542
- 4. Send reports:
543
294
 
544
- ```ruby
545
295
  PgReports.slow_queries.send_to_telegram
546
296
  PgReports.health_report.send_to_telegram_as_file
547
297
  ```
548
298
 
299
+ Get a bot token from [@BotFather](https://t.me/BotFather) and your chat ID from [@userinfobot](https://t.me/userinfobot).
300
+
549
301
  ## Development
550
302
 
551
303
  ```bash
552
- # Clone the repo
553
304
  git clone https://github.com/yourusername/pg_reports
554
305
  cd pg_reports
555
-
556
- # Install dependencies
557
306
  bundle install
558
-
559
- # Run tests
560
307
  bundle exec rspec
561
-
562
- # Run linter
563
308
  bundle exec rubocop
564
309
  ```
565
310
 
@@ -567,14 +312,14 @@ bundle exec rubocop
567
312
 
568
313
  1. Fork it
569
314
  2. Create your feature branch (`git checkout -b feature/my-feature`)
570
- 3. Commit your changes (`git commit -am 'Add my feature'`)
571
- 4. Push to the branch (`git push origin feature/my-feature`)
315
+ 3. Commit your changes
316
+ 4. Push to the branch
572
317
  5. Create a Pull Request
573
318
 
574
319
  ## License
575
320
 
576
- The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
321
+ MIT. See [LICENSE.txt](LICENSE.txt).
577
322
 
578
323
  ## Acknowledgments
579
324
 
580
- Inspired by [rails-pg-extras](https://github.com/pawurb/rails-pg-extras) and built with ❤️ for the Rails community.
325
+ Inspired by [rails-pg-extras](https://github.com/pawurb/rails-pg-extras). UI built with [Claude](https://www.anthropic.com/claude) by Anthropic.