tobias 0.2.0 → 0.4.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 +4 -4
- data/bin/tobias +4 -1
- data/lib/tobias/cli.rb +37 -21
- data/lib/tobias/container.rb +80 -5
- data/lib/tobias/evaluations/base.rb +64 -0
- data/lib/tobias/evaluations/work_mem.rb +61 -0
- data/lib/tobias/evaluations.rb +11 -0
- data/lib/tobias/version.rb +1 -2
- data/lib/tobias/work_mem.rb +17 -1
- data/lib/tobias.rb +12 -2
- metadata +89 -4
- data/lib/tobias/evaluation.rb +0 -36
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6e8024f2a5efa0e4ffe41d2973817f1906fccfb32193782b86e55f822a82f5e7
|
4
|
+
data.tar.gz: 5b6b27888ed7341c76fea8503bc8e577662949903089970930fced41367583e0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2e289f868dad239629020799f64c05be1f5658f2facd905d917ce162bcccbefc957e979c19bdb36ec37e1c21eb89ae26d7b2f2a458f11b8d1d335eb5b9a9a37c
|
7
|
+
data.tar.gz: fece84c5f4c2d65961c091ee4bb7e2879b43646e60a8c470079d20529541757e0e61d9f4a2163e0615660c38b62c758504bdde9009a35e426f0578d598089468
|
data/bin/tobias
CHANGED
data/lib/tobias/cli.rb
CHANGED
@@ -8,14 +8,46 @@ module Tobias
|
|
8
8
|
true
|
9
9
|
end
|
10
10
|
|
11
|
+
desc "recommend", "recommend a work_mem setting for a database"
|
12
|
+
option :database_url, type: :string, required: true
|
13
|
+
option :debug, type: :boolean, default: false
|
14
|
+
def recommend
|
15
|
+
database = Sequel.connect(options[:database_url])
|
16
|
+
database.extension :pgvector
|
17
|
+
database.loggers << Logger.new(STDERR) if options[:debug]
|
18
|
+
|
19
|
+
parsed = TTY::Markdown.parse(<<~MARKDOWN)
|
20
|
+
# @tobias is thinking...
|
21
|
+
MARKDOWN
|
22
|
+
puts parsed
|
23
|
+
|
24
|
+
work_mem = Tobias::WorkMem.valid_for(database).sort_by(&:amount).reverse.first
|
25
|
+
|
26
|
+
parsed = TTY::Markdown.parse(<<~MARKDOWN)
|
27
|
+
# @tobias has sent you a new message
|
28
|
+
|
29
|
+
I've reviewed your database by analyzing your shared buffers and connection limits and
|
30
|
+
recommend setting `work_mem` to `#{work_mem.to_sql}`. To apply my recommendation, run the following SQL:
|
31
|
+
|
32
|
+
```sql
|
33
|
+
ALTER SYSTEM SET work_mem = '#{work_mem.to_sql}';
|
34
|
+
SELECT pg_reload_conf();
|
35
|
+
```
|
36
|
+
|
37
|
+
Regards,
|
38
|
+
~ Tobias
|
39
|
+
MARKDOWN
|
40
|
+
puts parsed
|
41
|
+
end
|
42
|
+
|
11
43
|
desc "profile SCRIPT", "profile"
|
12
44
|
option :database_url, type: :string, required: true
|
13
|
-
option :iterations, type: :numeric, default: 100
|
14
45
|
option :debug, type: :boolean, default: false
|
15
46
|
def profile(script)
|
16
|
-
database = Sequel.connect(options[:database_url])
|
47
|
+
database = Sequel.connect(options[:database_url], max_connections: Etc.nprocessors + 2)
|
17
48
|
database.loggers << Logger.new(STDERR) if options[:debug]
|
18
49
|
database.extension :pg_json
|
50
|
+
database.extension :pgvector
|
19
51
|
|
20
52
|
if File.exist?(script)
|
21
53
|
code = File.read(script)
|
@@ -23,8 +55,7 @@ module Tobias
|
|
23
55
|
raise "Script not found at: #{script}"
|
24
56
|
end
|
25
57
|
|
26
|
-
container = Container.new(code)
|
27
|
-
work_mems = WorkMem.valid_for(database)
|
58
|
+
container = Container.new(code, database)
|
28
59
|
results = {}
|
29
60
|
|
30
61
|
parsed = TTY::Markdown.parse(<<~MARKDOWN)
|
@@ -33,11 +64,7 @@ module Tobias
|
|
33
64
|
puts parsed
|
34
65
|
|
35
66
|
thinking_time = Benchmark.realtime do
|
36
|
-
|
37
|
-
work_mem = Evaluation.new(database, work_mems).run(options, &block)
|
38
|
-
|
39
|
-
results[name] = work_mem
|
40
|
-
end
|
67
|
+
results = Evaluations.run(database, container, options)
|
41
68
|
end
|
42
69
|
|
43
70
|
parsed = TTY::Markdown.parse(<<~MARKDOWN)
|
@@ -45,18 +72,7 @@ module Tobias
|
|
45
72
|
|
46
73
|
I thought about your queries for precisely #{thinking_time.round(2)} seconds and here is what I recommend:
|
47
74
|
|
48
|
-
|
49
|
-
|-------|-------------------|
|
50
|
-
#{results.map { |name, work_mem| "| #{name} | #{work_mem.to_sql} |" }.join("\n")}
|
51
|
-
|
52
|
-
Your application will need to run with at least #{results.values.max.to_sql} of work_mem.
|
53
|
-
|
54
|
-
To apply my recommendations, run the following SQL:
|
55
|
-
|
56
|
-
```sql
|
57
|
-
ALTER SYSTEM SET work_mem = '#{results.values.max.to_sql}';
|
58
|
-
SELECT pg_reload_conf();
|
59
|
-
```
|
75
|
+
#{results.join("\n")}
|
60
76
|
|
61
77
|
Regards,
|
62
78
|
~ Tobias
|
data/lib/tobias/container.rb
CHANGED
@@ -1,20 +1,95 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "open3"
|
4
|
+
|
3
5
|
module Tobias
|
4
6
|
class Container
|
5
|
-
def initialize(code)
|
7
|
+
def initialize(code, database)
|
6
8
|
@code = code
|
7
|
-
@
|
9
|
+
@database = database
|
10
|
+
@queries = Concurrent::Hash.new
|
11
|
+
@sql = Concurrent::Hash.new
|
12
|
+
@options = Concurrent::Hash.new
|
13
|
+
@setup = Proc.new { }
|
14
|
+
@teardown = Proc.new { }
|
15
|
+
@load_data = Proc.new { }
|
16
|
+
@helpers = Module.new
|
8
17
|
|
9
18
|
eval(code, binding, __FILE__, __LINE__)
|
10
19
|
end
|
11
20
|
|
21
|
+
module DefaultHelpers
|
22
|
+
def db
|
23
|
+
@database
|
24
|
+
end
|
25
|
+
|
26
|
+
def run_parallel(list = Etc.nprocessors.times, &block)
|
27
|
+
db.disconnect
|
28
|
+
|
29
|
+
Parallel.each(list, in_processes: Etc.nprocessors) do |item|
|
30
|
+
instance_exec(item, &block)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def run_setup
|
36
|
+
@database.run("CREATE EXTENSION IF NOT EXISTS pg_stat_statements")
|
37
|
+
run_action(@setup)
|
38
|
+
end
|
39
|
+
|
40
|
+
def run_query(query)
|
41
|
+
@database.run(run_action(query).sql)
|
42
|
+
end
|
43
|
+
|
44
|
+
def run_teardown
|
45
|
+
run_action(@teardown)
|
46
|
+
end
|
47
|
+
|
48
|
+
def options
|
49
|
+
Struct.new(*@options.keys).new(*@options.values)
|
50
|
+
end
|
51
|
+
|
52
|
+
def run_action(action)
|
53
|
+
helpers = @helpers
|
54
|
+
|
55
|
+
class_eval do
|
56
|
+
include DefaultHelpers
|
57
|
+
include helpers
|
58
|
+
end
|
59
|
+
|
60
|
+
instance_eval(&action)
|
61
|
+
end
|
62
|
+
|
12
63
|
def queries
|
13
64
|
@queries
|
14
65
|
end
|
15
66
|
|
16
|
-
def
|
17
|
-
@
|
67
|
+
def option(name, default = nil, &block)
|
68
|
+
@options[name] = block || default
|
69
|
+
end
|
70
|
+
|
71
|
+
def helpers(&block)
|
72
|
+
@helpers.class_eval(&block) if block_given?
|
73
|
+
end
|
74
|
+
|
75
|
+
def setup(&block)
|
76
|
+
@setup = block
|
77
|
+
end
|
78
|
+
|
79
|
+
def teardown(&block)
|
80
|
+
@teardown = block
|
81
|
+
end
|
82
|
+
|
83
|
+
def load_data(&block)
|
84
|
+
@load_data = block
|
85
|
+
end
|
86
|
+
|
87
|
+
def query(name, sql = nil, &block)
|
88
|
+
if sql.is_a?(String)
|
89
|
+
@queries[name] = Proc.new { sql }
|
90
|
+
else
|
91
|
+
@queries[name] = block || Proc.new { raise "No SQL provided for query '#{name}'" }
|
92
|
+
end
|
18
93
|
end
|
19
94
|
end
|
20
|
-
end
|
95
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class MarkdownTableBorder < TTY::Table::Border
|
4
|
+
def_border do
|
5
|
+
left "|"
|
6
|
+
center "|"
|
7
|
+
right "|"
|
8
|
+
bottom " "
|
9
|
+
bottom_mid " "
|
10
|
+
bottom_left " "
|
11
|
+
bottom_right " "
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
module Tobias
|
16
|
+
module Evaluations
|
17
|
+
Result = Struct.new(:name, :value, keyword_init: true) do
|
18
|
+
def <=>(other)
|
19
|
+
value <=> other.value
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
class Base
|
24
|
+
attr_reader :database, :container, :options
|
25
|
+
|
26
|
+
def initialize(database, container, options)
|
27
|
+
@database = database
|
28
|
+
@container = container
|
29
|
+
@options = options
|
30
|
+
end
|
31
|
+
|
32
|
+
def run(&block)
|
33
|
+
results = Concurrent::Array.new
|
34
|
+
|
35
|
+
container.run_setup
|
36
|
+
container.queries.each do |name, query|
|
37
|
+
result = run_each(name, query)
|
38
|
+
results << result if result
|
39
|
+
end
|
40
|
+
|
41
|
+
to_markdown(results)
|
42
|
+
ensure
|
43
|
+
container.run_teardown
|
44
|
+
end
|
45
|
+
|
46
|
+
def run_each(query)
|
47
|
+
raise NotImplementedError
|
48
|
+
end
|
49
|
+
|
50
|
+
def to_markdown(results)
|
51
|
+
raise NotImplementedError
|
52
|
+
end
|
53
|
+
|
54
|
+
def render_table(headers:, body:)
|
55
|
+
table = TTY::Table.new(header: headers)
|
56
|
+
body.each do |row|
|
57
|
+
table << row
|
58
|
+
end
|
59
|
+
|
60
|
+
table.render_with(MarkdownTableBorder)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Tobias
|
4
|
+
module Evaluations
|
5
|
+
class WorkMem < Base
|
6
|
+
def work_mems
|
7
|
+
@work_mems ||= Tobias::WorkMem.valid_for(database)
|
8
|
+
end
|
9
|
+
|
10
|
+
def current_work_mem
|
11
|
+
@current_work_mem ||= Tobias::WorkMem.from_sql(database.fetch("SHOW work_mem").first[:work_mem])
|
12
|
+
end
|
13
|
+
|
14
|
+
def description
|
15
|
+
"Optimal work_mem settings"
|
16
|
+
end
|
17
|
+
|
18
|
+
def run_each(name, query)
|
19
|
+
database.run("CREATE EXTENSION IF NOT EXISTS pg_stat_statements")
|
20
|
+
|
21
|
+
work_mems.each do |value|
|
22
|
+
database.transaction do
|
23
|
+
database.run("SET LOCAL work_mem = '#{value.to_sql}'")
|
24
|
+
database.select(Sequel.function(:pg_stat_reset)).first
|
25
|
+
container.run_query(query)
|
26
|
+
|
27
|
+
stats = database[:pg_stat_database].
|
28
|
+
where(datname: Sequel.function(:current_database)).
|
29
|
+
first
|
30
|
+
|
31
|
+
if stats[:temp_files] == 0 && stats[:temp_bytes] == 0
|
32
|
+
return Result.new(name: name, value: value)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
# Fallback to the highest work_mem setting if no results are found.
|
38
|
+
Result.new(name: name, value: work_mems.last)
|
39
|
+
end
|
40
|
+
|
41
|
+
def to_markdown(results)
|
42
|
+
<<~MARKDOWN
|
43
|
+
## #{description}
|
44
|
+
|
45
|
+
#{render_table(headers: ["Query", "Required work_mem"], body: results.map { |r| [r.name, r.value.to_sql] })}
|
46
|
+
|
47
|
+
I see that your current `work_mem` setting is `#{current_work_mem.to_sql}`.
|
48
|
+
|
49
|
+
Your application will need to run with at least `#{results.max.value.to_sql}` of `work_mem`.
|
50
|
+
|
51
|
+
To apply my recommendations, run the following SQL:
|
52
|
+
|
53
|
+
```sql
|
54
|
+
ALTER SYSTEM SET work_mem = '#{results.max.value.to_sql}';
|
55
|
+
SELECT pg_reload_conf();
|
56
|
+
```
|
57
|
+
MARKDOWN
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
data/lib/tobias/version.rb
CHANGED
@@ -1,8 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Tobias
|
4
|
-
VERSION = "0.
|
5
|
-
|
4
|
+
VERSION = "0.4.0"
|
6
5
|
SUMMARY = "Tobias is a tool to help you find the optimal work_mem for your queries."
|
7
6
|
DESCRIPTION = "Tobias is a tool to help you find the optimal work_mem for your queries."
|
8
7
|
end
|
data/lib/tobias/work_mem.rb
CHANGED
@@ -4,6 +4,21 @@ module Tobias
|
|
4
4
|
class WorkMem
|
5
5
|
attr_reader :amount
|
6
6
|
|
7
|
+
def self.from_sql(sql)
|
8
|
+
case sql
|
9
|
+
when /^\d+B$/
|
10
|
+
new(sql.to_i)
|
11
|
+
when /^\d+kB$/
|
12
|
+
new(sql.to_i * 1024)
|
13
|
+
when /^\d+MB$/
|
14
|
+
new(sql.to_i * 1024 * 1024)
|
15
|
+
when /^\d+GB$/
|
16
|
+
new(sql.to_i * 1024 * 1024 * 1024)
|
17
|
+
else
|
18
|
+
raise "Invalid work_mem setting: #{sql}"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
7
22
|
def initialize(amount)
|
8
23
|
@amount = amount
|
9
24
|
end
|
@@ -52,6 +67,7 @@ module Tobias
|
|
52
67
|
[
|
53
68
|
new(64.kilobytes),
|
54
69
|
new(128.kilobytes),
|
70
|
+
new(256.kilobytes),
|
55
71
|
new(512.kilobytes),
|
56
72
|
new(1.megabyte),
|
57
73
|
new(4.megabytes),
|
@@ -101,4 +117,4 @@ module Tobias
|
|
101
117
|
self.all.select { |work_mem| work_mem.amount < bytes_per_connection.to_i }
|
102
118
|
end
|
103
119
|
end
|
104
|
-
end
|
120
|
+
end
|
data/lib/tobias.rb
CHANGED
@@ -11,21 +11,31 @@ if RUBY_ENGINE == "ruby"
|
|
11
11
|
end
|
12
12
|
end
|
13
13
|
|
14
|
+
# See: https://github.com/ged/ruby-pg/issues/538#issuecomment-1591629049
|
15
|
+
ENV["PGGSSENCMODE"] = "disable"
|
16
|
+
|
14
17
|
require "bundler/setup"
|
15
18
|
Bundler.require(:default)
|
16
19
|
|
17
20
|
require "thor"
|
18
21
|
require "active_support/all"
|
19
22
|
require "sequel"
|
23
|
+
require "pgvector"
|
20
24
|
require "enumerable-stats"
|
21
25
|
require "benchmark"
|
26
|
+
require "parquet"
|
27
|
+
require "parallel"
|
22
28
|
require "tty-markdown"
|
29
|
+
require "tty-table"
|
23
30
|
|
24
31
|
$LOAD_PATH.unshift File.dirname(__FILE__)
|
25
32
|
|
33
|
+
require "tobias/evaluations"
|
34
|
+
require "tobias/evaluations/base"
|
35
|
+
require "tobias/evaluations/work_mem"
|
36
|
+
|
26
37
|
module Tobias
|
27
38
|
autoload :CLI, "tobias/cli"
|
28
39
|
autoload :Container, "tobias/container"
|
29
|
-
autoload :Evaluation, "tobias/evaluation"
|
30
40
|
autoload :WorkMem, "tobias/work_mem"
|
31
|
-
end
|
41
|
+
end
|
metadata
CHANGED
@@ -1,13 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: tobias
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jon Daniel
|
8
|
+
autorequire:
|
8
9
|
bindir: bin
|
9
10
|
cert_chain: []
|
10
|
-
date: 2025-08-
|
11
|
+
date: 2025-08-23 00:00:00.000000000 Z
|
11
12
|
dependencies:
|
12
13
|
- !ruby/object:Gem::Dependency
|
13
14
|
name: activesupport
|
@@ -189,6 +190,66 @@ dependencies:
|
|
189
190
|
- - ">="
|
190
191
|
- !ruby/object:Gem::Version
|
191
192
|
version: 5.76.0
|
193
|
+
- !ruby/object:Gem::Dependency
|
194
|
+
name: parquet
|
195
|
+
requirement: !ruby/object:Gem::Requirement
|
196
|
+
requirements:
|
197
|
+
- - "~>"
|
198
|
+
- !ruby/object:Gem::Version
|
199
|
+
version: '0.7'
|
200
|
+
- - ">="
|
201
|
+
- !ruby/object:Gem::Version
|
202
|
+
version: 0.7.3
|
203
|
+
type: :runtime
|
204
|
+
prerelease: false
|
205
|
+
version_requirements: !ruby/object:Gem::Requirement
|
206
|
+
requirements:
|
207
|
+
- - "~>"
|
208
|
+
- !ruby/object:Gem::Version
|
209
|
+
version: '0.7'
|
210
|
+
- - ">="
|
211
|
+
- !ruby/object:Gem::Version
|
212
|
+
version: 0.7.3
|
213
|
+
- !ruby/object:Gem::Dependency
|
214
|
+
name: pgvector
|
215
|
+
requirement: !ruby/object:Gem::Requirement
|
216
|
+
requirements:
|
217
|
+
- - "~>"
|
218
|
+
- !ruby/object:Gem::Version
|
219
|
+
version: '0.3'
|
220
|
+
- - ">="
|
221
|
+
- !ruby/object:Gem::Version
|
222
|
+
version: 0.3.0
|
223
|
+
type: :runtime
|
224
|
+
prerelease: false
|
225
|
+
version_requirements: !ruby/object:Gem::Requirement
|
226
|
+
requirements:
|
227
|
+
- - "~>"
|
228
|
+
- !ruby/object:Gem::Version
|
229
|
+
version: '0.3'
|
230
|
+
- - ">="
|
231
|
+
- !ruby/object:Gem::Version
|
232
|
+
version: 0.3.0
|
233
|
+
- !ruby/object:Gem::Dependency
|
234
|
+
name: parallel
|
235
|
+
requirement: !ruby/object:Gem::Requirement
|
236
|
+
requirements:
|
237
|
+
- - "~>"
|
238
|
+
- !ruby/object:Gem::Version
|
239
|
+
version: '1.20'
|
240
|
+
- - ">="
|
241
|
+
- !ruby/object:Gem::Version
|
242
|
+
version: 1.20.0
|
243
|
+
type: :runtime
|
244
|
+
prerelease: false
|
245
|
+
version_requirements: !ruby/object:Gem::Requirement
|
246
|
+
requirements:
|
247
|
+
- - "~>"
|
248
|
+
- !ruby/object:Gem::Version
|
249
|
+
version: '1.20'
|
250
|
+
- - ">="
|
251
|
+
- !ruby/object:Gem::Version
|
252
|
+
version: 1.20.0
|
192
253
|
- !ruby/object:Gem::Dependency
|
193
254
|
name: thor
|
194
255
|
requirement: !ruby/object:Gem::Requirement
|
@@ -229,6 +290,26 @@ dependencies:
|
|
229
290
|
- - ">="
|
230
291
|
- !ruby/object:Gem::Version
|
231
292
|
version: 0.7.0
|
293
|
+
- !ruby/object:Gem::Dependency
|
294
|
+
name: tty-table
|
295
|
+
requirement: !ruby/object:Gem::Requirement
|
296
|
+
requirements:
|
297
|
+
- - "~>"
|
298
|
+
- !ruby/object:Gem::Version
|
299
|
+
version: '0.12'
|
300
|
+
- - ">="
|
301
|
+
- !ruby/object:Gem::Version
|
302
|
+
version: 0.12.0
|
303
|
+
type: :runtime
|
304
|
+
prerelease: false
|
305
|
+
version_requirements: !ruby/object:Gem::Requirement
|
306
|
+
requirements:
|
307
|
+
- - "~>"
|
308
|
+
- !ruby/object:Gem::Version
|
309
|
+
version: '0.12'
|
310
|
+
- - ">="
|
311
|
+
- !ruby/object:Gem::Version
|
312
|
+
version: 0.12.0
|
232
313
|
description: Tobias is a tool to help you find the optimal work_mem for your queries.
|
233
314
|
email: binarycleric@gmail.com
|
234
315
|
executables:
|
@@ -240,7 +321,9 @@ files:
|
|
240
321
|
- lib/tobias.rb
|
241
322
|
- lib/tobias/cli.rb
|
242
323
|
- lib/tobias/container.rb
|
243
|
-
- lib/tobias/
|
324
|
+
- lib/tobias/evaluations.rb
|
325
|
+
- lib/tobias/evaluations/base.rb
|
326
|
+
- lib/tobias/evaluations/work_mem.rb
|
244
327
|
- lib/tobias/version.rb
|
245
328
|
- lib/tobias/work_mem.rb
|
246
329
|
homepage: https://github.com/binarycleric/tobias
|
@@ -249,6 +332,7 @@ licenses:
|
|
249
332
|
metadata:
|
250
333
|
source_code_uri: https://github.com/binarycleric/tobias
|
251
334
|
rubygems_mfa_required: 'true'
|
335
|
+
post_install_message:
|
252
336
|
rdoc_options: []
|
253
337
|
require_paths:
|
254
338
|
- lib
|
@@ -263,7 +347,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
263
347
|
- !ruby/object:Gem::Version
|
264
348
|
version: '0'
|
265
349
|
requirements: []
|
266
|
-
rubygems_version: 3.
|
350
|
+
rubygems_version: 3.5.22
|
351
|
+
signing_key:
|
267
352
|
specification_version: 4
|
268
353
|
summary: Tobias is a tool to help you find the optimal work_mem for your queries.
|
269
354
|
test_files: []
|
data/lib/tobias/evaluation.rb
DELETED
@@ -1,36 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Tobias
|
4
|
-
class Evaluation
|
5
|
-
attr_reader :database
|
6
|
-
|
7
|
-
def initialize(database, work_mems)
|
8
|
-
@database = database
|
9
|
-
@work_mems = work_mems
|
10
|
-
end
|
11
|
-
|
12
|
-
def run(options, &block)
|
13
|
-
@work_mems.each do |value|
|
14
|
-
database.transaction do
|
15
|
-
database.run("CREATE EXTENSION IF NOT EXISTS pg_stat_statements")
|
16
|
-
database.run("SET LOCAL work_mem = '#{value.to_sql}'")
|
17
|
-
database.select(Sequel.function(:pg_stat_reset)).first
|
18
|
-
database.instance_eval(&block)
|
19
|
-
|
20
|
-
query = database.instance_eval(&block)
|
21
|
-
options[:iterations].to_i.times do
|
22
|
-
database.run(query.sql)
|
23
|
-
end
|
24
|
-
|
25
|
-
stats = database[:pg_stat_database].where(datname: Sequel.function(:current_database)).first
|
26
|
-
|
27
|
-
if stats[:temp_files] == 0 && stats[:temp_bytes] == 0
|
28
|
-
return value
|
29
|
-
end
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|
33
|
-
raise "No work_mem found."
|
34
|
-
end
|
35
|
-
end
|
36
|
-
end
|