ruby-pg-extras 1.5.3 → 2.2.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3de471f9f4d008d006000667b4b52d4e0d8aec0c5dc9ef55ce6045e5e8f96994
4
- data.tar.gz: bb78196a09f0af1d7f4be623f1794223b3db074d19eb439e59670b8e960d06e1
3
+ metadata.gz: 67ee92c7a3fefa174bf843ee90a6d0247968726dbddfa57d83e02bec80614b3c
4
+ data.tar.gz: '0893af856168f741cfe505c39415211b079202684e4472f38614f76bc6bfc7e2'
5
5
  SHA512:
6
- metadata.gz: 7ed2d3f75a540b6e8b22db825a1479a53649ee3ddde59ca7783301f5e42d89f8a8c1d40e11b42a5f1340f3b72d866531a6878c57ccfb211f43ca00d21d82c177
7
- data.tar.gz: 5f6618ededb729f9d085cfdc5c5c722065ccd8beee6cb191bdfc3e6e58700fa2468d0f5aa3f7829fc9d520d7fb66fe248d897d989fe7fec9c21896cb2234bdad
6
+ metadata.gz: b63de82209cb9b52710c29360025058e88987ec14d48c5d59ab16f677512fab8d11f7042cd168358971b30421edf0910d59a69d082dc024440991cac5820ddf7
7
+ data.tar.gz: 4c9bd4431a96091ba6e5ba581ae7b7493a3b49d5329e0cd85903f90dbc13477e2d519a719ff078117b9e03a0b7dc89efb263eaafc3e458a4ff5ff6beb5e6fe23
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
@@ -165,6 +165,22 @@ RubyPGExtras.db_settings
165
165
 
166
166
  This method displays values for selected PostgreSQL settings. You can compare them with settings recommended by [PGTune](https://pgtune.leopard.in.ua/#/) and tweak values to improve performance.
167
167
 
168
+ [More info](https://pawelurbanek.com/postgresql-fix-performance#cache-hit)
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
+
168
184
  ### `index_usage`
169
185
 
170
186
  ```ruby
@@ -385,6 +401,19 @@ This command displays indexes that have < 50 scans recorded against them, and ar
385
401
 
386
402
  [More info](https://pawelurbanek.com/postgresql-fix-performance#unused-indexes)
387
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
+
388
417
  ### `null_indexes`
389
418
 
390
419
  ```ruby
@@ -512,6 +541,22 @@ RubyPGExtras.kill_all
512
541
 
513
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.
514
543
 
544
+ ### `buffercache_stats`
545
+
546
+ ```ruby
547
+ RubyPGExtras.buffercache_stats(args: { limit: 10 })
548
+ ```
549
+
550
+ 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.
551
+
552
+ ### `buffercache_usage`
553
+
554
+ ```ruby
555
+ RubyPGExtras.buffercache_usage(args: { limit: 20 })
556
+ ```
557
+
558
+ This command calculates how many blocks from which table are currently cached.
559
+
515
560
  ### `extensions`
516
561
 
517
562
  ```ruby
@@ -531,3 +576,18 @@ RubyPGExtras.mandelbrot
531
576
  ```
532
577
 
533
578
  This command outputs the Mandelbrot set, calculated through SQL.
579
+
580
+ ## Testing
581
+
582
+ ```bash
583
+ cp docker-compose.yml.sample docker-compose.yml
584
+ docker compose up -d
585
+ rake test_all
586
+ ```
587
+
588
+ ## Query sources
589
+
590
+ - [https://github.com/heroku/heroku-pg-extras](https://github.com/heroku/heroku-pg-extras)
591
+ - [https://hakibenita.com/postgresql-unused-index-size](https://hakibenita.com/postgresql-unused-index-size)
592
+ - [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)
593
+ - [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'
@@ -0,0 +1,13 @@
1
+ /* Calculates percentages of relations buffered in database share buffer */
2
+
3
+ SELECT
4
+ c.relname,
5
+ pg_size_pretty(count(*) * 8192) AS buffered,
6
+ round(100.0 * count(*) / (SELECT setting FROM pg_settings WHERE name = 'shared_buffers')::integer, 1) AS buffer_percent,
7
+ round(100.0 * count(*) * 8192 / pg_table_size(c.oid), 1) AS percent_of_relation
8
+ FROM pg_class c
9
+ INNER JOIN pg_buffercache b ON b.relfilenode = c.relfilenode
10
+ INNER JOIN pg_database d ON (b.reldatabase = d.oid AND d.datname = current_database())
11
+ GROUP BY c.oid,c.relname
12
+ ORDER BY 3 DESC
13
+ LIMIT %{limit};
@@ -0,0 +1,9 @@
1
+ /* Calculate how many blocks from which table are currently cached */
2
+
3
+ SELECT c.relname, count(*) AS buffers
4
+ FROM pg_class c
5
+ INNER JOIN pg_buffercache b ON b.relfilenode = c.relfilenode
6
+ INNER JOIN pg_database d ON (b.reldatabase = d.oid AND d.datname = current_database())
7
+ GROUP BY c.relname
8
+ ORDER BY 2 DESC
9
+ LIMIT %{limit};
@@ -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,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.5.3"
4
+ VERSION = "2.2.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,13 +15,18 @@ 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
+ unused_indexes duplicate_indexes vacuum_stats kill_all
19
+ buffercache_stats buffercache_usage ssl_used
18
20
  )
19
21
 
20
22
  DEFAULT_ARGS = Hash.new({}).merge({
21
23
  calls: { limit: 10 },
24
+ calls_legacy: { limit: 10 },
22
25
  long_running_queries: { threshold: "500 milliseconds" },
23
26
  outliers: { limit: 10 },
27
+ outliers_legacy: { limit: 10 },
28
+ buffercache_stats: { limit: 10 },
29
+ buffercache_usage: { limit: 20 },
24
30
  unused_indexes: { min_scans: 50 },
25
31
  null_indexes: { min_relation_size_mb: 10 }
26
32
  })
@@ -36,6 +42,16 @@ module RubyPGExtras
36
42
  end
37
43
 
38
44
  def self.run_query(query_name:, in_format:, args: {})
45
+ if %i(calls outliers).include?(query_name)
46
+ pg_stat_statements_ver = RubyPGExtras.connection.exec("select installed_version from pg_available_extensions where name='pg_stat_statements'")
47
+ .to_a[0].fetch("installed_version", nil)
48
+ if pg_stat_statements_ver != nil
49
+ if Gem::Version.new(pg_stat_statements_ver) < Gem::Version.new(NEW_PG_STAT_STATEMENTS)
50
+ query_name = "#{query_name}_legacy".to_sym
51
+ end
52
+ end
53
+ end
54
+
39
55
  sql = if (custom_args = DEFAULT_ARGS[query_name].merge(args)) != {}
40
56
  sql_for(query_name: query_name) % custom_args
41
57
  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.5.3
4
+ version: 2.2.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-02-16 00:00:00.000000000 Z
11
+ date: 2021-10-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: pg
@@ -86,9 +86,13 @@ files:
86
86
  - lib/ruby-pg-extras/queries/all_locks.sql
87
87
  - lib/ruby-pg-extras/queries/bloat.sql
88
88
  - lib/ruby-pg-extras/queries/blocking.sql
89
+ - lib/ruby-pg-extras/queries/buffercache_stats.sql
90
+ - lib/ruby-pg-extras/queries/buffercache_usage.sql
89
91
  - lib/ruby-pg-extras/queries/cache_hit.sql
90
92
  - lib/ruby-pg-extras/queries/calls.sql
93
+ - lib/ruby-pg-extras/queries/calls_legacy.sql
91
94
  - lib/ruby-pg-extras/queries/db_settings.sql
95
+ - lib/ruby-pg-extras/queries/duplicate_indexes.sql
92
96
  - lib/ruby-pg-extras/queries/extensions.sql
93
97
  - lib/ruby-pg-extras/queries/index_cache_hit.sql
94
98
  - lib/ruby-pg-extras/queries/index_size.sql
@@ -99,8 +103,10 @@ files:
99
103
  - lib/ruby-pg-extras/queries/mandelbrot.sql
100
104
  - lib/ruby-pg-extras/queries/null_indexes.sql
101
105
  - lib/ruby-pg-extras/queries/outliers.sql
106
+ - lib/ruby-pg-extras/queries/outliers_legacy.sql
102
107
  - lib/ruby-pg-extras/queries/records_rank.sql
103
108
  - lib/ruby-pg-extras/queries/seq_scans.sql
109
+ - lib/ruby-pg-extras/queries/ssl_used.sql
104
110
  - lib/ruby-pg-extras/queries/table_cache_hit.sql
105
111
  - lib/ruby-pg-extras/queries/table_indexes_size.sql
106
112
  - lib/ruby-pg-extras/queries/table_size.sql
@@ -131,7 +137,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
131
137
  - !ruby/object:Gem::Version
132
138
  version: '0'
133
139
  requirements: []
134
- rubygems_version: 3.1.4
140
+ rubygems_version: 3.1.6
135
141
  signing_key:
136
142
  specification_version: 4
137
143
  summary: Ruby PostgreSQL performance database insights