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.
@@ -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