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