droonga-engine 1.0.7 → 1.0.8
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 +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
|