fluent-plugin-droonga 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (77) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +1 -0
  3. data/.travis.yml +7 -0
  4. data/Gemfile +40 -0
  5. data/LICENSE.txt +14 -0
  6. data/README.md +18 -0
  7. data/Rakefile +25 -0
  8. data/benchmark/benchmark.rb +123 -0
  9. data/benchmark/utils.rb +243 -0
  10. data/benchmark/watch/benchmark-notify.rb +143 -0
  11. data/benchmark/watch/benchmark-notify.sh +19 -0
  12. data/benchmark/watch/benchmark-publish.rb +120 -0
  13. data/benchmark/watch/benchmark-scan.rb +210 -0
  14. data/benchmark/watch/catalog.json +32 -0
  15. data/benchmark/watch/fluentd.conf +12 -0
  16. data/bin/grn2jsons +85 -0
  17. data/fluent-plugin-droonga.gemspec +41 -0
  18. data/lib/droonga/adapter.rb +156 -0
  19. data/lib/droonga/catalog.rb +153 -0
  20. data/lib/droonga/command_mapper.rb +45 -0
  21. data/lib/droonga/engine.rb +83 -0
  22. data/lib/droonga/executor.rb +289 -0
  23. data/lib/droonga/handler.rb +140 -0
  24. data/lib/droonga/handler_plugin.rb +35 -0
  25. data/lib/droonga/job_queue.rb +83 -0
  26. data/lib/droonga/job_queue_schema.rb +65 -0
  27. data/lib/droonga/logger.rb +34 -0
  28. data/lib/droonga/plugin.rb +41 -0
  29. data/lib/droonga/plugin/adapter/groonga/select.rb +88 -0
  30. data/lib/droonga/plugin/adapter_groonga.rb +40 -0
  31. data/lib/droonga/plugin/handler/groonga/column_create.rb +103 -0
  32. data/lib/droonga/plugin/handler/groonga/table_create.rb +100 -0
  33. data/lib/droonga/plugin/handler_add.rb +44 -0
  34. data/lib/droonga/plugin/handler_forward.rb +70 -0
  35. data/lib/droonga/plugin/handler_groonga.rb +52 -0
  36. data/lib/droonga/plugin/handler_proxy.rb +82 -0
  37. data/lib/droonga/plugin/handler_search.rb +33 -0
  38. data/lib/droonga/plugin/handler_watch.rb +102 -0
  39. data/lib/droonga/proxy.rb +371 -0
  40. data/lib/droonga/searcher.rb +415 -0
  41. data/lib/droonga/server.rb +112 -0
  42. data/lib/droonga/sweeper.rb +42 -0
  43. data/lib/droonga/watch_schema.rb +88 -0
  44. data/lib/droonga/watcher.rb +256 -0
  45. data/lib/droonga/worker.rb +51 -0
  46. data/lib/fluent/plugin/out_droonga.rb +56 -0
  47. data/lib/groonga_command_converter.rb +137 -0
  48. data/sample/cluster/catalog.json +43 -0
  49. data/sample/cluster/fluentd.conf +12 -0
  50. data/sample/fluentd.conf +8 -0
  51. data/test/fixtures/catalog.json +43 -0
  52. data/test/fixtures/document.grn +23 -0
  53. data/test/helper.rb +24 -0
  54. data/test/helper/fixture.rb +28 -0
  55. data/test/helper/sandbox.rb +73 -0
  56. data/test/helper/stub_worker.rb +27 -0
  57. data/test/helper/watch_helper.rb +35 -0
  58. data/test/plugin/adapter/groonga/test_select.rb +176 -0
  59. data/test/plugin/handler/groonga/test_column_create.rb +127 -0
  60. data/test/plugin/handler/groonga/test_table_create.rb +140 -0
  61. data/test/plugin/handler/test_handler_add.rb +135 -0
  62. data/test/plugin/handler/test_handler_groonga.rb +64 -0
  63. data/test/plugin/handler/test_handler_search.rb +512 -0
  64. data/test/plugin/handler/test_handler_watch.rb +168 -0
  65. data/test/run-test.rb +55 -0
  66. data/test/test_adapter.rb +48 -0
  67. data/test/test_catalog.rb +59 -0
  68. data/test/test_command_mapper.rb +44 -0
  69. data/test/test_groonga_command_converter.rb +242 -0
  70. data/test/test_handler.rb +53 -0
  71. data/test/test_job_queue_schema.rb +45 -0
  72. data/test/test_output.rb +99 -0
  73. data/test/test_sweeper.rb +95 -0
  74. data/test/test_watch_schema.rb +57 -0
  75. data/test/test_watcher.rb +336 -0
  76. data/test/test_worker.rb +144 -0
  77. metadata +299 -0
@@ -0,0 +1,143 @@
1
+ # -*- coding: utf-8 -*-
2
+ #
3
+ # Copyright (C) 2013 droonga project
4
+ #
5
+ # This library is free software; you can redistribute it and/or
6
+ # modify it under the terms of the GNU Lesser General Public
7
+ # License version 2.1 as published by the Free Software Foundation.
8
+ #
9
+ # This library is distributed in the hope that it will be useful,
10
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12
+ # Lesser General Public License for more details.
13
+ #
14
+ # You should have received a copy of the GNU Lesser General Public
15
+ # License along with this library; if not, write to the Free Software
16
+ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17
+
18
+ # this benchmark must be done by benchmark-notify.sh.
19
+
20
+ require "benchmark"
21
+ require "fileutils"
22
+ require "optparse"
23
+ require "csv"
24
+ require "json"
25
+
26
+ require "droonga/client"
27
+
28
+ require File.expand_path(File.join(__FILE__, "..", "..", "utils.rb"))
29
+
30
+ class NotifyBenchmark
31
+ attr_reader :n_subscribers
32
+
33
+ WATCHING_KEYWORD = "a"
34
+
35
+ def initialize(params)
36
+ @params = params || {}
37
+ @n_times = params[:n_times] || 0
38
+ @timeout = params[:timeout] || 0
39
+
40
+ @n_subscribers = 0
41
+
42
+ @client = Droonga::Client.new(tag: "droonga", port: 23003)
43
+ @receiver = DroongaBenchmark::MessageReceiver.new
44
+ @route = "#{@receiver.host}:#{@receiver.port}/droonga"
45
+ setup
46
+ end
47
+
48
+ def setup
49
+ add_subscribers(@params[:n_initial_subscribers])
50
+ end
51
+
52
+ def run
53
+ @n_times.times do |index|
54
+ do_feed("#{WATCHING_KEYWORD} #{index}")
55
+ end
56
+
57
+ notifications = []
58
+ while notifications.size != @n_times do
59
+ notifications << @receiver.new_message
60
+ end
61
+ notifications
62
+ end
63
+
64
+ def add_subscribers(n_subscribers)
65
+ n_subscribers.times do |index|
66
+ message = DroongaBenchmark::MessageCreator.envelope_to_subscribe(WATCHING_KEYWORD)
67
+ message["body"]["subscriber"] += " #{@n_subscribers + index}"
68
+ message["body"]["route"] = @route
69
+ @client.connection.send_receive(message)
70
+ end
71
+ @n_subscribers += n_subscribers
72
+ end
73
+
74
+ def do_feed(target)
75
+ message = DroongaBenchmark::MessageCreator.envelope_to_feed(target)
76
+ @client.connection.send(message)
77
+ end
78
+ end
79
+
80
+ options = {
81
+ :n_subscribers => 1000,
82
+ :n_times => 1000,
83
+ :n_steps => 10,
84
+ :output_path => "/tmp/watch-benchmark-notify.csv",
85
+ }
86
+ option_parser = OptionParser.new do |parser|
87
+ parser.on("--subscribers=N", Integer,
88
+ "initial number of subscribers") do |n_subscribers|
89
+ options[:n_subscribers] = n_subscribers
90
+ end
91
+ parser.on("--times=N", Integer,
92
+ "number of publish times") do |n_times|
93
+ options[:n_times] = n_times
94
+ end
95
+ parser.on("--steps=N", Integer,
96
+ "number of benchmark steps") do |n_steps|
97
+ options[:n_steps] = n_steps
98
+ end
99
+ parser.on("--timeout=N", Float,
100
+ "timeout for receiving") do |timeout|
101
+ options[:timeout] = timeout
102
+ end
103
+ parser.on("--output-path=PATH", String,
104
+ "path to the output CSV file") do |output_path|
105
+ options[:output_path] = output_path
106
+ end
107
+ end
108
+ args = option_parser.parse!(ARGV)
109
+
110
+
111
+ notify_benchmark = NotifyBenchmark.new(:n_initial_subscribers => options[:n_subscribers],
112
+ :n_times => options[:n_times],
113
+ :timeout => options[:timeout])
114
+ results = []
115
+ options[:n_steps].times do |try_count|
116
+ notify_benchmark.add_subscribers(notify_benchmark.n_subscribers) if try_count > 0
117
+ label = "#{notify_benchmark.n_subscribers} subscribers"
118
+ percentage = nil
119
+ result = Benchmark.bm do |benchmark|
120
+ benchmark.report(label) do
121
+ sent_notifications = notify_benchmark.run
122
+ percentage = sent_notifications.size.to_f / options[:n_times] * 100
123
+ end
124
+ end
125
+ puts "=> #{percentage} % feeds are notified"
126
+ result = result.join("").strip.gsub(/[()]/, "").split(/\s+/)
127
+ qps = options[:n_times].to_f / result.last.to_f
128
+ puts " (#{qps} queries per second)"
129
+ results << [label, qps]
130
+ end
131
+ total_results = [
132
+ ["case", "qps"],
133
+ ]
134
+ total_results += results
135
+
136
+ puts ""
137
+ puts "Results (saved to #{options[:output_path]}):"
138
+ File.open(options[:output_path], "w") do |file|
139
+ total_results.each do |row|
140
+ file.puts(CSV.generate_line(row))
141
+ puts row.join(",")
142
+ end
143
+ end
@@ -0,0 +1,19 @@
1
+ #!/bin/sh
2
+
3
+ base_dir=$(cd $(dirname $0); pwd)
4
+
5
+ rm -rf $base_dir/watch
6
+ mkdir -p $base_dir/watch
7
+
8
+ DROONGA_CATALOG=$base_dir/catalog.json \
9
+ bundle exec fluentd \
10
+ --config $base_dir/fluentd.conf &
11
+ FLUENTD_PID=$!
12
+
13
+ sleep 1
14
+
15
+ bundle exec ruby $base_dir/benchmark-notify.rb "$@"
16
+
17
+ kill $FLUENTD_PID
18
+ wait $FLUENTD_PID
19
+
@@ -0,0 +1,120 @@
1
+ # -*- coding: utf-8 -*-
2
+ #
3
+ # Copyright (C) 2013 droonga project
4
+ #
5
+ # This library is free software; you can redistribute it and/or
6
+ # modify it under the terms of the GNU Lesser General Public
7
+ # License version 2.1 as published by the Free Software Foundation.
8
+ #
9
+ # This library is distributed in the hope that it will be useful,
10
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12
+ # Lesser General Public License for more details.
13
+ #
14
+ # You should have received a copy of the GNU Lesser General Public
15
+ # License along with this library; if not, write to the Free Software
16
+ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17
+
18
+ require "benchmark"
19
+ require "fileutils"
20
+ require "optparse"
21
+ require "csv"
22
+
23
+ require "groonga"
24
+
25
+ require "droonga/watcher"
26
+ require File.expand_path(File.join(__FILE__, "..", "..", "utils.rb"))
27
+
28
+ class PublishBenchmark
29
+ attr_reader :n_subscribers
30
+
31
+ def initialize(n_initial_subscribers)
32
+ @database = DroongaBenchmark::WatchDatabase.new
33
+ @watcher = Droonga::Watcher.new(@database.context)
34
+ @keywords_generator = DroongaBenchmark::KeywordsGenerator.new
35
+ @keywords = []
36
+ @n_subscribers = 0
37
+ add_subscribers(n_initial_subscribers)
38
+ end
39
+
40
+ def run
41
+ @matched_keywords.each do |keyword|
42
+ publish(keyword)
43
+ end
44
+ end
45
+
46
+ def prepare_keywords(n_keywords)
47
+ @matched_keywords = @keywords.sample(n_keywords)
48
+ end
49
+
50
+ def add_subscribers(n_subscribers)
51
+ new_keywords = []
52
+ n_subscribers.times do
53
+ new_keywords << @keywords_generator.next
54
+ end
55
+ @database.subscribe_to(new_keywords)
56
+ @keywords += new_keywords
57
+ @n_subscribers += n_subscribers
58
+ end
59
+
60
+ private
61
+ def publish(matched_keyword)
62
+ @watcher.publish([matched_keyword], {}) do |route, subscribers|
63
+ end
64
+ end
65
+ end
66
+
67
+ options = {
68
+ :n_subscribers => 1000,
69
+ :n_times => 1000,
70
+ :n_steps => 10,
71
+ :output_path => "/tmp/watch-benchmark-notify.csv",
72
+ }
73
+ option_parser = OptionParser.new do |parser|
74
+ parser.on("--subscribers=N", Integer,
75
+ "initial number of subscribers (optional)") do |n_subscribers|
76
+ options[:n_subscribers] = n_subscribers
77
+ end
78
+ parser.on("--times=N", Integer,
79
+ "number of publish times (optional)") do |n_times|
80
+ options[:n_times] = n_times
81
+ end
82
+ parser.on("--steps=N", Integer,
83
+ "number of benchmark steps (optional)") do |n_steps|
84
+ options[:n_steps] = n_steps
85
+ end
86
+ parser.on("--output-path=PATH", String,
87
+ "path to the output CSV file (optional)") do |output_path|
88
+ options[:output_path] = output_path
89
+ end
90
+ end
91
+ args = option_parser.parse!(ARGV)
92
+
93
+
94
+ publish_benchmark = PublishBenchmark.new(options[:n_subscribers])
95
+ results = []
96
+ options[:n_steps].times do |try_count|
97
+ publish_benchmark.add_subscribers(publish_benchmark.n_subscribers) if try_count > 0
98
+ label = "#{publish_benchmark.n_subscribers} subscribers"
99
+ result = Benchmark.bmbm do |benchmark|
100
+ publish_benchmark.prepare_keywords(options[:n_times])
101
+ benchmark.report(label) do
102
+ publish_benchmark.run
103
+ end
104
+ end
105
+ result = result.join("").strip.gsub(/[()]/, "").split(/\s+/)
106
+ results << [label] + result
107
+ end
108
+ total_results = [
109
+ ["case", "user", "system", "total", "real"],
110
+ ]
111
+ total_results += results
112
+
113
+ puts ""
114
+ puts "Results (saved to #{options[:output_path]}):"
115
+ File.open(options[:output_path], "w") do |file|
116
+ total_results.each do |row|
117
+ file.puts(CSV.generate_line(row))
118
+ puts row.join(",")
119
+ end
120
+ end
@@ -0,0 +1,210 @@
1
+ # -*- coding: utf-8 -*-
2
+ #
3
+ # Copyright (C) 2013 droonga project
4
+ #
5
+ # This library is free software; you can redistribute it and/or
6
+ # modify it under the terms of the GNU Lesser General Public
7
+ # License version 2.1 as published by the Free Software Foundation.
8
+ #
9
+ # This library is distributed in the hope that it will be useful,
10
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12
+ # Lesser General Public License for more details.
13
+ #
14
+ # You should have received a copy of the GNU Lesser General Public
15
+ # License along with this library; if not, write to the Free Software
16
+ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17
+
18
+ require "benchmark"
19
+ require "fileutils"
20
+ require "optparse"
21
+ require "csv"
22
+
23
+ require "groonga"
24
+
25
+ require "droonga/watcher"
26
+ require File.expand_path(File.join(__FILE__, "..", "..", "utils.rb"))
27
+
28
+ class ScanBenchmark
29
+ attr_reader :n_keywords
30
+
31
+ def initialize(n_times, options={})
32
+ @n_times = n_times
33
+ @incidence = options[:incidence]
34
+
35
+ @database = DroongaBenchmark::WatchDatabase.new
36
+
37
+ @watcher = Droonga::Watcher.new(@database.context)
38
+
39
+ @keywords_generator = DroongaBenchmark::KeywordsGenerator.new
40
+ @keywords = @keywords_generator.generate(@n_times)
41
+ prepare_targets(options)
42
+
43
+ @database.subscribe_to(@keywords)
44
+ @n_keywords = @keywords.size
45
+
46
+ @hits = []
47
+ end
48
+
49
+ def run
50
+ @targets.each do |target|
51
+ scan(target)
52
+ end
53
+ end
54
+
55
+ def prepare_targets(options={})
56
+ @incidence = options[:incidence] || 0
57
+ @matched_keywords = options[:matched_keywords] || 0
58
+ @targets = DroongaBenchmark::TargetsGenerator.generate(@n_times,
59
+ :keywords => @keywords.sample(@n_times),
60
+ :incidence => @incidence,
61
+ :matched_keywords => @matched_keywords)
62
+ end
63
+
64
+ def add_keywords(n_keywords)
65
+ new_keywords = []
66
+ n_keywords.times do
67
+ new_keywords << @keywords_generator.next
68
+ end
69
+ @database.subscribe_to(new_keywords)
70
+ @keywords += new_keywords
71
+ @n_keywords += n_keywords
72
+ end
73
+
74
+ def memory_usage
75
+ /^VmRSS:\s*(\d+) kB/ =~ File.read("/proc/self/status")
76
+ $1.to_i * 1024
77
+ end
78
+
79
+ private
80
+ def scan(target)
81
+ @watcher.scan_body(@hits, target)
82
+ @hits.clear
83
+ end
84
+ end
85
+
86
+ options = {
87
+ :n_watching_keywords => 1000,
88
+ :n_steps => 10,
89
+ :incidences => "0.1,0.5,0.9",
90
+ :matched_keywords => "1,5,10",
91
+ :output_path => "/tmp/watch-benchmark-scan",
92
+ }
93
+ option_parser = OptionParser.new do |parser|
94
+ parser.on("--keywords=N", Integer,
95
+ "number of watching keywords") do |n_watching_keywords|
96
+ options[:n_watching_keywords] = n_watching_keywords
97
+ end
98
+ parser.on("--steps=N", Integer,
99
+ "number of benchmark steps") do |n_steps|
100
+ options[:n_steps] = n_steps
101
+ end
102
+ parser.on("--incidences=INCIDENCES", String,
103
+ "list of matching incidences") do |incidences|
104
+ options[:incidences] = incidences
105
+ end
106
+ parser.on("--matched-keywords=MATCHED_KEYWORDS", String,
107
+ "number of keywords which is matched per a target") do |matched_keywords|
108
+ options[:matched_keywords] = matched_keywords
109
+ end
110
+ parser.on("--output-path=PATH", String,
111
+ "path to the output CSV file") do |output_path|
112
+ options[:output_path] = output_path
113
+ end
114
+ end
115
+ args = option_parser.parse!(ARGV)
116
+
117
+ results_for_specific_condition = {}
118
+ scan_benchmark = ScanBenchmark.new(options[:n_watching_keywords])
119
+ options[:n_steps].times do |try_count|
120
+ scan_benchmark.add_keywords(scan_benchmark.n_keywords) if try_count > 0
121
+ GC.start
122
+ sleep 1
123
+ puts "\n=============== #{scan_benchmark.n_keywords} keywords ===============\n"
124
+ options[:incidences].split(/[,\s]+/).each do |incidence|
125
+ incidence = incidence.to_f
126
+ options[:matched_keywords].split(/[,\s]+/).each do |matched_keywords|
127
+ matched_keywords = matched_keywords.to_i
128
+
129
+ GC.disable
130
+
131
+ condition = "#{incidence * 100}%/#{matched_keywords}match"
132
+ results_for_specific_condition[condition] ||= []
133
+ label = "#{incidence * 100} %/#{matched_keywords} match/#{scan_benchmark.n_keywords} keywords"
134
+
135
+ result = Benchmark.bmbm do |benchmark|
136
+ scan_benchmark.prepare_targets(:incidence => incidence,
137
+ :matched_keywords => matched_keywords)
138
+ benchmark.report(label) do
139
+ scan_benchmark.run
140
+ end
141
+ end
142
+
143
+ result = result.join("").strip.gsub(/[()]/, "").split(/\s+/)
144
+ result << scan_benchmark.memory_usage
145
+ results_for_specific_condition[condition] << [label] + result
146
+
147
+ GC.enable
148
+ GC.start
149
+ sleep 1
150
+ end
151
+ end
152
+ end
153
+
154
+ FileUtils.mkdir_p(options[:output_path])
155
+
156
+ puts ""
157
+ all_output = File.join(options[:output_path], "all.csv")
158
+ all_results = [
159
+ ["case", "user", "system", "total", "real"],
160
+ ]
161
+ results_for_specific_condition.values.each do |results|
162
+ all_results += results
163
+ end
164
+ puts "All (saved to #{all_output}):"
165
+ File.open(all_output, "w") do |file|
166
+ all_results.each do |row|
167
+ file.puts(CSV.generate_line(row))
168
+ puts row.join(",")
169
+ end
170
+ end
171
+
172
+ puts ""
173
+ total_output = File.join(options[:output_path], "total.csv")
174
+ total_results_header = ["case"]
175
+ total_results = []
176
+ results_for_specific_condition.each do |condition, results|
177
+ total_results_header << condition
178
+ results.each_index do |index|
179
+ total_results[index] ||= [results[index].first.split("/").last]
180
+ total_results[index] << results[index][3]
181
+ end
182
+ end
183
+ total_results.unshift(total_results_header)
184
+ puts "Total (saved to #{total_output}):"
185
+ File.open(total_output, "w") do |file|
186
+ total_results.each do |row|
187
+ file.puts(CSV.generate_line(row))
188
+ puts row.join(",")
189
+ end
190
+ end
191
+
192
+ puts ""
193
+ real_output = File.join(options[:output_path], "real.csv")
194
+ real_results_header = ["case"]
195
+ real_results = []
196
+ results_for_specific_condition.each do |condition, results|
197
+ real_results_header << condition
198
+ results.each_index do |index|
199
+ real_results[index] ||= [results[index].first.split("/").last]
200
+ real_results[index] << results[index][4]
201
+ end
202
+ end
203
+ real_results.unshift(real_results_header)
204
+ puts "Real (saved to #{real_output}):"
205
+ File.open(real_output, "w") do |file|
206
+ real_results.each do |row|
207
+ file.puts(CSV.generate_line(row))
208
+ puts row.join(",")
209
+ end
210
+ end