pg_reports 0.5.4 → 0.6.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.
Files changed (37) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +40 -0
  3. data/README.md +12 -4
  4. data/app/views/layouts/pg_reports/application.html.erb +70 -61
  5. data/app/views/pg_reports/dashboard/_show_scripts.html.erb +53 -1
  6. data/app/views/pg_reports/dashboard/_show_styles.html.erb +31 -11
  7. data/app/views/pg_reports/dashboard/index.html.erb +14 -8
  8. data/app/views/pg_reports/dashboard/show.html.erb +6 -2
  9. data/config/locales/en.yml +109 -0
  10. data/config/locales/ru.yml +81 -0
  11. data/config/locales/uk.yml +126 -0
  12. data/lib/pg_reports/compatibility.rb +63 -0
  13. data/lib/pg_reports/configuration.rb +2 -0
  14. data/lib/pg_reports/dashboard/reports_registry.rb +36 -0
  15. data/lib/pg_reports/definitions/indexes/fk_without_indexes.yml +30 -0
  16. data/lib/pg_reports/definitions/indexes/index_correlation.yml +31 -0
  17. data/lib/pg_reports/definitions/indexes/inefficient_indexes.yml +45 -0
  18. data/lib/pg_reports/definitions/queries/temp_file_queries.yml +39 -0
  19. data/lib/pg_reports/definitions/system/wraparound_risk.yml +31 -0
  20. data/lib/pg_reports/definitions/tables/tables_without_pk.yml +28 -0
  21. data/lib/pg_reports/engine.rb +6 -0
  22. data/lib/pg_reports/modules/indexes.rb +3 -0
  23. data/lib/pg_reports/modules/queries.rb +1 -0
  24. data/lib/pg_reports/modules/system.rb +27 -0
  25. data/lib/pg_reports/modules/tables.rb +1 -0
  26. data/lib/pg_reports/query_monitor.rb +64 -32
  27. data/lib/pg_reports/sql/indexes/fk_without_indexes.sql +23 -0
  28. data/lib/pg_reports/sql/indexes/index_correlation.sql +27 -0
  29. data/lib/pg_reports/sql/indexes/inefficient_indexes.sql +22 -0
  30. data/lib/pg_reports/sql/queries/temp_file_queries.sql +16 -0
  31. data/lib/pg_reports/sql/system/checkpoint_stats.sql +20 -0
  32. data/lib/pg_reports/sql/system/checkpoint_stats_legacy.sql +19 -0
  33. data/lib/pg_reports/sql/system/wraparound_risk.sql +21 -0
  34. data/lib/pg_reports/sql/tables/tables_without_pk.sql +20 -0
  35. data/lib/pg_reports/version.rb +1 -1
  36. data/lib/pg_reports.rb +5 -0
  37. metadata +16 -1
@@ -0,0 +1,27 @@
1
+ -- Index correlation: how well physical row order matches index order
2
+ -- Low correlation on frequently range-scanned columns means excessive random I/O
3
+
4
+ SELECT
5
+ s.schemaname AS schema,
6
+ s.tablename AS table_name,
7
+ s.attname AS column_name,
8
+ i.indexrelname AS index_name,
9
+ ROUND(s.correlation::numeric, 4) AS correlation,
10
+ ABS(s.correlation) AS abs_correlation,
11
+ s.n_distinct,
12
+ pg_size_pretty(pg_relation_size(c.oid)) AS table_size,
13
+ ROUND(pg_relation_size(c.oid) / 1024.0 / 1024.0, 2) AS table_size_mb,
14
+ si.idx_scan
15
+ FROM pg_stats s
16
+ JOIN pg_class c ON c.relname = s.tablename
17
+ AND c.relnamespace = (SELECT oid FROM pg_namespace WHERE nspname = s.schemaname)
18
+ JOIN pg_index idx ON idx.indrelid = c.oid
19
+ JOIN pg_attribute a ON a.attrelid = c.oid AND a.attname = s.attname
20
+ AND a.attnum = idx.indkey[0]
21
+ JOIN pg_stat_user_indexes i ON i.indexrelid = idx.indexrelid
22
+ JOIN pg_stat_user_indexes si ON si.indexrelid = idx.indexrelid
23
+ WHERE s.schemaname NOT IN ('pg_catalog', 'information_schema')
24
+ AND ABS(s.correlation) < 0.5
25
+ AND pg_relation_size(c.oid) > 10 * 1024 * 1024
26
+ AND si.idx_scan > 100
27
+ ORDER BY ABS(s.correlation) ASC, pg_relation_size(c.oid) DESC;
@@ -0,0 +1,22 @@
1
+ -- Inefficient index scans: indexes that are used but read far more entries than they fetch
2
+ -- A high idx_tup_read / idx_tup_fetch ratio indicates the index column order
3
+ -- does not match query predicates, forcing PostgreSQL to scan large index ranges
4
+ -- Reference: https://www.datadoghq.com/blog/detect-inefficient-index-scans-with-dbm/
5
+
6
+ SELECT
7
+ schemaname AS schema,
8
+ relname AS table_name,
9
+ indexrelname AS index_name,
10
+ idx_scan,
11
+ idx_tup_read,
12
+ idx_tup_fetch,
13
+ ROUND((idx_tup_read::numeric / NULLIF(idx_tup_fetch, 0)), 1) AS read_to_fetch_ratio,
14
+ pg_size_pretty(pg_relation_size(indexrelid)) AS index_size,
15
+ ROUND(pg_relation_size(indexrelid) / 1024.0 / 1024.0, 2) AS index_size_mb,
16
+ pg_get_indexdef(indexrelid) AS index_definition
17
+ FROM pg_stat_user_indexes
18
+ WHERE schemaname NOT IN ('pg_catalog', 'information_schema')
19
+ AND idx_scan > 0
20
+ AND idx_tup_fetch > 0
21
+ AND (idx_tup_read::numeric / NULLIF(idx_tup_fetch, 0)) > 10
22
+ ORDER BY (idx_tup_read::numeric / NULLIF(idx_tup_fetch, 0)) DESC;
@@ -0,0 +1,16 @@
1
+ -- Queries that spill to disk via temporary files
2
+ -- High temp file usage indicates insufficient work_mem for these queries
3
+
4
+ SELECT
5
+ queryid,
6
+ LEFT(query, :max_query_length) AS query,
7
+ calls,
8
+ ROUND(temp_blks_written::numeric * 8 / 1024, 2) AS temp_mb_written,
9
+ ROUND(temp_blks_read::numeric * 8 / 1024, 2) AS temp_mb_read,
10
+ ROUND((total_exec_time / 1000)::numeric, 2) AS total_time_sec,
11
+ ROUND((mean_exec_time)::numeric, 2) AS mean_time_ms,
12
+ rows
13
+ FROM pg_stat_statements
14
+ WHERE temp_blks_written > 0
15
+ AND dbid = (SELECT oid FROM pg_database WHERE datname = current_database())
16
+ ORDER BY temp_blks_written DESC;
@@ -0,0 +1,20 @@
1
+ -- Checkpoint and background writer statistics (PostgreSQL 17+)
2
+ -- Uses pg_stat_checkpointer (introduced in PG 17) + pg_stat_bgwriter for bgwriter-only stats
3
+
4
+ SELECT
5
+ pg_stat_get_checkpointer_num_timed() AS checkpoints_timed,
6
+ pg_stat_get_checkpointer_num_requested() AS checkpoints_requested,
7
+ ROUND(pg_stat_get_checkpointer_write_time()::numeric / 1000, 2) AS checkpoint_write_time_sec,
8
+ ROUND(pg_stat_get_checkpointer_sync_time()::numeric / 1000, 2) AS checkpoint_sync_time_sec,
9
+ pg_stat_get_checkpointer_buffers_written() AS buffers_checkpoint,
10
+ pg_stat_get_bgwriter_buf_written_clean() AS buffers_clean,
11
+ pg_stat_get_bgwriter_maxwritten_clean() AS bgwriter_stops,
12
+ pg_stat_get_buf_alloc() AS buffers_alloc,
13
+ CASE
14
+ WHEN (pg_stat_get_checkpointer_num_timed() + pg_stat_get_checkpointer_num_requested()) > 0
15
+ THEN ROUND(
16
+ pg_stat_get_checkpointer_num_requested()::numeric /
17
+ (pg_stat_get_checkpointer_num_timed() + pg_stat_get_checkpointer_num_requested()) * 100, 1)
18
+ ELSE 0
19
+ END AS requested_pct,
20
+ pg_stat_get_bgwriter_stat_reset_time() AS stats_reset;
@@ -0,0 +1,19 @@
1
+ -- Checkpoint and background writer statistics (PostgreSQL < 17)
2
+ -- All stats from pg_stat_bgwriter (before checkpoint columns were moved out)
3
+
4
+ SELECT
5
+ checkpoints_timed,
6
+ checkpoints_req AS checkpoints_requested,
7
+ ROUND(checkpoint_write_time::numeric / 1000, 2) AS checkpoint_write_time_sec,
8
+ ROUND(checkpoint_sync_time::numeric / 1000, 2) AS checkpoint_sync_time_sec,
9
+ buffers_checkpoint,
10
+ buffers_clean,
11
+ maxwritten_clean AS bgwriter_stops,
12
+ buffers_alloc,
13
+ CASE
14
+ WHEN (checkpoints_timed + checkpoints_req) > 0
15
+ THEN ROUND(checkpoints_req::numeric / (checkpoints_timed + checkpoints_req) * 100, 1)
16
+ ELSE 0
17
+ END AS requested_pct,
18
+ stats_reset
19
+ FROM pg_stat_bgwriter;
@@ -0,0 +1,21 @@
1
+ -- Transaction ID wraparound risk
2
+ -- When age(datfrozenxid) approaches 2 billion, PostgreSQL will shut down
3
+ -- to prevent data corruption. Monitor this to trigger preventive VACUUM FREEZE.
4
+
5
+ SELECT
6
+ d.datname AS database_name,
7
+ age(d.datfrozenxid) AS xid_age,
8
+ ROUND(age(d.datfrozenxid)::numeric / 2147483647 * 100, 2) AS pct_towards_wraparound,
9
+ 2147483647 - age(d.datfrozenxid) AS remaining_xids,
10
+ current_setting('autovacuum_freeze_max_age')::bigint AS freeze_max_age,
11
+ CASE
12
+ WHEN age(d.datfrozenxid) > current_setting('autovacuum_freeze_max_age')::bigint
13
+ THEN 'CRITICAL - exceeds freeze_max_age'
14
+ WHEN age(d.datfrozenxid) > current_setting('autovacuum_freeze_max_age')::bigint * 0.75
15
+ THEN 'WARNING - approaching freeze_max_age'
16
+ ELSE 'OK'
17
+ END AS status,
18
+ pg_size_pretty(pg_database_size(d.datname)) AS database_size
19
+ FROM pg_database d
20
+ WHERE d.datallowconn = true
21
+ ORDER BY age(d.datfrozenxid) DESC;
@@ -0,0 +1,20 @@
1
+ -- Tables without primary keys
2
+ -- Missing PKs break logical replication and make row identification unreliable
3
+
4
+ SELECT
5
+ n.nspname AS schema,
6
+ c.relname AS table_name,
7
+ c.reltuples::bigint AS estimated_rows,
8
+ pg_size_pretty(pg_relation_size(c.oid)) AS table_size,
9
+ ROUND(pg_relation_size(c.oid) / 1024.0 / 1024.0, 2) AS table_size_mb
10
+ FROM pg_class c
11
+ JOIN pg_namespace n ON n.oid = c.relnamespace
12
+ WHERE c.relkind = 'r'
13
+ AND n.nspname NOT IN ('pg_catalog', 'information_schema')
14
+ AND NOT EXISTS (
15
+ SELECT 1
16
+ FROM pg_index i
17
+ WHERE i.indrelid = c.oid
18
+ AND i.indisprimary
19
+ )
20
+ ORDER BY pg_relation_size(c.oid) DESC;
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module PgReports
4
- VERSION = "0.5.4"
4
+ VERSION = "0.6.0"
5
5
  end
data/lib/pg_reports.rb CHANGED
@@ -6,6 +6,7 @@ require "active_record"
6
6
 
7
7
  require_relative "pg_reports/version"
8
8
  require_relative "pg_reports/error"
9
+ require_relative "pg_reports/compatibility"
9
10
  require_relative "pg_reports/configuration"
10
11
  require_relative "pg_reports/sql_loader"
11
12
  require_relative "pg_reports/executor"
@@ -138,3 +139,7 @@ end
138
139
 
139
140
  # Generate YAML-based methods on load
140
141
  PgReports::ModuleGenerator.generate!
142
+
143
+ # Check Ruby and Rails versions immediately (no DB needed)
144
+ PgReports::Compatibility.check_ruby!
145
+ PgReports::Compatibility.check_rails!
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pg_reports
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.4
4
+ version: 0.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Eldar Avatov
@@ -161,6 +161,7 @@ files:
161
161
  - config/routes.rb
162
162
  - lib/pg_reports.rb
163
163
  - lib/pg_reports/annotation_parser.rb
164
+ - lib/pg_reports/compatibility.rb
164
165
  - lib/pg_reports/configuration.rb
165
166
  - lib/pg_reports/dashboard/reports_registry.rb
166
167
  - lib/pg_reports/definitions/connections/active_connections.yml
@@ -175,8 +176,11 @@ files:
175
176
  - lib/pg_reports/definitions/connections/pool_wait_times.yml
176
177
  - lib/pg_reports/definitions/indexes/bloated_indexes.yml
177
178
  - lib/pg_reports/definitions/indexes/duplicate_indexes.yml
179
+ - lib/pg_reports/definitions/indexes/fk_without_indexes.yml
180
+ - lib/pg_reports/definitions/indexes/index_correlation.yml
178
181
  - lib/pg_reports/definitions/indexes/index_sizes.yml
179
182
  - lib/pg_reports/definitions/indexes/index_usage.yml
183
+ - lib/pg_reports/definitions/indexes/inefficient_indexes.yml
180
184
  - lib/pg_reports/definitions/indexes/invalid_indexes.yml
181
185
  - lib/pg_reports/definitions/indexes/missing_indexes.yml
182
186
  - lib/pg_reports/definitions/indexes/unused_indexes.yml
@@ -186,17 +190,20 @@ files:
186
190
  - lib/pg_reports/definitions/queries/low_cache_hit_queries.yml
187
191
  - lib/pg_reports/definitions/queries/missing_index_queries.yml
188
192
  - lib/pg_reports/definitions/queries/slow_queries.yml
193
+ - lib/pg_reports/definitions/queries/temp_file_queries.yml
189
194
  - lib/pg_reports/definitions/system/activity_overview.yml
190
195
  - lib/pg_reports/definitions/system/cache_stats.yml
191
196
  - lib/pg_reports/definitions/system/database_sizes.yml
192
197
  - lib/pg_reports/definitions/system/extensions.yml
193
198
  - lib/pg_reports/definitions/system/settings.yml
199
+ - lib/pg_reports/definitions/system/wraparound_risk.yml
194
200
  - lib/pg_reports/definitions/tables/bloated_tables.yml
195
201
  - lib/pg_reports/definitions/tables/cache_hit_ratios.yml
196
202
  - lib/pg_reports/definitions/tables/recently_modified.yml
197
203
  - lib/pg_reports/definitions/tables/row_counts.yml
198
204
  - lib/pg_reports/definitions/tables/seq_scans.yml
199
205
  - lib/pg_reports/definitions/tables/table_sizes.yml
206
+ - lib/pg_reports/definitions/tables/tables_without_pk.yml
200
207
  - lib/pg_reports/definitions/tables/vacuum_needed.yml
201
208
  - lib/pg_reports/engine.rb
202
209
  - lib/pg_reports/error.rb
@@ -226,8 +233,11 @@ files:
226
233
  - lib/pg_reports/sql/connections/pool_wait_times.sql
227
234
  - lib/pg_reports/sql/indexes/bloated_indexes.sql
228
235
  - lib/pg_reports/sql/indexes/duplicate_indexes.sql
236
+ - lib/pg_reports/sql/indexes/fk_without_indexes.sql
237
+ - lib/pg_reports/sql/indexes/index_correlation.sql
229
238
  - lib/pg_reports/sql/indexes/index_sizes.sql
230
239
  - lib/pg_reports/sql/indexes/index_usage.sql
240
+ - lib/pg_reports/sql/indexes/inefficient_indexes.sql
231
241
  - lib/pg_reports/sql/indexes/invalid_indexes.sql
232
242
  - lib/pg_reports/sql/indexes/missing_indexes.sql
233
243
  - lib/pg_reports/sql/indexes/unused_indexes.sql
@@ -237,20 +247,25 @@ files:
237
247
  - lib/pg_reports/sql/queries/low_cache_hit_queries.sql
238
248
  - lib/pg_reports/sql/queries/missing_index_queries.sql
239
249
  - lib/pg_reports/sql/queries/slow_queries.sql
250
+ - lib/pg_reports/sql/queries/temp_file_queries.sql
240
251
  - lib/pg_reports/sql/schema_analysis/unique_indexes.sql
241
252
  - lib/pg_reports/sql/system/activity_overview.sql
242
253
  - lib/pg_reports/sql/system/cache_stats.sql
254
+ - lib/pg_reports/sql/system/checkpoint_stats.sql
255
+ - lib/pg_reports/sql/system/checkpoint_stats_legacy.sql
243
256
  - lib/pg_reports/sql/system/database_sizes.sql
244
257
  - lib/pg_reports/sql/system/databases_list.sql
245
258
  - lib/pg_reports/sql/system/extensions.sql
246
259
  - lib/pg_reports/sql/system/live_metrics.sql
247
260
  - lib/pg_reports/sql/system/settings.sql
261
+ - lib/pg_reports/sql/system/wraparound_risk.sql
248
262
  - lib/pg_reports/sql/tables/bloated_tables.sql
249
263
  - lib/pg_reports/sql/tables/cache_hit_ratios.sql
250
264
  - lib/pg_reports/sql/tables/recently_modified.sql
251
265
  - lib/pg_reports/sql/tables/row_counts.sql
252
266
  - lib/pg_reports/sql/tables/seq_scans.sql
253
267
  - lib/pg_reports/sql/tables/table_sizes.sql
268
+ - lib/pg_reports/sql/tables/tables_without_pk.sql
254
269
  - lib/pg_reports/sql/tables/vacuum_needed.sql
255
270
  - lib/pg_reports/sql_loader.rb
256
271
  - lib/pg_reports/telegram_sender.rb