tobias 0.1.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 6f62ad896d11551538f57fe41808b243b3484b99e6271ce3aa33610794a71c96
4
+ data.tar.gz: f7bd71569888157686b8a3f605ea7be8286d3967197789a55b5dc45f361f9170
5
+ SHA512:
6
+ metadata.gz: f678080160f435018f4a2c4e867e0363ef4e386fed008b94542ca3a369a19f25e4ce718bf49e1e8cc24c12b5a7b12a0071395ab94128d8573c56af52abcdbae7
7
+ data.tar.gz: 7cc8e7b73904fcf7dce92dcae85cc5f338c96ae0a7e52ae80b519c5d1cf1875450e3fdaf062e09c905dd52073efb889751e63ebba62343ce30a5fac64964d16d
data/bin/tobias ADDED
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require_relative "../lib/tobias"
5
+
6
+ RubyVM::YJIT.enable
7
+ Tobias::CLI.start(ARGV, exit_on_failure: true)
data/lib/tobias/cli.rb ADDED
@@ -0,0 +1,75 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Tobias
4
+ class CLI < Thor
5
+ include ActiveSupport::NumberHelper
6
+
7
+ def self.exit_on_failure?
8
+ true
9
+ end
10
+
11
+ desc "profile SCRIPT", "profile"
12
+ option :database_url, type: :string, required: true
13
+ option :iterations, type: :numeric, default: 100
14
+ def profile(script)
15
+ database = Sequel.connect(options[:database_url])
16
+ database.loggers << Logger.new(nil)
17
+ database.extension :pg_json
18
+
19
+ if File.exist?(script)
20
+ code = File.read(script)
21
+ else
22
+ raise "Script not found at: #{script}"
23
+ end
24
+
25
+ WorkMem.all.each do |value|
26
+ database.transaction do
27
+ database.run("SET LOCAL work_mem = '#{value.to_sql}'")
28
+
29
+ eval(code, binding, script)
30
+
31
+ @queries.each do |name, block|
32
+ database.select(Sequel.function(:pg_stat_reset)).first
33
+
34
+ query = instance_eval(&block)
35
+ times = []
36
+
37
+ options[:iterations].to_i.times do
38
+ time = Benchmark.realtime do
39
+ database.run(query.sql)
40
+ end
41
+ times << time
42
+ end
43
+
44
+ stats = database[:pg_stat_database].where(datname: Sequel.function(:current_database)).first
45
+
46
+ puts "--------------------------------"
47
+ puts "query: #{name}"
48
+ puts "work_mem: #{value.to_sql}"
49
+ puts "clock time (mean): #{times.mean.round(2)}"
50
+ puts "clock time (95%): #{times.percentile(95).round(2)}"
51
+
52
+ if stats[:temp_files] > 0 || stats[:temp_bytes] > 0
53
+ puts "Not enough work_mem"
54
+ puts "temp_files: #{stats[:temp_files]}"
55
+ puts "temp_bytes: #{stats[:temp_bytes]}"
56
+ else
57
+ puts "No temporary files written."
58
+ puts "Current work_mem: '#{value.to_sql}' is sufficient."
59
+ return
60
+ end
61
+ puts "--------------------------------"
62
+ end
63
+ end
64
+ end
65
+ end
66
+
67
+ private
68
+
69
+ def query(name, &block)
70
+ @queries ||= {}
71
+ @queries[name] = block
72
+ end
73
+
74
+ end
75
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Tobias
4
+ class Evaluation
5
+ def initialize(database)
6
+ @database = database
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Tobias
4
+ VERSION = "0.1.0"
5
+ end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Tobias
4
+ class WorkMem
5
+ def initialize(amount)
6
+ @amount = amount
7
+ end
8
+
9
+ def to_sql
10
+ case @amount
11
+ when 0...1024
12
+ "#{@amount}B"
13
+ when 1024...1048576
14
+ kb = @amount / 1024.0
15
+ kb == kb.to_i ? "#{kb.to_i}kB" : "#{kb}kB"
16
+ when 1048576...1073741824
17
+ mb = @amount / 1048576.0
18
+ mb == mb.to_i ? "#{mb.to_i}MB" : "#{mb}MB"
19
+ else
20
+ gb = @amount / 1073741824.0
21
+ gb == gb.to_i ? "#{gb.to_i}GB" : "#{gb}GB"
22
+ end
23
+ end
24
+
25
+ def inspect
26
+ to_sql
27
+ end
28
+
29
+ def self.all
30
+ [
31
+ new(64.kilobytes),
32
+ new(128.kilobytes),
33
+ new(512.kilobytes),
34
+ new(1.megabyte),
35
+ new(4.megabytes),
36
+ new(8.megabytes),
37
+ new(16.megabytes),
38
+ new(32.megabytes),
39
+ new(64.megabytes),
40
+ new(128.megabytes),
41
+ new(256.megabytes),
42
+ new(512.megabytes),
43
+ new(1.gigabyte),
44
+ new(2.gigabytes),
45
+ new(4.gigabytes),
46
+ new(8.gigabytes),
47
+ ]
48
+ end
49
+ end
50
+ end
data/lib/tobias.rb ADDED
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Performance optimizations for Ruby
4
+ if RUBY_ENGINE == "ruby"
5
+ # Optimize GC for high-throughput scenarios
6
+ GC.start(full_mark: true, immediate_sweep: true) if GC.respond_to?(:start)
7
+
8
+ # Configure for better concurrent performance
9
+ if defined?(GC.compact)
10
+ at_exit { GC.compact }
11
+ end
12
+ end
13
+
14
+ require "bundler/setup"
15
+ Bundler.require(:default)
16
+
17
+ require "thor"
18
+ require "active_support/all"
19
+ require "sequel"
20
+ require "enumerable-stats"
21
+ require "benchmark"
22
+
23
+ $LOAD_PATH.unshift File.dirname(__FILE__)
24
+
25
+ module Tobias
26
+ autoload :CLI, "tobias/cli"
27
+ autoload :Evaluation, "tobias/evaluation"
28
+ autoload :WorkMem, "tobias/work_mem"
29
+ end
metadata ADDED
@@ -0,0 +1,262 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: tobias
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Jon Daniel
8
+ bindir: bin
9
+ cert_chain: []
10
+ date: 2025-08-03 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: activesupport
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - "~>"
17
+ - !ruby/object:Gem::Version
18
+ version: '8.0'
19
+ - - ">="
20
+ - !ruby/object:Gem::Version
21
+ version: 8.0.0
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ requirements:
26
+ - - "~>"
27
+ - !ruby/object:Gem::Version
28
+ version: '8.0'
29
+ - - ">="
30
+ - !ruby/object:Gem::Version
31
+ version: 8.0.0
32
+ - !ruby/object:Gem::Dependency
33
+ name: benchmark
34
+ requirement: !ruby/object:Gem::Requirement
35
+ requirements:
36
+ - - "~>"
37
+ - !ruby/object:Gem::Version
38
+ version: '0.4'
39
+ - - ">="
40
+ - !ruby/object:Gem::Version
41
+ version: 0.4.0
42
+ type: :runtime
43
+ prerelease: false
44
+ version_requirements: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - "~>"
47
+ - !ruby/object:Gem::Version
48
+ version: '0.4'
49
+ - - ">="
50
+ - !ruby/object:Gem::Version
51
+ version: 0.4.0
52
+ - !ruby/object:Gem::Dependency
53
+ name: bundler
54
+ requirement: !ruby/object:Gem::Requirement
55
+ requirements:
56
+ - - "~>"
57
+ - !ruby/object:Gem::Version
58
+ version: '2.4'
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: 2.4.0
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '2.4'
69
+ - - ">="
70
+ - !ruby/object:Gem::Version
71
+ version: 2.4.0
72
+ - !ruby/object:Gem::Dependency
73
+ name: concurrent-ruby
74
+ requirement: !ruby/object:Gem::Requirement
75
+ requirements:
76
+ - - "~>"
77
+ - !ruby/object:Gem::Version
78
+ version: '1.3'
79
+ - - ">="
80
+ - !ruby/object:Gem::Version
81
+ version: 1.3.0
82
+ type: :runtime
83
+ prerelease: false
84
+ version_requirements: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - "~>"
87
+ - !ruby/object:Gem::Version
88
+ version: '1.3'
89
+ - - ">="
90
+ - !ruby/object:Gem::Version
91
+ version: 1.3.0
92
+ - !ruby/object:Gem::Dependency
93
+ name: dry-configurable
94
+ requirement: !ruby/object:Gem::Requirement
95
+ requirements:
96
+ - - "~>"
97
+ - !ruby/object:Gem::Version
98
+ version: '1.0'
99
+ - - ">="
100
+ - !ruby/object:Gem::Version
101
+ version: 1.0.0
102
+ type: :runtime
103
+ prerelease: false
104
+ version_requirements: !ruby/object:Gem::Requirement
105
+ requirements:
106
+ - - "~>"
107
+ - !ruby/object:Gem::Version
108
+ version: '1.0'
109
+ - - ">="
110
+ - !ruby/object:Gem::Version
111
+ version: 1.0.0
112
+ - !ruby/object:Gem::Dependency
113
+ name: enumerable-stats
114
+ requirement: !ruby/object:Gem::Requirement
115
+ requirements:
116
+ - - "~>"
117
+ - !ruby/object:Gem::Version
118
+ version: '1.1'
119
+ - - ">="
120
+ - !ruby/object:Gem::Version
121
+ version: 1.1.0
122
+ type: :runtime
123
+ prerelease: false
124
+ version_requirements: !ruby/object:Gem::Requirement
125
+ requirements:
126
+ - - "~>"
127
+ - !ruby/object:Gem::Version
128
+ version: '1.1'
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ version: 1.1.0
132
+ - !ruby/object:Gem::Dependency
133
+ name: json
134
+ requirement: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - "~>"
137
+ - !ruby/object:Gem::Version
138
+ version: '2.13'
139
+ - - ">="
140
+ - !ruby/object:Gem::Version
141
+ version: 2.13.0
142
+ type: :runtime
143
+ prerelease: false
144
+ version_requirements: !ruby/object:Gem::Requirement
145
+ requirements:
146
+ - - "~>"
147
+ - !ruby/object:Gem::Version
148
+ version: '2.13'
149
+ - - ">="
150
+ - !ruby/object:Gem::Version
151
+ version: 2.13.0
152
+ - !ruby/object:Gem::Dependency
153
+ name: pg
154
+ requirement: !ruby/object:Gem::Requirement
155
+ requirements:
156
+ - - "~>"
157
+ - !ruby/object:Gem::Version
158
+ version: '1.6'
159
+ - - ">="
160
+ - !ruby/object:Gem::Version
161
+ version: 1.6.0
162
+ type: :runtime
163
+ prerelease: false
164
+ version_requirements: !ruby/object:Gem::Requirement
165
+ requirements:
166
+ - - "~>"
167
+ - !ruby/object:Gem::Version
168
+ version: '1.6'
169
+ - - ">="
170
+ - !ruby/object:Gem::Version
171
+ version: 1.6.0
172
+ - !ruby/object:Gem::Dependency
173
+ name: sequel
174
+ requirement: !ruby/object:Gem::Requirement
175
+ requirements:
176
+ - - "~>"
177
+ - !ruby/object:Gem::Version
178
+ version: '5.76'
179
+ - - ">="
180
+ - !ruby/object:Gem::Version
181
+ version: 5.76.0
182
+ type: :runtime
183
+ prerelease: false
184
+ version_requirements: !ruby/object:Gem::Requirement
185
+ requirements:
186
+ - - "~>"
187
+ - !ruby/object:Gem::Version
188
+ version: '5.76'
189
+ - - ">="
190
+ - !ruby/object:Gem::Version
191
+ version: 5.76.0
192
+ - !ruby/object:Gem::Dependency
193
+ name: thor
194
+ requirement: !ruby/object:Gem::Requirement
195
+ requirements:
196
+ - - "~>"
197
+ - !ruby/object:Gem::Version
198
+ version: '1.3'
199
+ - - ">="
200
+ - !ruby/object:Gem::Version
201
+ version: 1.3.0
202
+ type: :runtime
203
+ prerelease: false
204
+ version_requirements: !ruby/object:Gem::Requirement
205
+ requirements:
206
+ - - "~>"
207
+ - !ruby/object:Gem::Version
208
+ version: '1.3'
209
+ - - ">="
210
+ - !ruby/object:Gem::Version
211
+ version: 1.3.0
212
+ - !ruby/object:Gem::Dependency
213
+ name: rspec
214
+ requirement: !ruby/object:Gem::Requirement
215
+ requirements:
216
+ - - "~>"
217
+ - !ruby/object:Gem::Version
218
+ version: '3.12'
219
+ type: :development
220
+ prerelease: false
221
+ version_requirements: !ruby/object:Gem::Requirement
222
+ requirements:
223
+ - - "~>"
224
+ - !ruby/object:Gem::Version
225
+ version: '3.12'
226
+ description: Tobias
227
+ email: binarycleric@gmail.com
228
+ executables:
229
+ - tobias
230
+ extensions: []
231
+ extra_rdoc_files: []
232
+ files:
233
+ - bin/tobias
234
+ - lib/tobias.rb
235
+ - lib/tobias/cli.rb
236
+ - lib/tobias/evaluation.rb
237
+ - lib/tobias/version.rb
238
+ - lib/tobias/work_mem.rb
239
+ homepage: https://github.com/binarycleric/tobias
240
+ licenses:
241
+ - MIT
242
+ metadata:
243
+ source_code_uri: https://github.com/binarycleric/tobias
244
+ rubygems_mfa_required: 'true'
245
+ rdoc_options: []
246
+ require_paths:
247
+ - lib
248
+ required_ruby_version: !ruby/object:Gem::Requirement
249
+ requirements:
250
+ - - ">="
251
+ - !ruby/object:Gem::Version
252
+ version: 3.3.0
253
+ required_rubygems_version: !ruby/object:Gem::Requirement
254
+ requirements:
255
+ - - ">="
256
+ - !ruby/object:Gem::Version
257
+ version: '0'
258
+ requirements: []
259
+ rubygems_version: 3.6.2
260
+ specification_version: 4
261
+ summary: Tobias
262
+ test_files: []