ruby-pg-extras 1.5.3 → 2.2.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: 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