dbfire 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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: []