droonga-engine 1.0.5 → 1.0.6

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 (57) hide show
  1. checksums.yaml +4 -4
  2. data/bin/droonga-engine-absorb-data +2 -1
  3. data/bin/droonga-engine-catalog-generate +21 -5
  4. data/bin/droonga-engine-catalog-modify +22 -6
  5. data/bin/droonga-engine-configure +215 -0
  6. data/bin/droonga-engine-join +48 -123
  7. data/bin/droonga-engine-unjoin +14 -1
  8. data/doc/text/news.md +21 -0
  9. data/droonga-engine.gemspec +12 -10
  10. data/install/centos/droonga-engine +60 -0
  11. data/install/centos/functions.sh +35 -0
  12. data/install/debian/droonga-engine +155 -0
  13. data/install/debian/functions.sh +33 -0
  14. data/install.sh +360 -0
  15. data/lib/droonga/address.rb +3 -1
  16. data/lib/droonga/catalog/dataset.rb +2 -0
  17. data/lib/droonga/catalog/version1.rb +16 -3
  18. data/lib/droonga/catalog/version2.rb +16 -3
  19. data/lib/droonga/catalog_fetcher.rb +51 -0
  20. data/lib/droonga/catalog_generator.rb +6 -5
  21. data/lib/droonga/catalog_modifier.rb +45 -0
  22. data/lib/droonga/command/droonga_engine.rb +96 -29
  23. data/lib/droonga/command/droonga_engine_service.rb +5 -0
  24. data/lib/droonga/command/remote.rb +368 -0
  25. data/lib/droonga/command/serf_event_handler.rb +37 -304
  26. data/lib/droonga/dispatcher.rb +15 -1
  27. data/lib/droonga/engine/version.rb +1 -1
  28. data/lib/droonga/engine.rb +11 -4
  29. data/lib/droonga/engine_state.rb +2 -0
  30. data/lib/droonga/farm.rb +14 -5
  31. data/lib/droonga/fluent_message_receiver.rb +23 -6
  32. data/lib/droonga/fluent_message_sender.rb +5 -1
  33. data/lib/droonga/node_status.rb +67 -0
  34. data/lib/droonga/path.rb +28 -4
  35. data/lib/droonga/plugins/catalog.rb +40 -0
  36. data/lib/droonga/safe_file_writer.rb +1 -1
  37. data/lib/droonga/searcher.rb +3 -15
  38. data/lib/droonga/serf.rb +17 -32
  39. data/lib/droonga/serf_downloader.rb +26 -1
  40. data/lib/droonga/service_installation.rb +123 -0
  41. data/lib/droonga/session.rb +4 -0
  42. data/lib/droonga/slice.rb +22 -12
  43. data/lib/droonga/supervisor.rb +16 -2
  44. data/lib/droonga/worker_process_agent.rb +13 -1
  45. data/sample/droonga-engine.yaml +5 -0
  46. data/test/command/config/default/catalog.json +1 -1
  47. data/test/command/config/default/droonga-engine.yaml +4 -0
  48. data/test/command/config/version1/catalog.json +1 -1
  49. data/test/command/suite/catalog/fetch.expected +64 -0
  50. data/test/command/suite/catalog/fetch.test +6 -0
  51. data/test/unit/catalog/test_version1.rb +2 -2
  52. data/test/unit/catalog/test_version2.rb +3 -3
  53. data/test/unit/helper/sandbox.rb +3 -1
  54. data/test/unit/plugins/catalog/test_fetch.rb +76 -0
  55. data/test/unit/test_catalog_generator.rb +7 -3
  56. metadata +74 -27
  57. data/bin/droonga-engine-data-publisher +0 -66
@@ -0,0 +1,368 @@
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
15
+
16
+ require "json"
17
+
18
+ require "droonga/path"
19
+ require "droonga/serf"
20
+ require "droonga/node_status"
21
+ require "droonga/catalog_generator"
22
+ require "droonga/catalog_modifier"
23
+ require "droonga/catalog_fetcher"
24
+ require "droonga/data_absorber"
25
+ require "droonga/safe_file_writer"
26
+ require "droonga/service_installation"
27
+
28
+ module Droonga
29
+ module Command
30
+ module Remote
31
+ class Base
32
+ attr_reader :response
33
+
34
+ def initialize(serf_name, params)
35
+ @serf_name = serf_name
36
+ @params = params
37
+ @response = {
38
+ "log" => []
39
+ }
40
+ @serf = Serf.new(nil, @serf_name)
41
+
42
+ @service_installation = ServiceInstallation.new
43
+ @service_installation.ensure_using_service_base_directory
44
+
45
+ log("params = #{params}")
46
+ end
47
+
48
+ def process
49
+ # override me!
50
+ end
51
+
52
+ def should_process?
53
+ for_me? or @params.nil? or not @params.include?("node")
54
+ end
55
+
56
+ private
57
+ def node
58
+ @serf_name
59
+ end
60
+
61
+ def host
62
+ node.split(":").first
63
+ end
64
+
65
+ def target_node
66
+ @params && @params["node"]
67
+ end
68
+
69
+ def for_me?
70
+ target_node == @serf_name
71
+ end
72
+
73
+ def log(message)
74
+ @response["log"] << message
75
+ end
76
+ end
77
+
78
+ class ChangeRole < Base
79
+ def process
80
+ status = NodeStatus.new
81
+ status.set(:role, @params["role"])
82
+ end
83
+ end
84
+
85
+ class ReportStatus < Base
86
+ def process
87
+ status = NodeStatus.new
88
+ @response["value"] = status.get(@params["key"])
89
+ end
90
+ end
91
+
92
+ class Join < Base
93
+ def process
94
+ log("type = #{type}")
95
+ case type
96
+ when "replica"
97
+ join_as_replica
98
+ end
99
+ end
100
+
101
+ private
102
+ def type
103
+ @params["type"]
104
+ end
105
+
106
+ def source_node
107
+ @params["source"]
108
+ end
109
+
110
+ def joining_node
111
+ @params["node"]
112
+ end
113
+
114
+ def dataset_name
115
+ @params["dataset"]
116
+ end
117
+
118
+ def valid_params?
119
+ have_required_params? and
120
+ valid_node?(source_node) and
121
+ valid_node?(joining_node)
122
+ end
123
+
124
+ def have_required_params?
125
+ required_params = [
126
+ source_node,
127
+ joining_node,
128
+ dataset_name,
129
+ ]
130
+ required_params.all? do |param|
131
+ not param.nil?
132
+ end
133
+ end
134
+
135
+ NODE_PATTERN = /\A([^:]+):(\d+)\/(.+)\z/
136
+
137
+ def valid_node?(node)
138
+ node =~ NODE_PATTERN
139
+ end
140
+
141
+ def source_host
142
+ @source_host ||= (source_node =~ NODE_PATTERN && $1)
143
+ end
144
+
145
+ def joining_host
146
+ @joining_host ||= (joining_node =~ NODE_PATTERN && $1)
147
+ end
148
+
149
+ def port
150
+ @port ||= (source_node =~ NODE_PATTERN && $2 && $2.to_i)
151
+ end
152
+
153
+ def tag
154
+ @tag ||= (source_node =~ NODE_PATTERN && $3)
155
+ end
156
+
157
+ def should_absorb_data?
158
+ @params["copy"]
159
+ end
160
+
161
+ def join_as_replica
162
+ return unless valid_params?
163
+
164
+ log("source_node = #{source_node}")
165
+
166
+ @catalog = fetch_catalog
167
+
168
+ other_hosts = replica_hosts
169
+ log("other_hosts = #{other_hosts}")
170
+ return if other_hosts.empty?
171
+
172
+ # restart self with the fetched catalog.
173
+ SafeFileWriter.write(Path.catalog) do |output, file|
174
+ output.puts(JSON.pretty_generate(@catalog))
175
+ @service_installation.ensure_correct_file_permission(file)
176
+ end
177
+
178
+ absorb_data if should_absorb_data?
179
+
180
+ log("joining to the cluster: update myself")
181
+
182
+ CatalogModifier.modify do |modifier, file|
183
+ modifier.datasets[dataset_name].replicas.hosts += other_hosts
184
+ modifier.datasets[dataset_name].replicas.hosts.uniq!
185
+ @service_installation.ensure_correct_file_permission(file)
186
+ end
187
+
188
+ @serf.join(*other_hosts)
189
+ end
190
+
191
+ def replica_hosts
192
+ generator = CatalogGenerator.new
193
+ generator.load(@catalog)
194
+ dataset = generator.dataset_for_host(source_host) ||
195
+ generator.dataset_for_host(host)
196
+ return [] unless dataset
197
+ dataset.replicas.hosts
198
+ end
199
+
200
+ def fetch_catalog
201
+ fetcher = CatalogFetcher.new(:host => source_host,
202
+ :port => port,
203
+ :tag => tag,
204
+ :receiver_host => host)
205
+ fetcher.fetch(:dataset => dataset_name)
206
+ end
207
+
208
+ def absorb_data
209
+ log("starting to copy data from #{source_host}")
210
+
211
+ CatalogModifier.modify do |modifier, file|
212
+ modifier.datasets[dataset_name].replicas.hosts = [host]
213
+ @service_installation.ensure_correct_file_permission(file)
214
+ end
215
+ sleep(5) #TODO: wait for restart. this should be done more safely, to avoid starting of absorbing with old catalog.json.
216
+
217
+ status = NodeStatus.new
218
+ status.set(:absorbing, true)
219
+ DataAbsorber.absorb(:dataset => dataset_name,
220
+ :source_host => source_host,
221
+ :destination_host => joining_host,
222
+ :port => port,
223
+ :tag => tag)
224
+ status.delete(:absorbing)
225
+ sleep(1)
226
+ end
227
+ end
228
+
229
+ class AbsorbData < Base
230
+ attr_writer :dataset_name, :port, :tag
231
+
232
+ def process
233
+ return unless source
234
+
235
+ log("start to absorb data from #{source}")
236
+
237
+ if dataset_name.nil? or port.nil? or tag.nil?
238
+ current_catalog = JSON.parse(Path.catalog.read)
239
+ generator = CatalogGenerator.new
240
+ generator.load(current_catalog)
241
+
242
+ dataset = generator.dataset_for_host(source)
243
+ return unless dataset
244
+
245
+ self.dataset_name = dataset.name
246
+ self.port = dataset.replicas.port
247
+ self.tag = dataset.replicas.tag
248
+ end
249
+
250
+ log("dataset = #{dataset_name}")
251
+ log("port = #{port}")
252
+ log("tag = #{tag}")
253
+
254
+ status = NodeStatus.new
255
+ status.set(:absorbing, true)
256
+ DataAbsorber.absorb(:dataset => dataset_name,
257
+ :source_host => source,
258
+ :destination_host => host,
259
+ :port => port,
260
+ :tag => tag,
261
+ :client => "droonga-send")
262
+ status.delete(:absorbing)
263
+ end
264
+
265
+ private
266
+ def source
267
+ @params["source"]
268
+ end
269
+
270
+ def dataset_name
271
+ @dataset_name ||= @params["dataset"]
272
+ end
273
+
274
+ def port
275
+ @port ||= @params["port"]
276
+ end
277
+
278
+ def tag
279
+ @tag ||= @params["tag"]
280
+ end
281
+ end
282
+
283
+ class ModifyReplicasBase < Base
284
+ private
285
+ def dataset
286
+ @params["dataset"]
287
+ end
288
+
289
+ def hosts
290
+ @hosts ||= prepare_hosts
291
+ end
292
+
293
+ def prepare_hosts
294
+ hosts = @params["hosts"]
295
+ return nil unless hosts
296
+ hosts = [hosts] if hosts.is_a?(String)
297
+ hosts
298
+ end
299
+ end
300
+
301
+ class SetReplicas < ModifyReplicasBase
302
+ def process
303
+ return if dataset.nil? or hosts.nil?
304
+
305
+ log("new replicas: #{hosts.join(",")}")
306
+
307
+ CatalogModifier.modify do |modifier, file|
308
+ modifier.datasets[dataset].replicas.hosts = hosts
309
+ @service_installation.ensure_correct_file_permission(file)
310
+ end
311
+
312
+ @serf.join(*hosts)
313
+ #XXX Now we should restart serf agent to remove unjoined nodes from the list of members...
314
+ end
315
+ end
316
+
317
+ class AddReplicas < ModifyReplicasBase
318
+ def process
319
+ return if dataset.nil? or hosts.nil?
320
+
321
+ added_hosts = hosts - [host]
322
+ log("adding replicas: #{added_hosts.join(",")}")
323
+ return if added_hosts.empty?
324
+
325
+ CatalogModifier.modify do |modifier, file|
326
+ modifier.datasets[dataset].replicas.hosts += added_hosts
327
+ modifier.datasets[dataset].replicas.hosts.uniq!
328
+ @service_installation.ensure_correct_file_permission(file)
329
+ end
330
+
331
+ @serf.join(*added_hosts)
332
+ end
333
+ end
334
+
335
+ class RemoveReplicas < ModifyReplicasBase
336
+ def process
337
+ return if dataset.nil? or hosts.nil?
338
+
339
+ log("removing replicas: #{hosts.join(",")}")
340
+
341
+ CatalogModifier.modify do |modifier, file|
342
+ modifier.datasets[dataset].replicas.hosts -= hosts
343
+ @service_installation.ensure_correct_file_permission(file)
344
+ end
345
+
346
+ #XXX Now we should restart serf agent to remove unjoined nodes from the list of members...
347
+ end
348
+ end
349
+
350
+ class UpdateLiveNodes < Base
351
+ def process
352
+ path = Path.live_nodes
353
+ nodes = live_nodes
354
+ file_contents = JSON.pretty_generate(nodes)
355
+ SafeFileWriter.write(path) do |output, file|
356
+ output.puts(file_contents)
357
+ @service_installation.ensure_correct_file_permission(file)
358
+ end
359
+ end
360
+
361
+ private
362
+ def live_nodes
363
+ @serf.live_nodes
364
+ end
365
+ end
366
+ end
367
+ end
368
+ end