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.
- checksums.yaml +4 -4
- data/.travis.yml +1 -0
- data/benchmark/timer-watcher/benchmark.rb +44 -0
- data/bin/droonga-engine-absorb-data +246 -187
- data/bin/droonga-engine-catalog-generate +12 -12
- data/bin/droonga-engine-catalog-modify +4 -4
- data/bin/droonga-engine-join +352 -171
- data/bin/droonga-engine-set-role +54 -0
- data/bin/droonga-engine-unjoin +107 -112
- data/droonga-engine.gemspec +3 -3
- data/install.sh +55 -36
- data/install/centos/functions.sh +2 -2
- data/install/debian/functions.sh +2 -2
- data/lib/droonga/address.rb +26 -24
- data/lib/droonga/buffered_tcp_socket.rb +65 -10
- data/lib/droonga/catalog/base.rb +9 -6
- data/lib/droonga/catalog/dataset.rb +17 -41
- data/lib/droonga/catalog/fetcher.rb +64 -0
- data/lib/droonga/catalog/generator.rb +245 -0
- data/lib/droonga/catalog/loader.rb +66 -0
- data/lib/droonga/{catalog_modifier.rb → catalog/modifier.rb} +11 -18
- data/lib/droonga/catalog/replicas_volume.rb +123 -0
- data/lib/droonga/catalog/schema.rb +37 -37
- data/lib/droonga/catalog/single_volume.rb +11 -3
- data/lib/droonga/catalog/slice.rb +10 -6
- data/lib/droonga/catalog/{collection_volume.rb → slices_volume.rb} +47 -11
- data/lib/droonga/catalog/version1.rb +47 -19
- data/lib/droonga/catalog/version2.rb +11 -10
- data/lib/droonga/catalog/version2_validator.rb +4 -4
- data/lib/droonga/catalog/volume.rb +17 -5
- data/lib/droonga/changable.rb +25 -0
- data/lib/droonga/cluster.rb +237 -0
- data/lib/droonga/collector_runner.rb +4 -0
- data/lib/droonga/collectors.rb +2 -1
- data/lib/droonga/collectors/recursive_sum.rb +26 -0
- data/lib/droonga/command/droonga_engine.rb +404 -127
- data/lib/droonga/command/droonga_engine_service.rb +47 -11
- data/lib/droonga/command/droonga_engine_worker.rb +21 -1
- data/lib/droonga/command/remote_command_base.rb +78 -0
- data/lib/droonga/command/serf_event_handler.rb +29 -20
- data/lib/droonga/data_absorber_client.rb +222 -0
- data/lib/droonga/database_scanner.rb +106 -0
- data/lib/droonga/{live_nodes_list_loader.rb → deferrable.rb} +11 -24
- data/lib/droonga/differ.rb +58 -0
- data/lib/droonga/dispatcher.rb +155 -32
- data/lib/droonga/distributed_command_planner.rb +9 -11
- data/lib/droonga/engine.rb +83 -78
- data/lib/droonga/engine/version.rb +1 -1
- data/lib/droonga/engine_node.rb +301 -0
- data/lib/droonga/engine_state.rb +62 -40
- data/lib/droonga/farm.rb +44 -5
- data/lib/droonga/file_observer.rb +16 -12
- data/lib/droonga/fluent_message_receiver.rb +98 -29
- data/lib/droonga/fluent_message_sender.rb +30 -23
- data/lib/droonga/forward_buffer.rb +160 -0
- data/lib/droonga/forwarder.rb +73 -40
- data/lib/droonga/handler.rb +7 -6
- data/lib/droonga/handler_messenger.rb +15 -6
- data/lib/droonga/handler_runner.rb +6 -1
- data/lib/droonga/internal_fluent_message_receiver.rb +28 -8
- data/lib/droonga/job_pusher.rb +10 -7
- data/lib/droonga/job_receiver.rb +6 -4
- data/lib/droonga/logger.rb +7 -1
- data/lib/droonga/node_name.rb +90 -0
- data/lib/droonga/node_role.rb +72 -0
- data/lib/droonga/path.rb +34 -9
- data/lib/droonga/planner.rb +73 -7
- data/lib/droonga/plugin/async_command.rb +154 -0
- data/lib/droonga/plugins/catalog.rb +1 -0
- data/lib/droonga/plugins/crud.rb +22 -6
- data/lib/droonga/plugins/dump.rb +66 -135
- data/lib/droonga/plugins/groonga/delete.rb +13 -0
- data/lib/droonga/plugins/search/distributed_search_planner.rb +4 -4
- data/lib/droonga/plugins/system.rb +5 -26
- data/lib/droonga/plugins/system/absorb_data.rb +405 -0
- data/lib/droonga/plugins/system/statistics.rb +71 -0
- data/lib/droonga/plugins/system/status.rb +53 -0
- data/lib/droonga/process_control_protocol.rb +3 -1
- data/lib/droonga/process_supervisor.rb +32 -15
- data/lib/droonga/reducer.rb +69 -0
- data/lib/droonga/safe_file_writer.rb +1 -1
- data/lib/droonga/serf.rb +207 -276
- data/lib/droonga/serf/agent.rb +228 -0
- data/lib/droonga/serf/command.rb +94 -0
- data/lib/droonga/serf/downloader.rb +120 -0
- data/lib/droonga/serf/remote_command.rb +348 -0
- data/lib/droonga/serf/tag.rb +56 -0
- data/lib/droonga/service_installation.rb +2 -2
- data/lib/droonga/session.rb +49 -1
- data/lib/droonga/single_step.rb +6 -11
- data/lib/droonga/single_step_definition.rb +32 -1
- data/lib/droonga/slice.rb +14 -9
- data/lib/droonga/supervisor.rb +27 -20
- data/lib/droonga/test/stub_handler_messenger.rb +2 -1
- data/lib/droonga/timestamp.rb +69 -0
- data/lib/droonga/worker_process_agent.rb +33 -15
- data/sample/cluster-state.json +8 -0
- data/sample/cluster/Rakefile +30 -6
- data/test/command/fixture/integer-key-table.jsons +11 -0
- data/test/command/fixture/string-key-table.jsons +11 -0
- data/test/command/run-test.rb +4 -0
- data/test/command/suite/add/error/invalid-integer.expected +3 -3
- data/test/command/suite/add/error/invalid-time.expected +3 -3
- data/test/command/suite/add/{minimum.expected → key-integer.expected} +0 -0
- data/test/command/suite/add/{minimum.test → key-integer.test} +0 -0
- data/test/command/suite/add/key-string.expected +6 -0
- data/test/command/suite/add/key-string.test +9 -0
- data/test/command/suite/add/mismatched-key-type/acceptable/integer-for-string.expected +6 -0
- data/test/command/suite/add/mismatched-key-type/acceptable/integer-for-string.test +9 -0
- data/test/command/suite/add/mismatched-key-type/acceptable/string-for-integer.expected +6 -0
- data/test/command/suite/add/mismatched-key-type/acceptable/string-for-integer.test +9 -0
- data/test/command/suite/add/without-values.expected +6 -0
- data/test/command/suite/add/without-values.test +11 -0
- data/test/command/suite/dump/column/index.expected +33 -1
- data/test/command/suite/dump/column/index.test +1 -0
- data/test/command/suite/dump/column/scalar.expected +29 -1
- data/test/command/suite/dump/column/scalar.test +1 -0
- data/test/command/suite/dump/column/vector.expected +29 -1
- data/test/command/suite/dump/column/vector.test +1 -0
- data/test/command/suite/dump/record/scalar.catalog.json +12 -0
- data/test/command/suite/dump/record/scalar.expected +84 -0
- data/test/command/suite/dump/record/scalar.test +16 -0
- data/test/command/suite/dump/record/vector/reference.expected +83 -1
- data/test/command/suite/dump/record/vector/reference.test +1 -0
- data/test/command/suite/dump/table/array.expected +27 -1
- data/test/command/suite/dump/table/array.test +1 -0
- data/test/command/suite/dump/table/double_array_trie.expected +27 -1
- data/test/command/suite/dump/table/double_array_trie.test +1 -0
- data/test/command/suite/dump/table/hash.expected +27 -1
- data/test/command/suite/dump/table/hash.test +1 -0
- data/test/command/suite/dump/table/patricia_trie.expected +27 -1
- data/test/command/suite/dump/table/patricia_trie.test +1 -0
- data/test/command/suite/groonga/delete/{success.expected → key-integer.expected} +0 -0
- data/test/command/suite/groonga/delete/key-integer.test +17 -0
- data/test/command/suite/groonga/delete/key-string.expected +19 -0
- data/test/command/suite/groonga/delete/{success.test → key-string.test} +4 -6
- data/test/command/suite/groonga/delete/mismatched-type-key/acceptable/integer-for-string.expected +19 -0
- data/test/command/suite/groonga/delete/mismatched-type-key/acceptable/integer-for-string.test +17 -0
- data/test/command/suite/groonga/delete/mismatched-type-key/acceptable/string-for-integer.expected +19 -0
- data/test/command/suite/groonga/delete/mismatched-type-key/acceptable/string-for-integer.test +17 -0
- data/test/command/suite/message/error/missing-dataset.test +1 -0
- data/test/command/suite/system/absorb-data/records.catalog.json +58 -0
- data/test/command/suite/system/absorb-data/records.expected +32 -0
- data/test/command/suite/system/absorb-data/records.test +24 -0
- data/test/command/suite/system/statistics/object/count/empty.expected +11 -0
- data/test/command/suite/system/statistics/object/count/empty.test +12 -0
- data/test/command/suite/system/statistics/object/count/per-volume/empty.catalog.json +36 -0
- data/test/command/suite/system/statistics/object/count/per-volume/empty.expected +19 -0
- data/test/command/suite/system/statistics/object/count/per-volume/empty.test +12 -0
- data/test/command/suite/system/statistics/object/count/per-volume/record.catalog.json +40 -0
- data/test/command/suite/system/statistics/object/count/per-volume/record.expected +19 -0
- data/test/command/suite/system/statistics/object/count/per-volume/record.test +23 -0
- data/test/command/suite/system/statistics/object/count/per-volume/schema.catalog.json +40 -0
- data/test/command/suite/system/statistics/object/count/per-volume/schema.expected +19 -0
- data/test/command/suite/system/statistics/object/count/per-volume/schema.test +13 -0
- data/test/command/suite/system/statistics/object/count/record.catalog.json +12 -0
- data/test/command/suite/system/statistics/object/count/record.expected +11 -0
- data/test/command/suite/system/statistics/object/count/record.test +23 -0
- data/test/command/suite/system/statistics/object/count/schema.catalog.json +12 -0
- data/test/command/suite/system/statistics/object/count/schema.expected +11 -0
- data/test/command/suite/system/statistics/object/count/schema.test +13 -0
- data/test/command/suite/system/status.expected +3 -2
- data/test/unit/catalog/test_dataset.rb +4 -1
- data/test/unit/{test_catalog_generator.rb → catalog/test_generator.rb} +2 -2
- data/test/unit/catalog/test_replicas_volume.rb +79 -0
- data/test/unit/catalog/test_single_volume.rb +2 -2
- data/test/unit/catalog/test_slice.rb +33 -1
- data/test/unit/catalog/{test_collection_volume.rb → test_slices_volume.rb} +72 -11
- data/test/unit/catalog/test_version2.rb +3 -0
- data/test/unit/helper/distributed_search_planner_helper.rb +2 -2
- data/test/unit/plugins/catalog/test_fetch.rb +4 -4
- data/test/unit/plugins/crud/test_add.rb +44 -4
- data/test/unit/plugins/groonga/test_column_create.rb +4 -4
- data/test/unit/plugins/groonga/test_column_list.rb +4 -4
- data/test/unit/plugins/groonga/test_column_remove.rb +4 -4
- data/test/unit/plugins/groonga/test_column_rename.rb +4 -4
- data/test/unit/plugins/groonga/test_delete.rb +73 -10
- data/test/unit/plugins/groonga/test_table_create.rb +4 -4
- data/test/unit/plugins/groonga/test_table_list.rb +4 -4
- data/test/unit/plugins/groonga/test_table_remove.rb +4 -4
- data/test/unit/plugins/search/test_handler.rb +4 -4
- data/test/unit/plugins/search/test_planner.rb +4 -2
- data/test/unit/plugins/system/test_status.rb +31 -15
- data/test/unit/plugins/test_watch.rb +16 -16
- data/test/unit/test_address.rb +4 -4
- metadata +134 -35
- data/lib/droonga/catalog/volume_collection.rb +0 -79
- data/lib/droonga/catalog_fetcher.rb +0 -53
- data/lib/droonga/catalog_generator.rb +0 -243
- data/lib/droonga/catalog_loader.rb +0 -56
- data/lib/droonga/command/remote.rb +0 -404
- data/lib/droonga/data_absorber.rb +0 -264
- data/lib/droonga/node_status.rb +0 -71
- data/lib/droonga/serf_downloader.rb +0 -115
- data/test/unit/catalog/test_volume_collection.rb +0 -78
@@ -37,6 +37,7 @@ module Droonga
|
|
37
37
|
|
38
38
|
def initialize
|
39
39
|
@engine_name = nil
|
40
|
+
@internal_connection_lifetime = nil
|
40
41
|
@listen_fd = nil
|
41
42
|
@heartbeat_fd = nil
|
42
43
|
@contrtol_read_fd = nil
|
@@ -85,6 +86,10 @@ module Droonga
|
|
85
86
|
"Use NAME as the name of the engine") do |name|
|
86
87
|
@engine_name = name
|
87
88
|
end
|
89
|
+
parser.on("--internal-connection-lifetime=SECONDS", Float,
|
90
|
+
"The time to expire internal connections, in seconds") do |seconds|
|
91
|
+
@internal_connection_lifetime = seconds
|
92
|
+
end
|
88
93
|
parser.on("--listen-fd=FD", Integer,
|
89
94
|
"Use FD as the listen file descriptor") do |fd|
|
90
95
|
@listen_fd = fd
|
@@ -129,18 +134,31 @@ module Droonga
|
|
129
134
|
|
130
135
|
def create_internal_message_receiver
|
131
136
|
InternalFluentMessageReceiver.new(@loop, host) do |tag, time, record|
|
137
|
+
logger.trace("InternalFluentMessageReceiver receive")
|
132
138
|
on_message(tag, time, record)
|
133
139
|
end
|
134
140
|
end
|
135
141
|
|
136
|
-
def
|
142
|
+
def shutdown_internal_message_receiver_gracefully
|
143
|
+
if @internal_message_receiver.nil?
|
144
|
+
yield
|
145
|
+
return
|
146
|
+
end
|
147
|
+
@internal_message_receiver, receiver = nil, @internal_message_receiver
|
148
|
+
receiver.shutdown_gracefully do
|
149
|
+
yield
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
def shutdown_internal_message_receiver_immediately
|
137
154
|
return if @internal_message_receiver.nil?
|
138
155
|
@internal_message_receiver, receiver = nil, @internal_message_receiver
|
139
|
-
receiver.
|
156
|
+
receiver.shutdown_immediately
|
140
157
|
end
|
141
158
|
|
142
159
|
def run_engine
|
143
|
-
@engine = Engine.new(@loop, @engine_name, @internal_engine_name
|
160
|
+
@engine = Engine.new(@loop, @engine_name, @internal_engine_name,
|
161
|
+
:internal_connection_lifetime => @internal_connection_lifetime)
|
144
162
|
@engine.on_ready = lambda do
|
145
163
|
@worker_process_agent.ready
|
146
164
|
end
|
@@ -164,6 +182,9 @@ module Droonga
|
|
164
182
|
@worker_process_agent.on_stop_immediately = lambda do
|
165
183
|
stop_immediately
|
166
184
|
end
|
185
|
+
@worker_process_agent.on_refresh_self_reference = lambda do
|
186
|
+
@engine.refresh_self_reference
|
187
|
+
end
|
167
188
|
@worker_process_agent.start
|
168
189
|
end
|
169
190
|
|
@@ -177,11 +198,14 @@ module Droonga
|
|
177
198
|
:heartbeat_fd => @heartbeat_fd,
|
178
199
|
}
|
179
200
|
FluentMessageReceiver.new(@loop, options) do |tag, time, record|
|
201
|
+
logger.trace("FluentMessageReceiver receive")
|
180
202
|
on_message(tag, time, record)
|
181
203
|
end
|
182
204
|
end
|
183
205
|
|
184
206
|
def on_message(tag, time, record)
|
207
|
+
logger.trace("on_message: start", :record => record)
|
208
|
+
|
185
209
|
prefix, type, *arguments = tag.split(/\./)
|
186
210
|
if type.nil? or type.empty? or type == "message"
|
187
211
|
message = record
|
@@ -201,6 +225,8 @@ module Droonga
|
|
201
225
|
end
|
202
226
|
|
203
227
|
@engine.process(message)
|
228
|
+
|
229
|
+
logger.trace("on_message: done")
|
204
230
|
end
|
205
231
|
|
206
232
|
def stop_gracefully
|
@@ -208,13 +234,23 @@ module Droonga
|
|
208
234
|
logger.trace("stop_gracefully: start")
|
209
235
|
@stopping = true
|
210
236
|
@receiver.stop_gracefully
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
237
|
+
#XXX To disconnect all clients to myself (old service),
|
238
|
+
# we must refresh the connection via EngineNode
|
239
|
+
# and Forwarder.
|
240
|
+
# However, connections from workers can be still
|
241
|
+
# there. Then we have to wait for their timeout.
|
242
|
+
@engine.refresh_self_reference
|
243
|
+
@receiver.ensure_no_client do
|
244
|
+
logger.trace("stop_gracefully: ready to stop service")
|
245
|
+
@engine.stop_gracefully do
|
246
|
+
logger.trace("stop_gracefully: ready to stop workers")
|
247
|
+
shutdown_worker_process_agent
|
248
|
+
shutdown_internal_message_receiver_gracefully do
|
249
|
+
logger.trace("stop_gracefully: done",
|
250
|
+
:n_rest_watchers => @loop.watchers.size,
|
251
|
+
:rest_watchers => @loop.watchers)
|
252
|
+
end
|
253
|
+
end
|
218
254
|
end
|
219
255
|
end
|
220
256
|
|
@@ -222,7 +258,7 @@ module Droonga
|
|
222
258
|
def stop_immediately
|
223
259
|
shutdown_worker_process_agent
|
224
260
|
@receiver.stop_immediately
|
225
|
-
|
261
|
+
shutdown_internal_message_receiver_immediately
|
226
262
|
@engine.stop_immediately
|
227
263
|
@loop.stop
|
228
264
|
end
|
@@ -40,9 +40,11 @@ module Droonga
|
|
40
40
|
@contrtol_read_fd = nil
|
41
41
|
@contrtol_write_fd = nil
|
42
42
|
@pid_file_path = nil
|
43
|
+
@label = nil
|
43
44
|
@dataset = nil
|
44
45
|
@database_path = nil
|
45
46
|
@plugins = []
|
47
|
+
@internal_connection_lifetime = nil
|
46
48
|
@worker_process_agent = nil
|
47
49
|
end
|
48
50
|
|
@@ -90,6 +92,10 @@ module Droonga
|
|
90
92
|
"Put PID to PATH") do |path|
|
91
93
|
@pid_file_path = Pathname.new(path)
|
92
94
|
end
|
95
|
+
parser.on("--label=LABEL",
|
96
|
+
"Use given value as the label") do |label|
|
97
|
+
@label = label
|
98
|
+
end
|
93
99
|
parser.on("--dataset=DATASET",
|
94
100
|
"Process DATASET") do |dataset|
|
95
101
|
@dataset = dataset
|
@@ -102,6 +108,10 @@ module Droonga
|
|
102
108
|
"Use PLUGINs") do |plugins|
|
103
109
|
@plugins = plugins
|
104
110
|
end
|
111
|
+
parser.on("--internal-connection-lifetime=SECONDS", Float,
|
112
|
+
"The time to expire internal connections, in seconds") do |seconds|
|
113
|
+
@internal_connection_lifetime = seconds
|
114
|
+
end
|
105
115
|
end
|
106
116
|
|
107
117
|
def write_pid_file
|
@@ -159,8 +169,14 @@ module Droonga
|
|
159
169
|
@loop.stop
|
160
170
|
end
|
161
171
|
|
172
|
+
def refresh_node_reference
|
173
|
+
@forwarder.refresh_all_connections
|
174
|
+
end
|
175
|
+
|
162
176
|
def start_forwarder
|
163
|
-
@forwarder = Forwarder.new(@loop
|
177
|
+
@forwarder = Forwarder.new(@loop,
|
178
|
+
:auto_close_timeout =>
|
179
|
+
@internal_connection_lifetime)
|
164
180
|
@forwarder.start
|
165
181
|
end
|
166
182
|
|
@@ -170,6 +186,7 @@ module Droonga
|
|
170
186
|
|
171
187
|
def start_handler_runner
|
172
188
|
options = {
|
189
|
+
:label => @label,
|
173
190
|
:forwarder => @forwarder,
|
174
191
|
:dataset => @dataset,
|
175
192
|
:database => @database_path.to_s,
|
@@ -216,6 +233,9 @@ module Droonga
|
|
216
233
|
@worker_process_agent.on_stop_immediately = lambda do
|
217
234
|
stop_immediately
|
218
235
|
end
|
236
|
+
@worker_process_agent.on_refresh_node_reference = lambda do
|
237
|
+
refresh_node_reference
|
238
|
+
end
|
219
239
|
@worker_process_agent.start
|
220
240
|
@worker_process_agent.ready
|
221
241
|
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#
|
3
|
+
# Copyright (C) 2015 Droonga Project
|
4
|
+
#
|
5
|
+
# This library is free software; you can redistribute it and/or
|
6
|
+
# modify it under the terms of the GNU Lesser General Public
|
7
|
+
# License version 2.1 as published by the Free Software Foundation.
|
8
|
+
#
|
9
|
+
# This library is distributed in the hope that it will be useful,
|
10
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
11
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
12
|
+
# Lesser General Public License for more details.
|
13
|
+
#
|
14
|
+
# You should have received a copy of the GNU Lesser General Public
|
15
|
+
# License along with this library; if not, write to the Free Software
|
16
|
+
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
17
|
+
|
18
|
+
require "slop"
|
19
|
+
|
20
|
+
require "droonga/engine/version"
|
21
|
+
require "droonga/node_name"
|
22
|
+
require "droonga/serf"
|
23
|
+
|
24
|
+
module Droonga
|
25
|
+
module Command
|
26
|
+
class RemoteCommandBase
|
27
|
+
private
|
28
|
+
def parse_options(&block)
|
29
|
+
options = Slop.parse(:help => true) do |option|
|
30
|
+
yield(option) if block_given?
|
31
|
+
|
32
|
+
option.separator("Connections:")
|
33
|
+
option.on(:host=,
|
34
|
+
"Host name of the target node.",
|
35
|
+
:required => true)
|
36
|
+
option.on(:port=,
|
37
|
+
"Port number of the source cluster to be connected.",
|
38
|
+
:as => Integer,
|
39
|
+
:default => NodeName::DEFAULT_PORT)
|
40
|
+
option.on(:tag=,
|
41
|
+
"Tag name of the soruce cluster to be connected.",
|
42
|
+
:default => NodeName::DEFAULT_TAG)
|
43
|
+
|
44
|
+
option.separator("Miscellaneous:")
|
45
|
+
option.on(:verbose, "Output details for internal operations.",
|
46
|
+
:default => false)
|
47
|
+
end
|
48
|
+
@options = options
|
49
|
+
rescue Slop::MissingOptionError => error
|
50
|
+
$stderr.puts(error)
|
51
|
+
exit(false)
|
52
|
+
end
|
53
|
+
|
54
|
+
def host
|
55
|
+
@options[:host]
|
56
|
+
end
|
57
|
+
|
58
|
+
def port
|
59
|
+
@options[:port]
|
60
|
+
end
|
61
|
+
|
62
|
+
def tag
|
63
|
+
@options[:tag]
|
64
|
+
end
|
65
|
+
|
66
|
+
def node
|
67
|
+
@node ||= NodeName.new(:host => host,
|
68
|
+
:port => port,
|
69
|
+
:tag => tag)
|
70
|
+
end
|
71
|
+
|
72
|
+
def serf
|
73
|
+
@serf ||= Serf.new(node.to_s,
|
74
|
+
:verbose => @options[:verbose])
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -16,7 +16,7 @@
|
|
16
16
|
require "json"
|
17
17
|
require "fileutils"
|
18
18
|
|
19
|
-
require "droonga/
|
19
|
+
require "droonga/serf/remote_command"
|
20
20
|
|
21
21
|
module Droonga
|
22
22
|
module Command
|
@@ -37,17 +37,28 @@ module Droonga
|
|
37
37
|
|
38
38
|
serf_name = ENV["SERF_SELF_NAME"]
|
39
39
|
command = command_class.new(serf_name, @payload)
|
40
|
-
|
41
|
-
|
40
|
+
begin
|
41
|
+
command.process if command.should_process?
|
42
|
+
rescue Exception => exception
|
43
|
+
command.log("Exception: #{exception.inspect}, #{exception.message}, #{exception.backtrace.join(", ")}")
|
44
|
+
raise exception
|
45
|
+
ensure
|
46
|
+
output_response(command.response)
|
47
|
+
end
|
42
48
|
true
|
43
49
|
rescue Exception => exception
|
44
50
|
#XXX Any exception blocks following serf operations.
|
45
51
|
# To keep it working, I rescue any exception for now.
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
52
|
+
begin
|
53
|
+
FileUtils.mkdir_p(Path.serf_event_handler_errors)
|
54
|
+
File.open(Path.serf_event_handler_error_file, "w") do |file|
|
55
|
+
file.write(exception.inspect)
|
56
|
+
file.write(exception.backtrace)
|
57
|
+
end
|
58
|
+
rescue Errno::EACCES => permission_denied_exception
|
50
59
|
end
|
60
|
+
puts exception.inspect
|
61
|
+
puts exception.backtrace
|
51
62
|
true
|
52
63
|
end
|
53
64
|
|
@@ -61,7 +72,7 @@ module Droonga
|
|
61
72
|
@payload = JSON.parse($stdin.gets)
|
62
73
|
detect_command_class_from_custom_event(ENV["SERF_QUERY_NAME"])
|
63
74
|
when "member-join", "member-leave", "member-update", "member-reap"
|
64
|
-
|
75
|
+
Serf::RemoteCommand::UpdateClusterState
|
65
76
|
else
|
66
77
|
nil
|
67
78
|
end
|
@@ -70,23 +81,21 @@ module Droonga
|
|
70
81
|
def detect_command_class_from_custom_event(event_name)
|
71
82
|
case event_name
|
72
83
|
when "change_role"
|
73
|
-
|
74
|
-
when "
|
75
|
-
|
76
|
-
when "
|
77
|
-
|
84
|
+
Serf::RemoteCommand::ChangeRole
|
85
|
+
when "report_last_message_timestamp"
|
86
|
+
Serf::RemoteCommand::ReportLastMessageTimestamp
|
87
|
+
when "accept_messages_newer_than"
|
88
|
+
Serf::RemoteCommand::AcceptMessagesNewerThan
|
78
89
|
when "join"
|
79
|
-
|
90
|
+
Serf::RemoteCommand::Join
|
80
91
|
when "unjoin"
|
81
|
-
|
92
|
+
Serf::RemoteCommand::Unjoin
|
82
93
|
when "set_replicas"
|
83
|
-
|
94
|
+
Serf::RemoteCommand::SetReplicas
|
84
95
|
when "add_replicas"
|
85
|
-
|
96
|
+
Serf::RemoteCommand::AddReplicas
|
86
97
|
when "remove_replicas"
|
87
|
-
|
88
|
-
when "absorb_data"
|
89
|
-
Remote::AbsorbData
|
98
|
+
Serf::RemoteCommand::RemoveReplicas
|
90
99
|
else
|
91
100
|
nil
|
92
101
|
end
|
@@ -0,0 +1,222 @@
|
|
1
|
+
# Copyright (C) 2014-2015 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 "droonga/loggable"
|
17
|
+
require "droonga/node_name"
|
18
|
+
require "droonga/catalog/dataset"
|
19
|
+
require "droonga/client"
|
20
|
+
require "droonga/catalog/generator"
|
21
|
+
require "droonga/catalog/fetcher"
|
22
|
+
|
23
|
+
module Droonga
|
24
|
+
class DataAbsorberClient
|
25
|
+
include Loggable
|
26
|
+
|
27
|
+
class DestinationEqualsToSource < StandardError
|
28
|
+
def initialize(params)
|
29
|
+
super("The source and the destination are same", params)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
class EmptyResponse < StandardError
|
34
|
+
end
|
35
|
+
|
36
|
+
class EmptyBody < StandardError
|
37
|
+
end
|
38
|
+
|
39
|
+
DEFAULT_MESSAGES_PER_SECOND = 100
|
40
|
+
DEFAULT_PROGRESS_INTERVAL_SECONDS = 3
|
41
|
+
|
42
|
+
DEFAULT_HOST = NodeName::DEFAULT_HOST
|
43
|
+
DEFAULT_PORT = NodeName::DEFAULT_PORT
|
44
|
+
DEFAULT_TAG = NodeName::DEFAULT_TAG
|
45
|
+
DEFAULT_DATASET = Catalog::Dataset::DEFAULT_NAME
|
46
|
+
|
47
|
+
attr_reader :params
|
48
|
+
attr_reader :host, :port, :tag, :dataset
|
49
|
+
attr_reader :messages_per_second, :progress_interval_seconds
|
50
|
+
attr_reader :source_host, :source_port, :source_tag, :source_dataset
|
51
|
+
attr_reader :error_message
|
52
|
+
|
53
|
+
def initialize(params)
|
54
|
+
@params = params
|
55
|
+
|
56
|
+
@messages_per_second = @params[:messages_per_second] ||
|
57
|
+
DEFAULT_MESSAGES_PER_SECOND
|
58
|
+
@progress_interval_seconds = @params[:progress_interval_seconds] ||
|
59
|
+
DEFAULT_PROGRESS_INTERVAL_SECONDS
|
60
|
+
@target_role = @params[:target_role]
|
61
|
+
|
62
|
+
@host = @params[:host] || DEFAULT_HOST
|
63
|
+
@port = @params[:port] || DEFAULT_PORT
|
64
|
+
@tag = @params[:tag] || DEFAULT_TAG
|
65
|
+
@dataset = @params[:dataset] || DEFAULT_DATASET
|
66
|
+
|
67
|
+
@source_host = @params[:source_host] || @host || DEFAULT_HOST
|
68
|
+
@source_port = @params[:source_port] || @port || DEFAULT_PORT
|
69
|
+
@source_tag = @params[:source_tag] || @tag || DEFAULT_TAG
|
70
|
+
@source_dataset = @params[:source_dataset] || @dataset || DEFAULT_DATASET
|
71
|
+
|
72
|
+
@receiver_host = @params[:receiver_host] || @host
|
73
|
+
@receiver_port = @params[:receiver_port] || 0
|
74
|
+
|
75
|
+
@client_options = @params[:client_options] || {}
|
76
|
+
|
77
|
+
@error_message = nil
|
78
|
+
|
79
|
+
validate_params
|
80
|
+
end
|
81
|
+
|
82
|
+
def run
|
83
|
+
n_absorbers = 0
|
84
|
+
|
85
|
+
absorb_message = {
|
86
|
+
"type" => "system.absorb-data",
|
87
|
+
"dataset" => @dataset,
|
88
|
+
"body" => {
|
89
|
+
"host" => @source_host,
|
90
|
+
"port" => @source_port,
|
91
|
+
"tag" => @source_tag,
|
92
|
+
"dataset" => @source_dataset,
|
93
|
+
"messagesPerSecond" => @messages_per_second,
|
94
|
+
"progressIntervalSeconds" => @progress_interval_seconds,
|
95
|
+
"targetRole" => @target_role,
|
96
|
+
},
|
97
|
+
}
|
98
|
+
client = Droonga::Client.new(destination_client_options)
|
99
|
+
client.subscribe(absorb_message) do |message|
|
100
|
+
case message
|
101
|
+
when Droonga::Client::Error
|
102
|
+
client.close
|
103
|
+
@error_message = message.to_s
|
104
|
+
else
|
105
|
+
case message["type"]
|
106
|
+
when "system.absorb-data.result", "system.absorb-data.error"
|
107
|
+
if message["statusCode"] != 200
|
108
|
+
client.close
|
109
|
+
error = message["body"]
|
110
|
+
@error_message = "#{error['name']}: #{error['message']}"
|
111
|
+
end
|
112
|
+
when "system.absorb-data.progress"
|
113
|
+
body = message["body"]
|
114
|
+
yield(:n_processed_messages => body["nProcessedMessages"],
|
115
|
+
:percentage => body["percentage"],
|
116
|
+
:message => body["message"])
|
117
|
+
when "system.absorb-data.start"
|
118
|
+
n_absorbers += 1
|
119
|
+
when "system.absorb-data.end"
|
120
|
+
n_absorbers -= 1
|
121
|
+
client.close if n_absorbers <= 0
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
def source_node_suspendable?
|
128
|
+
(source_replica_hosts - [@source_host]).size >= 1
|
129
|
+
end
|
130
|
+
|
131
|
+
def empty_destination?
|
132
|
+
table_names_in_destination_node.empty?
|
133
|
+
end
|
134
|
+
|
135
|
+
private
|
136
|
+
def validate_params
|
137
|
+
source_node_name = NodeName.new(:host => @source_host,
|
138
|
+
:port => @source_port,
|
139
|
+
:tag => @source_tag)
|
140
|
+
destination_node_name = NodeName.new(:host => @host,
|
141
|
+
:port => @port,
|
142
|
+
:tag => @tag)
|
143
|
+
if source_node_name == destination_node_name and
|
144
|
+
@source_dataset == @dataset
|
145
|
+
raise DestinationEqualsToSource.new(:host => @host,
|
146
|
+
:port => @port,
|
147
|
+
:tag => @tag,
|
148
|
+
:dataset => @dataset)
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
def destination_client_options
|
153
|
+
{
|
154
|
+
:host => @host,
|
155
|
+
:port => @port,
|
156
|
+
:tag => @tag,
|
157
|
+
:protocol => :droonga,
|
158
|
+
:receiver_host => @receiver_host,
|
159
|
+
:receiver_port => @receiver_port,
|
160
|
+
}.merge(@client_options)
|
161
|
+
end
|
162
|
+
|
163
|
+
def synchronous_destination_client_options
|
164
|
+
destination_client_options.merge(:backend => :thread,
|
165
|
+
:loop => nil)
|
166
|
+
end
|
167
|
+
|
168
|
+
def table_names_in_destination_node
|
169
|
+
@table_names_in_destination_node ||= get_table_names_in_destination_node
|
170
|
+
end
|
171
|
+
|
172
|
+
def get_table_names_in_destination_node
|
173
|
+
response = nil
|
174
|
+
Droonga::Client.open(synchronous_destination_client_options) do |client|
|
175
|
+
response = client.request("dataset" => @source_dataset,
|
176
|
+
"type" => "table_list")
|
177
|
+
end
|
178
|
+
|
179
|
+
unless response
|
180
|
+
raise EmptyResponse.new("table_list returns nil response")
|
181
|
+
end
|
182
|
+
unless response["body"]
|
183
|
+
raise EmptyBody.new("table_list returns nil result")
|
184
|
+
end
|
185
|
+
|
186
|
+
message_body = response["body"]
|
187
|
+
body = message_body[1]
|
188
|
+
tables = body[1..-1]
|
189
|
+
tables.collect do |table|
|
190
|
+
table[1]
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
def source_replica_hosts
|
195
|
+
@source_replica_hosts ||= get_source_replica_hosts
|
196
|
+
end
|
197
|
+
|
198
|
+
def get_source_replica_hosts
|
199
|
+
generator = Catalog::Generator.new
|
200
|
+
generator.load(source_catalog)
|
201
|
+
dataset = generator.dataset_for_host(@source_host)
|
202
|
+
return [] unless dataset
|
203
|
+
dataset.replicas.hosts
|
204
|
+
end
|
205
|
+
|
206
|
+
def source_catalog
|
207
|
+
@source_catalog ||= fetch_source_catalog
|
208
|
+
end
|
209
|
+
|
210
|
+
def fetch_source_catalog
|
211
|
+
fetcher = Catalog::Fetcher.new(:host => @source_host,
|
212
|
+
:port => @source_port,
|
213
|
+
:tag => @source_tag,
|
214
|
+
:receiver_host => @receiver_host)
|
215
|
+
fetcher.fetch(:dataset => @source_dataset)
|
216
|
+
end
|
217
|
+
|
218
|
+
def log_tag
|
219
|
+
"data-absorber"
|
220
|
+
end
|
221
|
+
end
|
222
|
+
end
|