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