fluent-plugin-droonga 1.0.0 → 1.0.1

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