ruby-pg-extras 2.1.0 → 3.0.6

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a88ff7e79fc4b77781012f9ee52afeee2462dcc46177d6bec8d24885c9822526
4
- data.tar.gz: 9befdd7dd6f98195391fe323f5fabf0d74a6bb9bb0d717f7cf74c30219f91462
3
+ metadata.gz: ed716b44ced4653bf0c794ea1690b99cecf460f30dc767a56f328748790d4c0f
4
+ data.tar.gz: 3652a5add3cfd162959578af4d840ee494f99311d4b2e48ffbd2f1ca5c078b1e
5
5
  SHA512:
6
- metadata.gz: 0c5df44798b60ae20ad1d8bb395cce56d92e89ddde8912d0a747fcf7e695c74ea3f9a58fa71b7d570a956d908ef3261bfc776c8feb23b21a7f61c595bc3a0855
7
- data.tar.gz: d8d2ddcc09c7127ff75b8897bbef5a4ad98f057c80e836bcf8849833c0580914bfd1fd3a435cce34ef4124458dcf0fb12b3dcdb1d78b6932bd629f0ddb90a2f5
6
+ metadata.gz: 3078b1e69e4a36686cc76a2c5326a35b8a61b4751cfe1031df74cda6306ae62fcd07026be2218ca40ac83dd535d0748e608bf00e5e3571aae9de0f1d794d43ff
7
+ data.tar.gz: 116e9de1f4a0dc927eef92e6bc88f354c54a5929831d68d7a19c58efbd8085329b0abff6cde1d482757c41d27225191e50bf2852acd9d57637d2306f61568fa7
data/.circleci/config.yml CHANGED
@@ -31,7 +31,8 @@ jobs:
31
31
  - checkout
32
32
  - run: gem update --system
33
33
  - run: gem install bundler
34
- - run: bundle install --path vendor/bundle
34
+ - run: bundle config set --local path 'vendor/bundle'
35
+ - run: bundle install
35
36
  - run: sudo apt-get update --allow-releaseinfo-change
36
37
  - run: sudo apt install postgresql-client-11
37
38
  - run: dockerize -wait tcp://postgres11:5432 -timeout 1m
data/README.md CHANGED
@@ -26,7 +26,7 @@ In your Gemfile
26
26
  gem "ruby-pg-extras"
27
27
  ```
28
28
 
29
- Some of the queries (e.g., `calls` and `outliers`) require [pg_stat_statements](https://www.postgresql.org/docs/current/pgstatstatements.html) extension enabled.
29
+ `calls` and `outliers` queries require [pg_stat_statements](https://www.postgresql.org/docs/current/pgstatstatements.html) extension.
30
30
 
31
31
  You can check if it is enabled in your database by running:
32
32
 
@@ -39,6 +39,12 @@ You should see the similar line in the output:
39
39
  | pg_stat_statements | 1.7 | 1.7 | track execution statistics of all SQL statements executed |
40
40
  ```
41
41
 
42
+ `ssl_used` requires `sslinfo` extension, and `buffercache_usage`/`buffercache_usage` queries need `pg_buffercache`. You can enable them all by running:
43
+
44
+ ```ruby
45
+ RubyPGExtras.add_extensions
46
+ ```
47
+
42
48
  ## Usage
43
49
 
44
50
  Gem expects the `ENV['DATABASE_URL']` value in the following format:
@@ -92,6 +98,18 @@ RubyPGExtras.long_running_queries(args: { threshold: "200 milliseconds" })
92
98
 
93
99
  ```
94
100
 
101
+ ## Diagnose report
102
+
103
+ The simplest way to start using pg-extras is to execute a `diagnose` method. It runs a set of checks and prints out a report highlighting areas that may require additional investigation:
104
+
105
+ ```ruby
106
+ RubyPGExtras.diagnose
107
+ ```
108
+
109
+ ![Diagnose report](https://github.com/pawurb/ruby-pg-extras/raw/master/ruby-pg-extras-diagnose.png)
110
+
111
+ Keep reading to learn about methods that `diagnose` uses under the hood.
112
+
95
113
  ## Available methods
96
114
 
97
115
  ### `cache_hit`
@@ -167,6 +185,20 @@ This method displays values for selected PostgreSQL settings. You can compare th
167
185
 
168
186
  [More info](https://pawelurbanek.com/postgresql-fix-performance#cache-hit)
169
187
 
188
+ ### `ssl_used`
189
+
190
+ ```ruby
191
+
192
+ RubyPGExtras.ssl_used
193
+
194
+ | ssl_is_used |
195
+ +---------------------------------+
196
+ | t |
197
+
198
+ ```
199
+
200
+ Returns boolean indicating if an encrypted SSL is currently used. Connecting to the database via an unencrypted connection is a critical security risk.
201
+
170
202
  ### `index_usage`
171
203
 
172
204
  ```ruby
@@ -222,7 +254,7 @@ This command displays all the current locks, regardless of their type.
222
254
 
223
255
  RubyPGExtras.outliers(args: { limit: 20 })
224
256
 
225
- qry | exec_time | prop_exec_time | ncalls | sync_io_time
257
+ query | exec_time | prop_exec_time | ncalls | sync_io_time
226
258
  -----------------------------------------+------------------+----------------+-------------+--------------
227
259
  SELECT * FROM archivable_usage_events.. | 154:39:26.431466 | 72.2% | 34,211,877 | 00:00:00
228
260
  COPY public.archivable_usage_events (.. | 50:38:33.198418 | 23.6% | 13 | 13:34:21.00108
@@ -373,7 +405,7 @@ This command displays the total size of each table and materialized view in the
373
405
 
374
406
  ```ruby
375
407
 
376
- RubyPGExtras.unused_indexes(args: { min_scans: 20 })
408
+ RubyPGExtras.unused_indexes(args: { max_scans: 20 })
377
409
 
378
410
  table | index | index_size | index_scans
379
411
  ---------------------+--------------------------------------------+------------+-------------
@@ -527,6 +559,14 @@ RubyPGExtras.kill_all
527
559
 
528
560
  This commands kills all the currently active connections to the database. It can be useful as a last resort when your database is stuck in a deadlock.
529
561
 
562
+ ### `pg_stat_statements_reset`
563
+
564
+ ```ruby
565
+ RubyPGExtras.pg_stat_statements_reset
566
+ ```
567
+
568
+ This command discards all statistics gathered so far by pg_stat_statements.
569
+
530
570
  ### `buffercache_stats`
531
571
 
532
572
  ```ruby
@@ -0,0 +1,230 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'filesize'
4
+
5
+ module RubyPGExtras
6
+ class DiagnoseData
7
+ PG_EXTRAS_TABLE_CACHE_HIT_MIN_EXPECTED = "0.985"
8
+ PG_EXTRAS_INDEX_CACHE_HIT_MIN_EXPECTED = "0.985"
9
+ PG_EXTRAS_UNUSED_INDEXES_MAX_SCANS = 20
10
+ PG_EXTRAS_UNUSED_INDEXES_MIN_SIZE_BYTES = Filesize.from("1 MB").to_i # 1000000 bytes
11
+ PG_EXTRAS_NULL_INDEXES_MIN_SIZE_MB = 1 # 1 MB
12
+ PG_EXTRAS_NULL_MIN_NULL_FRAC_PERCENT = 50 # 50%
13
+ PG_EXTRAS_BLOAT_MIN_VALUE = 10
14
+ PG_EXTRAS_OUTLIERS_MIN_EXEC_RATIO = 33 # 33%
15
+
16
+ def self.call
17
+ new.call
18
+ end
19
+
20
+ def call
21
+ {
22
+ table_cache_hit: table_cache_hit,
23
+ index_cache_hit: index_cache_hit,
24
+ unused_indexes: unused_indexes,
25
+ null_indexes: null_indexes,
26
+ bloat: bloat,
27
+ duplicate_indexes: duplicate_indexes
28
+ }.yield_self do |checks|
29
+ extensions_data = query_module.extensions(in_format: :hash)
30
+ pg_stats_enabled = extensions_data.find do |el|
31
+ el.fetch("name") == "pg_stat_statements"
32
+ end.fetch("installed_version", false)
33
+
34
+ ssl_info_enabled = extensions_data.find do |el|
35
+ el.fetch("name") == "sslinfo"
36
+ end.fetch("installed_version", false)
37
+
38
+ if pg_stats_enabled
39
+ checks.merge!({
40
+ outliers: outliers
41
+ })
42
+ end
43
+
44
+ if ssl_info_enabled
45
+ checks.merge!({
46
+ ssl_used: ssl_used
47
+ })
48
+ end
49
+
50
+ checks
51
+ end
52
+ end
53
+
54
+ private
55
+
56
+ def query_module
57
+ RubyPGExtras
58
+ end
59
+
60
+ def table_cache_hit
61
+ min_expected = ENV.fetch(
62
+ "PG_EXTRAS_TABLE_CACHE_HIT_MIN_EXPECTED",
63
+ PG_EXTRAS_TABLE_CACHE_HIT_MIN_EXPECTED
64
+ ).to_f
65
+
66
+ table_cache_hit_ratio = query_module.cache_hit(in_format: :hash)[1].fetch("ratio").to_f.round(6)
67
+
68
+ if table_cache_hit_ratio > min_expected
69
+ {
70
+ ok: true,
71
+ message: "Table cache hit ratio is correct: #{table_cache_hit_ratio}."
72
+ }
73
+ else
74
+ {
75
+ ok: false,
76
+ message: "Table hit ratio is too low: #{table_cache_hit_ratio}."
77
+ }
78
+ end
79
+ end
80
+
81
+ def index_cache_hit
82
+ min_expected = ENV.fetch(
83
+ "PG_EXTRAS_INDEX_CACHE_HIT_MIN_EXPECTED",
84
+ PG_EXTRAS_INDEX_CACHE_HIT_MIN_EXPECTED
85
+ ).to_f
86
+
87
+ index_cache_hit_ratio = query_module.cache_hit(in_format: :hash)[0].fetch("ratio").to_f.round(6)
88
+
89
+ if index_cache_hit_ratio > min_expected
90
+ {
91
+ ok: true,
92
+ message: "Index hit ratio is correct: #{index_cache_hit_ratio}."
93
+ }
94
+ else
95
+ {
96
+ ok: false,
97
+ message: "Index hit ratio is too low: #{index_cache_hit_ratio}."
98
+ }
99
+ end
100
+ end
101
+
102
+ def ssl_used
103
+ ssl_connection = query_module.ssl_used(in_format: :hash)[0].fetch("ssl_is_used")
104
+
105
+ if ssl_connection
106
+ {
107
+ ok: true,
108
+ message: "Database client is using a secure SSL connection."
109
+ }
110
+ else
111
+ {
112
+ ok: false,
113
+ message: "Database client is using an unencrypted connection."
114
+ }
115
+ end
116
+ end
117
+
118
+ def unused_indexes
119
+ indexes = query_module.unused_indexes(
120
+ in_format: :hash,
121
+ args: { min_scans: PG_EXTRAS_UNUSED_INDEXES_MAX_SCANS }
122
+ ).select do |i|
123
+ Filesize.from(i.fetch("index_size")).to_i >= PG_EXTRAS_UNUSED_INDEXES_MIN_SIZE_BYTES
124
+ end
125
+
126
+ if indexes.count == 0
127
+ {
128
+ ok: true,
129
+ message: "No unused indexes detected."
130
+ }
131
+ else
132
+ print_indexes = indexes.map do |i|
133
+ "'#{i.fetch('index')}' on '#{i.fetch('table')}' size #{i.fetch('index_size')}"
134
+ end.join(",\n")
135
+ {
136
+ ok: false,
137
+ message: "Unused indexes detected:\n#{print_indexes}"
138
+ }
139
+ end
140
+ end
141
+
142
+ def null_indexes
143
+ indexes = query_module.null_indexes(
144
+ in_format: :hash,
145
+ args: { min_relation_size_mb: PG_EXTRAS_NULL_INDEXES_MIN_SIZE_MB }
146
+ ).select do |i|
147
+ i.fetch("null_frac").gsub("%", "").to_f >= PG_EXTRAS_NULL_MIN_NULL_FRAC_PERCENT
148
+ end
149
+
150
+ if indexes.count == 0
151
+ {
152
+ ok: true,
153
+ message: "No null indexes detected."
154
+ }
155
+ else
156
+ print_indexes = indexes.map do |i|
157
+ "'#{i.fetch('index')}' size #{i.fetch('index_size')} null values fraction #{i.fetch('null_frac')}"
158
+ end.join(",\n")
159
+ {
160
+ ok: false,
161
+ message: "Null indexes detected:\n#{print_indexes}"
162
+ }
163
+ end
164
+ end
165
+
166
+ def bloat
167
+ bloat_data = query_module.bloat(in_format: :hash).select do |b|
168
+ b.fetch("bloat").to_f >= PG_EXTRAS_BLOAT_MIN_VALUE
169
+ end
170
+
171
+ if bloat_data.count == 0
172
+ {
173
+ ok: true,
174
+ message: "No bloat detected."
175
+ }
176
+ else
177
+ print_bloat = bloat_data.map do |b|
178
+ "'#{b.fetch('object_name')}' bloat #{b.fetch('bloat')} waste #{b.fetch('waste')}"
179
+ end.join(",\n")
180
+
181
+ {
182
+ ok: false,
183
+ message: "Bloat detected:\n#{print_bloat}"
184
+ }
185
+ end
186
+ end
187
+
188
+ def duplicate_indexes
189
+ indexes = query_module.duplicate_indexes(in_format: :hash)
190
+
191
+ if indexes.count == 0
192
+ {
193
+ ok: true,
194
+ message: "No duplicate indexes detected."
195
+ }
196
+ else
197
+ print_indexes = indexes.map do |i|
198
+ "'#{i.fetch('idx1')}' of size #{i.fetch('size')} is identical to '#{i.fetch('idx2')}'"
199
+ end.join(",\n")
200
+
201
+ {
202
+ ok: false,
203
+ message: "Duplicate indexes detected:\n#{print_indexes}"
204
+ }
205
+ end
206
+ end
207
+
208
+ def outliers
209
+ queries = query_module.outliers(in_format: :hash).select do |q|
210
+ q.fetch("prop_exec_time").gsub("%", "").to_f >= PG_EXTRAS_OUTLIERS_MIN_EXEC_RATIO
211
+ end
212
+
213
+ if queries.count == 0
214
+ {
215
+ ok: true,
216
+ message: "No queries using significant execution ratio detected."
217
+ }
218
+ else
219
+ print_queries = queries.map do |q|
220
+ "'#{q.fetch('query').slice(0, 30)}...' called #{q.fetch('ncalls')} times, using #{q.fetch('prop_exec_time')} of total exec time."
221
+ end.join(",\n")
222
+
223
+ {
224
+ ok: false,
225
+ message: "Queries using significant execution ratio detected:\n#{print_queries}"
226
+ }
227
+ end
228
+ end
229
+ end
230
+ end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'terminal-table'
4
+
5
+ module RubyPGExtras
6
+ class DiagnosePrint
7
+ def self.call(data)
8
+ new.call(data)
9
+ end
10
+
11
+ def call(data)
12
+ rows = data.sort do |key|
13
+ key[1].fetch(:ok) ? 1 : -1
14
+ end.map do |el|
15
+ key = el[0]
16
+ val = el[1]
17
+ symbol = val.fetch(:ok) ? "√" : "x"
18
+ color = val.fetch(:ok) ? :green : :red
19
+
20
+ [
21
+ colorize("[#{symbol}] - #{key}", color),
22
+ colorize(val.fetch(:message), color)
23
+ ]
24
+ end
25
+
26
+ puts Terminal::Table.new(
27
+ title: title,
28
+ rows: rows
29
+ )
30
+ end
31
+
32
+ private
33
+
34
+ def title
35
+ "ruby-pg-extras - diagnose report"
36
+ end
37
+
38
+ def colorize(string, color)
39
+ if color == :red
40
+ "\e[0;31;49m#{string}\e[0m"
41
+ elsif color == :green
42
+ "\e[0;32;49m#{string}\e[0m"
43
+ else
44
+ raise "Unsupported color: #{color}"
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,5 @@
1
+ /* Configure extensions necessary for other queries to work */
2
+
3
+ CREATE EXTENSION IF NOT EXISTS sslinfo;
4
+ CREATE EXTENSION IF NOT EXISTS pg_buffercache;
5
+ CREATE EXTENSION IF NOT EXISTS pg_stat_statements;
@@ -1,4 +1,4 @@
1
- /* Multiple indexes that have the same set of columns, same opclass, expression and predicate -- which make them equivalent. */
1
+ /* Multiple indexes that have the same set of columns, same opclass, expression and predicate. */
2
2
 
3
3
  SELECT pg_size_pretty(sum(pg_relation_size(idx))::bigint) as size,
4
4
  (array_agg(idx))[1] as idx1, (array_agg(idx))[2] as idx2,
@@ -0,0 +1,3 @@
1
+ /* pg_stat_statements_reset discards statistics gathered so far by pg_stat_statements */
2
+
3
+ SELECT pg_stat_statements_reset();
@@ -0,0 +1,3 @@
1
+ /* Check if SSL connection is used */
2
+
3
+ SELECT ssl_is_used();
@@ -11,6 +11,6 @@ SELECT
11
11
  idx_scan as index_scans
12
12
  FROM pg_stat_user_indexes ui
13
13
  JOIN pg_index i ON ui.indexrelid = i.indexrelid
14
- WHERE NOT indisunique AND idx_scan < %{min_scans} AND pg_relation_size(relid) > 5 * 8192
14
+ WHERE NOT indisunique AND idx_scan < %{max_scans} AND pg_relation_size(relid) > 5 * 8192
15
15
  ORDER BY pg_relation_size(i.indexrelid) / nullif(idx_scan, 0) DESC NULLS FIRST,
16
16
  pg_relation_size(i.indexrelid) DESC;
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module RubyPGExtras
4
- VERSION = "2.1.0"
4
+ VERSION = "3.0.6"
5
5
  end
@@ -3,20 +3,23 @@
3
3
  require 'terminal-table'
4
4
  require 'uri'
5
5
  require 'pg'
6
+ require 'ruby-pg-extras/diagnose_data'
7
+ require 'ruby-pg-extras/diagnose_print'
6
8
 
7
9
  module RubyPGExtras
8
10
  @@database_url = nil
9
11
  NEW_PG_STAT_STATEMENTS = "1.8"
10
12
 
11
13
  QUERIES = %i(
12
- bloat blocking cache_hit db_settings
14
+ add_extensions bloat blocking cache_hit db_settings
13
15
  calls extensions table_cache_hit index_cache_hit
14
16
  index_size index_usage null_indexes locks all_locks
15
17
  long_running_queries mandelbrot outliers
16
18
  records_rank seq_scans table_indexes_size
17
19
  table_size total_index_size total_table_size
18
20
  unused_indexes duplicate_indexes vacuum_stats kill_all
19
- buffercache_stats buffercache_usage
21
+ pg_stat_statements_reset buffercache_stats
22
+ buffercache_usage ssl_used
20
23
  )
21
24
 
22
25
  DEFAULT_ARGS = Hash.new({}).merge({
@@ -27,7 +30,7 @@ module RubyPGExtras
27
30
  outliers_legacy: { limit: 10 },
28
31
  buffercache_stats: { limit: 10 },
29
32
  buffercache_usage: { limit: 20 },
30
- unused_indexes: { min_scans: 50 },
33
+ unused_indexes: { max_scans: 50 },
31
34
  null_indexes: { min_relation_size_mb: 10 }
32
35
  })
33
36
 
@@ -66,6 +69,18 @@ module RubyPGExtras
66
69
  )
67
70
  end
68
71
 
72
+ def self.diagnose(in_format: :display_table)
73
+ data = RubyPGExtras::DiagnoseData.call
74
+
75
+ if in_format == :display_table
76
+ RubyPGExtras::DiagnosePrint.call(data)
77
+ elsif in_format == :hash
78
+ data
79
+ else
80
+ raise "Invalid 'in_format' argument!"
81
+ end
82
+ end
83
+
69
84
  def self.display_result(result, title:, in_format:)
70
85
  case in_format
71
86
  when :array
Binary file
@@ -16,6 +16,7 @@ Gem::Specification.new do |gem|
16
16
  gem.require_paths = ["lib"]
17
17
  gem.license = "MIT"
18
18
  gem.add_dependency "pg"
19
+ gem.add_dependency "filesize"
19
20
  gem.add_dependency "terminal-table"
20
21
  gem.add_development_dependency "rake"
21
22
  gem.add_development_dependency "rspec"
@@ -0,0 +1,76 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ describe RubyPGExtras::DiagnoseData do
6
+ subject(:result) do
7
+ RubyPGExtras::DiagnoseData.call
8
+ end
9
+
10
+ describe "call" do
11
+ context "stubbed cases" do
12
+ before do
13
+ expect(RubyPGExtras).to receive(:unused_indexes) {
14
+ [
15
+ { "table" => "public.plans", "index" => "index_plans_on_payer_id", "index_size" => "16 MB", "index_scans" => 0 },
16
+ { "table" => "public.feedbacks", "index" => "index_feedbacks_on_target_id", "index_size" => "80 kB", "index_scans" => 1 },
17
+ { "table" => "public.channels", "index" => "index_channels_on_slack_id", "index_size" => "56 MB", "index_scans" => 7}
18
+ ]
19
+ }
20
+
21
+ expect(RubyPGExtras).to receive(:null_indexes) {
22
+ [
23
+ { "oid" => 123, "index" => "index_plans_on_payer_id", "index_size" => "16 MB", "unique" => "t", "null_frac" => "00.00%", "expected_saving" => "0 kb" },
24
+ { "oid" => 321, "index" => "index_feedbacks_on_target_id", "index_size" => "80 kB", "unique" => "f", "null_frac" => "97.00%", "expected_saving" => "77 kb" },
25
+ { "oid" => 231, "index" => "index_channels_on_slack_id", "index_size" => "56 MB", "unique" => "t", "null_frac" => "49.99%", "expected_saving" => "28 MB" }
26
+ ]
27
+ }
28
+
29
+ expect(RubyPGExtras).to receive(:bloat) {
30
+ [
31
+ { "type" => "table", "schemaname" => "public", "object_name" => "bloated_table_1", "bloat" => 8, "waste" => "0 kb" },
32
+ { "type" => "table", "schemaname" => "public", "object_name" => "bloated_table_2", "bloat" => 8, "waste" => "77 kb" },
33
+ { "type" => "schemaname", "public" => "index_channels_on_slack_id", "object_name" => "bloated_index", "bloat" => 11, "waste" => "28 MB" }
34
+ ]
35
+ }
36
+
37
+ expect(RubyPGExtras).to receive(:duplicate_indexes) {
38
+ [
39
+ { "size" => "128 kb", "idx1" => "users_pkey", "idx2" => "index_users_id" }
40
+ ]
41
+ }
42
+
43
+ expect(RubyPGExtras).to receive(:outliers) {
44
+ [
45
+ { "query" => "SELECT * FROM users WHERE users.age > 20 AND users.height > 160", "exec_time" => "154:39:26.431466", "prop_exec_time" => "72.2%", "ncalls" => "34,211,877", "sync_io_time" => "00:34:19.784318" }
46
+ ]
47
+ }
48
+ end
49
+
50
+ it "works" do
51
+ expect {
52
+ RubyPGExtras::DiagnosePrint.call(result)
53
+ }.not_to raise_error
54
+ end
55
+ end
56
+
57
+ context "real database data" do
58
+ before do
59
+ expect(RubyPGExtras).to receive(:unused_indexes) {
60
+ [
61
+ { "table" => "public.plans", "index" => "index_plans_on_payer_id", "index_size" => "2 MB", "index_scans" => 0 },
62
+ { "table" => "public.feedbacks", "index" => "index_feedbacks_on_target_id", "index_size" => "80 kB", "index_scans" => 1 },
63
+ { "table" => "public.channels", "index" => "index_channels_on_slack_id", "index_size" => "1.1 MB", "index_scans" => 7}
64
+ ]
65
+ }
66
+
67
+ end
68
+
69
+ it "works" do
70
+ expect {
71
+ RubyPGExtras::DiagnosePrint.call(result)
72
+ }.not_to raise_error
73
+ end
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ describe RubyPGExtras::DiagnosePrint do
6
+ subject(:print_result) do
7
+ RubyPGExtras::DiagnosePrint.call(data)
8
+ end
9
+
10
+ let(:data) do
11
+ {
12
+ :table_cache_hit => {
13
+ :ok => false,
14
+ :message => "Table hit ratio too low: 0.906977."
15
+ },
16
+ :index_cache_hit => {
17
+ :ok => false,
18
+ :message => "Index hit ratio is too low: 0.818182."
19
+ },
20
+ :ssl_used => {
21
+ :ok => true,
22
+ :message => "Database client is using a secure SSL connection."
23
+ }
24
+ }
25
+ end
26
+
27
+ describe "call" do
28
+ it "works" do
29
+ expect {
30
+ print_result
31
+ }.not_to raise_error
32
+ end
33
+ end
34
+ end
data/spec/smoke_spec.rb CHANGED
@@ -3,11 +3,6 @@
3
3
  require 'spec_helper'
4
4
 
5
5
  describe RubyPGExtras do
6
- before(:all) do
7
- RubyPGExtras.connection.exec("CREATE EXTENSION IF NOT EXISTS pg_buffercache;")
8
- RubyPGExtras.connection.exec("CREATE EXTENSION IF NOT EXISTS pg_stat_statements;")
9
- end
10
-
11
6
  RubyPGExtras::QUERIES.each do |query_name|
12
7
  it "#{query_name} description can be read" do
13
8
  expect do
data/spec/spec_helper.rb CHANGED
@@ -17,3 +17,11 @@ else
17
17
  end
18
18
 
19
19
  ENV["DATABASE_URL"] ||= "postgresql://postgres:secret@localhost:#{port}/ruby-pg-extras-test"
20
+
21
+ RSpec.configure do |config|
22
+ config.before(:suite) do
23
+ RubyPGExtras.connection.exec("CREATE EXTENSION IF NOT EXISTS pg_stat_statements;")
24
+ RubyPGExtras.connection.exec("CREATE EXTENSION IF NOT EXISTS pg_buffercache;")
25
+ RubyPGExtras.connection.exec("CREATE EXTENSION IF NOT EXISTS sslinfo;")
26
+ end
27
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruby-pg-extras
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.1.0
4
+ version: 3.0.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - pawurb
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-10-15 00:00:00.000000000 Z
11
+ date: 2021-10-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: pg
@@ -24,6 +24,20 @@ dependencies:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
26
  version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: filesize
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
27
41
  - !ruby/object:Gem::Dependency
28
42
  name: terminal-table
29
43
  requirement: !ruby/object:Gem::Requirement
@@ -83,6 +97,9 @@ files:
83
97
  - Rakefile
84
98
  - docker-compose.yml.sample
85
99
  - lib/ruby-pg-extras.rb
100
+ - lib/ruby-pg-extras/diagnose_data.rb
101
+ - lib/ruby-pg-extras/diagnose_print.rb
102
+ - lib/ruby-pg-extras/queries/add_extensions.sql
86
103
  - lib/ruby-pg-extras/queries/all_locks.sql
87
104
  - lib/ruby-pg-extras/queries/bloat.sql
88
105
  - lib/ruby-pg-extras/queries/blocking.sql
@@ -104,8 +121,10 @@ files:
104
121
  - lib/ruby-pg-extras/queries/null_indexes.sql
105
122
  - lib/ruby-pg-extras/queries/outliers.sql
106
123
  - lib/ruby-pg-extras/queries/outliers_legacy.sql
124
+ - lib/ruby-pg-extras/queries/pg_stat_statements_reset.sql
107
125
  - lib/ruby-pg-extras/queries/records_rank.sql
108
126
  - lib/ruby-pg-extras/queries/seq_scans.sql
127
+ - lib/ruby-pg-extras/queries/ssl_used.sql
109
128
  - lib/ruby-pg-extras/queries/table_cache_hit.sql
110
129
  - lib/ruby-pg-extras/queries/table_indexes_size.sql
111
130
  - lib/ruby-pg-extras/queries/table_size.sql
@@ -114,7 +133,10 @@ files:
114
133
  - lib/ruby-pg-extras/queries/unused_indexes.sql
115
134
  - lib/ruby-pg-extras/queries/vacuum_stats.sql
116
135
  - lib/ruby-pg-extras/version.rb
136
+ - ruby-pg-extras-diagnose.png
117
137
  - ruby-pg-extras.gemspec
138
+ - spec/diagnose_data_spec.rb
139
+ - spec/diagnose_print_spec.rb
118
140
  - spec/smoke_spec.rb
119
141
  - spec/spec_helper.rb
120
142
  homepage: http://github.com/pawurb/ruby-pg-extras
@@ -141,5 +163,7 @@ signing_key:
141
163
  specification_version: 4
142
164
  summary: Ruby PostgreSQL performance database insights
143
165
  test_files:
166
+ - spec/diagnose_data_spec.rb
167
+ - spec/diagnose_print_spec.rb
144
168
  - spec/smoke_spec.rb
145
169
  - spec/spec_helper.rb