aerospike 2.6.0 → 2.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +9 -0
  3. data/README.md +3 -1
  4. data/lib/aerospike.rb +9 -5
  5. data/lib/aerospike/client.rb +101 -83
  6. data/lib/aerospike/cluster.rb +11 -50
  7. data/lib/aerospike/cluster/create_connection.rb +1 -1
  8. data/lib/aerospike/cluster/find_nodes_to_remove.rb +66 -0
  9. data/lib/aerospike/cluster/partition.rb +5 -2
  10. data/lib/aerospike/command/batch_direct_command.rb +104 -0
  11. data/lib/aerospike/command/batch_direct_exists_command.rb +51 -0
  12. data/lib/aerospike/command/batch_direct_node.rb +40 -0
  13. data/lib/aerospike/command/batch_index_command.rb +119 -0
  14. data/lib/aerospike/command/batch_index_exists_command.rb +45 -0
  15. data/lib/aerospike/command/batch_index_node.rb +52 -0
  16. data/lib/aerospike/command/batch_item.rb +18 -47
  17. data/lib/aerospike/command/command.rb +6 -65
  18. data/lib/aerospike/command/field_type.rb +13 -10
  19. data/lib/aerospike/command/{batch_command.rb → multi_command.rb} +29 -9
  20. data/lib/aerospike/command/read_command.rb +4 -2
  21. data/lib/aerospike/command/single_command.rb +6 -9
  22. data/lib/aerospike/connection/create.rb +3 -3
  23. data/lib/aerospike/host/parse.rb +28 -2
  24. data/lib/aerospike/node.rb +6 -2
  25. data/lib/aerospike/node/refresh/friends.rb +1 -1
  26. data/lib/aerospike/node/refresh/peers.rb +1 -1
  27. data/lib/aerospike/node_validator.rb +3 -3
  28. data/lib/aerospike/peers.rb +4 -0
  29. data/lib/aerospike/peers/parse.rb +26 -6
  30. data/lib/aerospike/policy/batch_policy.rb +25 -15
  31. data/lib/aerospike/policy/client_policy.rb +2 -2
  32. data/lib/aerospike/policy/query_policy.rb +25 -12
  33. data/lib/aerospike/policy/scan_policy.rb +39 -16
  34. data/lib/aerospike/query/stream_command.rb +6 -5
  35. data/lib/aerospike/record.rb +4 -3
  36. data/lib/aerospike/socket/ssl.rb +13 -13
  37. data/lib/aerospike/socket/tcp.rb +8 -1
  38. data/lib/aerospike/utils/string_parser.rb +7 -3
  39. data/lib/aerospike/version.rb +1 -1
  40. metadata +11 -7
  41. data/lib/aerospike/command/batch_command_exists.rb +0 -93
  42. data/lib/aerospike/command/batch_command_get.rb +0 -84
  43. data/lib/aerospike/command/batch_node.rb +0 -82
@@ -25,7 +25,7 @@ require 'aerospike/atomic/atomic'
25
25
  module Aerospike
26
26
  class Cluster
27
27
  attr_reader :connection_timeout, :connection_queue_size, :user, :password
28
- attr_reader :features, :ssl_options
28
+ attr_reader :features, :tls_options
29
29
  attr_reader :cluster_id, :aliases
30
30
  attr_reader :cluster_name
31
31
 
@@ -36,7 +36,7 @@ module Aerospike
36
36
  @connection_timeout = policy.timeout
37
37
  @tend_interval = policy.tend_interval
38
38
  @cluster_name = policy.cluster_name
39
- @ssl_options = policy.ssl_options
39
+ @tls_options = policy.tls
40
40
 
41
41
  @aliases = {}
42
42
  @cluster_nodes = []
@@ -75,7 +75,7 @@ module Aerospike
75
75
  end
76
76
 
77
77
  def tls_enabled?
78
- !ssl_options.nil? && ssl_options[:enable] != false
78
+ !tls_options.nil? && tls_options[:enable] != false
79
79
  end
80
80
 
81
81
  def initialize_tls_host_names(hosts)
@@ -102,7 +102,9 @@ module Aerospike
102
102
  (node_array.length > 0) && !@closed.value
103
103
  end
104
104
 
105
- def get_node(partition)
105
+ def get_node_for_key(key)
106
+ partition = Partition.new_by_key(key)
107
+
106
108
  # Must copy hashmap reference for copy on write semantics to work.
107
109
  nmap = partitions
108
110
  if node_array = nmap[partition.namespace]
@@ -268,6 +270,8 @@ module Aerospike
268
270
  if peers.generation_changed?
269
271
  # Refresh peers for all nodes that responded the first time even if only
270
272
  # one node's peers changed.
273
+ peers.reset_refresh_count!
274
+
271
275
  nodes.each do |node|
272
276
  node.refresh_peers(peers)
273
277
  end
@@ -371,7 +375,7 @@ module Aerospike
371
375
 
372
376
  seed_array.each do |seed|
373
377
  begin
374
- seed_node_validator = NodeValidator.new(self, seed, @connection_timeout, @cluster_name, ssl_options)
378
+ seed_node_validator = NodeValidator.new(self, seed, @connection_timeout, @cluster_name, tls_options)
375
379
  rescue => e
376
380
  Aerospike.logger.error("Seed #{seed} failed: #{e}\n#{e.backtrace.join("\n")}")
377
381
  next
@@ -384,7 +388,7 @@ module Aerospike
384
388
  nv = seed_node_validator
385
389
  else
386
390
  begin
387
- nv = NodeValidator.new(self, aliass, @connection_timeout, @cluster_name, ssl_options)
391
+ nv = NodeValidator.new(self, aliass, @connection_timeout, @cluster_name, tls_options)
388
392
  rescue => e
389
393
  Aerospike.logger.error("Seed #{seed} failed: #{e}")
390
394
  next
@@ -427,50 +431,7 @@ module Aerospike
427
431
  end
428
432
 
429
433
  def find_nodes_to_remove(refresh_count)
430
- node_list = nodes
431
-
432
- remove_list = []
433
-
434
- node_list.each do |node|
435
- if !node.active?
436
- # Inactive nodes must be removed.
437
- remove_list << node
438
- next
439
- end
440
-
441
- case node_list.length
442
- when 1
443
- # Single node clusters rely solely on node health.
444
- remove_list << node if node.unhealthy?
445
-
446
- when 2
447
- # Two node clusters require at least one successful refresh before removing.
448
- if refresh_count == 2 && node.reference_count.value == 0 && !node.responded?
449
- # Node is not referenced nor did it respond.
450
- remove_list << node
451
- end
452
-
453
- else
454
- # Multi-node clusters require two successful node refreshes before removing.
455
- if refresh_count >= 2 && node.reference_count.value == 0
456
- # Node is not referenced by other nodes.
457
- # Check if node responded to info request.
458
- if node.responded?
459
- # Node is alive, but not referenced by other nodes. Check if mapped.
460
- unless find_node_in_partition_map(node)
461
- # Node doesn't have any partitions mapped to it.
462
- # There is not point in keeping it in the cluster.
463
- remove_list << node
464
- end
465
- else
466
- # Node not responding. Remove it.
467
- remove_list << node
468
- end
469
- end
470
- end
471
- end
472
-
473
- remove_list
434
+ FindNodesToRemove.(self, refresh_count)
474
435
  end
475
436
 
476
437
  def find_node_in_partition_map(filter)
@@ -28,7 +28,7 @@ module Aerospike
28
28
  host.port,
29
29
  tls_name: host.tls_name,
30
30
  timeout: cluster.connection_timeout,
31
- ssl_options: cluster.ssl_options
31
+ tls_options: cluster.tls_options
32
32
  ).tap do |conn|
33
33
  if cluster.credentials_given?
34
34
  # Authenticate will raise and close connection if invalid credentials
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright 2018 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
11
+ #
12
+ # http://www.apache.org/licenses/LICENSE-2.0
13
+ #
14
+ # Unless required by applicable law or agreed to in writing, software
15
+ # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
16
+ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
17
+ # License for the specific language governing permissions and limitations under
18
+ # the License.
19
+
20
+ module Aerospike
21
+ class Cluster
22
+ # Calculates which nodes that should be removed from the cluster
23
+ module FindNodesToRemove
24
+ class << self
25
+ def call(cluster, refresh_count)
26
+ node_list = cluster.nodes
27
+
28
+ remove_list = []
29
+
30
+ node_list.each do |node|
31
+ unless node.active?
32
+ # Inactive nodes must be removed.
33
+ remove_list << node
34
+ next
35
+ end
36
+
37
+ if refresh_count.zero? && node.failed?(5) # 5 or more failures counts as failed
38
+ # All node info requests failed and this node had 5 consecutive failures.
39
+ # Remove node. If no nodes are left, seeds will be tried in next cluster
40
+ # tend iteration.
41
+ remove_list << node
42
+ next
43
+ end
44
+
45
+ if node_list.size > 1 && refresh_count >= 1 && !node.referenced?
46
+ # Node is not referenced by other nodes.
47
+ # Check if node responded to info request.
48
+ if node.failed?
49
+ remove_list << node
50
+ else
51
+ # Node is alive, but not referenced by other nodes. Check if mapped.
52
+ unless cluster.find_node_in_partition_map(node)
53
+ # Node doesn't have any partitions mapped to it.
54
+ # There is no point in keeping it in the cluster.
55
+ remove_list << node
56
+ end
57
+ end
58
+ end
59
+ end
60
+
61
+ remove_list
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
@@ -1,4 +1,5 @@
1
- # encoding: utf-8
1
+ # frozen_string_literal: true
2
+
2
3
  # Copyright 2014-2017 Aerospike, Inc.
3
4
  #
4
5
  # Portions may be licensed to Aerospike, Inc. under one or more contributor
@@ -19,6 +20,8 @@ module Aerospike
19
20
  private
20
21
 
21
22
  class Partition # :nodoc:
23
+ UNPACK_FORMAT = 'l<'
24
+
22
25
  attr_reader :namespace, :partition_id
23
26
 
24
27
  def initialize(namespace, partition_id)
@@ -31,7 +34,7 @@ module Aerospike
31
34
  def self.new_by_key(key)
32
35
  Partition.new(
33
36
  key.namespace,
34
- (key.digest[0..3].unpack('l<')[0] & 0xFFFF) % Node::PARTITIONS
37
+ (key.digest[0..3].unpack(UNPACK_FORMAT)[0] & 0xFFFF) % Node::PARTITIONS
35
38
  )
36
39
  end
37
40
 
@@ -0,0 +1,104 @@
1
+ # Copyright 2014-2018 Aerospike, Inc.
2
+ #
3
+ # Portions may be licensed to Aerospike, Inc. under one or more contributor
4
+ # license agreements.
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License"); you may not
7
+ # use this file except in compliance with the License. You may obtain a copy of
8
+ # the License at
9
+ #
10
+ # 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 'aerospike/command/multi_command'
19
+
20
+ module Aerospike
21
+
22
+ class BatchDirectCommand < MultiCommand #:nodoc:
23
+
24
+ attr_accessor :batch
25
+ attr_accessor :policy
26
+ attr_accessor :key_map
27
+ attr_accessor :bin_names
28
+ attr_accessor :results
29
+ attr_accessor :read_attr
30
+
31
+ def initialize(node, batch, policy, key_map, bin_names, results, read_attr)
32
+ super(node)
33
+
34
+ @batch = batch
35
+ @policy = policy
36
+ @key_map = key_map
37
+ @bin_names = bin_names
38
+ @results = results
39
+ @read_attr = read_attr
40
+ end
41
+
42
+ def write_buffer
43
+ # Estimate buffer size
44
+ begin_cmd
45
+ byte_size = batch.keys.length * DIGEST_SIZE
46
+
47
+ @data_offset += batch.namespace.bytesize +
48
+ FIELD_HEADER_SIZE + byte_size + FIELD_HEADER_SIZE
49
+
50
+ if bin_names
51
+ bin_names.each do |bin_name|
52
+ estimate_operation_size_for_bin_name(bin_name)
53
+ end
54
+ end
55
+
56
+ size_buffer
57
+
58
+ operation_count = 0
59
+ if bin_names
60
+ operation_count = bin_names.length
61
+ end
62
+
63
+ write_header(policy, read_attr, 0, 2, operation_count)
64
+ write_field_string(batch.namespace, Aerospike::FieldType::NAMESPACE)
65
+ write_field_header(byte_size, Aerospike::FieldType::DIGEST_RIPE_ARRAY)
66
+
67
+ batch.keys.each do |key|
68
+ @data_offset += @data_buffer.write_binary(key.digest, @data_offset)
69
+ end
70
+
71
+ if bin_names
72
+ bin_names.each do |bin_name|
73
+ write_operation_for_bin_name(bin_name, Aerospike::Operation::READ)
74
+ end
75
+ end
76
+
77
+ end_cmd
78
+ end
79
+
80
+ # Parse all results in the batch. Add records to shared list.
81
+ # If the record was not found, the bins will be nil.
82
+ def parse_row(result_code)
83
+ generation = @data_buffer.read_int32(6)
84
+ expiration = @data_buffer.read_int32(10)
85
+ field_count = @data_buffer.read_int16(18)
86
+ op_count = @data_buffer.read_int16(20)
87
+
88
+ key = parse_key(field_count)
89
+
90
+ item = key_map[key.digest]
91
+ if item
92
+ if result_code == 0
93
+ index = item.index
94
+ key = item.key
95
+ results[index] = parse_record(key, op_count, generation, expiration)
96
+ end
97
+ else
98
+ Aerospike.logger.warn("Unexpected batch key returned: #{key}")
99
+ end
100
+ end
101
+
102
+ end # class
103
+
104
+ end # module
@@ -0,0 +1,51 @@
1
+ # Copyright 2014-2018 Aerospike, Inc.
2
+ #
3
+ # Portions may be licensed to Aerospike, Inc. under one or more contributor
4
+ # license agreements.
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License"); you may not
7
+ # use this file except in compliance with the License. You may obtain a copy of
8
+ # the License at
9
+ #
10
+ # 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 'aerospike/command/batch_direct_command'
19
+
20
+ module Aerospike
21
+
22
+ class BatchDirectExistsCommand < BatchDirectCommand #:nodoc:
23
+
24
+ def initialize(node, batch, policy, key_map, results)
25
+ super(node, batch, policy, key_map, nil, results, INFO1_READ | INFO1_NOBINDATA)
26
+ end
27
+
28
+ # Parse all results in the batch. Add records to shared list.
29
+ # If the record was not found, the bins will be nil.
30
+ def parse_row(result_code)
31
+ field_count = @data_buffer.read_int16(18)
32
+ op_count = @data_buffer.read_int16(20)
33
+
34
+ if op_count > 0
35
+ raise Aerospike::Exceptions::Parse.new('Received bins that were not requested!')
36
+ end
37
+
38
+ key = parse_key(field_count)
39
+ item = key_map[key.digest]
40
+
41
+ if item
42
+ index = item.index
43
+ results[index] = (result_code == 0)
44
+ else
45
+ Aerospike::logger.debug("Unexpected batch key returned: #{key.namespace}, #{key.digest}")
46
+ end
47
+ end
48
+
49
+ end # class
50
+
51
+ end # module
@@ -0,0 +1,40 @@
1
+ # Copyright 2014-2018 Aerospike, Inc.
2
+ #
3
+ # Portions may be licensed to Aerospike, Inc. under one or more contributor
4
+ # license agreements.
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License"); you may not
7
+ # use this file except in compliance with the License. You may obtain a copy of
8
+ # the License at
9
+ #
10
+ # 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
+ module Aerospike
19
+
20
+ BatchNamespace = Struct.new :namespace, :keys
21
+
22
+ class BatchDirectNode #:nodoc:
23
+
24
+ attr_accessor :node
25
+ attr_accessor :batch_namespaces
26
+
27
+ def self.generate_list(cluster, keys)
28
+ keys.group_by { |key| cluster.get_node_for_key(key) }
29
+ .map { |node, keys_for_node| BatchDirectNode.new(node, keys_for_node) }
30
+ end
31
+
32
+ def initialize(node, keys)
33
+ @node = node
34
+ @batch_namespaces = keys.group_by(&:namespace)
35
+ .map { |ns, keys_for_ns| BatchNamespace.new(ns, keys_for_ns) }
36
+ end
37
+
38
+ end
39
+
40
+ end
@@ -0,0 +1,119 @@
1
+ # Copyright 2018 Aerospike, Inc.
2
+ #
3
+ # Portions may be licensed to Aerospike, Inc. under one or more contributor
4
+ # license agreements.
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License"); you may not
7
+ # use this file except in compliance with the License. You may obtain a copy of
8
+ # the License at
9
+ #
10
+ # 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 'aerospike/command/multi_command'
19
+
20
+ module Aerospike
21
+
22
+ class BatchIndexCommand < MultiCommand #:nodoc:
23
+
24
+ attr_accessor :batch
25
+ attr_accessor :policy
26
+ attr_accessor :bin_names
27
+ attr_accessor :results
28
+ attr_accessor :read_attr
29
+
30
+ def initialize(node, batch, policy, bin_names, results, read_attr)
31
+ super(node)
32
+ @batch = batch
33
+ @policy = policy
34
+ @bin_names = bin_names
35
+ @results = results
36
+ @read_attr = read_attr
37
+ end
38
+
39
+ def write_buffer
40
+ bin_name_size = 0
41
+ operation_count = 0
42
+ field_count = 1
43
+ if bin_names
44
+ bin_names.each do |bin_name|
45
+ bin_name_size += bin_name.bytesize + OPERATION_HEADER_SIZE
46
+ end
47
+ operation_count = bin_names.length
48
+ end
49
+ begin_cmd
50
+ @data_offset += FIELD_HEADER_SIZE + 4 + 1 # batch.keys.length + flags
51
+
52
+ prev = nil
53
+ batch.keys.each do |key|
54
+ @data_offset += key.digest.length + 4 # 4 byte batch offset
55
+
56
+ if prev != nil && prev.namespace == key.namespace
57
+ @data_offset += 1
58
+ else
59
+ @data_offset += key.namespace.bytesize + FIELD_HEADER_SIZE + 1 + 1 + 2 + 2 # repeat/no-repeat flag + read_attr flags + field_count + operation_count
60
+ @data_offset += bin_name_size
61
+ end
62
+ end
63
+ size_buffer
64
+ write_header(policy,read_attr | INFO1_BATCH, 0, 1, 0)
65
+ write_field_header(0, Aerospike::FieldType::BATCH_INDEX)
66
+ @data_offset += @data_buffer.write_int32(batch.keys.length, @data_offset)
67
+ @data_offset += @data_buffer.write_byte(1, @data_offset)
68
+
69
+ prev = nil
70
+
71
+ batch.each_key_with_index do |key, index|
72
+ @data_offset += @data_buffer.write_int32(index, @data_offset)
73
+ @data_offset += @data_buffer.write_binary(key.digest, @data_offset)
74
+
75
+ if (prev != nil && prev.namespace == key.namespace)
76
+ @data_offset += @data_buffer.write_byte(1, @data_offset)
77
+ else
78
+ @data_offset += @data_buffer.write_byte(0, @data_offset)
79
+ @data_offset += @data_buffer.write_byte(read_attr, @data_offset)
80
+ @data_offset += @data_buffer.write_int16(field_count, @data_offset)
81
+ @data_offset += @data_buffer.write_int16(operation_count, @data_offset)
82
+ write_field_string(key.namespace, Aerospike::FieldType::NAMESPACE)
83
+
84
+ if bin_names
85
+ bin_names.each do |bin_name|
86
+ write_operation_for_bin_name(bin_name, Aerospike::Operation::READ)
87
+ end
88
+ end
89
+ prev = key
90
+ end
91
+ end
92
+ end_cmd
93
+ end
94
+
95
+ # Parse all results in the batch. Add records to shared list.
96
+ # If the record was not found, the bins will be nil.
97
+ def parse_row(result_code)
98
+ generation = @data_buffer.read_int32(6)
99
+ expiration = @data_buffer.read_int32(10)
100
+ batch_index = @data_buffer.read_int32(14)
101
+ field_count = @data_buffer.read_int16(18)
102
+ op_count = @data_buffer.read_int16(20)
103
+
104
+ key = parse_key(field_count)
105
+ req_key = batch.key_for_index(batch_index)
106
+
107
+ if key.digest == req_key.digest
108
+ if result_code == 0
109
+ record = parse_record(req_key, op_count, generation, expiration)
110
+ results[batch_index] = record
111
+ end
112
+ else
113
+ Aerospike.logger.warn("Unexpected batch key returned: #{key}")
114
+ end
115
+ end
116
+
117
+ end # class
118
+
119
+ end # module