droonga-engine 1.0.2 → 1.0.3

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 (203) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +1 -0
  3. data/.travis.yml +3 -0
  4. data/Gemfile +7 -0
  5. data/Rakefile +6 -2
  6. data/bin/droonga-engine +2 -2
  7. data/bin/{droonga-catalog-generate → droonga-engine-catalog-generate} +15 -3
  8. data/bin/droonga-engine-serf-event-handler +20 -0
  9. data/bin/droonga-engine-service +2 -2
  10. data/doc/text/news.md +21 -1
  11. data/droonga-engine.gemspec +5 -2
  12. data/lib/droonga/catalog/collection_volume.rb +12 -0
  13. data/lib/droonga/catalog/dataset.rb +25 -0
  14. data/lib/droonga/catalog/single_volume.rb +10 -0
  15. data/lib/droonga/catalog/slice.rb +4 -0
  16. data/lib/droonga/catalog/version1.rb +59 -48
  17. data/lib/droonga/catalog/version2.rb +10 -20
  18. data/lib/droonga/catalog/volume_collection.rb +27 -4
  19. data/lib/droonga/catalog_generator.rb +12 -5
  20. data/lib/droonga/catalog_observer.rb +17 -35
  21. data/lib/droonga/command/droonga_engine.rb +436 -0
  22. data/lib/droonga/command/droonga_engine_service.rb +273 -0
  23. data/lib/droonga/command/serf_event_handler.rb +85 -0
  24. data/lib/droonga/dispatcher.rb +8 -8
  25. data/lib/droonga/engine.rb +90 -26
  26. data/lib/droonga/engine/version.rb +1 -1
  27. data/lib/droonga/engine_state.rb +29 -3
  28. data/lib/droonga/internal_fluent_message_receiver.rb +100 -0
  29. data/lib/droonga/live_nodes_list_loader.rb +48 -0
  30. data/lib/droonga/live_nodes_list_observer.rb +72 -0
  31. data/lib/droonga/path.rb +47 -0
  32. data/lib/droonga/plugins/dump.rb +279 -38
  33. data/lib/droonga/plugins/groonga/select.rb +26 -14
  34. data/lib/droonga/plugins/search.rb +30 -2
  35. data/lib/droonga/plugins/search/distributed_search_planner.rb +28 -11
  36. data/lib/droonga/processor.rb +4 -0
  37. data/lib/droonga/searcher.rb +26 -0
  38. data/lib/droonga/serf.rb +119 -0
  39. data/lib/droonga/serf_downloader.rb +90 -0
  40. data/lib/droonga/server.rb +2 -2
  41. data/lib/droonga/service_control_protocol.rb +26 -0
  42. data/sample/cluster/catalog.json +1 -1
  43. data/test/command/config/default/catalog.json +2 -2
  44. data/test/command/config/version1/catalog.json +1 -1
  45. data/test/command/fixture/documents.jsons +18 -18
  46. data/test/command/fixture/event.jsons +4 -4
  47. data/test/command/fixture/user-table-array.jsons +4 -4
  48. data/test/command/fixture/user-table.jsons +5 -5
  49. data/test/command/suite/add/dimension/column.catalog.json +1 -1
  50. data/test/command/suite/add/dimension/column.test +4 -4
  51. data/test/command/suite/add/dimension/integer.catalog.json +1 -1
  52. data/test/command/suite/add/dimension/integer.test +4 -4
  53. data/test/command/suite/add/error/invalid-integer.test +1 -1
  54. data/test/command/suite/add/error/invalid-time.test +1 -1
  55. data/test/command/suite/add/error/missing-key.test +1 -1
  56. data/test/command/suite/add/error/missing-table.test +1 -1
  57. data/test/command/suite/add/error/unknown-column.test +1 -1
  58. data/test/command/suite/add/error/unknown-table.test +1 -1
  59. data/test/command/suite/add/minimum.test +1 -1
  60. data/test/command/suite/add/vector/short_text.catalog.json +26 -0
  61. data/test/command/suite/add/vector/short_text.expected +42 -0
  62. data/test/command/suite/add/vector/short_text.test +35 -0
  63. data/test/command/suite/add/with-values.test +1 -1
  64. data/test/command/suite/add/without-key.test +1 -1
  65. data/test/command/suite/dump/column/index.catalog.json +40 -0
  66. data/test/command/suite/dump/column/index.expected +195 -0
  67. data/test/command/suite/dump/column/index.test +5 -0
  68. data/test/command/suite/dump/column/scalar.catalog.json +19 -0
  69. data/test/command/suite/dump/column/scalar.expected +99 -0
  70. data/test/command/suite/dump/column/scalar.test +5 -0
  71. data/test/command/suite/dump/column/vector.catalog.json +22 -0
  72. data/test/command/suite/dump/column/vector.expected +108 -0
  73. data/test/command/suite/dump/column/vector.test +5 -0
  74. data/test/command/suite/dump/record/vector/reference.catalog.json +27 -0
  75. data/test/command/suite/dump/record/vector/reference.expected +213 -0
  76. data/test/command/suite/dump/record/vector/reference.test +21 -0
  77. data/test/command/suite/dump/table/array.catalog.json +13 -0
  78. data/test/command/suite/dump/table/array.expected +63 -0
  79. data/test/command/suite/dump/table/array.test +5 -0
  80. data/test/command/suite/dump/table/double_array_trie.catalog.json +14 -0
  81. data/test/command/suite/dump/table/double_array_trie.expected +66 -0
  82. data/test/command/suite/dump/table/double_array_trie.test +5 -0
  83. data/test/command/suite/dump/table/hash.catalog.json +14 -0
  84. data/test/command/suite/dump/table/hash.expected +66 -0
  85. data/test/command/suite/dump/table/hash.test +5 -0
  86. data/test/command/suite/dump/table/patricia_trie.catalog.json +14 -0
  87. data/test/command/suite/dump/table/patricia_trie.expected +66 -0
  88. data/test/command/suite/dump/table/patricia_trie.test +5 -0
  89. data/test/command/suite/groonga/column_create/scalar.test +2 -2
  90. data/test/command/suite/groonga/column_create/unknown-table.test +1 -1
  91. data/test/command/suite/groonga/column_create/vector.test +2 -2
  92. data/test/command/suite/groonga/column_list/success.test +3 -3
  93. data/test/command/suite/groonga/column_list/unknown-table.test +1 -1
  94. data/test/command/suite/groonga/column_remove/success.test +3 -3
  95. data/test/command/suite/groonga/column_remove/unknown-column.test +2 -2
  96. data/test/command/suite/groonga/column_remove/unknown-table.test +1 -1
  97. data/test/command/suite/groonga/column_rename/success.test +3 -3
  98. data/test/command/suite/groonga/column_rename/unknown-column.test +2 -2
  99. data/test/command/suite/groonga/column_rename/unknown-table.test +1 -1
  100. data/test/command/suite/groonga/delete/duplicated-identifiers.test +2 -2
  101. data/test/command/suite/groonga/delete/filter.test +2 -2
  102. data/test/command/suite/groonga/delete/invalid-filter.test +1 -1
  103. data/test/command/suite/groonga/delete/no-identifier.test +2 -2
  104. data/test/command/suite/groonga/delete/success.test +2 -2
  105. data/test/command/suite/groonga/delete/unknown-table.test +1 -1
  106. data/test/command/suite/groonga/select/minimum.expected +24 -1
  107. data/test/command/suite/groonga/select/minimum.test +1 -1
  108. data/test/command/suite/groonga/select/type/time.catalog.json +19 -0
  109. data/test/command/suite/groonga/select/type/time.expected +37 -0
  110. data/test/command/suite/groonga/select/type/time.test +35 -0
  111. data/test/command/suite/groonga/table_create/array.test +1 -1
  112. data/test/command/suite/groonga/table_create/hash.test +1 -1
  113. data/test/command/suite/groonga/table_list/success.test +2 -2
  114. data/test/command/suite/groonga/table_remove/success.test +1 -1
  115. data/test/command/suite/groonga/table_remove/unknown-table.test +1 -1
  116. data/test/command/suite/message/error/unknown-type.expected +1 -1
  117. data/test/command/suite/message/error/unknown-type.test +1 -1
  118. data/test/command/suite/search/adjusters/multiple.catalog.json +1 -1
  119. data/test/command/suite/search/adjusters/multiple.test +3 -3
  120. data/test/command/suite/search/adjusters/one.catalog.json +1 -1
  121. data/test/command/suite/search/adjusters/one.test +3 -3
  122. data/test/command/suite/search/attributes/array.expected +7 -0
  123. data/test/command/suite/search/attributes/array.test +1 -1
  124. data/test/command/suite/search/attributes/hash.expected +18 -0
  125. data/test/command/suite/search/attributes/hash.test +1 -1
  126. data/test/command/suite/search/complex.expected +12 -0
  127. data/test/command/suite/search/complex.test +1 -1
  128. data/test/command/suite/search/condition/nested.catalog.json +37 -0
  129. data/test/command/suite/search/condition/nested.expected +7 -0
  130. data/test/command/suite/search/condition/nested.test +103 -2
  131. data/test/command/suite/search/condition/query.catalog.json +37 -0
  132. data/test/command/suite/search/condition/query.expected +7 -0
  133. data/test/command/suite/search/condition/query.test +103 -2
  134. data/test/command/suite/search/condition/query/nonexistent_column.catalog.json +1 -1
  135. data/test/command/suite/search/condition/query/nonexistent_column.test +2 -2
  136. data/test/command/suite/search/condition/query/syntax_error.catalog.json +1 -1
  137. data/test/command/suite/search/condition/query/syntax_error.test +2 -2
  138. data/test/command/suite/search/condition/script.catalog.json +37 -0
  139. data/test/command/suite/search/condition/script.expected +7 -0
  140. data/test/command/suite/search/condition/script.test +103 -2
  141. data/test/command/suite/search/error/cyclic-source.test +1 -1
  142. data/test/command/suite/search/error/deeply-cyclic-source.test +1 -1
  143. data/test/command/suite/search/error/missing-source-parameter.test +1 -1
  144. data/test/command/suite/search/error/no-query.test +1 -1
  145. data/test/command/suite/search/error/unknown-source.test +1 -1
  146. data/test/command/suite/search/group/count.test +1 -1
  147. data/test/command/suite/search/group/limit.test +1 -1
  148. data/test/command/suite/search/group/string.catalog.json +41 -0
  149. data/test/command/suite/search/group/string.expected +18 -18
  150. data/test/command/suite/search/group/string.test +67 -22
  151. data/test/command/suite/search/group/subrecord/with-sort.catalog.json +1 -1
  152. data/test/command/suite/search/group/subrecord/with-sort.test +5 -5
  153. data/test/command/suite/search/multiple/chained.catalog.json +37 -0
  154. data/test/command/suite/search/multiple/chained.expected +14 -0
  155. data/test/command/suite/search/multiple/chained.test +103 -2
  156. data/test/command/suite/search/multiple/parallel.expected +14 -0
  157. data/test/command/suite/search/multiple/parallel.test +1 -1
  158. data/test/command/suite/search/output/attributes/invalid.catalog.json +1 -1
  159. data/test/command/suite/search/output/attributes/invalid.test +2 -2
  160. data/test/command/suite/search/output/attributes/star.catalog.json +23 -0
  161. data/test/command/suite/search/output/attributes/star.expected +27 -0
  162. data/test/command/suite/search/output/attributes/star.test +32 -0
  163. data/test/command/suite/search/range/only-output.expected +7 -0
  164. data/test/command/suite/search/range/only-output.test +1 -1
  165. data/test/command/suite/search/range/only-sort.expected +7 -0
  166. data/test/command/suite/search/range/only-sort.test +1 -1
  167. data/test/command/suite/search/range/sort-and-output.expected +7 -0
  168. data/test/command/suite/search/range/sort-and-output.test +1 -1
  169. data/test/command/suite/search/range/too-large-output-offset.expected +8 -0
  170. data/test/command/suite/search/range/too-large-output-offset.test +1 -1
  171. data/test/command/suite/search/range/too-large-sort-offset.expected +8 -0
  172. data/test/command/suite/search/range/too-large-sort-offset.test +1 -1
  173. data/test/command/suite/search/response/elapsed_time.catalog.json +1 -1
  174. data/test/command/suite/search/response/elapsed_time.test +2 -2
  175. data/test/command/suite/search/response/records/value/time.expected +12 -0
  176. data/test/command/suite/search/response/records/value/time.test +1 -1
  177. data/test/command/suite/search/simple.expected +12 -0
  178. data/test/command/suite/search/simple.test +1 -1
  179. data/test/command/suite/search/sort/default-offset-limit.expected +7 -0
  180. data/test/command/suite/search/sort/default-offset-limit.test +1 -1
  181. data/test/command/suite/search/sort/invisible-column.expected +7 -0
  182. data/test/command/suite/search/sort/invisible-column.test +1 -1
  183. data/test/unit/catalog/test_collection_volume.rb +16 -0
  184. data/test/unit/catalog/test_dataset.rb +36 -0
  185. data/test/unit/catalog/test_single_volume.rb +9 -0
  186. data/test/unit/catalog/test_slice.rb +11 -0
  187. data/test/unit/catalog/test_version1.rb +7 -12
  188. data/test/unit/catalog/test_version2.rb +7 -0
  189. data/test/unit/catalog/test_volume_collection.rb +28 -0
  190. data/test/unit/fixtures/catalog/version1.json +10 -3
  191. data/test/unit/fixtures/catalog/version2.json +2 -2
  192. data/test/unit/plugins/groonga/select/test_adapter_output.rb +8 -14
  193. data/test/unit/plugins/groonga/test_column_create.rb +5 -5
  194. data/test/unit/plugins/groonga/test_column_remove.rb +2 -2
  195. data/test/unit/plugins/groonga/test_column_rename.rb +2 -2
  196. data/test/unit/plugins/groonga/test_delete.rb +2 -2
  197. data/test/unit/plugins/groonga/test_table_create.rb +9 -9
  198. data/test/unit/plugins/groonga/test_table_remove.rb +1 -1
  199. data/test/unit/test_catalog_generator.rb +1 -1
  200. data/test/unit/test_schema_applier.rb +2 -2
  201. data/test/unit/test_watch_schema.rb +4 -4
  202. metadata +241 -72
  203. data/lib/droonga/engine/command/droonga_engine.rb +0 -441
@@ -26,8 +26,8 @@ module Droonga
26
26
  @table = select_request["table"]
27
27
  @result_name = @table + "_result"
28
28
 
29
- output_columns = select_request["output_columns"] || ""
30
- attributes = output_columns.split(/, */)
29
+ output_columns = select_request["output_columns"] || "_id, _key, *"
30
+ attributes = output_columns.split(/\s*,\s*/)
31
31
  offset = (select_request["offset"] || "0").to_i
32
32
  limit = (select_request["limit"] || "10").to_i
33
33
 
@@ -121,7 +121,7 @@ module Droonga
121
121
  drilldown_keys = drilldown_keys.split(",")
122
122
 
123
123
  sort_keys = (select_request["drilldown_sortby"] || "").split(",")
124
- columns = (select_request["drilldown_output_columns"] || "").split(",")
124
+ columns = (select_request["drilldown_output_columns"] || "_key,_nsubrecs").split(",")
125
125
  offset = (select_request["drilldown_offset"] || "0").to_i
126
126
  limit = (select_request["drilldown_limit"] || "10").to_i
127
127
 
@@ -177,16 +177,18 @@ module Droonga
177
177
  def convert_main_result(result)
178
178
  status_code = 0
179
179
  start_time = result["startTime"]
180
- start_time_in_unix_time = if start_time
181
- Time.parse(start_time).to_f
182
- else
183
- Time.now.to_f
184
- end
180
+ start_time_in_unix_time = normalize_time(start_time).to_f
185
181
  elapsed_time = result["elapsedTime"] || 0
186
182
  @header = [status_code, start_time_in_unix_time, elapsed_time]
187
183
  @body = convert_search_result(result)
188
184
  end
189
185
 
186
+ def normalize_time(time)
187
+ time ||= Time.now
188
+ time = Time.parse(time) if time.is_a?(String)
189
+ time
190
+ end
191
+
190
192
  def convert_drilldown_result(key, result)
191
193
  @drilldown_results << convert_search_result(result)
192
194
  end
@@ -194,12 +196,8 @@ module Droonga
194
196
  def convert_search_result(result)
195
197
  count = result["count"]
196
198
  attributes = convert_attributes(result["attributes"])
197
- records = result["records"]
198
- if records.nil? or records.empty?
199
- [[count], attributes]
200
- else
201
- [[count], attributes, records]
202
- end
199
+ records = convert_records(attributes, result["records"] || [])
200
+ [[count], attributes, *records]
203
201
  end
204
202
 
205
203
  def convert_attributes(attributes)
@@ -210,6 +208,20 @@ module Droonga
210
208
  [name, type]
211
209
  end
212
210
  end
211
+
212
+ def convert_records(attributes, records)
213
+ records.collect do |record|
214
+ record.collect.each_with_index do |value, i|
215
+ name, type = attributes[i]
216
+ case type
217
+ when "Time"
218
+ normalize_time(value).to_f
219
+ else
220
+ value
221
+ end
222
+ end
223
+ end
224
+ end
213
225
  end
214
226
 
215
227
  class Adapter < Droonga::Adapter
@@ -76,6 +76,28 @@ module Droonga
76
76
  end
77
77
  end
78
78
 
79
+ attributes_mapper = elements["attributes"]
80
+ if attributes_mapper and value["attributes"]
81
+ attributes = value["attributes"]
82
+ output_attributes = []
83
+ attributes_mapper["names"].each do |name|
84
+ if name == "*"
85
+ attributes.each do |attribute|
86
+ next if attribute["name"].start_with?("_")
87
+ output_attributes << attribute
88
+ end
89
+ else
90
+ attributes.each do |attribute|
91
+ if attribute["name"] == name
92
+ output_attributes << attribute
93
+ break
94
+ end
95
+ end
96
+ end
97
+ end
98
+ value["attributes"] = output_attributes
99
+ end
100
+
79
101
  records_mapper = elements["records"]
80
102
  if records_mapper and value["records"]
81
103
  if records_mapper["no_output"]
@@ -99,8 +121,14 @@ module Droonga
99
121
  complex_item
100
122
  end
101
123
  else
102
- items.collect do |item|
103
- item[0...attributes.size]
124
+ # FIXME: Compare with "attributes" value from "search" not
125
+ # gather parameter like the following.
126
+ if attributes.include?("*")
127
+ items
128
+ else
129
+ items.collect do |item|
130
+ item[0...attributes.size]
131
+ end
104
132
  end
105
133
  end
106
134
  end
@@ -77,18 +77,9 @@ module Droonga
77
77
  end
78
78
 
79
79
  def transform_query(input_name, query)
80
- output = query["output"]
81
-
82
- # Skip reducing phase for a result with no output.
83
- if output.nil? or
84
- output["elements"].nil? or
85
- (!output["elements"].include?("count") and
86
- !output["elements"].include?("records"))
87
- return
88
- end
80
+ return unless need_reduce?(query)
89
81
 
90
82
  transformer = QueryTransformer.new(query)
91
-
92
83
  elements = transformer.mappers
93
84
  mapper = {}
94
85
  mapper["elements"] = elements unless elements.empty?
@@ -96,6 +87,19 @@ module Droonga
96
87
  :gather => mapper })
97
88
  end
98
89
 
90
+ def need_reduce?(query)
91
+ output = query["output"]
92
+ return false if output.nil?
93
+
94
+ output_elements = output["elements"]
95
+ return false if output_elements.nil?
96
+
97
+ need_reduce_elements = ["count", "attributes", "records"]
98
+ output_elements.any? do |element|
99
+ need_reduce_elements.include?(element)
100
+ end
101
+ end
102
+
99
103
  class QueryTransformer
100
104
  attr_reader :reducers, :mappers
101
105
 
@@ -123,6 +127,7 @@ module Droonga
123
127
  calculate_offset_and_limit!
124
128
  build_count_mapper_and_reducer!
125
129
  build_elapsed_time_mapper_and_reducer!
130
+ build_attributes_mapper_and_reducer!
126
131
  build_records_mapper_and_reducer!
127
132
  end
128
133
 
@@ -265,6 +270,18 @@ module Droonga
265
270
  }
266
271
  end
267
272
 
273
+ def build_attributes_mapper_and_reducer!
274
+ return unless @output["elements"].include?("attributes")
275
+
276
+ @reducers["attributes"] = {
277
+ "type" => "or",
278
+ }
279
+
280
+ @mappers["attributes"] = {
281
+ "names" => output_attribute_names,
282
+ }
283
+ end
284
+
268
285
  def build_records_mapper_and_reducer!
269
286
  # Skip reducing phase for a result with no record output.
270
287
  return if !@output["elements"].include?("records") || @records_limit.zero?
@@ -325,7 +342,7 @@ module Droonga
325
342
  }
326
343
  when Hash
327
344
  attribute["label"] = key
328
- attribute
345
+ attribute
329
346
  end
330
347
  end
331
348
  else
@@ -15,6 +15,7 @@
15
15
 
16
16
  require "droonga/loggable"
17
17
  require "droonga/handler_runner"
18
+ require "fileutils"
18
19
 
19
20
  module Droonga
20
21
  class Processor
@@ -46,6 +47,9 @@ module Droonga
46
47
  synchronous = @handler_runner.prefer_synchronous?(type)
47
48
  if @n_workers.zero? or synchronous
48
49
  @handler_runner.process(message)
50
+ #XXX Workaround to restart system by any schema change.
51
+ # This should be done more smartly...
52
+ FileUtils.touch(Path.catalog.to_s) if synchronous
49
53
  else
50
54
  @job_pusher.push(message)
51
55
  end
@@ -663,6 +663,7 @@ module Droonga
663
663
 
664
664
  def output_target_attributes
665
665
  attributes = @request.output["attributes"]
666
+ attributes = expand_attributes(attributes)
666
667
  normalize_target_attributes(attributes)
667
668
  end
668
669
 
@@ -675,6 +676,31 @@ module Droonga
675
676
  formatter.format(output_target_attributes, @result.records, output_limit, output_offset)
676
677
  end
677
678
 
679
+ def expand_attributes(attributes, domain = @result.records)
680
+ expanded_attributes = []
681
+ attributes.each do |attribute|
682
+ if attribute.is_a?(String)
683
+ source = attribute
684
+ else
685
+ source = attribute["source"]
686
+ end
687
+ if source == "*"
688
+ real_table = domain
689
+ loop do
690
+ next_domain = real_table.domain
691
+ break unless next_domain.is_a?(Groonga::Table)
692
+ real_table = next_domain
693
+ end
694
+ real_table.columns.each do |column|
695
+ expanded_attributes << column.local_name
696
+ end
697
+ else
698
+ expanded_attributes << attribute
699
+ end
700
+ end
701
+ expanded_attributes
702
+ end
703
+
678
704
  def normalize_target_attributes(attributes, domain = @result.records)
679
705
  attributes.collect do |attribute|
680
706
  if attribute.is_a?(String)
@@ -0,0 +1,119 @@
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
+ require "droonga/path"
17
+ require "droonga/loggable"
18
+ require "droonga/catalog_loader"
19
+ require "droonga/serf_downloader"
20
+
21
+ module Droonga
22
+ class Serf
23
+ class << self
24
+ def path
25
+ Droonga::Path.base + "serf"
26
+ end
27
+ end
28
+
29
+ include Loggable
30
+
31
+ def initialize(loop, name)
32
+ @loop = loop
33
+ @name = name
34
+ @pid = nil
35
+ end
36
+
37
+ def start
38
+ logger.trace("start: start")
39
+ ensure_serf
40
+ ENV["SERF"] = @serf
41
+ ENV["SERF_RPC_ADDRESS"] = rpc_address
42
+ retry_joins = []
43
+ detect_other_hosts.each do |other_host|
44
+ retry_joins.push("-retry-join", other_host)
45
+ end
46
+ @pid = run("agent",
47
+ "-node", @name,
48
+ "-bind", extract_host(@name),
49
+ "-event-handler", "droonga-engine-serf-event-handler",
50
+ *retry_joins)
51
+ logger.trace("start: done")
52
+ end
53
+
54
+ def running?
55
+ not @pid.nil?
56
+ end
57
+
58
+ def shutdown
59
+ logger.trace("shutdown: start")
60
+ Process.waitpid(run("leave"))
61
+ Process.waitpid(@pid)
62
+ @pid = nil
63
+ logger.trace("shutdown: done")
64
+ end
65
+
66
+ def restart
67
+ shutdown
68
+ start
69
+ end
70
+
71
+ private
72
+ def ensure_serf
73
+ @serf = find_system_serf
74
+ return if @serf
75
+
76
+ serf_path = self.class.path
77
+ @serf = serf_path.to_s
78
+ return if serf_path.executable?
79
+ downloader = SerfDownloader.new(serf_path)
80
+ downloader.download
81
+ end
82
+
83
+ def find_system_serf
84
+ paths = (ENV["PATH"] || "").split(File::PATH_SEPARATOR)
85
+ paths.each do |path|
86
+ serf = File.join(path, "serf")
87
+ return serf if File.executable?(serf)
88
+ end
89
+ nil
90
+ end
91
+
92
+ def run(command, *options)
93
+ spawn(@serf, command, "-rpc-addr", rpc_address, *options)
94
+ end
95
+
96
+ def extract_host(node_name)
97
+ node_name.split(":").first
98
+ end
99
+
100
+ def rpc_address
101
+ "#{extract_host(@name)}:7373"
102
+ end
103
+
104
+ def detect_other_hosts
105
+ loader = CatalogLoader.new(Path.catalog.to_s)
106
+ catalog = loader.load
107
+ other_nodes = catalog.all_nodes.reject do |node|
108
+ node == @name
109
+ end
110
+ other_nodes.collect do |node|
111
+ extract_host(node)
112
+ end
113
+ end
114
+
115
+ def log_tag
116
+ "serf"
117
+ end
118
+ end
119
+ end
@@ -0,0 +1,90 @@
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
+ require "stringio"
17
+ require "tmpdir"
18
+ require "fileutils"
19
+
20
+ require "faraday"
21
+ require "faraday_middleware"
22
+ require "archive/zip"
23
+
24
+ require "droonga/loggable"
25
+
26
+ module Droonga
27
+ class SerfDownloader
28
+ include Loggable
29
+
30
+ def initialize(output_path)
31
+ @output_path = output_path
32
+ end
33
+
34
+ def download
35
+ detect_platform
36
+ version = "0.6.0"
37
+ url_base = "https://dl.bintray.com/mitchellh/serf"
38
+ base_name = "#{version}_#{@os}_#{@architecture}.zip"
39
+ connection = Faraday.new(url_base) do |builder|
40
+ builder.response(:follow_redirects)
41
+ builder.adapter(Faraday.default_adapter)
42
+ end
43
+ response = connection.get(base_name)
44
+ absolete_output_path = @output_path.expand_path
45
+ Dir.mktmpdir do |dir|
46
+ Archive::Zip.extract(StringIO.new(response.body),
47
+ dir,
48
+ :directories => false)
49
+ FileUtils.mv("#{dir}/serf", absolete_output_path.to_s)
50
+ FileUtils.chmod(0755, absolete_output_path.to_s)
51
+ end
52
+ end
53
+
54
+ private
55
+ def detect_platform
56
+ detect_os
57
+ detect_architecture
58
+ end
59
+
60
+ def detect_os
61
+ case RUBY_PLATFORM
62
+ when /linux/
63
+ @os = "linux"
64
+ when /freebsd/
65
+ @os = "freebsd"
66
+ when /darwin/
67
+ @os = "darwin"
68
+ when /mswin|mingw/
69
+ @os = "windows"
70
+ else
71
+ raise "Unsupported OS: #{RUBY_PLATFORM}"
72
+ end
73
+ end
74
+
75
+ def detect_architecture
76
+ case RUBY_PLATFORM
77
+ when /x86_64|x64/
78
+ @architecture = "amd64"
79
+ when /i\d86/
80
+ @architecture = "i386"
81
+ else
82
+ raise "Unsupported architecture: #{RUBY_PLATFORM}"
83
+ end
84
+ end
85
+
86
+ def log_tag
87
+ "serf-downloader"
88
+ end
89
+ end
90
+ end