fluent-plugin-droonga 0.0.2

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