droonga-engine 1.0.9 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (195) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +1 -0
  3. data/benchmark/timer-watcher/benchmark.rb +44 -0
  4. data/bin/droonga-engine-absorb-data +246 -187
  5. data/bin/droonga-engine-catalog-generate +12 -12
  6. data/bin/droonga-engine-catalog-modify +4 -4
  7. data/bin/droonga-engine-join +352 -171
  8. data/bin/droonga-engine-set-role +54 -0
  9. data/bin/droonga-engine-unjoin +107 -112
  10. data/droonga-engine.gemspec +3 -3
  11. data/install.sh +55 -36
  12. data/install/centos/functions.sh +2 -2
  13. data/install/debian/functions.sh +2 -2
  14. data/lib/droonga/address.rb +26 -24
  15. data/lib/droonga/buffered_tcp_socket.rb +65 -10
  16. data/lib/droonga/catalog/base.rb +9 -6
  17. data/lib/droonga/catalog/dataset.rb +17 -41
  18. data/lib/droonga/catalog/fetcher.rb +64 -0
  19. data/lib/droonga/catalog/generator.rb +245 -0
  20. data/lib/droonga/catalog/loader.rb +66 -0
  21. data/lib/droonga/{catalog_modifier.rb → catalog/modifier.rb} +11 -18
  22. data/lib/droonga/catalog/replicas_volume.rb +123 -0
  23. data/lib/droonga/catalog/schema.rb +37 -37
  24. data/lib/droonga/catalog/single_volume.rb +11 -3
  25. data/lib/droonga/catalog/slice.rb +10 -6
  26. data/lib/droonga/catalog/{collection_volume.rb → slices_volume.rb} +47 -11
  27. data/lib/droonga/catalog/version1.rb +47 -19
  28. data/lib/droonga/catalog/version2.rb +11 -10
  29. data/lib/droonga/catalog/version2_validator.rb +4 -4
  30. data/lib/droonga/catalog/volume.rb +17 -5
  31. data/lib/droonga/changable.rb +25 -0
  32. data/lib/droonga/cluster.rb +237 -0
  33. data/lib/droonga/collector_runner.rb +4 -0
  34. data/lib/droonga/collectors.rb +2 -1
  35. data/lib/droonga/collectors/recursive_sum.rb +26 -0
  36. data/lib/droonga/command/droonga_engine.rb +404 -127
  37. data/lib/droonga/command/droonga_engine_service.rb +47 -11
  38. data/lib/droonga/command/droonga_engine_worker.rb +21 -1
  39. data/lib/droonga/command/remote_command_base.rb +78 -0
  40. data/lib/droonga/command/serf_event_handler.rb +29 -20
  41. data/lib/droonga/data_absorber_client.rb +222 -0
  42. data/lib/droonga/database_scanner.rb +106 -0
  43. data/lib/droonga/{live_nodes_list_loader.rb → deferrable.rb} +11 -24
  44. data/lib/droonga/differ.rb +58 -0
  45. data/lib/droonga/dispatcher.rb +155 -32
  46. data/lib/droonga/distributed_command_planner.rb +9 -11
  47. data/lib/droonga/engine.rb +83 -78
  48. data/lib/droonga/engine/version.rb +1 -1
  49. data/lib/droonga/engine_node.rb +301 -0
  50. data/lib/droonga/engine_state.rb +62 -40
  51. data/lib/droonga/farm.rb +44 -5
  52. data/lib/droonga/file_observer.rb +16 -12
  53. data/lib/droonga/fluent_message_receiver.rb +98 -29
  54. data/lib/droonga/fluent_message_sender.rb +30 -23
  55. data/lib/droonga/forward_buffer.rb +160 -0
  56. data/lib/droonga/forwarder.rb +73 -40
  57. data/lib/droonga/handler.rb +7 -6
  58. data/lib/droonga/handler_messenger.rb +15 -6
  59. data/lib/droonga/handler_runner.rb +6 -1
  60. data/lib/droonga/internal_fluent_message_receiver.rb +28 -8
  61. data/lib/droonga/job_pusher.rb +10 -7
  62. data/lib/droonga/job_receiver.rb +6 -4
  63. data/lib/droonga/logger.rb +7 -1
  64. data/lib/droonga/node_name.rb +90 -0
  65. data/lib/droonga/node_role.rb +72 -0
  66. data/lib/droonga/path.rb +34 -9
  67. data/lib/droonga/planner.rb +73 -7
  68. data/lib/droonga/plugin/async_command.rb +154 -0
  69. data/lib/droonga/plugins/catalog.rb +1 -0
  70. data/lib/droonga/plugins/crud.rb +22 -6
  71. data/lib/droonga/plugins/dump.rb +66 -135
  72. data/lib/droonga/plugins/groonga/delete.rb +13 -0
  73. data/lib/droonga/plugins/search/distributed_search_planner.rb +4 -4
  74. data/lib/droonga/plugins/system.rb +5 -26
  75. data/lib/droonga/plugins/system/absorb_data.rb +405 -0
  76. data/lib/droonga/plugins/system/statistics.rb +71 -0
  77. data/lib/droonga/plugins/system/status.rb +53 -0
  78. data/lib/droonga/process_control_protocol.rb +3 -1
  79. data/lib/droonga/process_supervisor.rb +32 -15
  80. data/lib/droonga/reducer.rb +69 -0
  81. data/lib/droonga/safe_file_writer.rb +1 -1
  82. data/lib/droonga/serf.rb +207 -276
  83. data/lib/droonga/serf/agent.rb +228 -0
  84. data/lib/droonga/serf/command.rb +94 -0
  85. data/lib/droonga/serf/downloader.rb +120 -0
  86. data/lib/droonga/serf/remote_command.rb +348 -0
  87. data/lib/droonga/serf/tag.rb +56 -0
  88. data/lib/droonga/service_installation.rb +2 -2
  89. data/lib/droonga/session.rb +49 -1
  90. data/lib/droonga/single_step.rb +6 -11
  91. data/lib/droonga/single_step_definition.rb +32 -1
  92. data/lib/droonga/slice.rb +14 -9
  93. data/lib/droonga/supervisor.rb +27 -20
  94. data/lib/droonga/test/stub_handler_messenger.rb +2 -1
  95. data/lib/droonga/timestamp.rb +69 -0
  96. data/lib/droonga/worker_process_agent.rb +33 -15
  97. data/sample/cluster-state.json +8 -0
  98. data/sample/cluster/Rakefile +30 -6
  99. data/test/command/fixture/integer-key-table.jsons +11 -0
  100. data/test/command/fixture/string-key-table.jsons +11 -0
  101. data/test/command/run-test.rb +4 -0
  102. data/test/command/suite/add/error/invalid-integer.expected +3 -3
  103. data/test/command/suite/add/error/invalid-time.expected +3 -3
  104. data/test/command/suite/add/{minimum.expected → key-integer.expected} +0 -0
  105. data/test/command/suite/add/{minimum.test → key-integer.test} +0 -0
  106. data/test/command/suite/add/key-string.expected +6 -0
  107. data/test/command/suite/add/key-string.test +9 -0
  108. data/test/command/suite/add/mismatched-key-type/acceptable/integer-for-string.expected +6 -0
  109. data/test/command/suite/add/mismatched-key-type/acceptable/integer-for-string.test +9 -0
  110. data/test/command/suite/add/mismatched-key-type/acceptable/string-for-integer.expected +6 -0
  111. data/test/command/suite/add/mismatched-key-type/acceptable/string-for-integer.test +9 -0
  112. data/test/command/suite/add/without-values.expected +6 -0
  113. data/test/command/suite/add/without-values.test +11 -0
  114. data/test/command/suite/dump/column/index.expected +33 -1
  115. data/test/command/suite/dump/column/index.test +1 -0
  116. data/test/command/suite/dump/column/scalar.expected +29 -1
  117. data/test/command/suite/dump/column/scalar.test +1 -0
  118. data/test/command/suite/dump/column/vector.expected +29 -1
  119. data/test/command/suite/dump/column/vector.test +1 -0
  120. data/test/command/suite/dump/record/scalar.catalog.json +12 -0
  121. data/test/command/suite/dump/record/scalar.expected +84 -0
  122. data/test/command/suite/dump/record/scalar.test +16 -0
  123. data/test/command/suite/dump/record/vector/reference.expected +83 -1
  124. data/test/command/suite/dump/record/vector/reference.test +1 -0
  125. data/test/command/suite/dump/table/array.expected +27 -1
  126. data/test/command/suite/dump/table/array.test +1 -0
  127. data/test/command/suite/dump/table/double_array_trie.expected +27 -1
  128. data/test/command/suite/dump/table/double_array_trie.test +1 -0
  129. data/test/command/suite/dump/table/hash.expected +27 -1
  130. data/test/command/suite/dump/table/hash.test +1 -0
  131. data/test/command/suite/dump/table/patricia_trie.expected +27 -1
  132. data/test/command/suite/dump/table/patricia_trie.test +1 -0
  133. data/test/command/suite/groonga/delete/{success.expected → key-integer.expected} +0 -0
  134. data/test/command/suite/groonga/delete/key-integer.test +17 -0
  135. data/test/command/suite/groonga/delete/key-string.expected +19 -0
  136. data/test/command/suite/groonga/delete/{success.test → key-string.test} +4 -6
  137. data/test/command/suite/groonga/delete/mismatched-type-key/acceptable/integer-for-string.expected +19 -0
  138. data/test/command/suite/groonga/delete/mismatched-type-key/acceptable/integer-for-string.test +17 -0
  139. data/test/command/suite/groonga/delete/mismatched-type-key/acceptable/string-for-integer.expected +19 -0
  140. data/test/command/suite/groonga/delete/mismatched-type-key/acceptable/string-for-integer.test +17 -0
  141. data/test/command/suite/message/error/missing-dataset.test +1 -0
  142. data/test/command/suite/system/absorb-data/records.catalog.json +58 -0
  143. data/test/command/suite/system/absorb-data/records.expected +32 -0
  144. data/test/command/suite/system/absorb-data/records.test +24 -0
  145. data/test/command/suite/system/statistics/object/count/empty.expected +11 -0
  146. data/test/command/suite/system/statistics/object/count/empty.test +12 -0
  147. data/test/command/suite/system/statistics/object/count/per-volume/empty.catalog.json +36 -0
  148. data/test/command/suite/system/statistics/object/count/per-volume/empty.expected +19 -0
  149. data/test/command/suite/system/statistics/object/count/per-volume/empty.test +12 -0
  150. data/test/command/suite/system/statistics/object/count/per-volume/record.catalog.json +40 -0
  151. data/test/command/suite/system/statistics/object/count/per-volume/record.expected +19 -0
  152. data/test/command/suite/system/statistics/object/count/per-volume/record.test +23 -0
  153. data/test/command/suite/system/statistics/object/count/per-volume/schema.catalog.json +40 -0
  154. data/test/command/suite/system/statistics/object/count/per-volume/schema.expected +19 -0
  155. data/test/command/suite/system/statistics/object/count/per-volume/schema.test +13 -0
  156. data/test/command/suite/system/statistics/object/count/record.catalog.json +12 -0
  157. data/test/command/suite/system/statistics/object/count/record.expected +11 -0
  158. data/test/command/suite/system/statistics/object/count/record.test +23 -0
  159. data/test/command/suite/system/statistics/object/count/schema.catalog.json +12 -0
  160. data/test/command/suite/system/statistics/object/count/schema.expected +11 -0
  161. data/test/command/suite/system/statistics/object/count/schema.test +13 -0
  162. data/test/command/suite/system/status.expected +3 -2
  163. data/test/unit/catalog/test_dataset.rb +4 -1
  164. data/test/unit/{test_catalog_generator.rb → catalog/test_generator.rb} +2 -2
  165. data/test/unit/catalog/test_replicas_volume.rb +79 -0
  166. data/test/unit/catalog/test_single_volume.rb +2 -2
  167. data/test/unit/catalog/test_slice.rb +33 -1
  168. data/test/unit/catalog/{test_collection_volume.rb → test_slices_volume.rb} +72 -11
  169. data/test/unit/catalog/test_version2.rb +3 -0
  170. data/test/unit/helper/distributed_search_planner_helper.rb +2 -2
  171. data/test/unit/plugins/catalog/test_fetch.rb +4 -4
  172. data/test/unit/plugins/crud/test_add.rb +44 -4
  173. data/test/unit/plugins/groonga/test_column_create.rb +4 -4
  174. data/test/unit/plugins/groonga/test_column_list.rb +4 -4
  175. data/test/unit/plugins/groonga/test_column_remove.rb +4 -4
  176. data/test/unit/plugins/groonga/test_column_rename.rb +4 -4
  177. data/test/unit/plugins/groonga/test_delete.rb +73 -10
  178. data/test/unit/plugins/groonga/test_table_create.rb +4 -4
  179. data/test/unit/plugins/groonga/test_table_list.rb +4 -4
  180. data/test/unit/plugins/groonga/test_table_remove.rb +4 -4
  181. data/test/unit/plugins/search/test_handler.rb +4 -4
  182. data/test/unit/plugins/search/test_planner.rb +4 -2
  183. data/test/unit/plugins/system/test_status.rb +31 -15
  184. data/test/unit/plugins/test_watch.rb +16 -16
  185. data/test/unit/test_address.rb +4 -4
  186. metadata +134 -35
  187. data/lib/droonga/catalog/volume_collection.rb +0 -79
  188. data/lib/droonga/catalog_fetcher.rb +0 -53
  189. data/lib/droonga/catalog_generator.rb +0 -243
  190. data/lib/droonga/catalog_loader.rb +0 -56
  191. data/lib/droonga/command/remote.rb +0 -404
  192. data/lib/droonga/data_absorber.rb +0 -264
  193. data/lib/droonga/node_status.rb +0 -71
  194. data/lib/droonga/serf_downloader.rb +0 -115
  195. data/test/unit/catalog/test_volume_collection.rb +0 -78
@@ -1,79 +0,0 @@
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
- module Droonga
17
- module Catalog
18
- class VolumeCollection
19
- include Enumerable
20
-
21
- def initialize(volumes)
22
- @volumes = volumes
23
- end
24
-
25
- def each(&block)
26
- @volumes.each(&block)
27
- end
28
-
29
- def ==(other)
30
- other.is_a?(self.class) and
31
- to_a == other.to_a
32
- end
33
-
34
- def eql?(other)
35
- self == other
36
- end
37
-
38
- def hash
39
- to_a.hash
40
- end
41
-
42
- def select(how=nil, live_nodes=nil)
43
- volumes = live_volumes(live_nodes)
44
- case how
45
- when :top
46
- [volumes.first]
47
- when :random
48
- [volumes.sample]
49
- when :all
50
- @volumes
51
- else
52
- super
53
- end
54
- end
55
-
56
- def all_nodes
57
- @all_nodes ||= collect_all_nodes
58
- end
59
-
60
- def live_volumes(live_nodes=nil)
61
- return @volumes unless live_nodes
62
-
63
- @volumes.select do |volume|
64
- dead_nodes = volume.all_nodes - live_nodes
65
- dead_nodes.empty?
66
- end
67
- end
68
-
69
- private
70
- def collect_all_nodes
71
- nodes = []
72
- @volumes.each do |volume|
73
- nodes += volume.all_nodes
74
- end
75
- nodes.sort.uniq
76
- end
77
- end
78
- end
79
- end
@@ -1,53 +0,0 @@
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 "socket"
17
-
18
- require "droonga/client"
19
-
20
- require "droonga/address"
21
- require "droonga/catalog/dataset"
22
-
23
- module Droonga
24
- class CatalogFetcher
25
- def initialize(client_options)
26
- @client_options = default_options.merge(client_options)
27
- end
28
-
29
- def fetch(options={})
30
- message = {
31
- "dataset" => options[:dataset] || Catalog::Dataset::DEFAULT_NAME,
32
- "type" => "catalog.fetch"
33
- }
34
- Droonga::Client.open(@client_options) do |client|
35
- response = client.request(message)
36
- response["body"]
37
- end
38
- end
39
-
40
- private
41
- def default_options
42
- {
43
- :host => "127.0.0.1",
44
- :port => Address::DEFAULT_PORT,
45
- :tag => Address::DEFAULT_TAG,
46
- :protocol => :droonga,
47
- :timeout => 1,
48
- :receiver_host => Socket.gethostname,
49
- :receiver_port => 0,
50
- }
51
- end
52
- end
53
- end
@@ -1,243 +0,0 @@
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 "time"
17
-
18
- require "droonga/address"
19
- require "droonga/catalog/dataset"
20
-
21
- module Droonga
22
- class CatalogGenerator
23
- DEFAULT_DATASET = Catalog::Dataset::DEFAULT_NAME
24
- DEFAULT_HOSTS = [Address::DEFAULT_HOST]
25
- DEFAULT_N_WORKERS = 4
26
- DEFAULT_N_SLICES = 1
27
- DEFAULT_PLUGINS = ["groonga", "search", "crud", "dump", "system", "catalog"]
28
- DEFAULT_PORT = Address::DEFAULT_PORT
29
- DEFAULT_TAG = Address::DEFAULT_TAG
30
-
31
- attr_reader :datasets
32
-
33
- class << self
34
- def generate(datasets_params)
35
- generator = new
36
- datasets_params.each do |name, params|
37
- generator.add_dataset(name, params)
38
- end
39
- generator.generate
40
- end
41
- end
42
-
43
- def initialize
44
- @version = 2
45
- @effective_date = Time.now
46
- @datasets = {}
47
- end
48
-
49
- def add_dataset(name, options)
50
- @datasets[name] = Dataset.new(name, options)
51
- end
52
-
53
- def generate
54
- {
55
- "version" => @version,
56
- "effectiveDate" => @effective_date.iso8601,
57
- "datasets" => catalog_datasets,
58
- }
59
- end
60
-
61
- def load(catalog)
62
- catalog["datasets"].each do |name, catalog_dataset|
63
- load_dataset(name, catalog_dataset)
64
- end
65
- self
66
- end
67
-
68
- def dataset_for_host(host)
69
- @datasets.each do |name, dataset|
70
- if dataset.replicas.hosts.include?(host)
71
- return dataset
72
- end
73
- end
74
- nil
75
- end
76
-
77
- def modify(dataset_modifications)
78
- dataset_modifications.each do |name, modification|
79
- dataset = @datasets[name]
80
- next unless dataset
81
-
82
- replicas = dataset.replicas
83
-
84
- if modification[:replica_hosts]
85
- replicas.hosts = modification[:replica_hosts]
86
- end
87
-
88
- if modification[:add_replica_hosts]
89
- replicas.hosts += modification[:add_replica_hosts]
90
- replicas.hosts.uniq!
91
- end
92
-
93
- if modification[:remove_replica_hosts]
94
- replicas.hosts -= modification[:remove_replica_hosts]
95
- end
96
- end
97
- end
98
-
99
- private
100
- def catalog_datasets
101
- catalog_datasets = {}
102
- @datasets.each do |name, dataset|
103
- catalog_datasets[name] = dataset.to_catalog
104
- end
105
- catalog_datasets
106
- end
107
-
108
- def load_dataset(name, catalog_dataset)
109
- options = {}
110
- options[:n_workers] = catalog_dataset["nWorkers"]
111
- options[:plugins] = catalog_dataset["plugins"]
112
- options[:schema] = catalog_dataset["schema"]
113
- options[:fact] = catalog_dataset["fact"]
114
- options[:replicas] = catalog_dataset["replicas"]
115
- add_dataset(name, options)
116
- end
117
-
118
- class Dataset
119
- attr_reader :name
120
-
121
- def initialize(name, options)
122
- @name = name
123
- @options = options
124
- end
125
-
126
- def n_workers
127
- @options[:n_workers] || DEFAULT_N_WORKERS
128
- end
129
-
130
- def plugins
131
- @options[:plugins] || DEFAULT_PLUGINS
132
- end
133
-
134
- def schema
135
- @options[:schema] || {}
136
- end
137
-
138
- def fact
139
- @options[:fact]
140
- end
141
-
142
- def replicas
143
- @replicas ||= create_replicas
144
- end
145
-
146
- def to_catalog
147
- catalog = {
148
- "nWorkers" => n_workers,
149
- "plugins" => plugins,
150
- "schema" => schema,
151
- "replicas" => replicas.to_catalog,
152
- }
153
- catalog["fact"] = fact if fact
154
- catalog
155
- end
156
-
157
- private
158
- def create_replicas
159
- catalog_replicas = @options[:replicas]
160
- if catalog_replicas
161
- replicas = Replicas.new
162
- replicas.load(catalog_replicas)
163
- replicas
164
- else
165
- Replicas.new(@options)
166
- end
167
- end
168
- end
169
-
170
- class Replicas
171
- attr_accessor :hosts
172
- attr_reader :port, :tag, :n_slices
173
-
174
- def initialize(options={})
175
- @hosts = options[:hosts] || DEFAULT_HOSTS
176
- @port = options[:port]
177
- @tag = options[:tag]
178
- @n_slices = options[:n_slices]
179
- end
180
-
181
- def load(catalog_replicas)
182
- dataset = Catalog::Dataset.new("temporary",
183
- "replicas" => catalog_replicas)
184
- @hosts = dataset.replicas.collect do |replica|
185
- replica.slices.first.volume.address.host
186
- end
187
- collection_volume = dataset.replicas.first
188
- slices = collection_volume.slices
189
- @n_slices = slices.size
190
- single_volume_address = slices.first.volume.address
191
- @port = single_volume_address.port
192
- @tag = single_volume_address.tag
193
- end
194
-
195
- def to_catalog
196
- catalog_replicas = []
197
- @hosts.each do |host|
198
- replica = Replica.new(host, :port => @port,
199
- :tag => @tag,
200
- :n_slices => @n_slices)
201
- catalog_replicas << replica.to_catalog
202
- end
203
- catalog_replicas
204
- end
205
- end
206
-
207
- class Replica
208
- def initialize(host, options={})
209
- @host = host
210
- @port = options[:port] || DEFAULT_PORT
211
- @tag = options[:tag] || DEFAULT_TAG
212
- @n_slices = options[:n_slices] || DEFAULT_N_SLICES
213
- end
214
-
215
- def to_catalog
216
- slices = []
217
- @n_slices.times do |i|
218
- slices << catalog_slice(i)
219
- end
220
- {
221
- "dimension" => "_key",
222
- "slicer" => "hash",
223
- "slices" => slices,
224
- }
225
- end
226
-
227
- private
228
- def catalog_slice(nth_slice)
229
- name = "%03d" % nth_slice
230
- {
231
- "weight" => weight,
232
- "volume" => {
233
- "address" => "#{@host}:#{@port}/#{@tag}.#{name}",
234
- },
235
- }
236
- end
237
-
238
- def weight
239
- @weight ||= 100 / @n_slices
240
- end
241
- end
242
- end
243
- end
@@ -1,56 +0,0 @@
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
15
-
16
- require "json"
17
-
18
- require "droonga/catalog/version1"
19
- require "droonga/catalog/version2"
20
-
21
- module Droonga
22
- class CatalogLoader
23
- def initialize(path)
24
- @path = path
25
- end
26
-
27
- def load
28
- data = nil
29
- begin
30
- data = File.open(@path) do |file|
31
- JSON.parse(file.read)
32
- end
33
- rescue Errno::ENOENT => error
34
- raise Error.new("Missing catalog file #{@path}")
35
- rescue JSON::ParserError => error
36
- raise Error.new("Syntax error in #{@path}:\n#{error.to_s}")
37
- end
38
-
39
- unless data.is_a?(Hash)
40
- raise Error.new("Root element of catalog must be an object in #{@path}")
41
- end
42
-
43
- version = data["version"]
44
- case version
45
- when 1
46
- Catalog::Version1.new(data, @path)
47
- when 2
48
- Catalog::Version2.new(data, @path)
49
- when nil
50
- raise Error.new("Catalog version must be specified in #{@path}")
51
- else
52
- raise Error.new("Unsupported catalog version <#{version}> is specified in #{@path}")
53
- end
54
- end
55
- end
56
- end
@@ -1,404 +0,0 @@
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 SetStatus < Base
93
- def process
94
- status = NodeStatus.new
95
- status.set(@params["key"], @params["value"])
96
- end
97
- end
98
-
99
- class Join < Base
100
- def process
101
- log("type = #{type}")
102
- case type
103
- when "replica"
104
- join_as_replica
105
- end
106
- end
107
-
108
- private
109
- def type
110
- @params["type"]
111
- end
112
-
113
- def source_node
114
- @params["source"]
115
- end
116
-
117
- def joining_node
118
- @params["node"]
119
- end
120
-
121
- def dataset_name
122
- @params["dataset"]
123
- end
124
-
125
- def messages_per_second
126
- @params["messages_per_second"]
127
- end
128
-
129
- def valid_params?
130
- have_required_params? and
131
- valid_node?(source_node) and
132
- valid_node?(joining_node)
133
- end
134
-
135
- def have_required_params?
136
- required_params = [
137
- source_node,
138
- joining_node,
139
- dataset_name,
140
- ]
141
- required_params.all? do |param|
142
- not param.nil?
143
- end
144
- end
145
-
146
- NODE_PATTERN = /\A([^:]+):(\d+)\/(.+)\z/
147
-
148
- def valid_node?(node)
149
- node =~ NODE_PATTERN
150
- end
151
-
152
- def source_host
153
- @source_host ||= (source_node =~ NODE_PATTERN && $1)
154
- end
155
-
156
- def joining_host
157
- @joining_host ||= (joining_node =~ NODE_PATTERN && $1)
158
- end
159
-
160
- def port
161
- @port ||= (source_node =~ NODE_PATTERN && $2 && $2.to_i)
162
- end
163
-
164
- def tag
165
- @tag ||= (source_node =~ NODE_PATTERN && $3)
166
- end
167
-
168
- def should_absorb_data?
169
- @params["copy"]
170
- end
171
-
172
- def join_as_replica
173
- return unless valid_params?
174
-
175
- log("source_node = #{source_node}")
176
-
177
- @catalog = fetch_catalog
178
-
179
- other_hosts = replica_hosts
180
- log("other_hosts = #{other_hosts}")
181
- return if other_hosts.empty?
182
-
183
- # restart self with the fetched catalog.
184
- SafeFileWriter.write(Path.catalog) do |output, file|
185
- output.puts(JSON.pretty_generate(@catalog))
186
- @service_installation.ensure_correct_file_permission(file)
187
- end
188
-
189
- absorb_data if should_absorb_data?
190
-
191
- log("joining to the cluster: update myself")
192
-
193
- CatalogModifier.modify do |modifier, file|
194
- modifier.datasets[dataset_name].replicas.hosts += other_hosts
195
- modifier.datasets[dataset_name].replicas.hosts.uniq!
196
- @service_installation.ensure_correct_file_permission(file)
197
- end
198
-
199
- @serf.join(*other_hosts)
200
- end
201
-
202
- def replica_hosts
203
- generator = CatalogGenerator.new
204
- generator.load(@catalog)
205
- dataset = generator.dataset_for_host(source_host) ||
206
- generator.dataset_for_host(host)
207
- return [] unless dataset
208
- dataset.replicas.hosts
209
- end
210
-
211
- def fetch_catalog
212
- fetcher = CatalogFetcher.new(:host => source_host,
213
- :port => port,
214
- :tag => tag,
215
- :receiver_host => host)
216
- fetcher.fetch(:dataset => dataset_name)
217
- end
218
-
219
- def absorb_data
220
- log("starting to copy data from #{source_host}")
221
-
222
- CatalogModifier.modify do |modifier, file|
223
- modifier.datasets[dataset_name].replicas.hosts = [host]
224
- @service_installation.ensure_correct_file_permission(file)
225
- end
226
- sleep(5) #TODO: wait for restart. this should be done more safely, to avoid starting of absorbing with old catalog.json.
227
-
228
- status = NodeStatus.new
229
- status.set(:absorbing, true)
230
- DataAbsorber.absorb(:dataset => dataset_name,
231
- :source_host => source_host,
232
- :destination_host => joining_host,
233
- :port => port,
234
- :tag => tag,
235
- :messages_per_second => messages_per_second)
236
- status.delete(:absorbing)
237
- sleep(1)
238
- end
239
- end
240
-
241
- class AbsorbData < Base
242
- attr_writer :dataset_name, :port, :tag
243
-
244
- def process
245
- return unless source
246
-
247
- log("start to absorb data from #{source}")
248
-
249
- if dataset_name.nil? or port.nil? or tag.nil?
250
- current_catalog = JSON.parse(Path.catalog.read)
251
- generator = CatalogGenerator.new
252
- generator.load(current_catalog)
253
-
254
- dataset = generator.dataset_for_host(source)
255
- return unless dataset
256
-
257
- self.dataset_name = dataset.name
258
- self.port = dataset.replicas.port
259
- self.tag = dataset.replicas.tag
260
- end
261
-
262
- log("dataset = #{dataset_name}")
263
- log("port = #{port}")
264
- log("tag = #{tag}")
265
-
266
- status = NodeStatus.new
267
- status.set(:absorbing, true)
268
- DataAbsorber.absorb(:dataset => dataset_name,
269
- :source_host => source,
270
- :destination_host => host,
271
- :port => port,
272
- :tag => tag,
273
- :messages_per_second => messages_per_second,
274
- :client => "droonga-send")
275
- status.delete(:absorbing)
276
- end
277
-
278
- private
279
- def source
280
- @params["source"]
281
- end
282
-
283
- def dataset_name
284
- @dataset_name ||= @params["dataset"]
285
- end
286
-
287
- def port
288
- @port ||= @params["port"]
289
- end
290
-
291
- def tag
292
- @tag ||= @params["tag"]
293
- end
294
-
295
- def messages_per_second
296
- @messages_per_second ||= @params["messages_per_second"]
297
- end
298
- end
299
-
300
- class ModifyReplicasBase < Base
301
- private
302
- def dataset
303
- @params["dataset"]
304
- end
305
-
306
- def hosts
307
- @hosts ||= prepare_hosts
308
- end
309
-
310
- def prepare_hosts
311
- hosts = @params["hosts"]
312
- return nil unless hosts
313
- hosts = [hosts] if hosts.is_a?(String)
314
- hosts
315
- end
316
- end
317
-
318
- class SetReplicas < ModifyReplicasBase
319
- def process
320
- return if dataset.nil? or hosts.nil?
321
-
322
- log("new replicas: #{hosts.join(",")}")
323
-
324
- CatalogModifier.modify do |modifier, file|
325
- modifier.datasets[dataset].replicas.hosts = hosts
326
- @service_installation.ensure_correct_file_permission(file)
327
- end
328
-
329
- @serf.join(*hosts)
330
- end
331
- end
332
-
333
- class AddReplicas < ModifyReplicasBase
334
- def process
335
- return if dataset.nil? or hosts.nil?
336
-
337
- added_hosts = hosts - [host]
338
- log("adding replicas: #{added_hosts.join(",")}")
339
- return if added_hosts.empty?
340
-
341
- CatalogModifier.modify do |modifier, file|
342
- modifier.datasets[dataset].replicas.hosts += added_hosts
343
- modifier.datasets[dataset].replicas.hosts.uniq!
344
- @service_installation.ensure_correct_file_permission(file)
345
- end
346
-
347
- @serf.join(*added_hosts)
348
- end
349
- end
350
-
351
- class RemoveReplicas < ModifyReplicasBase
352
- def process
353
- return if dataset.nil? or hosts.nil?
354
-
355
- log("removing replicas: #{hosts.join(",")}")
356
-
357
- CatalogModifier.modify do |modifier, file|
358
- modifier.datasets[dataset].replicas.hosts -= hosts
359
- @service_installation.ensure_correct_file_permission(file)
360
- end
361
- end
362
- end
363
-
364
- class Unjoin < ModifyReplicasBase
365
- def process
366
- return if dataset.nil? or hosts.nil?
367
-
368
- log("unjoining replicas: #{hosts.join(",")}")
369
-
370
- CatalogModifier.modify do |modifier, file|
371
- if unjoining_node?
372
- modifier.datasets[dataset].replicas.hosts = hosts
373
- else
374
- modifier.datasets[dataset].replicas.hosts -= hosts
375
- end
376
- @service_installation.ensure_correct_file_permission(file)
377
- end
378
- end
379
-
380
- private
381
- def unjoining_node?
382
- hosts.include?(host)
383
- end
384
- end
385
-
386
- class UpdateLiveNodes < Base
387
- def process
388
- path = Path.live_nodes
389
- nodes = live_nodes
390
- file_contents = JSON.pretty_generate(nodes)
391
- SafeFileWriter.write(path) do |output, file|
392
- output.puts(file_contents)
393
- @service_installation.ensure_correct_file_permission(file)
394
- end
395
- end
396
-
397
- private
398
- def live_nodes
399
- @serf.live_nodes
400
- end
401
- end
402
- end
403
- end
404
- end