droonga-engine 1.0.4 → 1.0.5

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 (216) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +3 -2
  3. data/Gemfile +1 -1
  4. data/LICENSE.txt +1 -1
  5. data/Rakefile +1 -1
  6. data/benchmark/benchmark.rb +1 -1
  7. data/benchmark/utils.rb +1 -1
  8. data/benchmark/watch/benchmark-notify.rb +1 -1
  9. data/benchmark/watch/benchmark-publish.rb +1 -1
  10. data/benchmark/watch/benchmark-scan.rb +1 -1
  11. data/bin/droonga-engine +1 -1
  12. data/bin/droonga-engine-absorb-data +48 -8
  13. data/bin/droonga-engine-catalog-generate +1 -1
  14. data/bin/droonga-engine-catalog-modify +1 -1
  15. data/bin/droonga-engine-data-publisher +66 -0
  16. data/bin/droonga-engine-join +72 -17
  17. data/bin/droonga-engine-serf-event-handler +1 -1
  18. data/bin/droonga-engine-service +1 -1
  19. data/bin/droonga-engine-unjoin +11 -4
  20. data/bin/droonga-engine-worker +20 -0
  21. data/doc/text/news.md +8 -0
  22. data/droonga-engine.gemspec +3 -3
  23. data/lib/droonga/adapter.rb +1 -1
  24. data/lib/droonga/adapter_runner.rb +1 -1
  25. data/lib/droonga/address.rb +69 -0
  26. data/lib/droonga/buffered_tcp_socket.rb +44 -22
  27. data/lib/droonga/catalog/base.rb +1 -1
  28. data/lib/droonga/catalog/collection_volume.rb +1 -1
  29. data/lib/droonga/catalog/dataset.rb +8 -8
  30. data/lib/droonga/catalog/errors.rb +1 -1
  31. data/lib/droonga/catalog/schema.rb +1 -1
  32. data/lib/droonga/catalog/single_volume.rb +6 -8
  33. data/lib/droonga/catalog/slice.rb +1 -1
  34. data/lib/droonga/catalog/version1.rb +2 -2
  35. data/lib/droonga/catalog/version2.rb +6 -6
  36. data/lib/droonga/catalog/version2_validator.rb +1 -1
  37. data/lib/droonga/catalog/volume.rb +1 -1
  38. data/lib/droonga/catalog/volume_collection.rb +2 -2
  39. data/lib/droonga/catalog_generator.rb +49 -53
  40. data/lib/droonga/catalog_loader.rb +1 -1
  41. data/lib/droonga/collector.rb +1 -1
  42. data/lib/droonga/collector_message.rb +1 -1
  43. data/lib/droonga/collector_runner.rb +1 -1
  44. data/lib/droonga/collectors/and.rb +1 -1
  45. data/lib/droonga/collectors/or.rb +1 -1
  46. data/lib/droonga/collectors/sum.rb +1 -1
  47. data/lib/droonga/collectors.rb +1 -1
  48. data/lib/droonga/command/droonga_engine.rb +103 -55
  49. data/lib/droonga/command/droonga_engine_service.rb +22 -67
  50. data/lib/droonga/command/droonga_engine_worker.rb +232 -0
  51. data/lib/droonga/command/serf_event_handler.rb +126 -46
  52. data/lib/droonga/data_absorber.rb +32 -14
  53. data/lib/droonga/dispatcher.rb +15 -11
  54. data/lib/droonga/distributed_command_planner.rb +1 -1
  55. data/lib/droonga/distributor.rb +1 -1
  56. data/lib/droonga/engine/version.rb +2 -2
  57. data/lib/droonga/engine.rb +8 -3
  58. data/lib/droonga/engine_state.rb +15 -6
  59. data/lib/droonga/error.rb +1 -1
  60. data/lib/droonga/error_messages.rb +1 -1
  61. data/lib/droonga/event_loop.rb +1 -1
  62. data/lib/droonga/farm.rb +9 -1
  63. data/lib/droonga/file_observer.rb +1 -1
  64. data/lib/droonga/fluent_message_receiver.rb +11 -5
  65. data/lib/droonga/fluent_message_sender.rb +14 -17
  66. data/lib/droonga/forwarder.rb +23 -13
  67. data/lib/droonga/handler.rb +1 -1
  68. data/lib/droonga/handler_message.rb +1 -1
  69. data/lib/droonga/handler_messenger.rb +2 -2
  70. data/lib/droonga/handler_runner.rb +2 -2
  71. data/lib/droonga/input_message.rb +1 -1
  72. data/lib/droonga/internal_fluent_message_receiver.rb +3 -2
  73. data/lib/droonga/job_protocol.rb +1 -1
  74. data/lib/droonga/job_pusher.rb +1 -1
  75. data/lib/droonga/job_receiver.rb +1 -1
  76. data/lib/droonga/line_buffer.rb +1 -1
  77. data/lib/droonga/live_nodes_list_loader.rb +1 -1
  78. data/lib/droonga/loggable.rb +1 -1
  79. data/lib/droonga/logger.rb +3 -3
  80. data/lib/droonga/message_matcher.rb +1 -1
  81. data/lib/droonga/output_message.rb +1 -1
  82. data/lib/droonga/path.rb +5 -1
  83. data/lib/droonga/planner.rb +1 -1
  84. data/lib/droonga/pluggable.rb +1 -1
  85. data/lib/droonga/plugin/metadata/adapter_input_message.rb +1 -1
  86. data/lib/droonga/plugin/metadata/adapter_output_message.rb +1 -1
  87. data/lib/droonga/plugin/metadata/collector_message.rb +1 -1
  88. data/lib/droonga/plugin/metadata/handler_action.rb +1 -1
  89. data/lib/droonga/plugin/metadata/input_message.rb +1 -1
  90. data/lib/droonga/plugin.rb +2 -1
  91. data/lib/droonga/plugin_loader.rb +1 -1
  92. data/lib/droonga/plugin_registry.rb +3 -1
  93. data/lib/droonga/plugins/basic.rb +1 -1
  94. data/lib/droonga/plugins/crud.rb +1 -1
  95. data/lib/droonga/plugins/dump.rb +13 -2
  96. data/lib/droonga/plugins/error.rb +1 -1
  97. data/lib/droonga/plugins/groonga/column_create.rb +1 -1
  98. data/lib/droonga/plugins/groonga/column_list.rb +1 -1
  99. data/lib/droonga/plugins/groonga/column_remove.rb +1 -1
  100. data/lib/droonga/plugins/groonga/column_rename.rb +1 -1
  101. data/lib/droonga/plugins/groonga/delete.rb +1 -1
  102. data/lib/droonga/plugins/groonga/generic_command.rb +1 -1
  103. data/lib/droonga/plugins/groonga/generic_response.rb +1 -1
  104. data/lib/droonga/plugins/groonga/select.rb +1 -1
  105. data/lib/droonga/plugins/groonga/table_create.rb +1 -1
  106. data/lib/droonga/plugins/groonga/table_list.rb +1 -1
  107. data/lib/droonga/plugins/groonga/table_remove.rb +1 -1
  108. data/lib/droonga/plugins/groonga.rb +1 -1
  109. data/lib/droonga/plugins/search/distributed_search_planner.rb +1 -1
  110. data/lib/droonga/plugins/search.rb +1 -1
  111. data/lib/droonga/plugins/system.rb +1 -1
  112. data/lib/droonga/plugins/watch.rb +1 -1
  113. data/lib/droonga/{service_control_protocol.rb → process_control_protocol.rb} +2 -2
  114. data/lib/droonga/process_supervisor.rb +91 -0
  115. data/lib/droonga/processor.rb +1 -1
  116. data/lib/droonga/reducer.rb +1 -1
  117. data/lib/droonga/replier.rb +2 -2
  118. data/lib/droonga/safe_file_writer.rb +1 -1
  119. data/lib/droonga/schema_applier.rb +1 -1
  120. data/lib/droonga/searcher/mecab_filter.rb +1 -1
  121. data/lib/droonga/searcher.rb +31 -19
  122. data/lib/droonga/serf.rb +81 -14
  123. data/lib/droonga/serf_downloader.rb +2 -2
  124. data/lib/droonga/session.rb +1 -1
  125. data/lib/droonga/single_step.rb +1 -1
  126. data/lib/droonga/single_step_definition.rb +1 -1
  127. data/lib/droonga/slice.rb +30 -28
  128. data/lib/droonga/status_code.rb +1 -1
  129. data/lib/droonga/step_runner.rb +1 -1
  130. data/lib/droonga/supervisor.rb +170 -0
  131. data/lib/droonga/sweeper.rb +1 -1
  132. data/lib/droonga/test/stub_handler.rb +1 -1
  133. data/lib/droonga/test/stub_handler_message.rb +1 -1
  134. data/lib/droonga/test/stub_handler_messenger.rb +1 -1
  135. data/lib/droonga/test/stub_planner.rb +1 -1
  136. data/lib/droonga/test.rb +1 -1
  137. data/lib/droonga/watch_schema.rb +2 -2
  138. data/lib/droonga/watcher.rb +1 -1
  139. data/lib/droonga/worker_process_agent.rb +111 -0
  140. data/sample/cluster/Rakefile +150 -0
  141. data/test/command/config/default/catalog.json +1 -34
  142. data/test/command/config/version1/catalog.json +3 -12
  143. data/test/command/run-test.rb +1 -1
  144. data/test/command/suite/dump/column/index.expected +19 -82
  145. data/test/command/suite/dump/column/scalar.expected +5 -36
  146. data/test/command/suite/dump/column/vector.expected +5 -39
  147. data/test/command/suite/dump/record/vector/reference.expected +24 -93
  148. data/test/command/suite/dump/table/array.expected +0 -19
  149. data/test/command/suite/dump/table/double_array_trie.expected +0 -20
  150. data/test/command/suite/dump/table/hash.expected +0 -20
  151. data/test/command/suite/dump/table/patricia_trie.expected +0 -20
  152. data/test/command/suite/search/condition/query/nonexistent_column.expected +0 -11
  153. data/test/command/suite/search/condition/query/syntax_error.expected +0 -11
  154. data/test/command/suite/search/error/unknown-source.expected +0 -12
  155. data/test/command/suite/search/output/attributes/invalid.expected +0 -10
  156. data/test/command/suite/search/output/attributes/reference_vector.catalog.json +27 -0
  157. data/test/command/suite/search/output/attributes/reference_vector.expected +30 -0
  158. data/test/command/suite/search/output/attributes/reference_vector.test +32 -0
  159. data/test/command/suite/watch/subscribe.catalog.json +23 -0
  160. data/test/command/suite/watch/subscribe.test +2 -0
  161. data/test/command/suite/watch/unsubscribe.catalog.json +23 -0
  162. data/test/command/suite/watch/unsubscribe.test +2 -0
  163. data/test/performance/run-test.rb +1 -1
  164. data/test/unit/catalog/test_collection_volume.rb +1 -1
  165. data/test/unit/catalog/test_dataset.rb +1 -1
  166. data/test/unit/catalog/test_schema.rb +1 -1
  167. data/test/unit/catalog/test_single_volume.rb +27 -19
  168. data/test/unit/catalog/test_slice.rb +2 -2
  169. data/test/unit/catalog/test_version1.rb +1 -1
  170. data/test/unit/catalog/test_version2.rb +1 -1
  171. data/test/unit/catalog/test_version2_validator.rb +1 -1
  172. data/test/unit/catalog/test_volume_collection.rb +1 -1
  173. data/test/unit/helper/distributed_search_planner_helper.rb +1 -1
  174. data/test/unit/helper/fixture.rb +1 -1
  175. data/test/unit/helper/plugin_helper.rb +1 -1
  176. data/test/unit/helper/sandbox.rb +1 -1
  177. data/test/unit/helper/stub_worker.rb +1 -1
  178. data/test/unit/helper/watch_helper.rb +1 -1
  179. data/test/unit/helper.rb +1 -1
  180. data/test/unit/plugins/crud/test_add.rb +1 -1
  181. data/test/unit/plugins/groonga/select/test_adapter_input.rb +1 -1
  182. data/test/unit/plugins/groonga/select/test_adapter_output.rb +1 -1
  183. data/test/unit/plugins/groonga/test_column_create.rb +1 -1
  184. data/test/unit/plugins/groonga/test_column_list.rb +1 -1
  185. data/test/unit/plugins/groonga/test_column_remove.rb +1 -1
  186. data/test/unit/plugins/groonga/test_column_rename.rb +1 -1
  187. data/test/unit/plugins/groonga/test_delete.rb +1 -1
  188. data/test/unit/plugins/groonga/test_table_create.rb +1 -1
  189. data/test/unit/plugins/groonga/test_table_list.rb +1 -1
  190. data/test/unit/plugins/groonga/test_table_remove.rb +1 -1
  191. data/test/unit/plugins/search/planner/test_basic.rb +1 -1
  192. data/test/unit/plugins/search/planner/test_group_by.rb +1 -1
  193. data/test/unit/plugins/search/planner/test_output.rb +1 -1
  194. data/test/unit/plugins/search/planner/test_sort_by.rb +1 -1
  195. data/test/unit/plugins/search/test_collector.rb +1 -1
  196. data/test/unit/plugins/search/test_handler.rb +1 -1
  197. data/test/unit/plugins/search/test_planner.rb +1 -1
  198. data/test/unit/plugins/system/test_status.rb +1 -1
  199. data/test/unit/plugins/test_basic.rb +1 -1
  200. data/test/unit/plugins/test_groonga.rb +1 -1
  201. data/test/unit/plugins/test_watch.rb +1 -1
  202. data/test/unit/run-test.rb +1 -1
  203. data/test/unit/test_address.rb +53 -0
  204. data/test/unit/test_catalog_generator.rb +59 -1
  205. data/test/unit/test_line_buffer.rb +1 -1
  206. data/test/unit/test_message_matcher.rb +1 -1
  207. data/test/unit/test_schema_applier.rb +1 -1
  208. data/test/unit/test_sweeper.rb +1 -1
  209. data/test/unit/test_watch_schema.rb +1 -1
  210. data/test/unit/test_watcher.rb +1 -1
  211. metadata +39 -24
  212. data/lib/droonga/server.rb +0 -45
  213. data/lib/droonga/worker.rb +0 -66
  214. data/sample/cluster/catalog.json +0 -42
  215. data/test/command/config/default/fluentd.conf +0 -11
  216. data/test/command/config/version1/fluentd.conf +0 -11
@@ -11,7 +11,7 @@
11
11
  #
12
12
  # You should have received a copy of the GNU Lesser General Public
13
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
14
+ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
15
15
 
16
16
  require "json"
17
17
 
@@ -34,14 +34,22 @@ module Droonga
34
34
  @serf = ENV["SERF"] || Serf.path
35
35
  @serf_rpc_address = ENV["SERF_RPC_ADDRESS"] || "127.0.0.1:7373"
36
36
  @serf_name = ENV["SERF_SELF_NAME"]
37
+ @response = {
38
+ "log" => []
39
+ }
37
40
  end
38
41
 
39
42
  def run
40
43
  parse_event
41
- return true unless event_for_me?
44
+ unless event_for_me?
45
+ log(" => ignoring event not for me")
46
+ output_response
47
+ return true
48
+ end
42
49
 
43
50
  process_event
44
51
  output_live_nodes
52
+ output_response
45
53
  true
46
54
  end
47
55
 
@@ -53,11 +61,13 @@ module Droonga
53
61
  when "user"
54
62
  @event_sub_name = ENV["SERF_USER_EVENT"]
55
63
  @payload = JSON.parse($stdin.gets)
56
- puts "event sub name = #{@event_sub_name}"
64
+ log("event sub name = #{@event_sub_name}")
57
65
  when "query"
58
66
  @event_sub_name = ENV["SERF_QUERY_NAME"]
59
67
  @payload = JSON.parse($stdin.gets)
60
- puts "event sub name = #{@event_sub_name}"
68
+ log("event sub name = #{@event_sub_name}")
69
+ when "member-join", "member-leave", "member-update", "member-reap"
70
+ output_live_nodes
61
71
  end
62
72
  end
63
73
 
@@ -72,6 +82,8 @@ module Droonga
72
82
  case @event_sub_name
73
83
  when "change_role"
74
84
  save_status(:role, @payload["role"])
85
+ when "report_status"
86
+ report_status
75
87
  when "join"
76
88
  join
77
89
  when "set_replicas"
@@ -82,9 +94,17 @@ module Droonga
82
94
  remove_replicas
83
95
  when "absorb_data"
84
96
  absorb_data
97
+ when "publish_catalog"
98
+ publish_catalog
99
+ when "unpublish_catalog"
100
+ unpublish_catalog
85
101
  end
86
102
  end
87
103
 
104
+ def output_response
105
+ puts JSON.generate(@response)
106
+ end
107
+
88
108
  def host
89
109
  @serf_name.split(":").first
90
110
  end
@@ -96,9 +116,13 @@ module Droonga
96
116
  hosts
97
117
  end
98
118
 
119
+ def report_status
120
+ @response["value"] = status(@payload["key"].to_sym)
121
+ end
122
+
99
123
  def join
100
124
  type = @payload["type"]
101
- puts "type = #{type}"
125
+ log("type = #{type}")
102
126
  case type
103
127
  when "replica"
104
128
  join_as_replica
@@ -106,55 +130,106 @@ module Droonga
106
130
  end
107
131
 
108
132
  def join_as_replica
109
- source = @payload["source"]
110
- return unless source
133
+ source_node = @payload["source"]
134
+ return unless source_node
111
135
 
112
- puts "source = #{source}"
136
+ log("source_node = #{source_node}")
113
137
 
114
- generator = create_current_catalog_generator
115
- dataset = generator.dataset_for_host(source) ||
138
+ source_host = source_node.split(":").first
139
+
140
+ # fetch_port = @payload["fetch_port"]
141
+ # catalog = fetch_catalog(source_node, fetch_port)
142
+ catalog = JSON.parse(Path.catalog.read) #XXX workaround until "fetch" become available
143
+
144
+ generator = create_current_catalog_generator(catalog)
145
+ dataset = generator.dataset_for_host(source_host) ||
116
146
  generator.dataset_for_host(host)
117
147
  return unless dataset
118
148
 
149
+ # restart self with the fetched catalog.
150
+ SafeFileWriter.write(Path.catalog, JSON.pretty_generate(catalog))
151
+
119
152
  dataset_name = dataset.name
120
153
  tag = dataset.replicas.tag
121
154
  port = dataset.replicas.port
122
155
  other_hosts = dataset.replicas.hosts
123
156
 
124
- puts "dataset = #{dataset_name}"
125
- puts "port = #{port}"
126
- puts "tag = #{tag}"
157
+ log("dataset = #{dataset_name}")
158
+ log("port = #{port}")
159
+ log("tag = #{tag}")
127
160
 
128
161
  if @payload["copy"]
129
- puts "starting to copy data from #{source}"
162
+ log("starting to copy data from #{source_host}")
130
163
 
131
164
  modify_catalog do |modifier|
132
165
  modifier.datasets[dataset_name].replicas.hosts = [host]
133
166
  end
134
- sleep(1) # wait for restart
167
+ sleep(5) #TODO: wait for restart. this should be done more safely, to avoid starting of absorbing with old catalog.json.
135
168
 
169
+ save_status(:absorbing, true)
136
170
  DataAbsorber.absorb(:dataset => dataset_name,
137
- :source_host => source,
171
+ :source_host => source_host,
138
172
  :destination_host => host,
139
173
  :port => port,
140
174
  :tag => tag)
175
+ delete_status(:absorbing)
141
176
  sleep(1)
142
177
  end
143
178
 
144
- puts "joining to the cluster: update myself"
179
+ log("joining to the cluster: update myself")
145
180
 
146
181
  modify_catalog do |modifier|
147
182
  modifier.datasets[dataset_name].replicas.hosts += other_hosts
148
183
  modifier.datasets[dataset_name].replicas.hosts.uniq!
149
184
  end
150
- sleep(1) # wait for restart
185
+ end
186
+
187
+ def fetch_catalog(source_node, port)
188
+ source_host = source_node.split(":").first
189
+
190
+ url = "http://#{source_host}:#{port}"
191
+ connection = Faraday.new(url) do |builder|
192
+ builder.response(:follow_redirects)
193
+ builder.adapter(Faraday.default_adapter)
194
+ end
195
+ response = connection.get("/catalog.json")
196
+ catalog = response.body
197
+
198
+ JSON.parse(catalog)
199
+ end
200
+
201
+ def publish_catalog
202
+ port = @payload["port"]
203
+ return unless port
204
+
205
+ env = {}
206
+ publisher_command_line = [
207
+ "droonga-engine-data-publisher",
208
+ "--base-dir", Path.base.to_s,
209
+ "--port", port.to_s,
210
+ "--published-file", Path.catalog.to_s
211
+ ]
212
+ pid = spawn(env, *publisher_command_line)
213
+ Process.detach(pid)
214
+ sleep(1) # wait until the directory is published
215
+
216
+ published_dir = Path.published(port)
217
+ pid_file = published_dir + ".pid"
218
+
219
+ File.open(pid_file.to_s, "w") do |file|
220
+ file.puts(pid)
221
+ end
222
+ end
151
223
 
152
- puts "joining to the cluster: update others"
224
+ def unpublish_catalog
225
+ port = @payload["port"]
226
+ return unless port
153
227
 
154
- source_node = "#{source}:#{port}/#{tag}"
155
- Serf.send_query(source_node, "add_replicas",
156
- "dataset" => dataset_name,
157
- "hosts" => [host])
228
+ published_dir = Path.published(port)
229
+ pid_file = published_dir + ".pid"
230
+ pid = pid_file.read.to_i
231
+
232
+ Process.kill("INT", pid)
158
233
  end
159
234
 
160
235
  def set_replicas
@@ -164,7 +239,7 @@ module Droonga
164
239
  hosts = given_hosts
165
240
  return unless hosts
166
241
 
167
- puts "new replicas: #{hosts.join(",")}"
242
+ log("new replicas: #{hosts.join(",")}")
168
243
 
169
244
  modify_catalog do |modifier|
170
245
  modifier.datasets[dataset].replicas.hosts = hosts
@@ -181,7 +256,7 @@ module Droonga
181
256
  hosts -= [host]
182
257
  return if hosts.empty?
183
258
 
184
- puts "adding replicas: #{hosts.join(",")}"
259
+ log("adding replicas: #{hosts.join(",")}")
185
260
 
186
261
  modify_catalog do |modifier|
187
262
  modifier.datasets[dataset].replicas.hosts += hosts
@@ -196,7 +271,7 @@ module Droonga
196
271
  hosts = given_hosts
197
272
  return unless hosts
198
273
 
199
- puts "removing replicas: #{hosts.join(",")}"
274
+ log("removing replicas: #{hosts.join(",")}")
200
275
 
201
276
  modify_catalog do |modifier|
202
277
  modifier.datasets[dataset].replicas.hosts -= hosts
@@ -209,19 +284,17 @@ module Droonga
209
284
  SafeFileWriter.write(Path.catalog, JSON.pretty_generate(generator.generate))
210
285
  end
211
286
 
212
- def create_current_catalog_generator
213
- current_catalog = JSON.parse(Path.catalog.read)
287
+ def create_current_catalog_generator(current_catalog=nil)
288
+ current_catalog ||= JSON.parse(Path.catalog.read)
214
289
  generator = CatalogGenerator.new
215
290
  generator.load(current_catalog)
216
291
  end
217
292
 
218
293
  def absorb_data
219
- return unless event_for_me?
220
-
221
294
  source = @payload["source"]
222
295
  return unless source
223
296
 
224
- puts "start to absorb data from #{source}"
297
+ log("start to absorb data from #{source}")
225
298
 
226
299
  dataset_name = @payload["dataset"]
227
300
  port = @payload["port"]
@@ -240,29 +313,22 @@ module Droonga
240
313
  tag = dataset.replicas.tag
241
314
  end
242
315
 
243
- puts "dataset = #{dataset_name}"
244
- puts "port = #{port}"
245
- puts "tag = #{tag}"
316
+ log("dataset = #{dataset_name}")
317
+ log("port = #{port}")
318
+ log("tag = #{tag}")
246
319
 
320
+ save_status(:absorbing, true)
247
321
  DataAbsorber.absorb(:dataset => dataset_name,
248
322
  :source_host => source,
249
323
  :destination_host => host,
250
324
  :port => port,
251
- :tag => tag)
325
+ :tag => tag,
326
+ :client => "droonga-send")
327
+ delete_status(:absorbing)
252
328
  end
253
329
 
254
330
  def live_nodes
255
- nodes = {}
256
- members = `#{@serf} members -rpc-addr #{@serf_rpc_address}`
257
- members.each_line do |member|
258
- name, address, status, = member.strip.split(/\s+/)
259
- if status == "alive"
260
- nodes[name] = {
261
- "serfAddress" => address,
262
- }
263
- end
264
- end
265
- nodes
331
+ Serf.live_nodes(@serf_name)
266
332
  end
267
333
 
268
334
  def output_live_nodes
@@ -272,11 +338,25 @@ module Droonga
272
338
  SafeFileWriter.write(path, file_contents)
273
339
  end
274
340
 
341
+ def status(key)
342
+ Serf.status(key)
343
+ end
344
+
275
345
  def save_status(key, value)
276
346
  status = Serf.load_status
277
347
  status[key] = value
278
348
  SafeFileWriter.write(Serf.status_file, JSON.pretty_generate(status))
279
349
  end
350
+
351
+ def delete_status(key)
352
+ status = Serf.load_status
353
+ status.delete(key)
354
+ SafeFileWriter.write(Serf.status_file, JSON.pretty_generate(status))
355
+ end
356
+
357
+ def log(message)
358
+ @response["log"] << message
359
+ end
280
360
  end
281
361
  end
282
362
  end
@@ -11,7 +11,7 @@
11
11
  #
12
12
  # You should have received a copy of the GNU Lesser General Public
13
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
14
+ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
15
15
 
16
16
  require "open3"
17
17
 
@@ -28,25 +28,43 @@ module Droonga
28
28
  drndump_options += ["--receiver-host", params[:destination_host]]
29
29
  drndump_options += ["--receiver-port", params[:receiver_port].to_s] if params[:receiver_port]
30
30
 
31
+ #TODO: We should use droonga-send instead of droonga-request,
32
+ # because droonga-request is too slow.
33
+ # However, to do it, we have to implement an API to know
34
+ # that all messages sent by droonga-send are completely
35
+ # processed.
31
36
  client = params[:client] || "droonga-request"
32
37
  client_options = []
33
- client_options += ["--host", params[:destination_host]]
34
- client_options += ["--port", params[:port].to_s] if params[:port]
35
- client_options += ["--tag", params[:tag]] if params[:tag]
36
- client_options += ["--receiver-host", params[:destination_host]]
37
- client_options += ["--receiver-port", params[:receiver_port].to_s] if params[:receiver_port]
38
+ if client.include?("droonga-request")
39
+ client_options += ["--host", params[:destination_host]]
40
+ client_options += ["--port", params[:port].to_s] if params[:port]
41
+ client_options += ["--tag", params[:tag]] if params[:tag]
42
+ client_options += ["--receiver-host", params[:destination_host]]
43
+ client_options += ["--receiver-port", params[:receiver_port].to_s] if params[:receiver_port]
44
+ elsif client.include?("droonga-send")
45
+ #XXX Don't use round-robin with multiple endpoints
46
+ # even if there are too much data.
47
+ # Schema and indexes must be sent to just one endpoint
48
+ # to keep their order, but currently there is no way to
49
+ # extract only schema and indexes via drndump.
50
+ # So, we always use just one endpoint for now,
51
+ # even if there are too much data.
52
+ server = "droonga:#{params[:destination_host]}"
53
+ server = "#{server}:#{params[:port].to_s}" if params[:port]
54
+ server = "#{server}/#{params[:tag].to_s}" if params[:tag]
55
+ client_options += ["--server", server]
56
+ else
57
+ raise ArgumentError.new("Unknwon type client: #{client}")
58
+ end
38
59
 
39
60
  drndump_command_line = [drndump] + drndump_options
40
61
  client_command_line = [client] + client_options
41
62
 
42
- Open3.popen3(*drndump_command_line) do |dump_in, dump_out, dump_error, dump_thread|
43
- dump_in.close
44
- Open3.popen3(*client_command_line) do |client_in, client_out, client_error, client_thread|
45
- client_out.close
46
- dump_out.each do |dump|
47
- yield dump if block_given?
48
- client_in.puts(dump)
49
- end
63
+ env = {}
64
+ Open3.pipeline_r([env, *drndump_command_line],
65
+ [env, *client_command_line]) do |last_stdout, thread|
66
+ last_stdout.each do |output|
67
+ yield output if block_given?
50
68
  end
51
69
  end
52
70
  end
@@ -11,7 +11,7 @@
11
11
  #
12
12
  # You should have received a copy of the GNU Lesser General Public
13
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
14
+ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
15
15
 
16
16
  require "tsort"
17
17
 
@@ -63,6 +63,9 @@ module Droonga
63
63
  end
64
64
 
65
65
  def start
66
+ @farm.on_ready = lambda do
67
+ @engine_state.on_ready
68
+ end
66
69
  @farm.start
67
70
  end
68
71
 
@@ -143,10 +146,10 @@ module Droonga
143
146
  else
144
147
  steps = message["steps"]
145
148
  if steps
146
- session_planner = SessionPlanner.new(self, steps)
149
+ session_planner = SessionPlanner.new(@engine_state, steps)
147
150
  dataset = message["dataset"] || @message["dataset"]
148
151
  collector_runner = @collector_runners[dataset]
149
- session = session_planner.create_session(id, collector_runner)
152
+ session = session_planner.create_session(id, self, collector_runner)
150
153
  @engine_state.register_session(id, session)
151
154
  else
152
155
  logger.error("no steps error: id=#{id}, message=#{message}")
@@ -175,7 +178,7 @@ module Droonga
175
178
  steps.each do |step|
176
179
  dataset = @catalog.dataset(step["dataset"])
177
180
  if dataset
178
- routes = dataset.get_routes(step, @engine_state.live_nodes)
181
+ routes = dataset.compute_routes(step, @engine_state.live_nodes)
179
182
  step["routes"] = routes
180
183
  else
181
184
  step["routes"] ||= [id]
@@ -279,24 +282,24 @@ module Droonga
279
282
  end
280
283
 
281
284
  def log_tag
282
- "[#{Process.ppid}][#{Process.pid}] dispatcher"
285
+ "dispatcher"
283
286
  end
284
287
 
285
288
  class SessionPlanner
286
289
  attr_reader :steps
287
290
 
288
- def initialize(dispatcher, steps)
289
- @dispatcher = dispatcher
291
+ def initialize(engine_state, steps)
292
+ @engine_state = engine_state
290
293
  @steps = steps
291
294
  end
292
295
 
293
- def create_session(id, collector_runner)
296
+ def create_session(id, dispatcher, collector_runner)
294
297
  resolve_descendants
295
298
  tasks = []
296
299
  inputs = {}
297
300
  @steps.each do |step|
298
301
  step["routes"].each do |route|
299
- next unless @dispatcher.local?(route)
302
+ next unless @engine_state.local_route?(route)
300
303
  task = {
301
304
  "route" => route,
302
305
  "step" => step,
@@ -310,7 +313,7 @@ module Droonga
310
313
  end
311
314
  end
312
315
  end
313
- Session.new(id, @dispatcher, collector_runner, tasks, inputs)
316
+ Session.new(id, dispatcher, collector_runner, tasks, inputs)
314
317
  end
315
318
 
316
319
  def resolve_descendants
@@ -328,7 +331,8 @@ module Droonga
328
331
  (step["outputs"] || []).each do |output|
329
332
  descendants[output] = []
330
333
  @descendants[output].each do |index|
331
- @steps[index]["n_of_expects"] += step["routes"].size
334
+ live_routes = @engine_state.remove_dead_routes(step["routes"])
335
+ @steps[index]["n_of_expects"] += live_routes.size
332
336
  descendants[output].concat(@steps[index]["routes"])
333
337
  end
334
338
  end
@@ -13,7 +13,7 @@
13
13
  #
14
14
  # You should have received a copy of the GNU Lesser General Public
15
15
  # License along with this library; if not, write to the Free Software
16
- # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
16
+ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17
17
 
18
18
  module Droonga
19
19
  class DistributedCommandPlanner
@@ -13,7 +13,7 @@
13
13
  #
14
14
  # You should have received a copy of the GNU Lesser General Public
15
15
  # License along with this library; if not, write to the Free Software
16
- # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
16
+ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17
17
 
18
18
  require "tsort"
19
19
 
@@ -11,10 +11,10 @@
11
11
  #
12
12
  # You should have received a copy of the GNU Lesser General Public
13
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
14
+ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
15
15
 
16
16
  module Droonga
17
17
  class Engine
18
- VERSION = "1.0.4"
18
+ VERSION = "1.0.5"
19
19
  end
20
20
  end
@@ -13,7 +13,7 @@
13
13
  #
14
14
  # You should have received a copy of the GNU Lesser General Public
15
15
  # License along with this library; if not, write to the Free Software
16
- # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
16
+ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17
17
 
18
18
  require "time"
19
19
  require "fileutils"
@@ -29,6 +29,7 @@ module Droonga
29
29
  class Engine
30
30
  include Loggable
31
31
 
32
+ attr_writer :on_ready
32
33
  def initialize(loop, name, internal_name)
33
34
  @state = EngineState.new(loop, name, internal_name)
34
35
  @catalog = load_catalog
@@ -38,10 +39,14 @@ module Droonga
38
39
  @live_nodes_list_observer.on_change = lambda do
39
40
  @state.live_nodes = load_live_nodes
40
41
  end
42
+ @on_ready = nil
41
43
  end
42
44
 
43
45
  def start
44
46
  logger.trace("start: start")
47
+ @state.on_ready = lambda do
48
+ @on_ready.call if @on_ready
49
+ end
45
50
  @state.start
46
51
  @live_nodes_list_observer.start
47
52
  @dispatcher.start
@@ -87,7 +92,7 @@ module Droonga
87
92
  loader = CatalogLoader.new(catalog_path.to_s)
88
93
  catalog = loader.load
89
94
  logger.info("catalog loaded",
90
- :path => catalog_path,
95
+ :path => catalog_path.to_s,
91
96
  :mtime => catalog_path.mtime)
92
97
  catalog
93
98
  end
@@ -97,7 +102,7 @@ module Droonga
97
102
  loader = LiveNodesListLoader.new(path)
98
103
  live_nodes = loader.load
99
104
  logger.info("live-nodes loaded",
100
- :path => path,
105
+ :path => path.to_s,
101
106
  :mtime => path.mtime)
102
107
  live_nodes
103
108
  end
@@ -11,7 +11,7 @@
11
11
  #
12
12
  # You should have received a copy of the GNU Lesser General Public
13
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
14
+ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
15
15
 
16
16
  require "English"
17
17
 
@@ -31,21 +31,22 @@ module Droonga
31
31
  attr_reader :internal_name
32
32
  attr_reader :forwarder
33
33
  attr_reader :replier
34
+ attr_writer :on_ready
34
35
  attr_accessor :on_finish
35
36
  attr_accessor :catalog
36
- attr_writer :dead_nodes
37
37
  def initialize(loop, name, internal_name)
38
38
  @loop = loop
39
39
  @name = name
40
40
  @internal_name = internal_name
41
41
  @sessions = {}
42
42
  @current_id = 0
43
- @forwarder = Forwarder.new(@loop)
44
- @forwarder.resume
43
+ @forwarder = Forwarder.new(@loop, :buffering => true)
45
44
  @replier = Replier.new(@forwarder)
45
+ @on_ready = nil
46
46
  @on_finish = nil
47
47
  @catalog = nil
48
48
  @live_nodes = nil
49
+ @dead_nodes = []
49
50
  end
50
51
 
51
52
  def start
@@ -111,13 +112,21 @@ module Droonga
111
112
  end
112
113
 
113
114
  def live_nodes=(nodes)
115
+ old_live_nodes = @live_nodes
114
116
  @live_nodes = nodes
115
117
  @dead_nodes = all_nodes - @live_nodes
118
+ @forwarder.resume if old_live_nodes != @live_nodes
116
119
  @live_nodes
117
120
  end
118
121
 
119
- def dead_nodes
120
- @dead_nodes || []
122
+ def remove_dead_routes(routes)
123
+ routes.reject do |route|
124
+ @dead_nodes.include?(farm_path(route))
125
+ end
126
+ end
127
+
128
+ def on_ready
129
+ @on_ready.call if @on_ready
121
130
  end
122
131
 
123
132
  private
data/lib/droonga/error.rb CHANGED
@@ -13,7 +13,7 @@
13
13
  #
14
14
  # You should have received a copy of the GNU Lesser General Public
15
15
  # License along with this library; if not, write to the Free Software
16
- # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
16
+ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17
17
 
18
18
  module Droonga
19
19
  class Error < StandardError
@@ -11,7 +11,7 @@
11
11
  #
12
12
  # You should have received a copy of the GNU Lesser General Public
13
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
14
+ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
15
15
 
16
16
  require "droonga/error"
17
17
  require "droonga/status_code"
@@ -13,7 +13,7 @@
13
13
  #
14
14
  # You should have received a copy of the GNU Lesser General Public
15
15
  # License along with this library; if not, write to the Free Software
16
- # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
16
+ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17
17
 
18
18
  require "coolio"
19
19