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.
- checksums.yaml +4 -4
- data/README.md +51 -14
- data/lib/cassandra.rb +164 -78
- data/lib/cassandra/address_resolution.rb +36 -0
- data/lib/cassandra/address_resolution/policies.rb +2 -0
- data/lib/cassandra/address_resolution/policies/ec2_multi_region.rb +56 -0
- data/lib/cassandra/address_resolution/policies/none.rb +35 -0
- data/lib/cassandra/auth.rb +1 -1
- data/lib/cassandra/auth/providers/password.rb +1 -1
- data/lib/cassandra/cluster.rb +18 -5
- data/lib/cassandra/cluster/client.rb +175 -101
- data/lib/cassandra/{client/connection_manager.rb → cluster/connection_pool.rb} +5 -5
- data/lib/cassandra/cluster/connector.rb +142 -56
- data/lib/cassandra/cluster/control_connection.rb +385 -134
- data/lib/cassandra/{client/null_logger.rb → cluster/failed_connection.rb} +12 -14
- data/lib/cassandra/cluster/options.rb +13 -2
- data/lib/cassandra/cluster/registry.rb +19 -9
- data/lib/cassandra/column.rb +5 -0
- data/lib/cassandra/compression.rb +1 -1
- data/lib/cassandra/compression/compressors/lz4.rb +1 -1
- data/lib/cassandra/compression/compressors/snappy.rb +1 -1
- data/lib/cassandra/driver.rb +29 -21
- data/lib/cassandra/errors.rb +325 -35
- data/lib/cassandra/execution/options.rb +13 -6
- data/lib/cassandra/execution/trace.rb +4 -4
- data/lib/cassandra/future.rb +7 -3
- data/lib/cassandra/keyspace.rb +5 -0
- data/lib/cassandra/load_balancing/policies/dc_aware_round_robin.rb +13 -4
- data/lib/cassandra/load_balancing/policies/token_aware.rb +2 -4
- data/lib/cassandra/load_balancing/policies/white_list.rb +3 -6
- data/lib/cassandra/null_logger.rb +35 -0
- data/lib/cassandra/protocol.rb +0 -16
- data/lib/cassandra/protocol/cql_byte_buffer.rb +18 -18
- data/lib/cassandra/protocol/cql_protocol_handler.rb +78 -8
- data/lib/cassandra/protocol/frame_decoder.rb +2 -2
- data/lib/cassandra/protocol/frame_encoder.rb +1 -1
- data/lib/cassandra/protocol/requests/query_request.rb +1 -11
- data/lib/cassandra/protocol/response.rb +1 -1
- data/lib/cassandra/protocol/responses/detailed_error_response.rb +16 -1
- data/lib/cassandra/protocol/responses/error_response.rb +17 -0
- data/lib/cassandra/protocol/responses/event_response.rb +1 -1
- data/lib/cassandra/protocol/responses/raw_rows_result_response.rb +1 -1
- data/lib/cassandra/protocol/responses/result_response.rb +1 -1
- data/lib/cassandra/protocol/responses/rows_result_response.rb +1 -1
- data/lib/cassandra/protocol/type_converter.rb +4 -3
- data/lib/cassandra/reconnection.rb +1 -1
- data/lib/cassandra/result.rb +4 -6
- data/lib/cassandra/retry.rb +3 -5
- data/lib/cassandra/session.rb +14 -5
- data/lib/cassandra/statements/prepared.rb +5 -1
- data/lib/cassandra/table.rb +6 -1
- data/lib/cassandra/time_uuid.rb +21 -83
- data/lib/cassandra/util.rb +131 -1
- data/lib/cassandra/uuid.rb +6 -4
- data/lib/cassandra/uuid/generator.rb +207 -0
- data/lib/cassandra/version.rb +1 -1
- data/lib/cassandra_murmur3.jar +0 -0
- metadata +43 -49
- data/lib/cassandra/client.rb +0 -144
- data/lib/cassandra/client/batch.rb +0 -212
- data/lib/cassandra/client/client.rb +0 -591
- data/lib/cassandra/client/column_metadata.rb +0 -54
- data/lib/cassandra/client/connector.rb +0 -277
- data/lib/cassandra/client/execute_options_decoder.rb +0 -59
- data/lib/cassandra/client/peer_discovery.rb +0 -50
- data/lib/cassandra/client/prepared_statement.rb +0 -314
- data/lib/cassandra/client/query_result.rb +0 -230
- data/lib/cassandra/client/request_runner.rb +0 -71
- data/lib/cassandra/client/result_metadata.rb +0 -48
- 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,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
|
data/lib/cassandra/auth.rb
CHANGED
@@ -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.
|
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.
|
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,
|
data/lib/cassandra/cluster.rb
CHANGED
@@ -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
|
-
#
|
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 = ::
|
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
|
-
@
|
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 =
|
68
|
+
futures = @connecting_hosts.map do |(host, distance)|
|
69
|
+
f = connect_to_host(host, distance)
|
62
70
|
f.recover do |error|
|
63
|
-
|
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
|
-
|
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,
|
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('
|
213
|
-
|
214
|
-
|
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
|
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(
|
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(
|
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)
|
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.
|
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
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
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
|
-
|
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
|
-
|
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] ||=
|
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
|
-
|
365
|
-
|
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
|
-
|
399
|
-
|
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
|
-
|
437
|
-
|
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
|
-
|
496
|
-
|
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
|
-
|
534
|
-
|
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[:
|
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(
|
573
|
-
|
631
|
+
promise.break(r.to_error(statement))
|
632
|
+
|
633
|
+
nil
|
574
634
|
end
|
575
635
|
rescue => e
|
576
636
|
promise.break(e)
|
577
|
-
|
637
|
+
|
638
|
+
nil
|
578
639
|
end
|
579
640
|
|
580
|
-
|
581
|
-
|
582
|
-
|
583
|
-
|
584
|
-
|
585
|
-
|
586
|
-
|
587
|
-
|
588
|
-
|
589
|
-
|
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
|
-
|
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.
|
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
|
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
|
-
|
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
|
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
|
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
|
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
|
742
|
+
@logger.debug("Host #{host.ip} schema version is #{version}")
|
673
743
|
end
|
674
744
|
|
675
745
|
if versions.one?
|
676
|
-
@logger.
|
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
|
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
|
-
|
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
|
779
|
+
raise r.to_error(Statements::Simple.new("USE #{Util.escape_name(keyspace)}"))
|
711
780
|
else
|
712
|
-
raise "
|
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
|
815
|
+
raise r.to_error(VOID_STATEMENT)
|
749
816
|
else
|
750
|
-
raise "
|
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
|
762
|
-
|
763
|
-
|
764
|
-
|
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
|