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