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
|
@@ -69,10 +69,23 @@ module Droonga
|
|
|
69
69
|
end
|
|
70
70
|
|
|
71
71
|
def delete_record_by_key(table, key)
|
|
72
|
+
key = normalize_record_key(key, table)
|
|
72
73
|
record = table[key]
|
|
73
74
|
record.delete unless record.nil?
|
|
74
75
|
end
|
|
75
76
|
|
|
77
|
+
def normalize_record_key(key, table)
|
|
78
|
+
case table.domain.name
|
|
79
|
+
when "Int8", "UInt8",
|
|
80
|
+
"Int16", "UInt16",
|
|
81
|
+
"Int32", "UInt32",
|
|
82
|
+
"Int64", "UInt64"
|
|
83
|
+
key.to_i
|
|
84
|
+
else
|
|
85
|
+
key.to_s
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
|
|
76
89
|
def delete_record_by_id(table, id)
|
|
77
90
|
record = table[id.to_i]
|
|
78
91
|
record.delete if record and record.valid_id?
|
|
@@ -210,13 +210,13 @@ module Droonga
|
|
|
210
210
|
end
|
|
211
211
|
|
|
212
212
|
def final_offset
|
|
213
|
-
return @original_output_offset
|
|
213
|
+
return @original_output_offset unless @dataset.sliced?
|
|
214
214
|
|
|
215
215
|
@original_sort_offset + @original_output_offset
|
|
216
216
|
end
|
|
217
217
|
|
|
218
218
|
def final_limit
|
|
219
|
-
return @original_output_limit
|
|
219
|
+
return @original_output_limit unless @dataset.sliced?
|
|
220
220
|
|
|
221
221
|
if @original_sort_limit == UNLIMITED and
|
|
222
222
|
@original_output_limit == UNLIMITED
|
|
@@ -251,7 +251,7 @@ module Droonga
|
|
|
251
251
|
"type" => "sum",
|
|
252
252
|
}
|
|
253
253
|
if unifiable?
|
|
254
|
-
|
|
254
|
+
if @dataset.sliced?
|
|
255
255
|
if @query["sortBy"].is_a?(Hash)
|
|
256
256
|
@query["sortBy"]["limit"] = UNLIMITED
|
|
257
257
|
end
|
|
@@ -261,7 +261,7 @@ module Droonga
|
|
|
261
261
|
"target" => "records",
|
|
262
262
|
}
|
|
263
263
|
unless @output["elements"].include?("records")
|
|
264
|
-
@records_limit = UNLIMITED
|
|
264
|
+
@records_limit = UNLIMITED if @dataset.sliced?
|
|
265
265
|
@output["elements"] << "records"
|
|
266
266
|
@output["attributes"] ||= ["_key"]
|
|
267
267
|
@output_records = false
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Copyright (C) 2013-
|
|
1
|
+
# Copyright (C) 2013-2015 Droonga Project
|
|
2
2
|
#
|
|
3
3
|
# This library is free software; you can redistribute it and/or
|
|
4
4
|
# modify it under the terms of the GNU Lesser General Public
|
|
@@ -20,31 +20,10 @@ module Droonga
|
|
|
20
20
|
module System
|
|
21
21
|
extend Plugin
|
|
22
22
|
register("system")
|
|
23
|
-
|
|
24
|
-
class StatusHandler < Droonga::Handler
|
|
25
|
-
action.synchronous = true
|
|
26
|
-
|
|
27
|
-
def handle(message)
|
|
28
|
-
engine_state = @messenger.engine_state
|
|
29
|
-
live_nodes = engine_state.live_nodes
|
|
30
|
-
nodes = {}
|
|
31
|
-
engine_state.all_nodes.collect do |identifier|
|
|
32
|
-
nodes[identifier] = {
|
|
33
|
-
"live" => live_nodes.include?(identifier),
|
|
34
|
-
}
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
{
|
|
38
|
-
"nodes" => nodes,
|
|
39
|
-
}
|
|
40
|
-
end
|
|
41
|
-
end
|
|
42
|
-
|
|
43
|
-
define_single_step do |step|
|
|
44
|
-
step.name = "system.status"
|
|
45
|
-
step.handler = StatusHandler
|
|
46
|
-
step.collector = Collectors::Or
|
|
47
|
-
end
|
|
48
23
|
end
|
|
49
24
|
end
|
|
50
25
|
end
|
|
26
|
+
|
|
27
|
+
require "droonga/plugins/system/status"
|
|
28
|
+
require "droonga/plugins/system/statistics"
|
|
29
|
+
require "droonga/plugins/system/absorb_data"
|
|
@@ -0,0 +1,405 @@
|
|
|
1
|
+
# Copyright (C) 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 "fiber"
|
|
17
|
+
|
|
18
|
+
require "droonga/plugin"
|
|
19
|
+
require "droonga/plugin/async_command"
|
|
20
|
+
require "droonga/catalog/dataset"
|
|
21
|
+
require "droonga/serf"
|
|
22
|
+
require "droonga/node_name"
|
|
23
|
+
|
|
24
|
+
require "drndump/dump_client"
|
|
25
|
+
|
|
26
|
+
module Droonga
|
|
27
|
+
module Plugins
|
|
28
|
+
module System
|
|
29
|
+
class AbsorbDataHandler < AsyncCommand::Handler
|
|
30
|
+
DEFAULT_MESSAGES_PER_SECOND = 100
|
|
31
|
+
DEFAULT_PROGRESS_INTERVAL_SECONDS = 3
|
|
32
|
+
MIN_PROGRESS_INTERVAL_SECONDS = 1
|
|
33
|
+
|
|
34
|
+
class MissingHostParameter < BadRequest
|
|
35
|
+
def initialize
|
|
36
|
+
super("\"host\" must be specified.")
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
class DataAbsorber < AsyncCommand::AsyncHandler
|
|
41
|
+
class EmptyResponse < StandardError
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
class EmptyBody < StandardError
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def start
|
|
48
|
+
logger.trace("start: start")
|
|
49
|
+
on_start
|
|
50
|
+
|
|
51
|
+
count_total_n_objects do |n_objects|
|
|
52
|
+
@n_initial_objects = n_objects
|
|
53
|
+
logger.info("initially #{n_objects} objects exist in the dataset")
|
|
54
|
+
do_absorb do
|
|
55
|
+
ensure_completely_restored do
|
|
56
|
+
on_finish
|
|
57
|
+
logger.trace("start: finish")
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
logger.trace("start: done")
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
private
|
|
66
|
+
def prefix
|
|
67
|
+
"system.absorb-data"
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def error_name
|
|
71
|
+
"AbsorbFailure"
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def error_message
|
|
75
|
+
"failed to absorb data"
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def do_absorb(&block)
|
|
79
|
+
logger.trace("do_absorb: start")
|
|
80
|
+
@dumper_error_message = nil
|
|
81
|
+
|
|
82
|
+
@n_restored_objects = 0
|
|
83
|
+
@measure_start_time = Time.now
|
|
84
|
+
@previous_measure_time = @measure_start_time
|
|
85
|
+
@previous_n_restored_objects = 0.0
|
|
86
|
+
@previous_report_time = Time.now
|
|
87
|
+
|
|
88
|
+
@dumper = create_dumper
|
|
89
|
+
@dumper.on_finish = lambda do
|
|
90
|
+
yield
|
|
91
|
+
end
|
|
92
|
+
begin
|
|
93
|
+
logger.info("starting to absorb the source dataset")
|
|
94
|
+
@dumper_error_message = @dumper.run(dump_options) do |message|
|
|
95
|
+
begin
|
|
96
|
+
message["dataset"] = current_dataset
|
|
97
|
+
message["targetRole"] = target_role if target_role
|
|
98
|
+
message["xSender"] = "system.absorb-data"
|
|
99
|
+
@messenger.forward(message,
|
|
100
|
+
"to" => my_node_name,
|
|
101
|
+
"type" => message["type"])
|
|
102
|
+
try_report_progress(:count_restored_objects => true)
|
|
103
|
+
rescue Exception => exception
|
|
104
|
+
@dumper_error_message = exception.to_s
|
|
105
|
+
logger.exception("failed to process progress",
|
|
106
|
+
exception)
|
|
107
|
+
on_finish
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
rescue Exception => exception
|
|
111
|
+
@dumper_error_message = exception.to_s
|
|
112
|
+
logger.exception("failed to start dump",
|
|
113
|
+
exception)
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
on_finish if @dumper_error_message
|
|
117
|
+
logger.trace("do_absorb: done")
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def create_dumper
|
|
121
|
+
dumper = Drndump::DumpClient.new(dumper_params)
|
|
122
|
+
dumper.on_progress = lambda do |message|
|
|
123
|
+
logger.trace("dump progress",
|
|
124
|
+
:message => message)
|
|
125
|
+
end
|
|
126
|
+
dumper.on_error = lambda do |error|
|
|
127
|
+
if error.is_a?(Exception)
|
|
128
|
+
logger.exception("unexpected exception while dump",
|
|
129
|
+
error)
|
|
130
|
+
else
|
|
131
|
+
logger.error("unexpected error while dump",
|
|
132
|
+
:error => error)
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
dumper
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
def dumper_params
|
|
139
|
+
{
|
|
140
|
+
:host => source_host,
|
|
141
|
+
:port => source_port,
|
|
142
|
+
:tag => source_tag,
|
|
143
|
+
:dataset => source_dataset,
|
|
144
|
+
|
|
145
|
+
:receiver_host => myself.host,
|
|
146
|
+
:receiver_port => 0,
|
|
147
|
+
}
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
def dump_options
|
|
151
|
+
{
|
|
152
|
+
:backend => :coolio,
|
|
153
|
+
:loop => @loop,
|
|
154
|
+
:messages_per_second => messages_per_second,
|
|
155
|
+
}
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
def ensure_completely_restored(&block)
|
|
159
|
+
runner = Fiber.new do
|
|
160
|
+
completely_restored = false
|
|
161
|
+
n_expected_objects = @dumper.n_forecasted_messages
|
|
162
|
+
while not completely_restored
|
|
163
|
+
count_total_n_objects do |count|
|
|
164
|
+
@n_restored_objects = count - @n_initial_objects
|
|
165
|
+
completely_restored ||= @n_restored_objects == n_expected_objects
|
|
166
|
+
try_report_progress
|
|
167
|
+
end
|
|
168
|
+
Fiber.yield
|
|
169
|
+
end
|
|
170
|
+
count_client.close
|
|
171
|
+
yield
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
timer = Coolio::TimerWatcher.new(DEFAULT_PROGRESS_INTERVAL_SECONDS, true)
|
|
175
|
+
timer.on_timer do
|
|
176
|
+
if runner.alive?
|
|
177
|
+
begin
|
|
178
|
+
runner.resume
|
|
179
|
+
rescue
|
|
180
|
+
timer.detach
|
|
181
|
+
logger.trace("start: timer detached on unexpected exception",
|
|
182
|
+
:watcher => timer)
|
|
183
|
+
logger.exception(error_message, $!)
|
|
184
|
+
error(error_name, error_message)
|
|
185
|
+
end
|
|
186
|
+
else
|
|
187
|
+
timer.detach
|
|
188
|
+
logger.trace("start: timer detached on unexpected exception",
|
|
189
|
+
:watcher => timer)
|
|
190
|
+
end
|
|
191
|
+
end
|
|
192
|
+
@loop.attach(timer)
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
def on_finish
|
|
196
|
+
begin
|
|
197
|
+
if @dumper_error_message
|
|
198
|
+
error(error_name, @dumper_error_message)
|
|
199
|
+
else
|
|
200
|
+
report_progress
|
|
201
|
+
end
|
|
202
|
+
rescue Exception => exception
|
|
203
|
+
@dumper_error_message = exception.to_s
|
|
204
|
+
logger.exception("failed to finish dump",
|
|
205
|
+
exception)
|
|
206
|
+
error(error_name, @dumper_error_message)
|
|
207
|
+
end
|
|
208
|
+
super
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
#TODO: Currently we are counting/comparing the number of
|
|
212
|
+
# physical objects, but it will be different from
|
|
213
|
+
# the source dataset if the destination is differently
|
|
214
|
+
# sliced dataset. So, we should count/compare the
|
|
215
|
+
# number of logical objects in the future after
|
|
216
|
+
# supporting management of sliced datasets.
|
|
217
|
+
def count_total_n_objects(&block)
|
|
218
|
+
count_message = {
|
|
219
|
+
"type" => "system.statistics.object.count",
|
|
220
|
+
"dataset" => current_dataset,
|
|
221
|
+
"body" => {
|
|
222
|
+
"output" => ["total"],
|
|
223
|
+
},
|
|
224
|
+
}
|
|
225
|
+
count_client.request(count_message) do |response|
|
|
226
|
+
yield(response["body"]["total"])
|
|
227
|
+
end
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
def count_client
|
|
231
|
+
@count_client ||= Droonga::Client.new(count_client_options)
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
def count_client_options
|
|
235
|
+
{
|
|
236
|
+
:host => myself.host,
|
|
237
|
+
:port => myself.port,
|
|
238
|
+
:tag => myself.tag,
|
|
239
|
+
:protocol => :droonga,
|
|
240
|
+
:backend => :coolio,
|
|
241
|
+
:loop => @loop,
|
|
242
|
+
}
|
|
243
|
+
end
|
|
244
|
+
|
|
245
|
+
def try_report_progress(options={})
|
|
246
|
+
now = Time.now
|
|
247
|
+
elapsed_seconds = (now - @previous_report_time).to_i
|
|
248
|
+
if elapsed_seconds >= progress_interval_seconds
|
|
249
|
+
if options[:count_restored_objects]
|
|
250
|
+
@previous_report_time = now
|
|
251
|
+
count_total_n_objects do |count|
|
|
252
|
+
@previous_report_time = Time.now
|
|
253
|
+
@n_restored_objects = count - @n_initial_objects
|
|
254
|
+
report_progress
|
|
255
|
+
end
|
|
256
|
+
else
|
|
257
|
+
@previous_report_time = now
|
|
258
|
+
report_progress
|
|
259
|
+
end
|
|
260
|
+
end
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
def report_progress
|
|
264
|
+
message = "#{progress_percentage}% done " +
|
|
265
|
+
"(maybe #{formatted_remaining_time} remaining)"
|
|
266
|
+
forward("#{prefix}.progress",
|
|
267
|
+
"nProcessedMessages" => @dumper.n_received_messages,
|
|
268
|
+
"nRestoredObjects" => @n_restored_objects,
|
|
269
|
+
"percentage" => progress_percentage,
|
|
270
|
+
"message" => message)
|
|
271
|
+
end
|
|
272
|
+
|
|
273
|
+
MIN_REPORTED_THROUGHPUT = 0.01
|
|
274
|
+
|
|
275
|
+
def recent_throughput
|
|
276
|
+
now = Time.now
|
|
277
|
+
n_objects = @n_restored_objects - @previous_n_restored_objects
|
|
278
|
+
|
|
279
|
+
if now - @previous_measure_time < 1
|
|
280
|
+
now = @previous_measure_time
|
|
281
|
+
n_objects = @previous_n_restored_objects
|
|
282
|
+
else
|
|
283
|
+
@previous_measure_time = now
|
|
284
|
+
@previous_n_restored_objects = n_objects.to_f
|
|
285
|
+
end
|
|
286
|
+
|
|
287
|
+
if now == @measure_start_time
|
|
288
|
+
actual_throughput = 0
|
|
289
|
+
else
|
|
290
|
+
elapsed_seconds = now - @measure_start_time
|
|
291
|
+
actual_throughput = n_objects / elapsed_seconds
|
|
292
|
+
end
|
|
293
|
+
|
|
294
|
+
[actual_throughput, MIN_REPORTED_THROUGHPUT].max
|
|
295
|
+
end
|
|
296
|
+
|
|
297
|
+
def n_remaining_objects
|
|
298
|
+
[@dumper.n_forecasted_messages - @n_restored_objects, 0].max
|
|
299
|
+
end
|
|
300
|
+
|
|
301
|
+
def remaining_seconds
|
|
302
|
+
throughput = [recent_throughput, messages_per_second].min
|
|
303
|
+
remaining_seconds = n_remaining_objects.to_f / throughput
|
|
304
|
+
@dumper.remaining_seconds + remaining_seconds
|
|
305
|
+
end
|
|
306
|
+
|
|
307
|
+
ONE_MINUTE_IN_SECONDS = 60
|
|
308
|
+
ONE_HOUR_IN_SECONDS = ONE_MINUTE_IN_SECONDS * 60
|
|
309
|
+
|
|
310
|
+
def formatted_remaining_time
|
|
311
|
+
seconds = remaining_seconds
|
|
312
|
+
hours = (seconds / ONE_HOUR_IN_SECONDS).floor
|
|
313
|
+
seconds -= hours * ONE_HOUR_IN_SECONDS
|
|
314
|
+
minutes = (seconds / ONE_MINUTE_IN_SECONDS).floor
|
|
315
|
+
seconds -= minutes * ONE_MINUTE_IN_SECONDS
|
|
316
|
+
sprintf("%02i:%02i:%02i", hours, minutes, seconds)
|
|
317
|
+
end
|
|
318
|
+
|
|
319
|
+
def progress_percentage
|
|
320
|
+
return 0 if @dumper.n_forecasted_messages.zero?
|
|
321
|
+
processed = @dumper.n_received_messages + @n_restored_objects
|
|
322
|
+
expected = @dumper.n_forecasted_messages * 2
|
|
323
|
+
progress = processed.to_f / expected
|
|
324
|
+
[(progress * 100).to_i, 100].min
|
|
325
|
+
end
|
|
326
|
+
|
|
327
|
+
def myself
|
|
328
|
+
@myself ||= NodeName.parse(my_node_name)
|
|
329
|
+
end
|
|
330
|
+
|
|
331
|
+
def my_node_name
|
|
332
|
+
ENV["DROONGA_ENGINE_NAME"]
|
|
333
|
+
end
|
|
334
|
+
|
|
335
|
+
def current_dataset
|
|
336
|
+
@request.dataset
|
|
337
|
+
end
|
|
338
|
+
|
|
339
|
+
def target_role
|
|
340
|
+
@request.request["targetRole"]
|
|
341
|
+
end
|
|
342
|
+
|
|
343
|
+
def prepare_progress_interval_seconds
|
|
344
|
+
interval_seconds = @request.request["progressIntervalSeconds"] ||
|
|
345
|
+
DEFAULT_PROGRESS_INTERVAL_SECONDS
|
|
346
|
+
interval_seconds = interval_seconds.to_i
|
|
347
|
+
[interval_seconds, MIN_PROGRESS_INTERVAL_SECONDS].max
|
|
348
|
+
end
|
|
349
|
+
|
|
350
|
+
def progress_interval_seconds
|
|
351
|
+
@progress_interval_seconds ||= prepare_progress_interval_seconds
|
|
352
|
+
end
|
|
353
|
+
|
|
354
|
+
def source_host
|
|
355
|
+
@source_host ||= @request.request["host"]
|
|
356
|
+
end
|
|
357
|
+
|
|
358
|
+
def source_port
|
|
359
|
+
@source_port ||= @request.request["port"] ||
|
|
360
|
+
NodeName::DEFAULT_PORT
|
|
361
|
+
end
|
|
362
|
+
|
|
363
|
+
def source_tag
|
|
364
|
+
@source_tag ||= @request.request["tag"] ||
|
|
365
|
+
NodeName::DEFAULT_TAG
|
|
366
|
+
end
|
|
367
|
+
|
|
368
|
+
def source_dataset
|
|
369
|
+
@source_dataset ||= @request.request["dataset"] ||
|
|
370
|
+
Catalog::Dataset::DEFAULT_NAME
|
|
371
|
+
end
|
|
372
|
+
|
|
373
|
+
def messages_per_second
|
|
374
|
+
@messages_per_second ||= @request.request["messagesPerSecond"] ||
|
|
375
|
+
DEFAULT_MESSAGES_PER_SECOND
|
|
376
|
+
end
|
|
377
|
+
|
|
378
|
+
def log_tag
|
|
379
|
+
"[#{Process.ppid}] data-absorber"
|
|
380
|
+
end
|
|
381
|
+
end
|
|
382
|
+
|
|
383
|
+
def handle(message)
|
|
384
|
+
unless message.request.include?("host")
|
|
385
|
+
raise MissingHostParameter.new
|
|
386
|
+
end
|
|
387
|
+
super
|
|
388
|
+
end
|
|
389
|
+
|
|
390
|
+
private
|
|
391
|
+
def start(request)
|
|
392
|
+
absorber = DataAbsorber.new(loop, messenger, request)
|
|
393
|
+
absorber.start
|
|
394
|
+
end
|
|
395
|
+
end
|
|
396
|
+
|
|
397
|
+
define_single_step do |step|
|
|
398
|
+
step.name = "system.absorb-data"
|
|
399
|
+
step.single_operation = true
|
|
400
|
+
step.handler = AbsorbDataHandler
|
|
401
|
+
step.collector = Collectors::Or
|
|
402
|
+
end
|
|
403
|
+
end
|
|
404
|
+
end
|
|
405
|
+
end
|