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.
Files changed (108) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +50 -1
  3. data/README.md +1 -1
  4. data/lib/aerospike.rb +20 -4
  5. data/lib/aerospike/aerospike_exception.rb +7 -1
  6. data/lib/aerospike/atomic/atomic.rb +1 -1
  7. data/lib/aerospike/bin.rb +1 -1
  8. data/lib/aerospike/cdt/hll_operation.rb +200 -0
  9. data/lib/aerospike/cdt/hll_policy.rb +34 -0
  10. data/lib/aerospike/cdt/hll_write_flags.rb +53 -0
  11. data/lib/aerospike/cdt/list_operation.rb +1 -1
  12. data/lib/aerospike/cdt/map_operation.rb +1 -1
  13. data/lib/aerospike/cdt/map_order.rb +1 -1
  14. data/lib/aerospike/cdt/map_policy.rb +1 -1
  15. data/lib/aerospike/cdt/map_return_type.rb +1 -1
  16. data/lib/aerospike/cdt/map_write_mode.rb +1 -1
  17. data/lib/aerospike/client.rb +34 -18
  18. data/lib/aerospike/cluster.rb +139 -17
  19. data/lib/aerospike/cluster/partition.rb +1 -1
  20. data/lib/aerospike/cluster/partition_parser.rb +169 -0
  21. data/lib/aerospike/cluster/rack_parser.rb +117 -0
  22. data/lib/aerospike/command/admin_command.rb +1 -1
  23. data/lib/aerospike/command/batch_direct_command.rb +2 -1
  24. data/lib/aerospike/command/batch_direct_exists_command.rb +1 -1
  25. data/lib/aerospike/command/batch_direct_node.rb +3 -3
  26. data/lib/aerospike/command/batch_index_command.rb +11 -2
  27. data/lib/aerospike/command/batch_index_node.rb +2 -2
  28. data/lib/aerospike/command/batch_item.rb +1 -1
  29. data/lib/aerospike/command/command.rb +168 -11
  30. data/lib/aerospike/command/delete_command.rb +21 -5
  31. data/lib/aerospike/command/execute_command.rb +1 -1
  32. data/lib/aerospike/command/exists_command.rb +21 -5
  33. data/lib/aerospike/command/field_type.rb +3 -1
  34. data/lib/aerospike/command/multi_command.rb +55 -5
  35. data/lib/aerospike/command/operate_command.rb +6 -1
  36. data/lib/aerospike/command/read_command.rb +63 -20
  37. data/lib/aerospike/command/read_header_command.rb +18 -6
  38. data/lib/aerospike/command/roles.rb +1 -1
  39. data/lib/aerospike/command/single_command.rb +9 -3
  40. data/lib/aerospike/command/touch_command.rb +48 -4
  41. data/lib/aerospike/command/unsupported_particle_type_validator.rb +1 -1
  42. data/lib/aerospike/command/write_command.rb +13 -4
  43. data/lib/aerospike/connection/create.rb +1 -1
  44. data/lib/aerospike/features.rb +3 -1
  45. data/lib/aerospike/geo_json.rb +70 -1
  46. data/lib/aerospike/host.rb +1 -1
  47. data/lib/aerospike/info.rb +1 -1
  48. data/lib/aerospike/key.rb +1 -1
  49. data/lib/aerospike/language.rb +1 -1
  50. data/lib/aerospike/node.rb +21 -7
  51. data/lib/aerospike/node/rebalance.rb +50 -0
  52. data/lib/aerospike/node/refresh/info.rb +4 -1
  53. data/lib/aerospike/node/refresh/partitions.rb +6 -15
  54. data/lib/aerospike/node/refresh/racks.rb +47 -0
  55. data/lib/aerospike/node/refresh/reset.rb +1 -0
  56. data/lib/aerospike/node/verify/rebalance_generation.rb +43 -0
  57. data/lib/aerospike/node_validator.rb +4 -19
  58. data/lib/aerospike/operation.rb +8 -1
  59. data/lib/aerospike/policy/admin_policy.rb +1 -1
  60. data/lib/aerospike/policy/batch_policy.rb +1 -1
  61. data/lib/aerospike/policy/client_policy.rb +16 -1
  62. data/lib/aerospike/policy/commit_level.rb +1 -1
  63. data/lib/aerospike/policy/consistency_level.rb +1 -1
  64. data/lib/aerospike/policy/generation_policy.rb +1 -1
  65. data/lib/aerospike/policy/operate_policy.rb +1 -1
  66. data/lib/aerospike/policy/policy.rb +64 -2
  67. data/lib/aerospike/policy/priority.rb +1 -1
  68. data/lib/aerospike/policy/query_policy.rb +8 -1
  69. data/lib/aerospike/policy/record_bin_multiplicity.rb +1 -1
  70. data/lib/aerospike/policy/record_exists_action.rb +1 -1
  71. data/lib/aerospike/policy/replica.rb +45 -0
  72. data/lib/aerospike/policy/scan_policy.rb +8 -1
  73. data/lib/aerospike/policy/write_policy.rb +1 -1
  74. data/lib/aerospike/query/filter.rb +1 -1
  75. data/lib/aerospike/query/pred_exp.rb +192 -0
  76. data/lib/aerospike/query/pred_exp/and_or.rb +32 -0
  77. data/lib/aerospike/query/pred_exp/geo_json_value.rb +41 -0
  78. data/lib/aerospike/query/pred_exp/integer_value.rb +32 -0
  79. data/lib/aerospike/query/pred_exp/op.rb +27 -0
  80. data/lib/aerospike/query/pred_exp/regex.rb +32 -0
  81. data/lib/aerospike/query/pred_exp/regex_flags.rb +23 -0
  82. data/lib/aerospike/query/pred_exp/string_value.rb +29 -0
  83. data/lib/aerospike/query/query_command.rb +27 -1
  84. data/lib/aerospike/query/recordset.rb +5 -5
  85. data/lib/aerospike/query/scan_command.rb +1 -1
  86. data/lib/aerospike/query/statement.rb +12 -3
  87. data/lib/aerospike/query/stream_command.rb +1 -1
  88. data/lib/aerospike/record.rb +1 -1
  89. data/lib/aerospike/result_code.rb +26 -7
  90. data/lib/aerospike/socket/base.rb +4 -3
  91. data/lib/aerospike/task/execute_task.rb +1 -1
  92. data/lib/aerospike/task/index_task.rb +1 -1
  93. data/lib/aerospike/task/task.rb +1 -1
  94. data/lib/aerospike/task/udf_register_task.rb +1 -1
  95. data/lib/aerospike/task/udf_remove_task.rb +1 -1
  96. data/lib/aerospike/ttl.rb +1 -1
  97. data/lib/aerospike/udf.rb +1 -1
  98. data/lib/aerospike/user_role.rb +1 -1
  99. data/lib/aerospike/utils/buffer.rb +14 -4
  100. data/lib/aerospike/utils/packer.rb +1 -1
  101. data/lib/aerospike/utils/pool.rb +1 -1
  102. data/lib/aerospike/utils/unpacker.rb +7 -2
  103. data/lib/aerospike/value/particle_type.rb +2 -2
  104. data/lib/aerospike/value/value.rb +106 -29
  105. data/lib/aerospike/version.rb +1 -1
  106. metadata +22 -8
  107. data/lib/aerospike/cluster/partition_tokenizer_new.rb +0 -130
  108. data/lib/aerospike/cluster/partition_tokenizer_old.rb +0 -135
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Copyright 2016-2018 Aerospike, Inc.
3
+ # Copyright 2016-2020 Aerospike, Inc.
4
4
  #
5
5
  # Portions may be licensed to Aerospike, Inc. under one or more contributor
6
6
  # license agreements.
@@ -1,5 +1,5 @@
1
1
  # encoding: utf-8
2
- # Copyright 2016-2018 Aerospike, Inc.
2
+ # Copyright 2016-2020 Aerospike, Inc.
3
3
  #
4
4
  # Portions may be licensed to Aerospike, Inc. under one or more contributor
5
5
  # license agreements.
@@ -1,5 +1,5 @@
1
1
  # encoding: utf-8
2
- # Copyright 2016-2017 Aerospike, Inc.
2
+ # Copyright 2016-2020 Aerospike, Inc.
3
3
  #
4
4
  # Portions may be licensed to Aerospike, Inc. under one or more contributor
5
5
  # license agreements.
@@ -1,5 +1,5 @@
1
1
  # encoding: utf-8
2
- # Copyright 2016-2018 Aerospike, Inc.
2
+ # Copyright 2016-2020 Aerospike, Inc.
3
3
  #
4
4
  # Portions may be licensed to Aerospike, Inc. under one or more contributor
5
5
  # license agreements.
@@ -1,5 +1,5 @@
1
1
  # encoding: utf-8
2
- # Copyright 2016-2017 Aerospike, Inc.
2
+ # Copyright 2016-2020 Aerospike, Inc.
3
3
  #
4
4
  # Portions may be licensed to Aerospike, Inc. under one or more contributor
5
5
  # license agreements.
@@ -1,5 +1,5 @@
1
1
  # encoding: utf-8
2
- # Copyright 2016-2018 Aerospike, Inc.
2
+ # Copyright 2016-2020 Aerospike, Inc.
3
3
  #
4
4
  # Portions may be licensed to Aerospike, Inc. under one or more contributor
5
5
  # license agreements.
@@ -1,4 +1,4 @@
1
- # Copyright 2014-2018 Aerospike, Inc.
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
- str_cmd = "truncate:namespace=#{namespace}"
228
- str_cmd << ";set=#{set_name}" unless set_name.to_s.strip.empty?
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
- # Send index command to one node. That node will distribute the command to other nodes.
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, WritePolicy, default_write_policy)
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
- # TODO: wait until all migrations are finished
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
- _, response = @cluster.request_info(policy, command).first
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
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Copyright 2014-2018 Aerospike, Inc.
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
- def get_node_for_key(key)
106
- partition = Partition.new_by_key(key)
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
- # Must copy hashmap reference for copy on write semantics to work.
109
- nmap = partitions
110
- if node_array = nmap[partition.namespace]
111
- node = node_array.value[partition.partition_id]
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
- random_node
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
- index = (@node_index.update{ |v| v+1 } % node_array.length).abs
128
- node = node_array[index]
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(tokens, node)
171
- nmap = tokens.update_partition(partitions, node)
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 |node_array|
445
- return true if node_array.value.any? { |node| node == filter }
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
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Copyright 2014-2017 Aerospike, Inc.
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.
@@ -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