droonga-engine 1.1.0 → 1.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (64) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +2 -1
  3. data/Rakefile +6 -0
  4. data/bin/droonga-engine-absorb-data +14 -14
  5. data/bin/droonga-engine-catalog-generate +24 -12
  6. data/bin/droonga-engine-catalog-modify +13 -7
  7. data/bin/droonga-engine-join +8 -8
  8. data/bin/droonga-engine-set-role +1 -1
  9. data/bin/droonga-engine-unjoin +2 -2
  10. data/lib/droonga/address.rb +3 -0
  11. data/lib/droonga/cluster.rb +16 -10
  12. data/lib/droonga/command/droonga_engine_service.rb +5 -2
  13. data/lib/droonga/command/remote_command_base.rb +3 -3
  14. data/lib/droonga/distributed_command_planner.rb +11 -1
  15. data/lib/droonga/engine.rb +12 -11
  16. data/lib/droonga/engine/version.rb +2 -2
  17. data/lib/droonga/engine_node.rb +28 -28
  18. data/lib/droonga/engine_state.rb +41 -36
  19. data/lib/droonga/forward_buffer.rb +21 -10
  20. data/lib/droonga/node_role.rb +2 -0
  21. data/lib/droonga/plugins/groonga/select.rb +3 -0
  22. data/lib/droonga/plugins/search.rb +3 -1
  23. data/lib/droonga/plugins/search/distributed_search_planner.rb +17 -5
  24. data/lib/droonga/plugins/system/statistics.rb +1 -0
  25. data/lib/droonga/searcher.rb +13 -4
  26. data/test/command/config/single_slice/catalog.json +38 -0
  27. data/test/command/config/single_slice/droonga-engine.yaml +4 -0
  28. data/test/command/run-test.rb +3 -2
  29. data/test/command/suite/catalog/fetch.expected.single_slice +50 -0
  30. data/test/command/suite/dump/column/index.expected.single_slice +86 -0
  31. data/test/command/suite/dump/column/scalar.expected.single_slice +52 -0
  32. data/test/command/suite/dump/column/vector.expected.single_slice +55 -0
  33. data/test/command/suite/dump/record/scalar.expected.single_slice +52 -0
  34. data/test/command/suite/dump/record/vector/reference.expected.single_slice +117 -0
  35. data/test/command/suite/dump/table/array.expected.single_slice +39 -0
  36. data/test/command/suite/dump/table/double_array_trie.expected.single_slice +40 -0
  37. data/test/command/suite/dump/table/hash.expected.single_slice +40 -0
  38. data/test/command/suite/dump/table/patricia_trie.expected.single_slice +40 -0
  39. data/test/command/suite/message/error/missing-dataset.test +3 -0
  40. data/test/command/suite/search/condition/query/nonexistent_column.expected.single_slice +26 -0
  41. data/test/command/suite/search/condition/query/syntax_error.expected.single_slice +26 -0
  42. data/test/command/suite/search/error/unknown-source.expected.single_slice +28 -0
  43. data/test/command/suite/search/output/attributes/invalid.expected.single_slice +24 -0
  44. data/test/command/suite/system/absorb-data/records.catalog.json.single_slice +44 -0
  45. data/test/command/suite/system/absorb-data/records.expected.single_slice +32 -0
  46. data/test/command/suite/system/statistics/object/count/per-volume/empty.test +1 -0
  47. data/test/command/suite/system/statistics/object/count/record.expected.single_slice +11 -0
  48. data/test/command/suite/system/statistics/object/count/schema.expected.single_slice +11 -0
  49. data/test/unit/catalog/test_generator.rb +3 -2
  50. data/test/unit/helper.rb +2 -1
  51. data/test/unit/helper/stub_serf.rb +28 -0
  52. data/test/unit/plugins/system/statistics/test_object_count.rb +135 -0
  53. data/test/unit/plugins/system/statistics/test_object_count_per_volume.rb +149 -0
  54. data/test/unit/plugins/test_basic.rb +0 -406
  55. data/test/unit/test_address.rb +111 -10
  56. data/test/unit/test_cluster.rb +232 -0
  57. data/test/unit/test_differ.rb +49 -0
  58. data/test/unit/test_engine_node.rb +556 -0
  59. data/test/unit/test_engine_state.rb +151 -0
  60. data/test/unit/test_forward_buffer.rb +106 -0
  61. data/test/unit/test_node_name.rb +160 -0
  62. data/test/unit/test_node_role.rb +53 -0
  63. data/test/unit/test_reducer.rb +525 -0
  64. metadata +111 -49
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: bd5aad47933f58aee93072477c535d458556b183
4
- data.tar.gz: fc6e82289c99cc3ef4cdc269d0153ca6588dec7f
3
+ metadata.gz: 695a658bbd48a05e64b9e53f2764549212c63786
4
+ data.tar.gz: 799ed768d48b6e49150fa63ca4ccc7f956837495
5
5
  SHA512:
6
- metadata.gz: 08909575ab516f97f404396904a53a0920bdc7a6ad7ab8673e7065d681698dee2f7029c71cb74731824892df0eeb87b8de4383d4aa9d0107823e7a00f363a7dc
7
- data.tar.gz: fc2acfa0c8c133bc70ebad99f7d326e96fcc316fdedfb45347431e5ec3a96a751d4f00db03b9c21c4c44d03d26b8da5b0263615926e9321d50f44c60277f0cf9
6
+ metadata.gz: a4141f7a2c89b0cc5558ea24fe11a52c6aa4ea93bf323065866befabc32ca2b9060f72dc3b5a4a5c9ef5e8204027d2e67df0ffbea5612011a414caee7d7a2a07
7
+ data.tar.gz: 50a6459b3f7db24bdc112536f88aac95c6b6e1c69423293135fe03d06a3cde96a686b54ffb112517bfc6aa6e2a9979d36f66999c0cfa4c78c2c71fbb466d52e0
@@ -4,7 +4,8 @@ notifications:
4
4
  env:
5
5
  - DEFAULT_TEST_TASK=test:unit
6
6
  - DEFAULT_TEST_TASK=test:command:default
7
- - DEFAULT_TEST_TASK=test:command:version1
7
+ - DEFAULT_TEST_TASK=test:command:single_slice
8
+ # - DEFAULT_TEST_TASK=test:command:version1
8
9
  rvm:
9
10
  - 1.9.3
10
11
  - 2.0.0
data/Rakefile CHANGED
@@ -50,6 +50,11 @@ namespace :test do
50
50
  run_command_test
51
51
  end
52
52
 
53
+ desc "Run command test: single slice"
54
+ task :single_slice do
55
+ run_command_test("--config", "single_slice")
56
+ end
57
+
53
58
  desc "Run command test: version1"
54
59
  task :version1 do
55
60
  run_command_test("--config", "version1")
@@ -61,6 +66,7 @@ desc "Run test"
61
66
  task :test => [
62
67
  "test:unit",
63
68
  "test:command:default",
69
+ "test:command:single_slice",
64
70
  "test:command:version1",
65
71
  ]
66
72
 
@@ -83,46 +83,46 @@ module Droonga
83
83
  parser.version = Engine::VERSION
84
84
 
85
85
  parser.separator("")
86
- parser.separator("Destination node:")
86
+ parser.separator("Destination:")
87
87
  parser.on("--host=HOST",
88
- "Host name of the destination node.") do |host|
88
+ "Host name of the destination engine node to copy data.") do |host|
89
89
  options.host = host
90
90
  end
91
91
  parser.on("--port=PORT", Integer,
92
- "Port number of the destination node.",
92
+ "Port number to communicate with the destination engine node.",
93
93
  "(#{options.port})") do |port|
94
94
  options.port = port
95
95
  end
96
96
  parser.on("--tag=TAG", Integer,
97
- "Tag name of the destination node.",
97
+ "Tag name to communicate with the destination engine node.",
98
98
  "(#{options.tag})") do |tag|
99
99
  options.tag = tag
100
100
  end
101
101
  parser.on("--dataset=DATASET",
102
- "Name of the destination dataset.",
102
+ "Name of the destination dataset for copying data.",
103
103
  "(#{options.dataset})") do |dataset|
104
104
  options.dataset = dataset
105
105
  end
106
106
 
107
107
  parser.separator("")
108
- parser.separator("Source node:")
108
+ parser.separator("Source:")
109
109
  parser.on("--source-host=HOST",
110
- "Host name of the source node.",
110
+ "Host name of the soruce engine node to copy data.",
111
111
  "(#{options.source_host})") do |host|
112
112
  options.source_host = host
113
113
  end
114
114
  parser.on("--source-port=PORT", Integer,
115
- "Port number of the source node.",
115
+ "Port number to communicate with the soruce engine node.",
116
116
  "(#{options.source_port})") do |host|
117
117
  options.source_host = host
118
118
  end
119
119
  parser.on("--source-tag=TAG",
120
- "Tag name of the source node.",
120
+ "Tag name to communicate with the soruce engine node.",
121
121
  "(#{options.source_tag})") do |tag|
122
122
  options.source_tag = tag
123
123
  end
124
124
  parser.on("--source-dataset=DATASET",
125
- "Name of the source dataset.",
125
+ "Name of the soruce dataset for copying data.",
126
126
  "(#{options.source_dataset})") do |dataset|
127
127
  options.source_dataset = dataset
128
128
  end
@@ -130,7 +130,7 @@ module Droonga
130
130
  parser.separator("")
131
131
  parser.separator("Connection:")
132
132
  parser.on("--receiver-host=HOST",
133
- "Host name of this computer.",
133
+ "Host name of the computer you are running this command.",
134
134
  "(#{options.receiver_host})") do |host|
135
135
  options.receiver_host = host
136
136
  end
@@ -138,18 +138,18 @@ module Droonga
138
138
  parser.separator("")
139
139
  parser.separator("Miscellaneous:")
140
140
  parser.on("--records-per-second=N", Integer,
141
- "Maximum number of records per second to be absorbed.",
141
+ "Maximum number of records to be copied per one second.",
142
142
  "'#{Client::RateLimiter::NO_LIMIT}' means no limit.",
143
143
  "(#{options.messages_per_second})") do |n|
144
144
  options.messages_per_second = n
145
145
  end
146
146
  parser.on("--progress-interval-seconds=N", Integer,
147
- "Interval seconds to report progress.",
147
+ "Interval seconds to report progress of data copying.",
148
148
  "(#{options.progress_interval_seconds})") do |n|
149
149
  options.progress_interval_seconds = n
150
150
  end
151
151
  parser.on("--[no-]verbose",
152
- "Output details for internal operations.",
152
+ "Output details for internal operations or not.",
153
153
  "(#{options.verbose})") do |verbose|
154
154
  options.verbose = verbose
155
155
  end
@@ -43,59 +43,71 @@ end
43
43
  parser = OptionParser.new
44
44
  parser.version = Droonga::Engine::VERSION
45
45
  parser.on("--output=PATH",
46
- "Output catalog.json to PATH.",
46
+ "The output path of generated catalog.json to be saved as.",
47
47
  "\"-\" means the standard output.",
48
+ "Any existing file at the specified path will be overwritten without confirmation.",
48
49
  "(#{options.output_path})") do |path|
49
50
  options.output_path = path
50
51
  end
51
52
  parser.on("--dataset=NAME",
52
- "Add a dataset its name is NAME.",
53
- "And set the NAME to the current dataset.",
53
+ "The name of a new dataset.",
54
+ "This can be specified multiple times to define multiple datasets.",
54
55
  "(#{Droonga::Catalog::Generator::DEFAULT_DATASET})") do |name|
55
56
  current_dataset = datasets[name] = {}
56
57
  end
57
58
  parser.on("--n-workers=N", Integer,
58
- "Use N workers for the current dataset.",
59
+ "Number of workers for each volume in the dataset ",
60
+ "specified by the preceding --dataset option.",
59
61
  "(#{Droonga::Catalog::Generator::DEFAULT_N_WORKERS})") do |n|
60
62
  current_dataset[:n_workers] = n
61
63
  end
62
64
  parser.on("--hosts=NAME1,NAME2,...", Array,
63
- "Use given hosts for replicas of the current dataset.",
65
+ "Host names of engine nodes to be used as replicas in the dataset ",
66
+ "specified by the preceding --dataset option.",
64
67
  "(#{Droonga::Catalog::Generator::DEFAULT_HOSTS.join(",")})") do |hosts|
65
68
  current_dataset[:hosts] = hosts
66
69
  end
67
70
  parser.on("--port=PORT", Integer,
68
- "Use the PORT as the port for the current dataset.",
71
+ "Port number to communicate with engine nodes in the dataset ",
72
+ "specified by the preceding --dataset option.",
69
73
  "(#{Droonga::Catalog::Generator::DEFAULT_PORT})") do |port|
70
74
  current_dataset[:port] = port
71
75
  end
72
76
  parser.on("--tag=TAG",
73
- "Use the TAG as the tag for the current dataset.",
77
+ "Tag name to communicate with engine nodes in the dataset ",
78
+ "specified by the preceding --dataset option.",
74
79
  "(#{Droonga::Catalog::Generator::DEFAULT_TAG})") do |tag|
75
80
  current_dataset[:tag] = tag
76
81
  end
77
82
  parser.on("--n-slices=N", Integer,
78
- "Use N slices for each replica.",
83
+ "Number of slices for each replica in the dataset ",
84
+ "specified by the preceding --dataset option.",
79
85
  "(#{Droonga::Catalog::Generator::DEFAULT_N_SLICES})") do |n|
80
86
  current_dataset[:n_slices] = n
81
87
  end
82
88
  parser.on("--plugins=PLUGIN1,PLUGIN2,...", Array,
83
- "Use PLUGINS for the current dataset.",
89
+ "Plugin names activated for the dataset ",
90
+ "specified by the preceding --dataset option.",
84
91
  "(#{Droonga::Catalog::Generator::DEFAULT_PLUGINS.join(",")})") do |plugins|
85
92
  current_dataset[:plugins] = plugins
86
93
  end
87
94
  parser.on("--schema=PATH",
88
- "Use schema in JSON at PATH for the current dataset.") do |path|
95
+ "The path to a JSON file including schema definition for the dataset ",
96
+ "specified by the preceding --dataset option.") do |path|
89
97
  File.open(path) do |input|
90
98
  current_dataset[:schema] = JSON.parse(input.read)
91
99
  end
92
100
  end
93
101
  parser.on("--fact=TABLE",
94
- "Use TABLE as the fact table for the current dataset.") do |table|
102
+ "Name of the fact table in the dataset ",
103
+ "specified by the preceding --dataset option.") do |table|
95
104
  current_dataset[:fact] = table
96
105
  end
97
106
  parser.on("--replicas=PATH",
98
- "Use replicas in JSON at PATH for the current dataset.") do |path|
107
+ "The path to a JSON file including replicas definition for the dataset ",
108
+ "specified by the preceding --dataset option.",
109
+ "If this option is used, other options to define replicas in the dataset ",
110
+ "(--hosts, --port, --tag and --n-slices) are ignored.") do |path|
99
111
  File.open(path) do |input|
100
112
  current_dataset[:replicas] = JSON.parse(input.read)
101
113
  end
@@ -46,14 +46,15 @@ end
46
46
  parser = OptionParser.new
47
47
  parser.version = Droonga::Engine::VERSION
48
48
  parser.on("--source=PATH",
49
- "Path to an existing catalog.json.",
49
+ "The path to the catalog.json to be modified.",
50
50
  "\"-\" means the standard input.",
51
51
  "(#{options.source_path})") do |path|
52
52
  options.source_path = path
53
53
  end
54
54
  parser.on("--output=PATH",
55
- "Output catalog.json to PATH.",
55
+ "The output path of modified catalog.json to be saved as.",
56
56
  "\"-\" means the standard output.",
57
+ "Any existing file at the specified path will be overwritten without confirmation.",
57
58
  "(#{options.output_path})") do |path|
58
59
  options.output_path = path
59
60
  end
@@ -63,21 +64,26 @@ parser.on("--[no-]update",
63
64
  options.update = update
64
65
  end
65
66
  parser.on("--dataset=NAME",
66
- "Add a dataset its name is NAME.",
67
- "And set the NAME to the current dataset.",
67
+ "The name of an existing dataset to be modified.",
68
+ "This can be specified multiple times to modify multiple datasets.",
68
69
  "(#{Droonga::Catalog::Generator::DEFAULT_DATASET})") do |name|
69
70
  current_dataset = datasets[name] = {}
70
71
  end
71
72
  parser.on("--replica-hosts=NAME1,NAME2,...", Array,
72
- "Use given hosts as replicas for the current dataset.") do |hosts|
73
+ "Host names of engine nodes to be used as replicas in the dataset ",
74
+ "specified by the preceding --dataset option.",
75
+ "If you specify this option, all existing replica nodes ",
76
+ "defined in the dataset are replaced.") do |hosts|
73
77
  current_dataset[:replica_hosts] = hosts
74
78
  end
75
79
  parser.on("--add-replica-hosts=NAME1,NAME2,...", Array,
76
- "Use given hosts to be added as replicas to the current dataset.") do |hosts|
80
+ "Host names of engine nodes to be added to the cluster as replicas, ",
81
+ "in the dataset specified by the preceding --dataset option.") do |hosts|
77
82
  current_dataset[:add_replica_hosts] = hosts
78
83
  end
79
84
  parser.on("--remove-replica-hosts=NAME1,NAME2,...", Array,
80
- "Use given hosts to be removed as replicas from the current dataset.") do |hosts|
85
+ "Host names of engine nodes to be removed from the cluster, ",
86
+ "in the dataset specified by the preceding --dataset option.") do |hosts|
81
87
  current_dataset[:remove_replica_hosts] = hosts
82
88
  end
83
89
  parser.parse!(ARGV)
@@ -92,7 +92,7 @@ module Droonga
92
92
  private
93
93
  def parse_options
94
94
  options = Slop.parse(:help => true) do |option|
95
- option.on("no-copy", "Don't copy data from the source cluster.",
95
+ option.on("no-copy", "Don't copy data from the source node.",
96
96
  :default => false)
97
97
 
98
98
  option.separator("Target:")
@@ -100,33 +100,33 @@ module Droonga
100
100
  "Host name of the new node to be joined.",
101
101
  :required => true)
102
102
  option.on("replica-source-host=",
103
- "Host name of the soruce node in the cluster to be connected.",
103
+ "Host name of the soruce node in the cluster to join.",
104
104
  :required => true)
105
105
 
106
106
  option.on(:port=,
107
- "Port number of the source cluster to be connected.",
107
+ "Port number to communicate with engine nodes.",
108
108
  :as => Integer,
109
109
  :default => NodeName::DEFAULT_PORT)
110
110
  option.on(:tag=,
111
- "Tag name of the soruce cluster to be connected.",
111
+ "Tag name to communicate with engine nodes.",
112
112
  :default => NodeName::DEFAULT_TAG)
113
113
  option.on(:dataset=,
114
- "Dataset name of for the node to be joined.",
114
+ "Dataset name the node is going to join as a replica in.",
115
115
  :default => Catalog::Dataset::DEFAULT_NAME)
116
116
 
117
117
  option.separator("Connections:")
118
118
  option.on("receiver-host=",
119
- "Host name of this host.",
119
+ "Host name of the computer you are running this command.",
120
120
  :default => Socket.gethostname)
121
121
 
122
122
  option.separator("Miscellaneous:")
123
123
  option.on("records-per-second=",
124
- "Maximum number of records per second to be copied. " +
124
+ "Maximum number of records to be copied per one second. " +
125
125
  "'#{Client::RateLimiter::NO_LIMIT}' means no limit.",
126
126
  :as => Integer,
127
127
  :default => DataAbsorberClient::DEFAULT_MESSAGES_PER_SECOND)
128
128
  option.on("progress-interval-seconds=",
129
- "Interval seconds to report progress.",
129
+ "Interval seconds to report progress of data copying.",
130
130
  :as => Integer,
131
131
  :default => DataAbsorberClient::DEFAULT_PROGRESS_INTERVAL_SECONDS)
132
132
  option.on(:verbose, "Output details for internal operations.",
@@ -23,7 +23,7 @@ module Droonga
23
23
  def run
24
24
  parse_options do |option|
25
25
  option.on(:role=,
26
- "New role for the target node.",
26
+ "New role for the engine node.",
27
27
  :required => true)
28
28
  end
29
29
 
@@ -31,10 +31,10 @@ module Droonga
31
31
  def run
32
32
  parse_options do |option|
33
33
  option.on("receiver-host=",
34
- "Host name of this host.",
34
+ "Host name of the computer you are running this command.",
35
35
  :default => Socket.gethostname)
36
36
  option.on(:dataset=,
37
- "Dataset name of for the node to be unjoined.",
37
+ "Dataset name the node is going to be removed from.",
38
38
  :default => Catalog::Dataset::DEFAULT_NAME)
39
39
  end
40
40
 
@@ -68,6 +68,9 @@ module Droonga
68
68
  end
69
69
 
70
70
  def ==(other)
71
+ if other.is_a?(String)
72
+ return to_s == other
73
+ end
71
74
  other.is_a?(self.class) and to_a == other.to_a
72
75
  end
73
76
  end
@@ -17,6 +17,7 @@ require "droonga/loggable"
17
17
  require "droonga/changable"
18
18
  require "droonga/path"
19
19
  require "droonga/file_observer"
20
+ require "droonga/address"
20
21
  require "droonga/engine_node"
21
22
  require "droonga/differ"
22
23
 
@@ -63,12 +64,15 @@ module Droonga
63
64
 
64
65
  attr_accessor :catalog
65
66
 
66
- def initialize(loop, params)
67
- @loop = loop
67
+ def initialize(params)
68
+ @loop = params[:loop]
68
69
 
69
70
  @params = params
70
71
  @catalog = params[:catalog]
71
- @state = nil
72
+ @state = params[:state] || {}
73
+
74
+ @engine_nodes = nil
75
+ @on_change = nil
72
76
 
73
77
  reload
74
78
  end
@@ -158,7 +162,7 @@ module Droonga
158
162
 
159
163
  def forward(message, destination)
160
164
  receiver = destination["to"]
161
- receiver_node_name = receiver.match(/\A[^:]+:\d+\/[^.]+/).to_s
165
+ receiver_node_name = Address.parse(receiver).node
162
166
  raise NotStartedYet.new unless @engine_nodes
163
167
  @engine_nodes.each do |node|
164
168
  if node.name == receiver_node_name
@@ -215,21 +219,23 @@ module Droonga
215
219
  end
216
220
 
217
221
  def all_node_names
218
- raise NoCatalogLoaded.new unless @catalog
219
222
  @catalog.all_nodes
220
223
  end
221
224
 
222
225
  def create_engine_nodes
223
226
  all_node_names.collect do |name|
224
227
  node_state = @state[name] || {}
225
- EngineNode.new(@loop,
226
- name,
227
- node_state,
228
- :auto_close_timeout =>
229
- @params[:internal_connection_lifetime])
228
+ create_engine_node(:name => name,
229
+ :state => node_state)
230
230
  end
231
231
  end
232
232
 
233
+ def create_engine_node(params)
234
+ EngineNode.new(params.merge(:loop => @loop,
235
+ :auto_close_timeout =>
236
+ @params[:internal_connection_lifetime]))
237
+ end
238
+
233
239
  def log_tag
234
240
  "cluster_state"
235
241
  end
@@ -157,8 +157,11 @@ module Droonga
157
157
  end
158
158
 
159
159
  def run_engine
160
- @engine = Engine.new(@loop, @engine_name, @internal_engine_name,
161
- :internal_connection_lifetime => @internal_connection_lifetime)
160
+ @engine = Engine.new(:loop => @loop,
161
+ :name => @engine_name,
162
+ :internal_name => @internal_engine_name,
163
+ :internal_connection_lifetime =>
164
+ @internal_connection_lifetime)
162
165
  @engine.on_ready = lambda do
163
166
  @worker_process_agent.ready
164
167
  end
@@ -31,14 +31,14 @@ module Droonga
31
31
 
32
32
  option.separator("Connections:")
33
33
  option.on(:host=,
34
- "Host name of the target node.",
34
+ "Host name of the node to be operated.",
35
35
  :required => true)
36
36
  option.on(:port=,
37
- "Port number of the source cluster to be connected.",
37
+ "Port number to communicate with the engine node.",
38
38
  :as => Integer,
39
39
  :default => NodeName::DEFAULT_PORT)
40
40
  option.on(:tag=,
41
- "Tag name of the soruce cluster to be connected.",
41
+ "Tag name to communicate with the engine node.",
42
42
  :default => NodeName::DEFAULT_TAG)
43
43
 
44
44
  option.separator("Miscellaneous:")