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

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 (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