fluent-plugin-droonga 1.0.0 → 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (90) hide show
  1. data/.travis.yml +1 -1
  2. data/Gemfile +1 -1
  3. data/fluent-plugin-droonga.gemspec +1 -1
  4. data/lib/droonga/catalog/collection_volume.rb +97 -0
  5. data/lib/droonga/catalog/dataset.rb +28 -1
  6. data/lib/droonga/catalog/errors.rb +28 -9
  7. data/lib/droonga/catalog/schema.rb +23 -2
  8. data/lib/droonga/catalog/single_volume.rb +28 -0
  9. data/lib/droonga/catalog/slice.rb +43 -0
  10. data/lib/droonga/catalog/version1.rb +3 -3
  11. data/lib/droonga/catalog/version2.rb +17 -81
  12. data/lib/droonga/catalog/version2_validator.rb +63 -0
  13. data/lib/droonga/catalog/volume.rb +33 -0
  14. data/lib/droonga/catalog/volume_collection.rb +56 -0
  15. data/lib/droonga/catalog_observer.rb +7 -19
  16. data/lib/droonga/collectors.rb +1 -1
  17. data/lib/droonga/collectors/{add.rb → or.rb} +1 -1
  18. data/lib/droonga/dispatcher.rb +24 -18
  19. data/lib/droonga/distributed_command_planner.rb +7 -11
  20. data/lib/droonga/distributor.rb +29 -17
  21. data/lib/droonga/event_loop.rb +2 -11
  22. data/lib/droonga/fluent_message_sender.rb +51 -5
  23. data/lib/droonga/handler_runner.rb +1 -1
  24. data/lib/droonga/job_protocol.rb +20 -0
  25. data/lib/droonga/job_pusher.rb +178 -0
  26. data/lib/droonga/{message_receiver.rb → job_receiver.rb} +13 -6
  27. data/lib/droonga/message_matcher.rb +18 -15
  28. data/lib/droonga/planner.rb +2 -3
  29. data/lib/droonga/plugins/crud.rb +1 -1
  30. data/lib/droonga/plugins/groonga/column_create.rb +4 -1
  31. data/lib/droonga/plugins/groonga/table_create.rb +1 -1
  32. data/lib/droonga/plugins/groonga/table_remove.rb +1 -1
  33. data/lib/droonga/plugins/search/distributed_search_planner.rb +9 -0
  34. data/lib/droonga/processor.rb +3 -3
  35. data/lib/droonga/reducer.rb +15 -12
  36. data/lib/droonga/searcher.rb +49 -4
  37. data/lib/droonga/server.rb +2 -0
  38. data/lib/droonga/single_step.rb +22 -7
  39. data/lib/droonga/slice.rb +7 -7
  40. data/lib/droonga/step_runner.rb +3 -2
  41. data/lib/droonga/worker.rb +10 -8
  42. data/test/command/suite/add/dimension/column.catalog.json +27 -0
  43. data/test/command/suite/add/dimension/column.expected +57 -0
  44. data/test/command/suite/add/dimension/column.test +51 -0
  45. data/test/command/suite/search/adjusters/multiple.catalog.json +38 -0
  46. data/test/command/suite/search/adjusters/multiple.expected +23 -0
  47. data/test/command/suite/search/adjusters/multiple.test +75 -0
  48. data/test/command/suite/search/adjusters/one.catalog.json +38 -0
  49. data/test/command/suite/search/adjusters/one.expected +23 -0
  50. data/test/command/suite/search/adjusters/one.test +66 -0
  51. data/test/command/suite/search/attributes/array.test +0 -2
  52. data/test/command/suite/search/attributes/hash.test +0 -2
  53. data/test/command/suite/search/complex.test +0 -2
  54. data/test/command/suite/search/condition/nested.test +0 -2
  55. data/test/command/suite/search/condition/query.test +0 -2
  56. data/test/command/suite/search/condition/script.test +0 -2
  57. data/test/command/suite/search/group/string.test +0 -4
  58. data/test/command/suite/search/group/subrecord/with-sort.catalog.json +33 -0
  59. data/test/command/suite/search/group/subrecord/with-sort.expected +34 -0
  60. data/test/command/suite/search/group/subrecord/with-sort.test +81 -0
  61. data/test/command/suite/search/multiple/chained.test +0 -4
  62. data/test/command/suite/search/multiple/parallel.test +0 -4
  63. data/test/command/suite/search/range/only-output.test +0 -2
  64. data/test/command/suite/search/range/only-sort.test +0 -2
  65. data/test/command/suite/search/range/sort-and-output.test +0 -2
  66. data/test/command/suite/search/range/too-large-output-offset.test +0 -2
  67. data/test/command/suite/search/range/too-large-sort-offset.test +0 -2
  68. data/test/command/suite/search/response/elapsed_time.catalog.json +13 -0
  69. data/test/command/suite/search/response/elapsed_time.expected +15 -0
  70. data/test/command/suite/search/response/elapsed_time.test +26 -0
  71. data/test/command/suite/search/response/records/value/time.test +0 -2
  72. data/test/command/suite/search/simple.test +0 -2
  73. data/test/command/suite/search/sort/default-offset-limit.test +0 -2
  74. data/test/command/suite/search/sort/invisible-column.test +0 -2
  75. data/test/unit/catalog/test_collection_volume.rb +103 -0
  76. data/test/unit/catalog/test_dataset.rb +69 -8
  77. data/test/unit/catalog/test_schema.rb +63 -23
  78. data/test/unit/catalog/test_single_volume.rb +31 -0
  79. data/test/unit/catalog/test_slice.rb +92 -0
  80. data/test/unit/catalog/test_version1.rb +1 -1
  81. data/test/unit/catalog/test_version2.rb +1 -32
  82. data/test/unit/catalog/test_version2_validator.rb +66 -0
  83. data/test/unit/catalog/test_volume_collection.rb +50 -0
  84. data/test/unit/plugins/groonga/test_column_create.rb +4 -1
  85. data/test/unit/plugins/groonga/test_table_create.rb +1 -1
  86. data/test/unit/test_message_matcher.rb +15 -15
  87. data/test/unit/test_watch_schema.rb +1 -1
  88. metadata +107 -94
  89. checksums.yaml +0 -7
  90. data/lib/droonga/message_pusher.rb +0 -64
@@ -0,0 +1,63 @@
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 "droonga/catalog/errors"
17
+
18
+ module Droonga
19
+ module Catalog
20
+ class Version2Validator
21
+ def initialize(data, path)
22
+ @data = data
23
+ @path = path
24
+ end
25
+
26
+ def validate
27
+ @details = []
28
+
29
+ validate_datasets
30
+
31
+ unless @details.empty?
32
+ raise ValidationError.new(@path, @details)
33
+ end
34
+ end
35
+
36
+ private
37
+ def validate_datasets
38
+ unless @data.key?("datasets")
39
+ required_parameter_is_missing("datasets")
40
+ return
41
+ end
42
+ @data["datasets"].each do |name, dataset|
43
+ validate_dataset(name, dataset)
44
+ end
45
+ end
46
+
47
+ def validate_dataset(name, dataset)
48
+ unless dataset.key?("replicas")
49
+ required_parameter_is_missing("datasets.#{name}.replicas")
50
+ return
51
+ end
52
+ end
53
+
54
+ def add_detail(value_path, message)
55
+ @details << ValidationError::Detail.new(value_path, message)
56
+ end
57
+
58
+ def required_parameter_is_missing(value_path)
59
+ add_detail(value_path, "required parameter is missing")
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,33 @@
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 "droonga/catalog/single_volume"
17
+ require "droonga/catalog/collection_volume"
18
+
19
+ module Droonga
20
+ module Catalog
21
+ module Volume
22
+ class << self
23
+ def create(dataset, raw_volume)
24
+ if raw_volume.key?("address")
25
+ SingleVolume.new(raw_volume)
26
+ else
27
+ CollectionVolume.new(dataset, raw_volume)
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,56 @@
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
+ module Droonga
17
+ module Catalog
18
+ class VolumeCollection
19
+ include Enumerable
20
+
21
+ def initialize(volumes)
22
+ @volumes = volumes
23
+ end
24
+
25
+ def each(&block)
26
+ @volumes.each(&block)
27
+ end
28
+
29
+ def ==(other)
30
+ other.is_a?(self.class) and
31
+ to_a == other.to_a
32
+ end
33
+
34
+ def eql?(other)
35
+ self == other
36
+ end
37
+
38
+ def hash
39
+ to_a.hash
40
+ end
41
+
42
+ def select(how=nil)
43
+ case how
44
+ when :top
45
+ [@volumes.first]
46
+ when :random
47
+ [@volumes.sample]
48
+ when :all
49
+ @volumes
50
+ else
51
+ super
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
@@ -34,29 +34,17 @@ module Droonga
34
34
  end
35
35
 
36
36
  def start
37
- @loop = Cool.io::Loop.new
38
- attach(@loop)
39
- @thread = Thread.new do
40
- begin
41
- @loop.run
42
- rescue => error
43
- logger.exception("error in catalog observing thread", error)
44
- end
37
+ @watcher = Cool.io::TimerWatcher.new(CHECK_INTERVAL, true)
38
+ observer = self
39
+ @watcher.on_timer do
40
+ observer.ensure_latest_catalog_loaded
45
41
  end
42
+ loop = Coolio::Loop.default
43
+ @watcher.attach(loop)
46
44
  end
47
45
 
48
46
  def stop
49
- @loop.stop
50
- @thread.join
51
- end
52
-
53
- def attach(loop)
54
- watcher = Cool.io::TimerWatcher.new(CHECK_INTERVAL, true)
55
- observer = self
56
- watcher.on_timer do
57
- observer.ensure_latest_catalog_loaded
58
- end
59
- loop.attach(watcher)
47
+ @watcher.detach
60
48
  end
61
49
 
62
50
  def ensure_latest_catalog_loaded
@@ -13,6 +13,6 @@
13
13
  # License along with this library; if not, write to the Free Software
14
14
  # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
15
15
 
16
- require "droonga/collectors/add"
17
16
  require "droonga/collectors/and"
17
+ require "droonga/collectors/or"
18
18
  require "droonga/collectors/sum"
@@ -15,7 +15,7 @@
15
15
 
16
16
  module Droonga
17
17
  module Collectors
18
- class Add
18
+ class Or
19
19
  class << self
20
20
  def operator
21
21
  "or"
@@ -54,7 +54,7 @@ module Droonga
54
54
  @catalog = catalog
55
55
  @options = options
56
56
  @name = @options[:name]
57
- @loop = EventLoop.new
57
+ @loop = EventLoop.new(Coolio::Loop.default)
58
58
  @sessions = {}
59
59
  @current_id = 0
60
60
  @local = Regexp.new("^#{@name}")
@@ -69,9 +69,6 @@ module Droonga
69
69
  def start
70
70
  @forwarder.start
71
71
  @farm.start
72
- @loop_thread = Thread.new do
73
- @loop.run
74
- end
75
72
 
76
73
  ensure_schema
77
74
  end
@@ -85,8 +82,6 @@ module Droonga
85
82
  adapter_runner.shutdown
86
83
  end
87
84
  @farm.shutdown
88
- @loop.stop
89
- @loop_thread.join
90
85
  end
91
86
 
92
87
  def process_message(message)
@@ -134,8 +129,19 @@ module Droonga
134
129
  if adapter_runner
135
130
  adapted_message = adapter_runner.adapt_output(adapted_message)
136
131
  end
137
- return if adapted_message["replyTo"].nil?
138
- @replier.reply(adapted_message)
132
+ if adapted_message["replyTo"].nil?
133
+ status_code = adapted_message["statusCode"] || 200
134
+ if status_code != 200
135
+ dataset = adapted_message["dataset"]
136
+ body = adapted_message["body"] || {}
137
+ name = body["name"] || "Unknown"
138
+ message = body["message"] || "unknown error"
139
+ logger.error("orphan error: " +
140
+ "<#{dataset}>[#{name}](#{status_code}): #{message}")
141
+ end
142
+ else
143
+ @replier.reply(adapted_message)
144
+ end
139
145
  end
140
146
 
141
147
  def process_internal_message(message)
@@ -233,8 +239,8 @@ module Droonga
233
239
  adapted_message = adapter_runner.adapt_input(message)
234
240
  step_runner = @step_runners[dataset]
235
241
  plan = step_runner.plan(message)
236
- distributor = Distributor.new(self)
237
- distributor.distribute(plan)
242
+ distributor = Distributor.new(self, plan)
243
+ distributor.distribute
238
244
  rescue Droonga::UnsupportedMessageError => error
239
245
  target_message = error.message
240
246
  raise UnknownType.new(target_message["type"], target_message["dataset"])
@@ -252,27 +258,27 @@ module Droonga
252
258
 
253
259
  def create_runners
254
260
  runners = {}
255
- @catalog.datasets.each do |name, configuration|
256
- runners[name] = yield(configuration)
261
+ @catalog.datasets.each do |name, dataset|
262
+ runners[name] = yield(dataset)
257
263
  end
258
264
  runners
259
265
  end
260
266
 
261
267
  def create_adapter_runners
262
- create_runners do |configuration|
263
- AdapterRunner.new(self, configuration["plugins"] || [])
268
+ create_runners do |dataset|
269
+ AdapterRunner.new(self, dataset.plugins)
264
270
  end
265
271
  end
266
272
 
267
273
  def create_collector_runners
268
- create_runners do |configuration|
269
- CollectorRunner.new(configuration["plugins"] || [])
274
+ create_runners do |dataset|
275
+ CollectorRunner.new(dataset.plugins)
270
276
  end
271
277
  end
272
278
 
273
279
  def create_step_runners
274
- create_runners do |configuration|
275
- StepRunner.new(configuration["plugins"] || [])
280
+ create_runners do |dataset|
281
+ StepRunner.new(dataset, dataset.plugins)
276
282
  end
277
283
  end
278
284
 
@@ -55,12 +55,12 @@ module Droonga
55
55
  end
56
56
  end
57
57
 
58
- def scatter(options={})
58
+ def scatter(record, options={})
59
59
  @processor = {
60
60
  "command" => @source_message["type"],
61
61
  "dataset" => @dataset || @source_message["dataset"],
62
62
  "body" => options[:body] || @source_message["body"],
63
- "key" => nil,
63
+ "record" => record,
64
64
  "type" => "scatter",
65
65
  "outputs" => [],
66
66
  "replica" => "all",
@@ -100,10 +100,10 @@ module Droonga
100
100
  unified = unified_reducers[type]
101
101
  if unified
102
102
  unified["body"] = unified["body"].merge(reducer["body"])
103
- unified["inputs"] = unified["inputs"] + reducer["inputs"]
104
- unified["outputs"] = unified["outputs"] + reducer["outputs"]
103
+ unified["inputs"] += reducer["inputs"]
104
+ unified["outputs"] += reducer["outputs"]
105
105
  else
106
- unified_reducers[type] = Marshal.load(Marshal.dump(reducer))
106
+ unified_reducers[type] = reducer.dup
107
107
  end
108
108
  end
109
109
  unified_reducers.values
@@ -116,9 +116,9 @@ module Droonga
116
116
  unified = unified_gatherers[type]
117
117
  if unified
118
118
  unified["body"] = unified["body"].merge(gatherer["body"])
119
- unified["inputs"] = unified["inputs"] + gatherer["inputs"]
119
+ unified["inputs"] += gatherer["inputs"]
120
120
  else
121
- unified_gatherers[type] = Marshal.load(Marshal.dump(gatherer))
121
+ unified_gatherers[type] = gatherer.dup
122
122
  end
123
123
  end
124
124
  unified_gatherers.values
@@ -126,10 +126,6 @@ module Droonga
126
126
 
127
127
  def fixed_processor
128
128
  @processor["outputs"] = @outputs
129
- if @processor["type"] == "scatter"
130
- raise ErrorMessages::InternalServerError.new("missing key") unless @key
131
- @processor["key"] = @key
132
- end
133
129
  @processor
134
130
  end
135
131
 
@@ -37,38 +37,50 @@ module Droonga
37
37
 
38
38
  include TSort
39
39
 
40
- def initialize(dispatcher)
40
+ def initialize(dispatcher, plan)
41
41
  @dispatcher = dispatcher
42
+ @plan = plan
43
+ build_dependencies
42
44
  end
43
45
 
44
- def distribute(plan)
45
- @dependency = {}
46
- plan.each do |step|
47
- @dependency[step] = step["inputs"]
48
- next unless step["outputs"]
49
- step["outputs"].each do |output|
50
- @dependency[output] = [step]
51
- end
52
- end
46
+ def distribute
53
47
  steps = []
54
- each_strongly_connected_component do |cs|
55
- raise CyclicStepsError.new(cs) if cs.size > 1
56
- steps.concat(cs) unless cs.first.is_a? String
48
+ each_strongly_connected_component do |nodes|
49
+ raise CyclicStepsError.new(nodes) if nodes.size > 1
50
+ nodes.each do |node|
51
+ steps << @step_maps[node] if node.is_a?(Integer)
52
+ end
57
53
  end
58
54
  @dispatcher.dispatch_steps(steps)
59
55
  end
60
56
 
61
57
  private
58
+ def build_dependencies
59
+ @dependencies = {}
60
+ @step_maps = {}
61
+ step_id = 0
62
+ @plan.each do |step|
63
+ step_id += 1
64
+ # Integer#hash (step_id.hash) is very faster than Hash#hash (step.hash).
65
+ @step_maps[step_id] = step
66
+ @dependencies[step_id] = step["inputs"]
67
+ next unless step["outputs"]
68
+ step["outputs"].each do |output|
69
+ @dependencies[output] = [step_id]
70
+ end
71
+ end
72
+ end
73
+
62
74
  def tsort_each_node(&block)
63
- @dependency.each_key(&block)
75
+ @dependencies.each_key(&block)
64
76
  end
65
77
 
66
78
  def tsort_each_child(node, &block)
67
- if node.is_a? String and @dependency[node].nil?
79
+ if node.is_a? String and @dependencies[node].nil?
68
80
  raise UndefinedInputError.new(node)
69
81
  end
70
- if @dependency[node]
71
- @dependency[node].each(&block)
82
+ if @dependencies[node]
83
+ @dependencies[node].each(&block)
72
84
  end
73
85
  end
74
86
  end
@@ -19,21 +19,12 @@ require "coolio"
19
19
 
20
20
  module Droonga
21
21
  class EventLoop
22
- def initialize
23
- @loop = Coolio::Loop.new
22
+ def initialize(loop)
23
+ @loop = loop
24
24
  @loop_breaker = Coolio::AsyncWatcher.new
25
25
  @loop_breaker.attach(@loop)
26
26
  end
27
27
 
28
- def run
29
- @loop.run
30
- end
31
-
32
- def stop
33
- @loop.stop
34
- break_current_loop
35
- end
36
-
37
28
  def attach(watcher)
38
29
  @loop.attach(watcher)
39
30
  break_current_loop