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.
Files changed (57) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +36 -0
  3. data/app/controllers/pg_reports/dashboard_controller.rb +59 -4
  4. data/app/views/layouts/pg_reports/application.html.erb +1 -1
  5. data/app/views/pg_reports/dashboard/_show_modals.html.erb +8 -1
  6. data/app/views/pg_reports/dashboard/_show_scripts.html.erb +56 -18
  7. data/app/views/pg_reports/dashboard/_show_styles.html.erb +122 -1
  8. data/app/views/pg_reports/dashboard/show.html.erb +89 -47
  9. data/config/locales/en.yml +13 -0
  10. data/config/locales/ru.yml +13 -0
  11. data/config/locales/uk.yml +13 -0
  12. data/lib/pg_reports/dashboard/reports_registry.rb +14 -0
  13. data/lib/pg_reports/definitions/connections/active_connections.yml +23 -0
  14. data/lib/pg_reports/definitions/connections/blocking_queries.yml +20 -0
  15. data/lib/pg_reports/definitions/connections/connection_stats.yml +18 -0
  16. data/lib/pg_reports/definitions/connections/idle_connections.yml +21 -0
  17. data/lib/pg_reports/definitions/connections/locks.yml +22 -0
  18. data/lib/pg_reports/definitions/connections/long_running_queries.yml +43 -0
  19. data/lib/pg_reports/definitions/indexes/bloated_indexes.yml +43 -0
  20. data/lib/pg_reports/definitions/indexes/duplicate_indexes.yml +19 -0
  21. data/lib/pg_reports/definitions/indexes/index_sizes.yml +29 -0
  22. data/lib/pg_reports/definitions/indexes/index_usage.yml +27 -0
  23. data/lib/pg_reports/definitions/indexes/invalid_indexes.yml +19 -0
  24. data/lib/pg_reports/definitions/indexes/missing_indexes.yml +27 -0
  25. data/lib/pg_reports/definitions/indexes/unused_indexes.yml +41 -0
  26. data/lib/pg_reports/definitions/queries/all_queries.yml +35 -0
  27. data/lib/pg_reports/definitions/queries/expensive_queries.yml +43 -0
  28. data/lib/pg_reports/definitions/queries/heavy_queries.yml +49 -0
  29. data/lib/pg_reports/definitions/queries/low_cache_hit_queries.yml +47 -0
  30. data/lib/pg_reports/definitions/queries/missing_index_queries.yml +31 -0
  31. data/lib/pg_reports/definitions/queries/slow_queries.yml +48 -0
  32. data/lib/pg_reports/definitions/system/activity_overview.yml +17 -0
  33. data/lib/pg_reports/definitions/system/cache_stats.yml +18 -0
  34. data/lib/pg_reports/definitions/system/database_sizes.yml +18 -0
  35. data/lib/pg_reports/definitions/system/extensions.yml +19 -0
  36. data/lib/pg_reports/definitions/system/settings.yml +20 -0
  37. data/lib/pg_reports/definitions/tables/bloated_tables.yml +43 -0
  38. data/lib/pg_reports/definitions/tables/cache_hit_ratios.yml +26 -0
  39. data/lib/pg_reports/definitions/tables/recently_modified.yml +27 -0
  40. data/lib/pg_reports/definitions/tables/row_counts.yml +29 -0
  41. data/lib/pg_reports/definitions/tables/seq_scans.yml +31 -0
  42. data/lib/pg_reports/definitions/tables/table_sizes.yml +31 -0
  43. data/lib/pg_reports/definitions/tables/vacuum_needed.yml +39 -0
  44. data/lib/pg_reports/filter.rb +58 -0
  45. data/lib/pg_reports/module_generator.rb +44 -0
  46. data/lib/pg_reports/modules/connections.rb +8 -73
  47. data/lib/pg_reports/modules/indexes.rb +9 -94
  48. data/lib/pg_reports/modules/queries.rb +9 -100
  49. data/lib/pg_reports/modules/schema_analysis.rb +156 -0
  50. data/lib/pg_reports/modules/system.rb +7 -59
  51. data/lib/pg_reports/modules/tables.rb +9 -96
  52. data/lib/pg_reports/report_definition.rb +161 -0
  53. data/lib/pg_reports/report_loader.rb +38 -0
  54. data/lib/pg_reports/sql/schema_analysis/unique_indexes.sql +35 -0
  55. data/lib/pg_reports/version.rb +1 -1
  56. data/lib/pg_reports.rb +24 -0
  57. metadata +38 -1
@@ -295,6 +295,19 @@ uk:
295
295
  - "Цільове значення: >99% для OLTP, >95% для mixed workload."
296
296
  - "Низький cache hit: збільште shared_buffers (до 25% RAM), або проблема в запитах."
297
297
 
298
+ # === SCHEMA ANALYSIS ===
299
+ missing_validations:
300
+ title: "Відсутні валідації"
301
+ what: "Унікальні індекси в базі даних без відповідних валідацій uniqueness в Rails-моделях."
302
+ how: "Аналізує всі унікальні індекси (включаючи композитні) та перевіряє наявність validates :column, uniqueness: true у відповідній Rails-моделі."
303
+ nuances:
304
+ - "Обмеження БД (індекси) та валідації моделі служать різним цілям — обидва мають бути присутніми."
305
+ - "Унікальний індекс запобігає дублікатам на рівні БД, валідація дає зрозумілі повідомлення про помилки."
306
+ - "Для композитних індексів (a, b) потрібна валідація зі scope: validates :a, uniqueness: { scope: :b }"
307
+ - "Деякі таблиці можуть не мати моделей — це нормально для join-таблиць або legacy-таблиць."
308
+ - "Валідації в concerns та батьківських моделях також детектуються."
309
+ - "Первинні ключі не потребують валідацій — вони автоматично унікальні."
310
+
298
311
  # Problem explanations for highlighting
299
312
  problems:
300
313
  high_mean_time: "Високий середній час виконання. Розгляньте додавання індексів або оптимізацію запиту."
@@ -139,6 +139,12 @@ module PgReports
139
139
  cache_stats: {
140
140
  thresholds: {cache_hit_ratio: {warning: 0.95, critical: 0.90, inverted: true}},
141
141
  problem_fields: ["cache_hit_ratio"]
142
+ },
143
+
144
+ # === SCHEMA ANALYSIS ===
145
+ missing_validations: {
146
+ thresholds: {},
147
+ problem_fields: []
142
148
  }
143
149
  }.freeze
144
150
 
@@ -208,6 +214,14 @@ module PgReports
208
214
  activity_overview: {name: "Activity Overview", description: "Current activity summary"},
209
215
  cache_stats: {name: "Cache Stats", description: "Database cache statistics"}
210
216
  }
217
+ },
218
+ schema_analysis: {
219
+ name: "Schema Analysis",
220
+ icon: "🔍",
221
+ color: "#06b6d4",
222
+ reports: {
223
+ missing_validations: {name: "Missing Validations", description: "Unique indexes without model validations"}
224
+ }
211
225
  }
212
226
  }.freeze
213
227
 
@@ -0,0 +1,23 @@
1
+ # Active connections
2
+ # Simple report showing all active database connections
3
+
4
+ report:
5
+ name: active_connections
6
+ module: connections
7
+ description: "All active database connections"
8
+
9
+ sql:
10
+ category: connections
11
+ file: active_connections
12
+
13
+ title: "Active Connections"
14
+
15
+ columns:
16
+ - pid
17
+ - database
18
+ - username
19
+ - application
20
+ - state
21
+ - query_start
22
+ - state_change
23
+ - query
@@ -0,0 +1,20 @@
1
+ # Blocking queries - queries that are blocking others
2
+ # Simple report showing lock conflicts
3
+
4
+ report:
5
+ name: blocking_queries
6
+ module: connections
7
+ description: "Queries that are blocking other queries"
8
+
9
+ sql:
10
+ category: connections
11
+ file: blocking_queries
12
+
13
+ title: "Blocking Queries"
14
+
15
+ columns:
16
+ - blocked_pid
17
+ - blocking_pid
18
+ - blocked_query
19
+ - blocking_query
20
+ - blocked_duration
@@ -0,0 +1,18 @@
1
+ # Connection statistics by state
2
+ # Simple report showing connection counts grouped by state
3
+
4
+ report:
5
+ name: connection_stats
6
+ module: connections
7
+ description: "Connection counts by database and state"
8
+
9
+ sql:
10
+ category: connections
11
+ file: connection_stats
12
+
13
+ title: "Connection Statistics"
14
+
15
+ columns:
16
+ - database
17
+ - state
18
+ - count
@@ -0,0 +1,21 @@
1
+ # Idle connections
2
+ # Simple report showing connections in idle state
3
+
4
+ report:
5
+ name: idle_connections
6
+ module: connections
7
+ description: "Connections currently idle"
8
+
9
+ sql:
10
+ category: connections
11
+ file: idle_connections
12
+
13
+ title: "Idle Connections"
14
+
15
+ columns:
16
+ - pid
17
+ - database
18
+ - username
19
+ - application
20
+ - idle_duration
21
+ - state_change
@@ -0,0 +1,22 @@
1
+ # Lock statistics
2
+ # Simple report showing current database locks
3
+
4
+ report:
5
+ name: locks
6
+ module: connections
7
+ description: "Current database locks"
8
+
9
+ sql:
10
+ category: connections
11
+ file: locks
12
+
13
+ title: "Current Locks"
14
+
15
+ columns:
16
+ - pid
17
+ - database
18
+ - relation
19
+ - locktype
20
+ - mode
21
+ - granted
22
+ - waiting
@@ -0,0 +1,43 @@
1
+ # Long running queries
2
+ # Report with parameter filtering and title interpolation
3
+
4
+ report:
5
+ name: long_running_queries
6
+ module: connections
7
+ description: "Queries running for extended duration"
8
+
9
+ sql:
10
+ category: connections
11
+ file: long_running_queries
12
+
13
+ title: "Long Running Queries (>= ${min_duration_seconds}s)"
14
+ title_vars:
15
+ min_duration_seconds:
16
+ source: param
17
+ key: min_duration_seconds
18
+
19
+ columns:
20
+ - pid
21
+ - database
22
+ - username
23
+ - duration_seconds
24
+ - state
25
+ - query
26
+
27
+ parameters:
28
+ min_duration_seconds:
29
+ type: integer
30
+ default: 60
31
+ description: "Minimum query duration in seconds"
32
+
33
+ filters:
34
+ - field: duration_seconds
35
+ operator: gte
36
+ value:
37
+ source: param
38
+ key: min_duration_seconds
39
+ cast: float
40
+
41
+ problem_explanations:
42
+ duration_seconds: long_running
43
+ duration: long_running
@@ -0,0 +1,43 @@
1
+ # Bloated indexes - indexes with high bloat
2
+ # Report with threshold filtering and title interpolation
3
+
4
+ report:
5
+ name: bloated_indexes
6
+ module: indexes
7
+ description: "Indexes with high bloat percentage"
8
+
9
+ sql:
10
+ category: indexes
11
+ file: bloated_indexes
12
+
13
+ title: "Bloated Indexes (bloat >= ${threshold}%)"
14
+ title_vars:
15
+ threshold:
16
+ source: config
17
+ key: bloat_threshold_percent
18
+
19
+ columns:
20
+ - schema
21
+ - table_name
22
+ - index_name
23
+ - index_size_mb
24
+ - bloat_size_mb
25
+ - bloat_percent
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
+ bloat_ratio: high_bloat
43
+ bloat_size: high_bloat
@@ -0,0 +1,19 @@
1
+ # Duplicate indexes - indexes that may be redundant
2
+ # Simple report with no filtering or parameters
3
+
4
+ report:
5
+ name: duplicate_indexes
6
+ module: indexes
7
+ description: "Indexes that may be redundant"
8
+
9
+ sql:
10
+ category: indexes
11
+ file: duplicate_indexes
12
+
13
+ title: "Duplicate Indexes"
14
+
15
+ columns:
16
+ - table_name
17
+ - index_name
18
+ - duplicate_of
19
+ - index_size_mb
@@ -0,0 +1,29 @@
1
+ # Index sizes - largest indexes by size
2
+ # Report with title interpolation using limit parameter
3
+
4
+ report:
5
+ name: index_sizes
6
+ module: indexes
7
+ description: "Largest indexes by size"
8
+
9
+ sql:
10
+ category: indexes
11
+ file: index_sizes
12
+
13
+ title: "Index Sizes (top ${limit})"
14
+ title_vars:
15
+ limit:
16
+ source: param
17
+ key: limit
18
+
19
+ columns:
20
+ - schema
21
+ - table_name
22
+ - index_name
23
+ - index_size_mb
24
+
25
+ parameters:
26
+ limit:
27
+ type: integer
28
+ default: 50
29
+ description: "Maximum number of results"
@@ -0,0 +1,27 @@
1
+ # Index usage statistics
2
+ # Simple report with limit parameter
3
+
4
+ report:
5
+ name: index_usage
6
+ module: indexes
7
+ description: "Index usage statistics showing scan counts and tuples read"
8
+
9
+ sql:
10
+ category: indexes
11
+ file: index_usage
12
+
13
+ title: "Index Usage Statistics"
14
+
15
+ columns:
16
+ - schema
17
+ - table_name
18
+ - index_name
19
+ - idx_scan
20
+ - idx_tup_read
21
+ - index_size_mb
22
+
23
+ parameters:
24
+ limit:
25
+ type: integer
26
+ default: 50
27
+ description: "Maximum number of results"
@@ -0,0 +1,19 @@
1
+ # Invalid indexes - indexes that are not valid (e.g., failed to build)
2
+ # Simple report with no filtering or parameters
3
+
4
+ report:
5
+ name: invalid_indexes
6
+ module: indexes
7
+ description: "Indexes that are not valid (e.g., failed to build)"
8
+
9
+ sql:
10
+ category: indexes
11
+ file: invalid_indexes
12
+
13
+ title: "Invalid Indexes"
14
+
15
+ columns:
16
+ - schema
17
+ - table_name
18
+ - index_name
19
+ - index_definition
@@ -0,0 +1,27 @@
1
+ # Missing indexes - tables with high sequential scans
2
+ # Simple report with limit parameter
3
+
4
+ report:
5
+ name: missing_indexes
6
+ module: indexes
7
+ description: "Tables with high sequential scans that may benefit from indexes"
8
+
9
+ sql:
10
+ category: indexes
11
+ file: missing_indexes
12
+
13
+ title: "Tables Potentially Missing Indexes"
14
+
15
+ columns:
16
+ - schema
17
+ - table_name
18
+ - seq_scan
19
+ - seq_tup_read
20
+ - idx_scan
21
+ - table_size_mb
22
+
23
+ parameters:
24
+ limit:
25
+ type: integer
26
+ default: 20
27
+ description: "Maximum number of results"
@@ -0,0 +1,41 @@
1
+ # Unused indexes - indexes that are rarely or never scanned
2
+ # These indexes waste disk space and slow down writes
3
+
4
+ report:
5
+ name: unused_indexes
6
+ module: indexes
7
+ description: "Indexes that are rarely or never scanned"
8
+
9
+ sql:
10
+ category: indexes
11
+ file: unused_indexes
12
+
13
+ title: "Unused Indexes (scans <= ${threshold})"
14
+ title_vars:
15
+ threshold:
16
+ source: config
17
+ key: unused_index_threshold_scans
18
+
19
+ columns:
20
+ - schema
21
+ - table_name
22
+ - index_name
23
+ - idx_scan
24
+ - index_size_mb
25
+
26
+ parameters:
27
+ limit:
28
+ type: integer
29
+ default: 50
30
+ description: "Maximum number of results"
31
+
32
+ filters:
33
+ - field: idx_scan
34
+ operator: lte
35
+ value:
36
+ source: config
37
+ key: unused_index_threshold_scans
38
+ cast: integer
39
+
40
+ problem_explanations:
41
+ idx_scan: unused_index
@@ -0,0 +1,35 @@
1
+ # All query statistics ordered by total time
2
+ # Simple report with enrichment and title interpolation
3
+
4
+ report:
5
+ name: all_queries
6
+ module: queries
7
+ description: "All query statistics ordered by total time"
8
+
9
+ sql:
10
+ category: queries
11
+ file: all_queries
12
+
13
+ title: "All Query Statistics (top ${limit})"
14
+ title_vars:
15
+ limit:
16
+ source: param
17
+ key: limit
18
+
19
+ columns:
20
+ - query
21
+ - source
22
+ - calls
23
+ - total_time_ms
24
+ - mean_time_ms
25
+ - rows
26
+
27
+ parameters:
28
+ limit:
29
+ type: integer
30
+ default: 50
31
+ description: "Maximum number of results"
32
+
33
+ enrichment:
34
+ hook: enrich_with_annotations
35
+ module: queries
@@ -0,0 +1,43 @@
1
+ # Expensive queries - queries consuming most total time
2
+ # Report with threshold filtering, enrichment, and title interpolation
3
+
4
+ report:
5
+ name: expensive_queries
6
+ module: queries
7
+ description: "Queries consuming most total execution time"
8
+
9
+ sql:
10
+ category: queries
11
+ file: expensive_queries
12
+
13
+ title: "Expensive Queries (total time >= ${threshold}ms)"
14
+ title_vars:
15
+ threshold:
16
+ source: config
17
+ key: expensive_query_threshold_ms
18
+
19
+ columns:
20
+ - query
21
+ - source
22
+ - calls
23
+ - total_time_ms
24
+ - percent_of_total
25
+ - mean_time_ms
26
+
27
+ parameters:
28
+ limit:
29
+ type: integer
30
+ default: 20
31
+ description: "Maximum number of results"
32
+
33
+ filters:
34
+ - field: total_time_ms
35
+ operator: gte
36
+ value:
37
+ source: config
38
+ key: expensive_query_threshold_ms
39
+ cast: float
40
+
41
+ enrichment:
42
+ hook: enrich_with_annotations
43
+ module: queries
@@ -0,0 +1,49 @@
1
+ # Heavy queries - queries called most frequently
2
+ # Report with threshold filtering, enrichment, and title interpolation
3
+
4
+ report:
5
+ name: heavy_queries
6
+ module: queries
7
+ description: "Queries called most frequently"
8
+
9
+ sql:
10
+ category: queries
11
+ file: heavy_queries
12
+
13
+ title: "Heavy Queries (calls >= ${threshold})"
14
+ title_vars:
15
+ threshold:
16
+ source: config
17
+ key: heavy_query_threshold_calls
18
+
19
+ columns:
20
+ - query
21
+ - source
22
+ - calls
23
+ - total_time_ms
24
+ - mean_time_ms
25
+ - cache_hit_ratio
26
+
27
+ parameters:
28
+ limit:
29
+ type: integer
30
+ default: 20
31
+ description: "Maximum number of results"
32
+
33
+ filters:
34
+ - field: calls
35
+ operator: gte
36
+ value:
37
+ source: config
38
+ key: heavy_query_threshold_calls
39
+ cast: integer
40
+
41
+ enrichment:
42
+ hook: enrich_with_annotations
43
+ module: queries
44
+
45
+ problem_explanations:
46
+ calls: high_calls
47
+ total_time_ms: high_total_time
48
+ mean_time_ms: high_mean_time
49
+ cache_hit_ratio: low_cache_hit
@@ -0,0 +1,47 @@
1
+ # Queries with low cache hit ratio
2
+ # Report with parameter filtering, enrichment, and title interpolation
3
+
4
+ report:
5
+ name: low_cache_hit_queries
6
+ module: queries
7
+ description: "Queries with poor cache utilization"
8
+
9
+ sql:
10
+ category: queries
11
+ file: low_cache_hit_queries
12
+
13
+ title: "Queries with Low Cache Hit Ratio (min ${min_calls} calls)"
14
+ title_vars:
15
+ min_calls:
16
+ source: param
17
+ key: min_calls
18
+
19
+ columns:
20
+ - query
21
+ - source
22
+ - calls
23
+ - cache_hit_ratio
24
+ - shared_blks_hit
25
+ - shared_blks_read
26
+
27
+ parameters:
28
+ limit:
29
+ type: integer
30
+ default: 20
31
+ description: "Maximum number of results"
32
+ min_calls:
33
+ type: integer
34
+ default: 100
35
+ description: "Minimum number of calls to include"
36
+
37
+ filters:
38
+ - field: calls
39
+ operator: gte
40
+ value:
41
+ source: param
42
+ key: min_calls
43
+ cast: integer
44
+
45
+ enrichment:
46
+ hook: enrich_with_annotations
47
+ module: queries
@@ -0,0 +1,31 @@
1
+ # Queries missing indexes - sequential scans on large tables
2
+ # Simple report with enrichment
3
+
4
+ report:
5
+ name: missing_index_queries
6
+ module: queries
7
+ description: "Queries with sequential scans that may benefit from indexes"
8
+
9
+ sql:
10
+ category: queries
11
+ file: missing_index_queries
12
+
13
+ title: "Queries Potentially Missing Indexes"
14
+
15
+ columns:
16
+ - query
17
+ - source
18
+ - calls
19
+ - seq_scan_count
20
+ - rows_examined
21
+ - table_name
22
+
23
+ parameters:
24
+ limit:
25
+ type: integer
26
+ default: 20
27
+ description: "Maximum number of results"
28
+
29
+ enrichment:
30
+ hook: enrich_with_annotations
31
+ module: queries
@@ -0,0 +1,48 @@
1
+ # Slow queries - queries with high mean execution time
2
+ # Report with threshold filtering, enrichment, and title interpolation
3
+
4
+ report:
5
+ name: slow_queries
6
+ module: queries
7
+ description: "Queries with high mean execution time"
8
+
9
+ sql:
10
+ category: queries
11
+ file: slow_queries
12
+
13
+ title: "Slow Queries (mean time >= ${threshold}ms)"
14
+ title_vars:
15
+ threshold:
16
+ source: config
17
+ key: slow_query_threshold_ms
18
+
19
+ columns:
20
+ - query
21
+ - source
22
+ - calls
23
+ - mean_time_ms
24
+ - total_time_ms
25
+ - rows_per_call
26
+
27
+ parameters:
28
+ limit:
29
+ type: integer
30
+ default: 20
31
+ description: "Maximum number of results"
32
+
33
+ filters:
34
+ - field: mean_time_ms
35
+ operator: gte
36
+ value:
37
+ source: config
38
+ key: slow_query_threshold_ms
39
+ cast: float
40
+
41
+ enrichment:
42
+ hook: enrich_with_annotations
43
+ module: queries
44
+
45
+ problem_explanations:
46
+ mean_time_ms: high_mean_time
47
+ calls: high_calls
48
+ total_time_ms: high_total_time
@@ -0,0 +1,17 @@
1
+ # Database activity overview
2
+ # Simple report showing current database activity metrics
3
+
4
+ report:
5
+ name: activity_overview
6
+ module: system
7
+ description: "Current database activity overview"
8
+
9
+ sql:
10
+ category: system
11
+ file: activity_overview
12
+
13
+ title: "Database Activity Overview"
14
+
15
+ columns:
16
+ - metric
17
+ - value