aerospike 2.10.0 → 2.15.0
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/CHANGELOG.md +50 -1
- data/README.md +1 -1
- data/lib/aerospike.rb +20 -4
- data/lib/aerospike/aerospike_exception.rb +7 -1
- data/lib/aerospike/atomic/atomic.rb +1 -1
- data/lib/aerospike/bin.rb +1 -1
- data/lib/aerospike/cdt/hll_operation.rb +200 -0
- data/lib/aerospike/cdt/hll_policy.rb +34 -0
- data/lib/aerospike/cdt/hll_write_flags.rb +53 -0
- data/lib/aerospike/cdt/list_operation.rb +1 -1
- data/lib/aerospike/cdt/map_operation.rb +1 -1
- data/lib/aerospike/cdt/map_order.rb +1 -1
- data/lib/aerospike/cdt/map_policy.rb +1 -1
- data/lib/aerospike/cdt/map_return_type.rb +1 -1
- data/lib/aerospike/cdt/map_write_mode.rb +1 -1
- data/lib/aerospike/client.rb +34 -18
- data/lib/aerospike/cluster.rb +139 -17
- data/lib/aerospike/cluster/partition.rb +1 -1
- data/lib/aerospike/cluster/partition_parser.rb +169 -0
- data/lib/aerospike/cluster/rack_parser.rb +117 -0
- data/lib/aerospike/command/admin_command.rb +1 -1
- data/lib/aerospike/command/batch_direct_command.rb +2 -1
- data/lib/aerospike/command/batch_direct_exists_command.rb +1 -1
- data/lib/aerospike/command/batch_direct_node.rb +3 -3
- data/lib/aerospike/command/batch_index_command.rb +11 -2
- data/lib/aerospike/command/batch_index_node.rb +2 -2
- data/lib/aerospike/command/batch_item.rb +1 -1
- data/lib/aerospike/command/command.rb +168 -11
- data/lib/aerospike/command/delete_command.rb +21 -5
- data/lib/aerospike/command/execute_command.rb +1 -1
- data/lib/aerospike/command/exists_command.rb +21 -5
- data/lib/aerospike/command/field_type.rb +3 -1
- data/lib/aerospike/command/multi_command.rb +55 -5
- data/lib/aerospike/command/operate_command.rb +6 -1
- data/lib/aerospike/command/read_command.rb +63 -20
- data/lib/aerospike/command/read_header_command.rb +18 -6
- data/lib/aerospike/command/roles.rb +1 -1
- data/lib/aerospike/command/single_command.rb +9 -3
- data/lib/aerospike/command/touch_command.rb +48 -4
- data/lib/aerospike/command/unsupported_particle_type_validator.rb +1 -1
- data/lib/aerospike/command/write_command.rb +13 -4
- data/lib/aerospike/connection/create.rb +1 -1
- data/lib/aerospike/features.rb +3 -1
- data/lib/aerospike/geo_json.rb +70 -1
- data/lib/aerospike/host.rb +1 -1
- data/lib/aerospike/info.rb +1 -1
- data/lib/aerospike/key.rb +1 -1
- data/lib/aerospike/language.rb +1 -1
- data/lib/aerospike/node.rb +21 -7
- data/lib/aerospike/node/rebalance.rb +50 -0
- data/lib/aerospike/node/refresh/info.rb +4 -1
- data/lib/aerospike/node/refresh/partitions.rb +6 -15
- data/lib/aerospike/node/refresh/racks.rb +47 -0
- data/lib/aerospike/node/refresh/reset.rb +1 -0
- data/lib/aerospike/node/verify/rebalance_generation.rb +43 -0
- data/lib/aerospike/node_validator.rb +4 -19
- data/lib/aerospike/operation.rb +8 -1
- data/lib/aerospike/policy/admin_policy.rb +1 -1
- data/lib/aerospike/policy/batch_policy.rb +1 -1
- data/lib/aerospike/policy/client_policy.rb +16 -1
- data/lib/aerospike/policy/commit_level.rb +1 -1
- data/lib/aerospike/policy/consistency_level.rb +1 -1
- data/lib/aerospike/policy/generation_policy.rb +1 -1
- data/lib/aerospike/policy/operate_policy.rb +1 -1
- data/lib/aerospike/policy/policy.rb +64 -2
- data/lib/aerospike/policy/priority.rb +1 -1
- data/lib/aerospike/policy/query_policy.rb +8 -1
- data/lib/aerospike/policy/record_bin_multiplicity.rb +1 -1
- data/lib/aerospike/policy/record_exists_action.rb +1 -1
- data/lib/aerospike/policy/replica.rb +45 -0
- data/lib/aerospike/policy/scan_policy.rb +8 -1
- data/lib/aerospike/policy/write_policy.rb +1 -1
- data/lib/aerospike/query/filter.rb +1 -1
- data/lib/aerospike/query/pred_exp.rb +192 -0
- data/lib/aerospike/query/pred_exp/and_or.rb +32 -0
- data/lib/aerospike/query/pred_exp/geo_json_value.rb +41 -0
- data/lib/aerospike/query/pred_exp/integer_value.rb +32 -0
- data/lib/aerospike/query/pred_exp/op.rb +27 -0
- data/lib/aerospike/query/pred_exp/regex.rb +32 -0
- data/lib/aerospike/query/pred_exp/regex_flags.rb +23 -0
- data/lib/aerospike/query/pred_exp/string_value.rb +29 -0
- data/lib/aerospike/query/query_command.rb +27 -1
- data/lib/aerospike/query/recordset.rb +5 -5
- data/lib/aerospike/query/scan_command.rb +1 -1
- data/lib/aerospike/query/statement.rb +12 -3
- data/lib/aerospike/query/stream_command.rb +1 -1
- data/lib/aerospike/record.rb +1 -1
- data/lib/aerospike/result_code.rb +26 -7
- data/lib/aerospike/socket/base.rb +4 -3
- data/lib/aerospike/task/execute_task.rb +1 -1
- data/lib/aerospike/task/index_task.rb +1 -1
- data/lib/aerospike/task/task.rb +1 -1
- data/lib/aerospike/task/udf_register_task.rb +1 -1
- data/lib/aerospike/task/udf_remove_task.rb +1 -1
- data/lib/aerospike/ttl.rb +1 -1
- data/lib/aerospike/udf.rb +1 -1
- data/lib/aerospike/user_role.rb +1 -1
- data/lib/aerospike/utils/buffer.rb +14 -4
- data/lib/aerospike/utils/packer.rb +1 -1
- data/lib/aerospike/utils/pool.rb +1 -1
- data/lib/aerospike/utils/unpacker.rb +7 -2
- data/lib/aerospike/value/particle_type.rb +2 -2
- data/lib/aerospike/value/value.rb +106 -29
- data/lib/aerospike/version.rb +1 -1
- metadata +22 -8
- data/lib/aerospike/cluster/partition_tokenizer_new.rb +0 -130
- data/lib/aerospike/cluster/partition_tokenizer_old.rb +0 -135
data/lib/aerospike/client.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# Copyright 2014-
|
1
|
+
# Copyright 2014-2020 Aerospike, Inc.
|
2
2
|
#
|
3
3
|
# Portions may be licensed to Aerospike, Inc. under one or more contributor
|
4
4
|
# license agreements.
|
@@ -44,6 +44,8 @@ module Aerospike
|
|
44
44
|
attr_accessor :default_read_policy
|
45
45
|
attr_accessor :default_scan_policy
|
46
46
|
attr_accessor :default_write_policy
|
47
|
+
attr_accessor :default_operate_policy
|
48
|
+
attr_accessor :cluster
|
47
49
|
|
48
50
|
def initialize(hosts = nil, policy: ClientPolicy.new, connect: true)
|
49
51
|
|
@@ -224,8 +226,19 @@ module Aerospike
|
|
224
226
|
def truncate(namespace, set_name = nil, before_last_update = nil, options = {})
|
225
227
|
policy = create_policy(options, Policy, default_info_policy)
|
226
228
|
|
227
|
-
|
228
|
-
|
229
|
+
node = @cluster.random_node
|
230
|
+
conn = node.get_connection(policy.timeout)
|
231
|
+
|
232
|
+
if set_name && !set_name.to_s.strip.empty?
|
233
|
+
str_cmd = "truncate:namespace=#{namespace}"
|
234
|
+
str_cmd << ";set=#{set_name}" unless set_name.to_s.strip.empty?
|
235
|
+
else
|
236
|
+
if node.supports_feature(Aerospike::Features::TRUNCATE_NAMESPACE)
|
237
|
+
str_cmd = "truncate-namespace:namespace=#{namespace}"
|
238
|
+
else
|
239
|
+
str_cmd = "truncate:namespace=#{namespace}"
|
240
|
+
end
|
241
|
+
end
|
229
242
|
|
230
243
|
if before_last_update
|
231
244
|
lut_nanos = (before_last_update.to_f * 1_000_000_000.0).round
|
@@ -235,8 +248,7 @@ module Aerospike
|
|
235
248
|
str_cmd << ";lut=now"
|
236
249
|
end
|
237
250
|
|
238
|
-
|
239
|
-
response = send_info_command(policy, str_cmd).upcase
|
251
|
+
response = send_info_command(policy, str_cmd, node).upcase
|
240
252
|
return if response == 'OK'
|
241
253
|
raise Aerospike::Exceptions::Aerospike.new(Aerospike::ResultCode::SERVER_ERROR, "Truncate failed: #{response}")
|
242
254
|
end
|
@@ -322,11 +334,11 @@ module Aerospike
|
|
322
334
|
|
323
335
|
if policy.use_batch_direct
|
324
336
|
key_map = BatchItem.generate_map(keys)
|
325
|
-
execute_batch_direct_commands(keys) do |node, batch|
|
337
|
+
execute_batch_direct_commands(policy, keys) do |node, batch|
|
326
338
|
BatchDirectCommand.new(node, batch, policy, key_map, bin_names, results, info_flags)
|
327
339
|
end
|
328
340
|
else
|
329
|
-
execute_batch_index_commands(keys) do |node, batch|
|
341
|
+
execute_batch_index_commands(policy, keys) do |node, batch|
|
330
342
|
BatchIndexCommand.new(node, batch, policy, bin_names, results, info_flags)
|
331
343
|
end
|
332
344
|
end
|
@@ -351,11 +363,11 @@ module Aerospike
|
|
351
363
|
|
352
364
|
if policy.use_batch_direct
|
353
365
|
key_map = BatchItem.generate_map(keys)
|
354
|
-
execute_batch_direct_commands(keys) do |node, batch|
|
366
|
+
execute_batch_direct_commands(policy, keys) do |node, batch|
|
355
367
|
BatchDirectExistsCommand.new(node, batch, policy, key_map, results)
|
356
368
|
end
|
357
369
|
else
|
358
|
-
execute_batch_index_commands(keys) do |node, batch|
|
370
|
+
execute_batch_index_commands(policy, keys) do |node, batch|
|
359
371
|
BatchIndexExistsCommand.new(node, batch, policy, results)
|
360
372
|
end
|
361
373
|
end
|
@@ -372,7 +384,7 @@ module Aerospike
|
|
372
384
|
# read the result, all in one database call. Operations are executed in
|
373
385
|
# the order they are specified.
|
374
386
|
def operate(key, operations, options = nil)
|
375
|
-
policy = create_policy(options,
|
387
|
+
policy = create_policy(options, OperatePolicy, default_operate_policy)
|
376
388
|
|
377
389
|
command = OperateCommand.new(@cluster, policy, key, operations)
|
378
390
|
execute_command(command)
|
@@ -526,7 +538,7 @@ module Aerospike
|
|
526
538
|
raise Aerospike::Exceptions::Aerospike.new(Aerospike::ResultCode::SERVER_NOT_AVAILABLE, "Executing UDF failed because cluster is empty.")
|
527
539
|
end
|
528
540
|
|
529
|
-
|
541
|
+
statement = statement.clone
|
530
542
|
statement.set_aggregate_function(package_name, function_name, function_args, false)
|
531
543
|
|
532
544
|
# Use a thread per node
|
@@ -816,11 +828,16 @@ module Aerospike
|
|
816
828
|
self.default_query_policy = create_policy(policies[:query], QueryPolicy)
|
817
829
|
self.default_scan_policy = create_policy(policies[:scan], ScanPolicy)
|
818
830
|
self.default_write_policy = create_policy(policies[:write], WritePolicy)
|
831
|
+
self.default_operate_policy = create_policy(policies[:operate], OperatePolicy)
|
819
832
|
end
|
820
833
|
|
821
|
-
def send_info_command(policy, command)
|
834
|
+
def send_info_command(policy, command, node = nil)
|
822
835
|
Aerospike.logger.debug { "Sending info command: #{command}" }
|
823
|
-
|
836
|
+
if node
|
837
|
+
_, response = @cluster.request_node_info(node, policy, command).first
|
838
|
+
else
|
839
|
+
_, response = @cluster.request_info(policy, command).first
|
840
|
+
end
|
824
841
|
response.to_s
|
825
842
|
end
|
826
843
|
|
@@ -884,17 +901,16 @@ module Aerospike
|
|
884
901
|
command.execute
|
885
902
|
end
|
886
903
|
|
887
|
-
def execute_batch_index_commands(keys)
|
904
|
+
def execute_batch_index_commands(policy, keys)
|
888
905
|
if @cluster.nodes.empty?
|
889
906
|
raise Aerospike::Exceptions::Aerospike.new(Aerospike::ResultCode::SERVER_NOT_AVAILABLE, "Executing Batch Index command failed because cluster is empty.")
|
890
907
|
end
|
891
908
|
|
892
|
-
batch_nodes = BatchIndexNode.generate_list(@cluster, keys)
|
909
|
+
batch_nodes = BatchIndexNode.generate_list(@cluster, policy.replica, keys)
|
893
910
|
threads = []
|
894
911
|
|
895
912
|
batch_nodes.each do |batch|
|
896
913
|
threads << Thread.new do
|
897
|
-
Thread.current.abort_on_exception = true
|
898
914
|
command = yield batch.node, batch
|
899
915
|
execute_command(command)
|
900
916
|
end
|
@@ -903,12 +919,12 @@ module Aerospike
|
|
903
919
|
threads.each(&:join)
|
904
920
|
end
|
905
921
|
|
906
|
-
def execute_batch_direct_commands(keys)
|
922
|
+
def execute_batch_direct_commands(policy, keys)
|
907
923
|
if @cluster.nodes.empty?
|
908
924
|
raise Aerospike::Exceptions::Aerospike.new(Aerospike::ResultCode::SERVER_NOT_AVAILABLE, "Executing Batch Direct command failed because cluster is empty.")
|
909
925
|
end
|
910
926
|
|
911
|
-
batch_nodes = BatchDirectNode.generate_list(@cluster, keys)
|
927
|
+
batch_nodes = BatchDirectNode.generate_list(@cluster, policy.replica, keys)
|
912
928
|
threads = []
|
913
929
|
|
914
930
|
# Use a thread per namespace per node
|
data/lib/aerospike/cluster.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
# Copyright 2014-
|
3
|
+
# Copyright 2014-2020 Aerospike, Inc.
|
4
4
|
#
|
5
5
|
# Portions may be licensed to Aerospike, Inc. under one or more contributor
|
6
6
|
# license agreements.
|
@@ -28,6 +28,7 @@ module Aerospike
|
|
28
28
|
attr_reader :features, :tls_options
|
29
29
|
attr_reader :cluster_id, :aliases
|
30
30
|
attr_reader :cluster_name
|
31
|
+
attr_accessor :rack_aware, :rack_id
|
31
32
|
|
32
33
|
def initialize(policy, hosts)
|
33
34
|
@cluster_seeds = hosts
|
@@ -37,6 +38,10 @@ module Aerospike
|
|
37
38
|
@tend_interval = policy.tend_interval
|
38
39
|
@cluster_name = policy.cluster_name
|
39
40
|
@tls_options = policy.tls
|
41
|
+
@rack_aware = policy.rack_aware
|
42
|
+
@rack_id = policy.rack_id
|
43
|
+
|
44
|
+
@replica_index = Atomic.new(0)
|
40
45
|
|
41
46
|
@aliases = {}
|
42
47
|
@cluster_nodes = []
|
@@ -102,18 +107,128 @@ module Aerospike
|
|
102
107
|
(node_array.length > 0) && !@closed.value
|
103
108
|
end
|
104
109
|
|
105
|
-
|
106
|
-
|
110
|
+
# Returns a node on the cluster for read operations
|
111
|
+
def batch_read_node(partition, replica_policy)
|
112
|
+
case replica_policy
|
113
|
+
when Aerospike::Replica::MASTER, Aerospike::Replica::SEQUENCE
|
114
|
+
return master_node(partition)
|
115
|
+
when Aerospike::Replica::MASTER_PROLES
|
116
|
+
return master_proles_node(partition)
|
117
|
+
when Aerospike::Replica::PREFER_RACK
|
118
|
+
return rack_node(partition, seq)
|
119
|
+
when Aerospike::Replica::RANDOM
|
120
|
+
return random_node
|
121
|
+
else
|
122
|
+
raise Aerospike::Exceptions::InvalidNode("invalid policy.replica value")
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
# Returns a node on the cluster for read operations
|
127
|
+
def read_node(partition, replica_policy, seq)
|
128
|
+
case replica_policy
|
129
|
+
when Aerospike::Replica::MASTER
|
130
|
+
return master_node(partition)
|
131
|
+
when Aerospike::Replica::MASTER_PROLES
|
132
|
+
return master_proles_node(partition)
|
133
|
+
when Aerospike::Replica::PREFER_RACK
|
134
|
+
return rack_node(partition, seq)
|
135
|
+
when Aerospike::Replica::SEQUENCE
|
136
|
+
return sequence_node(partition, seq)
|
137
|
+
when Aerospike::Replica::RANDOM
|
138
|
+
return random_node
|
139
|
+
else
|
140
|
+
raise Aerospike::Exceptions::InvalidNode("invalid policy.replica value")
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
# Returns a node on the cluster for read operations
|
145
|
+
def master_node(partition)
|
146
|
+
partition_map = partitions
|
147
|
+
replica_array = partition_map[partition.namespace]
|
148
|
+
raise Aerospike::Exceptions::InvalidNamespace("namespace not found in the partition map") if !replica_array
|
149
|
+
|
150
|
+
node_array = (replica_array.get)[0]
|
151
|
+
raise Aerospike::Exceptions::InvalidNamespace("namespace not found in the partition map") if !node_array
|
152
|
+
|
153
|
+
node = (node_array.get)[partition.partition_id]
|
154
|
+
raise Aerospike::Exceptions::InvalidNode if !node || !node.active?
|
155
|
+
|
156
|
+
node
|
157
|
+
end
|
158
|
+
|
159
|
+
# Returns a node on the cluster
|
160
|
+
def rack_node(partition, seq)
|
161
|
+
partition_map = partitions
|
162
|
+
replica_array = partition_map[partition.namespace]
|
163
|
+
raise Aerospike::Exceptions::InvalidNamespace("namespace not found in the partition map") if !replica_array
|
164
|
+
|
165
|
+
replica_array = replica_array.get
|
166
|
+
|
167
|
+
is_retry = seq.value > -1
|
168
|
+
|
169
|
+
node = nil
|
170
|
+
fallback = nil
|
171
|
+
for i in 1..replica_array.length
|
172
|
+
idx = (seq.update{|v| v.succ} % replica_array.size).abs
|
173
|
+
node = (replica_array[idx].get)[partition.partition_id]
|
174
|
+
|
175
|
+
next if !node
|
176
|
+
|
177
|
+
fallback = node
|
178
|
+
|
179
|
+
# If fallback exists, do not retry on node where command failed,
|
180
|
+
# even if fallback is not on the same rack.
|
181
|
+
return fallback if is_retry && fallback && i == replica_array.length
|
182
|
+
|
183
|
+
return node if node && node.active? && node.has_rack(partition.namespace, @rack_id)
|
184
|
+
end
|
185
|
+
|
186
|
+
return fallback if fallback
|
187
|
+
|
188
|
+
raise Aerospike::Exceptions::InvalidNode
|
189
|
+
end
|
107
190
|
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
191
|
+
# Returns a node on the cluster for read operations
|
192
|
+
def master_proles_node(partition)
|
193
|
+
partition_map = partitions
|
194
|
+
replica_array = partition_map[partition.namespace]
|
195
|
+
raise Aerospike::Exceptions::InvalidNamespace("namespace not found in the partition map") if !replica_array
|
196
|
+
|
197
|
+
replica_array = replica_array.get
|
198
|
+
|
199
|
+
node = nil
|
200
|
+
for replica in replica_array
|
201
|
+
idx = (@replica_index.update{|v| v.succ} % replica_array.size).abs
|
202
|
+
node = (replica_array[idx].get)[partition.partition_id]
|
203
|
+
|
204
|
+
return node if node && node.active?
|
205
|
+
end
|
206
|
+
|
207
|
+
raise Aerospike::Exceptions::InvalidNode
|
208
|
+
end
|
209
|
+
|
210
|
+
# Returns a random node on the cluster
|
211
|
+
def sequence_node(partition, seq)
|
212
|
+
partition_map = partitions
|
213
|
+
replica_array = partition_map[partition.namespace]
|
214
|
+
raise Aerospike::Exceptions::InvalidNamespace("namespace not found in the partition map") if !replica_array
|
215
|
+
|
216
|
+
replica_array = replica_array.get
|
217
|
+
|
218
|
+
node = nil
|
219
|
+
for replica in replica_array
|
220
|
+
idx = (seq.update{|v| v.succ} % replica_array.size).abs
|
221
|
+
node = (replica_array[idx].get)[partition.partition_id]
|
112
222
|
|
113
223
|
return node if node && node.active?
|
114
224
|
end
|
115
225
|
|
116
|
-
|
226
|
+
raise Aerospike::Exceptions::InvalidNode
|
227
|
+
end
|
228
|
+
|
229
|
+
def get_node_for_key(replica_policy, key)
|
230
|
+
partition = Partition.new_by_key(key)
|
231
|
+
batch_read_node(partition, replica_policy)
|
117
232
|
end
|
118
233
|
|
119
234
|
# Returns a random node on the cluster
|
@@ -124,8 +239,8 @@ module Aerospike
|
|
124
239
|
i = 0
|
125
240
|
while i < length
|
126
241
|
# Must handle concurrency with other non-tending threads, so node_index is consistent.
|
127
|
-
|
128
|
-
node = node_array[
|
242
|
+
idx = (@node_index.update{ |v| v.succ } % node_array.length).abs
|
243
|
+
node = node_array[idx]
|
129
244
|
|
130
245
|
return node if node.active?
|
131
246
|
|
@@ -167,12 +282,9 @@ module Aerospike
|
|
167
282
|
end
|
168
283
|
end
|
169
284
|
|
170
|
-
def update_partitions(
|
171
|
-
nmap =
|
172
|
-
# update partition write map
|
285
|
+
def update_partitions(parser)
|
286
|
+
nmap = parser.update_partitions(partitions)
|
173
287
|
set_partitions(nmap) if nmap
|
174
|
-
|
175
|
-
Aerospike.logger.info("Partitions for node #{node.name} updated")
|
176
288
|
end
|
177
289
|
|
178
290
|
def request_info(policy, *commands)
|
@@ -183,6 +295,13 @@ module Aerospike
|
|
183
295
|
end
|
184
296
|
end
|
185
297
|
|
298
|
+
def request_node_info(node, policy, *commands)
|
299
|
+
conn = node.get_connection(policy.timeout)
|
300
|
+
Info.request(conn, *commands).tap do
|
301
|
+
node.put_connection(conn)
|
302
|
+
end
|
303
|
+
end
|
304
|
+
|
186
305
|
def supports_feature?(feature)
|
187
306
|
@features.get.include?(feature.to_s)
|
188
307
|
end
|
@@ -279,6 +398,7 @@ module Aerospike
|
|
279
398
|
|
280
399
|
nodes.each do |node|
|
281
400
|
node.refresh_partitions(peers) if node.partition_generation.changed?
|
401
|
+
node.refresh_racks if node.rebalance_generation.changed?
|
282
402
|
end
|
283
403
|
|
284
404
|
if peers.generation_changed? || !peers.use_peers?
|
@@ -441,8 +561,10 @@ module Aerospike
|
|
441
561
|
def find_node_in_partition_map(filter)
|
442
562
|
partitions_list = partitions
|
443
563
|
|
444
|
-
partitions_list.values.each do |
|
445
|
-
|
564
|
+
partitions_list.values.each do |replica_array|
|
565
|
+
replica_array.get.each do |node_array|
|
566
|
+
return true if node_array.value.any? { |node| node == filter }
|
567
|
+
end
|
446
568
|
end
|
447
569
|
false
|
448
570
|
end
|
@@ -0,0 +1,169 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Copyright 2014-2020 Aerospike, Inc.
|
4
|
+
#
|
5
|
+
# Portions may be licensed to Aerospike, Inc. under one or more contributor
|
6
|
+
# license agreements.
|
7
|
+
#
|
8
|
+
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
9
|
+
# use this file except in compliance with the License. You may obtain a copy of
|
10
|
+
# the License at http:#www.apache.org/licenses/LICENSE-2.0
|
11
|
+
#
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
14
|
+
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
15
|
+
# License for the specific language governing permissions and limitations under
|
16
|
+
# the License.
|
17
|
+
|
18
|
+
require 'base64'
|
19
|
+
|
20
|
+
module Aerospike
|
21
|
+
|
22
|
+
class PartitionParser #:nodoc:
|
23
|
+
|
24
|
+
attr_accessor :copied, :partition_generation
|
25
|
+
|
26
|
+
PARTITION_GENERATION = "partition-generation";
|
27
|
+
REPLICAS_ALL = "replicas-all";
|
28
|
+
|
29
|
+
def initialize(node, conn)
|
30
|
+
@node = node
|
31
|
+
@conn = conn
|
32
|
+
end
|
33
|
+
|
34
|
+
def update_partitions(current_map)
|
35
|
+
# Use low-level info methods and parse byte array directly for maximum performance.
|
36
|
+
# Receive format: replicas-all\t
|
37
|
+
# <ns1>:<count>,<base 64 encoded bitmap1>,<base 64 encoded bitmap2>...;
|
38
|
+
# <ns2>:<count>,<base 64 encoded bitmap1>,<base 64 encoded bitmap2>...;\n
|
39
|
+
info_map = Info.request(@conn, PARTITION_GENERATION, REPLICAS_ALL)
|
40
|
+
|
41
|
+
@partition_generation = info_map[PARTITION_GENERATION].to_i
|
42
|
+
|
43
|
+
info = info_map[REPLICAS_ALL]
|
44
|
+
if !info || info.length == 0
|
45
|
+
raise Aerospike::Exceptions::Connection.new("#{REPLICAS_ALL} response for node #{@node.name} is empty")
|
46
|
+
end
|
47
|
+
|
48
|
+
@buffer = info
|
49
|
+
@length = info.length
|
50
|
+
@offset = 0
|
51
|
+
|
52
|
+
new_map = nil
|
53
|
+
copied = false
|
54
|
+
beginning = @offset
|
55
|
+
|
56
|
+
while @offset < @length && @buffer[@offset] != '\n'
|
57
|
+
namespace = parse_name
|
58
|
+
replica_count = parse_replica_count
|
59
|
+
|
60
|
+
replica_array = current_map[namespace]
|
61
|
+
if !replica_array
|
62
|
+
if !copied
|
63
|
+
# Make shallow copy of map.
|
64
|
+
new_map = current_map.clone
|
65
|
+
copied = true
|
66
|
+
end
|
67
|
+
|
68
|
+
replica_array = Atomic.new(Array.new(replica_count))
|
69
|
+
new_map[namespace] = replica_array
|
70
|
+
end
|
71
|
+
|
72
|
+
for replica in 0...replica_count do
|
73
|
+
node_array = (replica_array.get)[replica]
|
74
|
+
|
75
|
+
if !node_array
|
76
|
+
if !copied
|
77
|
+
# Make shallow copy of map.
|
78
|
+
new_map = current_map.clone
|
79
|
+
copied = true
|
80
|
+
end
|
81
|
+
|
82
|
+
node_array = Atomic.new(Array.new(Aerospike::Node::PARTITIONS))
|
83
|
+
new_map[namespace].update{|v| v[replica] = node_array; v}
|
84
|
+
end
|
85
|
+
|
86
|
+
restore_buffer = parse_bitmap
|
87
|
+
i = 0
|
88
|
+
while i < Aerospike::Node::PARTITIONS
|
89
|
+
if (restore_buffer[i>>3].ord & (0x80 >> (i & 7))) != 0
|
90
|
+
node_array.update{|v| v[i] = @node; v}
|
91
|
+
end
|
92
|
+
i = i.succ
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
copied ? new_map : nil
|
98
|
+
end
|
99
|
+
|
100
|
+
private
|
101
|
+
|
102
|
+
def parse_name
|
103
|
+
beginning = @offset
|
104
|
+
while @offset < @length
|
105
|
+
break if @buffer[@offset] == ':'
|
106
|
+
@offset+=1
|
107
|
+
end
|
108
|
+
|
109
|
+
# Parse namespace.
|
110
|
+
namespace = @buffer[beginning...@offset].strip
|
111
|
+
|
112
|
+
if namespace.length <= 0 || namespace.length >= 32
|
113
|
+
response = get_truncated_response
|
114
|
+
raise Aerospike::Exceptions::Parse.new(
|
115
|
+
"Invalid partition namespace #{namespace}. Response=#{response}"
|
116
|
+
)
|
117
|
+
end
|
118
|
+
|
119
|
+
@offset+=1
|
120
|
+
namespace
|
121
|
+
end
|
122
|
+
|
123
|
+
def parse_replica_count
|
124
|
+
beginning = @offset
|
125
|
+
while @offset < @length
|
126
|
+
break if @buffer[@offset] == ','
|
127
|
+
@offset+=1
|
128
|
+
end
|
129
|
+
|
130
|
+
# Parse count
|
131
|
+
count = @buffer[beginning...@offset].strip.to_i
|
132
|
+
|
133
|
+
if count < 0 || count > 4096
|
134
|
+
response = get_truncated_response
|
135
|
+
raise Aerospike::Exceptions::Parse.new(
|
136
|
+
"Invalid partition count #{count}. Response=#{response}"
|
137
|
+
)
|
138
|
+
end
|
139
|
+
|
140
|
+
@offset+=1
|
141
|
+
count
|
142
|
+
end
|
143
|
+
|
144
|
+
def parse_bitmap
|
145
|
+
beginning = @offset
|
146
|
+
while @offset < @length
|
147
|
+
break if @buffer[@offset] == ','
|
148
|
+
break if @buffer[@offset] == ';'
|
149
|
+
@offset+=1
|
150
|
+
end
|
151
|
+
|
152
|
+
bit_map_length = @offset - beginning
|
153
|
+
restore_buffer = Base64.strict_decode64(@buffer[beginning, bit_map_length])
|
154
|
+
|
155
|
+
@offset+=1
|
156
|
+
restore_buffer
|
157
|
+
end
|
158
|
+
|
159
|
+
|
160
|
+
def get_truncated_response
|
161
|
+
max = @length
|
162
|
+
@length = max if @length > 200
|
163
|
+
@buffer[0...max]
|
164
|
+
end
|
165
|
+
|
166
|
+
|
167
|
+
end # class
|
168
|
+
|
169
|
+
end # module
|