pgdexter 0.3.3 → 0.3.4
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 +5 -5
- data/.travis.yml +2 -1
- data/CHANGELOG.md +6 -0
- data/README.md +10 -4
- data/guides/Linux.md +2 -0
- data/lib/dexter.rb +1 -0
- data/lib/dexter/client.rb +3 -0
- data/lib/dexter/indexer.rb +23 -3
- data/lib/dexter/pg_stat_activity_parser.rb +25 -0
- data/lib/dexter/processor.rb +6 -4
- data/lib/dexter/version.rb +1 -1
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: e51c5c9ce5c31760d773177a3b224674752b19de8cc9fa0978e1da2adff9a490
|
4
|
+
data.tar.gz: ce9b2e89d2524aafda39bd5a91bb53abde5d0819e0c8751281a5ff7ad10766c2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 63cdf589a11ab232f45e26ff8b6a713ef757ce3bc3baf3fecbc51217fb4c3a4bdda0ded52bc631bbb8aaa5fe7ca28d356ab99e200c5af22a3bd1bac9e5efc17b
|
7
|
+
data.tar.gz: fcbb680a98f5907a62216ee142fa91b8538965c1fd59c1de48645ee1b5e3525950245d010a128f52b28fda665d0f6fcbf95fa343184b559150646819049f173a
|
data/.travis.yml
CHANGED
@@ -6,8 +6,9 @@ addons:
|
|
6
6
|
postgresql: "9.6"
|
7
7
|
before_script:
|
8
8
|
- sudo apt-get install postgresql-server-dev-9.6
|
9
|
-
- git clone https://github.com/
|
9
|
+
- git clone https://github.com/HypoPG/hypopg.git
|
10
10
|
- cd hypopg
|
11
|
+
- git checkout 1.1.1
|
11
12
|
- make
|
12
13
|
- sudo make install
|
13
14
|
- psql -c 'create database dexter_test;' -U postgres
|
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
@@ -8,12 +8,12 @@ The automatic indexer for Postgres
|
|
8
8
|
|
9
9
|
## Installation
|
10
10
|
|
11
|
-
First, install [HypoPG](https://github.com/
|
11
|
+
First, install [HypoPG](https://github.com/HypoPG/hypopg) on your database server. This doesn’t require a restart.
|
12
12
|
|
13
13
|
```sh
|
14
14
|
cd /tmp
|
15
|
-
curl -L https://github.com/
|
16
|
-
cd hypopg-1.1.
|
15
|
+
curl -L https://github.com/HypoPG/hypopg/archive/1.1.1.tar.gz | tar xz
|
16
|
+
cd hypopg-1.1.1
|
17
17
|
make
|
18
18
|
make install # may need sudo
|
19
19
|
```
|
@@ -104,13 +104,19 @@ or pass files:
|
|
104
104
|
dexter <connection-options> <file1> <file2>
|
105
105
|
```
|
106
106
|
|
107
|
+
or collect running queries with: [master]
|
108
|
+
|
109
|
+
```sh
|
110
|
+
dexter <connection-options> --pg-stat-activity
|
111
|
+
```
|
112
|
+
|
107
113
|
or use the [pg_stat_statements](https://www.postgresql.org/docs/current/static/pgstatstatements.html) extension:
|
108
114
|
|
109
115
|
```sh
|
110
116
|
dexter <connection-options> --pg-stat-statements
|
111
117
|
```
|
112
118
|
|
113
|
-
> Note: Logs are highly preferred over pg_stat_statements, as pg_stat_statements often doesn’t store enough information to optimize queries.
|
119
|
+
> Note: Logs or running queries are highly preferred over pg_stat_statements, as pg_stat_statements often doesn’t store enough information to optimize queries.
|
114
120
|
|
115
121
|
### Collection Options
|
116
122
|
|
data/guides/Linux.md
CHANGED
@@ -32,6 +32,7 @@ sudo apt-get install dexter
|
|
32
32
|
### Debian 9 (Stretch)
|
33
33
|
|
34
34
|
```sh
|
35
|
+
sudo apt-get -y install apt-transport-https
|
35
36
|
wget -qO- https://dl.packager.io/srv/pghero/dexter/key | sudo apt-key add -
|
36
37
|
sudo wget -O /etc/apt/sources.list.d/dexter.list \
|
37
38
|
https://dl.packager.io/srv/pghero/dexter/master/installer/debian/9.repo
|
@@ -42,6 +43,7 @@ sudo apt-get install dexter
|
|
42
43
|
### Debian 8 (Jesse)
|
43
44
|
|
44
45
|
```sh
|
46
|
+
sudo apt-get -y install apt-transport-https
|
45
47
|
wget -qO- https://dl.packager.io/srv/pghero/dexter/key | sudo apt-key add -
|
46
48
|
sudo wget -O /etc/apt/sources.list.d/dexter.list \
|
47
49
|
https://dl.packager.io/srv/pghero/dexter/master/installer/debian/8.repo
|
data/lib/dexter.rb
CHANGED
data/lib/dexter/client.rb
CHANGED
@@ -18,6 +18,8 @@ module Dexter
|
|
18
18
|
elsif options[:pg_stat_statements]
|
19
19
|
# TODO support streaming option
|
20
20
|
Indexer.new(options).process_stat_statements
|
21
|
+
elsif options[:pg_stat_activity]
|
22
|
+
Processor.new(:pg_stat_activity, options).perform
|
21
23
|
elsif arguments.any?
|
22
24
|
ARGV.replace(arguments)
|
23
25
|
Processor.new(ARGF, options).perform
|
@@ -44,6 +46,7 @@ Options:)
|
|
44
46
|
o.float "--min-calls", "only process queries that have been called a certain number of times", default: 0
|
45
47
|
o.float "--min-time", "only process queries that have consumed a certain amount of DB time, in minutes", default: 0
|
46
48
|
o.integer "--min-cost-savings-pct", default: 50, help: false
|
49
|
+
o.boolean "--pg-stat-activity", "use pg_stat_activity", default: false, help: false
|
47
50
|
o.boolean "--pg-stat-statements", "use pg_stat_statements", default: false, help: false
|
48
51
|
o.string "-s", "--statement", "process a single statement"
|
49
52
|
# separator must go here to show up correctly - slop bug?
|
data/lib/dexter/indexer.rb
CHANGED
@@ -25,6 +25,23 @@ module Dexter
|
|
25
25
|
process_queries(queries)
|
26
26
|
end
|
27
27
|
|
28
|
+
def stat_activity
|
29
|
+
execute <<-SQL
|
30
|
+
SELECT
|
31
|
+
pid || ':' || COALESCE(query_start, xact_start) AS id,
|
32
|
+
query,
|
33
|
+
EXTRACT(EPOCH FROM NOW() - COALESCE(query_start, xact_start)) * 1000.0 AS duration_ms
|
34
|
+
FROM
|
35
|
+
pg_stat_activity
|
36
|
+
WHERE
|
37
|
+
datname = current_database()
|
38
|
+
AND state = 'active'
|
39
|
+
AND pid != pg_backend_pid()
|
40
|
+
ORDER BY
|
41
|
+
1
|
42
|
+
SQL
|
43
|
+
end
|
44
|
+
|
28
45
|
def process_queries(queries)
|
29
46
|
# reset hypothetical indexes
|
30
47
|
reset_hypothetical_indexes
|
@@ -166,7 +183,7 @@ module Dexter
|
|
166
183
|
# Pass format to prevent ANALYZE
|
167
184
|
puts execute("EXPLAIN (FORMAT TEXT) #{safe_statement(query.statement)}").map { |r| r["QUERY PLAN"] }.join("\n")
|
168
185
|
end
|
169
|
-
rescue PG::Error => e
|
186
|
+
rescue PG::Error, JSON::NestingError => e
|
170
187
|
if @log_explain
|
171
188
|
log e.message
|
172
189
|
end
|
@@ -479,6 +496,9 @@ module Dexter
|
|
479
496
|
|
480
497
|
def conn
|
481
498
|
@conn ||= begin
|
499
|
+
# set connect timeout if none set
|
500
|
+
ENV["PGCONNECT_TIMEOUT"] ||= "2"
|
501
|
+
|
482
502
|
if @options[:dbname] =~ /\Apostgres(ql)?:\/\//
|
483
503
|
config = @options[:dbname]
|
484
504
|
else
|
@@ -486,7 +506,7 @@ module Dexter
|
|
486
506
|
host: @options[:host],
|
487
507
|
port: @options[:port],
|
488
508
|
dbname: @options[:dbname],
|
489
|
-
user: @options[:
|
509
|
+
user: @options[:username]
|
490
510
|
}.reject { |_, value| value.to_s.empty? }
|
491
511
|
config = config[:dbname] if config.keys == [:dbname] && config[:dbname].include?("=")
|
492
512
|
end
|
@@ -511,7 +531,7 @@ module Dexter
|
|
511
531
|
|
512
532
|
def plan(query)
|
513
533
|
# strip semi-colons as another measure of defense
|
514
|
-
JSON.parse(execute("EXPLAIN (FORMAT JSON) #{safe_statement(query)}").first["QUERY PLAN"]).first["Plan"]
|
534
|
+
JSON.parse(execute("EXPLAIN (FORMAT JSON) #{safe_statement(query)}").first["QUERY PLAN"], max_nesting: 1000).first["Plan"]
|
515
535
|
end
|
516
536
|
|
517
537
|
# TODO for multicolumn indexes, use ordering
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Dexter
|
2
|
+
class PgStatActivityParser < LogParser
|
3
|
+
def perform
|
4
|
+
queries = {}
|
5
|
+
|
6
|
+
loop do
|
7
|
+
new_queries = {}
|
8
|
+
@logfile.stat_activity.each do |row|
|
9
|
+
new_queries[row["id"]] = row
|
10
|
+
end
|
11
|
+
|
12
|
+
# store queries after they complete
|
13
|
+
queries.each do |id, row|
|
14
|
+
unless new_queries[id]
|
15
|
+
process_entry(row["query"], row["duration_ms"].to_f)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
queries = new_queries
|
20
|
+
|
21
|
+
sleep(5)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
data/lib/dexter/processor.rb
CHANGED
@@ -6,15 +6,17 @@ module Dexter
|
|
6
6
|
@logfile = logfile
|
7
7
|
|
8
8
|
@collector = Collector.new(min_time: options[:min_time], min_calls: options[:min_calls])
|
9
|
+
@indexer = Indexer.new(options)
|
10
|
+
|
9
11
|
@log_parser =
|
10
|
-
if
|
12
|
+
if @logfile == :pg_stat_activity
|
13
|
+
PgStatActivityParser.new(@indexer, @collector)
|
14
|
+
elsif options[:input_format] == "csv"
|
11
15
|
CsvLogParser.new(logfile, @collector)
|
12
16
|
else
|
13
17
|
LogParser.new(logfile, @collector)
|
14
18
|
end
|
15
19
|
|
16
|
-
@indexer = Indexer.new(options)
|
17
|
-
|
18
20
|
@starting_interval = 3
|
19
21
|
@interval = options[:interval]
|
20
22
|
|
@@ -25,7 +27,7 @@ module Dexter
|
|
25
27
|
end
|
26
28
|
|
27
29
|
def perform
|
28
|
-
if @logfile
|
30
|
+
if [STDIN, :pg_stat_activity].include?(@logfile)
|
29
31
|
Thread.abort_on_exception = true
|
30
32
|
Thread.new do
|
31
33
|
sleep(@starting_interval)
|
data/lib/dexter/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: pgdexter
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.3.
|
4
|
+
version: 0.3.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andrew Kane
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-
|
11
|
+
date: 2018-04-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: slop
|
@@ -119,6 +119,7 @@ files:
|
|
119
119
|
- lib/dexter/indexer.rb
|
120
120
|
- lib/dexter/log_parser.rb
|
121
121
|
- lib/dexter/logging.rb
|
122
|
+
- lib/dexter/pg_stat_activity_parser.rb
|
122
123
|
- lib/dexter/processor.rb
|
123
124
|
- lib/dexter/query.rb
|
124
125
|
- lib/dexter/version.rb
|
@@ -142,7 +143,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
142
143
|
version: '0'
|
143
144
|
requirements: []
|
144
145
|
rubyforge_project:
|
145
|
-
rubygems_version: 2.6
|
146
|
+
rubygems_version: 2.7.6
|
146
147
|
signing_key:
|
147
148
|
specification_version: 4
|
148
149
|
summary: The automatic indexer for Postgres
|