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,5 @@
1
+ # News
2
+
3
+ ## 1.0.0: 2014-01-29
4
+
5
+ The first release!!!
@@ -0,0 +1,59 @@
1
+ # -*- mode: ruby; coding: utf-8 -*-
2
+ #
3
+ # Copyright (C) 2013-2014 Droonga Project
4
+ #
5
+ # This program is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU General Public License as published by
7
+ # the Free Software Foundation, either version 3 of the License, or
8
+ # (at your option) any later version.
9
+ #
10
+ # This program 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
13
+ # GNU General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU General Public License
16
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+
18
+ clean_white_space = lambda do |entry|
19
+ entry.gsub(/(\A\n+|\n+\z)/, '') + "\n"
20
+ end
21
+
22
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), "lib"))
23
+ require "drnbench/version"
24
+
25
+ Gem::Specification.new do |spec|
26
+ spec.name = "drnbench"
27
+ spec.version = Drnbench::VERSION
28
+ spec.homepage = "https://github.com/groonga/grntest"
29
+ spec.authors = ["YUKI Hiroshi", "Kouhei Sutou"]
30
+ spec.email = ["yuki@clear-code.com", "kou@clear-code.com"]
31
+ readme = File.read("README.md")
32
+ readme.force_encoding("UTF-8") if readme.respond_to?(:force_encoding)
33
+ entries = readme.split(/^\#\#\s(.*)$/)
34
+ description = clean_white_space.call(entries[entries.index("Description") + 1])
35
+ spec.summary, spec.description, = description.split(/\n\n+/, 3)
36
+ spec.license = "GPLv3 or later"
37
+ spec.files = [
38
+ "README.md",
39
+ "Rakefile",
40
+ "Gemfile",
41
+ "#{spec.name}.gemspec",
42
+ "LICENSE.txt",
43
+ ]
44
+ spec.files += Dir.glob("doc/text/**/*")
45
+ spec.files += Dir.glob("lib/**/*.rb")
46
+ spec.test_files += Dir.glob("test/**/*")
47
+ Dir.chdir("bin") do
48
+ spec.executables = Dir.glob("*")
49
+ end
50
+
51
+ spec.add_runtime_dependency("json")
52
+ spec.add_runtime_dependency("droonga-client")
53
+ spec.add_runtime_dependency("drntest")
54
+
55
+ spec.add_development_dependency("bundler")
56
+ spec.add_development_dependency("rake")
57
+ spec.add_development_dependency("packnga")
58
+ spec.add_development_dependency("kramdown")
59
+ end
@@ -0,0 +1,22 @@
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/version"
17
+ require "drnbench/request-response/configuration"
18
+ require "drnbench/request-response/runner"
19
+ require "drnbench/request-response/gradual-runner"
20
+ require "drnbench/publish-subscribe/configuration"
21
+ require "drnbench/publish-subscribe/runner"
22
+ require "drnbench/publish-subscribe/gradual-runner"
@@ -0,0 +1,37 @@
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
+ class HttpDroongaClient < HttpClient
18
+ DEFAULT_PATH_BASE = "/droonga"
19
+ DEFAULT_COMMAND = "search"
20
+ DEFAULT_METHOD = "POST"
21
+
22
+ def initialize(params, config)
23
+ super
24
+ @command = params["command"] || DEFAULT_COMMAND
25
+ end
26
+
27
+ private
28
+ def fixup_request(request)
29
+ request = {
30
+ "body" => request,
31
+ }
32
+ request["path"] ||= "#{DEFAULT_PATH_BASE}/#{@command}"
33
+ request["method"] ||= DEFAULT_METHOD
34
+ super(request)
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,68 @@
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 "thread"
17
+ require "droonga/client"
18
+ require "json"
19
+
20
+ module Drnbench
21
+ class HttpClient
22
+ attr_reader :requests, :results, :wait
23
+
24
+ def initialize(params, config)
25
+ @requests = params[:requests]
26
+ @result = params[:result]
27
+ @config = config
28
+ end
29
+
30
+ def run
31
+ @thread = Thread.new do
32
+ loop do
33
+ request = @requests.pop
34
+ request = fixup_request(request)
35
+
36
+ client = Droonga::Client.new(:protocol => :http,
37
+ :host => request["host"],
38
+ :port => request["port"])
39
+ request["headers"] ||= {}
40
+ request["headers"]["user-agent"] = "Ruby/#{RUBY_VERSION} Droonga::Benchmark::Runner::HttpClient"
41
+ start_time = Time.now
42
+ response = client.request(request)
43
+ @result << {
44
+ :request => request,
45
+ :status => response.code,
46
+ :elapsed_time => Time.now - start_time,
47
+ }
48
+ sleep @config.wait
49
+ end
50
+ end
51
+ self
52
+ end
53
+
54
+ def stop
55
+ @thread.exit
56
+ end
57
+
58
+ private
59
+ def fixup_request(request)
60
+ request["host"] ||= @config.default_host
61
+ request["port"] ||= @config.default_port
62
+ request["path"] ||= @config.default_path
63
+ request["method"] ||= @config.default_method
64
+ request["method"] = request["method"].upcase
65
+ request
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,76 @@
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/server/configuration"
17
+
18
+ module Drnbench
19
+ module PublishSubscribe
20
+ class Configuration
21
+ attr_accessor :start_n_subscribers, :n_publishings, :n_steps, :timeout
22
+ attr_accessor :subscribe_request_file, :feed_file, :engine, :protocol_adapter
23
+ attr_accessor :report_progressively, :output_path
24
+
25
+ def initialize
26
+ @start_n_subscribers = 1000
27
+ @n_publishings = 1000
28
+ @n_steps = 10
29
+ @timeout = 10
30
+
31
+ @report_progressively = true
32
+ @output_path = "/tmp/drnbench-pubsub-result.csv"
33
+
34
+ @engine = Server::EngineConfiguration.new
35
+ @protocol_adapter = Server::ProtocolAdapterConfiguration.new
36
+ @protocol_adapter.engine = @engine
37
+ end
38
+
39
+ def validate
40
+ if @subscribe_request_file.nil?
41
+ raise ArgumentError.new("You must specify a JSON file for a message pattern to subscribe.")
42
+ end
43
+ if @feed_file.nil?
44
+ raise ArgumentError.new("You must specify a JSON file for a message pattern to feed.")
45
+ end
46
+ end
47
+
48
+ def subscribe_request
49
+ @subscribe_request ||= prepare_subscribe_request
50
+ end
51
+
52
+ def new_subscribe_request
53
+ Marshal.load(Marshal.dump(subscribe_request))
54
+ end
55
+
56
+ def feed
57
+ @feed ||= prepare_feed
58
+ end
59
+
60
+ def new_feed
61
+ Marshal.load(Marshal.dump(feed))
62
+ end
63
+
64
+ private
65
+ def prepare_subscribe_request
66
+ subscribe_request_file = Pathname(@subscribe_request_file).expand_path(Dir.pwd)
67
+ JSON.parse(subscribe_request_file.read)
68
+ end
69
+
70
+ def prepare_feed
71
+ feed_file = Pathname(@feed_file).expand_path(Dir.pwd)
72
+ JSON.parse(feed_file.read)
73
+ end
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,66 @@
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 "benchmark"
17
+ require "csv"
18
+
19
+ module Drnbench
20
+ module PublishSubscribe
21
+ class GradualRunner
22
+ attr_reader :total_results
23
+
24
+ def initialize(config)
25
+ @config = config
26
+ @runner = Runner.new(@config)
27
+ end
28
+
29
+ def run
30
+ results = []
31
+ @runner.setup
32
+ begin
33
+ @config.n_steps.times do
34
+ run_once(results)
35
+ end
36
+ ensure
37
+ @runner.teardown
38
+ end
39
+ @total_results = [
40
+ ["case", "qps"],
41
+ ]
42
+ @total_results += results
43
+ end
44
+
45
+ private
46
+ def run_once(results)
47
+ @runner.increase_subscribers
48
+ label = "#{@runner.n_subscribers} subscribers"
49
+ GC.disable
50
+ result = Benchmark.bm do |benchmark|
51
+ benchmark.report(label) do
52
+ @runner.run
53
+ end
54
+ end
55
+ GC.enable
56
+ GC.start
57
+ result = result.join("").strip.gsub(/[()]/, "").split(/\s+/)
58
+ qps = @config.n_publishings.to_f / result.last.to_f
59
+ if @config.report_progressively
60
+ puts " (#{qps} queries per second)"
61
+ end
62
+ results << [label, qps]
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,136 @@
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 "pathname"
17
+ require "droonga/client"
18
+ require "drnbench/server/engine"
19
+ require "drnbench/server/protocol-adapter"
20
+
21
+ module Drnbench
22
+ module PublishSubscribe
23
+ class Runner
24
+ def initialize(config)
25
+ @config = config
26
+ @published_messages = Queue.new
27
+ end
28
+
29
+ def n_subscribers
30
+ @subscribers.size
31
+ end
32
+
33
+ def setup
34
+ setup_server
35
+ setup_subscribers
36
+ end
37
+
38
+ def teardown
39
+ teardown_subscribers
40
+ teardown_server
41
+ end
42
+
43
+ def run
44
+ publishing_times = @config.n_publishings
45
+ n_will_be_published_messages = @subscribers.size * publishing_times
46
+
47
+ do_feed(publishing_times)
48
+ receive_messages(n_will_be_published_messages)
49
+ end
50
+
51
+ def increase_subscribers
52
+ if @subscribers.empty?
53
+ new_n_subscribers = @config.start_n_subscribers
54
+ else
55
+ new_n_subscribers = @subscribers.size
56
+ end
57
+ add_subscribers(new_n_subscribers)
58
+ ensure_subscribers_ready
59
+ end
60
+
61
+ private
62
+ def setup_server
63
+ @engine = Engine.new(@config.engine)
64
+ @engine.start
65
+
66
+ @protocol_adapter = ProtocolAdapter.new(@config.protocol_adapter)
67
+ @protocol_adapter.start
68
+ end
69
+
70
+ def teardown_server
71
+ @protocol_adapter.stop
72
+ @engine.stop
73
+ end
74
+
75
+ def setup_subscribers
76
+ @subscribers = []
77
+ end
78
+
79
+ def teardown_subscribers
80
+ @subscribers.each do |subscriber|
81
+ subscriber.close
82
+ end
83
+ end
84
+
85
+ def add_subscribers(n_subscribers)
86
+ n_subscribers.times do
87
+ message = @config.new_subscribe_request
88
+ client = Droonga::Client.new(:protocol => :http,
89
+ :host => @config.protocol_adapter.host,
90
+ :port => @config.protocol_adapter.port)
91
+ client.subscribe(message) do |published_message|
92
+ @published_messages.push(published_message)
93
+ end
94
+ @subscribers << client
95
+ end
96
+ end
97
+
98
+ def ensure_subscribers_ready
99
+ sleep(1)
100
+ 2.times do
101
+ do_feed(1)
102
+ n_subscribers.times do
103
+ @published_messages.pop
104
+ break if @published_messages.empty?
105
+ end
106
+ end
107
+ @published_messages.clear
108
+ end
109
+
110
+ def do_feed(count)
111
+ Droonga::Client.open(:tag => @config.engine.tag,
112
+ :host => @config.engine.host,
113
+ :port => @config.engine.port) do |feeder|
114
+ count.times do
115
+ do_one_feed(feeder)
116
+ end
117
+ end
118
+ end
119
+
120
+ def do_one_feed(feeder)
121
+ message = @config.new_feed
122
+ feeder.send(message)
123
+ end
124
+
125
+ def receive_messages(count)
126
+ n_published_messages = 0
127
+ count.times do
128
+ # we should implement "timeout" for too slow cases
129
+ @published_messages.pop
130
+ n_published_messages += 1
131
+ end
132
+ n_published_messages
133
+ end
134
+ end
135
+ end
136
+ end