baracus 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.
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
+