pgdexter 0.6.0 → 0.6.1
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 +4 -4
- data/CHANGELOG.md +4 -0
- data/README.md +2 -2
- data/lib/dexter/client.rb +1 -1
- data/lib/dexter/connection.rb +4 -0
- data/lib/dexter/index_creator.rb +72 -0
- data/lib/dexter/indexer.rb +12 -72
- data/lib/dexter/logging.rb +1 -1
- data/lib/dexter/version.rb +1 -1
- data/lib/dexter.rb +1 -0
- metadata +2 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5d6f485cb3dbfbc0e20b1b46b2ac799c27664b2130dbff0c57a4451d9df4fd3f
|
4
|
+
data.tar.gz: 76d48363c209d2b17a0dd29dd8e8be9e691b3b161f004e016d533871a21da6ce
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ac7db9aa636b187745bdaba28c84581d2610397368eceb0c86173c55ea88733abdc9876280bac5cf4f940141510d1e37c75b5cb967de00c0b2cb434eaccee121
|
7
|
+
data.tar.gz: bebeb10a06631cd3ba5a69d40a67b3275d7d2dd8329ab617f3251327ab2f2980d0bce273d0c44ff1e31fb6992e04220524c22e3eaa15eaca674987cb2074024a
|
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
@@ -232,10 +232,10 @@ If compilation fails with `fatal error: postgres.h: No such file or directory`,
|
|
232
232
|
For Ubuntu and Debian, use:
|
233
233
|
|
234
234
|
```sh
|
235
|
-
sudo apt-get install postgresql-server-dev-
|
235
|
+
sudo apt-get install postgresql-server-dev-17
|
236
236
|
```
|
237
237
|
|
238
|
-
Note: Replace `
|
238
|
+
Note: Replace `17` with your Postgres server version
|
239
239
|
|
240
240
|
## Additional Installation Methods
|
241
241
|
|
data/lib/dexter/client.rb
CHANGED
@@ -112,7 +112,7 @@ module Dexter
|
|
112
112
|
|
113
113
|
options[:dbname] = arguments.shift unless options[:dbname]
|
114
114
|
|
115
|
-
# TODO
|
115
|
+
# TODO remove global variable
|
116
116
|
$log_level = options[:log_level].to_s.downcase
|
117
117
|
unless ["error", "info", "debug", "debug2", "debug3"].include?($log_level)
|
118
118
|
raise Error, "Unknown log level"
|
data/lib/dexter/connection.rb
CHANGED
@@ -41,6 +41,10 @@ module Dexter
|
|
41
41
|
end
|
42
42
|
end
|
43
43
|
|
44
|
+
def quote_ident(value)
|
45
|
+
value.split(".").map { |v| conn.quote_ident(v) }.join(".")
|
46
|
+
end
|
47
|
+
|
44
48
|
def server_version_num
|
45
49
|
@server_version_num ||= execute("SHOW server_version_num").first["server_version_num"].to_i
|
46
50
|
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
module Dexter
|
2
|
+
class IndexCreator
|
3
|
+
include Logging
|
4
|
+
|
5
|
+
def initialize(connection, indexer, new_indexes, tablespace)
|
6
|
+
@connection = connection
|
7
|
+
@indexer = indexer
|
8
|
+
@new_indexes = new_indexes
|
9
|
+
@tablespace = tablespace
|
10
|
+
end
|
11
|
+
|
12
|
+
# 1. create lock
|
13
|
+
# 2. refresh existing index list
|
14
|
+
# 3. create indexes that still don't exist
|
15
|
+
# 4. release lock
|
16
|
+
def perform
|
17
|
+
with_advisory_lock do
|
18
|
+
@new_indexes.each do |index|
|
19
|
+
unless index_exists?(index)
|
20
|
+
statement = String.new("CREATE INDEX CONCURRENTLY ON #{@connection.quote_ident(index[:table])} (#{index[:columns].map { |c| @connection.quote_ident(c) }.join(", ")})")
|
21
|
+
statement << " TABLESPACE #{@connection.quote_ident(@tablespace)}" if @tablespace
|
22
|
+
log "Creating index: #{statement}"
|
23
|
+
started_at = monotonic_time
|
24
|
+
begin
|
25
|
+
@connection.execute(statement)
|
26
|
+
log "Index created: #{((monotonic_time - started_at) * 1000).to_i} ms"
|
27
|
+
rescue PG::LockNotAvailable
|
28
|
+
log "Could not acquire lock: #{index[:table]}"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def monotonic_time
|
38
|
+
Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
39
|
+
end
|
40
|
+
|
41
|
+
def with_advisory_lock
|
42
|
+
lock_id = 123456
|
43
|
+
first_time = true
|
44
|
+
while @connection.execute("SELECT pg_try_advisory_lock($1)", params: [lock_id]).first["pg_try_advisory_lock"] != "t"
|
45
|
+
if first_time
|
46
|
+
log "Waiting for lock..."
|
47
|
+
first_time = false
|
48
|
+
end
|
49
|
+
sleep(1)
|
50
|
+
end
|
51
|
+
yield
|
52
|
+
ensure
|
53
|
+
suppress_messages do
|
54
|
+
@connection.execute("SELECT pg_advisory_unlock($1)", params: [lock_id])
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def suppress_messages
|
59
|
+
@connection.send(:conn).set_notice_processor do |message|
|
60
|
+
# do nothing
|
61
|
+
end
|
62
|
+
yield
|
63
|
+
ensure
|
64
|
+
# clear notice processor
|
65
|
+
@connection.send(:conn).set_notice_processor
|
66
|
+
end
|
67
|
+
|
68
|
+
def index_exists?(index)
|
69
|
+
@indexer.send(:indexes, [index[:table]]).find { |i| i["columns"] == index[:columns] }
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
data/lib/dexter/indexer.rb
CHANGED
@@ -30,6 +30,7 @@ module Dexter
|
|
30
30
|
|
31
31
|
if tables.any?
|
32
32
|
# analyze tables if needed
|
33
|
+
# TODO remove @log_level in 0.7.0
|
33
34
|
analyze_tables(tables) if @analyze || @log_level == "debug2"
|
34
35
|
|
35
36
|
# get initial costs for queries
|
@@ -41,7 +42,8 @@ module Dexter
|
|
41
42
|
ColumnResolver.new(@connection, candidate_queries, log_level: @log_level).perform
|
42
43
|
candidate_queries.each do |query|
|
43
44
|
# no reason to use btree index for json columns
|
44
|
-
|
45
|
+
# TODO check type supports btree
|
46
|
+
query.candidate_columns = query.columns.reject { |c| ["json", "jsonb", "point"].include?(c[:type]) }.sort_by { |c| [c[:table], c[:column]] }
|
45
47
|
end
|
46
48
|
candidate_queries.select! { |q| q.candidate_columns.any? }
|
47
49
|
|
@@ -59,7 +61,7 @@ module Dexter
|
|
59
61
|
show_debug_info(new_indexes, queries) if @log_level.start_with?("debug")
|
60
62
|
|
61
63
|
# create new indexes
|
62
|
-
|
64
|
+
IndexCreator.new(@connection, self, new_indexes, @tablespace).perform if @create && new_indexes.any?
|
63
65
|
end
|
64
66
|
|
65
67
|
private
|
@@ -120,7 +122,7 @@ module Dexter
|
|
120
122
|
end
|
121
123
|
|
122
124
|
if @analyze && (!la || la < Time.now - 3600)
|
123
|
-
statement = "ANALYZE #{quote_ident(table)}"
|
125
|
+
statement = "ANALYZE #{@connection.quote_ident(table)}"
|
124
126
|
log "Running analyze: #{statement}"
|
125
127
|
execute(statement)
|
126
128
|
end
|
@@ -183,8 +185,12 @@ module Dexter
|
|
183
185
|
|
184
186
|
def create_candidate_indexes(candidate_indexes, index_mapping)
|
185
187
|
candidate_indexes.each do |columns|
|
186
|
-
|
187
|
-
|
188
|
+
begin
|
189
|
+
index_name = create_hypothetical_index(columns[0][:table], columns.map { |c| c[:column] })
|
190
|
+
index_mapping[index_name] = columns
|
191
|
+
rescue PG::UndefinedObject
|
192
|
+
# data type x has no default operator class for access method "btree"
|
193
|
+
end
|
188
194
|
end
|
189
195
|
rescue PG::InternalError
|
190
196
|
# hypopg: not more oid available
|
@@ -446,37 +452,6 @@ module Dexter
|
|
446
452
|
end
|
447
453
|
end
|
448
454
|
|
449
|
-
# 1. create lock
|
450
|
-
# 2. refresh existing index list
|
451
|
-
# 3. create indexes that still don't exist
|
452
|
-
# 4. release lock
|
453
|
-
def create_indexes(new_indexes)
|
454
|
-
with_advisory_lock do
|
455
|
-
new_indexes.each do |index|
|
456
|
-
unless index_exists?(index)
|
457
|
-
statement = String.new("CREATE INDEX CONCURRENTLY ON #{quote_ident(index[:table])} (#{index[:columns].map { |c| quote_ident(c) }.join(", ")})")
|
458
|
-
statement << " TABLESPACE #{quote_ident(@tablespace)}" if @tablespace
|
459
|
-
log "Creating index: #{statement}"
|
460
|
-
started_at = monotonic_time
|
461
|
-
begin
|
462
|
-
execute(statement)
|
463
|
-
log "Index created: #{((monotonic_time - started_at) * 1000).to_i} ms"
|
464
|
-
rescue PG::LockNotAvailable
|
465
|
-
log "Could not acquire lock: #{index[:table]}"
|
466
|
-
end
|
467
|
-
end
|
468
|
-
end
|
469
|
-
end
|
470
|
-
end
|
471
|
-
|
472
|
-
def monotonic_time
|
473
|
-
Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
474
|
-
end
|
475
|
-
|
476
|
-
def conn
|
477
|
-
@connection.send(:conn)
|
478
|
-
end
|
479
|
-
|
480
455
|
def execute(...)
|
481
456
|
@connection.execute(...)
|
482
457
|
end
|
@@ -522,38 +497,7 @@ module Dexter
|
|
522
497
|
end
|
523
498
|
|
524
499
|
def create_hypothetical_index(table, columns)
|
525
|
-
execute("SELECT * FROM hypopg_create_index('CREATE INDEX ON #{quote_ident(table)} (#{columns.map { |c| quote_ident(c) }.join(", ")})')").first["indexname"]
|
526
|
-
end
|
527
|
-
|
528
|
-
def with_advisory_lock
|
529
|
-
lock_id = 123456
|
530
|
-
first_time = true
|
531
|
-
while execute("SELECT pg_try_advisory_lock($1)", params: [lock_id]).first["pg_try_advisory_lock"] != "t"
|
532
|
-
if first_time
|
533
|
-
log "Waiting for lock..."
|
534
|
-
first_time = false
|
535
|
-
end
|
536
|
-
sleep(1)
|
537
|
-
end
|
538
|
-
yield
|
539
|
-
ensure
|
540
|
-
suppress_messages do
|
541
|
-
execute("SELECT pg_advisory_unlock($1)", params: [lock_id])
|
542
|
-
end
|
543
|
-
end
|
544
|
-
|
545
|
-
def suppress_messages
|
546
|
-
conn.set_notice_processor do |message|
|
547
|
-
# do nothing
|
548
|
-
end
|
549
|
-
yield
|
550
|
-
ensure
|
551
|
-
# clear notice processor
|
552
|
-
conn.set_notice_processor
|
553
|
-
end
|
554
|
-
|
555
|
-
def index_exists?(index)
|
556
|
-
indexes([index[:table]]).find { |i| i["columns"] == index[:columns] }
|
500
|
+
execute("SELECT * FROM hypopg_create_index('CREATE INDEX ON #{@connection.quote_ident(table)} (#{columns.map { |c| @connection.quote_ident(c) }.join(", ")})')").first["indexname"]
|
557
501
|
end
|
558
502
|
|
559
503
|
def indexes(tables)
|
@@ -590,10 +534,6 @@ module Dexter
|
|
590
534
|
end
|
591
535
|
end
|
592
536
|
|
593
|
-
def quote_ident(value)
|
594
|
-
value.split(".").map { |v| conn.quote_ident(v) }.join(".")
|
595
|
-
end
|
596
|
-
|
597
537
|
def safe_statement(statement)
|
598
538
|
statement.gsub(";", "")
|
599
539
|
end
|
data/lib/dexter/logging.rb
CHANGED
data/lib/dexter/version.rb
CHANGED
data/lib/dexter.rb
CHANGED
@@ -15,6 +15,7 @@ require_relative "dexter/client"
|
|
15
15
|
require_relative "dexter/collector"
|
16
16
|
require_relative "dexter/column_resolver"
|
17
17
|
require_relative "dexter/connection"
|
18
|
+
require_relative "dexter/index_creator"
|
18
19
|
require_relative "dexter/indexer"
|
19
20
|
require_relative "dexter/processor"
|
20
21
|
require_relative "dexter/query"
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: pgdexter
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.6.
|
4
|
+
version: 0.6.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andrew Kane
|
@@ -80,6 +80,7 @@ files:
|
|
80
80
|
- lib/dexter/collector.rb
|
81
81
|
- lib/dexter/column_resolver.rb
|
82
82
|
- lib/dexter/connection.rb
|
83
|
+
- lib/dexter/index_creator.rb
|
83
84
|
- lib/dexter/indexer.rb
|
84
85
|
- lib/dexter/logging.rb
|
85
86
|
- lib/dexter/parsers/csv_log_parser.rb
|