aerospike 2.12.0 → 2.13.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 (89) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +10 -0
  3. data/lib/aerospike.rb +3 -4
  4. data/lib/aerospike/aerospike_exception.rb +7 -1
  5. data/lib/aerospike/atomic/atomic.rb +1 -1
  6. data/lib/aerospike/bin.rb +1 -1
  7. data/lib/aerospike/cdt/list_operation.rb +1 -1
  8. data/lib/aerospike/cdt/map_operation.rb +1 -1
  9. data/lib/aerospike/cdt/map_order.rb +1 -1
  10. data/lib/aerospike/cdt/map_policy.rb +1 -1
  11. data/lib/aerospike/cdt/map_return_type.rb +1 -1
  12. data/lib/aerospike/cdt/map_write_mode.rb +1 -1
  13. data/lib/aerospike/client.rb +9 -9
  14. data/lib/aerospike/cluster.rb +92 -17
  15. data/lib/aerospike/cluster/partition.rb +1 -1
  16. data/lib/aerospike/cluster/partition_parser.rb +169 -0
  17. data/lib/aerospike/command/admin_command.rb +1 -1
  18. data/lib/aerospike/command/batch_direct_command.rb +1 -1
  19. data/lib/aerospike/command/batch_direct_exists_command.rb +1 -1
  20. data/lib/aerospike/command/batch_direct_node.rb +3 -3
  21. data/lib/aerospike/command/batch_index_node.rb +2 -2
  22. data/lib/aerospike/command/batch_item.rb +1 -1
  23. data/lib/aerospike/command/command.rb +21 -4
  24. data/lib/aerospike/command/delete_command.rb +5 -1
  25. data/lib/aerospike/command/execute_command.rb +1 -1
  26. data/lib/aerospike/command/exists_command.rb +5 -1
  27. data/lib/aerospike/command/field_type.rb +2 -1
  28. data/lib/aerospike/command/multi_command.rb +5 -1
  29. data/lib/aerospike/command/operate_command.rb +6 -1
  30. data/lib/aerospike/command/read_command.rb +5 -1
  31. data/lib/aerospike/command/read_header_command.rb +5 -1
  32. data/lib/aerospike/command/roles.rb +1 -1
  33. data/lib/aerospike/command/single_command.rb +9 -3
  34. data/lib/aerospike/command/touch_command.rb +5 -1
  35. data/lib/aerospike/command/unsupported_particle_type_validator.rb +1 -1
  36. data/lib/aerospike/command/write_command.rb +5 -1
  37. data/lib/aerospike/connection/create.rb +1 -1
  38. data/lib/aerospike/features.rb +1 -1
  39. data/lib/aerospike/geo_json.rb +1 -1
  40. data/lib/aerospike/host.rb +1 -1
  41. data/lib/aerospike/info.rb +1 -1
  42. data/lib/aerospike/key.rb +1 -1
  43. data/lib/aerospike/language.rb +1 -1
  44. data/lib/aerospike/node.rb +3 -6
  45. data/lib/aerospike/node/refresh/partitions.rb +6 -15
  46. data/lib/aerospike/node_validator.rb +4 -19
  47. data/lib/aerospike/operation.rb +1 -1
  48. data/lib/aerospike/policy/admin_policy.rb +1 -1
  49. data/lib/aerospike/policy/batch_policy.rb +1 -1
  50. data/lib/aerospike/policy/client_policy.rb +1 -1
  51. data/lib/aerospike/policy/commit_level.rb +1 -1
  52. data/lib/aerospike/policy/consistency_level.rb +1 -1
  53. data/lib/aerospike/policy/generation_policy.rb +1 -1
  54. data/lib/aerospike/policy/operate_policy.rb +1 -1
  55. data/lib/aerospike/policy/policy.rb +12 -3
  56. data/lib/aerospike/policy/priority.rb +1 -1
  57. data/lib/aerospike/policy/query_policy.rb +8 -1
  58. data/lib/aerospike/policy/record_bin_multiplicity.rb +1 -1
  59. data/lib/aerospike/policy/record_exists_action.rb +1 -1
  60. data/lib/aerospike/policy/replica.rb +38 -0
  61. data/lib/aerospike/policy/scan_policy.rb +8 -1
  62. data/lib/aerospike/policy/write_policy.rb +1 -1
  63. data/lib/aerospike/query/filter.rb +1 -1
  64. data/lib/aerospike/query/query_command.rb +10 -1
  65. data/lib/aerospike/query/recordset.rb +1 -1
  66. data/lib/aerospike/query/scan_command.rb +1 -1
  67. data/lib/aerospike/query/statement.rb +1 -1
  68. data/lib/aerospike/query/stream_command.rb +1 -1
  69. data/lib/aerospike/record.rb +1 -1
  70. data/lib/aerospike/result_code.rb +1 -1
  71. data/lib/aerospike/socket/base.rb +1 -1
  72. data/lib/aerospike/task/execute_task.rb +1 -1
  73. data/lib/aerospike/task/index_task.rb +1 -1
  74. data/lib/aerospike/task/task.rb +1 -1
  75. data/lib/aerospike/task/udf_register_task.rb +1 -1
  76. data/lib/aerospike/task/udf_remove_task.rb +1 -1
  77. data/lib/aerospike/ttl.rb +1 -1
  78. data/lib/aerospike/udf.rb +1 -1
  79. data/lib/aerospike/user_role.rb +1 -1
  80. data/lib/aerospike/utils/buffer.rb +1 -1
  81. data/lib/aerospike/utils/packer.rb +1 -1
  82. data/lib/aerospike/utils/pool.rb +1 -1
  83. data/lib/aerospike/utils/unpacker.rb +1 -1
  84. data/lib/aerospike/value/particle_type.rb +1 -1
  85. data/lib/aerospike/value/value.rb +1 -1
  86. data/lib/aerospike/version.rb +1 -1
  87. metadata +6 -6
  88. data/lib/aerospike/cluster/partition_tokenizer_new.rb +0 -130
  89. data/lib/aerospike/cluster/partition_tokenizer_old.rb +0 -135
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e4509fc71b11809038c6ea0a8853e92de02fcade0f8e29723e7507a029f7a5e1
4
- data.tar.gz: 8fd24ad2f984e728c9e6137b7bf6527aaa07a1371c9d46539de7f066048b13e0
3
+ metadata.gz: 7bef523a7e7500d8e39cb80f3b9ee6831a5ddc0bd6d782685181cae6a6c6ada5
4
+ data.tar.gz: 760d626bffac6ed69bcdda048daf39b07a12a85264b67be0677ad5d552c5473c
5
5
  SHA512:
6
- metadata.gz: 41aaaabe27c7f9a9080fb33a10444bef59f5ba6482b775d6b812529b3cc4f4656665a4e7294d328df3048c18519fafd99ac448b333c7a0506c78305a685c579e
7
- data.tar.gz: adf16fbcb8247ac8724d1823a1072cf5796f52477fb5a9bbe84f17a6ad71402f33b3ba2db9777a8b907737d291b7e5d9a98fa6ec7595093c5fb2d0cf24915de8
6
+ metadata.gz: df19a0c2900463a35b73ba52bfae3bdb2c5f6cbdaba3ab17172219e96ad3de3878bbc1ff57403fbbffe0fb48cc147ee642297e2fe47c829d768e76b1e4dee0fa
7
+ data.tar.gz: 528ade6f9abfb9b9e266439fefa6606f2623aec2503ec6c5466a5c3f9ffd9656debea49ff434f715f042c3ae319546e80352aa36cdc925ed351cab2633c75c4b
@@ -2,6 +2,16 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file.
4
4
 
5
+ ## [2.13.0] - 2019-07-17
6
+
7
+ * **New Features**
8
+ * Adds support for replica policies.
9
+
10
+ * **Improvements**
11
+ * Remove support for "old" partition tokenizer.
12
+ * Refactor how partition parser is initialized and called.
13
+ * Adds support for 'replicas' and remove the old partition table queries from the server.
14
+
5
15
  ## [2.12.0] - 2019-04-21
6
16
 
7
17
  * **New Features**
@@ -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.
@@ -101,11 +101,10 @@ require 'aerospike/connection/create'
101
101
 
102
102
  require 'aerospike/cluster'
103
103
  require 'aerospike/cluster/create_connection'
104
- require 'aerospike/cluster/partition'
105
104
  require 'aerospike/cluster/find_nodes_to_remove'
106
105
  require 'aerospike/cluster/find_node'
107
- require 'aerospike/cluster/partition_tokenizer_new'
108
- require 'aerospike/cluster/partition_tokenizer_old'
106
+ require 'aerospike/cluster/partition'
107
+ require 'aerospike/cluster/partition_parser'
109
108
  require 'aerospike/node'
110
109
  require 'aerospike/node/generation'
111
110
  require 'aerospike/node/refresh/failed'
@@ -1,5 +1,5 @@
1
1
  # encoding: utf-8
2
- # Copyright 2014-2018 Aerospike, Inc.
2
+ # Copyright 2014-2020 Aerospike, Inc.
3
3
  #
4
4
  # Portions may be licensed to Aerospike, Inc. under one or more contributor
5
5
  # license agreements.
@@ -90,5 +90,11 @@ module Aerospike
90
90
  super(ResultCode::COMMAND_REJECTED, msg)
91
91
  end
92
92
  end
93
+
94
+ class InvalidNamespace < Aerospike
95
+ def initialize(msg=nil)
96
+ super(ResultCode::INVALID_NAMESPACE, msg)
97
+ end
98
+ end
93
99
  end
94
100
  end
@@ -1,5 +1,5 @@
1
1
  # encoding: utf-8
2
- # Copyright 2014-2017 Aerospike, Inc.
2
+ # Copyright 2014-2020 Aerospike, Inc.
3
3
  #
4
4
  # Licensed under the Apache License, Version 2.0 (the "License");
5
5
  # you may not use this file except in compliance with the License.
@@ -1,5 +1,5 @@
1
1
  # encoding: utf-8
2
- # Copyright 2014-2017 Aerospike, Inc.
2
+ # Copyright 2014-2020 Aerospike, Inc.
3
3
  #
4
4
  # Portions may be licensed to Aerospike, Inc. under one or more contributor
5
5
  # license agreements.
@@ -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.
@@ -322,11 +322,11 @@ module Aerospike
322
322
 
323
323
  if policy.use_batch_direct
324
324
  key_map = BatchItem.generate_map(keys)
325
- execute_batch_direct_commands(keys) do |node, batch|
325
+ execute_batch_direct_commands(policy, keys) do |node, batch|
326
326
  BatchDirectCommand.new(node, batch, policy, key_map, bin_names, results, info_flags)
327
327
  end
328
328
  else
329
- execute_batch_index_commands(keys) do |node, batch|
329
+ execute_batch_index_commands(policy, keys) do |node, batch|
330
330
  BatchIndexCommand.new(node, batch, policy, bin_names, results, info_flags)
331
331
  end
332
332
  end
@@ -351,11 +351,11 @@ module Aerospike
351
351
 
352
352
  if policy.use_batch_direct
353
353
  key_map = BatchItem.generate_map(keys)
354
- execute_batch_direct_commands(keys) do |node, batch|
354
+ execute_batch_direct_commands(policy, keys) do |node, batch|
355
355
  BatchDirectExistsCommand.new(node, batch, policy, key_map, results)
356
356
  end
357
357
  else
358
- execute_batch_index_commands(keys) do |node, batch|
358
+ execute_batch_index_commands(policy, keys) do |node, batch|
359
359
  BatchIndexExistsCommand.new(node, batch, policy, results)
360
360
  end
361
361
  end
@@ -884,12 +884,12 @@ module Aerospike
884
884
  command.execute
885
885
  end
886
886
 
887
- def execute_batch_index_commands(keys)
887
+ def execute_batch_index_commands(policy, keys)
888
888
  if @cluster.nodes.empty?
889
889
  raise Aerospike::Exceptions::Aerospike.new(Aerospike::ResultCode::SERVER_NOT_AVAILABLE, "Executing Batch Index command failed because cluster is empty.")
890
890
  end
891
891
 
892
- batch_nodes = BatchIndexNode.generate_list(@cluster, keys)
892
+ batch_nodes = BatchIndexNode.generate_list(@cluster, policy.replica, keys)
893
893
  threads = []
894
894
 
895
895
  batch_nodes.each do |batch|
@@ -902,12 +902,12 @@ module Aerospike
902
902
  threads.each(&:join)
903
903
  end
904
904
 
905
- def execute_batch_direct_commands(keys)
905
+ def execute_batch_direct_commands(policy, keys)
906
906
  if @cluster.nodes.empty?
907
907
  raise Aerospike::Exceptions::Aerospike.new(Aerospike::ResultCode::SERVER_NOT_AVAILABLE, "Executing Batch Direct command failed because cluster is empty.")
908
908
  end
909
909
 
910
- batch_nodes = BatchDirectNode.generate_list(@cluster, keys)
910
+ batch_nodes = BatchDirectNode.generate_list(@cluster, policy.replica, keys)
911
911
  threads = []
912
912
 
913
913
  # 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.
@@ -38,6 +38,8 @@ module Aerospike
38
38
  @cluster_name = policy.cluster_name
39
39
  @tls_options = policy.tls
40
40
 
41
+ @replica_index = Atomic.new(0)
42
+
41
43
  @aliases = {}
42
44
  @cluster_nodes = []
43
45
  @partition_write_map = {}
@@ -102,18 +104,92 @@ module Aerospike
102
104
  (node_array.length > 0) && !@closed.value
103
105
  end
104
106
 
105
- def get_node_for_key(key)
106
- partition = Partition.new_by_key(key)
107
+ # Returns a node on the cluster for read operations
108
+ def batch_read_node(partition, replica_policy)
109
+ case replica_policy
110
+ when Aerospike::Replica::MASTER, Aerospike::Replica::SEQUENCE
111
+ return master_node(partition)
112
+ when Aerospike::Replica::MASTER_PROLES
113
+ return master_proles_node(partition)
114
+ when Aerospike::Replica::RANDOM
115
+ return random_node
116
+ else
117
+ raise Aerospike::Exceptions::InvalidNode("invalid policy.replica value")
118
+ end
119
+ end
120
+
121
+ # Returns a node on the cluster for read operations
122
+ def read_node(partition, replica_policy, seq)
123
+ case replica_policy
124
+ when Aerospike::Replica::MASTER
125
+ return master_node(partition)
126
+ when Aerospike::Replica::MASTER_PROLES
127
+ return master_proles_node(partition)
128
+ when Aerospike::Replica::SEQUENCE
129
+ return sequence_node(partition, seq)
130
+ when Aerospike::Replica::RANDOM
131
+ return random_node
132
+ else
133
+ raise Aerospike::Exceptions::InvalidNode("invalid policy.replica value")
134
+ end
135
+ end
136
+
137
+ # Returns a node on the cluster for read operations
138
+ def master_node(partition)
139
+ partition_map = partitions
140
+ replica_array = partition_map[partition.namespace]
141
+ raise Aerospike::Exceptions::InvalidNamespace("namespace not found in the partition map") if !replica_array
142
+
143
+ node_array = (replica_array.get)[0]
144
+ raise Aerospike::Exceptions::InvalidNamespace("namespace not found in the partition map") if !node_array
145
+
146
+ node = (node_array.get)[partition.partition_id]
147
+ raise Aerospike::Exceptions::InvalidNode if !node || !node.active?
148
+
149
+ node
150
+ end
151
+
152
+ # Returns a node on the cluster for read operations
153
+ def master_proles_node(partition)
154
+ partition_map = partitions
155
+ replica_array = partition_map[partition.namespace]
156
+ raise Aerospike::Exceptions::InvalidNamespace("namespace not found in the partition map") if !replica_array
157
+
158
+ replica_array = replica_array.get
107
159
 
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]
160
+ node = nil
161
+ for replica in replica_array
162
+ idx = (@replica_index.update{|v| v.succ} % replica_array.size).abs
163
+ node = (replica_array[idx].get)[partition.partition_id]
112
164
 
113
165
  return node if node && node.active?
114
166
  end
115
167
 
116
- random_node
168
+ raise Aerospike::Exceptions::InvalidNode
169
+ end
170
+
171
+ # Returns a random node on the cluster
172
+ def sequence_node(partition, seq)
173
+ partition_map = partitions
174
+ replica_array = partition_map[partition.namespace]
175
+ raise Aerospike::Exceptions::InvalidNamespace("namespace not found in the partition map") if !replica_array
176
+
177
+ replica_array = replica_array.get
178
+
179
+ node = nil
180
+ for replica in replica_array
181
+ idx = (seq.update{|v| v.succ} % replica_array.size).abs
182
+ node = (replica_array[idx].get)[partition.partition_id]
183
+
184
+ return node if node && node.active?
185
+ end
186
+
187
+ raise Aerospike::Exceptions::InvalidNode
188
+ end
189
+
190
+ def get_node_for_key(replica_policy, key)
191
+ partition = Partition.new_by_key(key)
192
+ batch_read_node(partition, replica_policy)
117
193
  end
118
194
 
119
195
  # Returns a random node on the cluster
@@ -124,8 +200,8 @@ module Aerospike
124
200
  i = 0
125
201
  while i < length
126
202
  # 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]
203
+ idx = (@node_index.update{ |v| v.succ } % node_array.length).abs
204
+ node = node_array[idx]
129
205
 
130
206
  return node if node.active?
131
207
 
@@ -167,12 +243,9 @@ module Aerospike
167
243
  end
168
244
  end
169
245
 
170
- def update_partitions(tokens, node)
171
- nmap = tokens.update_partition(partitions, node)
172
- # update partition write map
246
+ def update_partitions(parser)
247
+ nmap = parser.update_partitions(partitions)
173
248
  set_partitions(nmap) if nmap
174
-
175
- Aerospike.logger.info("Partitions for node #{node.name} updated")
176
249
  end
177
250
 
178
251
  def request_info(policy, *commands)
@@ -441,8 +514,10 @@ module Aerospike
441
514
  def find_node_in_partition_map(filter)
442
515
  partitions_list = partitions
443
516
 
444
- partitions_list.values.each do |node_array|
445
- return true if node_array.value.any? { |node| node == filter }
517
+ partitions_list.values.each do |replica_array|
518
+ replica_array.get.each do |node_array|
519
+ return true if node_array.value.any? { |node| node == filter }
520
+ end
446
521
  end
447
522
  false
448
523
  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