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
|
@@ -55,33 +55,31 @@ module Droonga
|
|
|
55
55
|
end
|
|
56
56
|
end
|
|
57
57
|
|
|
58
|
-
def scatter(
|
|
58
|
+
def scatter(options={})
|
|
59
59
|
@processor = {
|
|
60
60
|
"command" => @source_message["type"],
|
|
61
61
|
"dataset" => @dataset.name,
|
|
62
62
|
"body" => options[:body] || @source_message["body"],
|
|
63
|
-
"record" => record,
|
|
63
|
+
"record" => options[:record],
|
|
64
64
|
"type" => "scatter",
|
|
65
65
|
"outputs" => [],
|
|
66
|
-
"replica" => "all",
|
|
67
|
-
"
|
|
66
|
+
"replica" => options[:replica] || "all",
|
|
67
|
+
"slice" => options[:slice] || "all",
|
|
68
|
+
"post" => options[:write] || false,
|
|
68
69
|
}
|
|
69
70
|
end
|
|
70
71
|
|
|
71
72
|
def broadcast(options={})
|
|
72
|
-
processor = {
|
|
73
|
+
@processor = {
|
|
73
74
|
"command" => @source_message["type"],
|
|
74
75
|
"dataset" => @dataset.name,
|
|
75
76
|
"body" => options[:body] || @source_message["body"],
|
|
76
77
|
"type" => "broadcast",
|
|
77
78
|
"outputs" => [],
|
|
78
|
-
"replica" => "random"
|
|
79
|
+
"replica" => options[:replica] || "random",
|
|
80
|
+
"slice" => options[:slice] || "all",
|
|
81
|
+
"post" => options[:write] || false,
|
|
79
82
|
}
|
|
80
|
-
if options[:write]
|
|
81
|
-
processor["replica"] = "all"
|
|
82
|
-
processor["post"] = true
|
|
83
|
-
end
|
|
84
|
-
@processor = processor
|
|
85
83
|
end
|
|
86
84
|
|
|
87
85
|
private
|
data/lib/droonga/engine.rb
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# -*- coding: utf-8 -*-
|
|
2
2
|
#
|
|
3
|
-
# Copyright (C) 2013-
|
|
3
|
+
# Copyright (C) 2013-2015 Droonga Project
|
|
4
4
|
#
|
|
5
5
|
# This library is free software; you can redistribute it and/or
|
|
6
6
|
# modify it under the terms of the GNU Lesser General Public
|
|
@@ -15,100 +15,123 @@
|
|
|
15
15
|
# License along with this library; if not, write to the Free Software
|
|
16
16
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
17
17
|
|
|
18
|
-
require "time"
|
|
19
18
|
require "fileutils"
|
|
19
|
+
require "time"
|
|
20
|
+
|
|
20
21
|
require "droonga/engine/version"
|
|
21
22
|
require "droonga/loggable"
|
|
23
|
+
require "droonga/deferrable"
|
|
22
24
|
require "droonga/engine_state"
|
|
23
|
-
require "droonga/
|
|
25
|
+
require "droonga/cluster"
|
|
26
|
+
require "droonga/catalog/loader"
|
|
24
27
|
require "droonga/dispatcher"
|
|
25
|
-
require "droonga/
|
|
26
|
-
require "droonga/
|
|
27
|
-
require "droonga/
|
|
28
|
+
require "droonga/serf"
|
|
29
|
+
require "droonga/serf/tag"
|
|
30
|
+
require "droonga/timestamp"
|
|
28
31
|
|
|
29
32
|
module Droonga
|
|
30
33
|
class Engine
|
|
31
34
|
include Loggable
|
|
35
|
+
include Deferrable
|
|
32
36
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
37
|
+
attr_reader :cluster
|
|
38
|
+
|
|
39
|
+
def initialize(loop, name, internal_name, options={})
|
|
40
|
+
@name = name
|
|
41
|
+
@internal_name = internal_name
|
|
42
|
+
@loop = loop
|
|
36
43
|
@catalog = load_catalog
|
|
37
|
-
@state
|
|
44
|
+
@state = EngineState.new(loop, name,
|
|
45
|
+
internal_name,
|
|
46
|
+
:catalog => @catalog,
|
|
47
|
+
:internal_connection_lifetime =>
|
|
48
|
+
options[:internal_connection_lifetime])
|
|
49
|
+
@cluster = Cluster.new(loop,
|
|
50
|
+
:catalog => @catalog,
|
|
51
|
+
:internal_connection_lifetime =>
|
|
52
|
+
options[:internal_connection_lifetime])
|
|
53
|
+
|
|
38
54
|
@dispatcher = create_dispatcher
|
|
39
|
-
@
|
|
40
|
-
|
|
41
|
-
@state.live_nodes = load_live_nodes
|
|
42
|
-
end
|
|
43
|
-
@node_status_observer = FileObserver.new(loop, Path.node_status)
|
|
44
|
-
@node_status_observer.on_change = lambda do
|
|
45
|
-
logger.trace("reloading node_status: start")
|
|
46
|
-
node_status.reload
|
|
47
|
-
logger.trace("reloading node_status: done")
|
|
55
|
+
@cluster.on_change = lambda do
|
|
56
|
+
@dispatcher.refresh_node_reference
|
|
48
57
|
end
|
|
49
|
-
@on_ready = nil
|
|
50
58
|
end
|
|
51
59
|
|
|
52
60
|
def start
|
|
53
61
|
logger.trace("start: start")
|
|
54
62
|
@state.on_ready = lambda do
|
|
55
|
-
|
|
63
|
+
on_ready
|
|
64
|
+
serf = Serf.new(@name.to_s)
|
|
65
|
+
serf.set_tag(Serf::Tag.internal_node_name, @internal_name)
|
|
66
|
+
end
|
|
67
|
+
@state.on_failure = lambda do
|
|
68
|
+
on_failure
|
|
56
69
|
end
|
|
57
70
|
@state.start
|
|
58
|
-
@
|
|
59
|
-
@node_status_observer.start
|
|
71
|
+
@cluster.start
|
|
60
72
|
@dispatcher.start
|
|
73
|
+
@last_message_timestamp_observer = run_last_message_timestamp_observer
|
|
61
74
|
logger.trace("start: done")
|
|
62
75
|
end
|
|
63
76
|
|
|
64
77
|
def stop_gracefully
|
|
65
78
|
logger.trace("stop_gracefully: start")
|
|
66
|
-
@
|
|
67
|
-
|
|
79
|
+
@last_message_timestamp_observer.stop
|
|
80
|
+
Timestamp.last_message_timestamp = nil # to avoid old timestamp is used
|
|
81
|
+
@cluster.shutdown
|
|
68
82
|
on_finish = lambda do
|
|
69
|
-
logger.trace("stop_gracefully
|
|
70
|
-
save_last_processed_message_timestamp
|
|
83
|
+
logger.trace("stop_gracefully: middle")
|
|
71
84
|
@dispatcher.stop_gracefully do
|
|
85
|
+
#XXX We must save last processed message timstamp
|
|
86
|
+
# based on forwarded/dispatched messages while
|
|
87
|
+
# "graceful stop" operations.
|
|
88
|
+
save_last_message_timestamp
|
|
72
89
|
@state.shutdown
|
|
73
90
|
yield
|
|
91
|
+
logger.trace("stop_gracefully: done")
|
|
74
92
|
end
|
|
75
|
-
logger.trace("stop_gracefully/on_finish: done")
|
|
76
93
|
end
|
|
77
94
|
if @state.have_session?
|
|
78
|
-
logger.trace("stop_gracefully
|
|
95
|
+
logger.trace("stop_gracefully: having sessions")
|
|
79
96
|
@state.on_finish = on_finish
|
|
80
97
|
else
|
|
81
|
-
logger.trace("stop_gracefully
|
|
98
|
+
logger.trace("stop_gracefully: no session")
|
|
82
99
|
on_finish.call
|
|
83
100
|
end
|
|
84
|
-
logger.trace("stop_gracefully: done")
|
|
85
101
|
end
|
|
86
102
|
|
|
87
103
|
# It may be called after stop_gracefully.
|
|
88
104
|
def stop_immediately
|
|
89
105
|
logger.trace("stop_immediately: start")
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
@node_status_observer.stop
|
|
106
|
+
@last_message_timestamp_observer.stop
|
|
107
|
+
Timestamp.last_message_timestamp = nil # to avoid old timestamp is used
|
|
93
108
|
@dispatcher.stop_immediately
|
|
109
|
+
save_last_message_timestamp
|
|
110
|
+
@cluster.shutdown
|
|
94
111
|
@state.shutdown
|
|
95
112
|
logger.trace("stop_immediately: done")
|
|
96
113
|
end
|
|
97
114
|
|
|
98
|
-
def
|
|
99
|
-
|
|
100
|
-
@
|
|
101
|
-
@dispatcher.process_message(message)
|
|
115
|
+
def refresh_self_reference
|
|
116
|
+
@cluster.refresh_connection_for(@name)
|
|
117
|
+
@state.forwarder.refresh_connection_for(@name)
|
|
102
118
|
end
|
|
103
119
|
|
|
104
|
-
def
|
|
105
|
-
|
|
120
|
+
def process(message)
|
|
121
|
+
if message.include?("date")
|
|
122
|
+
date = Time.parse(message["date"])
|
|
123
|
+
if @last_message_timestamp.nil? or
|
|
124
|
+
@last_message_timestamp < date
|
|
125
|
+
@last_message_timestamp = date
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
@dispatcher.process_message(message)
|
|
106
129
|
end
|
|
107
130
|
|
|
108
131
|
private
|
|
109
132
|
def load_catalog
|
|
110
133
|
catalog_path = Path.catalog
|
|
111
|
-
loader =
|
|
134
|
+
loader = Catalog::Loader.new(catalog_path.to_s)
|
|
112
135
|
catalog = loader.load
|
|
113
136
|
logger.info("catalog loaded",
|
|
114
137
|
:path => catalog_path.to_s,
|
|
@@ -116,48 +139,30 @@ module Droonga
|
|
|
116
139
|
catalog
|
|
117
140
|
end
|
|
118
141
|
|
|
119
|
-
def load_live_nodes
|
|
120
|
-
path = Path.live_nodes
|
|
121
|
-
loader = LiveNodesListLoader.new(path)
|
|
122
|
-
live_nodes = loader.load
|
|
123
|
-
logger.info("live-nodes loaded",
|
|
124
|
-
:path => path.to_s,
|
|
125
|
-
:mtime => path.mtime)
|
|
126
|
-
live_nodes
|
|
127
|
-
end
|
|
128
|
-
|
|
129
142
|
def create_dispatcher
|
|
130
|
-
Dispatcher.new(@state, @catalog)
|
|
131
|
-
end
|
|
132
|
-
|
|
133
|
-
def save_last_processed_message_timestamp
|
|
134
|
-
logger.trace("output_last_processed_message_timestamp: start")
|
|
135
|
-
node_status.set(:last_processed_message_timestamp, @last_processed_message_timestamp.to_s)
|
|
136
|
-
logger.trace("output_last_processed_message_timestamp: done")
|
|
143
|
+
Dispatcher.new(@state, @cluster, @catalog)
|
|
137
144
|
end
|
|
138
145
|
|
|
139
|
-
def
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
logger.trace("checking effective_message_timestamp (#{effective_timestamp}) vs message_timestamp(message_timestamp)")
|
|
145
|
-
return false if effective_timestamp >= message_timestamp
|
|
146
|
-
|
|
147
|
-
logger.trace("deleting obsolete effective_message_timestamp: start")
|
|
148
|
-
node_status.delete(:effective_message_timestamp)
|
|
149
|
-
logger.trace("deleting obsolete effective_message_timestamp: done")
|
|
150
|
-
true
|
|
146
|
+
def save_last_message_timestamp
|
|
147
|
+
logger.trace("save_last_message_timestamp: start",
|
|
148
|
+
:current => @last_message_timestamp)
|
|
149
|
+
Timestamp.last_message_timestamp = @last_message_timestamp
|
|
150
|
+
logger.trace("save_last_message_timestamp: done")
|
|
151
151
|
end
|
|
152
152
|
|
|
153
|
-
def
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
153
|
+
def run_last_message_timestamp_observer
|
|
154
|
+
Timestamp.run_last_message_timestamp_observer(@loop) do |timestamp|
|
|
155
|
+
logger.trace("last message timestamp file is modified",
|
|
156
|
+
:loaded => timestamp,
|
|
157
|
+
:current => @last_message_timestamp)
|
|
158
|
+
if timestamp
|
|
159
|
+
if @last_message_timestamp.nil? or
|
|
160
|
+
timestamp > @last_message_timestamp
|
|
161
|
+
@last_message_timestamp = timestamp
|
|
162
|
+
elsif timestamp < @last_message_timestamp
|
|
163
|
+
Timestamp.last_message_timestamp = @last_message_timestamp
|
|
164
|
+
end
|
|
165
|
+
end
|
|
161
166
|
end
|
|
162
167
|
end
|
|
163
168
|
|
|
@@ -0,0 +1,301 @@
|
|
|
1
|
+
# Copyright (C) 2015 Droonga Project
|
|
2
|
+
#
|
|
3
|
+
# This library is free software; you can redistribute it and/or
|
|
4
|
+
# modify it under the terms of the GNU Lesser General Public
|
|
5
|
+
# License version 2.1 as published by the Free Software Foundation.
|
|
6
|
+
#
|
|
7
|
+
# This library is distributed in the hope that it will be useful,
|
|
8
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
9
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
10
|
+
# Lesser General Public License for more details.
|
|
11
|
+
#
|
|
12
|
+
# You should have received a copy of the GNU Lesser General Public
|
|
13
|
+
# License along with this library; if not, write to the Free Software
|
|
14
|
+
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
15
|
+
|
|
16
|
+
require "time"
|
|
17
|
+
require "coolio"
|
|
18
|
+
|
|
19
|
+
require "droonga/loggable"
|
|
20
|
+
require "droonga/forward_buffer"
|
|
21
|
+
require "droonga/fluent_message_sender"
|
|
22
|
+
require "droonga/node_name"
|
|
23
|
+
require "droonga/node_role"
|
|
24
|
+
|
|
25
|
+
module Droonga
|
|
26
|
+
class EngineNode
|
|
27
|
+
include Loggable
|
|
28
|
+
|
|
29
|
+
DEFAULT_AUTO_CLOSE_TIMEOUT_SECONDS = 60
|
|
30
|
+
|
|
31
|
+
attr_reader :name
|
|
32
|
+
|
|
33
|
+
def initialize(loop, name, state, options={})
|
|
34
|
+
@loop = loop
|
|
35
|
+
@name = name
|
|
36
|
+
@state = state
|
|
37
|
+
logger.trace("initialize: start")
|
|
38
|
+
|
|
39
|
+
@buffer = ForwardBuffer.new(name)
|
|
40
|
+
boundary_timestamp = accept_messages_newer_than_timestamp
|
|
41
|
+
@buffer.process_messages_newer_than(boundary_timestamp)
|
|
42
|
+
@buffer.on_forward = lambda do |message, destination|
|
|
43
|
+
output(message, destination)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
@node_name = NodeName.parse(@name)
|
|
47
|
+
|
|
48
|
+
@sender = nil
|
|
49
|
+
@auto_close_timer = nil
|
|
50
|
+
@auto_close_timeout = options[:auto_close_timeout] ||
|
|
51
|
+
DEFAULT_AUTO_CLOSE_TIMEOUT_SECONDS
|
|
52
|
+
|
|
53
|
+
logger.trace("initialize: done")
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def start
|
|
57
|
+
logger.trace("start: start")
|
|
58
|
+
resume
|
|
59
|
+
logger.trace("start: done")
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def shutdown
|
|
63
|
+
logger.trace("shutdown: start")
|
|
64
|
+
if @sender
|
|
65
|
+
@sender.shutdown
|
|
66
|
+
@sender = nil
|
|
67
|
+
end
|
|
68
|
+
if @auto_close_timer
|
|
69
|
+
@auto_close_timer.detach
|
|
70
|
+
@auto_close_timer = nil
|
|
71
|
+
end
|
|
72
|
+
logger.trace("shutdown: done")
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def refresh_connection
|
|
76
|
+
logger.trace("refresh_connection: start")
|
|
77
|
+
shutdown
|
|
78
|
+
sender # instantiate new sender
|
|
79
|
+
logger.trace("refresh_connection: done")
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def forward(message, destination)
|
|
83
|
+
if read_message?(message)
|
|
84
|
+
# A node can receive read messages for other nodes,
|
|
85
|
+
# while changing its role. They must not be buffered.
|
|
86
|
+
output(message, destination)
|
|
87
|
+
return
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
unless really_writable?
|
|
91
|
+
# The target node is not ready. We should send the message later.
|
|
92
|
+
@buffer.add(message, destination)
|
|
93
|
+
return
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
# The target node is ready.
|
|
97
|
+
if @buffer.empty?
|
|
98
|
+
output(message, destination)
|
|
99
|
+
else
|
|
100
|
+
@buffer.add(message, destination)
|
|
101
|
+
@buffer.start_forward
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def bounce(message)
|
|
106
|
+
destination = {
|
|
107
|
+
"to" => name,
|
|
108
|
+
"type" => message["type"],
|
|
109
|
+
}
|
|
110
|
+
output(message, destination)
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def role
|
|
114
|
+
if @state
|
|
115
|
+
@state["role"]
|
|
116
|
+
else
|
|
117
|
+
NodeRole::SERVICE_PROVIDER
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def live?
|
|
122
|
+
@state.nil? or @state["live"]
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def forwardable?
|
|
126
|
+
return false unless live?
|
|
127
|
+
role == NodeRole.mine
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
def readable?
|
|
131
|
+
forwardable? and @buffer.empty? and
|
|
132
|
+
(complete_service_provider? or not service_provider?)
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
def writable?
|
|
136
|
+
case NodeRole.mine
|
|
137
|
+
when NodeRole::SERVICE_PROVIDER
|
|
138
|
+
true
|
|
139
|
+
when NodeRole::ABSORB_SOURCE
|
|
140
|
+
absorb_source?
|
|
141
|
+
when NodeRole::ABSORB_DESTINATION
|
|
142
|
+
absorb_destination?
|
|
143
|
+
else
|
|
144
|
+
false
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
def status
|
|
149
|
+
if readable?
|
|
150
|
+
"active"
|
|
151
|
+
elsif forwardable?
|
|
152
|
+
"inactive"
|
|
153
|
+
elsif dead?
|
|
154
|
+
"dead"
|
|
155
|
+
else
|
|
156
|
+
"inactive"
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
def to_json
|
|
161
|
+
{
|
|
162
|
+
"name" => name,
|
|
163
|
+
"role" => role,
|
|
164
|
+
"live" => live?,
|
|
165
|
+
"status" => status,
|
|
166
|
+
}
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
def resume
|
|
170
|
+
logger.trace("resume: start")
|
|
171
|
+
sender.resume
|
|
172
|
+
unless @buffer.empty?
|
|
173
|
+
if really_writable?
|
|
174
|
+
logger.info("Target becomes writable. Start to forwarding.")
|
|
175
|
+
@buffer.start_forward
|
|
176
|
+
else
|
|
177
|
+
logger.info("Target is still unwritable.")
|
|
178
|
+
end
|
|
179
|
+
end
|
|
180
|
+
logger.trace("resume: done")
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
private
|
|
184
|
+
def parse_node_name(name)
|
|
185
|
+
unless name =~ /\A(.*):(\d+)\/([^.]+)\z/
|
|
186
|
+
raise "name format: hostname:port/tag"
|
|
187
|
+
end
|
|
188
|
+
{
|
|
189
|
+
:host => $1,
|
|
190
|
+
:port => $2,
|
|
191
|
+
:tag => $3,
|
|
192
|
+
}
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
def have_unprocessed_messages?
|
|
196
|
+
@state and @state["have_unprocessed_messages"]
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
def accept_messages_newer_than_timestamp
|
|
200
|
+
@accept_messages_newer_than_timestamp ||= parse_accept_messages_newer_than_timestamp
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
def parse_accept_messages_newer_than_timestamp
|
|
204
|
+
return nil if @state.nil? or @state["accept_messages_newer_than"].nil?
|
|
205
|
+
Time.parse(@state["accept_messages_newer_than"])
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
def dead?
|
|
209
|
+
not live?
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
def service_provider?
|
|
213
|
+
role == NodeRole::SERVICE_PROVIDER
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
def absorb_source?
|
|
217
|
+
role == NodeRole::ABSORB_SOURCE
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
def absorb_destination?
|
|
221
|
+
role == NodeRole::ABSORB_DESTINATION
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
def complete_service_provider?
|
|
225
|
+
service_provider? and not have_unprocessed_messages?
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
def really_writable?
|
|
229
|
+
return false unless writable?
|
|
230
|
+
case NodeRole.mine
|
|
231
|
+
when NodeRole::SERVICE_PROVIDER
|
|
232
|
+
service_provider?
|
|
233
|
+
when NodeRole::ABSORB_SOURCE
|
|
234
|
+
not absorb_destination?
|
|
235
|
+
else
|
|
236
|
+
true
|
|
237
|
+
end
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
def read_message?(message)
|
|
241
|
+
steps = message["body"]["steps"]
|
|
242
|
+
return false unless steps
|
|
243
|
+
steps.all? do |step|
|
|
244
|
+
not step["write"]
|
|
245
|
+
end
|
|
246
|
+
end
|
|
247
|
+
|
|
248
|
+
def output(message, destination)
|
|
249
|
+
command = destination["type"]
|
|
250
|
+
receiver = destination["to"]
|
|
251
|
+
arguments = destination["arguments"]
|
|
252
|
+
parsed_receiver = parse_node_name(receiver)
|
|
253
|
+
|
|
254
|
+
override_message = {
|
|
255
|
+
"type" => command,
|
|
256
|
+
}
|
|
257
|
+
override_message["arguments"] = arguments if arguments
|
|
258
|
+
message = message.merge(override_message)
|
|
259
|
+
output_tag = "#{parsed_receiver[:tag]}.message"
|
|
260
|
+
log_info = "<#{receiver}>:<#{output_tag}>"
|
|
261
|
+
logger.trace("forward: start: #{log_info}")
|
|
262
|
+
sender.send(output_tag, message)
|
|
263
|
+
set_auto_close_timer
|
|
264
|
+
logger.trace("forward: end")
|
|
265
|
+
end
|
|
266
|
+
|
|
267
|
+
def sender
|
|
268
|
+
@sender ||= create_sender
|
|
269
|
+
end
|
|
270
|
+
|
|
271
|
+
def create_sender
|
|
272
|
+
sender = FluentMessageSender.new(@loop,
|
|
273
|
+
@node_name.host,
|
|
274
|
+
@node_name.port,
|
|
275
|
+
:buffering => true)
|
|
276
|
+
sender.start
|
|
277
|
+
sender
|
|
278
|
+
end
|
|
279
|
+
|
|
280
|
+
def set_auto_close_timer
|
|
281
|
+
previous_timer = @auto_close_timer
|
|
282
|
+
previous_timer.detach if previous_timer
|
|
283
|
+
|
|
284
|
+
@auto_close_timer = Coolio::TimerWatcher.new(@auto_close_timeout)
|
|
285
|
+
@auto_close_timer.on_timer do
|
|
286
|
+
@auto_close_timer.detach
|
|
287
|
+
@auto_close_timer = nil
|
|
288
|
+
if @sender
|
|
289
|
+
logger.info("sender for #{name} is automatically closed by timeout.")
|
|
290
|
+
@sender.shutdown
|
|
291
|
+
@sender = nil
|
|
292
|
+
end
|
|
293
|
+
end
|
|
294
|
+
@loop.attach(@auto_close_timer)
|
|
295
|
+
end
|
|
296
|
+
|
|
297
|
+
def log_tag
|
|
298
|
+
"[#{Process.ppid}] engine-node: #{@name}"
|
|
299
|
+
end
|
|
300
|
+
end
|
|
301
|
+
end
|