cassandra-driver 1.0.0.beta.3 → 1.0.0.rc.1
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 +13 -5
- data/README.md +8 -6
- data/lib/cassandra.rb +99 -13
- 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/client.rb +2 -2
- data/lib/cassandra/client/batch.rb +3 -3
- data/lib/cassandra/client/client.rb +12 -12
- data/lib/cassandra/client/connection_manager.rb +2 -2
- data/lib/cassandra/client/connector.rb +6 -10
- data/lib/cassandra/client/prepared_statement.rb +4 -4
- data/lib/cassandra/client/request_runner.rb +1 -2
- data/lib/cassandra/cluster.rb +15 -5
- data/lib/cassandra/cluster/client.rb +158 -96
- data/lib/cassandra/cluster/connector.rb +42 -27
- data/lib/cassandra/cluster/control_connection.rb +384 -132
- data/lib/cassandra/cluster/options.rb +5 -2
- data/lib/cassandra/cluster/registry.rb +19 -9
- 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 +28 -20
- data/lib/cassandra/errors.rb +325 -35
- data/lib/cassandra/future.rb +3 -3
- data/lib/cassandra/load_balancing/policies/dc_aware_round_robin.rb +7 -3
- data/lib/cassandra/load_balancing/policies/token_aware.rb +1 -1
- 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 +74 -8
- data/lib/cassandra/protocol/frame_decoder.rb +2 -2
- data/lib/cassandra/protocol/frame_encoder.rb +1 -1
- 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/retry.rb +3 -5
- data/lib/cassandra/session.rb +11 -5
- data/lib/cassandra/table.rb +1 -1
- data/lib/cassandra/time_uuid.rb +21 -83
- data/lib/cassandra/util.rb +1 -1
- data/lib/cassandra/uuid.rb +6 -4
- data/lib/cassandra/uuid/generator.rb +207 -0
- data/lib/cassandra/version.rb +1 -1
- metadata +24 -19
@@ -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/client.rb
CHANGED
@@ -118,9 +118,9 @@ module Cassandra
|
|
118
118
|
# significant events pass an object implementing the standard Ruby logger
|
119
119
|
# interface (e.g. quacks like `Logger` from the standard library) with
|
120
120
|
# this option.
|
121
|
-
# @raise Cassandra::
|
121
|
+
# @raise [Cassandra::Errors::IOError] when a connection couldn't be established
|
122
122
|
# to any node
|
123
|
-
# @raise Cassandra::Errors::
|
123
|
+
# @raise [Cassandra::Errors::ExecutionError] when the specified keyspace does not exist
|
124
124
|
# or when the specifed CQL version is not supported.
|
125
125
|
# @return [Cassandra::Client::Client]
|
126
126
|
def self.connect(options={})
|
@@ -54,7 +54,7 @@ module Cassandra
|
|
54
54
|
# @param [Hash] options an options hash or a symbol (as a shortcut for
|
55
55
|
# specifying the consistency), see {Cassandra::Client::Client#execute} for
|
56
56
|
# full details about how this value is interpreted.
|
57
|
-
# @raise [Cassandra::Errors::
|
57
|
+
# @raise [Cassandra::Errors::ExecutionError] raised when there is an error on the server side
|
58
58
|
# @raise [Cassandra::Errors::NotPreparedError] raised in the unlikely event that a
|
59
59
|
# prepared statement was not prepared on the chosen connection
|
60
60
|
# @return [Cassandra::Client::VoidResult] a batch always returns a void result
|
@@ -77,7 +77,7 @@ module Cassandra
|
|
77
77
|
#
|
78
78
|
# Execute the batch and return the result.
|
79
79
|
#
|
80
|
-
# @raise [Cassandra::Errors::
|
80
|
+
# @raise [Cassandra::Errors::ExecutionError] raised when there is an error on the server side
|
81
81
|
# @raise [Cassandra::Errors::NotPreparedError] raised in the unlikely event that a
|
82
82
|
# prepared statement was not prepared on the chosen connection
|
83
83
|
# @return [Cassandra::Client::VoidResult] a batch always returns a void result
|
@@ -86,7 +86,7 @@ module Cassandra
|
|
86
86
|
# @private
|
87
87
|
class AsynchronousBatch < Batch
|
88
88
|
def initialize(type, execute_options_decoder, connection_manager, options=nil)
|
89
|
-
raise ArgumentError, "Unknown batch type: #{type}" unless BATCH_TYPES.include?(type)
|
89
|
+
raise ::ArgumentError, "Unknown batch type: #{type}" unless BATCH_TYPES.include?(type)
|
90
90
|
@type = type
|
91
91
|
@execute_options_decoder = execute_options_decoder
|
92
92
|
@connection_manager = connection_manager
|
@@ -67,7 +67,7 @@ module Cassandra
|
|
67
67
|
# The the second parameter is meant for internal use only.
|
68
68
|
#
|
69
69
|
# @param [String] keyspace
|
70
|
-
# @raise [Cassandra::Errors::
|
70
|
+
# @raise [Cassandra::Errors::ClientError] raised when the client is not connected
|
71
71
|
# @return [nil]
|
72
72
|
|
73
73
|
# @!method execute(cql, *values, options={})
|
@@ -135,7 +135,7 @@ module Cassandra
|
|
135
135
|
# to be `:serial` by the server if none is specified. Ignored for non-
|
136
136
|
# conditional queries.
|
137
137
|
# @option options [Integer] :timeout (nil) How long to wait
|
138
|
-
# for a response. If this timeout expires a {Cassandra::TimeoutError} will
|
138
|
+
# for a response. If this timeout expires a {Cassandra::Errors::TimeoutError} will
|
139
139
|
# be raised.
|
140
140
|
# @option options [Boolean] :trace (false) Request tracing
|
141
141
|
# for this request. See {Cassandra::Client::QueryResult} and
|
@@ -152,10 +152,10 @@ module Cassandra
|
|
152
152
|
# element should be the type of the corresponding value, or nil if you
|
153
153
|
# prefer the encoder to guess. The types should be provided as lower
|
154
154
|
# case symbols, e.g. `:int`, `:time_uuid`, etc.
|
155
|
-
# @raise [Cassandra::Errors::
|
156
|
-
# @raise [Cassandra::TimeoutError] raised when a timeout was specified and no
|
155
|
+
# @raise [Cassandra::Errors::ClientError] raised when the client is not connected
|
156
|
+
# @raise [Cassandra::Errors::TimeoutError] raised when a timeout was specified and no
|
157
157
|
# response was received within the timeout.
|
158
|
-
# @raise [Cassandra::Errors::
|
158
|
+
# @raise [Cassandra::Errors::ExecutionError] raised when the CQL has syntax errors or for
|
159
159
|
# other situations when the server complains.
|
160
160
|
# @return [nil, Cassandra::Client::QueryResult, Cassandra::Client::VoidResult] Some
|
161
161
|
# queries have no result and return `nil`, but `SELECT` statements
|
@@ -170,10 +170,10 @@ module Cassandra
|
|
170
170
|
#
|
171
171
|
# @see Cassandra::Client::PreparedStatement
|
172
172
|
# @param [String] cql The CQL to prepare
|
173
|
-
# @raise [Cassandra::Errors::
|
173
|
+
# @raise [Cassandra::Errors::ClientError] raised when the client is not connected
|
174
174
|
# @raise [Cassandra::Errors::IoError] raised when there is an IO error, for example
|
175
175
|
# if the server suddenly closes the connection
|
176
|
-
# @raise [Cassandra::Errors::
|
176
|
+
# @raise [Cassandra::Errors::ExecutionError] raised when there is an error on the server
|
177
177
|
# side, for example when you specify a malformed CQL query
|
178
178
|
# @return [Cassandra::Client::PreparedStatement] an object encapsulating the
|
179
179
|
# prepared statement
|
@@ -230,7 +230,7 @@ module Cassandra
|
|
230
230
|
@cql_version = options[:cql_version]
|
231
231
|
@logger = options[:logger] || NullLogger.new
|
232
232
|
@protocol_version = options[:protocol_version] || 2
|
233
|
-
@io_reactor = options[:io_reactor] || Io::IoReactor.new
|
233
|
+
@io_reactor = options[:io_reactor] || Ione::Io::IoReactor.new
|
234
234
|
@hosts = extract_hosts(options)
|
235
235
|
@initial_keyspace = options[:keyspace]
|
236
236
|
@connections_per_node = options[:connections_per_node] || 1
|
@@ -380,12 +380,12 @@ module Cassandra
|
|
380
380
|
def connect_with_protocol_version_fallback
|
381
381
|
f = create_cluster_connector.connect_all(@hosts, @connections_per_node)
|
382
382
|
f.fallback do |error|
|
383
|
-
if error.is_a?(Errors::
|
383
|
+
if error.is_a?(Errors::ProtocolError) && @protocol_version > 1
|
384
384
|
@logger.warn('Could not connect using protocol version %d (will try again with %d): %s' % [@protocol_version, @protocol_version - 1, error.message])
|
385
385
|
@protocol_version -= 1
|
386
386
|
connect_with_protocol_version_fallback
|
387
387
|
else
|
388
|
-
raise error
|
388
|
+
raise(error, error.message, error.backtrace)
|
389
389
|
end
|
390
390
|
end
|
391
391
|
end
|
@@ -447,7 +447,7 @@ module Cassandra
|
|
447
447
|
end
|
448
448
|
|
449
449
|
def with_failure_handler
|
450
|
-
return Ione::Future.failed(Errors::
|
450
|
+
return Ione::Future.failed(Errors::ClientError.new) unless can_execute?
|
451
451
|
yield
|
452
452
|
rescue => e
|
453
453
|
Ione::Future.failed(e)
|
@@ -472,7 +472,7 @@ module Cassandra
|
|
472
472
|
if connected?
|
473
473
|
begin
|
474
474
|
register_event_listener(@connection_manager.random_connection)
|
475
|
-
rescue Errors::
|
475
|
+
rescue Errors::IOError
|
476
476
|
# we had started closing down after the connection check
|
477
477
|
end
|
478
478
|
end
|
@@ -53,7 +53,7 @@ module Cassandra
|
|
53
53
|
end
|
54
54
|
|
55
55
|
def random_connection
|
56
|
-
raise Errors::
|
56
|
+
raise Errors::IOError, 'Not connected' unless connected?
|
57
57
|
@lock.synchronize do
|
58
58
|
@connections.sample
|
59
59
|
end
|
@@ -61,7 +61,7 @@ module Cassandra
|
|
61
61
|
|
62
62
|
def each_connection(&callback)
|
63
63
|
return self unless block_given?
|
64
|
-
raise Errors::
|
64
|
+
raise Errors::IOError, 'Not connected' unless connected?
|
65
65
|
@lock.synchronize do
|
66
66
|
@connections.each(&callback)
|
67
67
|
end
|
@@ -39,11 +39,8 @@ module Cassandra
|
|
39
39
|
Ione::Future.all(*connections).map do |connections|
|
40
40
|
connected_connections = connections.select(&:connected?)
|
41
41
|
if connected_connections.empty?
|
42
|
-
|
43
|
-
|
44
|
-
e = Errors::AuthenticationError.new(e.message)
|
45
|
-
end
|
46
|
-
raise e
|
42
|
+
error = connections.first.error
|
43
|
+
raise(error, error.message, error.backtrace)
|
47
44
|
end
|
48
45
|
connected_connections
|
49
46
|
end
|
@@ -131,10 +128,9 @@ module Cassandra
|
|
131
128
|
compression = @compressor && @compressor.algorithm
|
132
129
|
supported_algorithms = pending_connection[:compression]
|
133
130
|
if compression && !supported_algorithms.include?(compression)
|
134
|
-
@logger.
|
131
|
+
@logger.info("Compression with #{compression.inspect} is not supported by host at #{pending_connection.host}, supported algorithms are #{supported_algorithms.inspect}")
|
135
132
|
compression = nil
|
136
133
|
end
|
137
|
-
@logger.debug('Using "%s" compression' % compression) if compression
|
138
134
|
supported_cql_versions = pending_connection[:cql_version]
|
139
135
|
cql_version = (supported_cql_versions && !supported_cql_versions.empty?) ? supported_cql_versions.first : '3.1.0'
|
140
136
|
|
@@ -165,10 +161,10 @@ module Cassandra
|
|
165
161
|
elsif @auth_provider
|
166
162
|
Ione::Future.failed(Errors::AuthenticationError.new('Auth provider does not support the required authentication class "%s" and/or protocol version %d' % [pending_connection.authentication_class, @protocol_version]))
|
167
163
|
else
|
168
|
-
Ione::Future.failed(Errors::AuthenticationError.new('Server requested authentication, but
|
164
|
+
Ione::Future.failed(Errors::AuthenticationError.new('Server requested authentication, but client was not configured to authenticate'))
|
169
165
|
end
|
170
166
|
rescue => e
|
171
|
-
Ione::Future.failed(Errors::AuthenticationError.new('Auth provider
|
167
|
+
Ione::Future.failed(Errors::AuthenticationError.new('Auth provider error (%s: %s)' % [e.class.name, e.message]))
|
172
168
|
end
|
173
169
|
else
|
174
170
|
Ione::Future.resolved(pending_connection)
|
@@ -205,7 +201,7 @@ module Cassandra
|
|
205
201
|
request = Protocol::CredentialsRequest.new(@credentials)
|
206
202
|
pending_connection.execute(request).map(pending_connection)
|
207
203
|
else
|
208
|
-
Ione::Future.failed(Errors::AuthenticationError.new('Server requested authentication, but
|
204
|
+
Ione::Future.failed(Errors::AuthenticationError.new('Server requested authentication, but client was not configured to authenticate'))
|
209
205
|
end
|
210
206
|
else
|
211
207
|
Ione::Future.resolved(pending_connection)
|
@@ -90,7 +90,7 @@ module Cassandra
|
|
90
90
|
# for details on which options are available.
|
91
91
|
# @raise [ArgumentError] raised when number of argument does not match
|
92
92
|
# the number of parameters needed to be bound to the statement.
|
93
|
-
# @raise [Cassandra::Errors::
|
93
|
+
# @raise [Cassandra::Errors::ClientError] raised when the client is not connected
|
94
94
|
# @raise [Cassandra::Errors::IoError] raised when there is an IO error, for example
|
95
95
|
# if the server suddenly closes the connection
|
96
96
|
# @raise [Cassandra::Errors::QueryError] raised when there is an error on the server side
|
@@ -221,10 +221,10 @@ module Cassandra
|
|
221
221
|
def add_to_batch(batch, connection, bound_args)
|
222
222
|
statement_id = connection[self]
|
223
223
|
unless statement_id
|
224
|
-
raise
|
224
|
+
raise ::ArgumentError, "Statement was not prepared"
|
225
225
|
end
|
226
226
|
unless bound_args.size == @raw_metadata.size
|
227
|
-
raise ArgumentError, "Expected #{@raw_metadata.size} arguments, got #{bound_args.size}"
|
227
|
+
raise ::ArgumentError, "Expected #{@raw_metadata.size} arguments, got #{bound_args.size}"
|
228
228
|
end
|
229
229
|
batch.add_prepared(statement_id, @raw_metadata, bound_args)
|
230
230
|
end
|
@@ -234,7 +234,7 @@ module Cassandra
|
|
234
234
|
def run(args, connection)
|
235
235
|
bound_args = args.shift(@raw_metadata.size)
|
236
236
|
unless bound_args.size == @raw_metadata.size && args.size <= 1
|
237
|
-
raise ArgumentError, "Expected #{@raw_metadata.size} arguments, got #{bound_args.size}"
|
237
|
+
raise ::ArgumentError, "Expected #{@raw_metadata.size} arguments, got #{bound_args.size}"
|
238
238
|
end
|
239
239
|
options = @execute_options_decoder.decode_options(args.last)
|
240
240
|
statement_id = connection[self]
|
@@ -31,8 +31,7 @@ module Cassandra
|
|
31
31
|
response.trace_id ? VoidResult.new(response.trace_id) : VoidResult::INSTANCE
|
32
32
|
when Protocol::ErrorResponse
|
33
33
|
cql = request.is_a?(Protocol::QueryRequest) ? request.cql : nil
|
34
|
-
|
35
|
-
raise Errors::QueryError.new(response.code, response.message, cql, details)
|
34
|
+
raise response.to_error(cql)
|
36
35
|
when Protocol::SetKeyspaceResultResponse
|
37
36
|
KeyspaceChanged.new(response.keyspace)
|
38
37
|
when Protocol::AuthenticateResponse
|
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
|
#
|
@@ -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,14 +52,20 @@ 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)
|
59
66
|
|
60
|
-
futures = @connecting_hosts.map do |host|
|
61
|
-
f =
|
67
|
+
futures = @connecting_hosts.map do |(host, distance)|
|
68
|
+
f = connect_to_host(host, distance)
|
62
69
|
f.recover do |error|
|
63
70
|
Cassandra::Client::FailedConnection.new(error, host)
|
64
71
|
end
|
@@ -116,12 +123,18 @@ module Cassandra
|
|
116
123
|
end
|
117
124
|
|
118
125
|
def host_up(host)
|
126
|
+
distance = nil
|
127
|
+
|
119
128
|
synchronize do
|
120
129
|
return Ione::Future.resolved if @connecting_hosts.include?(host)
|
121
|
-
|
130
|
+
|
131
|
+
distance = @load_balancing_policy.distance(host)
|
132
|
+
return Ione::Future.resolved if distance == :ignore
|
133
|
+
|
134
|
+
@connecting_hosts[host] = distance
|
122
135
|
end
|
123
136
|
|
124
|
-
connect_to_host_maybe_retry(host,
|
137
|
+
connect_to_host_maybe_retry(host, distance).map(nil)
|
125
138
|
end
|
126
139
|
|
127
140
|
def host_down(host)
|
@@ -130,7 +143,6 @@ module Cassandra
|
|
130
143
|
synchronize do
|
131
144
|
return Ione::Future.resolved if !@connections.has_key?(host) && !@connecting_hosts.include?(host)
|
132
145
|
|
133
|
-
@logger.info("Session disconnecting from ip=#{host.ip}")
|
134
146
|
@connecting_hosts.delete(host)
|
135
147
|
@prepared_statements.delete(host)
|
136
148
|
@preparing_statements.delete(host)
|
@@ -209,13 +221,17 @@ module Cassandra
|
|
209
221
|
:unlogged => Protocol::BatchRequest::UNLOGGED_TYPE,
|
210
222
|
:counter => Protocol::BatchRequest::COUNTER_TYPE,
|
211
223
|
}.freeze
|
212
|
-
CLIENT_CLOSED = Ione::Future.failed(Errors::ClientError.new('
|
213
|
-
|
214
|
-
|
224
|
+
CLIENT_CLOSED = Ione::Future.failed(Errors::ClientError.new('Client closed'))
|
225
|
+
NOT_CONNECTED = Errors::ClientError.new('Client not connected')
|
226
|
+
CLIENT_NOT_CONNECTED = Ione::Future.failed(NOT_CONNECTED)
|
215
227
|
|
216
228
|
UNAVAILABLE_ERROR_CODE = 0x1000
|
217
229
|
WRITE_TIMEOUT_ERROR_CODE = 0x1100
|
218
230
|
READ_TIMEOUT_ERROR_CODE = 0x1200
|
231
|
+
OVERLOADED_ERROR_CODE = 0x1001
|
232
|
+
SERVER_ERROR_CODE = 0x0000
|
233
|
+
BOOTSTRAPPING_ERROR_CODE = 0x1002
|
234
|
+
UNPREPARED_ERROR_CODE = 0x2500
|
219
235
|
|
220
236
|
SELECT_SCHEMA_PEERS = Protocol::QueryRequest.new("SELECT peer, rpc_address, schema_version FROM system.peers", nil, nil, :one)
|
221
237
|
SELECT_SCHEMA_LOCAL = Protocol::QueryRequest.new("SELECT schema_version FROM system.local WHERE key='local'", nil, nil, :one)
|
@@ -226,14 +242,14 @@ module Cassandra
|
|
226
242
|
@state = :connected
|
227
243
|
end
|
228
244
|
|
229
|
-
@logger.info('Session
|
245
|
+
@logger.info('Session created')
|
230
246
|
else
|
231
247
|
synchronize do
|
232
248
|
@state = :defunct
|
233
249
|
end
|
234
250
|
|
235
251
|
f.on_failure do |e|
|
236
|
-
@logger.error(
|
252
|
+
@logger.error("Session failed to connect (#{e.class.name}: #{e.message})")
|
237
253
|
end
|
238
254
|
|
239
255
|
close
|
@@ -248,20 +264,17 @@ module Cassandra
|
|
248
264
|
@logger.info('Session closed')
|
249
265
|
else
|
250
266
|
f.on_failure do |e|
|
251
|
-
@logger.error(
|
267
|
+
@logger.error("Session failed to close (#{e.class.name}: #{e.message})")
|
252
268
|
end
|
253
269
|
end
|
254
270
|
end
|
255
271
|
end
|
256
272
|
|
257
273
|
def close_connections
|
258
|
-
@logger.info('Session closing')
|
259
|
-
|
260
274
|
futures = []
|
261
275
|
synchronize do
|
262
276
|
@connections.each do |host, connections|
|
263
277
|
connections.snapshot.each do |c|
|
264
|
-
@logger.info("Disconnecting ip=#{c.host}")
|
265
278
|
futures << c.close
|
266
279
|
end
|
267
280
|
end.clear
|
@@ -274,7 +287,7 @@ module Cassandra
|
|
274
287
|
f = connect_to_host(host, distance)
|
275
288
|
|
276
289
|
f.on_failure do |e|
|
277
|
-
connect_to_host_with_retry(host, @reconnection_policy.schedule)
|
290
|
+
connect_to_host_with_retry(host, @reconnection_policy.schedule)
|
278
291
|
end
|
279
292
|
|
280
293
|
f
|
@@ -283,21 +296,23 @@ module Cassandra
|
|
283
296
|
def connect_to_host_with_retry(host, schedule)
|
284
297
|
interval = schedule.next
|
285
298
|
|
286
|
-
@logger.
|
299
|
+
@logger.debug("Reconnecting to #{host.ip} in #{interval} seconds")
|
287
300
|
|
288
301
|
f = @reactor.schedule_timer(interval)
|
289
302
|
f.flat_map do
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
Ione::Future.failed(e)
|
296
|
-
end
|
303
|
+
distance = nil
|
304
|
+
|
305
|
+
synchronize do
|
306
|
+
if @connecting_hosts.include?(host)
|
307
|
+
distance = @load_balancing_policy.distance(host)
|
297
308
|
end
|
298
|
-
|
299
|
-
@logger.info("Session reconnection to ip=#{host.ip} cancelled")
|
309
|
+
end
|
300
310
|
|
311
|
+
if distance && distance != :ignore
|
312
|
+
connect_to_host(host, distance).fallback do |e|
|
313
|
+
connect_to_host_with_retry(host, schedule)
|
314
|
+
end
|
315
|
+
else
|
301
316
|
NO_CONNECTIONS
|
302
317
|
end
|
303
318
|
end
|
@@ -312,18 +327,16 @@ module Cassandra
|
|
312
327
|
when :remote
|
313
328
|
pool_size = @connection_options.connections_per_remote_node
|
314
329
|
else
|
315
|
-
|
330
|
+
@logger.error("Invalid load balancing distance, not connecting to #{host.ip}. Distance must be one of #{LoadBalancing::DISTANCES.inspect}, #{distance.inspect} given")
|
331
|
+
return NO_CONNECTIONS
|
316
332
|
end
|
317
333
|
|
318
|
-
@logger.info("Session connecting to ip=#{host.ip}")
|
319
|
-
|
320
334
|
f = @connector.connect_many(host, pool_size)
|
321
335
|
|
322
336
|
f.on_value do |connections|
|
323
337
|
manager = nil
|
324
338
|
|
325
339
|
synchronize do
|
326
|
-
@logger.info("Session connected to ip=#{host.ip}")
|
327
340
|
@connecting_hosts.delete(host)
|
328
341
|
@prepared_statements[host] = {}
|
329
342
|
@preparing_statements[host] = {}
|
@@ -338,7 +351,7 @@ module Cassandra
|
|
338
351
|
|
339
352
|
def execute_by_plan(promise, keyspace, statement, options, request, plan, timeout, errors = nil, hosts = [])
|
340
353
|
unless plan.has_next?
|
341
|
-
promise.break(Errors::NoHostsAvailable.new(errors
|
354
|
+
promise.break(Errors::NoHostsAvailable.new(errors))
|
342
355
|
return
|
343
356
|
end
|
344
357
|
|
@@ -361,12 +374,13 @@ module Cassandra
|
|
361
374
|
prepare_and_send_request_by_plan(host, connection, promise, keyspace, statement, options, request, plan, timeout, errors, hosts)
|
362
375
|
else
|
363
376
|
s.on_failure do |e|
|
364
|
-
|
365
|
-
|
366
|
-
else
|
377
|
+
case e
|
378
|
+
when Errors::HostError
|
367
379
|
errors ||= {}
|
368
380
|
errors[host] = e
|
369
381
|
execute_by_plan(promise, keyspace, statement, options, request, plan, timeout, errors, hosts)
|
382
|
+
else
|
383
|
+
promise.break(e)
|
370
384
|
end
|
371
385
|
end
|
372
386
|
end
|
@@ -395,12 +409,13 @@ module Cassandra
|
|
395
409
|
do_send_request_by_plan(host, connection, promise, keyspace, statement, options, request, plan, timeout, errors, hosts)
|
396
410
|
else
|
397
411
|
prepare.on_failure do |e|
|
398
|
-
|
399
|
-
|
400
|
-
else
|
412
|
+
case e
|
413
|
+
when Errors::HostError
|
401
414
|
errors ||= {}
|
402
415
|
errors[host] = e
|
403
416
|
execute_by_plan(promise, keyspace, statement, options, request, plan, timeout, errors, hosts)
|
417
|
+
else
|
418
|
+
promise.break(e)
|
404
419
|
end
|
405
420
|
end
|
406
421
|
end
|
@@ -410,7 +425,7 @@ module Cassandra
|
|
410
425
|
|
411
426
|
def batch_by_plan(promise, keyspace, statement, options, plan, timeout, errors = nil, hosts = [])
|
412
427
|
unless plan.has_next?
|
413
|
-
promise.break(Errors::NoHostsAvailable.new(errors
|
428
|
+
promise.break(Errors::NoHostsAvailable.new(errors))
|
414
429
|
return
|
415
430
|
end
|
416
431
|
|
@@ -433,12 +448,13 @@ module Cassandra
|
|
433
448
|
batch_and_send_request_by_plan(host, connection, promise, keyspace, statement, options, plan, timeout, errors, hosts)
|
434
449
|
else
|
435
450
|
s.on_failure do |e|
|
436
|
-
|
437
|
-
|
438
|
-
else
|
451
|
+
case e
|
452
|
+
when Errors::HostError
|
439
453
|
errors ||= {}
|
440
454
|
errors[host] = e
|
441
455
|
batch_by_plan(promise, keyspace, statement, options, plan, timeout, errors, hosts)
|
456
|
+
else
|
457
|
+
promise.break(e)
|
442
458
|
end
|
443
459
|
end
|
444
460
|
end
|
@@ -492,12 +508,13 @@ module Cassandra
|
|
492
508
|
do_send_request_by_plan(host, connection, promise, keyspace, statement, options, request, plan, timeout, errors, hosts)
|
493
509
|
else
|
494
510
|
f.on_failure do |e|
|
495
|
-
|
496
|
-
|
497
|
-
else
|
511
|
+
case e
|
512
|
+
when Errors::HostError
|
498
513
|
errors ||= {}
|
499
514
|
errors[host] = e
|
500
515
|
batch_by_plan(promise, keyspace, statement, options, plan, timeout, errors, hosts)
|
516
|
+
else
|
517
|
+
promise.break(e)
|
501
518
|
end
|
502
519
|
end
|
503
520
|
end
|
@@ -507,7 +524,7 @@ module Cassandra
|
|
507
524
|
|
508
525
|
def send_request_by_plan(promise, keyspace, statement, options, request, plan, timeout, errors = nil, hosts = [])
|
509
526
|
unless plan.has_next?
|
510
|
-
promise.break(Errors::NoHostsAvailable.new(errors
|
527
|
+
promise.break(Errors::NoHostsAvailable.new(errors))
|
511
528
|
return
|
512
529
|
end
|
513
530
|
|
@@ -530,12 +547,13 @@ module Cassandra
|
|
530
547
|
do_send_request_by_plan(host, connection, promise, keyspace, statement, options, request, plan, timeout, errors, hosts)
|
531
548
|
else
|
532
549
|
s.on_failure do |e|
|
533
|
-
|
534
|
-
|
535
|
-
else
|
550
|
+
case e
|
551
|
+
when Errors::HostError
|
536
552
|
errors ||= {}
|
537
553
|
errors[host] = e
|
538
554
|
send_request_by_plan(promise, keyspace, statement, options, request, plan, timeout, errors, hosts)
|
555
|
+
else
|
556
|
+
promise.break(e)
|
539
557
|
end
|
540
558
|
end
|
541
559
|
end
|
@@ -564,32 +582,78 @@ module Cassandra
|
|
564
582
|
when UNAVAILABLE_ERROR_CODE
|
565
583
|
@retry_policy.unavailable(statement, details[:cl], details[:required], details[:alive], retries)
|
566
584
|
when WRITE_TIMEOUT_ERROR_CODE
|
567
|
-
details[:
|
568
|
-
@retry_policy.write_timeout(statement, details[:cl], write_type, details[:blockfor], details[:received], retries)
|
585
|
+
@retry_policy.write_timeout(statement, details[:cl], details[:write_type], details[:blockfor], details[:received], retries)
|
569
586
|
when READ_TIMEOUT_ERROR_CODE
|
570
587
|
@retry_policy.read_timeout(statement, details[:cl], details[:blockfor], details[:received], details[:data_present], retries)
|
588
|
+
when UNPREPARED_ERROR_CODE
|
589
|
+
cql = statement.cql
|
590
|
+
|
591
|
+
synchronize do
|
592
|
+
@preparing_statements[host].delete(cql)
|
593
|
+
@prepared_statements[host].delete(cql)
|
594
|
+
end
|
595
|
+
|
596
|
+
prepare = prepare_statement(host, connection, cql, timeout)
|
597
|
+
prepare.on_complete do |_|
|
598
|
+
if prepare.resolved?
|
599
|
+
request.id = prepare.value
|
600
|
+
do_send_request_by_plan(host, connection, promise, keyspace, statement, options, request, plan, timeout, errors, hosts)
|
601
|
+
else
|
602
|
+
prepare.on_failure do |e|
|
603
|
+
case e
|
604
|
+
when Errors::HostError
|
605
|
+
errors ||= {}
|
606
|
+
errors[host] = e
|
607
|
+
execute_by_plan(promise, keyspace, statement, options, request, plan, timeout, errors, hosts)
|
608
|
+
else
|
609
|
+
promise.break(e)
|
610
|
+
end
|
611
|
+
end
|
612
|
+
end
|
613
|
+
end
|
614
|
+
|
615
|
+
nil
|
571
616
|
else
|
572
|
-
promise.break(
|
573
|
-
|
617
|
+
promise.break(r.to_error(statement))
|
618
|
+
|
619
|
+
nil
|
574
620
|
end
|
575
621
|
rescue => e
|
576
622
|
promise.break(e)
|
577
|
-
|
623
|
+
|
624
|
+
nil
|
578
625
|
end
|
579
626
|
|
580
|
-
|
581
|
-
|
582
|
-
|
583
|
-
|
584
|
-
|
585
|
-
|
586
|
-
|
587
|
-
|
588
|
-
|
589
|
-
|
627
|
+
if decision
|
628
|
+
case decision
|
629
|
+
when Retry::Decisions::Retry
|
630
|
+
request.consistency = decision.consistency
|
631
|
+
do_send_request_by_plan(host, connection, promise, keyspace, statement, options, request, plan, timeout, errors, hosts, retries + 1)
|
632
|
+
when Retry::Decisions::Ignore
|
633
|
+
promise.fulfill(Results::Void.new(r.trace_id, keyspace, statement, options, hosts, request.consistency, retries, self, @futures))
|
634
|
+
when Retry::Decisions::Reraise
|
635
|
+
promise.break(r.to_error(statement))
|
636
|
+
else
|
637
|
+
promise.break(r.to_error(statement))
|
638
|
+
end
|
590
639
|
end
|
591
640
|
when Protocol::ErrorResponse
|
592
|
-
|
641
|
+
case r.code
|
642
|
+
when OVERLOADED_ERROR_CODE, SERVER_ERROR_CODE, BOOTSTRAPPING_ERROR_CODE
|
643
|
+
errors ||= {}
|
644
|
+
errors[host] = r.to_error(statement)
|
645
|
+
|
646
|
+
case request
|
647
|
+
when Protocol::QueryRequest, Protocol::PrepareRequest
|
648
|
+
send_request_by_plan(promise, keyspace, statement, options, request, plan, timeout, errors, hosts)
|
649
|
+
when Protocol::ExecuteRequest
|
650
|
+
execute_by_plan(promise, keyspace, statement, options, request, plan, timeout, errors, hosts)
|
651
|
+
when Protocol::BatchRequest
|
652
|
+
batch_by_plan(promise, keyspace, statement, options, plan, timeout, errors, hosts)
|
653
|
+
end
|
654
|
+
else
|
655
|
+
promise.break(r.to_error(statement))
|
656
|
+
end
|
593
657
|
when Protocol::SetKeyspaceResultResponse
|
594
658
|
@keyspace = r.keyspace
|
595
659
|
promise.fulfill(Results::Void.new(r.trace_id, keyspace, statement, options, hosts, request.consistency, retries, self, @futures))
|
@@ -611,10 +675,11 @@ module Cassandra
|
|
611
675
|
@keyspace = nil
|
612
676
|
end
|
613
677
|
|
678
|
+
@logger.debug('Waiting for schema to propagate to all hosts after a change')
|
614
679
|
wait_for_schema_agreement(connection, @reconnection_policy.schedule).on_complete do |f|
|
615
680
|
unless f.resolved?
|
616
681
|
f.on_failure do |e|
|
617
|
-
@logger.error("Schema agreement
|
682
|
+
@logger.error("Schema agreement failure (#{e.class.name}: #{e.message})")
|
618
683
|
end
|
619
684
|
end
|
620
685
|
promise.fulfill(Results::Void.new(r.trace_id, keyspace, statement, options, hosts, request.consistency, retries, self, @futures))
|
@@ -642,42 +707,35 @@ module Cassandra
|
|
642
707
|
end
|
643
708
|
|
644
709
|
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)
|
710
|
+
peers = send_select_request(connection, SELECT_SCHEMA_PEERS)
|
711
|
+
local = send_select_request(connection, SELECT_SCHEMA_LOCAL)
|
649
712
|
|
650
713
|
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
714
|
versions = ::Set.new
|
657
715
|
|
658
716
|
unless local.empty?
|
659
717
|
host = @registry.host(connection.host)
|
660
718
|
|
661
|
-
if host && host
|
719
|
+
if host && @load_balancing_policy.distance(host) != :ignore
|
662
720
|
versions << version = local.first['schema_version']
|
663
|
-
@logger.debug("Host #{host.ip} schema version
|
721
|
+
@logger.debug("Host #{host.ip} schema version is #{version}")
|
664
722
|
end
|
665
723
|
end
|
666
724
|
|
667
725
|
peers.each do |row|
|
668
726
|
host = @registry.host(peer_ip(row))
|
669
|
-
next unless host && host
|
727
|
+
next unless host && @load_balancing_policy.distance(host) != :ignore
|
670
728
|
|
671
729
|
versions << version = row['schema_version']
|
672
|
-
@logger.debug("Host #{host.ip} schema version
|
730
|
+
@logger.debug("Host #{host.ip} schema version is #{version}")
|
673
731
|
end
|
674
732
|
|
675
733
|
if versions.one?
|
676
|
-
@logger.
|
734
|
+
@logger.debug('All hosts have the same schema')
|
677
735
|
Ione::Future.resolved
|
678
736
|
else
|
679
737
|
interval = schedule.next
|
680
|
-
@logger.info("Hosts
|
738
|
+
@logger.info("Hosts have different schema versions: #{versions.to_a.inspect}, retrying in #{interval} seconds")
|
681
739
|
@reactor.schedule_timer(interval).flat_map do
|
682
740
|
wait_for_schema_agreement(connection, schedule)
|
683
741
|
end
|
@@ -688,7 +746,8 @@ module Cassandra
|
|
688
746
|
def peer_ip(data)
|
689
747
|
ip = data['rpc_address']
|
690
748
|
ip = data['peer'] if ip == '0.0.0.0'
|
691
|
-
|
749
|
+
|
750
|
+
@address_resolver.resolve(ip)
|
692
751
|
end
|
693
752
|
|
694
753
|
def switch_keyspace(connection, keyspace, timeout)
|
@@ -697,19 +756,17 @@ module Cassandra
|
|
697
756
|
|
698
757
|
return pending_switch || Ione::Future.resolved if pending_keyspace == keyspace
|
699
758
|
|
700
|
-
request = Protocol::QueryRequest.new("USE #{keyspace}", nil, nil, :one)
|
759
|
+
request = Protocol::QueryRequest.new("USE #{Util.escape_name(keyspace)}", nil, nil, :one)
|
701
760
|
|
702
761
|
f = connection.send_request(request, timeout).map do |r|
|
703
762
|
case r
|
704
763
|
when Protocol::SetKeyspaceResultResponse
|
705
764
|
@keyspace = r.keyspace
|
706
765
|
nil
|
707
|
-
when Protocol::DetailedErrorResponse
|
708
|
-
raise Errors::QueryError.new(r.code, r.message, request.cql, r.details)
|
709
766
|
when Protocol::ErrorResponse
|
710
|
-
raise
|
767
|
+
raise r.to_error(Statements::Simple.new("USE #{Util.escape_name(keyspace)}"))
|
711
768
|
else
|
712
|
-
raise "
|
769
|
+
raise Errors::InternalError, "Unexpected response #{r.inspect}"
|
713
770
|
end
|
714
771
|
end
|
715
772
|
|
@@ -742,12 +799,10 @@ module Cassandra
|
|
742
799
|
@preparing_statements[host].delete(cql)
|
743
800
|
end
|
744
801
|
id
|
745
|
-
when Protocol::DetailedErrorResponse
|
746
|
-
raise Errors::QueryError.new(r.code, r.message, cql, r.details)
|
747
802
|
when Protocol::ErrorResponse
|
748
|
-
raise
|
803
|
+
raise r.to_error(VOID_STATEMENT)
|
749
804
|
else
|
750
|
-
raise "
|
805
|
+
raise Errors::InternalError, "Unexpected response #{r.inspect}"
|
751
806
|
end
|
752
807
|
end
|
753
808
|
|
@@ -758,10 +813,17 @@ module Cassandra
|
|
758
813
|
f
|
759
814
|
end
|
760
815
|
|
761
|
-
def
|
762
|
-
|
763
|
-
|
764
|
-
|
816
|
+
def send_select_request(connection, request)
|
817
|
+
connection.send_request(request).map do |r|
|
818
|
+
case r
|
819
|
+
when Protocol::RowsResultResponse
|
820
|
+
r.rows
|
821
|
+
when Protocol::ErrorResponse
|
822
|
+
raise r.to_error(VOID_STATEMENT)
|
823
|
+
else
|
824
|
+
raise Errors::InternalError, "Unexpected response #{r.inspect}"
|
825
|
+
end
|
826
|
+
end
|
765
827
|
end
|
766
828
|
end
|
767
829
|
end
|