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