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
@@ -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)