ruby-pg-extras 2.1.0 → 3.0.6

Sign up to get free protection for your applications and to get access to all the features.
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