drnbench 1.0.2 → 1.0.3
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 +4 -4
- data/README.md +77 -34
- data/Rakefile +7 -0
- data/bin/drnbench-extract-searchterms +17 -5
- data/bin/drnbench-publish-subscribe +1 -1
- data/bin/drnbench-request-response +49 -22
- data/doc/text/news.md +9 -0
- data/drnbench.gemspec +1 -0
- data/lib/drnbench.rb +2 -2
- data/lib/drnbench/publish-subscribe/{gradual-runner.rb → progressive-runner.rb} +1 -1
- data/lib/drnbench/request-response/configuration.rb +29 -6
- data/lib/drnbench/request-response/{gradual-runner.rb → progressive-runner.rb} +1 -1
- data/lib/drnbench/request-response/request-pattern.rb +194 -0
- data/lib/drnbench/request-response/runner.rb +4 -39
- data/lib/drnbench/version.rb +1 -1
- data/test/request-response/test-request-pattern.rb +111 -0
- data/test/run-test.rb +42 -0
- metadata +42 -23
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3b10736f18dbeaf958fe4b9bd29c0a6788b69f3c
|
4
|
+
data.tar.gz: bc4363d42fd30a955a70f4d2241052486e121635
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c7d68baea7789ca3a4a803fa7a4b5d2ff3c24252a7cf49c89d0f33a5eb598f9c0f064dc64c6eb35251abe6a0de4fe910d554e562a5151e18eb00d43973e3ea37
|
7
|
+
data.tar.gz: 4dad01b34e627c89618827e67f2f070c6cdb229f208d65214361b76477b6d91b10f6be69bf62ed2695078356341714f64fd4007277e674a8f8f8f3a0f4d8f527
|
data/README.md
CHANGED
@@ -7,7 +7,7 @@ Drnbench is a benchmark tool for Droonga.
|
|
7
7
|
It may be used for other HTTP servers.
|
8
8
|
|
9
9
|
Drnbench provides features to send multiple random requests with different settings periodically.
|
10
|
-
Number of clients (requests) in each period will be automatically increased
|
10
|
+
Number of clients (requests) in each period will be automatically increased progressively.
|
11
11
|
So you'll be able to guess the limit performance of the throughput of a server, via the report like following:
|
12
12
|
|
13
13
|
n_clients,total_n_requests,queries_per_second,min_elapsed_time,max_elapsed_time,average_elapsed_time,200
|
@@ -35,39 +35,7 @@ In this scenario, you have to do:
|
|
35
35
|
|
36
36
|
Drnbench will start multiple clients and send many requests based on the patterns file.
|
37
37
|
|
38
|
-
1. Create a patterns file
|
39
|
-
|
40
|
-
{
|
41
|
-
"(pattern type 1 name)": {
|
42
|
-
"frequency": (appearance ratio in all requests),
|
43
|
-
"path": "(path to the endpoint)",
|
44
|
-
"method": "(HTTP method)",
|
45
|
-
"patterns": [
|
46
|
-
{ "body": (request body 1 sent by POST method) },
|
47
|
-
{ "body": (request body 2 sent by POST method) },
|
48
|
-
...
|
49
|
-
]
|
50
|
-
}
|
51
|
-
"(patterns type 2 name)": {
|
52
|
-
"frequency": (appearance ratio in all requests),
|
53
|
-
"patterns": [
|
54
|
-
{
|
55
|
-
"path": "(path to the endpoint 1)",
|
56
|
-
"method": "(HTTP method)",
|
57
|
-
"body": (request body 1 sent by POST method)
|
58
|
-
},
|
59
|
-
{
|
60
|
-
"path": "(path to the endpoint 2)",
|
61
|
-
"method": "(HTTP method)",
|
62
|
-
"body": (request body 2 sent by POST method)
|
63
|
-
},
|
64
|
-
...
|
65
|
-
]
|
66
|
-
},
|
67
|
-
...
|
68
|
-
}
|
69
|
-
|
70
|
-
For example, a file "patterns.json" like:
|
38
|
+
1. Create a patterns file "patterns.json", like:
|
71
39
|
|
72
40
|
{
|
73
41
|
"user search": {
|
@@ -108,6 +76,81 @@ Drnbench will start multiple clients and send many requests based on the pattern
|
|
108
76
|
|
109
77
|
4. You'll get a report.
|
110
78
|
|
79
|
+
#### Detailed format of the patterns file
|
80
|
+
|
81
|
+
##### Abstract
|
82
|
+
|
83
|
+
Abstract type A, grouped patterns with names:
|
84
|
+
|
85
|
+
~~~
|
86
|
+
{
|
87
|
+
"group name 1": (patterns group),
|
88
|
+
"group name 2": (patterns group),
|
89
|
+
...
|
90
|
+
}
|
91
|
+
~~~
|
92
|
+
|
93
|
+
Abstract type B, grouped patterns without names:
|
94
|
+
|
95
|
+
~~~
|
96
|
+
[
|
97
|
+
(patterns group),
|
98
|
+
(patterns group),
|
99
|
+
...
|
100
|
+
]
|
101
|
+
~~~
|
102
|
+
|
103
|
+
Abstract type C, non-grouped patterns:
|
104
|
+
|
105
|
+
~~~
|
106
|
+
(patterns group)
|
107
|
+
~~~
|
108
|
+
|
109
|
+
##### Patterns group
|
110
|
+
|
111
|
+
Patterns group type A, without options:
|
112
|
+
|
113
|
+
~~~
|
114
|
+
[
|
115
|
+
(pattern1),
|
116
|
+
(pattern2),
|
117
|
+
...
|
118
|
+
]
|
119
|
+
~~~
|
120
|
+
|
121
|
+
Patterns group type B, with options:
|
122
|
+
|
123
|
+
~~~
|
124
|
+
{
|
125
|
+
"frequency": (appearance ratio in all requests, optional),
|
126
|
+
"path": "(path to the endpoint, optional)",
|
127
|
+
"method": "(HTTP method, optional)",
|
128
|
+
"patterns": [
|
129
|
+
(pattern1),
|
130
|
+
(pattern2),
|
131
|
+
...
|
132
|
+
]
|
133
|
+
}
|
134
|
+
~~~
|
135
|
+
|
136
|
+
##### Pattern
|
137
|
+
|
138
|
+
Pattern type A, simple path string:
|
139
|
+
|
140
|
+
~~~
|
141
|
+
"/path/to/the/API/or/page"
|
142
|
+
~~~
|
143
|
+
|
144
|
+
Pattern B, detailed object:
|
145
|
+
|
146
|
+
~~~
|
147
|
+
{
|
148
|
+
"path": "/path/to/the/API/or/page",
|
149
|
+
"method": "(HTTP method)",
|
150
|
+
"body": (request body sent by POST method)
|
151
|
+
}
|
152
|
+
~~~
|
153
|
+
|
111
154
|
|
112
155
|
### Benchmarking of request-responsne style commands, with a Droonga-based search system
|
113
156
|
|
data/Rakefile
CHANGED
@@ -15,6 +15,8 @@
|
|
15
15
|
# You should have received a copy of the GNU General Public License
|
16
16
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
17
17
|
|
18
|
+
task :default => :test
|
19
|
+
|
18
20
|
require "bundler/gem_helper"
|
19
21
|
require "packnga"
|
20
22
|
|
@@ -31,3 +33,8 @@ Packnga::DocumentTask.new(spec) do |task|
|
|
31
33
|
task.original_language = "en"
|
32
34
|
task.translate_languages = ["ja"]
|
33
35
|
end
|
36
|
+
|
37
|
+
desc "Run tests"
|
38
|
+
task :test do
|
39
|
+
ruby("test/run-test.rb")
|
40
|
+
end
|
@@ -19,38 +19,50 @@ require "drnbench"
|
|
19
19
|
require "ostruct"
|
20
20
|
require "optparse"
|
21
21
|
require "json"
|
22
|
+
require "cgi"
|
22
23
|
|
23
24
|
options = OpenStruct.new
|
24
25
|
options.column_index = 0
|
26
|
+
options.escape = false
|
25
27
|
|
26
28
|
option_parser = OptionParser.new do |parser|
|
27
29
|
parser.version = Drnbench::VERSION
|
28
30
|
|
29
31
|
parser.on("--column-index=INDEX", Integer,
|
30
32
|
"Index number of the column to be extracted.",
|
31
|
-
"(#{options.
|
33
|
+
"(#{options.column_index})") do |index|
|
32
34
|
options.column_index = index
|
33
35
|
end
|
36
|
+
parser.on("--escape",
|
37
|
+
"Escape output for URL parameter") do
|
38
|
+
options.escape = true
|
39
|
+
end
|
34
40
|
end
|
35
41
|
|
36
42
|
groonga_select_result_files = option_parser.parse!(ARGV)
|
37
43
|
|
38
|
-
def
|
44
|
+
def escape_for_param(value)
|
45
|
+
CGI.escape(value.to_s)
|
46
|
+
end
|
47
|
+
|
48
|
+
def output_column_value(select_result, options)
|
39
49
|
select_result = JSON.parse(select_result)
|
40
50
|
body = select_result[1]
|
41
51
|
search_result = body.first
|
42
52
|
records = search_result[2..-1]
|
43
53
|
records.each do |record|
|
44
|
-
|
54
|
+
value = record[options.column_index]
|
55
|
+
value = escape_for_param(value) if options.escape
|
56
|
+
puts(value)
|
45
57
|
end
|
46
58
|
end
|
47
59
|
|
48
60
|
if groonga_select_result_files.empty?
|
49
|
-
output_column_value($stdin.read, options
|
61
|
+
output_column_value($stdin.read, options)
|
50
62
|
else
|
51
63
|
groonga_select_result_files.each do |select_result_file|
|
52
64
|
File.open(select_result_file) do |input|
|
53
|
-
output_column_value(input.read, options
|
65
|
+
output_column_value(input.read, options)
|
54
66
|
end
|
55
67
|
end
|
56
68
|
end
|
@@ -94,7 +94,7 @@ args = option_parser.parse!(ARGV)
|
|
94
94
|
|
95
95
|
config.validate
|
96
96
|
|
97
|
-
runner = Drnbench::PublishSubscribe::
|
97
|
+
runner = Drnbench::PublishSubscribe::ProgressiveRunner.new(config)
|
98
98
|
runner.run
|
99
99
|
|
100
100
|
File.open(config.output_path, "w") do |file|
|
@@ -23,70 +23,97 @@ config = Drnbench::RequestResponse::Configuration.new
|
|
23
23
|
option_parser = OptionParser.new do |parser|
|
24
24
|
parser.version = Drnbench::VERSION
|
25
25
|
|
26
|
+
parser.separator("")
|
26
27
|
parser.on("--duration=SECONDS", Float,
|
27
|
-
"
|
28
|
+
"Duration of each benchmark.",
|
29
|
+
"(#{config.duration})") do |duration|
|
28
30
|
config.duration = duration
|
29
31
|
end
|
30
32
|
parser.on("--wait=SECONDS", Float,
|
31
|
-
"
|
33
|
+
"Interval of each request sent by a client.",
|
34
|
+
"(#{config.wait})") do |wait|
|
32
35
|
config.wait = wait
|
33
36
|
end
|
37
|
+
parser.on("--n-slow-requests=N", Integer,
|
38
|
+
"Number of slow requests to be reported.",
|
39
|
+
"(#{config.n_slow_requests})") do |n_slow_requests|
|
40
|
+
config.n_slow_requests = n_slow_requests
|
41
|
+
end
|
42
|
+
|
43
|
+
parser.separator("")
|
44
|
+
parser.separator("Progressive benchmark:")
|
34
45
|
parser.on("--interval=SECONDS", Float,
|
35
|
-
"
|
46
|
+
"Interval between each benchmark.",
|
47
|
+
"(#{config.interval})") do |interval|
|
36
48
|
config.interval = interval
|
37
49
|
end
|
38
50
|
parser.on("--start-n-clients=N", Integer,
|
39
|
-
"
|
51
|
+
"Initial number of clients.",
|
52
|
+
"(#{config.start_n_clients})") do |n_clients|
|
40
53
|
config.start_n_clients = n_clients
|
41
54
|
end
|
42
55
|
parser.on("--end-n-clients=N", Integer,
|
43
|
-
"
|
56
|
+
"Maximum number of clients.",
|
57
|
+
"(#{config.end_n_clients})") do |n_clients|
|
44
58
|
config.end_n_clients = n_clients
|
45
59
|
end
|
46
60
|
parser.on("--step=COUNT", Integer,
|
47
|
-
"
|
61
|
+
"Number of clients increased on each",
|
62
|
+
"progress.",
|
63
|
+
"(#{config.step})") do |step|
|
48
64
|
config.step = step
|
49
65
|
end
|
50
|
-
parser.on("--n-slow-requests=N", Integer,
|
51
|
-
"number of reporting slow requests (optional)") do |n_slow_requests|
|
52
|
-
config.n_slow_requests = n_slow_requests
|
53
|
-
end
|
54
66
|
|
67
|
+
parser.separator("")
|
68
|
+
parser.separator("Request:")
|
55
69
|
parser.on("--mode=MODE", String,
|
56
|
-
"mode
|
70
|
+
"Request mode.",
|
57
71
|
"available modes:",
|
58
|
-
" http",
|
72
|
+
" http (default)",
|
59
73
|
" http-droonga-search") do |mode|
|
60
74
|
config.mode = mode.gsub(/-/, "_")
|
61
75
|
end
|
62
76
|
parser.on("--request-patterns-file=PATH",
|
63
|
-
"
|
77
|
+
"Path to request patterns file.") do |path|
|
64
78
|
config.request_patterns_file = File.expand_path(path)
|
65
79
|
end
|
66
80
|
|
67
|
-
parser.on("--default-
|
68
|
-
"
|
69
|
-
|
81
|
+
parser.on("--default-hosts=HOST1,HOST2,...", Array,
|
82
|
+
"Default host names for each request.",
|
83
|
+
"(#{config.default_hosts.join(",")})") do |hosts|
|
84
|
+
config.default_hosts = hosts
|
70
85
|
end
|
71
86
|
parser.on("--default-port=PORT", Integer,
|
72
|
-
"
|
87
|
+
"Default port number for each request.",
|
88
|
+
"(#{config.default_port})") do |port|
|
73
89
|
config.default_port = port
|
74
90
|
end
|
75
91
|
parser.on("--default-path=PATH", String,
|
76
|
-
"
|
92
|
+
"Default path for each request",
|
93
|
+
"(#{config.default_path})") do |path|
|
77
94
|
config.default_path = path
|
78
95
|
end
|
79
96
|
parser.on("--default-method=METHOD", String,
|
80
|
-
"
|
97
|
+
"Default HTTP method for each request.",
|
98
|
+
"(#{config.default_method})") do |method|
|
81
99
|
config.default_method = method
|
82
100
|
end
|
83
101
|
parser.on("--default-timeout=SECONDS", Float,
|
84
|
-
"
|
102
|
+
"Default timeout for each request.",
|
103
|
+
"(#{config.default_timeout})") do |timeout|
|
85
104
|
config.default_timeout = timeout
|
86
105
|
end
|
106
|
+
parser.on("--default-host=HOST1,HOST2,...", Array,
|
107
|
+
"An alias to \"--default-hosts\"",
|
108
|
+
"for backward compatibility.") do |hosts|
|
109
|
+
config.default_hosts = hosts
|
110
|
+
end
|
87
111
|
|
112
|
+
parser.separator("")
|
113
|
+
parser.separator("Output:")
|
88
114
|
parser.on("--output-path=PATH",
|
89
|
-
"
|
115
|
+
"Path to output statistics as a CSV file.",
|
116
|
+
"(#{config.output_path})") do |path|
|
90
117
|
config.output_path = File.expand_path(path)
|
91
118
|
end
|
92
119
|
end
|
@@ -94,7 +121,7 @@ args = option_parser.parse!(ARGV)
|
|
94
121
|
|
95
122
|
config.validate
|
96
123
|
|
97
|
-
runner = Drnbench::RequestResponse::
|
124
|
+
runner = Drnbench::RequestResponse::ProgressiveRunner.new(config)
|
98
125
|
runner.run
|
99
126
|
|
100
127
|
File.open(config.output_path, "w") do |file|
|
data/doc/text/news.md
CHANGED
@@ -1,5 +1,14 @@
|
|
1
1
|
# News
|
2
2
|
|
3
|
+
## 1.0.3: 2014-10-29 (planned)
|
4
|
+
|
5
|
+
* `drnbench-request-response`
|
6
|
+
* Accept multiple hosts as a comma separated list via the `--default-hosts` option.
|
7
|
+
You can simulate load balancing for multiple endpoints easily.
|
8
|
+
* Accept plain text file of a list of paths as the patterns file.
|
9
|
+
* `drnbench-extract-searchterms`
|
10
|
+
* New `--escape` option is introduced to escape URI-incompatible characters.
|
11
|
+
|
3
12
|
## 1.0.2: 2014-07-30
|
4
13
|
|
5
14
|
* 'drnbench-request-response'
|
data/drnbench.gemspec
CHANGED
data/lib/drnbench.rb
CHANGED
@@ -16,7 +16,7 @@
|
|
16
16
|
require "drnbench/version"
|
17
17
|
require "drnbench/request-response/configuration"
|
18
18
|
require "drnbench/request-response/runner"
|
19
|
-
require "drnbench/request-response/
|
19
|
+
require "drnbench/request-response/progressive-runner"
|
20
20
|
require "drnbench/publish-subscribe/configuration"
|
21
21
|
require "drnbench/publish-subscribe/runner"
|
22
|
-
require "drnbench/publish-subscribe/
|
22
|
+
require "drnbench/publish-subscribe/progressive-runner"
|
@@ -21,23 +21,25 @@ module Drnbench
|
|
21
21
|
attr_accessor :duration, :wait, :interval, :request_patterns_file
|
22
22
|
attr_accessor :start_n_clients, :end_n_clients, :step, :n_requests, :n_slow_requests
|
23
23
|
attr_accessor :mode
|
24
|
-
|
24
|
+
attr_reader :default_hosts
|
25
|
+
attr_accessor :default_port, :default_path, :default_method, :default_timeout
|
25
26
|
attr_accessor :report_progressively, :output_path
|
26
27
|
|
27
28
|
MIN_DURATION = 1
|
28
29
|
MIN_WAIT = 0
|
29
30
|
|
30
31
|
def initialize
|
31
|
-
@
|
32
|
-
@
|
33
|
-
@
|
32
|
+
@duration = 30
|
33
|
+
@wait = 0.01
|
34
|
+
@interval = 10
|
35
|
+
@start_n_clients = 0
|
34
36
|
@end_n_clients = 1
|
35
37
|
@step = 1
|
36
38
|
@n_requests = 1000
|
37
39
|
@mode = :http
|
38
40
|
@n_slow_requests = 5
|
39
41
|
|
40
|
-
@
|
42
|
+
@default_hosts = ["localhost"]
|
41
43
|
@default_port = 80
|
42
44
|
@default_path = "/"
|
43
45
|
@default_method = "GET"
|
@@ -45,6 +47,8 @@ module Drnbench
|
|
45
47
|
|
46
48
|
@report_progressively = true
|
47
49
|
@output_path = "/tmp/drnbench-result.csv"
|
50
|
+
|
51
|
+
@last_default_host_index = 0
|
48
52
|
end
|
49
53
|
|
50
54
|
def validate
|
@@ -72,10 +76,29 @@ module Drnbench
|
|
72
76
|
@request_patterns ||= prepare_request_patterns
|
73
77
|
end
|
74
78
|
|
79
|
+
def default_hosts=(hosts)
|
80
|
+
@last_default_host_index = 0
|
81
|
+
@default_hosts = hosts
|
82
|
+
end
|
83
|
+
|
84
|
+
def default_host
|
85
|
+
host = @default_hosts[@last_default_host_index]
|
86
|
+
@last_default_host_index += 1
|
87
|
+
if @last_default_host_index == @default_hosts.size
|
88
|
+
@last_default_host_index = 0
|
89
|
+
end
|
90
|
+
host
|
91
|
+
end
|
92
|
+
|
75
93
|
private
|
76
94
|
def prepare_request_patterns
|
77
95
|
request_patterns = File.read(@request_patterns_file)
|
78
|
-
|
96
|
+
begin
|
97
|
+
request_patterns = JSON.parse(request_patterns)
|
98
|
+
rescue JSON::ParserError
|
99
|
+
# it's a simple text file, list of paths
|
100
|
+
request_patterns = request_patterns.strip.split(/\r?\n/)
|
101
|
+
end
|
79
102
|
end
|
80
103
|
end
|
81
104
|
end
|
@@ -0,0 +1,194 @@
|
|
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
|
+
module RequestPattern
|
19
|
+
class Abstract
|
20
|
+
def initialize(source, config)
|
21
|
+
@source = source
|
22
|
+
@config = config
|
23
|
+
end
|
24
|
+
|
25
|
+
def groups
|
26
|
+
@groups ||= prepare_groups
|
27
|
+
end
|
28
|
+
|
29
|
+
def default_group_frequency
|
30
|
+
1.0 / groups.size
|
31
|
+
end
|
32
|
+
|
33
|
+
def requests
|
34
|
+
@requests ||= populate_requests
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
def prepare_groups
|
39
|
+
if @source.is_a?(Array)
|
40
|
+
if PatternsGroup.valid_source?(@source.first)
|
41
|
+
return @source.collect do |group|
|
42
|
+
PatternsGroup.new(group, self)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
if PatternsGroup.valid_source?(@source)
|
46
|
+
return [
|
47
|
+
PatternsGroup.new(@source, self),
|
48
|
+
]
|
49
|
+
end
|
50
|
+
elsif @source.is_a?(Hash)
|
51
|
+
if PatternsGroup.valid_source?(@source)
|
52
|
+
return [
|
53
|
+
PatternsGroup.new(@source, self),
|
54
|
+
]
|
55
|
+
end
|
56
|
+
return @source.values.collect do |group|
|
57
|
+
PatternsGroup.new(group, self)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
[]
|
62
|
+
end
|
63
|
+
|
64
|
+
def populate_requests
|
65
|
+
requests = []
|
66
|
+
groups.each do |group|
|
67
|
+
n_requests = @config.n_requests * @config.end_n_clients * group.frequency
|
68
|
+
base_patterns = group.patterns.shuffle
|
69
|
+
n_requests.round.times do |count|
|
70
|
+
pattern = base_patterns[count % base_patterns.size]
|
71
|
+
requests << pattern.to_request
|
72
|
+
end
|
73
|
+
end
|
74
|
+
requests
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
class PatternsGroup
|
79
|
+
class << self
|
80
|
+
def valid_source?(source)
|
81
|
+
if source.is_a?(Array)
|
82
|
+
return Pattern.valid_source?(source.first)
|
83
|
+
end
|
84
|
+
if source.is_a?(Hash)
|
85
|
+
return source.has_key?("patterns")
|
86
|
+
end
|
87
|
+
false
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
attr_reader :abstract
|
92
|
+
|
93
|
+
def initialize(source, abstract)
|
94
|
+
@source = source
|
95
|
+
@abstract = abstract
|
96
|
+
end
|
97
|
+
|
98
|
+
def frequency
|
99
|
+
if @source.is_a?(Hash) and @source.has_key?("frequency")
|
100
|
+
return @source["frequency"].to_f
|
101
|
+
end
|
102
|
+
@abstract.default_group_frequency
|
103
|
+
end
|
104
|
+
|
105
|
+
def host
|
106
|
+
return nil unless @source.is_a?(Hash)
|
107
|
+
@source["host"]
|
108
|
+
end
|
109
|
+
|
110
|
+
def port
|
111
|
+
return nil unless @source.is_a?(Hash)
|
112
|
+
@source["port"]
|
113
|
+
end
|
114
|
+
|
115
|
+
def method
|
116
|
+
return nil unless @source.is_a?(Hash)
|
117
|
+
@source["method"]
|
118
|
+
end
|
119
|
+
|
120
|
+
def timeout
|
121
|
+
return nil unless @source.is_a?(Hash)
|
122
|
+
@source["timeout"]
|
123
|
+
end
|
124
|
+
|
125
|
+
def patterns
|
126
|
+
@patterns ||= prepare_patterns
|
127
|
+
end
|
128
|
+
|
129
|
+
private
|
130
|
+
def prepare_patterns
|
131
|
+
if @source.is_a?(Hash)
|
132
|
+
if @source.has_key?("pattern")
|
133
|
+
return [
|
134
|
+
Pattern.new(@source["pattern"], self),
|
135
|
+
]
|
136
|
+
else
|
137
|
+
return @source["patterns"].collect do |pattern|
|
138
|
+
Pattern.new(pattern, self)
|
139
|
+
end
|
140
|
+
end
|
141
|
+
elsif @source.is_a?(Array)
|
142
|
+
return @source.collect do |pattern|
|
143
|
+
Pattern.new(pattern, self)
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
class Pattern
|
150
|
+
class << self
|
151
|
+
def valid_source?(source)
|
152
|
+
return true if source.is_a?(String)
|
153
|
+
return false if source.is_a?(Array)
|
154
|
+
return !source.has_key?("patterns") if source.is_a?(Hash)
|
155
|
+
false
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
attr_reader :source, :group
|
160
|
+
|
161
|
+
def initialize(source, group)
|
162
|
+
@source = source
|
163
|
+
@group = group
|
164
|
+
end
|
165
|
+
|
166
|
+
def path
|
167
|
+
if @source.is_a?(String)
|
168
|
+
@source
|
169
|
+
else
|
170
|
+
@source["path"]
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
def to_request
|
175
|
+
@populated ||= populate
|
176
|
+
end
|
177
|
+
|
178
|
+
private
|
179
|
+
def populate
|
180
|
+
if @source.is_a?(String)
|
181
|
+
request = { "path" => @source }
|
182
|
+
else
|
183
|
+
request = @source
|
184
|
+
end
|
185
|
+
request["host"] ||= @group.host
|
186
|
+
request["port"] ||= @group.port
|
187
|
+
request["method"] ||= @group.method
|
188
|
+
request["timeout"] ||= @group.timeout
|
189
|
+
request
|
190
|
+
end
|
191
|
+
end
|
192
|
+
end
|
193
|
+
end
|
194
|
+
end
|
@@ -16,6 +16,7 @@
|
|
16
16
|
require "drnbench/client/http"
|
17
17
|
require "drnbench/client/http-droonga"
|
18
18
|
require "drnbench/request-response/result"
|
19
|
+
require "drnbench/request-response/request-pattern"
|
19
20
|
|
20
21
|
module Drnbench
|
21
22
|
module RequestResponse
|
@@ -26,7 +27,9 @@ module Drnbench
|
|
26
27
|
n_clients = 1 if n_clients.zero?
|
27
28
|
@n_clients = n_clients
|
28
29
|
@config = config
|
29
|
-
|
30
|
+
|
31
|
+
abstract = RequestPattern::Abstract.new(@config.request_patterns, @config)
|
32
|
+
@requests = abstract.requests.shuffle
|
30
33
|
end
|
31
34
|
|
32
35
|
def run
|
@@ -79,44 +82,6 @@ module Drnbench
|
|
79
82
|
|
80
83
|
@result
|
81
84
|
end
|
82
|
-
|
83
|
-
def populate_requests
|
84
|
-
@requests = []
|
85
|
-
|
86
|
-
if @config.request_patterns.is_a?(Array)
|
87
|
-
@config.request_patterns.each do |request_pattern|
|
88
|
-
populate_request_pattern(request_pattern)
|
89
|
-
end
|
90
|
-
else
|
91
|
-
@config.request_patterns.each do |key, request_pattern|
|
92
|
-
populate_request_pattern(request_pattern)
|
93
|
-
end
|
94
|
-
end
|
95
|
-
|
96
|
-
@requests.shuffle!
|
97
|
-
end
|
98
|
-
|
99
|
-
def populate_request_pattern(request_pattern)
|
100
|
-
frequency = request_pattern["frequency"].to_f
|
101
|
-
n_requests = @config.n_requests * @config.end_n_clients * frequency
|
102
|
-
|
103
|
-
base_patterns = nil
|
104
|
-
if request_pattern["pattern"]
|
105
|
-
base_patterns = [request_pattern["pattern"]]
|
106
|
-
else
|
107
|
-
base_patterns = request_pattern["patterns"]
|
108
|
-
end
|
109
|
-
base_patterns = base_patterns.shuffle
|
110
|
-
|
111
|
-
n_requests.round.times do |count|
|
112
|
-
pattern = base_patterns[count % base_patterns.size]
|
113
|
-
pattern["host"] ||= request_pattern["host"]
|
114
|
-
pattern["port"] ||= request_pattern["port"]
|
115
|
-
pattern["method"] ||= request_pattern["method"]
|
116
|
-
pattern["timeout"] ||= request_pattern["timeout"]
|
117
|
-
@requests << pattern
|
118
|
-
end
|
119
|
-
end
|
120
85
|
end
|
121
86
|
end
|
122
87
|
end
|
data/lib/drnbench/version.rb
CHANGED
@@ -0,0 +1,111 @@
|
|
1
|
+
# Copyright (C) 2014 Droonga Project
|
2
|
+
#
|
3
|
+
# This library is free software; you can redistribute it and/or
|
4
|
+
# modify it under the terms of the GNU Lesser General Public
|
5
|
+
# License version 2.1 as published by the Free Software Foundation.
|
6
|
+
#
|
7
|
+
# This library is distributed in the hope that it will be useful,
|
8
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
9
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
10
|
+
# Lesser General Public License for more details.
|
11
|
+
#
|
12
|
+
# You should have received a copy of the GNU Lesser General Public
|
13
|
+
# License along with this library; if not, write to the Free Software
|
14
|
+
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
15
|
+
|
16
|
+
require "ostruct"
|
17
|
+
require "drnbench/request-response/request-pattern"
|
18
|
+
|
19
|
+
module Drnbench::RequestResponse::RequestPattern
|
20
|
+
class RequestResponsePatternsTest < Test::Unit::TestCase
|
21
|
+
CONFIG = OpenStruct.new
|
22
|
+
CONFIG.n_requests = 1
|
23
|
+
CONFIG.end_n_clients = 1
|
24
|
+
|
25
|
+
PATH_STRING = "/path/to/endpoint"
|
26
|
+
PATTERN_HASH = { "path" => PATH_STRING }
|
27
|
+
|
28
|
+
class PatternTest < self
|
29
|
+
data("path string" => PATH_STRING,
|
30
|
+
"hash" => PATTERN_HASH)
|
31
|
+
def test_validation(source)
|
32
|
+
Pattern.valid_source?(source)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
class PatternsGroupTest < self
|
37
|
+
data("path string array" => [
|
38
|
+
PATH_STRING,
|
39
|
+
],
|
40
|
+
"hash array" => [
|
41
|
+
PATTERN_HASH,
|
42
|
+
],
|
43
|
+
"hash with path string array" => {
|
44
|
+
"patterns" => [
|
45
|
+
PATH_STRING,
|
46
|
+
],
|
47
|
+
},
|
48
|
+
"hash with hash array" => {
|
49
|
+
"patterns" => [
|
50
|
+
PATTERN_HASH,
|
51
|
+
],
|
52
|
+
})
|
53
|
+
def test_validation(source)
|
54
|
+
PatternsGroup.valid_source?(source)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
class AbstractTest < self
|
59
|
+
data("path string array" => [
|
60
|
+
PATH_STRING,
|
61
|
+
],
|
62
|
+
"pattern hash array" => [
|
63
|
+
PATTERN_HASH,
|
64
|
+
],
|
65
|
+
|
66
|
+
"group, hash with path string array" => {
|
67
|
+
"patterns" => [
|
68
|
+
PATH_STRING,
|
69
|
+
],
|
70
|
+
},
|
71
|
+
"group, hash with hash array" => {
|
72
|
+
"patterns" => [
|
73
|
+
PATTERN_HASH,
|
74
|
+
],
|
75
|
+
},
|
76
|
+
|
77
|
+
"array of groups, path string array" => [
|
78
|
+
[PATH_STRING],
|
79
|
+
],
|
80
|
+
"array of groups, hash array" => [
|
81
|
+
[PATTERN_HASH],
|
82
|
+
],
|
83
|
+
"array of groups, hash with path string array" => [
|
84
|
+
{ "patterns" => [PATH_STRING] },
|
85
|
+
],
|
86
|
+
"array of groups, hash with hash array" => [
|
87
|
+
{ "patterns" => [PATTERN_HASH] },
|
88
|
+
],
|
89
|
+
|
90
|
+
"named groups, path string array" => {
|
91
|
+
"group" => [PATH_STRING],
|
92
|
+
},
|
93
|
+
"named groups, hash array" => {
|
94
|
+
"group" => [PATTERN_HASH],
|
95
|
+
},
|
96
|
+
"named groups, hash with path string array" => {
|
97
|
+
"group" => { "patterns" => [PATH_STRING] },
|
98
|
+
},
|
99
|
+
"named groups, hash with hash array" => {
|
100
|
+
"group" => { "patterns" => [PATTERN_HASH] },
|
101
|
+
})
|
102
|
+
def test_parse(source)
|
103
|
+
abstract = Abstract.new(source, CONFIG)
|
104
|
+
assert_equal(PATH_STRING,
|
105
|
+
abstract.groups.first.patterns.first.path)
|
106
|
+
assert_equal(1.0,
|
107
|
+
abstract.groups.first.frequency)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
data/test/run-test.rb
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# -*- coding: utf-8 -*-
|
3
|
+
#
|
4
|
+
# Copyright (C) 2013 Droonga Project
|
5
|
+
#
|
6
|
+
# This library is free software; you can redistribute it and/or
|
7
|
+
# modify it under the terms of the GNU Lesser General Public
|
8
|
+
# License version 2.1 as published by the Free Software Foundation.
|
9
|
+
#
|
10
|
+
# This library is distributed in the hope that it will be useful,
|
11
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
12
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
13
|
+
# Lesser General Public License for more details.
|
14
|
+
#
|
15
|
+
# You should have received a copy of the GNU Lesser General Public
|
16
|
+
# License along with this library; if not, write to the Free Software
|
17
|
+
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
18
|
+
|
19
|
+
require "pathname"
|
20
|
+
|
21
|
+
require "rubygems"
|
22
|
+
require "bundler"
|
23
|
+
begin
|
24
|
+
Bundler.setup(:default, :development)
|
25
|
+
rescue Bundler::BundlerError => e
|
26
|
+
$stderr.puts e.message
|
27
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
28
|
+
exit e.status_code
|
29
|
+
end
|
30
|
+
|
31
|
+
require "test-unit"
|
32
|
+
|
33
|
+
base_dir = File.expand_path(File.join(File.dirname(__FILE__), ".."))
|
34
|
+
lib_dir = File.join(base_dir, "lib")
|
35
|
+
test_dir = File.join(base_dir, "test")
|
36
|
+
|
37
|
+
$LOAD_PATH.unshift(lib_dir)
|
38
|
+
$LOAD_PATH.unshift(test_dir)
|
39
|
+
|
40
|
+
ARGV.unshift("--max-diff-target-string-size=10000")
|
41
|
+
|
42
|
+
exit Test::Unit::AutoRunner.run(true, test_dir)
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: drnbench
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.0.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- YUKI Hiroshi
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2014-07
|
12
|
+
date: 2014-10-07 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: json
|
@@ -109,6 +109,20 @@ dependencies:
|
|
109
109
|
- - '>='
|
110
110
|
- !ruby/object:Gem::Version
|
111
111
|
version: '0'
|
112
|
+
- !ruby/object:Gem::Dependency
|
113
|
+
name: test-unit
|
114
|
+
requirement: !ruby/object:Gem::Requirement
|
115
|
+
requirements:
|
116
|
+
- - '>='
|
117
|
+
- !ruby/object:Gem::Version
|
118
|
+
version: '0'
|
119
|
+
type: :development
|
120
|
+
prerelease: false
|
121
|
+
version_requirements: !ruby/object:Gem::Requirement
|
122
|
+
requirements:
|
123
|
+
- - '>='
|
124
|
+
- !ruby/object:Gem::Version
|
125
|
+
version: '0'
|
112
126
|
description: It may be used for other HTTP servers.
|
113
127
|
email:
|
114
128
|
- yuki@clear-code.com
|
@@ -121,33 +135,36 @@ executables:
|
|
121
135
|
extensions: []
|
122
136
|
extra_rdoc_files: []
|
123
137
|
files:
|
124
|
-
- README.md
|
125
|
-
- Rakefile
|
126
138
|
- Gemfile
|
127
|
-
- drnbench.gemspec
|
128
139
|
- LICENSE.txt
|
140
|
+
- README.md
|
141
|
+
- Rakefile
|
142
|
+
- bin/drnbench-extract-searchterms
|
143
|
+
- bin/drnbench-generate-select-patterns
|
144
|
+
- bin/drnbench-publish-subscribe
|
145
|
+
- bin/drnbench-request-response
|
129
146
|
- doc/text/news.md
|
130
|
-
-
|
147
|
+
- drnbench.gemspec
|
148
|
+
- lib/drnbench.rb
|
149
|
+
- lib/drnbench/chart/gnuplot.rb
|
131
150
|
- lib/drnbench/client/http-droonga.rb
|
132
|
-
- lib/drnbench/
|
151
|
+
- lib/drnbench/client/http.rb
|
152
|
+
- lib/drnbench/publish-subscribe/configuration.rb
|
153
|
+
- lib/drnbench/publish-subscribe/progressive-runner.rb
|
154
|
+
- lib/drnbench/publish-subscribe/runner.rb
|
155
|
+
- lib/drnbench/publish-subscribe/watch.rb
|
156
|
+
- lib/drnbench/reporters/throughput-reporter.rb
|
133
157
|
- lib/drnbench/request-response/configuration.rb
|
158
|
+
- lib/drnbench/request-response/progressive-runner.rb
|
159
|
+
- lib/drnbench/request-response/request-pattern.rb
|
134
160
|
- lib/drnbench/request-response/result.rb
|
135
161
|
- lib/drnbench/request-response/runner.rb
|
136
|
-
- lib/drnbench/reporters/throughput-reporter.rb
|
137
|
-
- lib/drnbench/version.rb
|
138
|
-
- lib/drnbench/chart/gnuplot.rb
|
139
|
-
- lib/drnbench/publish-subscribe/watch.rb
|
140
|
-
- lib/drnbench/publish-subscribe/gradual-runner.rb
|
141
|
-
- lib/drnbench/publish-subscribe/configuration.rb
|
142
|
-
- lib/drnbench/publish-subscribe/runner.rb
|
143
|
-
- lib/drnbench/server/protocol-adapter.rb
|
144
|
-
- lib/drnbench/server/engine.rb
|
145
162
|
- lib/drnbench/server/configuration.rb
|
146
|
-
- lib/drnbench.rb
|
147
|
-
-
|
148
|
-
-
|
149
|
-
-
|
150
|
-
-
|
163
|
+
- lib/drnbench/server/engine.rb
|
164
|
+
- lib/drnbench/server/protocol-adapter.rb
|
165
|
+
- lib/drnbench/version.rb
|
166
|
+
- test/request-response/test-request-pattern.rb
|
167
|
+
- test/run-test.rb
|
151
168
|
homepage: https://github.com/groonga/grntest
|
152
169
|
licenses:
|
153
170
|
- GPLv3 or later
|
@@ -168,9 +185,11 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
168
185
|
version: '0'
|
169
186
|
requirements: []
|
170
187
|
rubyforge_project:
|
171
|
-
rubygems_version: 2.
|
188
|
+
rubygems_version: 2.4.1
|
172
189
|
signing_key:
|
173
190
|
specification_version: 4
|
174
191
|
summary: Drnbench is a benchmark tool for Droonga.
|
175
|
-
test_files:
|
192
|
+
test_files:
|
193
|
+
- test/run-test.rb
|
194
|
+
- test/request-response/test-request-pattern.rb
|
176
195
|
has_rdoc:
|