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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +40 -0
- data/README.md +12 -4
- data/app/views/layouts/pg_reports/application.html.erb +70 -61
- data/app/views/pg_reports/dashboard/_show_scripts.html.erb +53 -1
- data/app/views/pg_reports/dashboard/_show_styles.html.erb +31 -11
- data/app/views/pg_reports/dashboard/index.html.erb +14 -8
- data/app/views/pg_reports/dashboard/show.html.erb +6 -2
- data/config/locales/en.yml +109 -0
- data/config/locales/ru.yml +81 -0
- data/config/locales/uk.yml +126 -0
- data/lib/pg_reports/compatibility.rb +63 -0
- data/lib/pg_reports/configuration.rb +2 -0
- data/lib/pg_reports/dashboard/reports_registry.rb +36 -0
- data/lib/pg_reports/definitions/indexes/fk_without_indexes.yml +30 -0
- data/lib/pg_reports/definitions/indexes/index_correlation.yml +31 -0
- data/lib/pg_reports/definitions/indexes/inefficient_indexes.yml +45 -0
- data/lib/pg_reports/definitions/queries/temp_file_queries.yml +39 -0
- data/lib/pg_reports/definitions/system/wraparound_risk.yml +31 -0
- data/lib/pg_reports/definitions/tables/tables_without_pk.yml +28 -0
- data/lib/pg_reports/engine.rb +6 -0
- data/lib/pg_reports/modules/indexes.rb +3 -0
- data/lib/pg_reports/modules/queries.rb +1 -0
- data/lib/pg_reports/modules/system.rb +27 -0
- data/lib/pg_reports/modules/tables.rb +1 -0
- data/lib/pg_reports/query_monitor.rb +64 -32
- data/lib/pg_reports/sql/indexes/fk_without_indexes.sql +23 -0
- data/lib/pg_reports/sql/indexes/index_correlation.sql +27 -0
- data/lib/pg_reports/sql/indexes/inefficient_indexes.sql +22 -0
- data/lib/pg_reports/sql/queries/temp_file_queries.sql +16 -0
- data/lib/pg_reports/sql/system/checkpoint_stats.sql +20 -0
- data/lib/pg_reports/sql/system/checkpoint_stats_legacy.sql +19 -0
- data/lib/pg_reports/sql/system/wraparound_risk.sql +21 -0
- data/lib/pg_reports/sql/tables/tables_without_pk.sql +20 -0
- data/lib/pg_reports/version.rb +1 -1
- data/lib/pg_reports.rb +5 -0
- 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;
|
data/lib/pg_reports/version.rb
CHANGED
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.
|
|
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
|