aerospike 2.5.1 → 2.6.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 (41) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +13 -0
  3. data/README.md +3 -3
  4. data/lib/aerospike.rb +33 -6
  5. data/lib/aerospike/aerospike_exception.rb +9 -26
  6. data/lib/aerospike/client.rb +7 -22
  7. data/lib/aerospike/{cluster/cluster.rb → cluster.rb} +122 -161
  8. data/lib/aerospike/cluster/create_connection.rb +42 -0
  9. data/lib/aerospike/cluster/find_node.rb +35 -0
  10. data/lib/aerospike/connection/authenticate.rb +35 -0
  11. data/lib/aerospike/connection/create.rb +36 -0
  12. data/lib/aerospike/host.rb +7 -4
  13. data/lib/aerospike/host/parse.rb +50 -0
  14. data/lib/aerospike/node.rb +232 -0
  15. data/lib/aerospike/node/generation.rb +50 -0
  16. data/lib/aerospike/node/refresh/failed.rb +34 -0
  17. data/lib/aerospike/node/refresh/friends.rb +100 -0
  18. data/lib/aerospike/node/refresh/info.rb +60 -0
  19. data/lib/aerospike/node/refresh/partitions.rb +60 -0
  20. data/lib/aerospike/node/refresh/peers.rb +83 -0
  21. data/lib/aerospike/node/refresh/reset.rb +36 -0
  22. data/lib/aerospike/node/verify/cluster_name.rb +35 -0
  23. data/lib/aerospike/node/verify/name.rb +43 -0
  24. data/lib/aerospike/node/verify/partition_generation.rb +43 -0
  25. data/lib/aerospike/node/verify/peers_generation.rb +41 -0
  26. data/lib/aerospike/{cluster/node_validator.rb → node_validator.rb} +29 -47
  27. data/lib/aerospike/peer.rb +24 -0
  28. data/lib/aerospike/peers.rb +44 -0
  29. data/lib/aerospike/peers/fetch.rb +36 -0
  30. data/lib/aerospike/peers/parse.rb +88 -0
  31. data/lib/aerospike/policy/client_policy.rb +16 -9
  32. data/lib/aerospike/socket/base.rb +86 -0
  33. data/lib/aerospike/socket/ssl.rb +70 -0
  34. data/lib/aerospike/socket/tcp.rb +57 -0
  35. data/lib/aerospike/utils/buffer.rb +7 -6
  36. data/lib/aerospike/utils/string_parser.rb +53 -0
  37. data/lib/aerospike/value/value.rb +7 -8
  38. data/lib/aerospike/version.rb +1 -1
  39. metadata +30 -7
  40. data/lib/aerospike/cluster/connection.rb +0 -124
  41. data/lib/aerospike/cluster/node.rb +0 -274
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ccefb4dd82c2d6d15f9f0eac08d32acceacf2da1d93c8ca3a462f4c7e4e10e95
4
- data.tar.gz: f133ccde7da4ea553d933a30c367ecf2a354c2e85ecce0b141c2071f94702237
3
+ metadata.gz: 2cf4004182c5166b136433e8be338b9187718684c2783808d18b3ed31f1dbd17
4
+ data.tar.gz: 3a18cdde5294fb88ee0d4d710fb870c29db3273f5e29bd2d2ea92fd824857f00
5
5
  SHA512:
6
- metadata.gz: e9baa1554bad7b3f3f8625940ee2e62f08b7185a662401757b8b172d7b9734427245fd8a18fc2aaeb7e4a90c15f3fa3f2346bfc53cce41efe03c15794b63a01c
7
- data.tar.gz: 52be62b23c582cdcc7fd12c5961c7ed82847a57b90bf8aea88a56580c7e05c1c416a0a51f7b8e170afde63763560a6acfa4200b95917ee5bc6542ae028954aec
6
+ metadata.gz: ab75f0c1d0d57c37f3d3f16966a27017b706067e414306c25a3388ad4a02571cdb18b1f019d67472e35386ad659db1d23d8685664ea6a7f2279e904b4c52383b
7
+ data.tar.gz: 501dabb13c9eb471e83cbda40aa22f2df7ae7e2c6083d447917a02e71017b9b64abe4bbff37798ef2517bced052c17347a109c842b97ab6b16f921939c6ebe23
@@ -1,3 +1,16 @@
1
+ v2.6.0 / 2018-03-27
2
+ ===================
3
+
4
+ * **New Features**
5
+ * Support for peers protocol for cluster discovery. Requires Aerospike server version 3.10 or later. Thanks to [@wallin](https://github.com/wallin) of [castle.io](https://castle.io/)! [[#59](https://github.com/aerospike/aerospike-client-ruby/pull/59)]
6
+ * TLS encryption support for client <-> server connections. Requires Aerospike Enterprise Edition version 3.11 or later. Thanks to [@wallin](https://github.com/wallin) of [castle.io](https://castle.io/)! [[#59](https://github.com/aerospike/aerospike-client-ruby/pull/59)]
7
+
8
+ * **Bug Fixes**
9
+ * Fix min./max. boundary check for Integer bin values and improve performance. Thanks to [@wallin](https://github.com/wallin) of [castle.io](https://castle.io/)! [[#60](https://github.com/aerospike/aerospike-client-ruby/pull/60)]
10
+
11
+ * **Updates**
12
+ * Update minimum required Ruby version to v2.3.
13
+
1
14
  v2.5.1 / 2018-01-25
2
15
  ===================
3
16
 
data/README.md CHANGED
@@ -7,7 +7,7 @@
7
7
 
8
8
  An Aerospike library for Ruby.
9
9
 
10
- This library is compatible with Ruby 2.2+ and supports Linux, Mac OS X and various other BSDs.
10
+ This library is compatible with Ruby 2.3+ and supports Linux, Mac OS X and various other BSDs.
11
11
 
12
12
  - [Usage](#Usage)
13
13
  - [Prerequisites](#Prerequisites)
@@ -61,7 +61,7 @@ Details about the API are available in the [`docs`](docs) directory.
61
61
  <a name="Prerequisites"></a>
62
62
  ## Prerequisites
63
63
 
64
- [Ruby](https://ruby-lang.org) version v2.2+ is required.
64
+ [Ruby](https://ruby-lang.org) version v2.3+ is required.
65
65
 
66
66
  Aerospike Ruby client implements the wire protocol, and does not depend on the C client.
67
67
  It is thread friendly.
@@ -81,7 +81,7 @@ Supported operating systems:
81
81
 
82
82
  ### Installation from source
83
83
 
84
- 1. Install Ruby 2.2+
84
+ 1. Install Ruby 2.3+
85
85
  2. Install RubyGems
86
86
  3. Install Bundler: ```gem install bundler```
87
87
  4. Install dependencies: ```bundler install```
@@ -1,12 +1,14 @@
1
1
  # encoding: utf-8
2
- # Copyright 2014-2017 Aerospike, Inc.
2
+ # Copyright 2014-2018 Aerospike, Inc.
3
3
  #
4
4
  # Portions may be licensed to Aerospike, Inc. under one or more contributor
5
5
  # license agreements.
6
6
  #
7
7
  # Licensed under the Apache License, Version 2.0 (the "License"); you may not
8
8
  # use this file except in compliance with the License. You may obtain a copy of
9
- # the License at http:#www.apache.org/licenses/LICENSE-2.0
9
+ # the License at
10
+ #
11
+ # http://www.apache.org/licenses/LICENSE-2.0
10
12
  #
11
13
  # Unless required by applicable law or agreed to in writing, software
12
14
  # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
@@ -29,7 +31,9 @@ require 'aerospike/utils/pool'
29
31
  require 'aerospike/utils/packer'
30
32
  require 'aerospike/utils/unpacker'
31
33
  require 'aerospike/utils/buffer'
34
+ require 'aerospike/utils/string_parser'
32
35
  require 'aerospike/host'
36
+ require 'aerospike/host/parse'
33
37
  require 'aerospike/loggable'
34
38
  require 'aerospike/record'
35
39
  require 'aerospike/result_code'
@@ -77,13 +81,36 @@ require 'aerospike/policy/consistency_level'
77
81
  require 'aerospike/policy/commit_level'
78
82
  require 'aerospike/policy/admin_policy'
79
83
 
80
- require 'aerospike/cluster/connection'
81
- require 'aerospike/cluster/cluster'
82
- require 'aerospike/cluster/node_validator'
84
+ require 'aerospike/socket/base'
85
+ require 'aerospike/socket/ssl'
86
+ require 'aerospike/socket/tcp'
87
+
88
+ require 'aerospike/connection/authenticate'
89
+ require 'aerospike/connection/create'
90
+
91
+ require 'aerospike/cluster'
92
+ require 'aerospike/cluster/create_connection'
83
93
  require 'aerospike/cluster/partition'
84
- require 'aerospike/cluster/node'
94
+ require 'aerospike/cluster/find_node'
85
95
  require 'aerospike/cluster/partition_tokenizer_new'
86
96
  require 'aerospike/cluster/partition_tokenizer_old'
97
+ require 'aerospike/node'
98
+ require 'aerospike/node/generation'
99
+ require 'aerospike/node/refresh/failed'
100
+ require 'aerospike/node/refresh/friends'
101
+ require 'aerospike/node/refresh/info'
102
+ require 'aerospike/node/refresh/partitions'
103
+ require 'aerospike/node/refresh/peers'
104
+ require 'aerospike/node/refresh/reset'
105
+ require 'aerospike/node/verify/cluster_name'
106
+ require 'aerospike/node/verify/name'
107
+ require 'aerospike/node/verify/partition_generation'
108
+ require 'aerospike/node/verify/peers_generation'
109
+ require 'aerospike/node_validator'
110
+ require 'aerospike/peer'
111
+ require 'aerospike/peers'
112
+ require 'aerospike/peers/fetch'
113
+ require 'aerospike/peers/parse'
87
114
  require 'aerospike/info'
88
115
  require 'aerospike/udf'
89
116
  require 'aerospike/bin'
@@ -1,12 +1,14 @@
1
1
  # encoding: utf-8
2
- # Copyright 2014-2017 Aerospike, Inc.
2
+ # Copyright 2014-2018 Aerospike, Inc.
3
3
  #
4
4
  # Portions may be licensed to Aerospike, Inc. under one or more contributor
5
5
  # license agreements.
6
6
  #
7
7
  # Licensed under the Apache License, Version 2.0 (the "License"); you may not
8
8
  # use this file except in compliance with the License. You may obtain a copy of
9
- # the License at http:#www.apache.org/licenses/LICENSE-2.0
9
+ # the License at
10
+ #
11
+ # http://www.apache.org/licenses/LICENSE-2.0
10
12
  #
11
13
  # Unless required by applicable law or agreed to in writing, software
12
14
  # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
@@ -17,95 +19,76 @@
17
19
  require 'aerospike/result_code'
18
20
 
19
21
  module Aerospike
20
-
21
22
  module Exceptions
22
-
23
23
  class Aerospike < StandardError
24
-
25
24
  attr_reader :result_code
26
25
 
27
26
  def initialize(result_code, message = nil)
28
27
  @result_code = result_code
29
28
  message ||= ResultCode.message(result_code)
30
29
  super(message)
31
-
32
- self
33
30
  end
34
-
35
31
  end
36
32
 
37
33
  class Timeout < Aerospike
38
-
39
34
  attr_reader :timeout, :iterations, :failed_nodes, :failed_connections
40
35
 
41
36
  def initialize(timeout, iterations, failed_nodes=nil, failed_connections=nil)
42
-
43
37
  @timeout = timeout
44
38
  @iterations = iterations
45
39
  @failed_nodes = failed_nodes
46
40
  @failed_connections = failed_connections
47
41
 
48
42
  super(ResultCode::TIMEOUT)
49
-
50
43
  end
44
+ end
51
45
 
46
+ class InvalidCredentials < Aerospike
47
+ def initialize(msg = nil)
48
+ super(ResultCode::NOT_AUTHENTICATED, msg)
49
+ end
52
50
  end
53
51
 
54
52
  class Serialize < Aerospike
55
-
56
53
  def initialize(msg=nil)
57
54
  super(ResultCode::SERIALIZE_ERROR, msg)
58
55
  end
59
-
60
56
  end
61
57
 
62
58
  class Parse < Aerospike
63
-
64
59
  def initialize(msg=nil)
65
60
  super(ResultCode::PARSE_ERROR, msg)
66
61
  end
67
-
68
62
  end
69
63
 
70
64
  class Connection < Aerospike
71
-
72
65
  def initialize(msg=nil)
73
66
  super(ResultCode::SERVER_NOT_AVAILABLE, msg)
74
67
  end
75
-
76
68
  end
77
69
 
78
70
  class InvalidNode < Aerospike
79
-
80
71
  def initialize(msg=nil)
81
72
  super(ResultCode::INVALID_NODE_ERROR, msg)
82
73
  end
83
-
84
74
  end
85
75
 
86
76
  class ScanTerminated < Aerospike
87
-
88
77
  def initialize(msg=nil)
89
78
  super(ResultCode::SCAN_TERMINATED, msg)
90
79
  end
91
-
92
80
  end
93
81
 
94
82
  class QueryTerminated < Aerospike
95
-
96
83
  def initialize(msg=nil)
97
84
  super(ResultCode::QUERY_TERMINATED, msg)
98
85
  end
99
-
100
86
  end
101
87
 
102
88
  class CommandRejected < Aerospike
103
-
104
89
  def initialize(msg=nil)
105
90
  super(ResultCode::COMMAND_REJECTED, msg)
106
91
  end
107
-
108
92
  end
109
-
110
93
  end
111
94
  end
@@ -1,12 +1,14 @@
1
1
  # encoding: utf-8
2
- # Copyright 2014-2017 Aerospike, Inc.
2
+ # Copyright 2014-2018 Aerospike, Inc.
3
3
  #
4
4
  # Portions may be licensed to Aerospike, Inc. under one or more contributor
5
5
  # license agreements.
6
6
  #
7
7
  # Licensed under the Apache License, Version 2.0 (the "License"); you may not
8
8
  # use this file except in compliance with the License. You may obtain a copy of
9
- # the License at http:#www.apache.org/licenses/LICENSE-2.0
9
+ # the License at
10
+ #
11
+ # http://www.apache.org/licenses/LICENSE-2.0
10
12
  #
11
13
  # Unless required by applicable law or agreed to in writing, software
12
14
  # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
@@ -46,9 +48,9 @@ module Aerospike
46
48
  @default_query_policy = QueryPolicy.new
47
49
  @default_admin_policy = QueryPolicy.new
48
50
 
49
- hosts = parse_hosts(hosts || ENV["AEROSPIKE_HOSTS"] || "localhost")
51
+ hosts = ::Aerospike::Host::Parse.(hosts || ENV['AEROSPIKE_HOSTS'] || 'localhost')
50
52
  policy = create_policy(policy, ClientPolicy)
51
- @cluster = Cluster.new(policy, *hosts)
53
+ @cluster = Cluster.new(policy, hosts)
52
54
  @cluster.add_cluster_config_change_listener(self)
53
55
 
54
56
  self.connect if connect
@@ -88,7 +90,7 @@ module Aerospike
88
90
  # Returns list of active server node names in the cluster.
89
91
 
90
92
  def node_names
91
- @cluster.nodes.map(&:get_name)
93
+ @cluster.nodes.map(&:name)
92
94
  end
93
95
 
94
96
  def supports_feature?(feature)
@@ -826,23 +828,6 @@ module Aerospike
826
828
  end
827
829
  end
828
830
 
829
- def parse_hosts(hosts)
830
- case hosts
831
- when Host
832
- [hosts]
833
- when Array
834
- hosts
835
- when String
836
- hosts.split(?,).map { |host|
837
- (addr, port) = host.split(?:)
838
- port ||= 3000
839
- Host.new(addr, port.to_i)
840
- }
841
- else
842
- fail TypeError, "hosts should be a Host object, an Array of Host objects, or a String"
843
- end
844
- end
845
-
846
831
  def cluster=(cluster)
847
832
  @cluster = cluster
848
833
  end
@@ -1,12 +1,15 @@
1
- # encoding: utf-8
2
- # Copyright 2014-2017 Aerospike, Inc.
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright 2014-2018 Aerospike, Inc.
3
4
  #
4
5
  # Portions may be licensed to Aerospike, Inc. under one or more contributor
5
6
  # license agreements.
6
7
  #
7
8
  # Licensed under the Apache License, Version 2.0 (the "License"); you may not
8
9
  # use this file except in compliance with the License. You may obtain a copy of
9
- # the License at http:#www.apache.org/licenses/LICENSE-2.0
10
+ # the License at
11
+ #
12
+ # http://www.apache.org/licenses/LICENSE-2.0
10
13
  #
11
14
  # Unless required by applicable law or agreed to in writing, software
12
15
  # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
@@ -15,27 +18,26 @@
15
18
  # the License.
16
19
 
17
20
  require 'set'
18
- require 'thread'
19
21
  require 'timeout'
20
22
 
21
23
  require 'aerospike/atomic/atomic'
22
24
 
23
25
  module Aerospike
24
-
25
- private
26
-
27
26
  class Cluster
28
-
29
27
  attr_reader :connection_timeout, :connection_queue_size, :user, :password
30
- attr_reader :features
28
+ attr_reader :features, :ssl_options
29
+ attr_reader :cluster_id, :aliases
30
+ attr_reader :cluster_name
31
31
 
32
- def initialize(policy, *hosts)
32
+ def initialize(policy, hosts)
33
33
  @cluster_seeds = hosts
34
34
  @fail_if_not_connected = policy.fail_if_not_connected
35
35
  @connection_queue_size = policy.connection_queue_size
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
40
+
39
41
  @aliases = {}
40
42
  @cluster_nodes = []
41
43
  @partition_write_map = {}
@@ -53,21 +55,33 @@ module Aerospike
53
55
  @password = AdminCommand.hash_password(policy.password)
54
56
  end
55
57
 
56
- self
58
+ initialize_tls_host_names(hosts) if tls_enabled?
57
59
  end
58
60
 
59
61
  def connect
60
62
  wait_till_stablized
61
63
 
62
64
  if @fail_if_not_connected && !connected?
63
- raise Aerospike::Exceptions::Aerospike.new(Aerospike::ResultCode::SERVER_NOT_AVAILABLE)
65
+ raise Aerospike::Exceptions::Aerospike, Aerospike::ResultCode::SERVER_NOT_AVAILABLE
64
66
  end
65
67
 
66
68
  launch_tend_thread
67
69
 
68
70
  Aerospike.logger.info('New cluster initialized and ready to be used...')
71
+ end
69
72
 
70
- self
73
+ def credentials_given?
74
+ !(@user.nil? || @user.empty?)
75
+ end
76
+
77
+ def tls_enabled?
78
+ !ssl_options.nil? && ssl_options[:enable] != false
79
+ end
80
+
81
+ def initialize_tls_host_names(hosts)
82
+ hosts.each do |host|
83
+ host.tls_name ||= cluster_id.nil? ? host.name : cluster_id
84
+ end
71
85
  end
72
86
 
73
87
  def add_seeds(hosts)
@@ -94,12 +108,10 @@ module Aerospike
94
108
  if node_array = nmap[partition.namespace]
95
109
  node = node_array.value[partition.partition_id]
96
110
 
97
- if node && node.active?
98
- return node
99
- end
111
+ return node if node && node.active?
100
112
  end
101
113
 
102
- return random_node
114
+ random_node
103
115
  end
104
116
 
105
117
  # Returns a random node on the cluster
@@ -110,16 +122,14 @@ module Aerospike
110
122
  i = 0
111
123
  while i < length
112
124
  # Must handle concurrency with other non-tending threads, so node_index is consistent.
113
- index = (@node_index.update{|v| v+1} % node_array.length).abs
125
+ index = (@node_index.update{ |v| v+1 } % node_array.length).abs
114
126
  node = node_array[index]
115
127
 
116
- if node.active?
117
- return node
118
- end
128
+ return node if node.active?
119
129
 
120
130
  i = i.succ
121
131
  end
122
- raise Aerospike::Exceptions::InvalidNode.new
132
+ raise Aerospike::Exceptions::InvalidNode
123
133
  end
124
134
 
125
135
  # Returns a list of all nodes in the cluster
@@ -134,23 +144,19 @@ module Aerospike
134
144
  def get_node_by_name(node_name)
135
145
  node = find_node_by_name(node_name)
136
146
 
137
- raise Aerospike::Exceptions::InvalidNode.new unless node
147
+ raise Aerospike::Exceptions::InvalidNode unless node
138
148
 
139
149
  node
140
150
  end
141
151
 
142
152
  # Closes all cached connections to the cluster nodes and stops the tend thread
143
153
  def close
144
- unless @closed.value
145
- # send close signal to maintenance channel
146
- @closed.value = true
147
- @tend_thread.kill
148
-
149
- nodes.each do |node|
150
- node.close
151
- end
152
- end
154
+ return if @closed.value
155
+ # send close signal to maintenance channel
156
+ @closed.value = true
157
+ @tend_thread.kill
153
158
 
159
+ nodes.each(&:close)
154
160
  end
155
161
 
156
162
  def find_alias(aliass)
@@ -159,25 +165,12 @@ module Aerospike
159
165
  end
160
166
  end
161
167
 
162
- def update_partitions(conn, node)
163
- # TODO: Cluster should not care about version of tokenizer
164
- # decouple clstr interface
165
- nmap = {}
166
- if node.use_new_info?
167
- Aerospike.logger.info("Updating partitions using new protocol...")
168
-
169
- tokens = PartitionTokenizerNew.new(conn)
170
- nmap = tokens.update_partition(partitions, node)
171
- else
172
- Aerospike.logger.info("Updating partitions using old protocol...")
173
- tokens = PartitionTokenizerOld.new(conn)
174
- nmap = tokens.update_partition(partitions, node)
175
- end
176
-
168
+ def update_partitions(tokens, node)
169
+ nmap = tokens.update_partition(partitions, node)
177
170
  # update partition write map
178
171
  set_partitions(nmap) if nmap
179
172
 
180
- Aerospike.logger.info("Partitions updated...")
173
+ Aerospike.logger.info("Partitions for node #{node.name} updated")
181
174
  end
182
175
 
183
176
  def request_info(policy, *commands)
@@ -192,9 +185,13 @@ module Aerospike
192
185
  @features.get.include?(feature.to_s)
193
186
  end
194
187
 
188
+ def supports_peers_protocol?
189
+ nodes.all? { |node| node.supports_feature?('peers') }
190
+ end
191
+
195
192
  def change_password(user, password)
196
- # change password ONLY if the user is the same
197
- @password = password if @user == user
193
+ # change password ONLY if the user is the same
194
+ @password = password if @user == user
198
195
  end
199
196
 
200
197
  def add_cluster_config_change_listener(listener)
@@ -213,85 +210,96 @@ module Aerospike
213
210
  "#<Aerospike::Cluster @cluster_nodes=#{@cluster_nodes}>"
214
211
  end
215
212
 
216
- private
217
-
218
213
  def launch_tend_thread
219
214
  @tend_thread = Thread.new do
220
215
  Thread.current.abort_on_exception = false
221
- while true
216
+ loop do
222
217
  begin
223
218
  tend
224
219
  sleep(@tend_interval / 1000.0)
225
220
  rescue => e
226
221
  Aerospike.logger.error("Exception occured during tend: #{e}")
222
+ Aerospike.logger.debug { e.backtrace.join("\n") }
227
223
  end
228
224
  end
229
225
  end
230
226
  end
231
227
 
228
+ # Check health of all nodes in cluster
232
229
  def tend
233
- nodes = self.nodes
230
+ was_changed = refresh_nodes
231
+
232
+ return unless was_changed
233
+
234
+ update_cluster_features
235
+ notify_cluster_config_changed
236
+ # only log the tend finish IF the number of nodes has been changed.
237
+ # This prevents spamming the log on every tend interval
238
+ log_tend_stats(nodes)
239
+ end
240
+
241
+ # Refresh status of all nodes in cluster. Adds new nodes and/or removes
242
+ # unhealty ones
243
+ def refresh_nodes
234
244
  cluster_config_changed = false
235
245
 
236
- # All node additions/deletions are performed in tend thread.
237
- # If active nodes don't exist, seed cluster.
246
+ nodes = self.nodes
238
247
  if nodes.empty?
239
- Aerospike.logger.info("No connections available; seeding...")
240
248
  seed_nodes
241
249
  cluster_config_changed = true
242
-
243
- # refresh nodes list after seeding
244
250
  nodes = self.nodes
245
251
  end
246
252
 
247
- # Refresh all known nodes.
248
- friend_list = []
249
- refresh_count = 0
253
+ peers = Peers.new
250
254
 
251
- # Clear node reference counts.
255
+ # Clear node reference count
252
256
  nodes.each do |node|
253
- node.reference_count.value = 0
254
- node.responded.value = false
257
+ node.refresh_reset
258
+ end
255
259
 
256
- if node.active?
257
- begin
258
- friends = node.refresh
259
- refresh_count += 1
260
- friend_list.concat(friends) if friends
261
- rescue => e
262
- Aerospike.logger.error("Node `#{node}` refresh failed: #{e}")
263
- Aerospike.logger.error(e.backtrace.join("\n"))
264
- end
260
+ peers.use_peers = supports_peers_protocol?
261
+
262
+ # refresh all known nodes
263
+ nodes.each do |node|
264
+ node.refresh_info(peers)
265
+ end
266
+
267
+ # refresh peers when necessary
268
+ if peers.generation_changed?
269
+ # Refresh peers for all nodes that responded the first time even if only
270
+ # one node's peers changed.
271
+ nodes.each do |node|
272
+ node.refresh_peers(peers)
265
273
  end
266
274
  end
267
275
 
268
- # Add nodes in a batch.
269
- add_list = find_nodes_to_add(friend_list)
270
- unless add_list.empty?
271
- add_nodes(add_list)
272
- cluster_config_changed = true
276
+ nodes.each do |node|
277
+ node.refresh_partitions(peers) if node.partition_generation.changed?
273
278
  end
274
279
 
275
- # Handle nodes changes determined from refreshes.
276
- # Remove nodes in a batch.
277
- remove_list = find_nodes_to_remove(refresh_count)
278
- unless remove_list.empty?
279
- remove_nodes(remove_list)
280
- cluster_config_changed = true
280
+ if peers.generation_changed? || !peers.use_peers?
281
+ nodes_to_remove = find_nodes_to_remove(peers.refresh_count)
282
+ if nodes_to_remove.any?
283
+ remove_nodes(nodes_to_remove)
284
+ cluster_config_changed = true
285
+ end
281
286
  end
282
287
 
283
- if cluster_config_changed
284
- update_cluster_features
288
+ # Add any new nodes from peer refresh
289
+ if peers.nodes.any?
290
+ # peers.nodes is a Hash. Pass only values, ie. the array of nodes
291
+ add_nodes(peers.nodes.values)
292
+ cluster_config_changed = true
293
+ end
285
294
 
286
- # only log the tend finish IF the number of nodes has been changed.
287
- # This prevents spamming the log on every tend interval
288
- diff = nodes.length - @old_node_count
289
- action = "#{diff.abs} #{diff.abs == 1 ? "node has" : "nodes have"} #{diff > 0 ? "joined" : "left"} the cluster."
290
- Aerospike.logger.info("Tend finished. #{action} Old node count: #{@old_node_count}, New node count: #{nodes.length}")
291
- @old_node_count = nodes.length
295
+ cluster_config_changed
296
+ end
292
297
 
293
- notify_cluster_config_changed
294
- end
298
+ def log_tend_stats(nodes)
299
+ diff = nodes.size - @old_node_count
300
+ action = "#{diff.abs} #{diff.abs == 1 ? "node has" : "nodes have"} #{diff > 0 ? "joined" : "left"} the cluster."
301
+ Aerospike.logger.info("Tend finished. #{action} Old node count: #{@old_node_count}, New node count: #{nodes.size}")
302
+ @old_node_count = nodes.size
295
303
  end
296
304
 
297
305
  def wait_till_stablized
@@ -299,14 +307,12 @@ module Aerospike
299
307
 
300
308
  # will run until the cluster is stablized
301
309
  thr = Thread.new do
302
- while true
310
+ loop do
303
311
  tend
304
312
 
305
313
  # Check to see if cluster has changed since the last Tend.
306
314
  # If not, assume cluster has stabilized and return.
307
- if count == nodes.length
308
- break
309
- end
315
+ break if count == nodes.length
310
316
 
311
317
  sleep(0.001) # sleep for a miliseconds
312
318
 
@@ -324,13 +330,12 @@ module Aerospike
324
330
  end
325
331
 
326
332
  @closed.value = false if @cluster_nodes.length > 0
327
-
328
333
  end
329
334
 
330
335
  def update_cluster_features
331
336
  # Cluster supports features that are supported by all nodes
332
337
  @features.update do
333
- node_features = self.nodes.map(&:features)
338
+ node_features = nodes.map(&:features)
334
339
  node_features.reduce(&:intersection) || Set.new
335
340
  end
336
341
  end
@@ -366,45 +371,39 @@ module Aerospike
366
371
 
367
372
  seed_array.each do |seed|
368
373
  begin
369
- seed_node_validator = NodeValidator.new(self, seed, @connection_timeout, @cluster_name)
374
+ seed_node_validator = NodeValidator.new(self, seed, @connection_timeout, @cluster_name, ssl_options)
370
375
  rescue => e
371
- Aerospike.logger.error("Seed #{seed.to_s} failed: #{e.backtrace.join("\n")}")
376
+ Aerospike.logger.error("Seed #{seed} failed: #{e}\n#{e.backtrace.join("\n")}")
372
377
  next
373
378
  end
374
379
 
375
380
  nv = nil
376
381
  # Seed host may have multiple aliases in the case of round-robin dns configurations.
377
382
  seed_node_validator.aliases.each do |aliass|
378
-
379
383
  if aliass == seed
380
384
  nv = seed_node_validator
381
385
  else
382
386
  begin
383
- nv = NodeValidator.new(self, aliass, @connection_timeout, @cluster_name)
387
+ nv = NodeValidator.new(self, aliass, @connection_timeout, @cluster_name, ssl_options)
384
388
  rescue => e
385
- Aerospike.logger.error("Seed #{seed.to_s} failed: #{e}")
389
+ Aerospike.logger.error("Seed #{seed} failed: #{e}")
386
390
  next
387
391
  end
388
392
  end
393
+ next if find_node_name(list, nv.name)
389
394
 
390
- if !find_node_name(list, nv.name)
391
- node = create_node(nv)
392
- add_aliases(node)
393
- list << node
394
- end
395
+ node = create_node(nv)
396
+ add_aliases(node)
397
+ list << node
395
398
  end
396
-
397
- end
398
-
399
- if list.length > 0
400
- add_nodes_copy(list)
401
399
  end
402
400
 
401
+ add_nodes_copy(list) if list.length > 0
403
402
  end
404
403
 
405
404
  # Finds a node by name in a list of nodes
406
405
  def find_node_name(list, name)
407
- list.any?{|node| node.name == name}
406
+ list.any? { |node| node.name == name }
408
407
  end
409
408
 
410
409
  def add_alias(host, node)
@@ -423,44 +422,8 @@ module Aerospike
423
422
  end
424
423
  end
425
424
 
426
- def find_nodes_to_add(hosts)
427
- list = []
428
-
429
- hosts.each do |host|
430
- begin
431
- nv = NodeValidator.new(self, host, @connection_timeout, @cluster_name)
432
-
433
- # if node is already in cluster's node list,
434
- # or already included in the list to be added, we should skip it
435
- node = find_node_by_name(nv.name)
436
- node ||= list.detect{|n| n.name == nv.name}
437
-
438
- # make sure node is not already in the list to add
439
- if node
440
- # Duplicate node name found. This usually occurs when the server
441
- # services list contains both internal and external IP addresses
442
- # for the same node. Add new host to list of alias filters
443
- # and do not add new node.
444
- node.reference_count.update{|v| v + 1}
445
- node.add_alias(host)
446
- add_alias(host, node)
447
- next
448
- end
449
-
450
- node = create_node(nv)
451
- list << node
452
-
453
- rescue => e
454
- Aerospike.logger.error("Add node #{node} failed: #{e}")
455
- Aerospike.logger.error(e.backtrace.join("\n"))
456
- end
457
- end
458
-
459
- list
460
- end
461
-
462
425
  def create_node(nv)
463
- Node.new(self, nv)
426
+ ::Aerospike::Node.new(self, nv)
464
427
  end
465
428
 
466
429
  def find_nodes_to_remove(refresh_count)
@@ -482,7 +445,7 @@ module Aerospike
482
445
 
483
446
  when 2
484
447
  # Two node clusters require at least one successful refresh before removing.
485
- if refresh_count == 2 && node.reference_count.value == 0 && !node.responded.value
448
+ if refresh_count == 2 && node.reference_count.value == 0 && !node.responded?
486
449
  # Node is not referenced nor did it respond.
487
450
  remove_list << node
488
451
  end
@@ -492,9 +455,9 @@ module Aerospike
492
455
  if refresh_count >= 2 && node.reference_count.value == 0
493
456
  # Node is not referenced by other nodes.
494
457
  # Check if node responded to info request.
495
- if node.responded.value
458
+ if node.responded?
496
459
  # Node is alive, but not referenced by other nodes. Check if mapped.
497
- if !find_node_in_partition_map(node)
460
+ unless find_node_in_partition_map(node)
498
461
  # Node doesn't have any partitions mapped to it.
499
462
  # There is not point in keeping it in the cluster.
500
463
  remove_list << node
@@ -531,7 +494,7 @@ module Aerospike
531
494
  def add_aliases(node)
532
495
  # Add node's aliases to global alias set.
533
496
  # Aliases are only used in tend thread, so synchronization is not necessary.
534
- node.get_aliases.each do |aliass|
497
+ node.aliases.each do |aliass|
535
498
  @aliases[aliass] = node
536
499
  end
537
500
  end
@@ -551,7 +514,7 @@ module Aerospike
551
514
  nodes_to_remove.each do |node|
552
515
  # Remove node's aliases from cluster alias set.
553
516
  # Aliases are only used in tend thread, so synchronization is not necessary.
554
- node.get_aliases.each do |aliass|
517
+ node.aliases.each do |aliass|
555
518
  Aerospike.logger.debug("Removing alias #{aliass}")
556
519
  remove_alias(aliass)
557
520
  end
@@ -607,7 +570,5 @@ module Aerospike
607
570
  def find_node_by_name(node_name)
608
571
  nodes.detect{|node| node.name == node_name }
609
572
  end
610
-
611
573
  end
612
-
613
574
  end