droonga-engine 1.0.9 → 1.1.0

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 (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