cassandra-driver 1.0.0.beta.3-java → 1.0.0-java

Sign up to get free protection for your applications and to get access to all the features.
Files changed (70) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +51 -14
  3. data/lib/cassandra.rb +164 -78
  4. data/lib/cassandra/address_resolution.rb +36 -0
  5. data/lib/cassandra/address_resolution/policies.rb +2 -0
  6. data/lib/cassandra/address_resolution/policies/ec2_multi_region.rb +56 -0
  7. data/lib/cassandra/address_resolution/policies/none.rb +35 -0
  8. data/lib/cassandra/auth.rb +1 -1
  9. data/lib/cassandra/auth/providers/password.rb +1 -1
  10. data/lib/cassandra/cluster.rb +18 -5
  11. data/lib/cassandra/cluster/client.rb +175 -101
  12. data/lib/cassandra/{client/connection_manager.rb → cluster/connection_pool.rb} +5 -5
  13. data/lib/cassandra/cluster/connector.rb +142 -56
  14. data/lib/cassandra/cluster/control_connection.rb +385 -134
  15. data/lib/cassandra/{client/null_logger.rb → cluster/failed_connection.rb} +12 -14
  16. data/lib/cassandra/cluster/options.rb +13 -2
  17. data/lib/cassandra/cluster/registry.rb +19 -9
  18. data/lib/cassandra/column.rb +5 -0
  19. data/lib/cassandra/compression.rb +1 -1
  20. data/lib/cassandra/compression/compressors/lz4.rb +1 -1
  21. data/lib/cassandra/compression/compressors/snappy.rb +1 -1
  22. data/lib/cassandra/driver.rb +29 -21
  23. data/lib/cassandra/errors.rb +325 -35
  24. data/lib/cassandra/execution/options.rb +13 -6
  25. data/lib/cassandra/execution/trace.rb +4 -4
  26. data/lib/cassandra/future.rb +7 -3
  27. data/lib/cassandra/keyspace.rb +5 -0
  28. data/lib/cassandra/load_balancing/policies/dc_aware_round_robin.rb +13 -4
  29. data/lib/cassandra/load_balancing/policies/token_aware.rb +2 -4
  30. data/lib/cassandra/load_balancing/policies/white_list.rb +3 -6
  31. data/lib/cassandra/null_logger.rb +35 -0
  32. data/lib/cassandra/protocol.rb +0 -16
  33. data/lib/cassandra/protocol/cql_byte_buffer.rb +18 -18
  34. data/lib/cassandra/protocol/cql_protocol_handler.rb +78 -8
  35. data/lib/cassandra/protocol/frame_decoder.rb +2 -2
  36. data/lib/cassandra/protocol/frame_encoder.rb +1 -1
  37. data/lib/cassandra/protocol/requests/query_request.rb +1 -11
  38. data/lib/cassandra/protocol/response.rb +1 -1
  39. data/lib/cassandra/protocol/responses/detailed_error_response.rb +16 -1
  40. data/lib/cassandra/protocol/responses/error_response.rb +17 -0
  41. data/lib/cassandra/protocol/responses/event_response.rb +1 -1
  42. data/lib/cassandra/protocol/responses/raw_rows_result_response.rb +1 -1
  43. data/lib/cassandra/protocol/responses/result_response.rb +1 -1
  44. data/lib/cassandra/protocol/responses/rows_result_response.rb +1 -1
  45. data/lib/cassandra/protocol/type_converter.rb +4 -3
  46. data/lib/cassandra/reconnection.rb +1 -1
  47. data/lib/cassandra/result.rb +4 -6
  48. data/lib/cassandra/retry.rb +3 -5
  49. data/lib/cassandra/session.rb +14 -5
  50. data/lib/cassandra/statements/prepared.rb +5 -1
  51. data/lib/cassandra/table.rb +6 -1
  52. data/lib/cassandra/time_uuid.rb +21 -83
  53. data/lib/cassandra/util.rb +131 -1
  54. data/lib/cassandra/uuid.rb +6 -4
  55. data/lib/cassandra/uuid/generator.rb +207 -0
  56. data/lib/cassandra/version.rb +1 -1
  57. data/lib/cassandra_murmur3.jar +0 -0
  58. metadata +43 -49
  59. data/lib/cassandra/client.rb +0 -144
  60. data/lib/cassandra/client/batch.rb +0 -212
  61. data/lib/cassandra/client/client.rb +0 -591
  62. data/lib/cassandra/client/column_metadata.rb +0 -54
  63. data/lib/cassandra/client/connector.rb +0 -277
  64. data/lib/cassandra/client/execute_options_decoder.rb +0 -59
  65. data/lib/cassandra/client/peer_discovery.rb +0 -50
  66. data/lib/cassandra/client/prepared_statement.rb +0 -314
  67. data/lib/cassandra/client/query_result.rb +0 -230
  68. data/lib/cassandra/client/request_runner.rb +0 -71
  69. data/lib/cassandra/client/result_metadata.rb +0 -48
  70. data/lib/cassandra/client/void_result.rb +0 -78
@@ -0,0 +1,36 @@
1
+ # encoding: utf-8
2
+
3
+ #--
4
+ # Copyright 2013-2014 DataStax, Inc.
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of 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,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #++
18
+
19
+ module Cassandra
20
+ # Address Resolution policy allows translating a node ip address from what is
21
+ # recorded in Cassandra's system tables to an actual ip address for the driver
22
+ # to use. It is very useful in various multi-region scenarios (e.g. on EC2).
23
+ module AddressResolution
24
+ class Policy
25
+ # Resolves a node ip address.
26
+ #
27
+ # @param address [IPAddr] node ip address from Cassandra's system table
28
+ #
29
+ # @return [IPAddr] actual ip address of the node
30
+ def resolve(address)
31
+ end
32
+ end
33
+ end
34
+ end
35
+
36
+ require 'cassandra/address_resolution/policies'
@@ -0,0 +1,2 @@
1
+ require 'cassandra/address_resolution/policies/none'
2
+ require 'cassandra/address_resolution/policies/ec2_multi_region'
@@ -0,0 +1,56 @@
1
+ # encoding: utf-8
2
+
3
+ #--
4
+ # Copyright 2013-2014 DataStax, Inc.
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of 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,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #++
18
+
19
+ module Cassandra
20
+ module AddressResolution
21
+ module Policies
22
+ # This policy resolves private ips of the hosts in the same datacenter and
23
+ # public ips of hosts in other datacenters.
24
+ #
25
+ # @note Initializing this policy is not necessary, you should just pass
26
+ # `:ec_multi_region` to the `:address_resolution` option of
27
+ # {Cassandra.cluster}
28
+ class EC2MultiRegion
29
+ # @private
30
+ def initialize(resolver = Resolv)
31
+ @resolver = resolver
32
+ end
33
+
34
+ # Returns ip address after a double DNS lookup. First, it will get
35
+ # hostname from a given ip, then resolve the resulting hostname. This
36
+ # policy works because AWS public hostnames resolve to a private ip
37
+ # address within the same datacenter.
38
+ #
39
+ # @param address [IPAddr] node ip address from Cassandra's system table
40
+ #
41
+ # @return [IPAddr] private ip withing the same datacenter, public ip
42
+ # otherwise. Returns original address if DNS lookups fail.
43
+ def resolve(address)
44
+ @resolver.each_name(Resolv::DNS::Name.create(address.reverse)) do |name|
45
+ @resolver.each_address(name) do |addr|
46
+ return ::IPAddr.new(addr)
47
+ end
48
+ end
49
+
50
+ # default to original address if reverse DNS lookup failed
51
+ address
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,35 @@
1
+ # encoding: utf-8
2
+
3
+ #--
4
+ # Copyright 2013-2014 DataStax, Inc.
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of 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,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #++
18
+
19
+ module Cassandra
20
+ module AddressResolution
21
+ module Policies
22
+ # The default address resolution policy. Always returns original address.
23
+ class None
24
+ # Returns original address.
25
+ #
26
+ # @param address [IPAddr] node ip address from Cassandra's system table
27
+ #
28
+ # @return [IPAddr] same as `address`
29
+ def resolve(address)
30
+ address
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -28,7 +28,7 @@ module Cassandra
28
28
  # @note Creating an authenticator must absolutely not block, or the whole
29
29
  # connection process will block.
30
30
  #
31
- # @abstract Auth providers given to {Cassandra.connect} don't need to be
31
+ # @abstract Auth providers given to {Cassandra.cluster} don't need to be
32
32
  # subclasses of this class, but need to implement the same methods. This
33
33
  # class exists only for documentation purposes.
34
34
  #
@@ -22,7 +22,7 @@ module Cassandra
22
22
  # Auth provider used for Cassandra's built in authentication.
23
23
  #
24
24
  # @note No need to instantiate this class manually, use `:username` and
25
- # `:password` options when calling {Cassandra.connect} and one will be
25
+ # `:password` options when calling {Cassandra.cluster} and one will be
26
26
  # created automatically for you.
27
27
  class Password < Provider
28
28
  # Authenticator used for Cassandra's built in authentication,
@@ -63,7 +63,7 @@ module Cassandra
63
63
  def_delegators :@metadata, :name, :find_replicas
64
64
 
65
65
  # @private
66
- def initialize(logger, io_reactor, control_connection, cluster_registry, cluster_schema, cluster_metadata, execution_options, connection_options, load_balancing_policy, reconnection_policy, retry_policy, connector, futures_factory)
66
+ def initialize(logger, io_reactor, control_connection, cluster_registry, cluster_schema, cluster_metadata, execution_options, connection_options, load_balancing_policy, reconnection_policy, retry_policy, address_resolution_policy, connector, futures_factory)
67
67
  @logger = logger
68
68
  @io_reactor = io_reactor
69
69
  @control_connection = control_connection
@@ -75,6 +75,7 @@ module Cassandra
75
75
  @load_balancing_policy = load_balancing_policy
76
76
  @reconnection_policy = reconnection_policy
77
77
  @retry_policy = retry_policy
78
+ @address_resolver = address_resolution_policy
78
79
  @connector = connector
79
80
  @futures = futures_factory
80
81
  end
@@ -133,19 +134,22 @@ module Cassandra
133
134
  #
134
135
  # @return [Cassandra::Future<Cassandra::Session>] a future new session that
135
136
  # can prepare and execute statements
137
+ #
138
+ # @see Cassandra::Cluster#connect A list of possible exceptions that this
139
+ # future can be resolved with
136
140
  def connect_async(keyspace = nil)
137
141
  if !keyspace.nil? && !keyspace.is_a?(::String)
138
142
  return @futures.error(::ArgumentError.new("keyspace must be a string, #{keyspace.inspect} given"))
139
143
  end
140
144
 
141
- client = Client.new(@logger, @registry, @schema, @io_reactor, @connector, @load_balancing_policy, @reconnection_policy, @retry_policy, @connection_options, @futures)
145
+ client = Client.new(@logger, @registry, @schema, @io_reactor, @connector, @load_balancing_policy, @reconnection_policy, @retry_policy, @address_resolver, @connection_options, @futures)
142
146
  session = Session.new(client, @execution_options, @futures)
143
147
  promise = @futures.promise
144
148
 
145
149
  client.connect.on_complete do |f|
146
150
  if f.resolved?
147
151
  if keyspace
148
- f = session.execute_async("USE #{keyspace}")
152
+ f = session.execute_async("USE #{Util.escape_name(keyspace)}")
149
153
 
150
154
  f.on_success {promise.fulfill(session)}
151
155
  f.on_failure {|e| promise.break(e)}
@@ -162,8 +166,14 @@ module Cassandra
162
166
 
163
167
  # Synchronously create a new session, optionally scoped to a keyspace
164
168
  #
165
- # @param keyspace [String] optional keyspace to scope session to
166
- # @raise [ArgumentError] if keyspace is not a String
169
+ # @param keyspace [String] optional keyspace to scope the session to
170
+ #
171
+ # @raise [ArgumentError] if keyspace is not a String
172
+ # @raise [Cassandra::Errors::NoHostsAvailable] when all hosts failed
173
+ # @raise [Cassandra::Errors::AuthenticationError] when authentication fails
174
+ # @raise [Cassandra::Errors::ProtocolError] when protocol negotiation fails
175
+ # @raise [Cassandra::Error] other unexpected errors
176
+ #
167
177
  # @return [Cassandra::Session] a new session that can prepare and execute
168
178
  # statements
169
179
  #
@@ -181,6 +191,7 @@ module Cassandra
181
191
 
182
192
  @control_connection.close_async.on_complete do |f|
183
193
  if f.resolved?
194
+ @load_balancing_policy.teardown(self) rescue nil
184
195
  promise.fulfill(self)
185
196
  else
186
197
  f.on_failure {|e| promise.break(e)}
@@ -207,8 +218,10 @@ module Cassandra
207
218
  end
208
219
 
209
220
  require 'cassandra/cluster/client'
221
+ require 'cassandra/cluster/connection_pool'
210
222
  require 'cassandra/cluster/connector'
211
223
  require 'cassandra/cluster/control_connection'
224
+ require 'cassandra/cluster/failed_connection'
212
225
  require 'cassandra/cluster/metadata'
213
226
  require 'cassandra/cluster/options'
214
227
  require 'cassandra/cluster/registry'
@@ -24,7 +24,7 @@ module Cassandra
24
24
 
25
25
  attr_reader :keyspace
26
26
 
27
- def initialize(logger, cluster_registry, cluster_schema, io_reactor, connector, load_balancing_policy, reconnection_policy, retry_policy, connection_options, futures_factory)
27
+ def initialize(logger, cluster_registry, cluster_schema, io_reactor, connector, load_balancing_policy, reconnection_policy, retry_policy, address_resolution_policy, connection_options, futures_factory)
28
28
  @logger = logger
29
29
  @registry = cluster_registry
30
30
  @schema = cluster_schema
@@ -33,9 +33,10 @@ module Cassandra
33
33
  @load_balancing_policy = load_balancing_policy
34
34
  @reconnection_policy = reconnection_policy
35
35
  @retry_policy = retry_policy
36
+ @address_resolver = address_resolution_policy
36
37
  @connection_options = connection_options
37
38
  @futures = futures_factory
38
- @connecting_hosts = ::Set.new
39
+ @connecting_hosts = ::Hash.new
39
40
  @connections = ::Hash.new
40
41
  @prepared_statements = ::Hash.new
41
42
  @preparing_statements = ::Hash.new
@@ -51,16 +52,23 @@ module Cassandra
51
52
  return @connected_future if @state == :connecting || @state == :connected
52
53
 
53
54
  @state = :connecting
54
- @connecting_hosts.merge(@registry.hosts)
55
+ @registry.each_host do |host|
56
+ distance = @load_balancing_policy.distance(host)
57
+ next if distance == :ignore
58
+
59
+ @connecting_hosts[host] = distance
60
+ end
55
61
  end
56
62
 
57
63
  @connected_future = begin
64
+ @logger.info('Creating session')
58
65
  @registry.add_listener(self)
66
+ @schema.add_listener(self)
59
67
 
60
- futures = @connecting_hosts.map do |host|
61
- f = connect_to_host_maybe_retry(host, @load_balancing_policy.distance(host))
68
+ futures = @connecting_hosts.map do |(host, distance)|
69
+ f = connect_to_host(host, distance)
62
70
  f.recover do |error|
63
- Cassandra::Client::FailedConnection.new(error, host)
71
+ FailedConnection.new(error, host)
64
72
  end
65
73
  end
66
74
 
@@ -93,6 +101,7 @@ module Cassandra
93
101
 
94
102
  @closed_future = begin
95
103
  @registry.remove_listener(self)
104
+ @schema.remove_listener(self)
96
105
 
97
106
  if state == :connecting
98
107
  f = @connected_future.recover.flat_map { close_connections }
@@ -116,12 +125,18 @@ module Cassandra
116
125
  end
117
126
 
118
127
  def host_up(host)
128
+ distance = nil
129
+
119
130
  synchronize do
120
131
  return Ione::Future.resolved if @connecting_hosts.include?(host)
121
- @connecting_hosts << host
132
+
133
+ distance = @load_balancing_policy.distance(host)
134
+ return Ione::Future.resolved if distance == :ignore
135
+
136
+ @connecting_hosts[host] = distance
122
137
  end
123
138
 
124
- connect_to_host_maybe_retry(host, @load_balancing_policy.distance(host)).map(nil)
139
+ connect_to_host_maybe_retry(host, distance).map(nil)
125
140
  end
126
141
 
127
142
  def host_down(host)
@@ -130,7 +145,6 @@ module Cassandra
130
145
  synchronize do
131
146
  return Ione::Future.resolved if !@connections.has_key?(host) && !@connecting_hosts.include?(host)
132
147
 
133
- @logger.info("Session disconnecting from ip=#{host.ip}")
134
148
  @connecting_hosts.delete(host)
135
149
  @prepared_statements.delete(host)
136
150
  @preparing_statements.delete(host)
@@ -145,6 +159,18 @@ module Cassandra
145
159
  end
146
160
  end
147
161
 
162
+ def keyspace_created(keyspace)
163
+ end
164
+
165
+ def keyspace_changed(keyspace)
166
+ end
167
+
168
+ def keyspace_dropped(keyspace)
169
+ @keyspace = nil if @keyspace == keyspace.name
170
+ nil
171
+ end
172
+
173
+
148
174
  def query(statement, options, paging_state = nil)
149
175
  request = Protocol::QueryRequest.new(statement.cql, statement.params, nil, options.consistency, options.serial_consistency, options.page_size, paging_state, options.trace?)
150
176
  timeout = options.timeout
@@ -209,13 +235,17 @@ module Cassandra
209
235
  :unlogged => Protocol::BatchRequest::UNLOGGED_TYPE,
210
236
  :counter => Protocol::BatchRequest::COUNTER_TYPE,
211
237
  }.freeze
212
- CLIENT_CLOSED = Ione::Future.failed(Errors::ClientError.new('Cannot connect a closed client'))
213
- CLIENT_NOT_CONNECTED = Ione::Future.failed(Errors::ClientError.new('Cannot close a not connected client'))
214
- NOT_CONNECTED = Errors::NotConnectedError.new
238
+ CLIENT_CLOSED = Ione::Future.failed(Errors::ClientError.new('Client closed'))
239
+ NOT_CONNECTED = Errors::ClientError.new('Client not connected')
240
+ CLIENT_NOT_CONNECTED = Ione::Future.failed(NOT_CONNECTED)
215
241
 
216
242
  UNAVAILABLE_ERROR_CODE = 0x1000
217
243
  WRITE_TIMEOUT_ERROR_CODE = 0x1100
218
244
  READ_TIMEOUT_ERROR_CODE = 0x1200
245
+ OVERLOADED_ERROR_CODE = 0x1001
246
+ SERVER_ERROR_CODE = 0x0000
247
+ BOOTSTRAPPING_ERROR_CODE = 0x1002
248
+ UNPREPARED_ERROR_CODE = 0x2500
219
249
 
220
250
  SELECT_SCHEMA_PEERS = Protocol::QueryRequest.new("SELECT peer, rpc_address, schema_version FROM system.peers", nil, nil, :one)
221
251
  SELECT_SCHEMA_LOCAL = Protocol::QueryRequest.new("SELECT schema_version FROM system.local WHERE key='local'", nil, nil, :one)
@@ -226,14 +256,14 @@ module Cassandra
226
256
  @state = :connected
227
257
  end
228
258
 
229
- @logger.info('Session connected')
259
+ @logger.info('Session created')
230
260
  else
231
261
  synchronize do
232
262
  @state = :defunct
233
263
  end
234
264
 
235
265
  f.on_failure do |e|
236
- @logger.error('Session connect failed: %s' % e.message)
266
+ @logger.error("Session failed to connect (#{e.class.name}: #{e.message})")
237
267
  end
238
268
 
239
269
  close
@@ -248,20 +278,17 @@ module Cassandra
248
278
  @logger.info('Session closed')
249
279
  else
250
280
  f.on_failure do |e|
251
- @logger.error('Session close failed: %s' % e.message)
281
+ @logger.error("Session failed to close (#{e.class.name}: #{e.message})")
252
282
  end
253
283
  end
254
284
  end
255
285
  end
256
286
 
257
287
  def close_connections
258
- @logger.info('Session closing')
259
-
260
288
  futures = []
261
289
  synchronize do
262
290
  @connections.each do |host, connections|
263
291
  connections.snapshot.each do |c|
264
- @logger.info("Disconnecting ip=#{c.host}")
265
292
  futures << c.close
266
293
  end
267
294
  end.clear
@@ -274,7 +301,7 @@ module Cassandra
274
301
  f = connect_to_host(host, distance)
275
302
 
276
303
  f.on_failure do |e|
277
- connect_to_host_with_retry(host, @reconnection_policy.schedule) if e.is_a?(Io::ConnectionError) || e.is_a?(::SystemCallError) || e.is_a?(::SocketError)
304
+ connect_to_host_with_retry(host, @reconnection_policy.schedule)
278
305
  end
279
306
 
280
307
  f
@@ -283,21 +310,23 @@ module Cassandra
283
310
  def connect_to_host_with_retry(host, schedule)
284
311
  interval = schedule.next
285
312
 
286
- @logger.info("Session started reconnecting to ip=#{host.ip} delay=#{interval}")
313
+ @logger.debug("Reconnecting to #{host.ip} in #{interval} seconds")
287
314
 
288
315
  f = @reactor.schedule_timer(interval)
289
316
  f.flat_map do
290
- if synchronize { @connecting_hosts.include?(host) }
291
- connect_to_host(host, @load_balancing_policy.distance(host)).fallback do |e|
292
- if e.is_a?(Io::ConnectionError) || e.is_a?(::SystemCallError) || e.is_a?(::SocketError)
293
- connect_to_host_with_retry(host, schedule)
294
- else
295
- Ione::Future.failed(e)
296
- end
317
+ distance = nil
318
+
319
+ synchronize do
320
+ if @connecting_hosts.include?(host)
321
+ distance = @load_balancing_policy.distance(host)
297
322
  end
298
- else
299
- @logger.info("Session reconnection to ip=#{host.ip} cancelled")
323
+ end
300
324
 
325
+ if distance && distance != :ignore
326
+ connect_to_host(host, distance).fallback do |e|
327
+ connect_to_host_with_retry(host, schedule)
328
+ end
329
+ else
301
330
  NO_CONNECTIONS
302
331
  end
303
332
  end
@@ -312,22 +341,20 @@ module Cassandra
312
341
  when :remote
313
342
  pool_size = @connection_options.connections_per_remote_node
314
343
  else
315
- raise ::ArgumentError, "distance must be one of :ignore, :local or :remote, #{distance.inspect} given"
344
+ @logger.error("Invalid load balancing distance, not connecting to #{host.ip}. Distance must be one of #{LoadBalancing::DISTANCES.inspect}, #{distance.inspect} given")
345
+ return NO_CONNECTIONS
316
346
  end
317
347
 
318
- @logger.info("Session connecting to ip=#{host.ip}")
319
-
320
348
  f = @connector.connect_many(host, pool_size)
321
349
 
322
350
  f.on_value do |connections|
323
351
  manager = nil
324
352
 
325
353
  synchronize do
326
- @logger.info("Session connected to ip=#{host.ip}")
327
354
  @connecting_hosts.delete(host)
328
355
  @prepared_statements[host] = {}
329
356
  @preparing_statements[host] = {}
330
- manager = @connections[host] ||= Cassandra::Client::ConnectionManager.new
357
+ manager = @connections[host] ||= ConnectionPool.new
331
358
  end
332
359
 
333
360
  manager.add_connections(connections)
@@ -338,7 +365,7 @@ module Cassandra
338
365
 
339
366
  def execute_by_plan(promise, keyspace, statement, options, request, plan, timeout, errors = nil, hosts = [])
340
367
  unless plan.has_next?
341
- promise.break(Errors::NoHostsAvailable.new(errors || {}))
368
+ promise.break(Errors::NoHostsAvailable.new(errors))
342
369
  return
343
370
  end
344
371
 
@@ -361,12 +388,13 @@ module Cassandra
361
388
  prepare_and_send_request_by_plan(host, connection, promise, keyspace, statement, options, request, plan, timeout, errors, hosts)
362
389
  else
363
390
  s.on_failure do |e|
364
- if e.is_a?(Errors::QueryError)
365
- promise.break(e)
366
- else
391
+ case e
392
+ when Errors::HostError
367
393
  errors ||= {}
368
394
  errors[host] = e
369
395
  execute_by_plan(promise, keyspace, statement, options, request, plan, timeout, errors, hosts)
396
+ else
397
+ promise.break(e)
370
398
  end
371
399
  end
372
400
  end
@@ -395,12 +423,13 @@ module Cassandra
395
423
  do_send_request_by_plan(host, connection, promise, keyspace, statement, options, request, plan, timeout, errors, hosts)
396
424
  else
397
425
  prepare.on_failure do |e|
398
- if e.is_a?(Errors::QueryError)
399
- promise.break(e)
400
- else
426
+ case e
427
+ when Errors::HostError
401
428
  errors ||= {}
402
429
  errors[host] = e
403
430
  execute_by_plan(promise, keyspace, statement, options, request, plan, timeout, errors, hosts)
431
+ else
432
+ promise.break(e)
404
433
  end
405
434
  end
406
435
  end
@@ -410,7 +439,7 @@ module Cassandra
410
439
 
411
440
  def batch_by_plan(promise, keyspace, statement, options, plan, timeout, errors = nil, hosts = [])
412
441
  unless plan.has_next?
413
- promise.break(Errors::NoHostsAvailable.new(errors || {}))
442
+ promise.break(Errors::NoHostsAvailable.new(errors))
414
443
  return
415
444
  end
416
445
 
@@ -433,12 +462,13 @@ module Cassandra
433
462
  batch_and_send_request_by_plan(host, connection, promise, keyspace, statement, options, plan, timeout, errors, hosts)
434
463
  else
435
464
  s.on_failure do |e|
436
- if e.is_a?(Errors::QueryError)
437
- promise.break(e)
438
- else
465
+ case e
466
+ when Errors::HostError
439
467
  errors ||= {}
440
468
  errors[host] = e
441
469
  batch_by_plan(promise, keyspace, statement, options, plan, timeout, errors, hosts)
470
+ else
471
+ promise.break(e)
442
472
  end
443
473
  end
444
474
  end
@@ -492,12 +522,13 @@ module Cassandra
492
522
  do_send_request_by_plan(host, connection, promise, keyspace, statement, options, request, plan, timeout, errors, hosts)
493
523
  else
494
524
  f.on_failure do |e|
495
- if e.is_a?(Errors::QueryError)
496
- promise.break(e)
497
- else
525
+ case e
526
+ when Errors::HostError
498
527
  errors ||= {}
499
528
  errors[host] = e
500
529
  batch_by_plan(promise, keyspace, statement, options, plan, timeout, errors, hosts)
530
+ else
531
+ promise.break(e)
501
532
  end
502
533
  end
503
534
  end
@@ -507,7 +538,7 @@ module Cassandra
507
538
 
508
539
  def send_request_by_plan(promise, keyspace, statement, options, request, plan, timeout, errors = nil, hosts = [])
509
540
  unless plan.has_next?
510
- promise.break(Errors::NoHostsAvailable.new(errors || {}))
541
+ promise.break(Errors::NoHostsAvailable.new(errors))
511
542
  return
512
543
  end
513
544
 
@@ -530,12 +561,13 @@ module Cassandra
530
561
  do_send_request_by_plan(host, connection, promise, keyspace, statement, options, request, plan, timeout, errors, hosts)
531
562
  else
532
563
  s.on_failure do |e|
533
- if e.is_a?(Errors::QueryError)
534
- promise.break(e)
535
- else
564
+ case e
565
+ when Errors::HostError
536
566
  errors ||= {}
537
567
  errors[host] = e
538
568
  send_request_by_plan(promise, keyspace, statement, options, request, plan, timeout, errors, hosts)
569
+ else
570
+ promise.break(e)
539
571
  end
540
572
  end
541
573
  end
@@ -564,32 +596,78 @@ module Cassandra
564
596
  when UNAVAILABLE_ERROR_CODE
565
597
  @retry_policy.unavailable(statement, details[:cl], details[:required], details[:alive], retries)
566
598
  when WRITE_TIMEOUT_ERROR_CODE
567
- details[:write_type] = write_type = details[:write_type].downcase!.to_sym
568
- @retry_policy.write_timeout(statement, details[:cl], write_type, details[:blockfor], details[:received], retries)
599
+ @retry_policy.write_timeout(statement, details[:cl], details[:write_type], details[:blockfor], details[:received], retries)
569
600
  when READ_TIMEOUT_ERROR_CODE
570
601
  @retry_policy.read_timeout(statement, details[:cl], details[:blockfor], details[:received], details[:data_present], retries)
602
+ when UNPREPARED_ERROR_CODE
603
+ cql = statement.cql
604
+
605
+ synchronize do
606
+ @preparing_statements[host].delete(cql)
607
+ @prepared_statements[host].delete(cql)
608
+ end
609
+
610
+ prepare = prepare_statement(host, connection, cql, timeout)
611
+ prepare.on_complete do |_|
612
+ if prepare.resolved?
613
+ request.id = prepare.value
614
+ do_send_request_by_plan(host, connection, promise, keyspace, statement, options, request, plan, timeout, errors, hosts)
615
+ else
616
+ prepare.on_failure do |e|
617
+ case e
618
+ when Errors::HostError
619
+ errors ||= {}
620
+ errors[host] = e
621
+ execute_by_plan(promise, keyspace, statement, options, request, plan, timeout, errors, hosts)
622
+ else
623
+ promise.break(e)
624
+ end
625
+ end
626
+ end
627
+ end
628
+
629
+ nil
571
630
  else
572
- promise.break(Errors::QueryError.new(r.code, r.message, statement.cql, details))
573
- break
631
+ promise.break(r.to_error(statement))
632
+
633
+ nil
574
634
  end
575
635
  rescue => e
576
636
  promise.break(e)
577
- break
637
+
638
+ nil
578
639
  end
579
640
 
580
- case decision
581
- when Retry::Decisions::Retry
582
- request.consistency = decision.consistency
583
- do_send_request_by_plan(host, connection, promise, keyspace, statement, options, request, plan, timeout, errors, hosts, retries + 1)
584
- when Retry::Decisions::Ignore
585
- promise.fulfill(Results::Void.new(r.trace_id, keyspace, statement, options, hosts, request.consistency, retries, self, @futures))
586
- when Retry::Decisions::Reraise
587
- promise.break(Errors::QueryError.new(r.code, r.message, statement.cql, r.details))
588
- else
589
- promise.break(Errors::QueryError.new(r.code, r.message, statement.cql, r.details))
641
+ if decision
642
+ case decision
643
+ when Retry::Decisions::Retry
644
+ request.consistency = decision.consistency
645
+ do_send_request_by_plan(host, connection, promise, keyspace, statement, options, request, plan, timeout, errors, hosts, retries + 1)
646
+ when Retry::Decisions::Ignore
647
+ promise.fulfill(Results::Void.new(r.trace_id, keyspace, statement, options, hosts, request.consistency, retries, self, @futures))
648
+ when Retry::Decisions::Reraise
649
+ promise.break(r.to_error(statement))
650
+ else
651
+ promise.break(r.to_error(statement))
652
+ end
590
653
  end
591
654
  when Protocol::ErrorResponse
592
- promise.break(Errors::QueryError.new(r.code, r.message, statement.cql, nil))
655
+ case r.code
656
+ when OVERLOADED_ERROR_CODE, SERVER_ERROR_CODE, BOOTSTRAPPING_ERROR_CODE
657
+ errors ||= {}
658
+ errors[host] = r.to_error(statement)
659
+
660
+ case request
661
+ when Protocol::QueryRequest, Protocol::PrepareRequest
662
+ send_request_by_plan(promise, keyspace, statement, options, request, plan, timeout, errors, hosts)
663
+ when Protocol::ExecuteRequest
664
+ execute_by_plan(promise, keyspace, statement, options, request, plan, timeout, errors, hosts)
665
+ when Protocol::BatchRequest
666
+ batch_by_plan(promise, keyspace, statement, options, plan, timeout, errors, hosts)
667
+ end
668
+ else
669
+ promise.break(r.to_error(statement))
670
+ end
593
671
  when Protocol::SetKeyspaceResultResponse
594
672
  @keyspace = r.keyspace
595
673
  promise.fulfill(Results::Void.new(r.trace_id, keyspace, statement, options, hosts, request.consistency, retries, self, @futures))
@@ -607,14 +685,13 @@ module Cassandra
607
685
  when Protocol::RowsResultResponse
608
686
  promise.fulfill(Results::Paged.new(r.rows, r.paging_state, r.trace_id, keyspace, statement, options, hosts, request.consistency, retries, self, @futures))
609
687
  when Protocol::SchemaChangeResultResponse
610
- if r.change == 'DROPPED' && r.keyspace == @keyspace && r.table.empty?
611
- @keyspace = nil
612
- end
688
+ @schema.delete_keyspace(r.keyspace) if r.change == 'DROPPED' && r.table.empty?
613
689
 
690
+ @logger.debug('Waiting for schema to propagate to all hosts after a change')
614
691
  wait_for_schema_agreement(connection, @reconnection_policy.schedule).on_complete do |f|
615
692
  unless f.resolved?
616
693
  f.on_failure do |e|
617
- @logger.error("Schema agreement error: #{e.class.name}: #{e.message}\n#{e.backtrace.join("\n")}")
694
+ @logger.error("Schema agreement failure (#{e.class.name}: #{e.message})")
618
695
  end
619
696
  end
620
697
  promise.fulfill(Results::Void.new(r.trace_id, keyspace, statement, options, hosts, request.consistency, retries, self, @futures))
@@ -642,42 +719,35 @@ module Cassandra
642
719
  end
643
720
 
644
721
  def wait_for_schema_agreement(connection, schedule)
645
- @logger.info('Fetching schema versions')
646
-
647
- peers = connection.send_request(SELECT_SCHEMA_PEERS)
648
- local = connection.send_request(SELECT_SCHEMA_LOCAL)
722
+ peers = send_select_request(connection, SELECT_SCHEMA_PEERS)
723
+ local = send_select_request(connection, SELECT_SCHEMA_LOCAL)
649
724
 
650
725
  Ione::Future.all(peers, local).flat_map do |(peers, local)|
651
- @logger.info('Fetched schema versions')
652
-
653
- peers = peers.rows
654
- local = local.rows
655
-
656
726
  versions = ::Set.new
657
727
 
658
728
  unless local.empty?
659
729
  host = @registry.host(connection.host)
660
730
 
661
- if host && host.up?
731
+ if host && @load_balancing_policy.distance(host) != :ignore
662
732
  versions << version = local.first['schema_version']
663
- @logger.debug("Host #{host.ip} schema version: #{version}")
733
+ @logger.debug("Host #{host.ip} schema version is #{version}")
664
734
  end
665
735
  end
666
736
 
667
737
  peers.each do |row|
668
738
  host = @registry.host(peer_ip(row))
669
- next unless host && host.up?
739
+ next unless host && @load_balancing_policy.distance(host) != :ignore
670
740
 
671
741
  versions << version = row['schema_version']
672
- @logger.debug("Host #{host.ip} schema version: #{version}")
742
+ @logger.debug("Host #{host.ip} schema version is #{version}")
673
743
  end
674
744
 
675
745
  if versions.one?
676
- @logger.info('All hosts have the same schema version')
746
+ @logger.debug('All hosts have the same schema')
677
747
  Ione::Future.resolved
678
748
  else
679
749
  interval = schedule.next
680
- @logger.info("Hosts don't yet agree on schema: #{versions.to_a.inspect}, retrying in #{interval} seconds")
750
+ @logger.info("Hosts have different schema versions: #{versions.to_a.inspect}, retrying in #{interval} seconds")
681
751
  @reactor.schedule_timer(interval).flat_map do
682
752
  wait_for_schema_agreement(connection, schedule)
683
753
  end
@@ -688,7 +758,8 @@ module Cassandra
688
758
  def peer_ip(data)
689
759
  ip = data['rpc_address']
690
760
  ip = data['peer'] if ip == '0.0.0.0'
691
- ip
761
+
762
+ @address_resolver.resolve(ip)
692
763
  end
693
764
 
694
765
  def switch_keyspace(connection, keyspace, timeout)
@@ -697,19 +768,17 @@ module Cassandra
697
768
 
698
769
  return pending_switch || Ione::Future.resolved if pending_keyspace == keyspace
699
770
 
700
- request = Protocol::QueryRequest.new("USE #{keyspace}", nil, nil, :one)
771
+ request = Protocol::QueryRequest.new("USE #{Util.escape_name(keyspace)}", nil, nil, :one)
701
772
 
702
773
  f = connection.send_request(request, timeout).map do |r|
703
774
  case r
704
775
  when Protocol::SetKeyspaceResultResponse
705
776
  @keyspace = r.keyspace
706
777
  nil
707
- when Protocol::DetailedErrorResponse
708
- raise Errors::QueryError.new(r.code, r.message, request.cql, r.details)
709
778
  when Protocol::ErrorResponse
710
- raise Errors::QueryError.new(r.code, r.message, request.cql, nil)
779
+ raise r.to_error(Statements::Simple.new("USE #{Util.escape_name(keyspace)}"))
711
780
  else
712
- raise "unexpected response #{r.inspect}"
781
+ raise Errors::InternalError, "Unexpected response #{r.inspect}"
713
782
  end
714
783
  end
715
784
 
@@ -742,12 +811,10 @@ module Cassandra
742
811
  @preparing_statements[host].delete(cql)
743
812
  end
744
813
  id
745
- when Protocol::DetailedErrorResponse
746
- raise Errors::QueryError.new(r.code, r.message, cql, r.details)
747
814
  when Protocol::ErrorResponse
748
- raise Errors::QueryError.new(r.code, r.message, cql, nil)
815
+ raise r.to_error(VOID_STATEMENT)
749
816
  else
750
- raise "unexpected response #{r.inspect}"
817
+ raise Errors::InternalError, "Unexpected response #{r.inspect}"
751
818
  end
752
819
  end
753
820
 
@@ -758,10 +825,17 @@ module Cassandra
758
825
  f
759
826
  end
760
827
 
761
- def create_execution_info(keyspace, statement, options, request, response, hosts)
762
- trace_id = response.trace_id
763
- trace = trace_id ? Execution::Trace.new(trace_id, self) : nil
764
- info = Execution::Info.new(keyspace, statement, options, hosts, request.consistency, request.retries, trace)
828
+ def send_select_request(connection, request)
829
+ connection.send_request(request).map do |r|
830
+ case r
831
+ when Protocol::RowsResultResponse
832
+ r.rows
833
+ when Protocol::ErrorResponse
834
+ raise r.to_error(VOID_STATEMENT)
835
+ else
836
+ raise Errors::InternalError, "Unexpected response #{r.inspect}"
837
+ end
838
+ end
765
839
  end
766
840
  end
767
841
  end