baracus 0.1

Sign up to get free protection for your applications and to get access to all the features.
data/README ADDED
@@ -0,0 +1,38 @@
1
+ httperf wrapper for benchmarking CouchDB
2
+
3
+ Features:
4
+
5
+ * Results sent to CouchDB and printed in TSV
6
+ * Creates wsesslog files dynamically using random data for document writes and _all_docs for reads
7
+ * Lots of knobs: session counts, document size, request rate, etc
8
+ * httperf session work logs are attached to results for replaying later
9
+
10
+
11
+ License:
12
+
13
+ This code is available as Open Source Software under the MIT license.
14
+
15
+
16
+ Dependencies:
17
+
18
+ gem sources -a http://gems.opscode.com/gems
19
+ gem install mixlib-config open4 json rest-client --no-ri --no-rdoc
20
+
21
+ httperf installed (see suggestions below before installing)
22
+
23
+
24
+ Usage:
25
+
26
+ $ ./baracus.rb baracus.yml
27
+
28
+
29
+ Suggestions:
30
+
31
+ * Set "ulimit -n 65535" before running baracus
32
+ * Change FD_SETSIZE to 65535 in /usr/include/bits/typesizes.h before compiling httperf
33
+ * For tests with more than 1000 sessions increase MAX_SESSION_TEMPLATES in gen/wsesslog.c before compiling httperf
34
+
35
+ Acknowlegdements:
36
+
37
+ Details on httperf http://www.hpl.hp.com/research/linux/httperf/httperf-man-0.9.txt
38
+ Some of the httperf output parsing code was taken from http://github.com/sv/httperf4r
@@ -0,0 +1,100 @@
1
+ #! /usr/bin/ruby
2
+
3
+ ## Copyright 2009, Joe Williams <joe@joetify.com>
4
+ ##
5
+ ## Permission is hereby granted, free of charge, to any person
6
+ ## obtaining a copy of this software and associated documentation
7
+ ## files (the "Software"), to deal in the Software without
8
+ ## restriction, including without limitation the rights to use,
9
+ ## copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ ## copies of the Software, and to permit persons to whom the
11
+ ## Software is furnished to do so, subject to the following
12
+ ## conditions:
13
+ ##
14
+ ## The above copyright notice and this permission notice shall be
15
+ ## included in all copies or substantial portions of the Software.
16
+ ##
17
+ ## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18
+ ## EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
19
+ ## OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20
+ ## NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
21
+ ## HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
22
+ ## WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23
+ ## FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
24
+ ## OTHER DEALINGS IN THE SOFTWARE.
25
+
26
+ require 'rubygems'
27
+ require 'rest_client'
28
+ require 'json'
29
+ require 'open4'
30
+ include Open4
31
+ require 'mixlib/config'
32
+ require 'base64'
33
+ require 'yaml'
34
+
35
+ require 'lib/config'
36
+ require 'lib/httperf'
37
+ require 'lib/results'
38
+
39
+ class Baracus
40
+
41
+ def self.get_url
42
+ if Baracus::Config.user && Baracus::Config.password
43
+ url = "http://#{Baracus::Config.user}:#{Baracus::Config.password}@#{Baracus::Config.host}:#{Baracus::Config.port}/#{Baracus::Config.db}"
44
+ else
45
+ url = "http://#{Baracus::Config.host}:#{Baracus::Config.port}/#{Baracus::Config.db}"
46
+ end
47
+ url
48
+ end
49
+
50
+
51
+ def self.main
52
+ date = Time.now
53
+
54
+ puts "Running #{Baracus::Config.bench_name}:"
55
+ puts "Database: http://#{Baracus::Config.host}:#{Baracus::Config.port}/#{Baracus::Config.db}"
56
+ puts "Rate: #{Baracus::Config.rate}"
57
+ puts "Sessions: #{Baracus::Config.sessions}"
58
+ puts "Writes: #{Baracus::Config.writes}"
59
+ puts "Reads: #{Baracus::Config.reads}"
60
+ puts "Document Size: #{Baracus::Config.doc_size}"
61
+ puts "Batch=ok: #{Baracus::Config.batchok}\n"
62
+
63
+
64
+ # create db
65
+ RestClient.put("#{get_url}", "")
66
+
67
+
68
+ # write test
69
+ write_wsesslog = Baracus::Httperf.create_write_wsesslog(date)
70
+ results = Baracus::Httperf.run_httperf(write_wsesslog)
71
+
72
+ puts "### #{Baracus::Config.bench_name} write results ###"
73
+ output = Baracus::Results.parse_results(results)
74
+ puts ""
75
+ puts Baracus::Results.format_results(output)
76
+ puts "#####################\n"
77
+
78
+ Baracus::Results.report_results(output, "writes", write_wsesslog, date)
79
+
80
+ puts "waiting for 60 seconds ...\n"
81
+ sleep 60
82
+
83
+ # read test
84
+ read_wsesslog = Baracus::Httperf.create_read_wsesslog(date)
85
+ results = Baracus::Httperf.run_httperf(read_wsesslog)
86
+
87
+ puts "### #{Baracus::Config.bench_name} read results ###"
88
+ output = Baracus::Results.parse_results(results)
89
+ puts ""
90
+ puts Baracus::Results.format_results(output)
91
+ puts "#####################"
92
+
93
+ Baracus::Results.report_results(output, "reads", read_wsesslog, date)
94
+
95
+ # clean up
96
+ RestClient.delete("#{get_url}")
97
+ end
98
+
99
+ main
100
+ end
@@ -0,0 +1,20 @@
1
+ httperf: "/usr/bin/httperf"
2
+ host: "localhost"
3
+ port: "5984"
4
+ user:
5
+ password:
6
+ db: "testdb"
7
+ report_url: "http://localhost:5984/bench"
8
+ name: "test123"
9
+ config:
10
+ timeout: 30
11
+ sessions: 100
12
+ rate: 100
13
+ doc_size: 100
14
+ writes: 10
15
+ reads: 10
16
+ batchok: false
17
+ info:
18
+ storage: "raid10, 4 disks, ebs"
19
+ anything: "stuff"
20
+ more: "foo"
@@ -0,0 +1,48 @@
1
+ ## Copyright 2009, Joe Williams <joe@joetify.com>
2
+ ##
3
+ ## Permission is hereby granted, free of charge, to any person
4
+ ## obtaining a copy of this software and associated documentation
5
+ ## files (the "Software"), to deal in the Software without
6
+ ## restriction, including without limitation the rights to use,
7
+ ## copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ ## copies of the Software, and to permit persons to whom the
9
+ ## Software is furnished to do so, subject to the following
10
+ ## conditions:
11
+ ##
12
+ ## The above copyright notice and this permission notice shall be
13
+ ## included in all copies or substantial portions of the Software.
14
+ ##
15
+ ## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ ## EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
17
+ ## OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ ## NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
19
+ ## HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20
+ ## WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21
+ ## FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22
+ ## OTHER DEALINGS IN THE SOFTWARE.
23
+
24
+ class Baracus
25
+ class Config
26
+ baracus_config = YAML.load(File.open(ARGV[0]))
27
+ extend(Mixlib::Config)
28
+ configure do |c|
29
+ c[:httperf] = baracus_config["httperf"]
30
+ c[:host] = baracus_config["host"]
31
+ c[:port] = baracus_config["port"]
32
+ c[:db] = baracus_config["db"]
33
+ c[:report_url] = baracus_config["report_url"]
34
+ c[:bench_name] = baracus_config["name"]
35
+ c[:config] = baracus_config["config"]
36
+ c[:timeout] = baracus_config["config"]["timeout"]
37
+ c[:sessions] = baracus_config["config"]["sessions"]
38
+ c[:rate] = baracus_config["config"]["rate"]
39
+ c[:doc_size] = baracus_config["config"]["doc_size"]
40
+ c[:writes] = baracus_config["config"]["writes"]
41
+ c[:reads] = baracus_config["config"]["reads"]
42
+ c[:batchok] = baracus_config["config"]["batchok"]
43
+ c[:info] = baracus_config["info"]
44
+ c[:user] = baracus_config["user"]
45
+ c[:password] = baracus_config["password"]
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,116 @@
1
+ ## Copyright 2009, Joe Williams <joe@joetify.com>
2
+ ##
3
+ ## Permission is hereby granted, free of charge, to any person
4
+ ## obtaining a copy of this software and associated documentation
5
+ ## files (the "Software"), to deal in the Software without
6
+ ## restriction, including without limitation the rights to use,
7
+ ## copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ ## copies of the Software, and to permit persons to whom the
9
+ ## Software is furnished to do so, subject to the following
10
+ ## conditions:
11
+ ##
12
+ ## The above copyright notice and this permission notice shall be
13
+ ## included in all copies or substantial portions of the Software.
14
+ ##
15
+ ## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ ## EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
17
+ ## OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ ## NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
19
+ ## HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20
+ ## WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21
+ ## FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22
+ ## OTHER DEALINGS IN THE SOFTWARE.
23
+
24
+ class Baracus
25
+ class Httperf
26
+
27
+ def self.create_write_wsesslog(date)
28
+ # create some random data for the doc writes
29
+ chars = ('a'..'z').to_a + ('A'..'Z').to_a
30
+ doc = (0...Baracus::Config.doc_size).collect { chars[rand(chars.length)] }.join
31
+
32
+ # use batch=ok for writes?
33
+ if Baracus::Config.batchok
34
+ batchok = "?batch=ok"
35
+ else
36
+ batchok = ""
37
+ end
38
+
39
+ # create the httperf wsesslog file
40
+ filename = "httperf_write_wsesslog_#{date.to_i}"
41
+ write_wsesslog = File.new(filename, "w")
42
+ (1..Baracus::Config.sessions).each do |session|
43
+ write_wsesslog.puts "# session #{session}"
44
+ (1..Baracus::Config.writes).each do |write|
45
+ write_wsesslog.puts "/#{Baracus::Config.db}/#{batchok} method=POST contents=\'{\"field\": \"#{doc}\"}\'"
46
+ end
47
+ write_wsesslog.puts ""
48
+ end
49
+ write_wsesslog.close
50
+ filename
51
+ end
52
+
53
+ def self.create_read_wsesslog(date)
54
+ # parse _all_docs to get all the doc ids for reads
55
+ if Baracus::Config.user && Baracus::Config.password
56
+ all_docs_json = RestClient.get("http://#{Baracus::Config.user}:#{Baracus::Config.password}@#{Baracus::Config.host}:#{Baracus::Config.port}/#{Baracus::Config.db}/_all_docs")
57
+ else
58
+ all_docs_json = RestClient.get("http://#{Baracus::Config.host}:#{Baracus::Config.port}/#{Baracus::Config.db}/_all_docs")
59
+ end
60
+
61
+ all_docs = JSON.parse(all_docs_json)
62
+ all_docs_rows = all_docs["rows"]
63
+
64
+ # create the httperf wsesslog file
65
+ filename = "httperf_read_wsesslog_#{date.to_i}"
66
+ read_wsesslog = File.new(filename, "w")
67
+ (1..Baracus::Config.sessions).each do |session|
68
+ read_wsesslog.puts "# session #{session}"
69
+ (1..Baracus::Config.reads).each do |read|
70
+ # pick a random doc
71
+ doc_index = rand(all_docs_rows.length)
72
+ read_wsesslog.puts "/#{Baracus::Config.db}/#{all_docs_rows[doc_index]["id"]} method=GET"
73
+ end
74
+ read_wsesslog.puts ""
75
+ end
76
+ read_wsesslog.close
77
+ filename
78
+ end
79
+
80
+
81
+ def self.run_httperf(wsesslog)
82
+ # for basic auth
83
+ if Baracus::Config.user && Baracus::Config.password
84
+ auth = Base64.encode64("#{Baracus::Config.user}:#{Baracus::Config.password}")
85
+ auth_switch = " --add-header 'Authorization: Basic #{auth}'"
86
+ else
87
+ auth_switch = ""
88
+ end
89
+
90
+ # run httperf with the wsesslog and write results to file
91
+ httperf_cmd = <<-EOH
92
+ #{Baracus::Config.httperf} \
93
+ --hog \
94
+ --rate=#{Baracus::Config.rate} \
95
+ --timeout=#{Baracus::Config.timeout} \
96
+ --send-buffer=4096 \
97
+ --recv-buffer=16384 \
98
+ --max-connections=4 \
99
+ --max-piped-calls=32 \
100
+ --server=#{Baracus::Config.host} \
101
+ --port=#{Baracus::Config.port} \
102
+ --wsesslog=#{Baracus::Config.sessions},0,#{wsesslog} #{auth_switch}
103
+ EOH
104
+
105
+ results = ""
106
+ status = popen4(httperf_cmd) do |pid, stdin, stdout, stderr|
107
+ stdout.each do |line|
108
+ results << line
109
+ end
110
+ end
111
+
112
+ results
113
+ end
114
+
115
+ end
116
+ end
@@ -0,0 +1,101 @@
1
+ ## Copyright 2009, Joe Williams <joe@joetify.com>
2
+ ##
3
+ ## Permission is hereby granted, free of charge, to any person
4
+ ## obtaining a copy of this software and associated documentation
5
+ ## files (the "Software"), to deal in the Software without
6
+ ## restriction, including without limitation the rights to use,
7
+ ## copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ ## copies of the Software, and to permit persons to whom the
9
+ ## Software is furnished to do so, subject to the following
10
+ ## conditions:
11
+ ##
12
+ ## The above copyright notice and this permission notice shall be
13
+ ## included in all copies or substantial portions of the Software.
14
+ ##
15
+ ## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ ## EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
17
+ ## OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ ## NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
19
+ ## HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20
+ ## WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21
+ ## FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22
+ ## OTHER DEALINGS IN THE SOFTWARE.
23
+
24
+ class Baracus
25
+ class Results
26
+
27
+ def self.parse_results(output)
28
+ results={}
29
+ output.each_line{ |line|
30
+ puts line
31
+ results['test_duration']=$1 if line=~/^Total: .*test-duration (\d+)/
32
+ results['replies']=$1.to_i if line=~/^Total: .*replies (\d+)/
33
+ results['con_rate']=$1 if line=~/^Connection rate: (\d+\.\d)/
34
+ results['req_rate']=$1 if line=~/^Request rate: (\d+\.\d)/
35
+ if line=~/^Reply rate .* min (\d+\.\d) avg (\d+\.\d) max (\d+\.\d) stddev (\d+\.\d)/
36
+ results['rep_rate_min']=$1
37
+ results['rep_rate_avg']=$2
38
+ results['rep_rate_max']=$3
39
+ results['rep_rate_stddev']=$4
40
+ end
41
+ results['rep_time']=$1 if line=~/^Reply time .* response (\d+\.\d)/
42
+ results['net_io']=$1 if line=~/^Net I\/O: (\d+\.\d)/
43
+ results['errors']=$1 if line=~/^Errors: total (\d+)/
44
+ }
45
+ if results['replies']==0
46
+ puts "Zero replies recieved,test invalid: rate #{results['rate']}\n"
47
+ else
48
+ results['percent_errors']=(100*results['errors'].to_f/results['replies'].to_f).to_s
49
+ end
50
+ results
51
+ end
52
+
53
+ def self.format_results(results)
54
+ tsv_results = results
55
+ line=""
56
+ header=['req_rate','con_rate','rep_rate_min','rep_rate_avg','rep_rate_max',
57
+ 'rep_rate_stddev', 'rep_time','net_io','percent_errors']
58
+ header.each{|h| line << "#{h}\t"}
59
+ line << "\n"
60
+ header.each{|h| line << tsv_results[h] << "\t"}
61
+ line << "\n"
62
+ line
63
+ end
64
+
65
+ def self.report_results(results, type, wsesslog, date)
66
+ # convert strings to floats
67
+ results.each do |k, v|
68
+ results[k] = v.to_f
69
+ end
70
+
71
+ config = {}
72
+ config.update(Baracus::Config.config)
73
+ config.store(
74
+ "database", "http://#{Baracus::Config.host}:#{Baracus::Config.port}/#{Baracus::Config.db}"
75
+ )
76
+ results['config'] = config
77
+
78
+ info = {}
79
+ info.update(Baracus::Config.info)
80
+ version_json = RestClient.get("http://#{Baracus::Config.host}:#{Baracus::Config.port}/")
81
+ version = JSON.parse(version_json)
82
+ info.store("version", version["version"])
83
+ results['info'] = info
84
+ results['date'] = date
85
+ results_json = results.to_json
86
+
87
+ # send the results to couch and create the db if it doesnt exist
88
+ begin
89
+ reply_json = RestClient.put("#{Baracus::Config.report_url}/#{Baracus::Config.bench_name}_#{type}_#{date.to_i}", results_json, :content_type => "application/json")
90
+ reply = JSON.parse(reply_json)
91
+ wsesslog_file = File.read(wsesslog)
92
+ RestClient.put("#{Baracus::Config.report_url}/#{reply["id"]}/attachment?rev=#{reply["rev"]}", wsesslog_file, :content_type => "application/text")
93
+ rescue
94
+ RestClient.put("#{Baracus::Config.report_url}/", "")
95
+ retry
96
+ end
97
+
98
+ end
99
+
100
+ end
101
+ end
metadata ADDED
@@ -0,0 +1,99 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: baracus
3
+ version: !ruby/object:Gem::Version
4
+ version: "0.1"
5
+ platform: ruby
6
+ authors:
7
+ - joe williams
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-11-02 00:00:00 -08:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: mixlib-config
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: "0"
24
+ version:
25
+ - !ruby/object:Gem::Dependency
26
+ name: rest-client
27
+ type: :runtime
28
+ version_requirement:
29
+ version_requirements: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: "0"
34
+ version:
35
+ - !ruby/object:Gem::Dependency
36
+ name: json
37
+ type: :runtime
38
+ version_requirement:
39
+ version_requirements: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ version: "0"
44
+ version:
45
+ - !ruby/object:Gem::Dependency
46
+ name: open4
47
+ type: :runtime
48
+ version_requirement:
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: "0"
54
+ version:
55
+ description:
56
+ email: joe@joetify.com
57
+ executables:
58
+ - baracus
59
+ extensions: []
60
+
61
+ extra_rdoc_files:
62
+ - README
63
+ files:
64
+ - bin/baracus
65
+ - lib/config.rb
66
+ - lib/httperf.rb
67
+ - lib/results.rb
68
+ - config/baracus.yml
69
+ - README
70
+ has_rdoc: true
71
+ homepage: http://github.com/joewilliams/baracus
72
+ licenses: []
73
+
74
+ post_install_message:
75
+ rdoc_options: []
76
+
77
+ require_paths:
78
+ - lib
79
+ required_ruby_version: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - ">="
82
+ - !ruby/object:Gem::Version
83
+ version: "0"
84
+ version:
85
+ required_rubygems_version: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: "0"
90
+ version:
91
+ requirements: []
92
+
93
+ rubyforge_project:
94
+ rubygems_version: 1.3.5
95
+ signing_key:
96
+ specification_version: 3
97
+ summary: httperf wrapper for couchdb
98
+ test_files: []
99
+