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
@@ -15,6 +15,8 @@
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 "thread"
19
+
18
20
  require "cool.io"
19
21
 
20
22
  require "droonga/loggable"
@@ -28,31 +30,40 @@ module Droonga
28
30
  @loop = loop
29
31
  @host = host
30
32
  @port = port
33
+ @socket = nil
34
+ @buffer = []
35
+ @write_mutex = Mutex.new
31
36
  end
32
37
 
33
38
  def start
34
39
  logger.trace("start: start")
35
- connect
40
+ start_writer
36
41
  logger.trace("start: done")
37
42
  end
38
43
 
39
44
  def shutdown
40
45
  logger.trace("shutdown: start")
41
- @socket.close unless @socket.closed?
46
+ shutdown_writer
47
+ shutdown_socket
42
48
  logger.trace("shutdown: done")
43
49
  end
44
50
 
45
51
  def send(tag, data)
46
52
  logger.trace("send: start")
47
- connect if @socket.closed?
48
53
  fluent_message = [tag, Time.now.to_i, data]
49
54
  packed_fluent_message = MessagePackPacker.pack(fluent_message)
50
- @socket.write(packed_fluent_message)
51
- @loop.break_current_loop
55
+ @write_mutex.synchronize do
56
+ @buffer << packed_fluent_message
57
+ @writer.signal
58
+ end
52
59
  logger.trace("send: done")
53
60
  end
54
61
 
55
62
  private
63
+ def connected?
64
+ not @socket.nil?
65
+ end
66
+
56
67
  def connect
57
68
  logger.trace("connect: start")
58
69
 
@@ -64,6 +75,10 @@ module Droonga
64
75
  end
65
76
  log_failed = lambda do
66
77
  logger.error("failed to connect to #{@host}:#{@port}")
78
+ @socket = nil
79
+ end
80
+ on_close = lambda do
81
+ @socket = nil
67
82
  end
68
83
 
69
84
  @socket = Coolio::TCPSocket.connect(@host, @port)
@@ -76,11 +91,42 @@ module Droonga
76
91
  @socket.on_connect_failed do
77
92
  log_failed.call
78
93
  end
94
+ @socket.on_close do
95
+ on_close.call
96
+ end
79
97
  @loop.attach(@socket)
80
98
 
81
99
  logger.trace("connect: done")
82
100
  end
83
101
 
102
+ def shutdown_socket
103
+ return unless connected?
104
+ @socket.close unless @socket.closed?
105
+ end
106
+
107
+ def start_writer
108
+ @writer = Coolio::AsyncWatcher.new
109
+
110
+ on_signal = lambda do
111
+ @write_mutex.synchronize do
112
+ connect unless connected?
113
+ @buffer.each do |data|
114
+ @socket.write(data)
115
+ end
116
+ @buffer.clear
117
+ end
118
+ end
119
+ @writer.on_signal do
120
+ on_signal.call
121
+ end
122
+
123
+ @loop.attach(@writer)
124
+ end
125
+
126
+ def shutdown_writer
127
+ @writer.detach
128
+ end
129
+
84
130
  def log_tag
85
131
  "[#{Process.ppid}][#{Process.pid}] fluent-message-sender"
86
132
  end
@@ -80,7 +80,7 @@ module Droonga
80
80
  end
81
81
  logger.debug("#{self.class.name}: activating plugins for the dataset \"#{@dataset_name}\": " +
82
82
  "#{@options[:plugins].join(", ")}")
83
- @step_runner = StepRunner.new(@options[:plugins] || [])
83
+ @step_runner = StepRunner.new(nil, @options[:plugins] || [])
84
84
  @forwarder = Forwarder.new(@loop)
85
85
  end
86
86
 
@@ -0,0 +1,20 @@
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 JobProtocol
18
+ READY_SIGNAL = "R"
19
+ end
20
+ end
@@ -0,0 +1,178 @@
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 "msgpack"
17
+
18
+ require "droonga/logger"
19
+ require "droonga/job_protocol"
20
+
21
+ module Droonga
22
+ class JobPusher
23
+ include Loggable
24
+
25
+ attr_reader :socket_path
26
+ def initialize(loop, base_path)
27
+ @loop = loop
28
+ @socket_path = "#{base_path}.sock"
29
+ @job_queue = JobQueue.new(@loop)
30
+ end
31
+
32
+ def start
33
+ FileUtils.rm_f(@socket_path)
34
+ @server = Coolio::UNIXServer.new(@socket_path) do |connection|
35
+ @job_queue.add_worker(WorkerConnection.new(connection))
36
+ end
37
+ FileUtils.chmod(0600, @socket_path)
38
+ @loop.attach(@server)
39
+ end
40
+
41
+ def close
42
+ @server.close
43
+ end
44
+
45
+ def shutdown
46
+ logger.trace("shutdown: start")
47
+ @server.close
48
+ @job_queue.close
49
+ FileUtils.rm_f(@socket_path)
50
+ logger.trace("shutdown: done")
51
+ end
52
+
53
+ def push(message)
54
+ logger.trace("push: start")
55
+ @job_queue.push(message)
56
+ logger.trace("push: done")
57
+ end
58
+
59
+ private
60
+ def log_tag
61
+ "job_pusher"
62
+ end
63
+
64
+ class JobQueue
65
+ include Loggable
66
+
67
+ def initialize(loop)
68
+ @loop = loop
69
+ @buffers = []
70
+ @ready_workers = []
71
+ @workers = []
72
+ @many_jobs_report_interval = 100
73
+ update_many_jobs_threshold
74
+ end
75
+
76
+ def close
77
+ @workers.each do |worker|
78
+ worker.close
79
+ end
80
+ end
81
+
82
+ def add_worker(worker)
83
+ @workers << worker
84
+ update_many_jobs_threshold
85
+ worker.on_ready = lambda do |ready_worker|
86
+ supply_job(ready_worker)
87
+ end
88
+ end
89
+
90
+ def push(message)
91
+ job = message.to_msgpack
92
+ if @ready_workers.empty?
93
+ @buffers << job
94
+ report_statistics_on_push
95
+ else
96
+ worker = @ready_workers.shift
97
+ if @buffers.empty?
98
+ worker.write(job)
99
+ else
100
+ @buffers << job
101
+ worker.write(@buffers.shift)
102
+ end
103
+ end
104
+ end
105
+
106
+ private
107
+ def supply_job(worker)
108
+ if @buffers.empty?
109
+ @ready_workers << worker
110
+ else
111
+ worker.write(@buffers.shift)
112
+ report_statistics_on_pull
113
+ end
114
+ end
115
+
116
+ def update_many_jobs_threshold
117
+ @many_jobs_threshold = @workers.size * 100
118
+ end
119
+
120
+ def report_statistics_on_push
121
+ if @buffers.size >= @many_jobs_threshold
122
+ if (@buffers.size % @many_jobs_report_interval).zero?
123
+ logger.warn("push: many jobs in queue: #{@buffers.size}")
124
+ end
125
+ end
126
+ end
127
+
128
+ def report_statistics_on_pull
129
+ if @buffers.size >= @many_jobs_threshold
130
+ if (@buffers.size % @many_jobs_report_interval).zero?
131
+ logger.info("pull: many jobs in queue: #{@buffers.size}")
132
+ end
133
+ elsif @buffers.size == (@many_jobs_threshold - 1)
134
+ logger.info("pull: reducing jobs in queue: #{@buffers.size}")
135
+ end
136
+ end
137
+
138
+ def log_tag
139
+ "job_queue"
140
+ end
141
+ end
142
+
143
+ class WorkerConnection
144
+ attr_writer :on_ready
145
+
146
+ def initialize(connection)
147
+ @connection = connection
148
+ @ready = false
149
+ @on_ready = nil
150
+ setup_connection
151
+ end
152
+
153
+ def ready?
154
+ @ready
155
+ end
156
+
157
+ def write(job)
158
+ @connection.write(job)
159
+ @ready = false
160
+ end
161
+
162
+ def close
163
+ @connection.close
164
+ end
165
+
166
+ private
167
+ def setup_connection
168
+ on_read = lambda do |data|
169
+ @ready = (data == JobProtocol::READY_SIGNAL)
170
+ @on_ready.call(self) if @on_ready
171
+ end
172
+ @connection.on_read do |data|
173
+ on_read.call(data)
174
+ end
175
+ end
176
+ end
177
+ end
178
+ end
@@ -16,21 +16,22 @@
16
16
  require "msgpack"
17
17
 
18
18
  require "droonga/loggable"
19
+ require "droonga/job_protocol"
19
20
 
20
21
  module Droonga
21
- class MessageReceiver
22
+ class JobReceiver
22
23
  include Loggable
23
24
 
24
- def initialize(loop, receiver, &callback)
25
+ def initialize(loop, socket_path, &callback)
25
26
  @loop = loop
26
- @receiver = Coolio::Server.new(receiver, Coolio::Socket) do |connection|
27
- setup_receive_handler(connection)
28
- end
27
+ @socket_path = socket_path
29
28
  @callback = callback
30
29
  end
31
30
 
32
31
  def start
33
32
  logger.trace("start: start")
33
+ @receiver = Coolio::UNIXSocket.connect(@socket_path)
34
+ setup_receive_handler(@receiver)
34
35
  @loop.attach(@receiver)
35
36
  logger.trace("start: done")
36
37
  end
@@ -50,14 +51,20 @@ module Droonga
50
51
  @callback.call(message)
51
52
  end
52
53
  logger.trace("on_read: done")
54
+ send_ready(connection)
53
55
  end
54
56
  connection.on_read do |data|
55
57
  on_read.call(data)
56
58
  end
59
+ send_ready(connection)
60
+ end
61
+
62
+ def send_ready(connection)
63
+ connection.write(JobProtocol::READY_SIGNAL)
57
64
  end
58
65
 
59
66
  def log_tag
60
- "message_receiver"
67
+ "job_receiver"
61
68
  end
62
69
  end
63
70
  end
@@ -59,47 +59,50 @@ module Droonga
59
59
  class MessageMatcher
60
60
  # @param [Array] pattern The pattern to be matched against a message.
61
61
  def initialize(pattern)
62
- @pattern = pattern
62
+ path, operator, *arguments = pattern
63
+ @path_components = path.split(".")
64
+ @operator = operator
65
+ @arguments = arguments
63
66
  end
64
67
 
65
68
  def match?(message)
66
- return false if @pattern.nil?
67
- path, operator, *arguments = @pattern
68
- target = resolve_path(path, message)
69
- apply_operator(operator, target, arguments)
69
+ target = extract_target(message)
70
+ apply_operator(target)
70
71
  end
71
72
 
72
73
  private
73
74
  NONEXISTENT_PATH = Object.new
74
- def resolve_path(path, message)
75
- path.split(".").inject(message) do |result, component|
75
+ def extract_target(message)
76
+ result = message
77
+ @path_components.each do |component|
76
78
  return NONEXISTENT_PATH unless result.is_a?(Hash)
77
- result[component]
79
+ result = result[component]
78
80
  end
81
+ result
79
82
  end
80
83
 
81
- def apply_operator(operator, target, arguments)
82
- case operator
84
+ def apply_operator(target)
85
+ case @operator
83
86
  when :equal
84
- [target] == arguments
87
+ [target] == @arguments
85
88
  when :in
86
- arguments.any? do |argument|
89
+ @arguments.any? do |argument|
87
90
  argument.include?(target)
88
91
  end
89
92
  when :include
90
93
  return false unless target.respond_to?(:include?)
91
- arguments.any? do |argument|
94
+ @arguments.any? do |argument|
92
95
  target.include?(argument)
93
96
  end
94
97
  when :exist
95
98
  target != NONEXISTENT_PATH
96
99
  when :start_with
97
100
  return false unless target.respond_to?(:start_with?)
98
- arguments.any? do |argument|
101
+ @arguments.any? do |argument|
99
102
  target.start_with?(argument)
100
103
  end
101
104
  else
102
- raise ArgumentError, "Unknown operator: <#{operator}>"
105
+ raise ArgumentError, "Unknown operator: <#{@operator}>"
103
106
  end
104
107
  end
105
108
  end
@@ -30,10 +30,9 @@ module Droonga
30
30
  end
31
31
 
32
32
  private
33
- def scatter(message, options={})
33
+ def scatter(message, record, options={})
34
34
  planner = DistributedCommandPlanner.new(message)
35
- planner.scatter
36
- planner.key = options[:key]
35
+ planner.scatter(record)
37
36
  planner.reduce(options[:reduce])
38
37
  planner.plan
39
38
  end
@@ -32,7 +32,7 @@ module Droonga
32
32
  request = input_message.body
33
33
  key = request["key"] || rand.to_s
34
34
  values = request["values"] || {}
35
- request["filter"] = values.merge("key" => key)
35
+ request["filter"] = values.merge("_key" => key)
36
36
  end
37
37
 
38
38
  def adapt_output(output_message)