droonga-engine 1.0.2 → 1.0.3
Sign up to get free protection for your applications and to get access to all the features.
- 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
|