droonga-engine 1.0.5 → 1.0.6

Sign up to get free protection for your applications and to get access to all the features.
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