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