dbfire 0.0.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.
Files changed (4) hide show
  1. checksums.yaml +7 -0
  2. data/bin/fire +260 -0
  3. data/lib/dbfire/filesize.rb +179 -0
  4. metadata +45 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: df593da02e1d142e3ac1f674f02bcf9d411c4f4c
4
+ data.tar.gz: 07ae4472da933e06aa32cc696d4fb1d193a047dd
5
+ SHA512:
6
+ metadata.gz: 88ded2d6f4fc0d89da6a51937932658dbe1d08de2fcc204709e134a9c9f70fc05a3e5edc590a8081e46ba54b5ddc5bad8e7f29a1b2a984e696a6bb687388f3ea
7
+ data.tar.gz: 90acc5f7c14db69f6bfc2d2220f66df71bdd55b5655ed3701f5fda27701880cb4bf7b6f1c22b0d1f48cb857c437c3d710dc2a893e41389606896eee98de7d05a
data/bin/fire ADDED
@@ -0,0 +1,260 @@
1
+ #!/usr/bin/ruby
2
+
3
+ load File.dirname(__FILE__) + '/../lib/dbfire/filesize.rb'
4
+
5
+ Thread.abort_on_exception = true
6
+
7
+ $blocksize = 65536
8
+
9
+ TYPE_PREFIXES = %w{Ki Mi Gi Ti Pi Ei Zi Yi}
10
+
11
+ class Fire
12
+ def self.usage
13
+ puts "Database load testing toolkit by m4rkw\n\n"
14
+ puts "Usage: fire <file1> [file2] [..]\n\n"
15
+ puts "To split your general.log:\n\n"
16
+ puts "fire -s <general log> <threads> [all]\n\n"
17
+ exit
18
+ end
19
+
20
+ def initialize(args)
21
+ if args[0] == '-s'
22
+ if args.length <3
23
+ self.class.usage
24
+ end
25
+ split(args)
26
+ return
27
+ end
28
+
29
+ @cli_string = ENV['CLI']
30
+
31
+ if !@cli_string
32
+ raise "The environment variable CLI is not set. Please set this to your cli command to invoke the database client."
33
+ end
34
+
35
+ @queue = Queue.new
36
+
37
+ @start_time = Time.now.to_i
38
+
39
+ fire(ARGV)
40
+ display
41
+ monitor
42
+ end
43
+
44
+ def split(args)
45
+ thread_id = 0
46
+
47
+ File.open(args[1],"r:ascii-8bit").each do |line|
48
+ line.chomp!
49
+
50
+ m = line.match(/[\s\t]+[0-9]+ Query[\t\s]+(\/\*.*?\*\/)?(.*)/)
51
+
52
+ if m
53
+ word = m[2].split(" ").first
54
+
55
+ if ["SELECT","INSERT","UPDATE","DELETE"].include? word
56
+ if args[3] == 'all'
57
+ target = "ALL.#{thread_id}"
58
+ else
59
+ target = "#{word}.#{thread_id}"
60
+ end
61
+
62
+ File.open(target,'a+') do |out|
63
+ out.write("#{m[2]};\n")
64
+ end
65
+
66
+ thread_id += 1
67
+
68
+ if thread_id >= args[2].to_i
69
+ thread_id = 0
70
+ end
71
+ end
72
+ end
73
+ end
74
+ end
75
+
76
+ def fire(files)
77
+ @threads = []
78
+ @progress = []
79
+
80
+ for thread_id in 0...files.length
81
+ @progress[thread_id] = {
82
+ :total_bytes => File.size(files[thread_id]),
83
+ :sent => 0,
84
+ :sent_at_last_interval => 0,
85
+ :sent_speed => 0,
86
+ :recv => 0,
87
+ :recv_at_last_interval => 0,
88
+ :recv_speed => 0,
89
+ :timestamp => Time.now.to_i
90
+ }
91
+
92
+ thread = Thread.new(thread_id, files[thread_id]) do |tid, thread_file|
93
+ file_thread(tid, thread_file)
94
+ end
95
+
96
+ @threads.push thread
97
+ end
98
+ end
99
+
100
+ def file_thread(tid, thread_file)
101
+ bytes_sent = 0
102
+ bytes_read = 0
103
+
104
+ File.open(thread_file, "r:utf-8") do |file|
105
+ IO.popen(@cli_string, "a+") do |db|
106
+ data = nil
107
+
108
+ while not file.eof?
109
+ if !data
110
+ data = file.readline
111
+ end
112
+
113
+ begin
114
+ db.write_nonblock(data)
115
+ bytes_sent += data.length
116
+ data = nil
117
+ @queue << {:thread_id => tid, :sent => bytes_sent, :recv => bytes_read}
118
+ rescue IO::WaitWritable
119
+ begin
120
+ recv = db.read_nonblock($blocksize)
121
+ bytes_read += recv.length
122
+ @queue << {:thread_id => tid, :sent => bytes_sent, :recv => bytes_read}
123
+ rescue IO::WaitReadable
124
+ end
125
+ end
126
+ end
127
+
128
+ db.close_write
129
+
130
+ while not db.eof?
131
+ begin
132
+ recv = db.read_nonblock($blocksize)
133
+ bytes_read += recv.length
134
+ @queue << {:thread_id => tid, :sent => bytes_sent, :recv => bytes_read}
135
+ rescue
136
+ IO::WaitReadable
137
+ end
138
+ end
139
+ end
140
+ end
141
+ end
142
+
143
+ def monitor
144
+ Thread.new do
145
+ while 1
146
+ update
147
+ sleep 0.5
148
+ end
149
+ end
150
+
151
+ Thread.new do
152
+ last_update = nil
153
+
154
+ while 1
155
+ begin
156
+ value = @queue.pop
157
+ process_update(value)
158
+ rescue
159
+ end
160
+ end
161
+ end
162
+
163
+ @threads.each do |thread|
164
+ thread.join
165
+ end
166
+
167
+ execution_time = Time.at(Time.now.to_i - @start_time).utc.strftime("%H:%M:%S")
168
+
169
+ puts "\nFinished in: #{execution_time}\n\n"
170
+ end
171
+
172
+ def process_update(value)
173
+ @progress[value[:thread_id]][:sent] = value[:sent]
174
+ @progress[value[:thread_id]][:recv] = value[:recv]
175
+
176
+ time_now = Time.now.to_i
177
+ seconds_elapsed = time_now - @progress[value[:thread_id]][:timestamp]
178
+
179
+ if seconds_elapsed > 0
180
+ sent_diff = value[:sent] - @progress[value[:thread_id]][:sent_at_last_interval]
181
+ recv_diff = value[:recv] - @progress[value[:thread_id]][:recv_at_last_interval]
182
+
183
+ @progress[value[:thread_id]][:sent_speed] = sent_diff / seconds_elapsed
184
+ @progress[value[:thread_id]][:recv_speed] = recv_diff / seconds_elapsed
185
+ @progress[value[:thread_id]][:timestamp] = time_now
186
+ @progress[value[:thread_id]][:sent_at_last_interval] = value[:sent]
187
+ @progress[value[:thread_id]][:recv_at_last_interval] = value[:recv]
188
+ end
189
+ end
190
+
191
+ def display
192
+ output = []
193
+
194
+ for i in 0...@progress.length
195
+ pro = @progress[i][:sent]
196
+ total = @progress[i][:total_bytes]
197
+ pc = sprintf("%.2f", pro / (total.to_f / 100))
198
+ data_out = Filesize.from("#{@progress[i][:sent]} B").pretty
199
+ data_in = Filesize.from("#{@progress[i][:recv]} B").pretty
200
+
201
+ out_speed = Filesize.from("#{@progress[i][:sent_speed]} B").pretty + "/s"
202
+ in_speed = Filesize.from("#{@progress[i][:recv_speed]} B").pretty + "/s"
203
+
204
+ time_now = Time.now.to_i
205
+
206
+ if pro > 0 and (time_now - @start_time) >0
207
+ overall_speed = pro / (time_now - @start_time)
208
+
209
+ if overall_speed > 0
210
+ eta = (total - pro) / overall_speed
211
+ @progress[i][:eta] = Time.at(eta).utc.strftime("%H:%M:%S")
212
+ else
213
+ @progress[i][:eta] = "--"
214
+ end
215
+ else
216
+ @progress[i][:eta] = "--"
217
+ end
218
+
219
+ output.push([data_out, pc, out_speed, data_in, in_speed, @progress[i][:eta]])
220
+ end
221
+
222
+ format_output output, "%d: out: [%s] %s%% (%s) in: [%s] (%s) eta: %s"
223
+ end
224
+
225
+ def format_output(data, format)
226
+ width = []
227
+
228
+ for i in 0...data.length
229
+ for j in 0...data[i].length
230
+ if !width[j]
231
+ width[j] = data[i][j].length
232
+ elsif data[i][j].length > width[j]
233
+ width[j] = data[i][j].length
234
+ end
235
+ end
236
+ end
237
+
238
+ for i in 0...data.length
239
+ for j in 0...data[i].length
240
+ data[i][j] = data[i][j].rjust(width[j], ' ')
241
+ end
242
+ puts sprintf(format, i, *data[i])
243
+ end
244
+ end
245
+
246
+ def update
247
+ for i in 0...@progress.length
248
+ print "\e[A"
249
+ print "\e[K"
250
+ end
251
+
252
+ display
253
+ end
254
+ end
255
+
256
+ if ARGV.length == 0
257
+ Fire.usage
258
+ end
259
+
260
+ Fire.new(ARGV)
@@ -0,0 +1,179 @@
1
+ class Filesize
2
+ include Comparable
3
+
4
+ TYPE_PREFIXES = {
5
+ # Unit prefixes used for SI file sizes.
6
+ :SI => %w{k M G T P E Z Y},
7
+ # Unit prefixes used for binary file sizes.
8
+ :BINARY => %w{Ki Mi Gi Ti Pi Ei Zi Yi}
9
+ }
10
+
11
+ # @deprecated Please use TYPE_PREFIXES[:SI] instead
12
+ PREFIXES = TYPE_PREFIXES[:SI]
13
+
14
+ # Set of rules describing file sizes according to SI units.
15
+ SI = {
16
+ :regexp => /^([\d,.]+)?\s?([kmgtpezy]?)b$/i,
17
+ :multiplier => 1000,
18
+ :prefixes => TYPE_PREFIXES[:SI],
19
+ :presuffix => '' # deprecated
20
+ }
21
+ # Set of rules describing file sizes according to binary units.
22
+ BINARY = {
23
+ :regexp => /^([\d,.]+)?\s?(?:([kmgtpezy])i)?b$/i,
24
+ :multiplier => 1024,
25
+ :prefixes => TYPE_PREFIXES[:BINARY],
26
+ :presuffix => 'i' # deprecated
27
+ }
28
+
29
+ # @param [Number] size A file size, in bytes.
30
+ # @param [SI, BINARY] type Which type to use for conversions.
31
+ def initialize(size, type = BINARY)
32
+ @bytes = size.to_i
33
+ @type = type
34
+ end
35
+
36
+ # @return [Number] Returns the size in bytes.
37
+ def to_i
38
+ @bytes
39
+ end
40
+ alias_method :to_int, :to_i
41
+
42
+ # @param [String] unit Which unit to convert to.
43
+ # @return [Float] Returns the size in a given unit.
44
+ def to(unit = 'B')
45
+ to_parts = self.class.parse(unit)
46
+ prefix = to_parts[:prefix]
47
+
48
+ if prefix == 'B' or prefix.empty?
49
+ return to_i.to_f
50
+ end
51
+
52
+ to_type = to_parts[:type]
53
+ size = @bytes
54
+
55
+ pos = (@type[:prefixes].map { |s| s[0].chr.downcase }.index(prefix.downcase) || -1) + 1
56
+
57
+ size = size/(to_type[:multiplier].to_f**(pos)) unless pos < 1
58
+ end
59
+ alias_method :to_f, :to
60
+
61
+ # @param (see #to_f)
62
+ # @return [String] Same as {#to_f}, but as a string, with the unit appended.
63
+ # @see #to_f
64
+ def to_s(unit = 'B')
65
+ "%.2f %s" % [to(unit).to_f.to_s, unit]
66
+ end
67
+
68
+ # Same as {#to_s} but with an automatic determination of the most
69
+ # sensible unit.
70
+ #
71
+ # @return [String]
72
+ # @see #to_s
73
+ def pretty
74
+ size = @bytes
75
+ if size < @type[:multiplier]
76
+ unit = "B"
77
+ else
78
+ pos = (Math.log(size) / Math.log(@type[:multiplier])).floor
79
+ pos = @type[:prefixes].size-1 if pos > @type[:prefixes].size - 1
80
+
81
+ unit = @type[:prefixes][pos-1] + "B"
82
+ end
83
+
84
+ to_s(unit)
85
+ end
86
+
87
+ # @return [Filesize]
88
+ def +(other)
89
+ self.class.new(@bytes + other.to_i, @type)
90
+ end
91
+
92
+ # @return [Filesize]
93
+ def -(other)
94
+ self.class.new(@bytes - other.to_i, @type)
95
+ end
96
+
97
+ # @return [Filesize]
98
+ def *(other)
99
+ self.class.new(@bytes * other.to_i, @type)
100
+ end
101
+
102
+ # @return [Filesize]
103
+ def /(other)
104
+ result = @bytes / other.to_f
105
+ if other.is_a? Filesize
106
+ result
107
+ else
108
+ self.class.new(result, @type)
109
+ end
110
+ end
111
+
112
+ def <=>(other)
113
+ self.to_i <=> other.to_i
114
+ end
115
+
116
+ # @return [Array<self, other>]
117
+ # @api private
118
+ def coerce(other)
119
+ return self, other
120
+ end
121
+
122
+ class << self
123
+ # Parses a string, which describes a file size, and returns a
124
+ # Filesize object.
125
+ #
126
+ # @param [String] arg A file size to parse.
127
+ # @raise [ArgumentError] Raised if the file size cannot be parsed properly.
128
+ # @return [Filesize]
129
+ def from(arg)
130
+ parts = parse(arg)
131
+ prefix = parts[:prefix]
132
+ size = parts[:size]
133
+ type = parts[:type]
134
+
135
+ raise ArgumentError, "Unparseable filesize" unless type
136
+
137
+ offset = (type[:prefixes].map { |s| s[0].chr.downcase }.index(prefix.downcase) || -1) + 1
138
+
139
+ new(size * (type[:multiplier] ** (offset)), type)
140
+ end
141
+
142
+ # @return [Hash<:prefix, :size, :type>]
143
+ # @api private
144
+ def parse(string)
145
+ type = nil
146
+ # in this order, so we prefer binary :)
147
+ [BINARY, SI].each { |_type|
148
+ if string =~ _type[:regexp]
149
+ type = _type
150
+ break
151
+ end
152
+ }
153
+
154
+ prefix = $2 || ''
155
+ size = ($1 || 0).to_f
156
+
157
+ return { :prefix => prefix, :size => size, :type => type}
158
+ end
159
+ end
160
+
161
+ # The size of a floppy disk
162
+ Floppy = Filesize.from("1474 KiB")
163
+ # The size of a CD
164
+ CD = Filesize.from("700 MB")
165
+ # The size of a common DVD
166
+ DVD_5 = Filesize.from("4.38 GiB")
167
+ # The same as a DVD 5
168
+ DVD = DVD_5
169
+ # The size of a single-sided dual-layer DVD
170
+ DVD_9 = Filesize.from("7.92 GiB")
171
+ # The size of a double-sided single-layer DVD
172
+ DVD_10 = DVD_5 * 2
173
+ # The size of a double-sided DVD, combining a DVD-9 and a DVD-5
174
+ DVD_14 = DVD_9 + DVD_5
175
+ # The size of a double-sided dual-layer DVD
176
+ DVD_18 = DVD_14 * 2
177
+ # The size of a Zip disk
178
+ ZIP = Filesize.from("100 MB")
179
+ end
metadata ADDED
@@ -0,0 +1,45 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: dbfire
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - m4rkw
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-05-29 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: Simple, fast, standalone database load testing toolkid
14
+ email: m@rkw.io
15
+ executables: []
16
+ extensions: []
17
+ extra_rdoc_files: []
18
+ files:
19
+ - bin/fire
20
+ - lib/dbfire/filesize.rb
21
+ homepage: https://github.com/m4rkw/dbfire
22
+ licenses:
23
+ - MIT
24
+ metadata: {}
25
+ post_install_message:
26
+ rdoc_options: []
27
+ require_paths:
28
+ - lib
29
+ required_ruby_version: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ required_rubygems_version: !ruby/object:Gem::Requirement
35
+ requirements:
36
+ - - ">="
37
+ - !ruby/object:Gem::Version
38
+ version: '0'
39
+ requirements: []
40
+ rubyforge_project:
41
+ rubygems_version: 2.4.5.1
42
+ signing_key:
43
+ specification_version: 4
44
+ summary: DBFire
45
+ test_files: []