droonga-engine 1.0.7 → 1.0.8
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 +4 -4
- data/.travis.yml +2 -2
- data/bin/droonga-engine-absorb-data +200 -86
- data/bin/droonga-engine-configure +14 -11
- data/bin/droonga-engine-join +178 -59
- data/droonga-engine.gemspec +1 -1
- data/install.sh +1 -0
- data/lib/droonga/buffered_tcp_socket.rb +2 -2
- data/lib/droonga/catalog/base.rb +18 -0
- data/lib/droonga/catalog/dataset.rb +8 -0
- data/lib/droonga/catalog/version1.rb +3 -12
- data/lib/droonga/catalog/version2.rb +5 -12
- data/lib/droonga/command/droonga_engine.rb +6 -61
- data/lib/droonga/command/droonga_engine_service.rb +1 -0
- data/lib/droonga/command/droonga_engine_worker.rb +1 -0
- data/lib/droonga/command/remote.rb +18 -1
- data/lib/droonga/command/serf_event_handler.rb +3 -0
- data/lib/droonga/data_absorber.rb +234 -44
- data/lib/droonga/distributed_command_planner.rb +5 -5
- data/lib/droonga/engine.rb +27 -15
- data/lib/droonga/engine/version.rb +1 -1
- data/lib/droonga/handler_runner.rb +4 -0
- data/lib/droonga/node_status.rb +6 -2
- data/lib/droonga/path.rb +8 -14
- data/lib/droonga/planner.rb +4 -3
- data/lib/droonga/plugin/metadata/handler_action.rb +8 -0
- data/lib/droonga/plugins/groonga/column_create.rb +1 -1
- data/lib/droonga/plugins/groonga/column_list.rb +23 -1
- data/lib/droonga/plugins/groonga/column_remove.rb +1 -1
- data/lib/droonga/plugins/groonga/column_rename.rb +1 -1
- data/lib/droonga/plugins/groonga/delete.rb +1 -1
- data/lib/droonga/plugins/groonga/select.rb +17 -2
- data/lib/droonga/plugins/groonga/table_create.rb +26 -1
- data/lib/droonga/plugins/groonga/table_remove.rb +1 -1
- data/lib/droonga/plugins/search.rb +1 -1
- data/lib/droonga/plugins/search/distributed_search_planner.rb +15 -7
- data/lib/droonga/processor.rb +3 -2
- data/lib/droonga/searcher.rb +31 -15
- data/lib/droonga/serf.rb +1 -0
- data/lib/droonga/service_installation.rb +2 -2
- data/lib/droonga/single_step.rb +2 -2
- data/test/command/fixture/event.jsons +3 -2
- data/test/command/fixture/user-table.jsons +3 -2
- data/test/command/fixture/users.jsons +25 -0
- data/test/command/run-test.rb +13 -1
- data/test/command/suite/groonga/column_create/scalar.test +3 -2
- data/test/command/suite/groonga/column_create/vector.test +3 -2
- data/test/command/suite/groonga/column_list/{success.expected → no-key.expected} +0 -0
- data/test/command/suite/groonga/column_list/{success.test → no-key.test} +1 -1
- data/test/command/suite/groonga/column_list/with-key.expected +96 -0
- data/test/command/suite/groonga/column_list/with-key.test +25 -0
- data/test/command/suite/groonga/column_remove/success.test +3 -2
- data/test/command/suite/groonga/column_remove/unknown-column.test +3 -2
- data/test/command/suite/groonga/column_rename/success.test +3 -2
- data/test/command/suite/groonga/column_rename/unknown-column.test +3 -2
- data/test/command/suite/groonga/delete/duplicated-identifiers.test +3 -2
- data/test/command/suite/groonga/delete/no-identifier.test +3 -2
- data/test/command/suite/groonga/select/output_columns/default/array.expected +33 -0
- data/test/command/suite/groonga/select/output_columns/default/array.test +38 -0
- data/test/command/suite/groonga/select/output_columns/nonexistent.expected +28 -0
- data/test/command/suite/groonga/select/output_columns/nonexistent.test +26 -0
- data/test/command/suite/groonga/table_create/dat-without-key-type.expected +14 -0
- data/test/command/suite/groonga/table_create/dat-without-key-type.test +8 -0
- data/test/command/suite/groonga/table_create/dat.expected +13 -0
- data/test/command/suite/groonga/table_create/dat.test +9 -0
- data/test/command/suite/groonga/table_create/hash-without-key-type.expected +14 -0
- data/test/command/suite/groonga/table_create/hash-without-key-type.test +8 -0
- data/test/command/suite/groonga/table_create/hash.test +3 -2
- data/test/command/suite/groonga/table_create/pat-without-key-type.expected +14 -0
- data/test/command/suite/groonga/table_create/pat-without-key-type.test +8 -0
- data/test/command/suite/groonga/table_create/pat.expected +13 -0
- data/test/command/suite/groonga/table_create/pat.test +9 -0
- data/test/command/suite/groonga/table_list/success.test +3 -2
- data/test/unit/catalog/test_version1.rb +2 -2
- data/test/unit/catalog/test_version2.rb +3 -3
- data/test/unit/helper.rb +2 -2
- data/test/unit/helper/distributed_search_planner_helper.rb +9 -1
- data/test/unit/plugins/groonga/select/test_adapter_input.rb +15 -2
- data/test/unit/plugins/groonga/test_column_list.rb +119 -4
- data/test/unit/plugins/groonga/test_table_create.rb +29 -0
- data/test/unit/plugins/search/planner/test_basic.rb +2 -2
- data/test/unit/plugins/search/test_planner.rb +10 -2
- metadata +43 -8
|
@@ -17,17 +17,17 @@
|
|
|
17
17
|
|
|
18
18
|
module Droonga
|
|
19
19
|
class DistributedCommandPlanner
|
|
20
|
-
attr_accessor :key
|
|
20
|
+
attr_accessor :key
|
|
21
21
|
|
|
22
22
|
REDUCE_SUM = "sum"
|
|
23
23
|
|
|
24
24
|
DEFAULT_LIMIT = -1
|
|
25
25
|
|
|
26
|
-
def initialize(source_message)
|
|
26
|
+
def initialize(dataset, source_message)
|
|
27
|
+
@dataset = dataset
|
|
27
28
|
@source_message = source_message
|
|
28
29
|
|
|
29
30
|
@key = nil
|
|
30
|
-
@dataset = nil
|
|
31
31
|
@outputs = []
|
|
32
32
|
|
|
33
33
|
@reducers = []
|
|
@@ -58,7 +58,7 @@ module Droonga
|
|
|
58
58
|
def scatter(record, options={})
|
|
59
59
|
@processor = {
|
|
60
60
|
"command" => @source_message["type"],
|
|
61
|
-
"dataset" => @dataset
|
|
61
|
+
"dataset" => @dataset.name,
|
|
62
62
|
"body" => options[:body] || @source_message["body"],
|
|
63
63
|
"record" => record,
|
|
64
64
|
"type" => "scatter",
|
|
@@ -71,7 +71,7 @@ module Droonga
|
|
|
71
71
|
def broadcast(options={})
|
|
72
72
|
processor = {
|
|
73
73
|
"command" => @source_message["type"],
|
|
74
|
-
"dataset" => @dataset
|
|
74
|
+
"dataset" => @dataset.name,
|
|
75
75
|
"body" => options[:body] || @source_message["body"],
|
|
76
76
|
"type" => "broadcast",
|
|
77
77
|
"outputs" => [],
|
data/lib/droonga/engine.rb
CHANGED
|
@@ -24,6 +24,7 @@ require "droonga/catalog_loader"
|
|
|
24
24
|
require "droonga/dispatcher"
|
|
25
25
|
require "droonga/file_observer"
|
|
26
26
|
require "droonga/live_nodes_list_loader"
|
|
27
|
+
require "droonga/node_status"
|
|
27
28
|
|
|
28
29
|
module Droonga
|
|
29
30
|
class Engine
|
|
@@ -39,6 +40,12 @@ module Droonga
|
|
|
39
40
|
@live_nodes_list_observer.on_change = lambda do
|
|
40
41
|
@state.live_nodes = load_live_nodes
|
|
41
42
|
end
|
|
43
|
+
@node_status_observer = FileObserver.new(loop, Path.node_status)
|
|
44
|
+
@node_status_observer.on_change = lambda do
|
|
45
|
+
logger.trace("reloading node_status: start")
|
|
46
|
+
node_status.reload
|
|
47
|
+
logger.trace("reloading node_status: done")
|
|
48
|
+
end
|
|
42
49
|
@on_ready = nil
|
|
43
50
|
end
|
|
44
51
|
|
|
@@ -49,6 +56,7 @@ module Droonga
|
|
|
49
56
|
end
|
|
50
57
|
@state.start
|
|
51
58
|
@live_nodes_list_observer.start
|
|
59
|
+
@node_status_observer.start
|
|
52
60
|
@dispatcher.start
|
|
53
61
|
logger.trace("start: done")
|
|
54
62
|
end
|
|
@@ -56,9 +64,10 @@ module Droonga
|
|
|
56
64
|
def stop_gracefully
|
|
57
65
|
logger.trace("stop_gracefully: start")
|
|
58
66
|
@live_nodes_list_observer.stop
|
|
67
|
+
@node_status_observer.stop
|
|
59
68
|
on_finish = lambda do
|
|
60
69
|
logger.trace("stop_gracefully/on_finish: start")
|
|
61
|
-
|
|
70
|
+
save_last_processed_message_timestamp
|
|
62
71
|
@dispatcher.stop_gracefully do
|
|
63
72
|
@state.shutdown
|
|
64
73
|
yield
|
|
@@ -78,8 +87,9 @@ module Droonga
|
|
|
78
87
|
# It may be called after stop_gracefully.
|
|
79
88
|
def stop_immediately
|
|
80
89
|
logger.trace("stop_immediately: start")
|
|
81
|
-
|
|
90
|
+
save_last_processed_message_timestamp
|
|
82
91
|
@live_nodes_list_observer.stop
|
|
92
|
+
@node_status_observer.stop
|
|
83
93
|
@dispatcher.stop_immediately
|
|
84
94
|
@state.shutdown
|
|
85
95
|
logger.trace("stop_immediately: done")
|
|
@@ -87,10 +97,14 @@ module Droonga
|
|
|
87
97
|
|
|
88
98
|
def process(message)
|
|
89
99
|
return unless effective_message?(message)
|
|
90
|
-
@
|
|
100
|
+
@last_processed_message_timestamp = message["date"]
|
|
91
101
|
@dispatcher.process_message(message)
|
|
92
102
|
end
|
|
93
103
|
|
|
104
|
+
def node_status
|
|
105
|
+
@node_status ||= NodeStatus.new
|
|
106
|
+
end
|
|
107
|
+
|
|
94
108
|
private
|
|
95
109
|
def load_catalog
|
|
96
110
|
catalog_path = Path.catalog
|
|
@@ -116,14 +130,10 @@ module Droonga
|
|
|
116
130
|
Dispatcher.new(@state, @catalog)
|
|
117
131
|
end
|
|
118
132
|
|
|
119
|
-
def
|
|
120
|
-
logger.trace("
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
path.open("w") do |file|
|
|
124
|
-
file.write(@last_processed_timestamp)
|
|
125
|
-
end
|
|
126
|
-
logger.trace("output_last_processed_timestamp: done")
|
|
133
|
+
def save_last_processed_message_timestamp
|
|
134
|
+
logger.trace("output_last_processed_message_timestamp: start")
|
|
135
|
+
node_status.set(:last_processed_message_timestamp, @last_processed_message_timestamp.to_s)
|
|
136
|
+
logger.trace("output_last_processed_message_timestamp: done")
|
|
127
137
|
end
|
|
128
138
|
|
|
129
139
|
def effective_message?(message)
|
|
@@ -131,17 +141,19 @@ module Droonga
|
|
|
131
141
|
return true if effective_timestamp.nil?
|
|
132
142
|
|
|
133
143
|
message_timestamp = Time.parse(message["date"])
|
|
144
|
+
logger.trace("checking effective_message_timestamp (#{effective_timestamp}) vs message_timestamp(message_timestamp)")
|
|
134
145
|
return false if effective_timestamp >= message_timestamp
|
|
135
146
|
|
|
136
|
-
|
|
147
|
+
logger.trace("deleting obsolete effective_message_timestamp: start")
|
|
148
|
+
node_status.delete(:effective_message_timestamp)
|
|
149
|
+
logger.trace("deleting obsolete effective_message_timestamp: done")
|
|
137
150
|
true
|
|
138
151
|
end
|
|
139
152
|
|
|
140
153
|
def effective_message_timestamp
|
|
141
|
-
|
|
142
|
-
return nil unless
|
|
154
|
+
timestamp = node_status.get(:effective_message_timestamp)
|
|
155
|
+
return nil unless timestamp
|
|
143
156
|
|
|
144
|
-
timestamp = path.read
|
|
145
157
|
begin
|
|
146
158
|
Time.parse(timestamp)
|
|
147
159
|
rescue ArgumentError
|
data/lib/droonga/node_status.rb
CHANGED
|
@@ -20,7 +20,7 @@ require "droonga/safe_file_writer"
|
|
|
20
20
|
module Droonga
|
|
21
21
|
class NodeStatus
|
|
22
22
|
def initialize
|
|
23
|
-
|
|
23
|
+
reload
|
|
24
24
|
end
|
|
25
25
|
|
|
26
26
|
def have?(key)
|
|
@@ -45,13 +45,17 @@ module Droonga
|
|
|
45
45
|
SafeFileWriter.write(status_file, JSON.pretty_generate(@status))
|
|
46
46
|
end
|
|
47
47
|
|
|
48
|
+
def reload
|
|
49
|
+
@status = load
|
|
50
|
+
end
|
|
51
|
+
|
|
48
52
|
private
|
|
49
53
|
def normalize_key(key)
|
|
50
54
|
key.to_sym
|
|
51
55
|
end
|
|
52
56
|
|
|
53
57
|
def status_file
|
|
54
|
-
@status_file ||= Path.
|
|
58
|
+
@status_file ||= Path.node_status
|
|
55
59
|
end
|
|
56
60
|
|
|
57
61
|
def load
|
data/lib/droonga/path.rb
CHANGED
|
@@ -34,34 +34,28 @@ module Droonga
|
|
|
34
34
|
ENV[BASE_DIR_ENV_NAME] = new_base
|
|
35
35
|
end
|
|
36
36
|
|
|
37
|
-
def databases
|
|
38
|
-
|
|
37
|
+
def databases(base_path=nil)
|
|
38
|
+
base_path ||= base
|
|
39
|
+
path = Pathname(base_path) + "databases"
|
|
40
|
+
path.expand_path
|
|
39
41
|
end
|
|
40
42
|
|
|
41
43
|
def state
|
|
42
44
|
base + "state"
|
|
43
45
|
end
|
|
44
46
|
|
|
45
|
-
def
|
|
46
|
-
state + "
|
|
47
|
-
end
|
|
48
|
-
|
|
49
|
-
def last_processed_timestamp
|
|
50
|
-
state + "last-processed.timestamp"
|
|
47
|
+
def node_status
|
|
48
|
+
state + "status_file"
|
|
51
49
|
end
|
|
52
50
|
|
|
53
|
-
def
|
|
54
|
-
state + "
|
|
51
|
+
def live_nodes
|
|
52
|
+
state + "live-nodes.json"
|
|
55
53
|
end
|
|
56
54
|
|
|
57
55
|
def config
|
|
58
56
|
base + "droonga-engine.yaml"
|
|
59
57
|
end
|
|
60
58
|
|
|
61
|
-
def default_pid_file
|
|
62
|
-
base + "droonga-engine.pid"
|
|
63
|
-
end
|
|
64
|
-
|
|
65
59
|
def default_log_file
|
|
66
60
|
base + "droonga-engine.log"
|
|
67
61
|
end
|
data/lib/droonga/planner.rb
CHANGED
|
@@ -22,7 +22,8 @@ module Droonga
|
|
|
22
22
|
include Loggable
|
|
23
23
|
include ErrorMessages
|
|
24
24
|
|
|
25
|
-
def initialize
|
|
25
|
+
def initialize(dataset)
|
|
26
|
+
@dataset = dataset
|
|
26
27
|
end
|
|
27
28
|
|
|
28
29
|
def plan(message)
|
|
@@ -31,14 +32,14 @@ module Droonga
|
|
|
31
32
|
|
|
32
33
|
private
|
|
33
34
|
def scatter(message, record, options={})
|
|
34
|
-
planner = DistributedCommandPlanner.new(message)
|
|
35
|
+
planner = DistributedCommandPlanner.new(@dataset, message)
|
|
35
36
|
planner.scatter(record)
|
|
36
37
|
planner.reduce(options[:reduce])
|
|
37
38
|
planner.plan
|
|
38
39
|
end
|
|
39
40
|
|
|
40
41
|
def broadcast(message, options={})
|
|
41
|
-
planner = DistributedCommandPlanner.new(message)
|
|
42
|
+
planner = DistributedCommandPlanner.new(@dataset, message)
|
|
42
43
|
planner.broadcast(:write => options[:write])
|
|
43
44
|
planner.reduce(options[:reduce])
|
|
44
45
|
planner.plan
|
|
@@ -21,6 +21,14 @@ module Droonga
|
|
|
21
21
|
@handler_class = handler_class
|
|
22
22
|
end
|
|
23
23
|
|
|
24
|
+
def change_schema?
|
|
25
|
+
configuration[:change_schema]
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def change_schema=(boolean)
|
|
29
|
+
configuration[:change_schema] = boolean
|
|
30
|
+
end
|
|
31
|
+
|
|
24
32
|
def synchronous?
|
|
25
33
|
configuration[:synchronous]
|
|
26
34
|
end
|
|
@@ -47,6 +47,28 @@ module Droonga
|
|
|
47
47
|
private
|
|
48
48
|
def list_columns(table_name)
|
|
49
49
|
table = @context[table_name]
|
|
50
|
+
virtual_columns(table) + real_columns(table)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def virtual_columns(table)
|
|
54
|
+
if not table.support_key? or table.domain.nil?
|
|
55
|
+
return []
|
|
56
|
+
end
|
|
57
|
+
[
|
|
58
|
+
[
|
|
59
|
+
table.id, # id
|
|
60
|
+
"_key", # name
|
|
61
|
+
"", # path
|
|
62
|
+
"", # type
|
|
63
|
+
"COLUMN_SCALAR", # flags
|
|
64
|
+
table.name, # domain
|
|
65
|
+
table.domain.name, # range
|
|
66
|
+
[], # source
|
|
67
|
+
]
|
|
68
|
+
]
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def real_columns(table)
|
|
50
72
|
table.columns.collect do |column|
|
|
51
73
|
format_column(column)
|
|
52
74
|
end
|
|
@@ -98,7 +120,7 @@ module Droonga
|
|
|
98
120
|
return [] unless column.is_a?(::Groonga::IndexColumn)
|
|
99
121
|
column.sources.collect do |source|
|
|
100
122
|
if source.is_a?(::Groonga::Table)
|
|
101
|
-
|
|
123
|
+
source.name
|
|
102
124
|
else
|
|
103
125
|
source.local_name
|
|
104
126
|
end
|
|
@@ -28,7 +28,7 @@ module Droonga
|
|
|
28
28
|
@result_name = @table + "_result"
|
|
29
29
|
|
|
30
30
|
output_columns = select_request["output_columns"] || "_id, _key, *"
|
|
31
|
-
attributes = output_columns
|
|
31
|
+
attributes = convert_output_columns(output_columns)
|
|
32
32
|
offset = (select_request["offset"] || "0").to_i
|
|
33
33
|
limit = (select_request["limit"] || "10").to_i
|
|
34
34
|
|
|
@@ -131,7 +131,7 @@ module Droonga
|
|
|
131
131
|
drilldown_keys = drilldown_keys.split(",")
|
|
132
132
|
|
|
133
133
|
sort_keys = (select_request["drilldown_sortby"] || "").split(",")
|
|
134
|
-
columns = (select_request["drilldown_output_columns"] || "_key,_nsubrecs")
|
|
134
|
+
columns = convert_output_columns(select_request["drilldown_output_columns"] || "_key,_nsubrecs")
|
|
135
135
|
offset = (select_request["drilldown_offset"] || "0").to_i
|
|
136
136
|
limit = (select_request["drilldown_limit"] || "10").to_i
|
|
137
137
|
|
|
@@ -165,6 +165,20 @@ module Droonga
|
|
|
165
165
|
end
|
|
166
166
|
queries
|
|
167
167
|
end
|
|
168
|
+
|
|
169
|
+
# for a backward compatibility for command_version=1,
|
|
170
|
+
# whitespace-separeted case (without functions) should be accepted.
|
|
171
|
+
COMMAND_VERSION_1_ONLY_PATTERN = /\A[^\s,()]+(\s+[^\s,()]+)+\z/
|
|
172
|
+
|
|
173
|
+
def convert_output_columns(output_columns)
|
|
174
|
+
output_columns = output_columns.strip
|
|
175
|
+
command_version_is_1 = output_columns =~ COMMAND_VERSION_1_ONLY_PATTERN
|
|
176
|
+
if command_version_is_1
|
|
177
|
+
output_columns.split(/\s+/)
|
|
178
|
+
else
|
|
179
|
+
output_columns.split(/\s*,\s*/)
|
|
180
|
+
end
|
|
181
|
+
end
|
|
168
182
|
end
|
|
169
183
|
|
|
170
184
|
class ResponseConverter
|
|
@@ -223,6 +237,7 @@ module Droonga
|
|
|
223
237
|
records.collect do |record|
|
|
224
238
|
record.collect.each_with_index do |value, i|
|
|
225
239
|
name, type = attributes[i]
|
|
240
|
+
_ = name # suppress a warning
|
|
226
241
|
case type
|
|
227
242
|
when "Time"
|
|
228
243
|
normalize_time(value).to_f
|
|
@@ -35,6 +35,8 @@ module Droonga
|
|
|
35
35
|
:result => false)
|
|
36
36
|
end
|
|
37
37
|
|
|
38
|
+
validate_key_type
|
|
39
|
+
|
|
38
40
|
options = parse_command
|
|
39
41
|
::Groonga::Schema.define(:context => @context) do |schema|
|
|
40
42
|
schema.create_table(name, options)
|
|
@@ -88,10 +90,33 @@ module Droonga
|
|
|
88
90
|
return unless @command["normalizer"]
|
|
89
91
|
options[:normalizer] = @command["normalizer"]
|
|
90
92
|
end
|
|
93
|
+
|
|
94
|
+
def validate_key_type
|
|
95
|
+
if @command.table_hash_key? and @command["key_type"].nil?
|
|
96
|
+
message = "key_type is required for TABLE_HASH_KEY table"
|
|
97
|
+
raise CommandError.new(:status => Status::INVALID_ARGUMENT,
|
|
98
|
+
:message => message,
|
|
99
|
+
:result => false)
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
if @command.table_pat_key? and @command["key_type"].nil?
|
|
103
|
+
message = "key_type is required for TABLE_PAT_KEY table"
|
|
104
|
+
raise CommandError.new(:status => Status::INVALID_ARGUMENT,
|
|
105
|
+
:message => message,
|
|
106
|
+
:result => false)
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
if @command.table_dat_key? and @command["key_type"].nil?
|
|
110
|
+
message = "key_type is required for TABLE_DAT_KEY table"
|
|
111
|
+
raise CommandError.new(:status => Status::INVALID_ARGUMENT,
|
|
112
|
+
:message => message,
|
|
113
|
+
:result => false)
|
|
114
|
+
end
|
|
115
|
+
end
|
|
91
116
|
end
|
|
92
117
|
|
|
93
118
|
class Handler < Droonga::Handler
|
|
94
|
-
action.
|
|
119
|
+
action.change_schema = true
|
|
95
120
|
|
|
96
121
|
def handle(message)
|
|
97
122
|
command = Command.new(@context)
|