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
data/lib/droonga/engine_state.rb
CHANGED
@@ -13,6 +13,8 @@
|
|
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
18
|
require "coolio"
|
17
19
|
|
18
20
|
require "droonga/loggable"
|
@@ -26,15 +28,19 @@ module Droonga
|
|
26
28
|
|
27
29
|
attr_reader :loop
|
28
30
|
attr_reader :name
|
31
|
+
attr_reader :internal_name
|
29
32
|
attr_reader :forwarder
|
30
33
|
attr_reader :replier
|
31
|
-
|
34
|
+
attr_accessor :on_finish
|
35
|
+
def initialize(loop, name, internal_name)
|
32
36
|
@loop = loop
|
33
37
|
@name = name
|
38
|
+
@internal_name = internal_name
|
34
39
|
@sessions = {}
|
35
40
|
@current_id = 0
|
36
41
|
@forwarder = Forwarder.new(@loop)
|
37
42
|
@replier = Replier.new(@forwarder)
|
43
|
+
@on_finish = nil
|
38
44
|
end
|
39
45
|
|
40
46
|
def start
|
@@ -50,13 +56,26 @@ module Droonga
|
|
50
56
|
end
|
51
57
|
|
52
58
|
def local_route?(route)
|
53
|
-
route.start_with?(@name)
|
59
|
+
route.start_with?(@name) or route.start_with?(@internal_name)
|
60
|
+
end
|
61
|
+
|
62
|
+
def farm_path(route)
|
63
|
+
if /\A[^:]+:\d+\/[^.]+/ =~ route
|
64
|
+
name = $MATCH
|
65
|
+
if name == @internal_name
|
66
|
+
@name
|
67
|
+
else
|
68
|
+
name
|
69
|
+
end
|
70
|
+
else
|
71
|
+
route
|
72
|
+
end
|
54
73
|
end
|
55
74
|
|
56
75
|
def generate_id
|
57
76
|
id = @current_id
|
58
77
|
@current_id = id.succ
|
59
|
-
return [@
|
78
|
+
return [@internal_name, id].join(".#")
|
60
79
|
end
|
61
80
|
|
62
81
|
def find_session(id)
|
@@ -69,6 +88,13 @@ module Droonga
|
|
69
88
|
|
70
89
|
def unregister_session(id)
|
71
90
|
@sessions.delete(id)
|
91
|
+
unless have_session?
|
92
|
+
@on_finish.call if @on_finish
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def have_session?
|
97
|
+
not @sessions.empty?
|
72
98
|
end
|
73
99
|
|
74
100
|
private
|
@@ -0,0 +1,100 @@
|
|
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 "socket"
|
17
|
+
require "ipaddr"
|
18
|
+
|
19
|
+
require "droonga/fluent_message_receiver"
|
20
|
+
|
21
|
+
module Droonga
|
22
|
+
class InternalFluentMessageReceiver
|
23
|
+
include Loggable
|
24
|
+
|
25
|
+
def initialize(loop, host, &on_message)
|
26
|
+
@loop = loop
|
27
|
+
@host = host
|
28
|
+
@on_message = on_message
|
29
|
+
end
|
30
|
+
|
31
|
+
def start
|
32
|
+
logger.trace("start: start")
|
33
|
+
start_listen_socket
|
34
|
+
start_heartbeat_socket
|
35
|
+
start_message_receiver
|
36
|
+
logger.trace("start: done")
|
37
|
+
|
38
|
+
[@host, @port]
|
39
|
+
end
|
40
|
+
|
41
|
+
def shutdown
|
42
|
+
logger.trace("shutdown: start")
|
43
|
+
shutdown_message_receiver
|
44
|
+
shutdown_heartbeat_socket
|
45
|
+
shutdown_listen_socket
|
46
|
+
logger.trace("shutdown: done")
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
def start_listen_socket
|
51
|
+
logger.trace("start_listen_socket: start")
|
52
|
+
@listen_socket = TCPServer.new(@host, 0)
|
53
|
+
@port = @listen_socket.addr[1]
|
54
|
+
logger.trace("start_listen_socket: done")
|
55
|
+
end
|
56
|
+
|
57
|
+
def shutdown_listen_socket
|
58
|
+
logger.trace("shutdown_listen_socket: start")
|
59
|
+
logger.trace("shutdown_listen_socket: done")
|
60
|
+
end
|
61
|
+
|
62
|
+
def address_family
|
63
|
+
ip_address = IPAddr.new(IPSocket.getaddress(@host))
|
64
|
+
ip_address.family
|
65
|
+
end
|
66
|
+
|
67
|
+
def start_heartbeat_socket
|
68
|
+
logger.trace("start_heartbeat_socket: start")
|
69
|
+
@heartbeat_socket = UDPSocket.new(address_family)
|
70
|
+
@heartbeat_socket.bind(@host, @port)
|
71
|
+
logger.trace("start_heartbeat_socket: done")
|
72
|
+
end
|
73
|
+
|
74
|
+
def shutdown_heartbeat_socket
|
75
|
+
logger.trace("shutdown_heartbeat_socket: start")
|
76
|
+
logger.trace("shutdown_heartbeat_socket: done")
|
77
|
+
end
|
78
|
+
|
79
|
+
def start_message_receiver
|
80
|
+
logger.trace("start_heartbeat_socket: start")
|
81
|
+
options = {
|
82
|
+
:listen_fd => @listen_socket.fileno,
|
83
|
+
:heartbeat_fd => @heartbeat_socket.fileno,
|
84
|
+
}
|
85
|
+
@message_receiver = FluentMessageReceiver.new(@loop, options, &@on_message)
|
86
|
+
@message_receiver.start
|
87
|
+
logger.trace("start_heartbeat_socket: done")
|
88
|
+
end
|
89
|
+
|
90
|
+
def shutdown_message_receiver
|
91
|
+
logger.trace("shutdown_message_receiver: start")
|
92
|
+
@message_receiver.shutdown
|
93
|
+
logger.trace("shutdown_message_receiver: done")
|
94
|
+
end
|
95
|
+
|
96
|
+
def log_tag
|
97
|
+
"internal-fluent-message-receiver"
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
@@ -0,0 +1,48 @@
|
|
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 "pathname"
|
17
|
+
require "json"
|
18
|
+
|
19
|
+
module Droonga
|
20
|
+
class LiveNodesListLoader
|
21
|
+
def initialize(path)
|
22
|
+
@path = path
|
23
|
+
end
|
24
|
+
|
25
|
+
def load
|
26
|
+
list = parse
|
27
|
+
list.keys
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
def parse
|
32
|
+
return default_list unless @path.exist?
|
33
|
+
|
34
|
+
contents = @path.read
|
35
|
+
return default_list if contents.empty?
|
36
|
+
|
37
|
+
begin
|
38
|
+
JSON.parse(contents)
|
39
|
+
rescue JSON::ParserError
|
40
|
+
default_list
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def default_list
|
45
|
+
{}
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,72 @@
|
|
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 "fileutils"
|
17
|
+
require "listen"
|
18
|
+
|
19
|
+
require "droonga/path"
|
20
|
+
require "droonga/loggable"
|
21
|
+
require "droonga/live_nodes_list_loader"
|
22
|
+
|
23
|
+
module Droonga
|
24
|
+
class LiveNodesListObserver
|
25
|
+
class << self
|
26
|
+
FILE_NAME = "live-nodes.json"
|
27
|
+
|
28
|
+
def path
|
29
|
+
Path.state + FILE_NAME
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
include Loggable
|
34
|
+
|
35
|
+
attr_accessor :on_update
|
36
|
+
|
37
|
+
def initialize
|
38
|
+
end
|
39
|
+
|
40
|
+
def start
|
41
|
+
path = self.class.path
|
42
|
+
file_name = path.expand_path.to_s
|
43
|
+
directory = path.dirname.to_s
|
44
|
+
FileUtils.mkdir_p(directory)
|
45
|
+
@listener = Listen.to(directory) do |modified, added, removed|
|
46
|
+
if added.include?(file_name) or
|
47
|
+
modified.include?(file_name)
|
48
|
+
load_list!
|
49
|
+
end
|
50
|
+
end
|
51
|
+
@listener.start
|
52
|
+
end
|
53
|
+
|
54
|
+
def stop
|
55
|
+
@listener.stop
|
56
|
+
end
|
57
|
+
|
58
|
+
def load_list!
|
59
|
+
path = self.class.path
|
60
|
+
loader = LiveNodesListLoader.new(path)
|
61
|
+
live_nodes = loader.load
|
62
|
+
logger.info("loaded", :path => path.to_s, :live_nodes => live_nodes)
|
63
|
+
|
64
|
+
on_update.call(live_nodes) if on_update
|
65
|
+
end
|
66
|
+
|
67
|
+
private
|
68
|
+
def log_tag
|
69
|
+
"live-nodes-list-observer"
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
data/lib/droonga/path.rb
ADDED
@@ -0,0 +1,47 @@
|
|
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 "pathname"
|
17
|
+
|
18
|
+
module Droonga
|
19
|
+
module Path
|
20
|
+
BASE_DIR_ENV_NAME = "DROONGA_BASE_DIR"
|
21
|
+
|
22
|
+
class << self
|
23
|
+
def setup
|
24
|
+
base_dir = ENV[BASE_DIR_ENV_NAME] || Dir.pwd
|
25
|
+
ENV[BASE_DIR_ENV_NAME] = File.expand_path(base_dir)
|
26
|
+
end
|
27
|
+
|
28
|
+
def base
|
29
|
+
@base ||= Pathname.new(ENV[BASE_DIR_ENV_NAME] || Dir.pwd).expand_path
|
30
|
+
end
|
31
|
+
|
32
|
+
def base=(new_base)
|
33
|
+
@base = nil
|
34
|
+
ENV[BASE_DIR_ENV_NAME] = new_base
|
35
|
+
end
|
36
|
+
|
37
|
+
def state
|
38
|
+
base + "state"
|
39
|
+
end
|
40
|
+
|
41
|
+
def catalog
|
42
|
+
base_file_name = ENV["DROONGA_CATALOG"] || "catalog.json"
|
43
|
+
Pathname.new(base_file_name).expand_path(base)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
data/lib/droonga/plugins/dump.rb
CHANGED
@@ -26,65 +26,306 @@ module Droonga
|
|
26
26
|
|
27
27
|
class Handler < Droonga::Handler
|
28
28
|
def handle(message)
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
29
|
+
request = Request.new(message)
|
30
|
+
if request.need_dump?
|
31
|
+
dumper = Dumper.new(@context, loop, messenger, request)
|
32
|
+
dumper.start_dump
|
33
|
+
true
|
34
|
+
else
|
35
|
+
false
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
class Request
|
41
|
+
def initialize(message)
|
42
|
+
@message = message
|
43
|
+
end
|
44
|
+
|
45
|
+
def need_dump?
|
46
|
+
reply_to
|
47
|
+
end
|
48
|
+
|
49
|
+
def id
|
50
|
+
@message["id"]
|
51
|
+
end
|
52
|
+
|
53
|
+
def dataset
|
54
|
+
@message.raw["dataset"]
|
55
|
+
end
|
56
|
+
|
57
|
+
def reply_to
|
58
|
+
(@message.raw["replyTo"] || {})["to"]
|
59
|
+
end
|
60
|
+
|
61
|
+
def messages_per_seconds
|
62
|
+
request = (@message.request || {})
|
63
|
+
minimum_messages_per_seconds = 10
|
64
|
+
[
|
65
|
+
minimum_messages_per_seconds,
|
66
|
+
(request["messagesPerSecond"] || 10000).to_i,
|
67
|
+
].max
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
class Dumper
|
72
|
+
include Loggable
|
73
|
+
|
74
|
+
def initialize(context, loop, messenger, request)
|
75
|
+
@context = context
|
76
|
+
@loop = loop
|
77
|
+
@messenger = messenger
|
78
|
+
@request = request
|
79
|
+
end
|
80
|
+
|
81
|
+
def start_dump
|
82
|
+
setup_forward_data
|
83
|
+
|
84
|
+
forward("dump.start")
|
85
|
+
|
86
|
+
dumper = Fiber.new do
|
87
|
+
dump_schema
|
88
|
+
dump_records
|
89
|
+
dump_indexes
|
90
|
+
forward("dump.end")
|
91
|
+
end
|
92
|
+
|
93
|
+
on_error = lambda do |exception|
|
94
|
+
message = "failed to dump"
|
95
|
+
logger.exception(message, $!)
|
96
|
+
error("DumpFailure", message)
|
59
97
|
end
|
60
98
|
|
61
99
|
timer = Coolio::TimerWatcher.new(0.1, true)
|
62
100
|
timer.on_timer do
|
63
101
|
begin
|
64
|
-
dumper.
|
65
|
-
rescue
|
102
|
+
dumper.resume
|
103
|
+
rescue FiberError
|
66
104
|
timer.detach
|
105
|
+
rescue
|
106
|
+
timer.detach
|
107
|
+
on_error.call($!)
|
67
108
|
end
|
68
109
|
end
|
69
|
-
loop.attach(timer)
|
70
110
|
|
71
|
-
|
111
|
+
@loop.attach(timer)
|
72
112
|
end
|
73
113
|
|
74
114
|
private
|
115
|
+
def setup_forward_data
|
116
|
+
@base_forward_message = {
|
117
|
+
"inReplyTo" => @request.id,
|
118
|
+
"dataset" => @request.dataset,
|
119
|
+
}
|
120
|
+
@forward_to = @request.reply_to
|
121
|
+
@n_forwarded_messages = 0
|
122
|
+
@messages_per_100msec = @request.messages_per_seconds / 10
|
123
|
+
end
|
124
|
+
|
125
|
+
def error(name, message)
|
126
|
+
message = {
|
127
|
+
"statusCode" => ErrorMessages::InternalServerError::STATUS_CODE,
|
128
|
+
"body" => {
|
129
|
+
"name" => name,
|
130
|
+
"message" => message,
|
131
|
+
},
|
132
|
+
}
|
133
|
+
error_message = @base_forward_message.merge(message)
|
134
|
+
@messenger.forward(error_message,
|
135
|
+
"to" => @forward_to,
|
136
|
+
"type" => "dump.error")
|
137
|
+
end
|
138
|
+
|
139
|
+
def forward(type, body=nil)
|
140
|
+
forward_message = @base_forward_message
|
141
|
+
if body
|
142
|
+
forward_message = forward_message.merge("body" => body)
|
143
|
+
end
|
144
|
+
@messenger.forward(forward_message,
|
145
|
+
"to" => @forward_to,
|
146
|
+
"type" => type)
|
147
|
+
|
148
|
+
@n_forwarded_messages += 1
|
149
|
+
@n_forwarded_messages %= @messages_per_100msec
|
150
|
+
Fiber.yield if @n_forwarded_messages.zero?
|
151
|
+
end
|
152
|
+
|
153
|
+
def dump_schema
|
154
|
+
reference_tables = []
|
155
|
+
each_table do |table|
|
156
|
+
if reference_table?(table)
|
157
|
+
reference_tables << table
|
158
|
+
next
|
159
|
+
end
|
160
|
+
dump_table(table)
|
161
|
+
end
|
162
|
+
|
163
|
+
reference_tables.each do |table|
|
164
|
+
dump_table(table)
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
def dump_table(table)
|
169
|
+
forward("dump.table", table_body(table))
|
170
|
+
|
171
|
+
columns = table.columns.sort_by(&:name)
|
172
|
+
columns.each do |column|
|
173
|
+
next if index_column?(column)
|
174
|
+
dump_column(column)
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
def table_body(table)
|
179
|
+
body = {
|
180
|
+
"type" => table_type(table),
|
181
|
+
"name" => table.name,
|
182
|
+
}
|
183
|
+
if table.support_key?
|
184
|
+
body["keyType"] = table.domain.name
|
185
|
+
end
|
186
|
+
if body["keyType"] == "ShortText"
|
187
|
+
if table.default_tokenizer
|
188
|
+
body["tokenizer"] = table.default_tokenizer.name
|
189
|
+
end
|
190
|
+
if table.normalizer
|
191
|
+
body["normalizer"] = table.normalizer.name
|
192
|
+
end
|
193
|
+
end
|
194
|
+
body
|
195
|
+
end
|
196
|
+
|
197
|
+
def table_type(table)
|
198
|
+
table.class.name.split(/::/).last
|
199
|
+
end
|
200
|
+
|
201
|
+
def dump_column(column)
|
202
|
+
forward("dump.column", column_body(column))
|
203
|
+
end
|
204
|
+
|
205
|
+
def column_body(column)
|
206
|
+
body = {
|
207
|
+
"table" => column.domain.name,
|
208
|
+
"name" => column.local_name,
|
209
|
+
"type" => column_type(column),
|
210
|
+
"valueType" => column.range.name,
|
211
|
+
}
|
212
|
+
case body["type"]
|
213
|
+
when "Index"
|
214
|
+
body["indexOptions"] = {
|
215
|
+
"section" => column.with_section?,
|
216
|
+
"weight" => column.with_weight?,
|
217
|
+
"position" => column.with_position?,
|
218
|
+
"sources" => index_column_sources(column),
|
219
|
+
}
|
220
|
+
when "Vector"
|
221
|
+
body["vectorOptions"] = {
|
222
|
+
"weight" => column.with_weight?,
|
223
|
+
}
|
224
|
+
end
|
225
|
+
body
|
226
|
+
end
|
227
|
+
|
228
|
+
def column_type(column)
|
229
|
+
if index_column?(column)
|
230
|
+
"Index"
|
231
|
+
elsif column.vector?
|
232
|
+
"Vector"
|
233
|
+
else
|
234
|
+
"Scalar"
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
def index_column_sources(index_column)
|
239
|
+
index_column.sources.collect do |source|
|
240
|
+
if source.is_a?(::Groonga::Table)
|
241
|
+
"_key"
|
242
|
+
else
|
243
|
+
source.local_name
|
244
|
+
end
|
245
|
+
end
|
246
|
+
end
|
247
|
+
|
248
|
+
def dump_records
|
249
|
+
each_table do |table|
|
250
|
+
next if index_only_table?(table)
|
251
|
+
table.each do |record|
|
252
|
+
values = {}
|
253
|
+
record.attributes.each do |key, value|
|
254
|
+
next if key.start_with?("_")
|
255
|
+
values[key] = normalize_record_value(value)
|
256
|
+
end
|
257
|
+
body = {
|
258
|
+
"table" => table.name,
|
259
|
+
"key" => record.key,
|
260
|
+
"values" => values,
|
261
|
+
}
|
262
|
+
forward("dump.record", body)
|
263
|
+
end
|
264
|
+
end
|
265
|
+
end
|
266
|
+
|
267
|
+
def normalize_record_value(value)
|
268
|
+
case value
|
269
|
+
when Array
|
270
|
+
value.collect do |element|
|
271
|
+
case element
|
272
|
+
when Hash
|
273
|
+
element["_key"]
|
274
|
+
else
|
275
|
+
element
|
276
|
+
end
|
277
|
+
end
|
278
|
+
else
|
279
|
+
value
|
280
|
+
end
|
281
|
+
end
|
282
|
+
|
283
|
+
def dump_indexes
|
284
|
+
each_index_columns do |column|
|
285
|
+
dump_column(column)
|
286
|
+
end
|
287
|
+
end
|
288
|
+
|
75
289
|
def each_table
|
76
|
-
|
77
|
-
|
78
|
-
|
290
|
+
options = {
|
291
|
+
:ignore_missing_object => true,
|
292
|
+
:order_by => :key,
|
293
|
+
}
|
294
|
+
@context.database.each(options) do |object|
|
295
|
+
next unless table?(object)
|
79
296
|
yield(object)
|
80
297
|
end
|
81
298
|
end
|
82
299
|
|
300
|
+
def table?(object)
|
301
|
+
object.is_a?(::Groonga::Table)
|
302
|
+
end
|
303
|
+
|
83
304
|
def index_only_table?(table)
|
84
305
|
table.columns.all? do |column|
|
85
|
-
|
306
|
+
index_column?(column)
|
86
307
|
end
|
87
308
|
end
|
309
|
+
|
310
|
+
def reference_table?(table)
|
311
|
+
table.support_key? and table?(table.domain)
|
312
|
+
end
|
313
|
+
|
314
|
+
def index_column?(column)
|
315
|
+
column.is_a?(::Groonga::IndexColumn)
|
316
|
+
end
|
317
|
+
|
318
|
+
def each_index_columns
|
319
|
+
each_table do |table|
|
320
|
+
table.columns.each do |column|
|
321
|
+
yield(column) if index_column?(column)
|
322
|
+
end
|
323
|
+
end
|
324
|
+
end
|
325
|
+
|
326
|
+
def log_tag
|
327
|
+
"[#{Process.ppid}][#{Process.pid}] dumper"
|
328
|
+
end
|
88
329
|
end
|
89
330
|
|
90
331
|
define_single_step do |step|
|