drnbench 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/Gemfile +20 -0
- data/LICENSE.txt +674 -0
- data/README.md +296 -0
- data/Rakefile +33 -0
- data/bin/drnbench-publish-subscribe +104 -0
- data/bin/drnbench-request-response +89 -0
- data/doc/text/news.md +5 -0
- data/drnbench.gemspec +59 -0
- data/lib/drnbench.rb +22 -0
- data/lib/drnbench/client/http-droonga.rb +37 -0
- data/lib/drnbench/client/http.rb +68 -0
- data/lib/drnbench/publish-subscribe/configuration.rb +76 -0
- data/lib/drnbench/publish-subscribe/gradual-runner.rb +66 -0
- data/lib/drnbench/publish-subscribe/runner.rb +136 -0
- data/lib/drnbench/publish-subscribe/watch.rb +43 -0
- data/lib/drnbench/request-response/configuration.rb +79 -0
- data/lib/drnbench/request-response/gradual-runner.rb +95 -0
- data/lib/drnbench/request-response/result.rb +127 -0
- data/lib/drnbench/request-response/runner.rb +111 -0
- data/lib/drnbench/server/configuration.rb +59 -0
- data/lib/drnbench/server/engine.rb +21 -0
- data/lib/drnbench/server/protocol-adapter.rb +89 -0
- data/lib/drnbench/version.rb +18 -0
- metadata +170 -0
@@ -0,0 +1,43 @@
|
|
1
|
+
# Copyright (C) 2013-2014 Droonga Project
|
2
|
+
#
|
3
|
+
# This program is free software: you can redistribute it and/or modify
|
4
|
+
# it under the terms of the GNU General Public License as published by
|
5
|
+
# the Free Software Foundation, either version 3 of the License, or
|
6
|
+
# (at your option) any later version.
|
7
|
+
#
|
8
|
+
# This program is distributed in the hope that it will be useful,
|
9
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
10
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
11
|
+
# GNU General Public License for more details.
|
12
|
+
#
|
13
|
+
# You should have received a copy of the GNU General Public License
|
14
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
15
|
+
|
16
|
+
require "droonga/watch_schema"
|
17
|
+
|
18
|
+
module Drnbench
|
19
|
+
module PublishSubscribe
|
20
|
+
class Watch
|
21
|
+
class << self
|
22
|
+
def command
|
23
|
+
"watch"
|
24
|
+
end
|
25
|
+
|
26
|
+
def subscribe(keyword)
|
27
|
+
{
|
28
|
+
"condition" => keyword,
|
29
|
+
"subscriber" => "subscriber for #{keyword}",
|
30
|
+
}
|
31
|
+
end
|
32
|
+
|
33
|
+
def feed(keyword)
|
34
|
+
{
|
35
|
+
"targets" => {
|
36
|
+
"keyword" => keyword,
|
37
|
+
},
|
38
|
+
}
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
# Copyright (C) 2013-2014 Droonga Project
|
2
|
+
#
|
3
|
+
# This program is free software: you can redistribute it and/or modify
|
4
|
+
# it under the terms of the GNU General Public License as published by
|
5
|
+
# the Free Software Foundation, either version 3 of the License, or
|
6
|
+
# (at your option) any later version.
|
7
|
+
#
|
8
|
+
# This program is distributed in the hope that it will be useful,
|
9
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
10
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
11
|
+
# GNU General Public License for more details.
|
12
|
+
#
|
13
|
+
# You should have received a copy of the GNU General Public License
|
14
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
15
|
+
|
16
|
+
require "json"
|
17
|
+
|
18
|
+
module Drnbench
|
19
|
+
module RequestResponse
|
20
|
+
class Configuration
|
21
|
+
attr_accessor :duration, :wait, :request_patterns_file
|
22
|
+
attr_accessor :start_n_clients, :end_n_clients, :step, :n_requests
|
23
|
+
attr_accessor :mode
|
24
|
+
attr_accessor :default_host, :default_port, :default_path, :default_method
|
25
|
+
attr_accessor :report_progressively, :output_path
|
26
|
+
|
27
|
+
MIN_DURATION = 1
|
28
|
+
MIN_WAIT = 0
|
29
|
+
|
30
|
+
def initialize
|
31
|
+
@wait = 1
|
32
|
+
@start_n_clients = 1
|
33
|
+
@end_n_clients = 1
|
34
|
+
@step = 1
|
35
|
+
@n_requests = 1000
|
36
|
+
@mode = :http
|
37
|
+
|
38
|
+
@default_host = "localhost"
|
39
|
+
@default_port = 80
|
40
|
+
@default_path = "/"
|
41
|
+
@default_method = "GET"
|
42
|
+
|
43
|
+
@report_progressively = true
|
44
|
+
@output_path = "/tmp/drnbench-result.csv"
|
45
|
+
end
|
46
|
+
|
47
|
+
def validate
|
48
|
+
if @duration.nil?
|
49
|
+
raise ArgumentError.new("You must specify the test duration.")
|
50
|
+
end
|
51
|
+
if @request_patterns_file.nil?
|
52
|
+
raise ArgumentError.new("You must specify the path to the request patterns JSON file.")
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def duration
|
57
|
+
[@duration, MIN_DURATION].max
|
58
|
+
end
|
59
|
+
|
60
|
+
def wait
|
61
|
+
[@wait, MIN_WAIT].max
|
62
|
+
end
|
63
|
+
|
64
|
+
def mode
|
65
|
+
@mode.to_sym
|
66
|
+
end
|
67
|
+
|
68
|
+
def request_patterns
|
69
|
+
@request_patterns ||= prepare_request_patterns
|
70
|
+
end
|
71
|
+
|
72
|
+
private
|
73
|
+
def prepare_request_patterns
|
74
|
+
request_patterns = File.read(@request_patterns_file)
|
75
|
+
request_patterns = JSON.parse(request_patterns)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
# Copyright (C) 2013-2014 Droonga Project
|
2
|
+
#
|
3
|
+
# This program is free software: you can redistribute it and/or modify
|
4
|
+
# it under the terms of the GNU General Public License as published by
|
5
|
+
# the Free Software Foundation, either version 3 of the License, or
|
6
|
+
# (at your option) any later version.
|
7
|
+
#
|
8
|
+
# This program is distributed in the hope that it will be useful,
|
9
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
10
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
11
|
+
# GNU General Public License for more details.
|
12
|
+
#
|
13
|
+
# You should have received a copy of the GNU General Public License
|
14
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
15
|
+
|
16
|
+
require "drnbench/request-response/runner"
|
17
|
+
require "csv"
|
18
|
+
|
19
|
+
module Drnbench
|
20
|
+
module RequestResponse
|
21
|
+
class GradualRunner
|
22
|
+
attr_reader :report_progressively, :result
|
23
|
+
|
24
|
+
def initialize(config)
|
25
|
+
@config = config
|
26
|
+
end
|
27
|
+
|
28
|
+
def run
|
29
|
+
run_benchmarks
|
30
|
+
@result
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
def run_benchmarks
|
35
|
+
@result = Result.new
|
36
|
+
@config.start_n_clients.step(@config.end_n_clients, @config.step) do |n_clients|
|
37
|
+
benchmark = Runner.new(n_clients, @config)
|
38
|
+
if @config.report_progressively
|
39
|
+
puts "Running benchmark with #{n_clients} clients..."
|
40
|
+
end
|
41
|
+
benchmark.run
|
42
|
+
if @config.report_progressively
|
43
|
+
puts benchmark.result.to_s
|
44
|
+
end
|
45
|
+
@result << benchmark.result
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
class Result
|
50
|
+
def initialize
|
51
|
+
@results = {}
|
52
|
+
end
|
53
|
+
|
54
|
+
def <<(result)
|
55
|
+
@statuses = nil
|
56
|
+
@results[result.n_clients] = result
|
57
|
+
end
|
58
|
+
|
59
|
+
def statuses
|
60
|
+
@statuses ||= prepare_statuses
|
61
|
+
end
|
62
|
+
|
63
|
+
def to_csv
|
64
|
+
([csv_header] + csv_body).collect do |row|
|
65
|
+
CSV.generate_line(row)
|
66
|
+
end.join("")
|
67
|
+
end
|
68
|
+
|
69
|
+
private
|
70
|
+
def prepare_statuses
|
71
|
+
statuses = []
|
72
|
+
@results.each do |n_clients, result|
|
73
|
+
statuses += result.statuses.keys
|
74
|
+
end
|
75
|
+
statuses.uniq!
|
76
|
+
statuses.sort!
|
77
|
+
statuses
|
78
|
+
end
|
79
|
+
|
80
|
+
def csv_header
|
81
|
+
Drnbench::Result.keys + statuses
|
82
|
+
end
|
83
|
+
|
84
|
+
def csv_body
|
85
|
+
@results.values.collect do |result|
|
86
|
+
result.values +
|
87
|
+
statuses.collect do |status|
|
88
|
+
result.status_percentages[status] || 0
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
@@ -0,0 +1,127 @@
|
|
1
|
+
# Copyright (C) 2013-2014 Droonga Project
|
2
|
+
#
|
3
|
+
# This program is free software: you can redistribute it and/or modify
|
4
|
+
# it under the terms of the GNU General Public License as published by
|
5
|
+
# the Free Software Foundation, either version 3 of the License, or
|
6
|
+
# (at your option) any later version.
|
7
|
+
#
|
8
|
+
# This program is distributed in the hope that it will be useful,
|
9
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
10
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
11
|
+
# GNU General Public License for more details.
|
12
|
+
#
|
13
|
+
# You should have received a copy of the GNU General Public License
|
14
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
15
|
+
|
16
|
+
module Drnbench
|
17
|
+
module RequestResponse
|
18
|
+
class Result
|
19
|
+
attr_reader :n_clients, :duration, :statuses
|
20
|
+
|
21
|
+
class << self
|
22
|
+
def keys
|
23
|
+
[
|
24
|
+
:n_clients,
|
25
|
+
:total_n_requests,
|
26
|
+
:queries_per_second,
|
27
|
+
:min_elapsed_time,
|
28
|
+
:max_elapsed_time,
|
29
|
+
:average_elapsed_time,
|
30
|
+
]
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def initialize(params)
|
35
|
+
@n_clients = params[:n_clients]
|
36
|
+
@duration = params[:duration]
|
37
|
+
|
38
|
+
@results = []
|
39
|
+
@total_elapsed_time = 0.0
|
40
|
+
@elapsed_times = []
|
41
|
+
@statuses = {}
|
42
|
+
end
|
43
|
+
|
44
|
+
def <<(result)
|
45
|
+
clear_cached_statistics
|
46
|
+
|
47
|
+
@results << result
|
48
|
+
|
49
|
+
@statuses[result[:status]] ||= 0
|
50
|
+
@statuses[result[:status]] += 1
|
51
|
+
|
52
|
+
@elapsed_times << result[:elapsed_time]
|
53
|
+
@total_elapsed_time += result[:elapsed_time]
|
54
|
+
end
|
55
|
+
|
56
|
+
def total_n_requests
|
57
|
+
@total_n_requests ||= @results.size
|
58
|
+
end
|
59
|
+
|
60
|
+
def queries_per_second
|
61
|
+
@queries_per_second ||= total_n_requests.to_f / @duration
|
62
|
+
end
|
63
|
+
|
64
|
+
def status_percentages
|
65
|
+
@status_percentages ||= prepare_status_percentages
|
66
|
+
end
|
67
|
+
|
68
|
+
def min_elapsed_time
|
69
|
+
@min_elapsed_time ||= @elapsed_times.min
|
70
|
+
end
|
71
|
+
|
72
|
+
def max_elapsed_time
|
73
|
+
@max_elapsed_time ||= @elapsed_times.min
|
74
|
+
end
|
75
|
+
|
76
|
+
def average_elapsed_time
|
77
|
+
@average_elapsed_time ||= @total_elapsed_time / @elapsed_times.size
|
78
|
+
end
|
79
|
+
|
80
|
+
def to_s
|
81
|
+
"Total requests: #{total_n_requests} " +
|
82
|
+
"(#{queries_per_second} queries per second)\n" +
|
83
|
+
"Status:\n" +
|
84
|
+
status_percentages.collect do |status, percentage|
|
85
|
+
" #{status}: #{percentage} %"
|
86
|
+
end.join("\n") + "\n" +
|
87
|
+
"Elapsed time:\n" +
|
88
|
+
" min: #{min_elapsed_time} sec\n" +
|
89
|
+
" max: #{max_elapsed_time} sec\n" +
|
90
|
+
" average: #{average_elapsed_time} sec"
|
91
|
+
end
|
92
|
+
|
93
|
+
def values
|
94
|
+
self.class.keys.collect do |column|
|
95
|
+
send(column)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
private
|
100
|
+
def clear_cached_statistics
|
101
|
+
@total_n_requests = nil
|
102
|
+
@queries_per_second = nil
|
103
|
+
@status_percentages = nil
|
104
|
+
@min_elapsed_time = nil
|
105
|
+
@max_elapsed_time = nil
|
106
|
+
@average_elapsed_time = nil
|
107
|
+
end
|
108
|
+
|
109
|
+
def prepare_status_percentages
|
110
|
+
status_percentages = []
|
111
|
+
@statuses.each do |status, n_results|
|
112
|
+
percentage = n_results.to_f / total_n_requests * 100
|
113
|
+
status_percentages << {:percentage => percentage,
|
114
|
+
:status => status}
|
115
|
+
end
|
116
|
+
status_percentages.sort! do |a, b|
|
117
|
+
(-1) * (a[:percentage] <=> b[:percentage])
|
118
|
+
end
|
119
|
+
status_percentages = {}
|
120
|
+
status_percentages.each do |status|
|
121
|
+
status_percentages[status[:status]] = status[:percentage]
|
122
|
+
end
|
123
|
+
status_percentages
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
@@ -0,0 +1,111 @@
|
|
1
|
+
# Copyright (C) 2013-2014 Droonga Project
|
2
|
+
#
|
3
|
+
# This program is free software: you can redistribute it and/or modify
|
4
|
+
# it under the terms of the GNU General Public License as published by
|
5
|
+
# the Free Software Foundation, either version 3 of the License, or
|
6
|
+
# (at your option) any later version.
|
7
|
+
#
|
8
|
+
# This program is distributed in the hope that it will be useful,
|
9
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
10
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
11
|
+
# GNU General Public License for more details.
|
12
|
+
#
|
13
|
+
# You should have received a copy of the GNU General Public License
|
14
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
15
|
+
|
16
|
+
require "drnbench/client/http"
|
17
|
+
require "drnbench/client/http-droonga"
|
18
|
+
require "drnbench/request-response/result"
|
19
|
+
|
20
|
+
module Drnbench
|
21
|
+
module RequestResponse
|
22
|
+
class Runner
|
23
|
+
attr_reader :n_clients, :result
|
24
|
+
|
25
|
+
def initialize(n_clients, config)
|
26
|
+
@n_clients = n_clients
|
27
|
+
@config = config
|
28
|
+
populate_requests
|
29
|
+
end
|
30
|
+
|
31
|
+
def run
|
32
|
+
process_requests
|
33
|
+
@result
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
def process_requests
|
38
|
+
requests_queue = Queue.new
|
39
|
+
@result = Result.new(:n_clients => @n_clients,
|
40
|
+
:duration => @config.duration)
|
41
|
+
|
42
|
+
client_params = {
|
43
|
+
:requests => requests_queue,
|
44
|
+
:result => @result,
|
45
|
+
}
|
46
|
+
@clients = @n_clients.times.collect do |index|
|
47
|
+
client = nil
|
48
|
+
case @config.mode
|
49
|
+
when :http
|
50
|
+
client = HttpClient.new(client_params, @config)
|
51
|
+
when :http_droonga
|
52
|
+
client = HttpDroongaClient.new(client_params, @config)
|
53
|
+
else
|
54
|
+
raise ArgumentError.new("Unknown mode: #{@config.mode}")
|
55
|
+
end
|
56
|
+
client.run
|
57
|
+
client
|
58
|
+
end
|
59
|
+
|
60
|
+
start_time = Time.now
|
61
|
+
while Time.now - start_time < @config.duration
|
62
|
+
if requests_queue.empty?
|
63
|
+
@requests.each do |request|
|
64
|
+
requests_queue.push(request)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
sleep 1
|
68
|
+
end
|
69
|
+
|
70
|
+
@clients.each do |client|
|
71
|
+
client.stop
|
72
|
+
end
|
73
|
+
|
74
|
+
@result
|
75
|
+
end
|
76
|
+
|
77
|
+
def populate_requests
|
78
|
+
@requests = []
|
79
|
+
|
80
|
+
if @config.request_patterns.is_a?(Array)
|
81
|
+
@config.request_patterns.each do |request_pattern|
|
82
|
+
populate_request_pattern(request_pattern)
|
83
|
+
end
|
84
|
+
else
|
85
|
+
@config.request_patterns.each do |key, request_pattern|
|
86
|
+
populate_request_pattern(request_pattern)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
@requests.shuffle!
|
91
|
+
end
|
92
|
+
|
93
|
+
def populate_request_pattern(request_pattern)
|
94
|
+
frequency = request_pattern["frequency"].to_f
|
95
|
+
n_requests = @config.n_requests * frequency
|
96
|
+
|
97
|
+
base_patterns = nil
|
98
|
+
if request_pattern["pattern"]
|
99
|
+
base_patterns = [request_pattern["pattern"]]
|
100
|
+
else
|
101
|
+
base_patterns = request_pattern["patterns"]
|
102
|
+
end
|
103
|
+
base_patterns = base_patterns.shuffle
|
104
|
+
|
105
|
+
n_requests.round.times do |count|
|
106
|
+
@requests << base_patterns[count % base_patterns.size]
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|