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