ruby-pg-extras 1.6.0 → 2.3.0

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: 517f1d39dfe5d78d1e8f7650767664b9bc349bf062efb60b421e568afe65be82
4
- data.tar.gz: b60152d78952c4dc8bcee791d445d1aa0028d60bb0af47736095c5b587f605bd
3
+ metadata.gz: a93ebb6cb69e2591e7e9c327b326edf9353105b985f0023ec00deceec631e398
4
+ data.tar.gz: 0e6b50933c072c9c04fba24875d71c0c561ecf4b5a0cb60f8cb66df9cb000075
5
5
  SHA512:
6
- metadata.gz: 8d827609746ab9a203a80c40945ca4b394d9014443d3fb89ae938734d996e3dc3e7f9bce584a7d98297a490365b76e8209e3d43501f932c9c7d39078891f77d4
7
- data.tar.gz: 95dd7b3bb562e194ddac894b9f1e66d05d37d933561333a127ae9dca649c0e2b2259a46b518117b06bcce72729574250b46be935e3f2245eaf9c812ed48346ad
6
+ metadata.gz: b855303347b32b4e20e9a9061f058843f8a337968344d3517a14a70000849691f378e077163720f487c2798df441c1a59f7f53e80b6d45ef2fe7796854cf79f6
7
+ data.tar.gz: 4429a4634e5b154dca182bdb232c64009178f287a00b1c58594dd089221ab2cc1520522ba2112153dd645905685d7751b7c51f9d9a87968d0ab416af93797d26
data/.circleci/config.yml CHANGED
@@ -6,6 +6,22 @@ jobs:
6
6
  environment:
7
7
  DATABASE_URL: postgresql://postgres:secret@localhost:5432/ruby-pg-extras-test
8
8
  - image: circleci/postgres:11.5
9
+ command: postgres -c shared_preload_libraries=pg_stat_statements
10
+ name: postgres11
11
+ environment:
12
+ POSTGRES_USER: postgres
13
+ POSTGRES_DB: ruby-pg-extras-test
14
+ POSTGRES_PASSWORD: secret
15
+ - image: circleci/postgres:12.7
16
+ command: postgres -c shared_preload_libraries=pg_stat_statements
17
+ name: postgres12
18
+ environment:
19
+ POSTGRES_USER: postgres
20
+ POSTGRES_DB: ruby-pg-extras-test
21
+ POSTGRES_PASSWORD: secret
22
+ - image: circleci/postgres:13.3
23
+ command: postgres -c shared_preload_libraries=pg_stat_statements
24
+ name: postgres13
9
25
  environment:
10
26
  POSTGRES_USER: postgres
11
27
  POSTGRES_DB: ruby-pg-extras-test
@@ -16,13 +32,24 @@ jobs:
16
32
  - run: gem update --system
17
33
  - run: gem install bundler
18
34
  - run: bundle install --path vendor/bundle
19
- - run: sudo apt-get update
35
+ - run: sudo apt-get update --allow-releaseinfo-change
20
36
  - run: sudo apt install postgresql-client-11
21
- - run: dockerize -wait tcp://localhost:5432 -timeout 1m
37
+ - run: dockerize -wait tcp://postgres11:5432 -timeout 1m
38
+ - run:
39
+ name: Run specs for PG 11
40
+ environment:
41
+ DATABASE_URL: postgresql://postgres:secret@postgres11:5432/ruby-pg-extras-test
42
+ command: bundle exec rspec spec/
43
+ - run:
44
+ name: Run specs for PG 12
45
+ environment:
46
+ DATABASE_URL: postgresql://postgres:secret@postgres12:5432/ruby-pg-extras-test
47
+ command: bundle exec rspec spec/
22
48
  - run:
23
- name: Run specs
24
- command: |
25
- bundle exec rspec spec/
49
+ name: Run specs for PG 13
50
+ environment:
51
+ DATABASE_URL: postgresql://postgres:secret@postgres13:5432/ruby-pg-extras-test
52
+ command: bundle exec rspec spec/
26
53
  workflows:
27
54
  version: 2
28
55
  test:
data/README.md CHANGED
@@ -167,6 +167,20 @@ This method displays values for selected PostgreSQL settings. You can compare th
167
167
 
168
168
  [More info](https://pawelurbanek.com/postgresql-fix-performance#cache-hit)
169
169
 
170
+ ### `ssl_used`
171
+
172
+ ```ruby
173
+
174
+ RubyPGExtras.ssl_used
175
+
176
+ | ssl_is_used |
177
+ +---------------------------------+
178
+ | t |
179
+
180
+ ```
181
+
182
+ Returns boolean indicating if an encrypted SSL is currently used. Connecting to the database via an unencrypted connection is a critical security risk.
183
+
170
184
  ### `index_usage`
171
185
 
172
186
  ```ruby
@@ -387,6 +401,19 @@ This command displays indexes that have < 50 scans recorded against them, and ar
387
401
 
388
402
  [More info](https://pawelurbanek.com/postgresql-fix-performance#unused-indexes)
389
403
 
404
+ ### `duplicate_indexes`
405
+
406
+ ```ruby
407
+
408
+ RubyPGExtras.duplicate_indexes
409
+
410
+ | size | idx1 | idx2 | idx3 | idx4 |
411
+ +------------+--------------+----------------+----------+-----------+
412
+ | 128 k | users_pkey | index_users_id | | |
413
+ ```
414
+
415
+ This command displays multiple indexes that have the same set of columns, same opclass, expression and predicate - which make them equivalent. Usually it's safe to drop one of them.
416
+
390
417
  ### `null_indexes`
391
418
 
392
419
  ```ruby
@@ -514,6 +541,30 @@ RubyPGExtras.kill_all
514
541
 
515
542
  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.
516
543
 
544
+ ### `pg_stat_statements_reset`
545
+
546
+ ```ruby
547
+ RubyPGExtras.pg_stat_statements_reset
548
+ ```
549
+
550
+ This command discards all statistics gathered so far by pg_stat_statements.
551
+
552
+ ### `buffercache_stats`
553
+
554
+ ```ruby
555
+ RubyPGExtras.buffercache_stats(args: { limit: 10 })
556
+ ```
557
+
558
+ This command shows the relations buffered in database share buffer, ordered by percentage taken. It also shows that how much of the whole relation is buffered.
559
+
560
+ ### `buffercache_usage`
561
+
562
+ ```ruby
563
+ RubyPGExtras.buffercache_usage(args: { limit: 20 })
564
+ ```
565
+
566
+ This command calculates how many blocks from which table are currently cached.
567
+
517
568
  ### `extensions`
518
569
 
519
570
  ```ruby
@@ -534,18 +585,17 @@ RubyPGExtras.mandelbrot
534
585
 
535
586
  This command outputs the Mandelbrot set, calculated through SQL.
536
587
 
537
- ### `buffercache_stats`
538
-
539
- This command shows the relations buffered in database share buffer, ordered by percentage taken. It also shows that how much of the whole relation is buffered.
588
+ ## Testing
540
589
 
541
- ```ruby
542
- RubyPGExtras.buffercache_stats(args: { limit: 10 })
590
+ ```bash
591
+ cp docker-compose.yml.sample docker-compose.yml
592
+ docker compose up -d
593
+ rake test_all
543
594
  ```
544
595
 
545
- ### `buffercache_usage`
546
-
547
- This command calculates how many blocks from which table are currently cached.
596
+ ## Query sources
548
597
 
549
- ```ruby
550
- RubyPGExtras.buffercache_usage(args: { limit: 20 })
551
- ```
598
+ - [https://github.com/heroku/heroku-pg-extras](https://github.com/heroku/heroku-pg-extras)
599
+ - [https://hakibenita.com/postgresql-unused-index-size](https://hakibenita.com/postgresql-unused-index-size)
600
+ - [https://sites.google.com/site/itmyshare/database-tips-and-examples/postgres/useful-sqls-to-check-contents-of-postgresql-shared_buffer](https://sites.google.com/site/itmyshare/database-tips-and-examples/postgres/useful-sqls-to-check-contents-of-postgresql-shared_buffer)
601
+ - [https://wiki.postgresql.org/wiki/Index_Maintenance](https://wiki.postgresql.org/wiki/Index_Maintenance)
data/Rakefile CHANGED
@@ -3,3 +3,7 @@ require 'rspec/core/rake_task'
3
3
 
4
4
  RSpec::Core::RakeTask.new(:spec)
5
5
 
6
+ desc 'Test all PG versions'
7
+ task :test_all do
8
+ system("PG_VERSION=11 bundle exec rspec spec/ && PG_VERSION=12 bundle exec rspec spec/ && PG_VERSION=13 bundle exec rspec spec/")
9
+ end
@@ -1,11 +1,30 @@
1
1
  version: '3'
2
2
 
3
3
  services:
4
- postgres:
4
+ postgres11:
5
5
  image: postgres:11.5-alpine
6
+ command: postgres -c shared_preload_libraries=pg_stat_statements
6
7
  environment:
7
8
  POSTGRES_USER: postgres
8
9
  POSTGRES_DB: ruby-pg-extras-test
9
10
  POSTGRES_PASSWORD: secret
10
11
  ports:
11
12
  - '5432:5432'
13
+ postgres12:
14
+ image: postgres:12.7-alpine
15
+ command: postgres -c shared_preload_libraries=pg_stat_statements
16
+ environment:
17
+ POSTGRES_USER: postgres
18
+ POSTGRES_DB: ruby-pg-extras-test
19
+ POSTGRES_PASSWORD: secret
20
+ ports:
21
+ - '5433:5432'
22
+ postgres13:
23
+ image: postgres:13.3-alpine
24
+ command: postgres -c shared_preload_libraries=pg_stat_statements
25
+ environment:
26
+ POSTGRES_USER: postgres
27
+ POSTGRES_DB: ruby-pg-extras-test
28
+ POSTGRES_PASSWORD: secret
29
+ ports:
30
+ - '5434:5432'
@@ -1,8 +1,8 @@
1
1
  /* Queries that have highest frequency of execution */
2
2
 
3
3
  SELECT query AS qry,
4
- interval '1 millisecond' * total_time AS exec_time,
5
- to_char((total_time/sum(total_time) OVER()) * 100, 'FM90D0') || '%%' AS prop_exec_time,
4
+ interval '1 millisecond' * total_exec_time AS exec_time,
5
+ to_char((total_exec_time/sum(total_exec_time) OVER()) * 100, 'FM90D0') || '%%' AS prop_exec_time,
6
6
  to_char(calls, 'FM999G999G990') AS ncalls,
7
7
  interval '1 millisecond' * (blk_read_time + blk_write_time) AS sync_io_time
8
8
  FROM pg_stat_statements WHERE userid = (SELECT usesysid FROM pg_user WHERE usename = current_user LIMIT 1)
@@ -0,0 +1,9 @@
1
+ /* Queries that have highest frequency of execution */
2
+
3
+ SELECT query AS qry,
4
+ interval '1 millisecond' * total_time AS exec_time,
5
+ to_char((total_time/sum(total_time) OVER()) * 100, 'FM90D0') || '%%' AS prop_exec_time,
6
+ to_char(calls, 'FM999G999G990') AS ncalls,
7
+ interval '1 millisecond' * (blk_read_time + blk_write_time) AS sync_io_time
8
+ FROM pg_stat_statements WHERE userid = (SELECT usesysid FROM pg_user WHERE usename = current_user LIMIT 1)
9
+ ORDER BY calls DESC LIMIT %{limit};
@@ -0,0 +1,11 @@
1
+ /* Multiple indexes that have the same set of columns, same opclass, expression and predicate. */
2
+
3
+ SELECT pg_size_pretty(sum(pg_relation_size(idx))::bigint) as size,
4
+ (array_agg(idx))[1] as idx1, (array_agg(idx))[2] as idx2,
5
+ (array_agg(idx))[3] as idx3, (array_agg(idx))[4] as idx4
6
+ FROM (
7
+ SELECT indexrelid::regclass as idx, (indrelid::text ||E'\n'|| indclass::text ||E'\n'|| indkey::text ||E'\n'||
8
+ coalesce(indexprs::text,'')||E'\n' || coalesce(indpred::text,'')) as key
9
+ FROM pg_index) sub
10
+ GROUP BY key HAVING count(*)>1
11
+ ORDER BY sum(pg_relation_size(idx)) DESC;
@@ -1,10 +1,10 @@
1
1
  /* Queries that have longest execution time in aggregate */
2
2
 
3
- SELECT interval '1 millisecond' * total_time AS total_exec_time,
4
- to_char((total_time/sum(total_time) OVER()) * 100, 'FM90D0') || '%%' AS prop_exec_time,
3
+ SELECT interval '1 millisecond' * total_exec_time AS total_exec_time,
4
+ to_char((total_exec_time/sum(total_exec_time) OVER()) * 100, 'FM90D0') || '%%' AS prop_exec_time,
5
5
  to_char(calls, 'FM999G999G999G990') AS ncalls,
6
6
  interval '1 millisecond' * (blk_read_time + blk_write_time) AS sync_io_time,
7
7
  query AS query
8
8
  FROM pg_stat_statements WHERE userid = (SELECT usesysid FROM pg_user WHERE usename = current_user LIMIT 1)
9
- ORDER BY total_time DESC
9
+ ORDER BY total_exec_time DESC
10
10
  LIMIT %{limit};
@@ -0,0 +1,10 @@
1
+ /* Queries that have longest execution time in aggregate */
2
+
3
+ SELECT interval '1 millisecond' * total_time AS total_exec_time,
4
+ to_char((total_time/sum(total_time) OVER()) * 100, 'FM90D0') || '%%' AS prop_exec_time,
5
+ to_char(calls, 'FM999G999G999G990') AS ncalls,
6
+ interval '1 millisecond' * (blk_read_time + blk_write_time) AS sync_io_time,
7
+ query AS query
8
+ FROM pg_stat_statements WHERE userid = (SELECT usesysid FROM pg_user WHERE usename = current_user LIMIT 1)
9
+ ORDER BY total_time DESC
10
+ LIMIT %{limit};
@@ -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,4 @@
1
+ /* Check if SSL connection is used */
2
+
3
+ CREATE EXTENSION IF NOT EXISTS sslinfo;
4
+ SELECT ssl_is_used();
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module RubyPGExtras
4
- VERSION = "1.6.0"
4
+ VERSION = "2.3.0"
5
5
  end
@@ -6,6 +6,7 @@ require 'pg'
6
6
 
7
7
  module RubyPGExtras
8
8
  @@database_url = nil
9
+ NEW_PG_STAT_STATEMENTS = "1.8"
9
10
 
10
11
  QUERIES = %i(
11
12
  bloat blocking cache_hit db_settings
@@ -14,14 +15,17 @@ module RubyPGExtras
14
15
  long_running_queries mandelbrot outliers
15
16
  records_rank seq_scans table_indexes_size
16
17
  table_size total_index_size total_table_size
17
- unused_indexes vacuum_stats kill_all
18
- buffercache_stats buffercache_usage
18
+ unused_indexes duplicate_indexes vacuum_stats kill_all
19
+ pg_stat_statements_reset buffercache_stats
20
+ buffercache_usage ssl_used
19
21
  )
20
22
 
21
23
  DEFAULT_ARGS = Hash.new({}).merge({
22
24
  calls: { limit: 10 },
25
+ calls_legacy: { limit: 10 },
23
26
  long_running_queries: { threshold: "500 milliseconds" },
24
27
  outliers: { limit: 10 },
28
+ outliers_legacy: { limit: 10 },
25
29
  buffercache_stats: { limit: 10 },
26
30
  buffercache_usage: { limit: 20 },
27
31
  unused_indexes: { min_scans: 50 },
@@ -39,6 +43,16 @@ module RubyPGExtras
39
43
  end
40
44
 
41
45
  def self.run_query(query_name:, in_format:, args: {})
46
+ if %i(calls outliers).include?(query_name)
47
+ pg_stat_statements_ver = RubyPGExtras.connection.exec("select installed_version from pg_available_extensions where name='pg_stat_statements'")
48
+ .to_a[0].fetch("installed_version", nil)
49
+ if pg_stat_statements_ver != nil
50
+ if Gem::Version.new(pg_stat_statements_ver) < Gem::Version.new(NEW_PG_STAT_STATEMENTS)
51
+ query_name = "#{query_name}_legacy".to_sym
52
+ end
53
+ end
54
+ end
55
+
42
56
  sql = if (custom_args = DEFAULT_ARGS[query_name].merge(args)) != {}
43
57
  sql_for(query_name: query_name) % custom_args
44
58
  else
data/spec/smoke_spec.rb CHANGED
@@ -3,6 +3,11 @@
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
+
6
11
  RubyPGExtras::QUERIES.each do |query_name|
7
12
  it "#{query_name} description can be read" do
8
13
  expect do
@@ -13,9 +18,7 @@ describe RubyPGExtras do
13
18
  end
14
19
  end
15
20
 
16
- PG_STATS_DEPENDENT_QUERIES = %i(calls outliers)
17
-
18
- (RubyPGExtras::QUERIES - PG_STATS_DEPENDENT_QUERIES).each do |query_name|
21
+ RubyPGExtras::QUERIES.each do |query_name|
19
22
  it "#{query_name} query can be executed" do
20
23
  expect do
21
24
  RubyPGExtras.run_query(
data/spec/spec_helper.rb CHANGED
@@ -4,4 +4,16 @@ require 'rubygems'
4
4
  require 'bundler/setup'
5
5
  require_relative '../lib/ruby-pg-extras'
6
6
 
7
- ENV["DATABASE_URL"] ||= "postgresql://postgres:secret@localhost:5432/ruby-pg-extras-test"
7
+ pg_version = ENV["PG_VERSION"]
8
+
9
+ port = if pg_version == "11"
10
+ "5432"
11
+ elsif pg_version == "12"
12
+ "5433"
13
+ elsif pg_version == "13"
14
+ "5434"
15
+ else
16
+ "5432"
17
+ end
18
+
19
+ ENV["DATABASE_URL"] ||= "postgresql://postgres:secret@localhost:#{port}/ruby-pg-extras-test"
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: 1.6.0
4
+ version: 2.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - pawurb
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-03-29 00:00:00.000000000 Z
11
+ date: 2021-10-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: pg
@@ -90,7 +90,9 @@ files:
90
90
  - lib/ruby-pg-extras/queries/buffercache_usage.sql
91
91
  - lib/ruby-pg-extras/queries/cache_hit.sql
92
92
  - lib/ruby-pg-extras/queries/calls.sql
93
+ - lib/ruby-pg-extras/queries/calls_legacy.sql
93
94
  - lib/ruby-pg-extras/queries/db_settings.sql
95
+ - lib/ruby-pg-extras/queries/duplicate_indexes.sql
94
96
  - lib/ruby-pg-extras/queries/extensions.sql
95
97
  - lib/ruby-pg-extras/queries/index_cache_hit.sql
96
98
  - lib/ruby-pg-extras/queries/index_size.sql
@@ -101,8 +103,11 @@ files:
101
103
  - lib/ruby-pg-extras/queries/mandelbrot.sql
102
104
  - lib/ruby-pg-extras/queries/null_indexes.sql
103
105
  - lib/ruby-pg-extras/queries/outliers.sql
106
+ - lib/ruby-pg-extras/queries/outliers_legacy.sql
107
+ - lib/ruby-pg-extras/queries/pg_stat_statements_reset.sql
104
108
  - lib/ruby-pg-extras/queries/records_rank.sql
105
109
  - lib/ruby-pg-extras/queries/seq_scans.sql
110
+ - lib/ruby-pg-extras/queries/ssl_used.sql
106
111
  - lib/ruby-pg-extras/queries/table_cache_hit.sql
107
112
  - lib/ruby-pg-extras/queries/table_indexes_size.sql
108
113
  - lib/ruby-pg-extras/queries/table_size.sql
@@ -133,7 +138,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
133
138
  - !ruby/object:Gem::Version
134
139
  version: '0'
135
140
  requirements: []
136
- rubygems_version: 3.1.4
141
+ rubygems_version: 3.1.6
137
142
  signing_key:
138
143
  specification_version: 4
139
144
  summary: Ruby PostgreSQL performance database insights