droonga-engine 1.0.7 → 1.0.8
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +2 -2
- data/bin/droonga-engine-absorb-data +200 -86
- data/bin/droonga-engine-configure +14 -11
- data/bin/droonga-engine-join +178 -59
- data/droonga-engine.gemspec +1 -1
- data/install.sh +1 -0
- data/lib/droonga/buffered_tcp_socket.rb +2 -2
- data/lib/droonga/catalog/base.rb +18 -0
- data/lib/droonga/catalog/dataset.rb +8 -0
- data/lib/droonga/catalog/version1.rb +3 -12
- data/lib/droonga/catalog/version2.rb +5 -12
- data/lib/droonga/command/droonga_engine.rb +6 -61
- data/lib/droonga/command/droonga_engine_service.rb +1 -0
- data/lib/droonga/command/droonga_engine_worker.rb +1 -0
- data/lib/droonga/command/remote.rb +18 -1
- data/lib/droonga/command/serf_event_handler.rb +3 -0
- data/lib/droonga/data_absorber.rb +234 -44
- data/lib/droonga/distributed_command_planner.rb +5 -5
- data/lib/droonga/engine.rb +27 -15
- data/lib/droonga/engine/version.rb +1 -1
- data/lib/droonga/handler_runner.rb +4 -0
- data/lib/droonga/node_status.rb +6 -2
- data/lib/droonga/path.rb +8 -14
- data/lib/droonga/planner.rb +4 -3
- data/lib/droonga/plugin/metadata/handler_action.rb +8 -0
- data/lib/droonga/plugins/groonga/column_create.rb +1 -1
- data/lib/droonga/plugins/groonga/column_list.rb +23 -1
- data/lib/droonga/plugins/groonga/column_remove.rb +1 -1
- data/lib/droonga/plugins/groonga/column_rename.rb +1 -1
- data/lib/droonga/plugins/groonga/delete.rb +1 -1
- data/lib/droonga/plugins/groonga/select.rb +17 -2
- data/lib/droonga/plugins/groonga/table_create.rb +26 -1
- data/lib/droonga/plugins/groonga/table_remove.rb +1 -1
- data/lib/droonga/plugins/search.rb +1 -1
- data/lib/droonga/plugins/search/distributed_search_planner.rb +15 -7
- data/lib/droonga/processor.rb +3 -2
- data/lib/droonga/searcher.rb +31 -15
- data/lib/droonga/serf.rb +1 -0
- data/lib/droonga/service_installation.rb +2 -2
- data/lib/droonga/single_step.rb +2 -2
- data/test/command/fixture/event.jsons +3 -2
- data/test/command/fixture/user-table.jsons +3 -2
- data/test/command/fixture/users.jsons +25 -0
- data/test/command/run-test.rb +13 -1
- data/test/command/suite/groonga/column_create/scalar.test +3 -2
- data/test/command/suite/groonga/column_create/vector.test +3 -2
- data/test/command/suite/groonga/column_list/{success.expected → no-key.expected} +0 -0
- data/test/command/suite/groonga/column_list/{success.test → no-key.test} +1 -1
- data/test/command/suite/groonga/column_list/with-key.expected +96 -0
- data/test/command/suite/groonga/column_list/with-key.test +25 -0
- data/test/command/suite/groonga/column_remove/success.test +3 -2
- data/test/command/suite/groonga/column_remove/unknown-column.test +3 -2
- data/test/command/suite/groonga/column_rename/success.test +3 -2
- data/test/command/suite/groonga/column_rename/unknown-column.test +3 -2
- data/test/command/suite/groonga/delete/duplicated-identifiers.test +3 -2
- data/test/command/suite/groonga/delete/no-identifier.test +3 -2
- data/test/command/suite/groonga/select/output_columns/default/array.expected +33 -0
- data/test/command/suite/groonga/select/output_columns/default/array.test +38 -0
- data/test/command/suite/groonga/select/output_columns/nonexistent.expected +28 -0
- data/test/command/suite/groonga/select/output_columns/nonexistent.test +26 -0
- data/test/command/suite/groonga/table_create/dat-without-key-type.expected +14 -0
- data/test/command/suite/groonga/table_create/dat-without-key-type.test +8 -0
- data/test/command/suite/groonga/table_create/dat.expected +13 -0
- data/test/command/suite/groonga/table_create/dat.test +9 -0
- data/test/command/suite/groonga/table_create/hash-without-key-type.expected +14 -0
- data/test/command/suite/groonga/table_create/hash-without-key-type.test +8 -0
- data/test/command/suite/groonga/table_create/hash.test +3 -2
- data/test/command/suite/groonga/table_create/pat-without-key-type.expected +14 -0
- data/test/command/suite/groonga/table_create/pat-without-key-type.test +8 -0
- data/test/command/suite/groonga/table_create/pat.expected +13 -0
- data/test/command/suite/groonga/table_create/pat.test +9 -0
- data/test/command/suite/groonga/table_list/success.test +3 -2
- data/test/unit/catalog/test_version1.rb +2 -2
- data/test/unit/catalog/test_version2.rb +3 -3
- data/test/unit/helper.rb +2 -2
- data/test/unit/helper/distributed_search_planner_helper.rb +9 -1
- data/test/unit/plugins/groonga/select/test_adapter_input.rb +15 -2
- data/test/unit/plugins/groonga/test_column_list.rb +119 -4
- data/test/unit/plugins/groonga/test_table_create.rb +29 -0
- data/test/unit/plugins/search/planner/test_basic.rb +2 -2
- data/test/unit/plugins/search/test_planner.rb +10 -2
- metadata +43 -8
data/droonga-engine.gemspec
CHANGED
@@ -38,7 +38,7 @@ Gem::Specification.new do |gem|
|
|
38
38
|
gem.add_dependency "archive-zip"
|
39
39
|
gem.add_dependency "cool.io"
|
40
40
|
gem.add_dependency "drndump"
|
41
|
-
gem.add_dependency "droonga-client", ">= 0.
|
41
|
+
gem.add_dependency "droonga-client", ">= 0.2.0"
|
42
42
|
gem.add_dependency "droonga-message-pack-packer", ">= 1.0.2"
|
43
43
|
gem.add_dependency "groonga-command-parser"
|
44
44
|
gem.add_dependency "faraday"
|
data/install.sh
CHANGED
@@ -133,6 +133,7 @@ setup_configuration_directory() {
|
|
133
133
|
echo "This node is configured with a hostname $HOST."
|
134
134
|
fi
|
135
135
|
|
136
|
+
# we should use --no-prompt instead of --quiet, for droonga-engine 1.0.8 and later.
|
136
137
|
droonga-engine-configure --quiet \
|
137
138
|
--host=$HOST --port=$PORT
|
138
139
|
if [ $? -ne 0 ]; then
|
@@ -140,14 +140,14 @@ module Droonga
|
|
140
140
|
|
141
141
|
def written_partial(size)
|
142
142
|
written
|
143
|
-
@data =
|
143
|
+
@data = data[size..-1]
|
144
144
|
@revision += 1
|
145
145
|
buffering
|
146
146
|
end
|
147
147
|
|
148
148
|
private
|
149
149
|
def path
|
150
|
-
@data_directory + "#{@time_stamp.iso8601(6)}.#{@revision}
|
150
|
+
@data_directory + "#{@time_stamp.iso8601(6)}.#{@revision}.#{SUFFIX}"
|
151
151
|
end
|
152
152
|
end
|
153
153
|
end
|
data/lib/droonga/catalog/base.rb
CHANGED
@@ -36,6 +36,24 @@ module Droonga
|
|
36
36
|
def dataset(name)
|
37
37
|
datasets[name]
|
38
38
|
end
|
39
|
+
|
40
|
+
private
|
41
|
+
def migrate_database_location(current_db_path, device, name)
|
42
|
+
return if current_db_path.exist?
|
43
|
+
|
44
|
+
common_base_path = Pathname(@base_path)
|
45
|
+
old_db_paths = {
|
46
|
+
:top_level => common_base_path + device + name + "db",
|
47
|
+
:singular_form => common_base_path + device + "database" + name + "db",
|
48
|
+
}
|
49
|
+
old_db_paths.each do |type, old_db_path|
|
50
|
+
if old_db_path.exist?
|
51
|
+
FileUtils.mkdir_p(current_db_path.parent.parent)
|
52
|
+
FileUtils.move(old_db_path.parent, current_db_path.parent)
|
53
|
+
return
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
39
57
|
end
|
40
58
|
end
|
41
59
|
end
|
@@ -85,6 +85,14 @@ module Droonga
|
|
85
85
|
routes
|
86
86
|
end
|
87
87
|
|
88
|
+
def single_slice?
|
89
|
+
# TODO: Support slice key
|
90
|
+
replicas.all? do |volume|
|
91
|
+
volume.is_a?(SingleVolume) or
|
92
|
+
volume.slices.size == 1
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
88
96
|
private
|
89
97
|
def create_volumes(raw_volumes)
|
90
98
|
raw_volumes.collect do |raw_volume|
|
@@ -54,10 +54,9 @@ module Droonga
|
|
54
54
|
partitions.each do |partition|
|
55
55
|
if partition =~ pattern
|
56
56
|
database_name = $POSTMATCH
|
57
|
-
path =
|
58
|
-
|
59
|
-
migrate_database_location(path,
|
60
|
-
:name => database_name)
|
57
|
+
path = Path.databases(base_path) +
|
58
|
+
device + database_name + "db"
|
59
|
+
migrate_database_location(path, device, database_name)
|
61
60
|
options = {
|
62
61
|
:dataset => dataset_name,
|
63
62
|
:database => path.to_s,
|
@@ -389,14 +388,6 @@ module Droonga
|
|
389
388
|
end
|
390
389
|
end
|
391
390
|
|
392
|
-
def migrate_database_location(path, params)
|
393
|
-
old_path = File.join([params[:device], params[:name], "db"])
|
394
|
-
old_path = Pathname(old_path).expand_path(base_path)
|
395
|
-
if old_path.exist? and not path.exist?
|
396
|
-
FileUtils.move(old_path.to_s, path.to_s)
|
397
|
-
end
|
398
|
-
end
|
399
|
-
|
400
391
|
class Dataset < Catalog::Dataset
|
401
392
|
def compute_routes(args, live_nodes=nil)
|
402
393
|
routes = []
|
@@ -44,10 +44,11 @@ module Droonga
|
|
44
44
|
volume.slices.each do |slice|
|
45
45
|
volume_address = slice.volume.address
|
46
46
|
if volume_address.node == node
|
47
|
-
|
48
|
-
path =
|
49
|
-
|
50
|
-
|
47
|
+
name = volume_address.name
|
48
|
+
path = Path.databases(base_path) +
|
49
|
+
device + name + "db"
|
50
|
+
migrate_database_location(path, device, name)
|
51
|
+
|
51
52
|
options = {
|
52
53
|
:dataset => dataset_name,
|
53
54
|
:database => path.to_s,
|
@@ -86,14 +87,6 @@ module Droonga
|
|
86
87
|
end
|
87
88
|
nodes.sort.uniq
|
88
89
|
end
|
89
|
-
|
90
|
-
def migrate_database_location(path, params)
|
91
|
-
old_path = File.join([params[:device], params[:name], "db"])
|
92
|
-
old_path = Pathname(old_path).expand_path(base_path)
|
93
|
-
if old_path.exist? and not path.exist?
|
94
|
-
FileUtils.move(old_path.to_s, path.to_s)
|
95
|
-
end
|
96
|
-
end
|
97
90
|
end
|
98
91
|
end
|
99
92
|
end
|
@@ -20,7 +20,7 @@ require "fileutils"
|
|
20
20
|
require "yaml"
|
21
21
|
|
22
22
|
require "coolio"
|
23
|
-
require "sigdump"
|
23
|
+
require "sigdump/setup"
|
24
24
|
|
25
25
|
require "droonga/engine/version"
|
26
26
|
require "droonga/path"
|
@@ -114,7 +114,7 @@ module Droonga
|
|
114
114
|
|
115
115
|
class Configuration
|
116
116
|
attr_reader :host, :port, :tag, :log_file, :pid_file_path
|
117
|
-
attr_reader :ready_notify_fd
|
117
|
+
attr_reader :ready_notify_fd
|
118
118
|
def initialize
|
119
119
|
config = load_config
|
120
120
|
|
@@ -122,23 +122,13 @@ module Droonga
|
|
122
122
|
@port = config["port"] || Address::DEFAULT_PORT
|
123
123
|
@tag = config["tag"] || Address::DEFAULT_TAG
|
124
124
|
|
125
|
-
@given_values = {}
|
126
|
-
|
127
125
|
@log_file = nil
|
128
126
|
@daemon = false
|
129
127
|
@pid_file_path = nil
|
130
128
|
@ready_notify_fd = nil
|
131
|
-
@orchestrated = true
|
132
129
|
|
133
130
|
if have_config_file?
|
134
|
-
if config
|
135
|
-
@daemon = config["daemon"]
|
136
|
-
else
|
137
|
-
@daemon = true
|
138
|
-
end
|
139
|
-
if @daemon
|
140
|
-
self.pid_file_path = config["pid_file"] || Path.default_pid_file
|
141
|
-
end
|
131
|
+
self.pid_file_path = config["pid_file"] if config["pid_file"]
|
142
132
|
self.log_file = config["log_file"] || Path.default_log_file
|
143
133
|
self.log_level = config["log_level"] if config.include?("log_level")
|
144
134
|
end
|
@@ -148,34 +138,6 @@ module Droonga
|
|
148
138
|
File.exist?(Path.config)
|
149
139
|
end
|
150
140
|
|
151
|
-
def have_given_host?
|
152
|
-
@given_values.include?(:host)
|
153
|
-
end
|
154
|
-
|
155
|
-
def have_given_port?
|
156
|
-
@given_values.include?(:port)
|
157
|
-
end
|
158
|
-
|
159
|
-
def have_given_tag?
|
160
|
-
@given_values.include?(:tag)
|
161
|
-
end
|
162
|
-
|
163
|
-
def have_given_daemon?
|
164
|
-
@given_values.include?(:daemon)
|
165
|
-
end
|
166
|
-
|
167
|
-
def have_given_log_file?
|
168
|
-
@given_values.include?(:log_file)
|
169
|
-
end
|
170
|
-
|
171
|
-
def have_given_log_level?
|
172
|
-
@given_values.include?(:log_level)
|
173
|
-
end
|
174
|
-
|
175
|
-
def have_given_pid_file?
|
176
|
-
@given_values.include?(:pid_file)
|
177
|
-
end
|
178
|
-
|
179
141
|
def load_config
|
180
142
|
if have_config_file?
|
181
143
|
YAML.load_file(Path.config)
|
@@ -225,7 +187,6 @@ module Droonga
|
|
225
187
|
add_log_options(parser)
|
226
188
|
add_process_options(parser)
|
227
189
|
add_path_options(parser)
|
228
|
-
add_orchestration_options(parser)
|
229
190
|
add_notification_options(parser)
|
230
191
|
end
|
231
192
|
|
@@ -245,19 +206,16 @@ module Droonga
|
|
245
206
|
"The host name of the Droonga engine",
|
246
207
|
"(#{@host})") do |host|
|
247
208
|
@host = host
|
248
|
-
@given_values[:host] = host
|
249
209
|
end
|
250
210
|
parser.on("--port=PORT", Integer,
|
251
211
|
"The port number of the Droonga engine",
|
252
212
|
"(#{@port})") do |port|
|
253
213
|
@port = port
|
254
|
-
@given_values[:port] = port
|
255
214
|
end
|
256
215
|
parser.on("--tag=TAG",
|
257
216
|
"The tag of the Droonga engine",
|
258
217
|
"(#{@tag})") do |tag|
|
259
218
|
@tag = tag
|
260
|
-
@given_values[:tag] = tag
|
261
219
|
end
|
262
220
|
end
|
263
221
|
|
@@ -271,12 +229,10 @@ module Droonga
|
|
271
229
|
"[#{levels_label}]",
|
272
230
|
"(#{log_level})") do |level|
|
273
231
|
self.log_level = level
|
274
|
-
@given_values[:log_level] = log_level
|
275
232
|
end
|
276
233
|
parser.on("--log-file=FILE",
|
277
234
|
"Output logs to FILE") do |file|
|
278
235
|
self.log_file = file
|
279
|
-
@given_values[:log_file] = file
|
280
236
|
end
|
281
237
|
end
|
282
238
|
|
@@ -286,17 +242,14 @@ module Droonga
|
|
286
242
|
parser.on("--daemon",
|
287
243
|
"Run as a daemon") do
|
288
244
|
@daemon = true
|
289
|
-
@given_values[:daemon] = true
|
290
245
|
end
|
291
246
|
parser.on("--no-daemon",
|
292
247
|
"Run as a regular process") do
|
293
248
|
@daemon = false
|
294
|
-
@given_values[:daemon] = false
|
295
249
|
end
|
296
250
|
parser.on("--pid-file=PATH",
|
297
251
|
"Put PID to PATH") do |path|
|
298
252
|
self.pid_file_path = path
|
299
|
-
@given_values[:pid_file] = path
|
300
253
|
end
|
301
254
|
end
|
302
255
|
|
@@ -310,13 +263,6 @@ module Droonga
|
|
310
263
|
end
|
311
264
|
end
|
312
265
|
|
313
|
-
def add_orchestration_options(parser)
|
314
|
-
parser.on("--no-orchestration",
|
315
|
-
"Disable orchestration with other nodes") do
|
316
|
-
@orchestrated = false
|
317
|
-
end
|
318
|
-
end
|
319
|
-
|
320
266
|
def add_notification_options(parser)
|
321
267
|
parser.separator("")
|
322
268
|
parser.separator("Notification:")
|
@@ -348,7 +294,7 @@ module Droonga
|
|
348
294
|
|
349
295
|
trap_signals
|
350
296
|
@loop.run
|
351
|
-
@serf.stop if @serf
|
297
|
+
@serf.stop if @serf.running?
|
352
298
|
|
353
299
|
@service_runner.success?
|
354
300
|
end
|
@@ -389,14 +335,14 @@ module Droonga
|
|
389
335
|
|
390
336
|
def stop_gracefully
|
391
337
|
@command_runner.stop
|
392
|
-
@serf.stop
|
338
|
+
@serf.stop
|
393
339
|
@catalog_observer.stop
|
394
340
|
@service_runner.stop_gracefully
|
395
341
|
end
|
396
342
|
|
397
343
|
def stop_immediately
|
398
344
|
@command_runner.stop
|
399
|
-
@serf.stop
|
345
|
+
@serf.stop
|
400
346
|
@catalog_observer.stop
|
401
347
|
@service_runner.stop_immediately
|
402
348
|
end
|
@@ -427,7 +373,6 @@ module Droonga
|
|
427
373
|
end
|
428
374
|
|
429
375
|
def run_serf
|
430
|
-
return nil unless @configuration.orchestrated
|
431
376
|
serf = Serf.new(@loop, @configuration.engine_name)
|
432
377
|
serf.start
|
433
378
|
serf
|
@@ -89,6 +89,13 @@ module Droonga
|
|
89
89
|
end
|
90
90
|
end
|
91
91
|
|
92
|
+
class SetStatus < Base
|
93
|
+
def process
|
94
|
+
status = NodeStatus.new
|
95
|
+
status.set(@params["key"], @params["value"])
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
92
99
|
class Join < Base
|
93
100
|
def process
|
94
101
|
log("type = #{type}")
|
@@ -115,6 +122,10 @@ module Droonga
|
|
115
122
|
@params["dataset"]
|
116
123
|
end
|
117
124
|
|
125
|
+
def messages_per_second
|
126
|
+
@params["messages_per_second"]
|
127
|
+
end
|
128
|
+
|
118
129
|
def valid_params?
|
119
130
|
have_required_params? and
|
120
131
|
valid_node?(source_node) and
|
@@ -220,7 +231,8 @@ module Droonga
|
|
220
231
|
:source_host => source_host,
|
221
232
|
:destination_host => joining_host,
|
222
233
|
:port => port,
|
223
|
-
:tag => tag
|
234
|
+
:tag => tag,
|
235
|
+
:messages_per_second => messages_per_second)
|
224
236
|
status.delete(:absorbing)
|
225
237
|
sleep(1)
|
226
238
|
end
|
@@ -258,6 +270,7 @@ module Droonga
|
|
258
270
|
:destination_host => host,
|
259
271
|
:port => port,
|
260
272
|
:tag => tag,
|
273
|
+
:messages_per_second => messages_per_second,
|
261
274
|
:client => "droonga-send")
|
262
275
|
status.delete(:absorbing)
|
263
276
|
end
|
@@ -278,6 +291,10 @@ module Droonga
|
|
278
291
|
def tag
|
279
292
|
@tag ||= @params["tag"]
|
280
293
|
end
|
294
|
+
|
295
|
+
def messages_per_second
|
296
|
+
@messages_per_second ||= @params["messages_per_second"]
|
297
|
+
end
|
281
298
|
end
|
282
299
|
|
283
300
|
class ModifyReplicasBase < Base
|
@@ -46,6 +46,7 @@ module Droonga
|
|
46
46
|
FileUtils.mkdir_p(Path.serf_event_handler_errors)
|
47
47
|
File.open(Path.serf_event_handler_error_file, "w") do |file|
|
48
48
|
file.write(exception.inspect)
|
49
|
+
file.write(exception.backtrace)
|
49
50
|
end
|
50
51
|
true
|
51
52
|
end
|
@@ -72,6 +73,8 @@ module Droonga
|
|
72
73
|
Remote::ChangeRole
|
73
74
|
when "report_status"
|
74
75
|
Remote::ReportStatus
|
76
|
+
when "set_status"
|
77
|
+
Remote::SetStatus
|
75
78
|
when "join"
|
76
79
|
Remote::Join
|
77
80
|
when "set_replicas"
|
@@ -15,59 +15,249 @@
|
|
15
15
|
|
16
16
|
require "open3"
|
17
17
|
|
18
|
+
require "droonga/loggable"
|
19
|
+
require "droonga/client"
|
20
|
+
require "droonga/catalog_generator"
|
21
|
+
require "droonga/catalog_fetcher"
|
22
|
+
|
18
23
|
module Droonga
|
19
24
|
class DataAbsorber
|
25
|
+
include Loggable
|
26
|
+
|
27
|
+
DEFAULT_MESSAGES_PER_SECOND = 100
|
28
|
+
|
29
|
+
TIME_UNKNOWN = -1
|
30
|
+
PROGRESS_UNKNOWN = -1
|
31
|
+
|
20
32
|
class << self
|
21
33
|
def absorb(params)
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
drndump_options += ["--receiver-port", params[:receiver_port].to_s] if params[:receiver_port]
|
30
|
-
|
31
|
-
#TODO: We should use droonga-send instead of droonga-request,
|
32
|
-
# because droonga-request is too slow.
|
33
|
-
# However, to do it, we have to implement an API to know
|
34
|
-
# that all messages sent by droonga-send are completely
|
35
|
-
# processed.
|
36
|
-
client = params[:client] || "droonga-request"
|
37
|
-
client_options = []
|
38
|
-
if client.include?("droonga-request")
|
39
|
-
client_options += ["--host", params[:destination_host]]
|
40
|
-
client_options += ["--port", params[:port].to_s] if params[:port]
|
41
|
-
client_options += ["--tag", params[:tag]] if params[:tag]
|
42
|
-
client_options += ["--receiver-host", params[:destination_host]]
|
43
|
-
client_options += ["--receiver-port", params[:receiver_port].to_s] if params[:receiver_port]
|
44
|
-
elsif client.include?("droonga-send")
|
45
|
-
#XXX Don't use round-robin with multiple endpoints
|
46
|
-
# even if there are too much data.
|
47
|
-
# Schema and indexes must be sent to just one endpoint
|
48
|
-
# to keep their order, but currently there is no way to
|
49
|
-
# extract only schema and indexes via drndump.
|
50
|
-
# So, we always use just one endpoint for now,
|
51
|
-
# even if there are too much data.
|
52
|
-
server = "droonga:#{params[:destination_host]}"
|
53
|
-
server = "#{server}:#{params[:port].to_s}" if params[:port]
|
54
|
-
server = "#{server}/#{params[:tag].to_s}" if params[:tag]
|
55
|
-
client_options += ["--server", server]
|
56
|
-
else
|
57
|
-
raise ArgumentError.new("Unknwon type client: #{client}")
|
58
|
-
end
|
34
|
+
new(params).absorb
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
attr_reader :params
|
39
|
+
def initialize(params)
|
40
|
+
@params = params
|
59
41
|
|
60
|
-
|
61
|
-
client_command_line = [client] + client_options
|
42
|
+
@messages_per_second = @params[:messages_per_second] || DEFAULT_MESSAGES_PER_SECOND
|
62
43
|
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
44
|
+
@drndump = @params[:drndump] || "drndump"
|
45
|
+
# We should use droonga-send instead of droonga-request,
|
46
|
+
# because droonga-request is too slow.
|
47
|
+
@client = @params[:client] || "droonga-send"
|
48
|
+
|
49
|
+
@dataset = @params[:dataset] || CatalogGenerator::DEFAULT_DATASET
|
50
|
+
@port = @params[:port] || CatalogGenerator::DEFAULT_PORT
|
51
|
+
@tag = @params[:tag] || CatalogGenerator::DEFAULT_TAG
|
52
|
+
|
53
|
+
@source_host = @params[:source_host]
|
54
|
+
@destination_host = @params[:destination_host]
|
55
|
+
|
56
|
+
@receiver_port = @params[:receiver_port]
|
57
|
+
end
|
58
|
+
|
59
|
+
MESSAGES_PER_SECOND_MATCHER = /(\d+(\.\d+)?) messages\/second/
|
60
|
+
|
61
|
+
def absorb
|
62
|
+
drndump_command_line = [@drndump] + drndump_options
|
63
|
+
client_command_line = [@client] + client_options(@client)
|
64
|
+
|
65
|
+
start_time_in_seconds = Time.new.to_i
|
66
|
+
env = {}
|
67
|
+
Open3.pipeline_r([env, *drndump_command_line],
|
68
|
+
[env, *client_command_line]) do |last_stdout, thread|
|
69
|
+
last_stdout.each do |output|
|
70
|
+
if block_given?
|
71
|
+
messages_per_second = nil
|
72
|
+
if output =~ MESSAGES_PER_SECOND_MATCHER
|
73
|
+
messages_per_second = $1.to_f
|
74
|
+
end
|
75
|
+
yield(:progress => report_progress(start_time_in_seconds),
|
76
|
+
:output => output,
|
77
|
+
:messages_per_second => messages_per_second)
|
68
78
|
end
|
69
79
|
end
|
70
80
|
end
|
71
81
|
end
|
82
|
+
|
83
|
+
def can_report_remaining_time?
|
84
|
+
required_time_in_seconds != Droonga::DataAbsorber::TIME_UNKNOWN and
|
85
|
+
required_time_in_seconds > 0
|
86
|
+
end
|
87
|
+
|
88
|
+
def required_time_in_seconds
|
89
|
+
@required_time_in_seconds ||= calculate_required_time_in_seconds
|
90
|
+
end
|
91
|
+
|
92
|
+
ONE_MINUTE_IN_SECONDS = 60
|
93
|
+
ONE_HOUR_IN_SECONDS = ONE_MINUTE_IN_SECONDS * 60
|
94
|
+
|
95
|
+
def report_progress(start_time_in_seconds)
|
96
|
+
return nil unless can_report_remaining_time?
|
97
|
+
|
98
|
+
elapsed_time = Time.new.to_i - start_time_in_seconds
|
99
|
+
progress = elapsed_time.to_f / required_time_in_seconds
|
100
|
+
progress = [(progress * 100).to_i, 100].min
|
101
|
+
|
102
|
+
remaining_seconds = [required_time_in_seconds - elapsed_time, 0].max
|
103
|
+
remaining_hours = (remaining_seconds / ONE_HOUR_IN_SECONDS).floor
|
104
|
+
remaining_seconds -= remaining_hours * ONE_HOUR_IN_SECONDS
|
105
|
+
remaining_minutes = (remaining_seconds / ONE_MINUTE_IN_SECONDS).floor
|
106
|
+
remaining_seconds -= remaining_minutes * ONE_MINUTE_IN_SECONDS
|
107
|
+
remaining_time = sprintf("%02i:%02i:%02i", remaining_hours, remaining_minutes, remaining_seconds)
|
108
|
+
|
109
|
+
"#{progress}% done (maybe #{remaining_time} remaining)"
|
110
|
+
end
|
111
|
+
|
112
|
+
def source_client
|
113
|
+
options = {
|
114
|
+
:host => @source_host,
|
115
|
+
:port => @port,
|
116
|
+
:tag => @tag,
|
117
|
+
:progocol => :droonga,
|
118
|
+
:receiver_host => @destination_host,
|
119
|
+
:receiver_port => 0,
|
120
|
+
}
|
121
|
+
@source_client ||= Droonga::Client.new(options)
|
122
|
+
end
|
123
|
+
|
124
|
+
def destination_client
|
125
|
+
options = {
|
126
|
+
:host => @destination_host,
|
127
|
+
:port => @port,
|
128
|
+
:tag => @tag,
|
129
|
+
:progocol => :droonga,
|
130
|
+
:receiver_host => @destination_host,
|
131
|
+
:receiver_port => 0,
|
132
|
+
}
|
133
|
+
@destination_client ||= Droonga::Client.new(options)
|
134
|
+
end
|
135
|
+
|
136
|
+
def source_node_suspendable?
|
137
|
+
(source_replica_hosts - [@source_host]).size > 1
|
138
|
+
end
|
139
|
+
|
140
|
+
private
|
141
|
+
def calculate_required_time_in_seconds
|
142
|
+
if @client.include?("droonga-send")
|
143
|
+
total_n_source_records / @messages_per_second
|
144
|
+
else
|
145
|
+
TIME_UNKNOWN
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
def drndump_options
|
150
|
+
options = []
|
151
|
+
options += ["--host", @source_host] if @source_host
|
152
|
+
options += ["--port", @port]
|
153
|
+
options += ["--tag", @tag]
|
154
|
+
options += ["--dataset", @dataset]
|
155
|
+
options += ["--receiver-host", @destination_host]
|
156
|
+
options += ["--receiver-port", @receiver_port] if @receiver_port
|
157
|
+
options.collect(&:to_s)
|
158
|
+
end
|
159
|
+
|
160
|
+
def droonga_request_options
|
161
|
+
options = []
|
162
|
+
options += ["--host", @destination_host]
|
163
|
+
options += ["--port", @port]
|
164
|
+
options += ["--tag", @tag]
|
165
|
+
options += ["--receiver-host", @destination_host]
|
166
|
+
options += ["--receiver-port", @receiver_port] if @receiver_port
|
167
|
+
options.collect(&:to_s)
|
168
|
+
end
|
169
|
+
|
170
|
+
def droonga_send_options
|
171
|
+
options = []
|
172
|
+
|
173
|
+
#XXX Don't use round-robin with multiple endpoints
|
174
|
+
# even if there are too much data.
|
175
|
+
# Schema and indexes must be sent to just one endpoint
|
176
|
+
# to keep their order, but currently there is no way to
|
177
|
+
# extract only schema and indexes via drndump.
|
178
|
+
# So, we always use just one endpoint for now,
|
179
|
+
# even if there are too much data.
|
180
|
+
server = "droonga:#{params[:destination_host]}"
|
181
|
+
server = "#{server}:#{params[:port].to_s}"
|
182
|
+
server = "#{server}/#{params[:tag].to_s}"
|
183
|
+
options += ["--server", server]
|
184
|
+
|
185
|
+
#XXX We should restrict the traffic to avoid overflowing!
|
186
|
+
options += ["--messages-per-second", @messages_per_second]
|
187
|
+
|
188
|
+
options += ["--report-throughput"]
|
189
|
+
|
190
|
+
options.collect(&:to_s)
|
191
|
+
end
|
192
|
+
|
193
|
+
def client_options(client)
|
194
|
+
if client.include?("droonga-request")
|
195
|
+
droonga_request_options
|
196
|
+
elsif client.include?("droonga-send")
|
197
|
+
droonga_send_options
|
198
|
+
else
|
199
|
+
raise ArgumentError.new("Unknwon type client: #{client}")
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
def source_tables
|
204
|
+
response = source_client.request("dataset" => @dataset,
|
205
|
+
"type" => "table_list")
|
206
|
+
body = response["body"][1]
|
207
|
+
tables = body[1..-1]
|
208
|
+
tables.collect do |table|
|
209
|
+
table[1]
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
def total_n_source_records
|
214
|
+
queries = {}
|
215
|
+
source_tables.each do |table|
|
216
|
+
queries["n_records_of_#{table}"] = {
|
217
|
+
"source" => table,
|
218
|
+
"output" => {
|
219
|
+
"elements" => ["count"],
|
220
|
+
},
|
221
|
+
}
|
222
|
+
end
|
223
|
+
response = source_client.request("dataset" => @dataset,
|
224
|
+
"type" => "search",
|
225
|
+
"body" => {
|
226
|
+
"queries" => queries,
|
227
|
+
})
|
228
|
+
n_records = 0
|
229
|
+
response["body"].each do |query_name, result|
|
230
|
+
n_records += result["count"]
|
231
|
+
end
|
232
|
+
n_records
|
233
|
+
end
|
234
|
+
|
235
|
+
def source_replica_hosts
|
236
|
+
@source_replica_hosts ||= get_source_replica_hosts
|
237
|
+
end
|
238
|
+
|
239
|
+
def get_source_replica_hosts
|
240
|
+
generator = CatalogGenerator.new
|
241
|
+
generator.load(source_catalog)
|
242
|
+
dataset = generator.dataset_for_host(@source_host)
|
243
|
+
return [] unless dataset
|
244
|
+
dataset.replicas.hosts
|
245
|
+
end
|
246
|
+
|
247
|
+
def source_catalog
|
248
|
+
@source_catalog ||= fetch_source_catalog
|
249
|
+
end
|
250
|
+
|
251
|
+
def fetch_source_catalog
|
252
|
+
fetcher = CatalogFetcher.new(:host => @source_host,
|
253
|
+
:port => @port,
|
254
|
+
:tag => @tag,
|
255
|
+
:receiver_host => @destination_host)
|
256
|
+
fetcher.fetch(:dataset => @dataset)
|
257
|
+
end
|
258
|
+
|
259
|
+
def log_tag
|
260
|
+
"data-absorber"
|
261
|
+
end
|
72
262
|
end
|
73
263
|
end
|