droonga-engine 1.0.9 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +1 -0
- data/benchmark/timer-watcher/benchmark.rb +44 -0
- data/bin/droonga-engine-absorb-data +246 -187
- data/bin/droonga-engine-catalog-generate +12 -12
- data/bin/droonga-engine-catalog-modify +4 -4
- data/bin/droonga-engine-join +352 -171
- data/bin/droonga-engine-set-role +54 -0
- data/bin/droonga-engine-unjoin +107 -112
- data/droonga-engine.gemspec +3 -3
- data/install.sh +55 -36
- data/install/centos/functions.sh +2 -2
- data/install/debian/functions.sh +2 -2
- data/lib/droonga/address.rb +26 -24
- data/lib/droonga/buffered_tcp_socket.rb +65 -10
- data/lib/droonga/catalog/base.rb +9 -6
- data/lib/droonga/catalog/dataset.rb +17 -41
- data/lib/droonga/catalog/fetcher.rb +64 -0
- data/lib/droonga/catalog/generator.rb +245 -0
- data/lib/droonga/catalog/loader.rb +66 -0
- data/lib/droonga/{catalog_modifier.rb → catalog/modifier.rb} +11 -18
- data/lib/droonga/catalog/replicas_volume.rb +123 -0
- data/lib/droonga/catalog/schema.rb +37 -37
- data/lib/droonga/catalog/single_volume.rb +11 -3
- data/lib/droonga/catalog/slice.rb +10 -6
- data/lib/droonga/catalog/{collection_volume.rb → slices_volume.rb} +47 -11
- data/lib/droonga/catalog/version1.rb +47 -19
- data/lib/droonga/catalog/version2.rb +11 -10
- data/lib/droonga/catalog/version2_validator.rb +4 -4
- data/lib/droonga/catalog/volume.rb +17 -5
- data/lib/droonga/changable.rb +25 -0
- data/lib/droonga/cluster.rb +237 -0
- data/lib/droonga/collector_runner.rb +4 -0
- data/lib/droonga/collectors.rb +2 -1
- data/lib/droonga/collectors/recursive_sum.rb +26 -0
- data/lib/droonga/command/droonga_engine.rb +404 -127
- data/lib/droonga/command/droonga_engine_service.rb +47 -11
- data/lib/droonga/command/droonga_engine_worker.rb +21 -1
- data/lib/droonga/command/remote_command_base.rb +78 -0
- data/lib/droonga/command/serf_event_handler.rb +29 -20
- data/lib/droonga/data_absorber_client.rb +222 -0
- data/lib/droonga/database_scanner.rb +106 -0
- data/lib/droonga/{live_nodes_list_loader.rb → deferrable.rb} +11 -24
- data/lib/droonga/differ.rb +58 -0
- data/lib/droonga/dispatcher.rb +155 -32
- data/lib/droonga/distributed_command_planner.rb +9 -11
- data/lib/droonga/engine.rb +83 -78
- data/lib/droonga/engine/version.rb +1 -1
- data/lib/droonga/engine_node.rb +301 -0
- data/lib/droonga/engine_state.rb +62 -40
- data/lib/droonga/farm.rb +44 -5
- data/lib/droonga/file_observer.rb +16 -12
- data/lib/droonga/fluent_message_receiver.rb +98 -29
- data/lib/droonga/fluent_message_sender.rb +30 -23
- data/lib/droonga/forward_buffer.rb +160 -0
- data/lib/droonga/forwarder.rb +73 -40
- data/lib/droonga/handler.rb +7 -6
- data/lib/droonga/handler_messenger.rb +15 -6
- data/lib/droonga/handler_runner.rb +6 -1
- data/lib/droonga/internal_fluent_message_receiver.rb +28 -8
- data/lib/droonga/job_pusher.rb +10 -7
- data/lib/droonga/job_receiver.rb +6 -4
- data/lib/droonga/logger.rb +7 -1
- data/lib/droonga/node_name.rb +90 -0
- data/lib/droonga/node_role.rb +72 -0
- data/lib/droonga/path.rb +34 -9
- data/lib/droonga/planner.rb +73 -7
- data/lib/droonga/plugin/async_command.rb +154 -0
- data/lib/droonga/plugins/catalog.rb +1 -0
- data/lib/droonga/plugins/crud.rb +22 -6
- data/lib/droonga/plugins/dump.rb +66 -135
- data/lib/droonga/plugins/groonga/delete.rb +13 -0
- data/lib/droonga/plugins/search/distributed_search_planner.rb +4 -4
- data/lib/droonga/plugins/system.rb +5 -26
- data/lib/droonga/plugins/system/absorb_data.rb +405 -0
- data/lib/droonga/plugins/system/statistics.rb +71 -0
- data/lib/droonga/plugins/system/status.rb +53 -0
- data/lib/droonga/process_control_protocol.rb +3 -1
- data/lib/droonga/process_supervisor.rb +32 -15
- data/lib/droonga/reducer.rb +69 -0
- data/lib/droonga/safe_file_writer.rb +1 -1
- data/lib/droonga/serf.rb +207 -276
- data/lib/droonga/serf/agent.rb +228 -0
- data/lib/droonga/serf/command.rb +94 -0
- data/lib/droonga/serf/downloader.rb +120 -0
- data/lib/droonga/serf/remote_command.rb +348 -0
- data/lib/droonga/serf/tag.rb +56 -0
- data/lib/droonga/service_installation.rb +2 -2
- data/lib/droonga/session.rb +49 -1
- data/lib/droonga/single_step.rb +6 -11
- data/lib/droonga/single_step_definition.rb +32 -1
- data/lib/droonga/slice.rb +14 -9
- data/lib/droonga/supervisor.rb +27 -20
- data/lib/droonga/test/stub_handler_messenger.rb +2 -1
- data/lib/droonga/timestamp.rb +69 -0
- data/lib/droonga/worker_process_agent.rb +33 -15
- data/sample/cluster-state.json +8 -0
- data/sample/cluster/Rakefile +30 -6
- data/test/command/fixture/integer-key-table.jsons +11 -0
- data/test/command/fixture/string-key-table.jsons +11 -0
- data/test/command/run-test.rb +4 -0
- data/test/command/suite/add/error/invalid-integer.expected +3 -3
- data/test/command/suite/add/error/invalid-time.expected +3 -3
- data/test/command/suite/add/{minimum.expected → key-integer.expected} +0 -0
- data/test/command/suite/add/{minimum.test → key-integer.test} +0 -0
- data/test/command/suite/add/key-string.expected +6 -0
- data/test/command/suite/add/key-string.test +9 -0
- data/test/command/suite/add/mismatched-key-type/acceptable/integer-for-string.expected +6 -0
- data/test/command/suite/add/mismatched-key-type/acceptable/integer-for-string.test +9 -0
- data/test/command/suite/add/mismatched-key-type/acceptable/string-for-integer.expected +6 -0
- data/test/command/suite/add/mismatched-key-type/acceptable/string-for-integer.test +9 -0
- data/test/command/suite/add/without-values.expected +6 -0
- data/test/command/suite/add/without-values.test +11 -0
- data/test/command/suite/dump/column/index.expected +33 -1
- data/test/command/suite/dump/column/index.test +1 -0
- data/test/command/suite/dump/column/scalar.expected +29 -1
- data/test/command/suite/dump/column/scalar.test +1 -0
- data/test/command/suite/dump/column/vector.expected +29 -1
- data/test/command/suite/dump/column/vector.test +1 -0
- data/test/command/suite/dump/record/scalar.catalog.json +12 -0
- data/test/command/suite/dump/record/scalar.expected +84 -0
- data/test/command/suite/dump/record/scalar.test +16 -0
- data/test/command/suite/dump/record/vector/reference.expected +83 -1
- data/test/command/suite/dump/record/vector/reference.test +1 -0
- data/test/command/suite/dump/table/array.expected +27 -1
- data/test/command/suite/dump/table/array.test +1 -0
- data/test/command/suite/dump/table/double_array_trie.expected +27 -1
- data/test/command/suite/dump/table/double_array_trie.test +1 -0
- data/test/command/suite/dump/table/hash.expected +27 -1
- data/test/command/suite/dump/table/hash.test +1 -0
- data/test/command/suite/dump/table/patricia_trie.expected +27 -1
- data/test/command/suite/dump/table/patricia_trie.test +1 -0
- data/test/command/suite/groonga/delete/{success.expected → key-integer.expected} +0 -0
- data/test/command/suite/groonga/delete/key-integer.test +17 -0
- data/test/command/suite/groonga/delete/key-string.expected +19 -0
- data/test/command/suite/groonga/delete/{success.test → key-string.test} +4 -6
- data/test/command/suite/groonga/delete/mismatched-type-key/acceptable/integer-for-string.expected +19 -0
- data/test/command/suite/groonga/delete/mismatched-type-key/acceptable/integer-for-string.test +17 -0
- data/test/command/suite/groonga/delete/mismatched-type-key/acceptable/string-for-integer.expected +19 -0
- data/test/command/suite/groonga/delete/mismatched-type-key/acceptable/string-for-integer.test +17 -0
- data/test/command/suite/message/error/missing-dataset.test +1 -0
- data/test/command/suite/system/absorb-data/records.catalog.json +58 -0
- data/test/command/suite/system/absorb-data/records.expected +32 -0
- data/test/command/suite/system/absorb-data/records.test +24 -0
- data/test/command/suite/system/statistics/object/count/empty.expected +11 -0
- data/test/command/suite/system/statistics/object/count/empty.test +12 -0
- data/test/command/suite/system/statistics/object/count/per-volume/empty.catalog.json +36 -0
- data/test/command/suite/system/statistics/object/count/per-volume/empty.expected +19 -0
- data/test/command/suite/system/statistics/object/count/per-volume/empty.test +12 -0
- data/test/command/suite/system/statistics/object/count/per-volume/record.catalog.json +40 -0
- data/test/command/suite/system/statistics/object/count/per-volume/record.expected +19 -0
- data/test/command/suite/system/statistics/object/count/per-volume/record.test +23 -0
- data/test/command/suite/system/statistics/object/count/per-volume/schema.catalog.json +40 -0
- data/test/command/suite/system/statistics/object/count/per-volume/schema.expected +19 -0
- data/test/command/suite/system/statistics/object/count/per-volume/schema.test +13 -0
- data/test/command/suite/system/statistics/object/count/record.catalog.json +12 -0
- data/test/command/suite/system/statistics/object/count/record.expected +11 -0
- data/test/command/suite/system/statistics/object/count/record.test +23 -0
- data/test/command/suite/system/statistics/object/count/schema.catalog.json +12 -0
- data/test/command/suite/system/statistics/object/count/schema.expected +11 -0
- data/test/command/suite/system/statistics/object/count/schema.test +13 -0
- data/test/command/suite/system/status.expected +3 -2
- data/test/unit/catalog/test_dataset.rb +4 -1
- data/test/unit/{test_catalog_generator.rb → catalog/test_generator.rb} +2 -2
- data/test/unit/catalog/test_replicas_volume.rb +79 -0
- data/test/unit/catalog/test_single_volume.rb +2 -2
- data/test/unit/catalog/test_slice.rb +33 -1
- data/test/unit/catalog/{test_collection_volume.rb → test_slices_volume.rb} +72 -11
- data/test/unit/catalog/test_version2.rb +3 -0
- data/test/unit/helper/distributed_search_planner_helper.rb +2 -2
- data/test/unit/plugins/catalog/test_fetch.rb +4 -4
- data/test/unit/plugins/crud/test_add.rb +44 -4
- data/test/unit/plugins/groonga/test_column_create.rb +4 -4
- data/test/unit/plugins/groonga/test_column_list.rb +4 -4
- data/test/unit/plugins/groonga/test_column_remove.rb +4 -4
- data/test/unit/plugins/groonga/test_column_rename.rb +4 -4
- data/test/unit/plugins/groonga/test_delete.rb +73 -10
- data/test/unit/plugins/groonga/test_table_create.rb +4 -4
- data/test/unit/plugins/groonga/test_table_list.rb +4 -4
- data/test/unit/plugins/groonga/test_table_remove.rb +4 -4
- data/test/unit/plugins/search/test_handler.rb +4 -4
- data/test/unit/plugins/search/test_planner.rb +4 -2
- data/test/unit/plugins/system/test_status.rb +31 -15
- data/test/unit/plugins/test_watch.rb +16 -16
- data/test/unit/test_address.rb +4 -4
- metadata +134 -35
- data/lib/droonga/catalog/volume_collection.rb +0 -79
- data/lib/droonga/catalog_fetcher.rb +0 -53
- data/lib/droonga/catalog_generator.rb +0 -243
- data/lib/droonga/catalog_loader.rb +0 -56
- data/lib/droonga/command/remote.rb +0 -404
- data/lib/droonga/data_absorber.rb +0 -264
- data/lib/droonga/node_status.rb +0 -71
- data/lib/droonga/serf_downloader.rb +0 -115
- data/test/unit/catalog/test_volume_collection.rb +0 -78
@@ -24,7 +24,7 @@ require "droonga/catalog/version2_validator"
|
|
24
24
|
module Droonga
|
25
25
|
module Catalog
|
26
26
|
class Version2 < Base
|
27
|
-
def initialize(
|
27
|
+
def initialize(raw, path)
|
28
28
|
super
|
29
29
|
validate
|
30
30
|
prepare_data
|
@@ -44,16 +44,17 @@ module Droonga
|
|
44
44
|
volume.slices.each do |slice|
|
45
45
|
volume_address = slice.volume.address
|
46
46
|
if volume_address.node == node
|
47
|
-
|
47
|
+
local_name = volume_address.local_name
|
48
48
|
path = Path.databases(base_path) +
|
49
|
-
device +
|
50
|
-
migrate_database_location(path, device,
|
49
|
+
device + local_name + "db"
|
50
|
+
migrate_database_location(path, device, local_name)
|
51
51
|
|
52
52
|
options = {
|
53
|
-
:
|
54
|
-
:
|
53
|
+
:label => volume_address.to_s,
|
54
|
+
:dataset => dataset_name,
|
55
|
+
:database => path.to_s,
|
55
56
|
:n_workers => n_workers,
|
56
|
-
:plugins
|
57
|
+
:plugins => plugins
|
57
58
|
}
|
58
59
|
results[volume_address.to_s] = options
|
59
60
|
end
|
@@ -69,13 +70,13 @@ module Droonga
|
|
69
70
|
|
70
71
|
private
|
71
72
|
def validate
|
72
|
-
validator = Version2Validator.new(@
|
73
|
+
validator = Version2Validator.new(@raw, @path)
|
73
74
|
validator.validate
|
74
75
|
end
|
75
76
|
|
76
77
|
def prepare_data
|
77
78
|
@datasets = {}
|
78
|
-
@
|
79
|
+
@raw["datasets"].each do |name, dataset|
|
79
80
|
@datasets[name] = Dataset.new(name, dataset)
|
80
81
|
end
|
81
82
|
end
|
@@ -83,7 +84,7 @@ module Droonga
|
|
83
84
|
def collect_all_nodes
|
84
85
|
nodes = []
|
85
86
|
@datasets.each do |name, dataset|
|
86
|
-
nodes
|
87
|
+
nodes.concat(dataset.all_nodes)
|
87
88
|
end
|
88
89
|
nodes.sort.uniq
|
89
90
|
end
|
@@ -18,8 +18,8 @@ require "droonga/catalog/errors"
|
|
18
18
|
module Droonga
|
19
19
|
module Catalog
|
20
20
|
class Version2Validator
|
21
|
-
def initialize(
|
22
|
-
@
|
21
|
+
def initialize(raw, path)
|
22
|
+
@raw = raw
|
23
23
|
@path = path
|
24
24
|
end
|
25
25
|
|
@@ -35,11 +35,11 @@ module Droonga
|
|
35
35
|
|
36
36
|
private
|
37
37
|
def validate_datasets
|
38
|
-
unless @
|
38
|
+
unless @raw.key?("datasets")
|
39
39
|
required_parameter_is_missing("datasets")
|
40
40
|
return
|
41
41
|
end
|
42
|
-
@
|
42
|
+
@raw["datasets"].each do |name, dataset|
|
43
43
|
validate_dataset(name, dataset)
|
44
44
|
end
|
45
45
|
end
|
@@ -14,17 +14,29 @@
|
|
14
14
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
15
15
|
|
16
16
|
require "droonga/catalog/single_volume"
|
17
|
-
require "droonga/catalog/
|
17
|
+
require "droonga/catalog/slices_volume"
|
18
|
+
require "droonga/catalog/replicas_volume"
|
18
19
|
|
19
20
|
module Droonga
|
20
21
|
module Catalog
|
21
22
|
module Volume
|
23
|
+
class UnknownTypeVolume < ArgumentError
|
24
|
+
def initialize(raw)
|
25
|
+
super("volume must have one of 'address', 'slices' or 'replicas': " +
|
26
|
+
"#{raw.inspect}")
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
22
30
|
class << self
|
23
|
-
def create(dataset,
|
24
|
-
if
|
25
|
-
SingleVolume.new(
|
31
|
+
def create(dataset, raw)
|
32
|
+
if raw.key?("address")
|
33
|
+
SingleVolume.new(raw)
|
34
|
+
elsif raw.key?("slices")
|
35
|
+
SlicesVolume.new(dataset, raw)
|
36
|
+
elsif raw.key?("replicas")
|
37
|
+
ReplicasVolume.new(dataset, raw)
|
26
38
|
else
|
27
|
-
|
39
|
+
raise UnknownTypeVolume.new(raw)
|
28
40
|
end
|
29
41
|
end
|
30
42
|
end
|
@@ -0,0 +1,25 @@
|
|
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
|
+
module Droonga
|
17
|
+
module Changable
|
18
|
+
attr_writer :on_change
|
19
|
+
|
20
|
+
private
|
21
|
+
def on_change
|
22
|
+
@on_change.call if @on_change
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,237 @@
|
|
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/changable"
|
18
|
+
require "droonga/path"
|
19
|
+
require "droonga/file_observer"
|
20
|
+
require "droonga/engine_node"
|
21
|
+
require "droonga/differ"
|
22
|
+
|
23
|
+
module Droonga
|
24
|
+
class Cluster
|
25
|
+
include Loggable
|
26
|
+
include Changable
|
27
|
+
|
28
|
+
class NoCatalogLoaded < StandardError
|
29
|
+
end
|
30
|
+
|
31
|
+
class NotStartedYet < StandardError
|
32
|
+
end
|
33
|
+
|
34
|
+
class UnknownTarget < StandardError
|
35
|
+
end
|
36
|
+
|
37
|
+
class NoAcceptableReceiver < StandardError
|
38
|
+
def initialize(message)
|
39
|
+
super(message.inspect)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
class << self
|
44
|
+
def load_state_file
|
45
|
+
path = Path.cluster_state
|
46
|
+
|
47
|
+
return default_state unless path.exist?
|
48
|
+
|
49
|
+
contents = path.read
|
50
|
+
return default_state if contents.empty?
|
51
|
+
|
52
|
+
begin
|
53
|
+
JSON.parse(contents)
|
54
|
+
rescue JSON::ParserError
|
55
|
+
default_state
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def default_state
|
60
|
+
{}
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
attr_accessor :catalog
|
65
|
+
|
66
|
+
def initialize(loop, params)
|
67
|
+
@loop = loop
|
68
|
+
|
69
|
+
@params = params
|
70
|
+
@catalog = params[:catalog]
|
71
|
+
@state = nil
|
72
|
+
|
73
|
+
reload
|
74
|
+
end
|
75
|
+
|
76
|
+
def start_observe
|
77
|
+
return if @file_observer
|
78
|
+
logger.trace("start_observe: start")
|
79
|
+
@file_observer = FileObserver.new(@loop, Path.cluster_state)
|
80
|
+
@file_observer.on_change = lambda do
|
81
|
+
reload
|
82
|
+
end
|
83
|
+
@file_observer.start
|
84
|
+
logger.trace("start_observe: done")
|
85
|
+
end
|
86
|
+
|
87
|
+
def stop_observe
|
88
|
+
return unless @file_observer
|
89
|
+
logger.trace("stop_observe: start")
|
90
|
+
@file_observer.stop
|
91
|
+
@file_observer = nil
|
92
|
+
logger.trace("stop_observe: done")
|
93
|
+
end
|
94
|
+
|
95
|
+
def start
|
96
|
+
logger.trace("start: start")
|
97
|
+
engine_nodes.each(&:start)
|
98
|
+
start_observe
|
99
|
+
logger.trace("start: done")
|
100
|
+
end
|
101
|
+
|
102
|
+
def shutdown
|
103
|
+
logger.trace("shutdown: start")
|
104
|
+
stop_observe
|
105
|
+
engine_nodes.each(&:shutdown)
|
106
|
+
logger.trace("shutdown: done")
|
107
|
+
end
|
108
|
+
|
109
|
+
def refresh_connection_for(name)
|
110
|
+
logger.trace("refresh_connection_for(#{name}): start")
|
111
|
+
engine_nodes.each do |node|
|
112
|
+
if node.name == name
|
113
|
+
node.refresh_connection
|
114
|
+
end
|
115
|
+
end
|
116
|
+
logger.trace("refresh_connection_for(#{name}): done")
|
117
|
+
end
|
118
|
+
|
119
|
+
def reload
|
120
|
+
logger.trace("reload: start")
|
121
|
+
if @state
|
122
|
+
old_state = @state.dup
|
123
|
+
else
|
124
|
+
old_state = nil
|
125
|
+
end
|
126
|
+
@state = self.class.load_state_file
|
127
|
+
if @state == old_state
|
128
|
+
logger.info("cluster state not changed")
|
129
|
+
else
|
130
|
+
logger.info("cluster state changed",
|
131
|
+
:before => old_state,
|
132
|
+
:after => @state,
|
133
|
+
:diff => Differ.diff(old_state, @state))
|
134
|
+
clear_cache
|
135
|
+
engine_nodes.each(&:resume)
|
136
|
+
on_change
|
137
|
+
end
|
138
|
+
logger.trace("reload: done")
|
139
|
+
end
|
140
|
+
|
141
|
+
def engine_nodes
|
142
|
+
@engine_nodes ||= create_engine_nodes
|
143
|
+
end
|
144
|
+
|
145
|
+
def engine_nodes_status
|
146
|
+
nodes_status = {}
|
147
|
+
engine_nodes.each do |node|
|
148
|
+
nodes_status[node.name] = {
|
149
|
+
"status" => node.status,
|
150
|
+
}
|
151
|
+
end
|
152
|
+
sorted_nodes_status = {}
|
153
|
+
nodes_status.keys.sort.each do |key|
|
154
|
+
sorted_nodes_status[key] = nodes_status[key]
|
155
|
+
end
|
156
|
+
sorted_nodes_status
|
157
|
+
end
|
158
|
+
|
159
|
+
def forward(message, destination)
|
160
|
+
receiver = destination["to"]
|
161
|
+
receiver_node_name = receiver.match(/\A[^:]+:\d+\/[^.]+/).to_s
|
162
|
+
raise NotStartedYet.new unless @engine_nodes
|
163
|
+
@engine_nodes.each do |node|
|
164
|
+
if node.name == receiver_node_name
|
165
|
+
node.forward(message, destination)
|
166
|
+
return
|
167
|
+
end
|
168
|
+
end
|
169
|
+
raise UnknownTarget.new(receiver)
|
170
|
+
end
|
171
|
+
|
172
|
+
def bounce(message)
|
173
|
+
role = message["targetRole"].downcase
|
174
|
+
logger.info("bounce: trying to bounce message to another " +
|
175
|
+
"node with the role: #{role}")
|
176
|
+
raise NotStartedYet.new unless @engine_nodes
|
177
|
+
|
178
|
+
acceptable_nodes = engine_nodes.select do |node|
|
179
|
+
node.role == role and
|
180
|
+
node.live?
|
181
|
+
end
|
182
|
+
receiver = acceptable_nodes.sample
|
183
|
+
if receiver
|
184
|
+
receiver.bounce(message)
|
185
|
+
else
|
186
|
+
logger.error("bounce: no available node with the role #{role}",
|
187
|
+
:message => message)
|
188
|
+
# raise NoAcceptableReceiver.new(message)
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
def engine_node_names
|
193
|
+
@engine_node_names ||= engine_nodes.collect(&:name)
|
194
|
+
end
|
195
|
+
|
196
|
+
def readable_nodes
|
197
|
+
@readable_nodes ||= engine_nodes.select do |node|
|
198
|
+
node.readable?
|
199
|
+
end.collect(&:name)
|
200
|
+
end
|
201
|
+
|
202
|
+
def writable_nodes
|
203
|
+
@writable_nodes ||= engine_nodes.select do |node|
|
204
|
+
node.writable?
|
205
|
+
end.collect(&:name)
|
206
|
+
end
|
207
|
+
|
208
|
+
private
|
209
|
+
def clear_cache
|
210
|
+
@engine_nodes.each(&:shutdown) if @engine_nodes
|
211
|
+
@engine_nodes = nil
|
212
|
+
@engine_node_names = nil
|
213
|
+
@readable_nodes = nil
|
214
|
+
@writable_nodes = nil
|
215
|
+
end
|
216
|
+
|
217
|
+
def all_node_names
|
218
|
+
raise NoCatalogLoaded.new unless @catalog
|
219
|
+
@catalog.all_nodes
|
220
|
+
end
|
221
|
+
|
222
|
+
def create_engine_nodes
|
223
|
+
all_node_names.collect do |name|
|
224
|
+
node_state = @state[name] || {}
|
225
|
+
EngineNode.new(@loop,
|
226
|
+
name,
|
227
|
+
node_state,
|
228
|
+
:auto_close_timeout =>
|
229
|
+
@params[:internal_connection_lifetime])
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
def log_tag
|
234
|
+
"cluster_state"
|
235
|
+
end
|
236
|
+
end
|
237
|
+
end
|
data/lib/droonga/collectors.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# Copyright (C) 2014 Droonga Project
|
1
|
+
# Copyright (C) 2014-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
|
@@ -16,3 +16,4 @@
|
|
16
16
|
require "droonga/collectors/and"
|
17
17
|
require "droonga/collectors/or"
|
18
18
|
require "droonga/collectors/sum"
|
19
|
+
require "droonga/collectors/recursive_sum"
|
@@ -0,0 +1,26 @@
|
|
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
|
+
module Droonga
|
17
|
+
module Collectors
|
18
|
+
class RecursiveSum
|
19
|
+
class << self
|
20
|
+
def operator
|
21
|
+
"recursive-sum"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
# Copyright (C) 2014 Droonga Project
|
1
|
+
# Copyright (C) 2014-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
|
@@ -23,12 +23,16 @@ require "coolio"
|
|
23
23
|
require "sigdump/setup"
|
24
24
|
|
25
25
|
require "droonga/engine/version"
|
26
|
+
require "droonga/loggable"
|
27
|
+
require "droonga/deferrable"
|
26
28
|
require "droonga/path"
|
27
|
-
require "droonga/
|
29
|
+
require "droonga/node_name"
|
30
|
+
require "droonga/forwarder"
|
28
31
|
require "droonga/serf"
|
29
|
-
require "droonga/
|
32
|
+
require "droonga/cluster"
|
30
33
|
require "droonga/file_observer"
|
31
34
|
require "droonga/process_supervisor"
|
35
|
+
require "droonga/differ"
|
32
36
|
|
33
37
|
module Droonga
|
34
38
|
module Command
|
@@ -48,16 +52,13 @@ module Droonga
|
|
48
52
|
parse_command_line_arguments!(command_line_arguments)
|
49
53
|
|
50
54
|
setup_path
|
55
|
+
setup_log
|
51
56
|
|
52
57
|
if @configuration.daemon?
|
53
58
|
Process.daemon
|
54
59
|
end
|
55
60
|
|
56
|
-
|
57
|
-
write_pid_file do
|
58
|
-
run_main_loop
|
59
|
-
end
|
60
|
-
end
|
61
|
+
run_main_loop
|
61
62
|
end
|
62
63
|
|
63
64
|
private
|
@@ -80,104 +81,111 @@ module Droonga
|
|
80
81
|
end
|
81
82
|
end
|
82
83
|
|
84
|
+
def setup_log
|
85
|
+
ENV["DROONGA_LOG_LEVEL"] = @configuration.log_level
|
86
|
+
end
|
87
|
+
|
83
88
|
def run_main_loop
|
84
89
|
main_loop = MainLoop.new(@configuration)
|
85
90
|
main_loop.run
|
86
91
|
end
|
87
92
|
|
88
|
-
def open_log_file
|
89
|
-
if @configuration.log_file
|
90
|
-
File.open(@configuration.log_file, "a") do |file|
|
91
|
-
$stdout.reopen(file)
|
92
|
-
$stderr.reopen(file)
|
93
|
-
yield
|
94
|
-
end
|
95
|
-
else
|
96
|
-
yield
|
97
|
-
end
|
98
|
-
end
|
99
|
-
|
100
|
-
def write_pid_file
|
101
|
-
if @configuration.pid_file_path
|
102
|
-
@configuration.pid_file_path.open("w") do |file|
|
103
|
-
file.puts(Process.pid)
|
104
|
-
end
|
105
|
-
begin
|
106
|
-
yield
|
107
|
-
ensure
|
108
|
-
FileUtils.rm_f(@configuration.pid_file_path.to_s)
|
109
|
-
end
|
110
|
-
else
|
111
|
-
yield
|
112
|
-
end
|
113
|
-
end
|
114
|
-
|
115
93
|
class Configuration
|
116
|
-
attr_reader :host, :port, :tag, :log_file, :pid_file_path
|
117
94
|
attr_reader :ready_notify_fd
|
118
95
|
def initialize
|
119
|
-
config =
|
96
|
+
@config = nil
|
97
|
+
|
98
|
+
@host = nil
|
99
|
+
@port = nil
|
100
|
+
@tag = nil
|
120
101
|
|
121
|
-
@
|
122
|
-
@port = config["port"] || Address::DEFAULT_PORT
|
123
|
-
@tag = config["tag"] || Address::DEFAULT_TAG
|
102
|
+
@internal_connection_lifetime = nil
|
124
103
|
|
104
|
+
@log_level = nil
|
125
105
|
@log_file = nil
|
126
|
-
@daemon =
|
106
|
+
@daemon = nil
|
127
107
|
@pid_file_path = nil
|
128
108
|
@ready_notify_fd = nil
|
129
109
|
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
110
|
+
@listen_fd = nil
|
111
|
+
@heartbeat_fd = nil
|
112
|
+
@serf_agent_pid = nil
|
113
|
+
end
|
114
|
+
|
115
|
+
def engine_name
|
116
|
+
"#{host}:#{port}/#{tag}"
|
135
117
|
end
|
136
118
|
|
137
|
-
def
|
138
|
-
|
119
|
+
def address_family
|
120
|
+
ip_address = IPAddr.new(IPSocket.getaddress(host))
|
121
|
+
ip_address.family
|
139
122
|
end
|
140
123
|
|
141
|
-
def
|
142
|
-
|
143
|
-
YAML.load_file(Path.config)
|
144
|
-
else
|
145
|
-
{}
|
146
|
-
end
|
124
|
+
def host
|
125
|
+
@host || config["host"] || default_host
|
147
126
|
end
|
148
127
|
|
149
|
-
def
|
150
|
-
|
128
|
+
def port
|
129
|
+
@port || config["port"] || default_port
|
151
130
|
end
|
152
131
|
|
153
|
-
def
|
154
|
-
|
155
|
-
ip_address.family
|
132
|
+
def tag
|
133
|
+
@tag || config["tag"] || default_tag
|
156
134
|
end
|
157
135
|
|
158
|
-
def
|
159
|
-
|
136
|
+
def internal_connection_lifetime
|
137
|
+
@internal_connection_lifetime ||
|
138
|
+
config["internal_connection_lifetime"] ||
|
139
|
+
default_internal_connection_lifetime
|
160
140
|
end
|
161
141
|
|
162
|
-
def log_level
|
163
|
-
|
142
|
+
def log_level
|
143
|
+
@log_level || config["log_level"] || default_log_level
|
164
144
|
end
|
165
145
|
|
166
|
-
def
|
167
|
-
@log_file
|
146
|
+
def log_file_path
|
147
|
+
@log_file_path || config["log_file"] || default_log_file_path
|
168
148
|
end
|
169
149
|
|
170
|
-
def pid_file_path
|
171
|
-
@pid_file_path
|
150
|
+
def pid_file_path
|
151
|
+
@pid_file_path || config["pid_file"] || default_pid_file_path
|
172
152
|
end
|
173
153
|
|
174
154
|
def daemon?
|
175
|
-
@daemon
|
155
|
+
daemon = @daemon
|
156
|
+
daemon = config["daemon"] if daemon.nil?
|
157
|
+
daemon = false if daemon.nil?
|
158
|
+
daemon
|
176
159
|
end
|
177
160
|
|
178
|
-
def
|
161
|
+
def to_engine_command_line
|
162
|
+
command_line_options = [
|
163
|
+
"--host", host,
|
164
|
+
"--port", port.to_s,
|
165
|
+
"--tag", tag,
|
166
|
+
"--internal-connection-lifetime",
|
167
|
+
internal_connection_lifetime.to_s,
|
168
|
+
"--log-level", log_level,
|
169
|
+
]
|
170
|
+
if log_file_path
|
171
|
+
command_line_options.concat(["--log-file", log_file_path.to_s])
|
172
|
+
end
|
173
|
+
if pid_file_path
|
174
|
+
command_line_options.concat(["--pid-file", pid_file_path.to_s])
|
175
|
+
end
|
176
|
+
if daemon?
|
177
|
+
command_line_options << "--daemon"
|
178
|
+
else
|
179
|
+
command_line_options << "--no-daemon"
|
180
|
+
end
|
181
|
+
command_line_options
|
182
|
+
end
|
183
|
+
|
184
|
+
def to_service_command_line
|
179
185
|
command_line_options = [
|
180
186
|
"--engine-name", engine_name,
|
187
|
+
"--internal-connection-lifetime",
|
188
|
+
internal_connection_lifetime.to_s,
|
181
189
|
]
|
182
190
|
command_line_options
|
183
191
|
end
|
@@ -188,35 +196,104 @@ module Droonga
|
|
188
196
|
add_process_options(parser)
|
189
197
|
add_path_options(parser)
|
190
198
|
add_notification_options(parser)
|
199
|
+
add_internal_options(parser)
|
191
200
|
end
|
192
201
|
|
193
202
|
def listen_socket
|
194
|
-
@listen_socket ||=
|
203
|
+
@listen_socket ||= create_listen_socket
|
195
204
|
end
|
196
205
|
|
197
206
|
def heartbeat_socket
|
198
|
-
@heartbeat_socket ||=
|
207
|
+
@heartbeat_socket ||= create_heartbeat_socket
|
208
|
+
end
|
209
|
+
|
210
|
+
def serf_agent_pid
|
211
|
+
@serf_agent_pid
|
199
212
|
end
|
200
213
|
|
201
214
|
private
|
215
|
+
def default_host
|
216
|
+
NodeName::DEFAULT_HOST
|
217
|
+
end
|
218
|
+
|
219
|
+
def default_port
|
220
|
+
NodeName::DEFAULT_PORT
|
221
|
+
end
|
222
|
+
|
223
|
+
def default_tag
|
224
|
+
NodeName::DEFAULT_TAG
|
225
|
+
end
|
226
|
+
|
227
|
+
def default_internal_connection_lifetime
|
228
|
+
Forwarder::DEFAULT_AUTO_CLOSE_TIMEOUT_SECONDS
|
229
|
+
end
|
230
|
+
|
231
|
+
def default_log_level
|
232
|
+
ENV["DROONGA_LOG_LEVEL"] || Logger::Level.default
|
233
|
+
end
|
234
|
+
|
235
|
+
def default_log_file_path
|
236
|
+
nil
|
237
|
+
end
|
238
|
+
|
239
|
+
def default_pid_file_path
|
240
|
+
nil
|
241
|
+
end
|
242
|
+
|
243
|
+
def normalize_path(path)
|
244
|
+
if path == "-"
|
245
|
+
nil
|
246
|
+
else
|
247
|
+
Pathname.new(path).expand_path
|
248
|
+
end
|
249
|
+
end
|
250
|
+
|
251
|
+
def config
|
252
|
+
@config ||= load_config
|
253
|
+
end
|
254
|
+
|
255
|
+
def load_config
|
256
|
+
config_path = Path.config
|
257
|
+
return {} unless config_path.exist?
|
258
|
+
|
259
|
+
config = YAML.load_file(config_path)
|
260
|
+
path_keys = ["log_file", "pid_file"]
|
261
|
+
path_keys.each do |path_key|
|
262
|
+
path = config[path_key]
|
263
|
+
next if path.nil?
|
264
|
+
|
265
|
+
path = Pathname.new(path)
|
266
|
+
unless path.absolute?
|
267
|
+
path = (config_path.dirname + path).expand_path
|
268
|
+
end
|
269
|
+
config[path_key] = path
|
270
|
+
end
|
271
|
+
config
|
272
|
+
end
|
273
|
+
|
202
274
|
def add_connection_options(parser)
|
203
275
|
parser.separator("")
|
204
276
|
parser.separator("Connection:")
|
205
277
|
parser.on("--host=HOST",
|
206
278
|
"The host name of the Droonga engine",
|
207
|
-
"(#{
|
279
|
+
"(#{default_host})") do |host|
|
208
280
|
@host = host
|
209
281
|
end
|
210
282
|
parser.on("--port=PORT", Integer,
|
211
283
|
"The port number of the Droonga engine",
|
212
|
-
"(#{
|
284
|
+
"(#{default_port})") do |port|
|
213
285
|
@port = port
|
214
286
|
end
|
215
287
|
parser.on("--tag=TAG",
|
216
288
|
"The tag of the Droonga engine",
|
217
|
-
"(#{
|
289
|
+
"(#{default_tag})") do |tag|
|
218
290
|
@tag = tag
|
219
291
|
end
|
292
|
+
parser.on("--internal-connection-lifetime=SECONDS", Float,
|
293
|
+
"The time to expire internal connections, in seconds",
|
294
|
+
"(#{default_internal_connection_lifetime})") do |seconds|
|
295
|
+
@internal_connection_lifetime = seconds
|
296
|
+
end
|
220
297
|
end
|
221
298
|
|
222
299
|
def add_log_options(parser)
|
@@ -227,12 +304,13 @@ module Droonga
|
|
227
304
|
parser.on("--log-level=LEVEL", levels,
|
228
305
|
"The log level of the Droonga engine",
|
229
306
|
"[#{levels_label}]",
|
230
|
-
"(#{
|
231
|
-
|
307
|
+
"(#{default_log_level})") do |level|
|
308
|
+
@log_level = level
|
232
309
|
end
|
233
310
|
parser.on("--log-file=FILE",
|
234
|
-
"Output logs to FILE"
|
235
|
-
|
311
|
+
"Output logs to FILE",
|
312
|
+
"(#{default_log_file_path})") do |path|
|
313
|
+
@log_file_path = normalize_path(path)
|
236
314
|
end
|
237
315
|
end
|
238
316
|
|
@@ -249,7 +327,7 @@ module Droonga
|
|
249
327
|
end
|
250
328
|
parser.on("--pid-file=PATH",
|
251
329
|
"Put PID to PATH") do |path|
|
252
|
-
|
330
|
+
@pid_file_path = normalize_path(path)
|
253
331
|
end
|
254
332
|
end
|
255
333
|
|
@@ -260,6 +338,7 @@ module Droonga
|
|
260
338
|
"Use DIR as the base directory",
|
261
339
|
"(#{Path.base})") do |dir|
|
262
340
|
Path.base = File.expand_path(dir)
|
341
|
+
@config = nil
|
263
342
|
end
|
264
343
|
end
|
265
344
|
|
@@ -272,34 +351,112 @@ module Droonga
|
|
272
351
|
end
|
273
352
|
end
|
274
353
|
|
275
|
-
def
|
276
|
-
|
277
|
-
|
278
|
-
|
354
|
+
def add_internal_options(parser)
|
355
|
+
parser.separator("")
|
356
|
+
parser.separator("Internal:")
|
357
|
+
parser.on("--listen-fd=FD", Integer,
|
358
|
+
"FD of listen socket") do |fd|
|
359
|
+
@listen_fd = fd
|
360
|
+
end
|
361
|
+
parser.on("--heartbeat-fd=FD", Integer,
|
362
|
+
"FD of heartbeat socket") do |fd|
|
363
|
+
@heartbeat_fd = fd
|
364
|
+
end
|
365
|
+
parser.on("--serf-agent-pid=PID", Integer,
|
366
|
+
"PID of Serf agent") do |pid|
|
367
|
+
@serf_agent_pid = pid
|
368
|
+
end
|
369
|
+
end
|
370
|
+
|
371
|
+
def create_listen_socket
|
372
|
+
begin
|
373
|
+
TCPServer.new(host, port)
|
374
|
+
rescue Errno::EADDRINUSE
|
375
|
+
raise if @listen_fd.nil?
|
376
|
+
TCPServer.for_fd(@listen_fd)
|
377
|
+
end
|
378
|
+
end
|
379
|
+
|
380
|
+
def create_heartbeat_socket
|
381
|
+
begin
|
382
|
+
socket = UDPSocket.new(address_family)
|
383
|
+
socket.bind(host, port)
|
384
|
+
socket
|
385
|
+
rescue Errno::EADDRINUSE
|
386
|
+
raise if @heartbeat_fd.nil?
|
387
|
+
UDPSocket.for_fd(@heartbeat_fd)
|
388
|
+
end
|
279
389
|
end
|
280
390
|
end
|
281
391
|
|
282
392
|
class MainLoop
|
393
|
+
include Loggable
|
394
|
+
|
283
395
|
def initialize(configuration)
|
284
396
|
@configuration = configuration
|
397
|
+
ENV["DROONGA_ENGINE_NAME"] = @configuration.engine_name
|
285
398
|
@loop = Coolio::Loop.default
|
399
|
+
@log_file = nil
|
400
|
+
@pid_file_path = nil
|
286
401
|
end
|
287
402
|
|
288
403
|
def run
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
404
|
+
reopen_log_file
|
405
|
+
write_pid_file do
|
406
|
+
run_internal
|
407
|
+
end
|
408
|
+
end
|
409
|
+
|
410
|
+
private
|
411
|
+
def reopen_log_file
|
412
|
+
return if @configuration.log_file_path.nil?
|
413
|
+
@log_file = @configuration.log_file_path.open("a")
|
414
|
+
$stdout.reopen(@log_file)
|
415
|
+
$stderr.reopen(@log_file)
|
416
|
+
end
|
417
|
+
|
418
|
+
def write_pid_file
|
419
|
+
@pid_file_path = @configuration.pid_file_path
|
420
|
+
if @pid_file_path
|
421
|
+
@pid_file_path.open("w") do |file|
|
422
|
+
file.puts(Process.pid)
|
423
|
+
end
|
424
|
+
begin
|
425
|
+
yield
|
426
|
+
ensure
|
427
|
+
FileUtils.rm_f(@pid_file_path.to_s)
|
428
|
+
end
|
429
|
+
else
|
430
|
+
yield
|
431
|
+
end
|
432
|
+
end
|
433
|
+
|
434
|
+
def run_internal
|
435
|
+
logger.trace("run_internal: start")
|
436
|
+
start_serf
|
437
|
+
@serf_agent.on_ready = lambda do
|
438
|
+
logger.trace("run_internal: serf agent is ready")
|
439
|
+
@serf.initialize_tags
|
440
|
+
@serf.update_cluster_state
|
441
|
+
@service_runner = run_service
|
442
|
+
setup_initial_on_ready
|
443
|
+
@catalog_observer = run_catalog_observer
|
444
|
+
@cluster_state_observer = run_cluster_state_observer
|
445
|
+
@command_runner = run_command_runner
|
446
|
+
end
|
294
447
|
|
295
448
|
trap_signals
|
296
449
|
@loop.run
|
297
|
-
@serf.stop if @serf.running?
|
298
450
|
|
299
|
-
@service_runner.
|
451
|
+
while @service_runner.nil? do
|
452
|
+
sleep 1
|
453
|
+
end
|
454
|
+
|
455
|
+
succeeded = @service_runner.success?
|
456
|
+
logger.trace("run_internal: done")
|
457
|
+
succeeded
|
300
458
|
end
|
301
459
|
|
302
|
-
private
|
303
460
|
def setup_initial_on_ready
|
304
461
|
return if @configuration.ready_notify_fd.nil?
|
305
462
|
@service_runner.on_ready = lambda do
|
@@ -334,36 +491,74 @@ module Droonga
|
|
334
491
|
end
|
335
492
|
|
336
493
|
def stop_gracefully
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
494
|
+
logger.trace("stop_gracefully: start")
|
495
|
+
logger.trace("stop_gracefully: stopping serf agent")
|
496
|
+
stop_serf do
|
497
|
+
logger.trace("stop_gracefully: stopping command runner")
|
498
|
+
@command_runner.stop
|
499
|
+
logger.trace("stop_gracefully: stopping cluster_state_observer")
|
500
|
+
@cluster_state_observer.stop
|
501
|
+
logger.trace("stop_gracefully: stopping catalog_observer")
|
502
|
+
@catalog_observer.stop
|
503
|
+
@service_runner.stop_gracefully
|
504
|
+
logger.trace("stop_gracefully: completely done")
|
505
|
+
end
|
506
|
+
logger.trace("stop_gracefully: done")
|
341
507
|
end
|
342
508
|
|
343
509
|
def stop_immediately
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
510
|
+
stop_serf do
|
511
|
+
@command_runner.stop
|
512
|
+
@cluster_state_observer.stop
|
513
|
+
@catalog_observer.stop
|
514
|
+
@service_runner.stop_immediately
|
515
|
+
end
|
348
516
|
end
|
349
517
|
|
350
518
|
def restart_graceful
|
519
|
+
return if @restarting
|
520
|
+
@restarting = true
|
521
|
+
logger.trace("restart_graceful: start")
|
351
522
|
old_service_runner = @service_runner
|
523
|
+
reopen_log_file
|
352
524
|
@service_runner = run_service
|
353
525
|
@service_runner.on_ready = lambda do
|
526
|
+
logger.info("restart_graceful: new service runner is ready")
|
354
527
|
@service_runner.on_failure = nil
|
528
|
+
@service_runner.refresh_self_reference
|
355
529
|
old_service_runner.stop_gracefully
|
530
|
+
@restarting = false
|
531
|
+
logger.trace("restart_graceful: done")
|
356
532
|
end
|
357
533
|
@service_runner.on_failure = lambda do
|
534
|
+
logger.info("restart_graceful: failed to setup new service runner")
|
358
535
|
@service_runner.on_failure = nil
|
359
536
|
@service_runner = old_service_runner
|
537
|
+
@restarting = false
|
538
|
+
logger.trace("restart_graceful: failed")
|
360
539
|
end
|
361
540
|
end
|
362
541
|
|
363
542
|
def restart_immediately
|
543
|
+
return if @restarting
|
544
|
+
@restarting = true
|
364
545
|
old_service_runner = @service_runner
|
546
|
+
reopen_log_file
|
365
547
|
@service_runner = run_service
|
366
548
|
old_service_runner.stop_immediately
|
549
|
+
@restarting = false
|
550
|
+
end
|
551
|
+
|
552
|
+
def restart_self
|
553
|
+
logger.trace("restart_self: start")
|
554
|
+
old_pid_file_path = Pathname.new("#{@pid_file_path}.old")
|
555
|
+
FileUtils.mv(@pid_file_path.to_s, old_pid_file_path.to_s)
|
556
|
+
@pid_file_path = old_pid_file_path
|
557
|
+
stop_gracefully
|
558
|
+
|
559
|
+
engine_runner = EngineRunner.new(@configuration)
|
560
|
+
engine_runner.run
|
561
|
+
logger.trace("restart_self: done")
|
367
562
|
end
|
368
563
|
|
369
564
|
def run_service
|
@@ -372,22 +567,68 @@ module Droonga
|
|
372
567
|
service_runner
|
373
568
|
end
|
374
569
|
|
375
|
-
def
|
376
|
-
serf = Serf.new(@
|
377
|
-
serf.
|
378
|
-
|
570
|
+
def start_serf
|
571
|
+
@serf = Serf.new(@configuration.engine_name)
|
572
|
+
@serf_agent = @serf.run_agent(@loop)
|
573
|
+
end
|
574
|
+
|
575
|
+
def stop_serf(&block)
|
576
|
+
logger.trace("stop_serf: start")
|
577
|
+
begin
|
578
|
+
@serf.leave
|
579
|
+
rescue Droonga::Serf::Command::Failure
|
580
|
+
logger.error("Failed to leave from Serf cluster: #{$!.message}")
|
581
|
+
end
|
582
|
+
@serf_agent.stop do
|
583
|
+
logger.trace("stop_serf: serf agent stopped")
|
584
|
+
yield
|
585
|
+
end
|
586
|
+
logger.trace("stop_serf: done")
|
379
587
|
end
|
380
588
|
|
381
589
|
def run_catalog_observer
|
382
590
|
catalog_observer = FileObserver.new(@loop, Path.catalog)
|
383
591
|
catalog_observer.on_change = lambda do
|
592
|
+
logger.info("restart by updated catalog.json")
|
384
593
|
restart_graceful
|
385
|
-
@serf.update_cluster_id
|
594
|
+
@serf.update_cluster_id
|
386
595
|
end
|
387
596
|
catalog_observer.start
|
388
597
|
catalog_observer
|
389
598
|
end
|
390
599
|
|
600
|
+
RESTART_TRIGGER_KEYS = [
|
601
|
+
"role",
|
602
|
+
"accept_messages_newer_than",
|
603
|
+
]
|
604
|
+
|
605
|
+
def run_cluster_state_observer
|
606
|
+
previous_state = nil
|
607
|
+
cluster_state_observer = FileObserver.new(@loop, Path.cluster_state)
|
608
|
+
cluster_state_observer.on_change = lambda do
|
609
|
+
my_name = @configuration.engine_name
|
610
|
+
new_state = Cluster.load_state_file
|
611
|
+
if new_state and previous_state
|
612
|
+
my_new_state = new_state[my_name].select do |key, _value|
|
613
|
+
RESTART_TRIGGER_KEYS.include?(key)
|
614
|
+
end
|
615
|
+
my_previous_state = previous_state[my_name].select do |key, _value|
|
616
|
+
RESTART_TRIGGER_KEYS.include?(key)
|
617
|
+
end
|
618
|
+
if my_new_state != my_previous_state
|
619
|
+
logger.info("restart by changes of myself in cluster-state.json",
|
620
|
+
:previous => my_previous_state,
|
621
|
+
:new => my_new_state,
|
622
|
+
:diff => Differ.diff(my_previous_state, my_new_state))
|
623
|
+
restart_graceful
|
624
|
+
end
|
625
|
+
end
|
626
|
+
previous_state = new_state
|
627
|
+
end
|
628
|
+
cluster_state_observer.start
|
629
|
+
cluster_state_observer
|
630
|
+
end
|
631
|
+
|
391
632
|
def run_command_runner
|
392
633
|
command_runner = CommandRunner.new(@loop)
|
393
634
|
command_runner.on_command = lambda do |command|
|
@@ -396,23 +637,45 @@ module Droonga
|
|
396
637
|
command_runner.start
|
397
638
|
command_runner
|
398
639
|
end
|
640
|
+
|
641
|
+
def log_tag
|
642
|
+
"droonga-engine"
|
643
|
+
end
|
399
644
|
end
|
400
645
|
|
401
|
-
class
|
402
|
-
def initialize(
|
403
|
-
@raw_loop = raw_loop
|
646
|
+
class EngineRunner
|
647
|
+
def initialize(configuration)
|
404
648
|
@configuration = configuration
|
405
|
-
@success = false
|
406
|
-
@on_ready = nil
|
407
|
-
@on_failure = nil
|
408
649
|
end
|
409
650
|
|
410
|
-
def
|
411
|
-
|
651
|
+
def run
|
652
|
+
listen_fd = @configuration.listen_socket.fileno
|
653
|
+
heartbeat_fd = @configuration.heartbeat_socket.fileno
|
654
|
+
env = {}
|
655
|
+
command_line = [
|
656
|
+
RbConfig.ruby,
|
657
|
+
"-S",
|
658
|
+
"droonga-engine",
|
659
|
+
"--listen-fd", listen_fd.to_s,
|
660
|
+
"--heartbeat-fd", heartbeat_fd.to_s,
|
661
|
+
*@configuration.to_engine_command_line,
|
662
|
+
]
|
663
|
+
options = {
|
664
|
+
listen_fd => listen_fd,
|
665
|
+
heartbeat_fd => heartbeat_fd,
|
666
|
+
}
|
667
|
+
spawn(env, *command_line, options)
|
412
668
|
end
|
669
|
+
end
|
413
670
|
|
414
|
-
|
415
|
-
|
671
|
+
class ServiceRunner
|
672
|
+
include Loggable
|
673
|
+
include Deferrable
|
674
|
+
|
675
|
+
def initialize(raw_loop, configuration)
|
676
|
+
@raw_loop = raw_loop
|
677
|
+
@configuration = configuration
|
678
|
+
@success = false
|
416
679
|
end
|
417
680
|
|
418
681
|
def run
|
@@ -429,7 +692,7 @@ module Droonga
|
|
429
692
|
"--heartbeat-fd", heartbeat_fd.to_s,
|
430
693
|
"--control-read-fd", control_write_in.fileno.to_s,
|
431
694
|
"--control-write-fd", control_read_out.fileno.to_s,
|
432
|
-
*@configuration.
|
695
|
+
*@configuration.to_service_command_line,
|
433
696
|
]
|
434
697
|
options = {
|
435
698
|
listen_fd => listen_fd,
|
@@ -446,17 +709,25 @@ module Droonga
|
|
446
709
|
end
|
447
710
|
|
448
711
|
def stop_gracefully
|
712
|
+
logger.trace("stop_gracefully: start")
|
449
713
|
@supervisor.stop_gracefully
|
714
|
+
logger.trace("stop_gracefully: done")
|
450
715
|
end
|
451
716
|
|
452
717
|
def stop_immediately
|
718
|
+
logger.trace("stop_immediately: start")
|
453
719
|
@supervisor.stop_immediately
|
720
|
+
logger.trace("stop_immediately: done")
|
454
721
|
end
|
455
722
|
|
456
723
|
def success?
|
457
724
|
@success
|
458
725
|
end
|
459
726
|
|
727
|
+
def refresh_self_reference
|
728
|
+
@supervisor.refresh_self_reference
|
729
|
+
end
|
730
|
+
|
460
731
|
private
|
461
732
|
def create_process_supervisor(input, output)
|
462
733
|
supervisor = ProcessSupervisor.new(@raw_loop, input, output)
|
@@ -469,23 +740,21 @@ module Droonga
|
|
469
740
|
supervisor
|
470
741
|
end
|
471
742
|
|
472
|
-
def on_ready
|
473
|
-
@on_ready.call if @on_ready
|
474
|
-
end
|
475
|
-
|
476
|
-
def on_failure
|
477
|
-
@on_failure.call if @on_failure
|
478
|
-
end
|
479
|
-
|
480
743
|
def on_finish
|
481
744
|
_, status = Process.waitpid2(@pid)
|
482
745
|
@success = status.success?
|
483
746
|
@supervisor.stop
|
484
747
|
on_failure unless success?
|
485
748
|
end
|
749
|
+
|
750
|
+
def log_tag
|
751
|
+
"service_runner"
|
752
|
+
end
|
486
753
|
end
|
487
754
|
|
488
755
|
class CommandRunner
|
756
|
+
include Loggable
|
757
|
+
|
489
758
|
attr_writer :on_command
|
490
759
|
def initialize(loop)
|
491
760
|
@loop = loop
|
@@ -494,8 +763,9 @@ module Droonga
|
|
494
763
|
end
|
495
764
|
|
496
765
|
def start
|
766
|
+
logger.trace("start: stert")
|
497
767
|
@async_watcher = Coolio::AsyncWatcher.new
|
498
|
-
on_signal
|
768
|
+
@async_watcher.on_signal do
|
499
769
|
commands = @commands.uniq
|
500
770
|
@commands.clear
|
501
771
|
until commands.empty?
|
@@ -503,16 +773,19 @@ module Droonga
|
|
503
773
|
@on_command.call(command) if @on_command
|
504
774
|
end
|
505
775
|
end
|
506
|
-
@async_watcher.on_signal do
|
507
|
-
on_signal.call
|
508
|
-
end
|
509
776
|
@loop.attach(@async_watcher)
|
777
|
+
logger.trace("start: async watcher attached",
|
778
|
+
:watcher => @async_watcher)
|
779
|
+
logger.trace("start: done")
|
510
780
|
end
|
511
781
|
|
512
782
|
def stop
|
513
783
|
return if @async_watcher.nil?
|
784
|
+
logger.trace("stop: stert")
|
514
785
|
@async_watcher.detach
|
786
|
+
# logger.trace("stop: watcher detached", :watcher => @async_watcher)
|
515
787
|
@async_watcher = nil
|
788
|
+
logger.trace("stop: done")
|
516
789
|
end
|
517
790
|
|
518
791
|
def push_command(command)
|
@@ -521,6 +794,10 @@ module Droonga
|
|
521
794
|
@commands << command
|
522
795
|
@async_watcher.signal if first_command_p
|
523
796
|
end
|
797
|
+
|
798
|
+
def log_tag
|
799
|
+
"command_runner"
|
800
|
+
end
|
524
801
|
end
|
525
802
|
end
|
526
803
|
end
|