fluent-plugin-droonga 0.9.9 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (126) hide show
  1. checksums.yaml +4 -4
  2. data/.dir-locals.el +3 -0
  3. data/.travis.yml +6 -2
  4. data/README.md +6 -7
  5. data/Rakefile +23 -6
  6. data/fluent-plugin-droonga.gemspec +2 -2
  7. data/lib/droonga/adapter.rb +12 -3
  8. data/lib/droonga/adapter_runner.rb +28 -23
  9. data/lib/droonga/catalog/base.rb +7 -111
  10. data/lib/droonga/catalog/dataset.rb +13 -25
  11. data/lib/droonga/catalog/errors.rb +94 -0
  12. data/lib/droonga/catalog/schema.rb +277 -0
  13. data/lib/droonga/catalog/version1.rb +404 -0
  14. data/lib/droonga/catalog/version2.rb +160 -0
  15. data/lib/droonga/catalog_loader.rb +27 -4
  16. data/lib/droonga/catalog_observer.rb +44 -6
  17. data/lib/droonga/collector.rb +12 -10
  18. data/lib/droonga/{handler_plugin.rb → collector_message.rb} +47 -20
  19. data/lib/droonga/collector_runner.rb +64 -0
  20. data/lib/droonga/collectors.rb +18 -0
  21. data/lib/droonga/{catalog.rb → collectors/add.rb} +9 -7
  22. data/lib/droonga/{command_repository.rb → collectors/and.rb} +7 -14
  23. data/lib/droonga/collectors/sum.rb +26 -0
  24. data/lib/droonga/dispatcher.rb +74 -41
  25. data/lib/droonga/distributed_command_planner.rb +2 -2
  26. data/lib/droonga/engine.rb +13 -5
  27. data/lib/droonga/{message_processing_error.rb → error.rb} +33 -12
  28. data/lib/droonga/{plugin/planner/search.rb → error_messages.rb} +12 -10
  29. data/lib/droonga/farm.rb +15 -14
  30. data/lib/droonga/fluent_message_sender.rb +15 -11
  31. data/lib/droonga/forwarder.rb +22 -18
  32. data/lib/droonga/handler.rb +8 -2
  33. data/lib/droonga/handler_runner.rb +47 -26
  34. data/lib/droonga/input_message.rb +6 -6
  35. data/lib/droonga/{command.rb → loggable.rb} +7 -14
  36. data/lib/droonga/logger.rb +56 -15
  37. data/lib/droonga/message_matcher.rb +12 -7
  38. data/lib/droonga/message_pusher.rb +8 -4
  39. data/lib/droonga/message_receiver.rb +11 -9
  40. data/lib/droonga/output_message.rb +2 -0
  41. data/lib/droonga/planner.rb +21 -10
  42. data/lib/droonga/plugin.rb +15 -0
  43. data/lib/droonga/plugin/metadata/{adapter_message.rb → adapter_input_message.rb} +6 -14
  44. data/lib/droonga/plugin/metadata/adapter_output_message.rb +39 -0
  45. data/lib/droonga/plugin/metadata/collector_message.rb +39 -0
  46. data/lib/droonga/plugin/metadata/input_message.rb +15 -0
  47. data/lib/droonga/plugin_loader.rb +33 -25
  48. data/lib/droonga/plugin_registry.rb +9 -1
  49. data/lib/droonga/plugins/basic.rb +54 -0
  50. data/lib/droonga/plugins/crud.rb +36 -15
  51. data/lib/droonga/plugins/error.rb +5 -4
  52. data/lib/droonga/plugins/groonga.rb +9 -6
  53. data/lib/droonga/plugins/groonga/column_create.rb +10 -5
  54. data/lib/droonga/plugins/groonga/generic_command.rb +2 -8
  55. data/lib/droonga/plugins/groonga/generic_response.rb +2 -2
  56. data/lib/droonga/plugins/groonga/select.rb +2 -2
  57. data/lib/droonga/plugins/groonga/table_create.rb +9 -4
  58. data/lib/droonga/plugins/groonga/table_remove.rb +10 -5
  59. data/lib/droonga/plugins/search.rb +106 -5
  60. data/lib/droonga/plugins/search/distributed_search_planner.rb +398 -0
  61. data/lib/droonga/plugins/watch.rb +41 -20
  62. data/lib/droonga/processor.rb +12 -9
  63. data/lib/droonga/{plugin/collector/basic.rb → reducer.rb} +36 -50
  64. data/lib/droonga/replier.rb +7 -4
  65. data/lib/droonga/searcher.rb +40 -37
  66. data/lib/droonga/server.rb +8 -6
  67. data/lib/droonga/session.rb +17 -7
  68. data/lib/droonga/single_step.rb +53 -0
  69. data/lib/droonga/{plugin/planner/watch.rb → single_step_definition.rb} +27 -26
  70. data/lib/droonga/{partition.rb → slice.rb} +23 -12
  71. data/lib/droonga/status_code.rb +25 -0
  72. data/lib/droonga/step_runner.rb +63 -0
  73. data/lib/droonga/watch_schema.rb +7 -3
  74. data/lib/droonga/watcher.rb +4 -4
  75. data/lib/droonga/worker.rb +6 -6
  76. data/lib/fluent/plugin/out_droonga.rb +27 -2
  77. data/sample/cluster/catalog.json +33 -32
  78. data/test/command/config/default/catalog.json +72 -45
  79. data/test/command/config/version1/catalog.json +68 -0
  80. data/test/command/config/version1/fluentd.conf +11 -0
  81. data/test/command/suite/message/error/missing-dataset.expected +1 -1
  82. data/test/command/suite/message/error/unknown-dataset.expected +1 -1
  83. data/test/command/suite/message/error/unknown-type.expected +13 -0
  84. data/test/command/suite/message/error/{unknown-command.test → unknown-type.test} +1 -1
  85. data/test/command/suite/search/error/missing-source-parameter.expected +1 -1
  86. data/test/command/suite/search/error/unknown-source.expected +15 -3
  87. data/test/command/suite/watch/subscribe.expected +1 -3
  88. data/test/command/suite/watch/unsubscribe.expected +1 -3
  89. data/test/performance/watch/catalog.json +1 -0
  90. data/test/unit/catalog/test_dataset.rb +16 -358
  91. data/test/unit/catalog/test_schema.rb +285 -0
  92. data/test/unit/catalog/test_version1.rb +222 -28
  93. data/test/unit/catalog/test_version2.rb +155 -0
  94. data/test/unit/fixtures/catalog/version2.json +62 -0
  95. data/test/unit/helper/distributed_search_planner_helper.rb +2 -2
  96. data/test/unit/plugins/crud/test_add.rb +13 -13
  97. data/test/unit/plugins/groonga/test_column_create.rb +14 -11
  98. data/test/unit/plugins/groonga/test_table_create.rb +4 -9
  99. data/test/unit/plugins/groonga/test_table_remove.rb +4 -9
  100. data/test/unit/{plugin/planner/search_planner → plugins/search/planner}/test_basic.rb +0 -0
  101. data/test/unit/{plugin/planner/search_planner → plugins/search/planner}/test_group_by.rb +0 -0
  102. data/test/unit/{plugin/planner/search_planner → plugins/search/planner}/test_output.rb +0 -0
  103. data/test/unit/{plugin/planner/search_planner → plugins/search/planner}/test_sort_by.rb +0 -0
  104. data/test/unit/{plugin/collector/test_search.rb → plugins/search/test_collector.rb} +40 -39
  105. data/test/unit/plugins/{test_search.rb → search/test_handler.rb} +6 -5
  106. data/test/unit/{plugin/planner/test_search.rb → plugins/search/test_planner.rb} +3 -3
  107. data/test/unit/{plugin/collector → plugins}/test_basic.rb +68 -50
  108. data/test/unit/plugins/test_groonga.rb +2 -15
  109. data/test/unit/plugins/test_watch.rb +25 -22
  110. data/test/unit/test_message_matcher.rb +29 -6
  111. data/test/unit/test_output.rb +4 -0
  112. metadata +58 -50
  113. data/lib/droonga/collector_plugin.rb +0 -50
  114. data/lib/droonga/legacy_pluggable.rb +0 -66
  115. data/lib/droonga/legacy_plugin.rb +0 -57
  116. data/lib/droonga/legacy_plugin_repository.rb +0 -54
  117. data/lib/droonga/planner_plugin.rb +0 -54
  118. data/lib/droonga/plugin/collector/search.rb +0 -98
  119. data/lib/droonga/plugin/planner/crud.rb +0 -49
  120. data/lib/droonga/plugin/planner/distributed_search_planner.rb +0 -393
  121. data/lib/droonga/plugin/planner/groonga.rb +0 -54
  122. data/lib/droonga/plugin_registerable.rb +0 -75
  123. data/test/command/suite/message/error/unknown-command.expected +0 -13
  124. data/test/unit/test_command_repository.rb +0 -39
  125. data/test/unit/test_legacy_plugin.rb +0 -50
  126. data/test/unit/test_legacy_plugin_repository.rb +0 -89
@@ -0,0 +1,160 @@
1
+ # Copyright (C) 2013-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/base"
17
+ require "droonga/catalog/dataset"
18
+
19
+ module Droonga
20
+ module Catalog
21
+ class Version2 < Base
22
+ def initialize(data, path)
23
+ super
24
+ prepare_data
25
+ end
26
+
27
+ def datasets
28
+ @datasets
29
+ end
30
+
31
+ def slices(name)
32
+ device = "."
33
+ pattern = Regexp.new("^#{name}\.")
34
+ results = {}
35
+ @datasets.each do |dataset_name, dataset|
36
+ n_workers = dataset["nWorkers"]
37
+ plugins = dataset["plugins"]
38
+ dataset["replicas"].each do |replica|
39
+ replica["slices"].each do |slice|
40
+ volume_address = slice["volume"]["address"]
41
+ if pattern =~ volume_address
42
+ path = File.join([device, $POSTMATCH, "db"])
43
+ path = File.expand_path(path, base_path)
44
+ options = {
45
+ :dataset => dataset_name,
46
+ :database => path,
47
+ :n_workers => n_workers,
48
+ :plugins => plugins
49
+ }
50
+ results[volume_address] = options
51
+ end
52
+ end
53
+ end
54
+ end
55
+ results
56
+ end
57
+
58
+ def get_routes(name, args)
59
+ routes = []
60
+ dataset = dataset(name)
61
+ case args["type"]
62
+ when "broadcast"
63
+ replicas = select_replicas(dataset["replicas"], args["replica"])
64
+ replicas.each do |replica|
65
+ slices = select_slices(replica)
66
+ slices.each do |slice|
67
+ routes << slice["volume"]["address"]
68
+ end
69
+ end
70
+ when "scatter"
71
+ replicas = select_replicas(dataset["replicas"], args["replica"])
72
+ replicas.each do |replica|
73
+ slice = select_slice(replica, args["key"])
74
+ routes << slice["volume"]["address"]
75
+ end
76
+ end
77
+ routes
78
+ end
79
+
80
+ private
81
+ def validate
82
+ # TODO: Implement me.
83
+ end
84
+
85
+ def prepare_data
86
+ @datasets = {}
87
+ @data["datasets"].each do |name, dataset|
88
+ replicas = dataset["replicas"]
89
+ replicas.each do |replica|
90
+ total_weight = compute_total_weight(replica)
91
+ continuum = []
92
+ slices = replica["slices"]
93
+ n_slices = slices.size
94
+ slices.each do |slice|
95
+ weight = slice["weight"] || default_weight
96
+ points = n_slices * 160 * weight / total_weight
97
+ points.times do |point|
98
+ hash = Digest::SHA1.hexdigest("#{name}:#{point}")
99
+ continuum << [hash[0..7].to_i(16), slice]
100
+ end
101
+ end
102
+ replica["continuum"] = continuum.sort do |a, b|
103
+ a[0] - b[0]
104
+ end
105
+ end
106
+ @datasets[name] = Dataset.new(name, dataset)
107
+ end
108
+ end
109
+
110
+ def default_weight
111
+ 1
112
+ end
113
+
114
+ def compute_total_weight(replica)
115
+ slices = replica["slices"]
116
+ slices.reduce(0) do |result, slice|
117
+ result + (slice["weight"] || default_weight)
118
+ end
119
+ end
120
+
121
+ def select_replicas(replicas, how)
122
+ case how
123
+ when "top"
124
+ [replicas.first]
125
+ when "random"
126
+ [replicas.sample]
127
+ when "all"
128
+ replicas
129
+ end
130
+ end
131
+
132
+ def select_slices(replica, range=0..-1)
133
+ sorted_slices = replica["slices"].sort_by do |slice|
134
+ slice["label"]
135
+ end
136
+ sorted_slices[range]
137
+ end
138
+
139
+ def select_slice(replica, key)
140
+ continuum = replica["continuum"]
141
+ return replica["slices"].first unless continuum
142
+
143
+ hash = Zlib.crc32(key)
144
+ min = 0
145
+ max = continuum.size - 1
146
+ while (min < max) do
147
+ index = (min + max) / 2
148
+ value, key = continuum[index]
149
+ return key if value == hash
150
+ if value > hash
151
+ max = index
152
+ else
153
+ min = index + 1
154
+ end
155
+ end
156
+ continuum[max][1]
157
+ end
158
+ end
159
+ end
160
+ end
@@ -1,4 +1,4 @@
1
- # Copyright (C) 2013 Droonga Project
1
+ # Copyright (C) 2013-2014 Droonga Project
2
2
  #
3
3
  # This library is free software; you can redistribute it and/or
4
4
  # modify it under the terms of the GNU Lesser General Public
@@ -16,6 +16,7 @@
16
16
  require "json"
17
17
 
18
18
  require "droonga/catalog/version1"
19
+ require "droonga/catalog/version2"
19
20
 
20
21
  module Droonga
21
22
  class CatalogLoader
@@ -24,10 +25,32 @@ module Droonga
24
25
  end
25
26
 
26
27
  def load
27
- data = File.open(@path) do |file|
28
- JSON.parse(file.read)
28
+ data = nil
29
+ begin
30
+ data = File.open(@path) do |file|
31
+ JSON.parse(file.read)
32
+ end
33
+ rescue Errno::ENOENT => error
34
+ raise Error.new("Missing catalog file #{@path}")
35
+ rescue JSON::ParserError => error
36
+ raise Error.new("Syntax error in #{@path}:\n#{error.to_s}")
37
+ end
38
+
39
+ unless data.is_a?(Hash)
40
+ raise Error.new("Root element of catalog must be an object in #{@path}")
41
+ end
42
+
43
+ version = data["version"]
44
+ case version
45
+ when 1
46
+ Catalog::Version1.new(data, @path)
47
+ when 2
48
+ Catalog::Version2.new(data, @path)
49
+ when nil
50
+ raise Error.new("Catalog version must be specified in #{@path}")
51
+ else
52
+ raise Error.new("Unsupported catalog version <#{version}> is specified in #{@path}")
29
53
  end
30
- Catalog::Version1.new(data, File.dirname(@path))
31
54
  end
32
55
  end
33
56
  end
@@ -15,15 +15,42 @@
15
15
  # License along with this library; if not, write to the Free Software
16
16
  # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17
17
 
18
+ require "droonga/loggable"
19
+ require "droonga/catalog_loader"
20
+
18
21
  module Droonga
19
22
  class CatalogObserver
23
+ include Loggable
24
+
20
25
  DEFAULT_CATALOG_PATH = "catalog.json"
21
26
  CHECK_INTERVAL = 1
22
27
 
23
- def initialize(loop)
28
+ attr_reader :catalog
29
+ attr_accessor :on_reload
30
+
31
+ def initialize
24
32
  @catalog_path = catalog_path
25
- load_catalog
33
+ load_catalog!
34
+ end
26
35
 
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
45
+ end
46
+ end
47
+
48
+ def stop
49
+ @loop.stop
50
+ @thread.join
51
+ end
52
+
53
+ def attach(loop)
27
54
  watcher = Cool.io::TimerWatcher.new(CHECK_INTERVAL, true)
28
55
  observer = self
29
56
  watcher.on_timer do
@@ -34,7 +61,12 @@ module Droonga
34
61
 
35
62
  def ensure_latest_catalog_loaded
36
63
  if catalog_updated?
37
- load_catalog
64
+ begin
65
+ load_catalog!
66
+ on_reload.call(catalog) if on_reload
67
+ rescue Droonga::Error => error
68
+ logger.warn("reload: fail", :path => @catalog_path, :error => error)
69
+ end
38
70
  end
39
71
  end
40
72
 
@@ -47,11 +79,17 @@ module Droonga
47
79
  File.mtime(catalog_path) > @catalog_mtime
48
80
  end
49
81
 
50
- def load_catalog
82
+ def load_catalog!
51
83
  loader = CatalogLoader.new(@catalog_path)
52
- Droonga.catalog = loader.load
84
+ @catalog = loader.load
85
+ logger.info("loaded", :path => @catalog_path, :mtime => @catalog_mtime)
86
+ ensure
53
87
  @catalog_mtime = File.mtime(@catalog_path)
54
- $log.info "catalog loaded", path: @catalog_path, mtime: @catalog_mtime
88
+ end
89
+
90
+ private
91
+ def log_tag
92
+ "catalog-observer"
55
93
  end
56
94
  end
57
95
  end
@@ -13,24 +13,26 @@
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/legacy_pluggable"
17
- require "droonga/collector_plugin"
16
+ require "droonga/pluggable"
17
+ require "droonga/plugin/metadata/collector_message"
18
+ require "droonga/error_messages"
18
19
 
19
20
  module Droonga
20
21
  class Collector
21
- include LegacyPluggable
22
+ extend Pluggable
23
+ include ErrorMessages
22
24
 
23
- def initialize(plugins)
24
- load_plugins(plugins)
25
+ class << self
26
+ def message
27
+ Plugin::Metadata::CollectorMessage.new(self)
28
+ end
25
29
  end
26
30
 
27
- private
28
- def instantiate_plugin(name)
29
- CollectorPlugin.repository.instantiate(name)
31
+ def initialize
30
32
  end
31
33
 
32
- def log_tag
33
- "collector"
34
+ def collect(message)
35
+ raise NotImplemented, "#{self.class.name}\##{__method__} must implement."
34
36
  end
35
37
  end
36
38
  end
@@ -1,6 +1,4 @@
1
- # -*- coding: utf-8 -*-
2
- #
3
- # Copyright (C) 2013 Droonga Project
1
+ # Copyright (C) 2014 Droonga Project
4
2
  #
5
3
  # This library is free software; you can redistribute it and/or
6
4
  # modify it under the terms of the GNU Lesser General Public
@@ -15,30 +13,59 @@
15
13
  # License along with this library; if not, write to the Free Software
16
14
  # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17
15
 
18
- require "droonga/legacy_plugin"
19
- require "droonga/message_processing_error"
20
-
21
16
  module Droonga
22
- class HandlerPlugin < LegacyPlugin
23
- extend PluginRegisterable
17
+ class CollectorMessage
18
+ attr_reader :raw
19
+ def initialize(raw)
20
+ @raw = raw
21
+ end
22
+
23
+ def valid?
24
+ task and step and values
25
+ end
24
26
 
25
- def initialize(handler)
26
- super()
27
- @handler = handler
28
- @context = @handler.context
27
+ def [](key)
28
+ @raw[key]
29
29
  end
30
30
 
31
- def prefer_synchronous?(command)
32
- false
31
+ def task
32
+ @raw["task"]
33
33
  end
34
34
 
35
- private
36
- def run_command(command, message, messenger)
37
- begin
38
- super
39
- rescue MessageProcessingError => error
40
- messenger.error(error.status_code, error.response_body)
35
+ def step
36
+ task["step"]
37
+ end
38
+
39
+ def type
40
+ step["type"]
41
+ end
42
+
43
+ def values
44
+ task["values"]
45
+ end
46
+
47
+ def body
48
+ step["body"]
49
+ end
50
+
51
+ def input
52
+ if body
53
+ body[name]
54
+ else
55
+ nil
41
56
  end
42
57
  end
58
+
59
+ def outputs
60
+ step["outputs"]
61
+ end
62
+
63
+ def name
64
+ @raw["name"]
65
+ end
66
+
67
+ def value
68
+ @raw["value"]
69
+ end
43
70
  end
44
71
  end
@@ -0,0 +1,64 @@
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/loggable"
17
+ require "droonga/message_matcher"
18
+ require "droonga/collector"
19
+ require "droonga/collector_message"
20
+
21
+ module Droonga
22
+ class CollectorRunner
23
+ include Loggable
24
+
25
+ def initialize(plugins)
26
+ default_plugins = ["basic"]
27
+ plugins += (default_plugins - plugins)
28
+ @collector_classes = Collector.find_sub_classes(plugins)
29
+ end
30
+
31
+ def shutdown
32
+ end
33
+
34
+ def collect(message)
35
+ collector_message = CollectorMessage.new(message)
36
+ logger.trace("collect: start",
37
+ :type => collector_message.type)
38
+ collector_class = find_collector_class(message)
39
+ if collector_class.nil?
40
+ raise UnsupportedMessageError.new(:collector, message)
41
+ end
42
+ collector = collector_class.new
43
+ collector.collect(collector_message)
44
+ logger.trace("collector: done")
45
+ end
46
+
47
+ private
48
+ def find_collector_class(message)
49
+ @collector_classes.find do |collector_class|
50
+ pattern = collector_class.message.pattern
51
+ if pattern
52
+ matcher = MessageMatcher.new(pattern)
53
+ matcher.match?(message)
54
+ else
55
+ false
56
+ end
57
+ end
58
+ end
59
+
60
+ def log_tag
61
+ "collector-runner"
62
+ end
63
+ end
64
+ end