droonga-engine 1.0.2 → 1.0.3
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 +7 -0
- data/.gitignore +1 -0
- data/.travis.yml +3 -0
- data/Gemfile +7 -0
- data/Rakefile +6 -2
- data/bin/droonga-engine +2 -2
- data/bin/{droonga-catalog-generate → droonga-engine-catalog-generate} +15 -3
- data/bin/droonga-engine-serf-event-handler +20 -0
- data/bin/droonga-engine-service +2 -2
- data/doc/text/news.md +21 -1
- data/droonga-engine.gemspec +5 -2
- data/lib/droonga/catalog/collection_volume.rb +12 -0
- data/lib/droonga/catalog/dataset.rb +25 -0
- data/lib/droonga/catalog/single_volume.rb +10 -0
- data/lib/droonga/catalog/slice.rb +4 -0
- data/lib/droonga/catalog/version1.rb +59 -48
- data/lib/droonga/catalog/version2.rb +10 -20
- data/lib/droonga/catalog/volume_collection.rb +27 -4
- data/lib/droonga/catalog_generator.rb +12 -5
- data/lib/droonga/catalog_observer.rb +17 -35
- data/lib/droonga/command/droonga_engine.rb +436 -0
- data/lib/droonga/command/droonga_engine_service.rb +273 -0
- data/lib/droonga/command/serf_event_handler.rb +85 -0
- data/lib/droonga/dispatcher.rb +8 -8
- data/lib/droonga/engine.rb +90 -26
- data/lib/droonga/engine/version.rb +1 -1
- data/lib/droonga/engine_state.rb +29 -3
- data/lib/droonga/internal_fluent_message_receiver.rb +100 -0
- data/lib/droonga/live_nodes_list_loader.rb +48 -0
- data/lib/droonga/live_nodes_list_observer.rb +72 -0
- data/lib/droonga/path.rb +47 -0
- data/lib/droonga/plugins/dump.rb +279 -38
- data/lib/droonga/plugins/groonga/select.rb +26 -14
- data/lib/droonga/plugins/search.rb +30 -2
- data/lib/droonga/plugins/search/distributed_search_planner.rb +28 -11
- data/lib/droonga/processor.rb +4 -0
- data/lib/droonga/searcher.rb +26 -0
- data/lib/droonga/serf.rb +119 -0
- data/lib/droonga/serf_downloader.rb +90 -0
- data/lib/droonga/server.rb +2 -2
- data/lib/droonga/service_control_protocol.rb +26 -0
- data/sample/cluster/catalog.json +1 -1
- data/test/command/config/default/catalog.json +2 -2
- data/test/command/config/version1/catalog.json +1 -1
- data/test/command/fixture/documents.jsons +18 -18
- data/test/command/fixture/event.jsons +4 -4
- data/test/command/fixture/user-table-array.jsons +4 -4
- data/test/command/fixture/user-table.jsons +5 -5
- data/test/command/suite/add/dimension/column.catalog.json +1 -1
- data/test/command/suite/add/dimension/column.test +4 -4
- data/test/command/suite/add/dimension/integer.catalog.json +1 -1
- data/test/command/suite/add/dimension/integer.test +4 -4
- data/test/command/suite/add/error/invalid-integer.test +1 -1
- data/test/command/suite/add/error/invalid-time.test +1 -1
- data/test/command/suite/add/error/missing-key.test +1 -1
- data/test/command/suite/add/error/missing-table.test +1 -1
- data/test/command/suite/add/error/unknown-column.test +1 -1
- data/test/command/suite/add/error/unknown-table.test +1 -1
- data/test/command/suite/add/minimum.test +1 -1
- data/test/command/suite/add/vector/short_text.catalog.json +26 -0
- data/test/command/suite/add/vector/short_text.expected +42 -0
- data/test/command/suite/add/vector/short_text.test +35 -0
- data/test/command/suite/add/with-values.test +1 -1
- data/test/command/suite/add/without-key.test +1 -1
- data/test/command/suite/dump/column/index.catalog.json +40 -0
- data/test/command/suite/dump/column/index.expected +195 -0
- data/test/command/suite/dump/column/index.test +5 -0
- data/test/command/suite/dump/column/scalar.catalog.json +19 -0
- data/test/command/suite/dump/column/scalar.expected +99 -0
- data/test/command/suite/dump/column/scalar.test +5 -0
- data/test/command/suite/dump/column/vector.catalog.json +22 -0
- data/test/command/suite/dump/column/vector.expected +108 -0
- data/test/command/suite/dump/column/vector.test +5 -0
- data/test/command/suite/dump/record/vector/reference.catalog.json +27 -0
- data/test/command/suite/dump/record/vector/reference.expected +213 -0
- data/test/command/suite/dump/record/vector/reference.test +21 -0
- data/test/command/suite/dump/table/array.catalog.json +13 -0
- data/test/command/suite/dump/table/array.expected +63 -0
- data/test/command/suite/dump/table/array.test +5 -0
- data/test/command/suite/dump/table/double_array_trie.catalog.json +14 -0
- data/test/command/suite/dump/table/double_array_trie.expected +66 -0
- data/test/command/suite/dump/table/double_array_trie.test +5 -0
- data/test/command/suite/dump/table/hash.catalog.json +14 -0
- data/test/command/suite/dump/table/hash.expected +66 -0
- data/test/command/suite/dump/table/hash.test +5 -0
- data/test/command/suite/dump/table/patricia_trie.catalog.json +14 -0
- data/test/command/suite/dump/table/patricia_trie.expected +66 -0
- data/test/command/suite/dump/table/patricia_trie.test +5 -0
- data/test/command/suite/groonga/column_create/scalar.test +2 -2
- data/test/command/suite/groonga/column_create/unknown-table.test +1 -1
- data/test/command/suite/groonga/column_create/vector.test +2 -2
- data/test/command/suite/groonga/column_list/success.test +3 -3
- data/test/command/suite/groonga/column_list/unknown-table.test +1 -1
- data/test/command/suite/groonga/column_remove/success.test +3 -3
- data/test/command/suite/groonga/column_remove/unknown-column.test +2 -2
- data/test/command/suite/groonga/column_remove/unknown-table.test +1 -1
- data/test/command/suite/groonga/column_rename/success.test +3 -3
- data/test/command/suite/groonga/column_rename/unknown-column.test +2 -2
- data/test/command/suite/groonga/column_rename/unknown-table.test +1 -1
- data/test/command/suite/groonga/delete/duplicated-identifiers.test +2 -2
- data/test/command/suite/groonga/delete/filter.test +2 -2
- data/test/command/suite/groonga/delete/invalid-filter.test +1 -1
- data/test/command/suite/groonga/delete/no-identifier.test +2 -2
- data/test/command/suite/groonga/delete/success.test +2 -2
- data/test/command/suite/groonga/delete/unknown-table.test +1 -1
- data/test/command/suite/groonga/select/minimum.expected +24 -1
- data/test/command/suite/groonga/select/minimum.test +1 -1
- data/test/command/suite/groonga/select/type/time.catalog.json +19 -0
- data/test/command/suite/groonga/select/type/time.expected +37 -0
- data/test/command/suite/groonga/select/type/time.test +35 -0
- data/test/command/suite/groonga/table_create/array.test +1 -1
- data/test/command/suite/groonga/table_create/hash.test +1 -1
- data/test/command/suite/groonga/table_list/success.test +2 -2
- data/test/command/suite/groonga/table_remove/success.test +1 -1
- data/test/command/suite/groonga/table_remove/unknown-table.test +1 -1
- data/test/command/suite/message/error/unknown-type.expected +1 -1
- data/test/command/suite/message/error/unknown-type.test +1 -1
- data/test/command/suite/search/adjusters/multiple.catalog.json +1 -1
- data/test/command/suite/search/adjusters/multiple.test +3 -3
- data/test/command/suite/search/adjusters/one.catalog.json +1 -1
- data/test/command/suite/search/adjusters/one.test +3 -3
- data/test/command/suite/search/attributes/array.expected +7 -0
- data/test/command/suite/search/attributes/array.test +1 -1
- data/test/command/suite/search/attributes/hash.expected +18 -0
- data/test/command/suite/search/attributes/hash.test +1 -1
- data/test/command/suite/search/complex.expected +12 -0
- data/test/command/suite/search/complex.test +1 -1
- data/test/command/suite/search/condition/nested.catalog.json +37 -0
- data/test/command/suite/search/condition/nested.expected +7 -0
- data/test/command/suite/search/condition/nested.test +103 -2
- data/test/command/suite/search/condition/query.catalog.json +37 -0
- data/test/command/suite/search/condition/query.expected +7 -0
- data/test/command/suite/search/condition/query.test +103 -2
- data/test/command/suite/search/condition/query/nonexistent_column.catalog.json +1 -1
- data/test/command/suite/search/condition/query/nonexistent_column.test +2 -2
- data/test/command/suite/search/condition/query/syntax_error.catalog.json +1 -1
- data/test/command/suite/search/condition/query/syntax_error.test +2 -2
- data/test/command/suite/search/condition/script.catalog.json +37 -0
- data/test/command/suite/search/condition/script.expected +7 -0
- data/test/command/suite/search/condition/script.test +103 -2
- data/test/command/suite/search/error/cyclic-source.test +1 -1
- data/test/command/suite/search/error/deeply-cyclic-source.test +1 -1
- data/test/command/suite/search/error/missing-source-parameter.test +1 -1
- data/test/command/suite/search/error/no-query.test +1 -1
- data/test/command/suite/search/error/unknown-source.test +1 -1
- data/test/command/suite/search/group/count.test +1 -1
- data/test/command/suite/search/group/limit.test +1 -1
- data/test/command/suite/search/group/string.catalog.json +41 -0
- data/test/command/suite/search/group/string.expected +18 -18
- data/test/command/suite/search/group/string.test +67 -22
- data/test/command/suite/search/group/subrecord/with-sort.catalog.json +1 -1
- data/test/command/suite/search/group/subrecord/with-sort.test +5 -5
- data/test/command/suite/search/multiple/chained.catalog.json +37 -0
- data/test/command/suite/search/multiple/chained.expected +14 -0
- data/test/command/suite/search/multiple/chained.test +103 -2
- data/test/command/suite/search/multiple/parallel.expected +14 -0
- data/test/command/suite/search/multiple/parallel.test +1 -1
- data/test/command/suite/search/output/attributes/invalid.catalog.json +1 -1
- data/test/command/suite/search/output/attributes/invalid.test +2 -2
- data/test/command/suite/search/output/attributes/star.catalog.json +23 -0
- data/test/command/suite/search/output/attributes/star.expected +27 -0
- data/test/command/suite/search/output/attributes/star.test +32 -0
- data/test/command/suite/search/range/only-output.expected +7 -0
- data/test/command/suite/search/range/only-output.test +1 -1
- data/test/command/suite/search/range/only-sort.expected +7 -0
- data/test/command/suite/search/range/only-sort.test +1 -1
- data/test/command/suite/search/range/sort-and-output.expected +7 -0
- data/test/command/suite/search/range/sort-and-output.test +1 -1
- data/test/command/suite/search/range/too-large-output-offset.expected +8 -0
- data/test/command/suite/search/range/too-large-output-offset.test +1 -1
- data/test/command/suite/search/range/too-large-sort-offset.expected +8 -0
- data/test/command/suite/search/range/too-large-sort-offset.test +1 -1
- data/test/command/suite/search/response/elapsed_time.catalog.json +1 -1
- data/test/command/suite/search/response/elapsed_time.test +2 -2
- data/test/command/suite/search/response/records/value/time.expected +12 -0
- data/test/command/suite/search/response/records/value/time.test +1 -1
- data/test/command/suite/search/simple.expected +12 -0
- data/test/command/suite/search/simple.test +1 -1
- data/test/command/suite/search/sort/default-offset-limit.expected +7 -0
- data/test/command/suite/search/sort/default-offset-limit.test +1 -1
- data/test/command/suite/search/sort/invisible-column.expected +7 -0
- data/test/command/suite/search/sort/invisible-column.test +1 -1
- data/test/unit/catalog/test_collection_volume.rb +16 -0
- data/test/unit/catalog/test_dataset.rb +36 -0
- data/test/unit/catalog/test_single_volume.rb +9 -0
- data/test/unit/catalog/test_slice.rb +11 -0
- data/test/unit/catalog/test_version1.rb +7 -12
- data/test/unit/catalog/test_version2.rb +7 -0
- data/test/unit/catalog/test_volume_collection.rb +28 -0
- data/test/unit/fixtures/catalog/version1.json +10 -3
- data/test/unit/fixtures/catalog/version2.json +2 -2
- data/test/unit/plugins/groonga/select/test_adapter_output.rb +8 -14
- data/test/unit/plugins/groonga/test_column_create.rb +5 -5
- data/test/unit/plugins/groonga/test_column_remove.rb +2 -2
- data/test/unit/plugins/groonga/test_column_rename.rb +2 -2
- data/test/unit/plugins/groonga/test_delete.rb +2 -2
- data/test/unit/plugins/groonga/test_table_create.rb +9 -9
- data/test/unit/plugins/groonga/test_table_remove.rb +1 -1
- data/test/unit/test_catalog_generator.rb +1 -1
- data/test/unit/test_schema_applier.rb +2 -2
- data/test/unit/test_watch_schema.rb +4 -4
- metadata +241 -72
- data/lib/droonga/engine/command/droonga_engine.rb +0 -441
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
# Copyright (C) 2014 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
15
|
+
|
|
16
|
+
require "optparse"
|
|
17
|
+
|
|
18
|
+
require "coolio"
|
|
19
|
+
|
|
20
|
+
require "droonga/service_control_protocol"
|
|
21
|
+
require "droonga/engine"
|
|
22
|
+
require "droonga/fluent_message_receiver"
|
|
23
|
+
require "droonga/internal_fluent_message_receiver"
|
|
24
|
+
require "droonga/plugin_loader"
|
|
25
|
+
|
|
26
|
+
module Droonga
|
|
27
|
+
module Command
|
|
28
|
+
class DroongaEngineService
|
|
29
|
+
class << self
|
|
30
|
+
def run(command_line_arguments)
|
|
31
|
+
new.run(command_line_arguments)
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
include Loggable
|
|
36
|
+
include ServiceControlProtocol
|
|
37
|
+
|
|
38
|
+
def initialize
|
|
39
|
+
@engine_name = nil
|
|
40
|
+
@listen_fd = nil
|
|
41
|
+
@heartbeat_fd = nil
|
|
42
|
+
@contrtol_read_fd = nil
|
|
43
|
+
@contrtol_write_fd = nil
|
|
44
|
+
@contrtol_write_closed = false
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def run(command_line_arguments)
|
|
48
|
+
create_new_process_group
|
|
49
|
+
|
|
50
|
+
parse_command_line_arguments!(command_line_arguments)
|
|
51
|
+
PluginLoader.load_all
|
|
52
|
+
|
|
53
|
+
control_write_io = IO.new(@control_write_fd)
|
|
54
|
+
success = true
|
|
55
|
+
begin
|
|
56
|
+
run_services
|
|
57
|
+
rescue
|
|
58
|
+
logger.exception("failed to run services", $!)
|
|
59
|
+
success = false
|
|
60
|
+
ensure
|
|
61
|
+
unless @control_write_closed
|
|
62
|
+
control_write_io.write(Messages::FINISH)
|
|
63
|
+
control_write_io.close
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
success
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
private
|
|
71
|
+
def create_new_process_group
|
|
72
|
+
begin
|
|
73
|
+
Process.setsid
|
|
74
|
+
rescue SystemCallError, NotImplementedError
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def parse_command_line_arguments!(command_line_arguments)
|
|
79
|
+
parser = OptionParser.new
|
|
80
|
+
add_internal_options(parser)
|
|
81
|
+
parser.parse!(command_line_arguments)
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def add_internal_options(parser)
|
|
85
|
+
parser.separator("")
|
|
86
|
+
parser.separator("Internal:")
|
|
87
|
+
parser.on("--engine-name=NAME",
|
|
88
|
+
"Use NAME as the name of the engine") do |name|
|
|
89
|
+
@engine_name = name
|
|
90
|
+
end
|
|
91
|
+
parser.on("--listen-fd=FD", Integer,
|
|
92
|
+
"Use FD as the listen file descriptor") do |fd|
|
|
93
|
+
@listen_fd = fd
|
|
94
|
+
end
|
|
95
|
+
parser.on("--heartbeat-fd=FD", Integer,
|
|
96
|
+
"Use FD as the heartbeat file descriptor") do |fd|
|
|
97
|
+
@heartbeat_fd = fd
|
|
98
|
+
end
|
|
99
|
+
parser.on("--control-read-fd=FD", Integer,
|
|
100
|
+
"Use FD to read control messages from the service") do |fd|
|
|
101
|
+
@control_read_fd = fd
|
|
102
|
+
end
|
|
103
|
+
parser.on("--control-write-fd=FD", Integer,
|
|
104
|
+
"Use FD to write control messages from the service") do |fd|
|
|
105
|
+
@control_write_fd = fd
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def host
|
|
110
|
+
@engine_name.split(":", 2).first
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def run_services
|
|
114
|
+
@stopping = false
|
|
115
|
+
@engine = nil
|
|
116
|
+
@receiver = nil
|
|
117
|
+
@loop = Coolio::Loop.default
|
|
118
|
+
|
|
119
|
+
run_internal_message_receiver
|
|
120
|
+
run_engine
|
|
121
|
+
run_receiver
|
|
122
|
+
run_control_io
|
|
123
|
+
@loop.run
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def run_internal_message_receiver
|
|
127
|
+
@internal_message_receiver = create_internal_message_receiver
|
|
128
|
+
host, port = @internal_message_receiver.start
|
|
129
|
+
tag = @engine_name.split("/", 2).last.split(".", 2).first
|
|
130
|
+
@internal_engine_name = "#{host}:#{port}/#{tag}"
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def create_internal_message_receiver
|
|
134
|
+
InternalFluentMessageReceiver.new(@loop, host) do |tag, time, record|
|
|
135
|
+
on_message(tag, time, record)
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def shutdown_internal_message_receiver
|
|
140
|
+
return if @internal_message_receiver.nil?
|
|
141
|
+
@internal_message_receiver, receiver = nil, @internal_message_receiver
|
|
142
|
+
receiver.shutdown
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def run_engine
|
|
146
|
+
@engine = Engine.new(@loop, @engine_name, @internal_engine_name)
|
|
147
|
+
@engine.start
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
def run_receiver
|
|
151
|
+
@receiver = create_receiver
|
|
152
|
+
@receiver.start
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
def shutdown_receiver
|
|
156
|
+
return if @receiver.nil?
|
|
157
|
+
@receiver, receiver = nil, @receiver
|
|
158
|
+
receiver.shutdown
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
def run_control_io
|
|
162
|
+
@control_read = Coolio::IO.new(IO.new(@control_read_fd))
|
|
163
|
+
@control_read_fd = nil
|
|
164
|
+
on_read = lambda do |data|
|
|
165
|
+
# TODO: should buffer data to handle half line received case
|
|
166
|
+
data.each_line do |line|
|
|
167
|
+
case line
|
|
168
|
+
when Messages::STOP_GRACEFUL
|
|
169
|
+
stop_gracefully
|
|
170
|
+
when Messages::STOP_IMMEDIATELY
|
|
171
|
+
stop_immediately
|
|
172
|
+
end
|
|
173
|
+
end
|
|
174
|
+
end
|
|
175
|
+
@control_read.on_read do |data|
|
|
176
|
+
on_read.call(data)
|
|
177
|
+
end
|
|
178
|
+
read_on_close = lambda do
|
|
179
|
+
if @control_read
|
|
180
|
+
@control_read = nil
|
|
181
|
+
stop_immediately
|
|
182
|
+
end
|
|
183
|
+
end
|
|
184
|
+
@control_read.on_close do
|
|
185
|
+
read_on_close.call
|
|
186
|
+
end
|
|
187
|
+
@loop.attach(@control_read)
|
|
188
|
+
|
|
189
|
+
@control_write = Coolio::IO.new(IO.new(@control_write_fd))
|
|
190
|
+
@control_write_fd = nil
|
|
191
|
+
write_on_close = lambda do
|
|
192
|
+
if @control_write
|
|
193
|
+
@control_write = nil
|
|
194
|
+
stop_immediately
|
|
195
|
+
end
|
|
196
|
+
@control_write_closed = true
|
|
197
|
+
end
|
|
198
|
+
@control_write.on_close do
|
|
199
|
+
write_on_close.call
|
|
200
|
+
end
|
|
201
|
+
@loop.attach(@control_write)
|
|
202
|
+
|
|
203
|
+
@control_write.write(Messages::READY)
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
def shutdown_control_io
|
|
207
|
+
if @control_write
|
|
208
|
+
@control_write, control_write = nil, @control_write
|
|
209
|
+
control_write.detach
|
|
210
|
+
end
|
|
211
|
+
if @control_read
|
|
212
|
+
@control_read, control_read = nil, @control_read
|
|
213
|
+
control_read.close
|
|
214
|
+
end
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
def create_receiver
|
|
218
|
+
options = {
|
|
219
|
+
:listen_fd => @listen_fd,
|
|
220
|
+
:heartbeat_fd => @heartbeat_fd,
|
|
221
|
+
}
|
|
222
|
+
FluentMessageReceiver.new(@loop, options) do |tag, time, record|
|
|
223
|
+
on_message(tag, time, record)
|
|
224
|
+
end
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
def on_message(tag, time, record)
|
|
228
|
+
prefix, type, *arguments = tag.split(/\./)
|
|
229
|
+
if type.nil? or type.empty? or type == "message"
|
|
230
|
+
message = record
|
|
231
|
+
else
|
|
232
|
+
message = {
|
|
233
|
+
"type" => type,
|
|
234
|
+
"arguments" => arguments,
|
|
235
|
+
"body" => record
|
|
236
|
+
}
|
|
237
|
+
end
|
|
238
|
+
reply_to = message["replyTo"]
|
|
239
|
+
if reply_to.is_a? String
|
|
240
|
+
message["replyTo"] = {
|
|
241
|
+
"type" => "#{message["type"]}.result",
|
|
242
|
+
"to" => reply_to
|
|
243
|
+
}
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
@engine.process(message)
|
|
247
|
+
end
|
|
248
|
+
|
|
249
|
+
def stop_gracefully
|
|
250
|
+
return if @stopping
|
|
251
|
+
@stopping = true
|
|
252
|
+
shutdown_receiver
|
|
253
|
+
@engine.stop_gracefully do
|
|
254
|
+
shutdown_control_io
|
|
255
|
+
shutdown_internal_message_receiver
|
|
256
|
+
end
|
|
257
|
+
end
|
|
258
|
+
|
|
259
|
+
# It may be called after stop_gracefully.
|
|
260
|
+
def stop_immediately
|
|
261
|
+
shutdown_control_io
|
|
262
|
+
shutdown_receiver if @receiver
|
|
263
|
+
shutdown_internal_message_receiver
|
|
264
|
+
@engine.stop_immediately
|
|
265
|
+
@loop.stop
|
|
266
|
+
end
|
|
267
|
+
|
|
268
|
+
def log_tag
|
|
269
|
+
"droonga-engine-service"
|
|
270
|
+
end
|
|
271
|
+
end
|
|
272
|
+
end
|
|
273
|
+
end
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# Copyright (C) 2014 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
15
|
+
|
|
16
|
+
require "optparse"
|
|
17
|
+
require "pathname"
|
|
18
|
+
require "json"
|
|
19
|
+
require "fileutils"
|
|
20
|
+
require "tempfile"
|
|
21
|
+
|
|
22
|
+
require "droonga/path"
|
|
23
|
+
require "droonga/serf"
|
|
24
|
+
require "droonga/live_nodes_list_observer"
|
|
25
|
+
|
|
26
|
+
module Droonga
|
|
27
|
+
module Command
|
|
28
|
+
class SerfEventHandler
|
|
29
|
+
class << self
|
|
30
|
+
def run
|
|
31
|
+
new.run
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def initialize
|
|
36
|
+
@serf = ENV["SERF"] || Serf.path
|
|
37
|
+
@serf_rpc_address = ENV["SERF_RPC_ADDRESS"] || "127.0.0.1:7373"
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def run
|
|
41
|
+
parse_event
|
|
42
|
+
|
|
43
|
+
output_live_nodes
|
|
44
|
+
true
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
private
|
|
48
|
+
def parse_event
|
|
49
|
+
@event_name = ENV["SERF_EVENT"]
|
|
50
|
+
case @event_name
|
|
51
|
+
when "user"
|
|
52
|
+
@event_name += ":#{ENV["SERF_USER_EVENT"]}"
|
|
53
|
+
when "query"
|
|
54
|
+
@event_name += ":#{ENV["SERF_USER_QUERY"]}"
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def live_nodes
|
|
59
|
+
nodes = {}
|
|
60
|
+
members = `#{@serf} members -rpc-addr #{@serf_rpc_address}`
|
|
61
|
+
members.each_line do |member|
|
|
62
|
+
name, address, status, = member.strip.split(/\s+/)
|
|
63
|
+
if status == "alive"
|
|
64
|
+
nodes[name] = {
|
|
65
|
+
"serfAddress" => address,
|
|
66
|
+
}
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
nodes
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def output_live_nodes
|
|
73
|
+
list_path = LiveNodesListObserver.path
|
|
74
|
+
nodes = live_nodes
|
|
75
|
+
file_contents = JSON.pretty_generate(nodes)
|
|
76
|
+
# Don't output the file directly to prevent loading of incomplete file!
|
|
77
|
+
Tempfile.open(list_path.basename.to_s, list_path.parent.to_s, "w") do |output|
|
|
78
|
+
output.write(file_contents)
|
|
79
|
+
output.flush
|
|
80
|
+
File.rename(output.path, list_path.to_s)
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
data/lib/droonga/dispatcher.rb
CHANGED
|
@@ -13,7 +13,6 @@
|
|
|
13
13
|
# License along with this library; if not, write to the Free Software
|
|
14
14
|
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
15
15
|
|
|
16
|
-
require "English"
|
|
17
16
|
require "tsort"
|
|
18
17
|
|
|
19
18
|
require "droonga/loggable"
|
|
@@ -47,9 +46,12 @@ module Droonga
|
|
|
47
46
|
end
|
|
48
47
|
end
|
|
49
48
|
|
|
49
|
+
attr_accessor :live_nodes
|
|
50
|
+
|
|
50
51
|
def initialize(engine_state, catalog)
|
|
51
52
|
@engine_state = engine_state
|
|
52
53
|
@catalog = catalog
|
|
54
|
+
@live_nodes = catalog.all_nodes
|
|
53
55
|
@adapter_runners = create_adapter_runners
|
|
54
56
|
@farm = Farm.new(@engine_state.name, @catalog, @engine_state.loop,
|
|
55
57
|
:dispatcher => self)
|
|
@@ -146,6 +148,8 @@ module Droonga
|
|
|
146
148
|
session = session_planner.create_session(id, collector_runner)
|
|
147
149
|
@engine_state.register_session(id, session)
|
|
148
150
|
else
|
|
151
|
+
logger.error("no steps error: id=#{id}, message=#{message}")
|
|
152
|
+
return
|
|
149
153
|
#todo: take cases receiving result before its query into account
|
|
150
154
|
end
|
|
151
155
|
session.start
|
|
@@ -167,9 +171,9 @@ module Droonga
|
|
|
167
171
|
id = @engine_state.generate_id
|
|
168
172
|
destinations = {}
|
|
169
173
|
steps.each do |step|
|
|
170
|
-
dataset = step["dataset"]
|
|
174
|
+
dataset = @catalog.dataset(step["dataset"])
|
|
171
175
|
if dataset
|
|
172
|
-
routes =
|
|
176
|
+
routes = dataset.get_routes(step, @live_nodes)
|
|
173
177
|
step["routes"] = routes
|
|
174
178
|
else
|
|
175
179
|
step["routes"] ||= [id]
|
|
@@ -208,11 +212,7 @@ module Droonga
|
|
|
208
212
|
|
|
209
213
|
private
|
|
210
214
|
def farm_path(route)
|
|
211
|
-
|
|
212
|
-
$MATCH
|
|
213
|
-
else
|
|
214
|
-
route
|
|
215
|
-
end
|
|
215
|
+
@engine_state.farm_path(route)
|
|
216
216
|
end
|
|
217
217
|
|
|
218
218
|
def process_input_message(message)
|
data/lib/droonga/engine.rb
CHANGED
|
@@ -15,62 +15,126 @@
|
|
|
15
15
|
# License along with this library; if not, write to the Free Software
|
|
16
16
|
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
17
17
|
|
|
18
|
+
require "time"
|
|
19
|
+
require "fileutils"
|
|
18
20
|
require "droonga/engine/version"
|
|
19
21
|
require "droonga/loggable"
|
|
20
22
|
require "droonga/engine_state"
|
|
21
|
-
require "droonga/
|
|
23
|
+
require "droonga/catalog_loader"
|
|
22
24
|
require "droonga/dispatcher"
|
|
25
|
+
require "droonga/live_nodes_list_observer"
|
|
23
26
|
|
|
24
27
|
module Droonga
|
|
25
28
|
class Engine
|
|
26
29
|
include Loggable
|
|
27
30
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
31
|
+
LAST_PROCESSED_TIMESTAMP = "last-processed.timestamp"
|
|
32
|
+
EFFECTIVE_MESSAGE_TIMESTAMP = "effective-message.timestamp"
|
|
33
|
+
|
|
34
|
+
def initialize(loop, name, internal_name)
|
|
35
|
+
@state = EngineState.new(loop, name, internal_name)
|
|
36
|
+
@catalog = load_catalog
|
|
37
|
+
@live_nodes = @catalog.all_nodes
|
|
38
|
+
@dispatcher = create_dispatcher
|
|
39
|
+
@live_nodes_list_observer = LiveNodesListObserver.new
|
|
40
|
+
@live_nodes_list_observer.on_update = lambda do |live_nodes|
|
|
41
|
+
@live_nodes = live_nodes
|
|
42
|
+
@dispatcher.live_nodes = live_nodes if @dispatcher
|
|
34
43
|
end
|
|
35
44
|
end
|
|
36
45
|
|
|
37
46
|
def start
|
|
38
47
|
logger.trace("start: start")
|
|
39
48
|
@state.start
|
|
40
|
-
@
|
|
41
|
-
catalog = @catalog_observer.catalog
|
|
42
|
-
@dispatcher = create_dispatcher(catalog)
|
|
49
|
+
@live_nodes_list_observer.start
|
|
43
50
|
@dispatcher.start
|
|
44
51
|
logger.trace("start: done")
|
|
45
52
|
end
|
|
46
53
|
|
|
47
|
-
def
|
|
48
|
-
logger.trace("
|
|
49
|
-
@
|
|
54
|
+
def stop_gracefully
|
|
55
|
+
logger.trace("stop_gracefully: start")
|
|
56
|
+
@live_nodes_list_observer.stop
|
|
57
|
+
on_finish = lambda do
|
|
58
|
+
output_last_processed_timestamp
|
|
59
|
+
@dispatcher.shutdown
|
|
60
|
+
@state.shutdown
|
|
61
|
+
yield
|
|
62
|
+
end
|
|
63
|
+
if @state.have_session?
|
|
64
|
+
@state.on_finish = on_finish
|
|
65
|
+
else
|
|
66
|
+
on_finish.call
|
|
67
|
+
end
|
|
68
|
+
logger.trace("stop_gracefully: done")
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# It may be called after stop_gracefully.
|
|
72
|
+
def stop_immediately
|
|
73
|
+
logger.trace("stop_immediately: start")
|
|
74
|
+
output_last_processed_timestamp
|
|
75
|
+
@live_nodes_list_observer.stop
|
|
50
76
|
@dispatcher.shutdown
|
|
51
77
|
@state.shutdown
|
|
52
|
-
logger.trace("
|
|
78
|
+
logger.trace("stop_immediately: done")
|
|
53
79
|
end
|
|
54
80
|
|
|
55
81
|
def process(message)
|
|
82
|
+
return unless effective_message?(message)
|
|
83
|
+
@last_processed_timestamp = message["date"]
|
|
56
84
|
@dispatcher.process_message(message)
|
|
57
85
|
end
|
|
58
86
|
|
|
59
87
|
private
|
|
60
|
-
def
|
|
61
|
-
|
|
88
|
+
def load_catalog
|
|
89
|
+
catalog_path = Path.catalog
|
|
90
|
+
loader = CatalogLoader.new(catalog_path.to_s)
|
|
91
|
+
catalog = loader.load
|
|
92
|
+
logger.info("catalog loaded",
|
|
93
|
+
:path => catalog_path,
|
|
94
|
+
:mtime => catalog_path.mtime)
|
|
95
|
+
catalog
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def create_dispatcher
|
|
99
|
+
dispatcher = Dispatcher.new(@state, @catalog)
|
|
100
|
+
dispatcher.live_nodes = @live_nodes
|
|
101
|
+
dispatcher
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def output_last_processed_timestamp
|
|
105
|
+
File.open(last_processed_timestamp_file, "w") do |file|
|
|
106
|
+
file.write(@last_processed_timestamp)
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def last_processed_timestamp_file
|
|
111
|
+
@last_processed_timestamp_file ||= File.join(Droonga::Path.state, LAST_PROCESSED_TIMESTAMP)
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def effective_message?(message)
|
|
115
|
+
effective_timestamp = effective_message_timestamp
|
|
116
|
+
return true if effective_timestamp.nil?
|
|
117
|
+
|
|
118
|
+
message_timestamp = Time.parse(message["date"])
|
|
119
|
+
return false if effective_timestamp >= message_timestamp
|
|
120
|
+
|
|
121
|
+
FileUtils.rm(effective_message_timestamp_file)
|
|
122
|
+
true
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def effective_message_timestamp
|
|
126
|
+
return nil unless File.exist?(effective_message_timestamp_file)
|
|
127
|
+
|
|
128
|
+
timestamp = File.read(effective_message_timestamp_file)
|
|
129
|
+
begin
|
|
130
|
+
Time.parse(timestamp)
|
|
131
|
+
rescue ArgumentError
|
|
132
|
+
nil
|
|
133
|
+
end
|
|
62
134
|
end
|
|
63
135
|
|
|
64
|
-
def
|
|
65
|
-
|
|
66
|
-
old_dispatcher = @dispatcher
|
|
67
|
-
logger.trace("graceful_restart: creating new dispatcher")
|
|
68
|
-
new_dispatcher = create_dispatcher(catalog)
|
|
69
|
-
new_dispatcher.start
|
|
70
|
-
@dispatcher = new_dispatcher
|
|
71
|
-
logger.trace("graceful_restart: shutdown old dispatcher")
|
|
72
|
-
old_dispatcher.shutdown
|
|
73
|
-
logger.trace("graceful_restart: done")
|
|
136
|
+
def effective_message_timestamp_file
|
|
137
|
+
@effective_message_timestamp_file ||= File.join(Droonga::Path.state, EFFECTIVE_MESSAGE_TIMESTAMP)
|
|
74
138
|
end
|
|
75
139
|
|
|
76
140
|
def log_tag
|