drnbench 1.0.0
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.
- 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
|