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