drnbench 1.0.0

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