cassandra-driver 3.0.0.beta.1 → 3.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 +8 -8
- data/README.md +90 -38
- data/ext/cassandra_murmur3/cassandra_murmur3.c +1 -1
- data/lib/cassandra.rb +327 -130
- data/lib/cassandra/address_resolution.rb +1 -1
- data/lib/cassandra/address_resolution/policies/ec2_multi_region.rb +1 -1
- data/lib/cassandra/address_resolution/policies/none.rb +1 -1
- data/lib/cassandra/aggregate.rb +21 -7
- data/lib/cassandra/argument.rb +2 -2
- data/lib/cassandra/auth.rb +4 -4
- data/lib/cassandra/auth/providers.rb +1 -1
- data/lib/cassandra/auth/providers/password.rb +9 -5
- data/lib/cassandra/cassandra_logger.rb +80 -0
- data/lib/cassandra/cluster.rb +38 -9
- data/lib/cassandra/cluster/client.rb +801 -205
- data/lib/cassandra/cluster/connection_pool.rb +2 -2
- data/lib/cassandra/cluster/connector.rb +74 -25
- data/lib/cassandra/cluster/control_connection.rb +217 -82
- data/lib/cassandra/cluster/failed_connection.rb +1 -1
- data/lib/cassandra/cluster/metadata.rb +12 -4
- data/lib/cassandra/cluster/options.rb +60 -11
- data/lib/cassandra/cluster/registry.rb +69 -16
- data/lib/cassandra/cluster/schema.rb +25 -7
- data/lib/cassandra/cluster/schema/cql_type_parser.rb +15 -10
- data/lib/cassandra/cluster/schema/fetchers.rb +263 -106
- data/lib/cassandra/cluster/schema/fqcn_type_parser.rb +41 -36
- data/lib/cassandra/cluster/schema/partitioners.rb +1 -1
- data/lib/cassandra/cluster/schema/partitioners/murmur3.rb +3 -3
- data/lib/cassandra/cluster/schema/partitioners/ordered.rb +1 -1
- data/lib/cassandra/cluster/schema/partitioners/random.rb +1 -1
- data/lib/cassandra/cluster/schema/replication_strategies.rb +1 -1
- data/lib/cassandra/cluster/schema/replication_strategies/network_topology.rb +19 -18
- data/lib/cassandra/cluster/schema/replication_strategies/none.rb +1 -1
- data/lib/cassandra/cluster/schema/replication_strategies/simple.rb +1 -1
- data/lib/cassandra/column.rb +3 -3
- data/lib/cassandra/compression.rb +1 -1
- data/lib/cassandra/compression/compressors/lz4.rb +4 -3
- data/lib/cassandra/compression/compressors/snappy.rb +4 -3
- data/lib/cassandra/driver.rb +103 -41
- data/lib/cassandra/errors.rb +265 -30
- data/lib/cassandra/execution/info.rb +16 -5
- data/lib/cassandra/execution/options.rb +99 -54
- data/lib/cassandra/execution/trace.rb +16 -9
- data/lib/cassandra/executors.rb +1 -1
- data/lib/cassandra/function.rb +19 -13
- data/lib/cassandra/function_collection.rb +85 -0
- data/lib/cassandra/future.rb +106 -48
- data/lib/cassandra/host.rb +10 -4
- data/lib/cassandra/keyspace.rb +90 -33
- data/lib/cassandra/listener.rb +1 -1
- data/lib/cassandra/load_balancing.rb +2 -2
- data/lib/cassandra/load_balancing/policies.rb +1 -1
- data/lib/cassandra/load_balancing/policies/dc_aware_round_robin.rb +18 -18
- data/lib/cassandra/load_balancing/policies/round_robin.rb +1 -1
- data/lib/cassandra/load_balancing/policies/token_aware.rb +15 -13
- data/lib/cassandra/load_balancing/policies/white_list.rb +11 -5
- data/lib/cassandra/null_logger.rb +27 -6
- data/lib/cassandra/protocol.rb +1 -1
- data/lib/cassandra/protocol/coder.rb +78 -39
- data/lib/cassandra/protocol/cql_byte_buffer.rb +50 -33
- data/lib/cassandra/protocol/cql_protocol_handler.rb +44 -45
- data/lib/cassandra/protocol/request.rb +2 -2
- data/lib/cassandra/protocol/requests/auth_response_request.rb +3 -3
- data/lib/cassandra/protocol/requests/batch_request.rb +16 -7
- data/lib/cassandra/protocol/requests/credentials_request.rb +3 -3
- data/lib/cassandra/protocol/requests/execute_request.rb +41 -20
- data/lib/cassandra/protocol/requests/options_request.rb +1 -1
- data/lib/cassandra/protocol/requests/prepare_request.rb +5 -5
- data/lib/cassandra/protocol/requests/query_request.rb +27 -22
- data/lib/cassandra/protocol/requests/register_request.rb +2 -2
- data/lib/cassandra/protocol/requests/startup_request.rb +6 -4
- data/lib/cassandra/protocol/requests/void_query_request.rb +1 -1
- data/lib/cassandra/protocol/response.rb +2 -2
- data/lib/cassandra/protocol/responses/already_exists_error_response.rb +12 -2
- data/lib/cassandra/protocol/responses/auth_challenge_response.rb +1 -1
- data/lib/cassandra/protocol/responses/auth_success_response.rb +1 -1
- data/lib/cassandra/protocol/responses/authenticate_response.rb +1 -1
- data/lib/cassandra/protocol/responses/error_response.rb +101 -13
- data/lib/cassandra/protocol/responses/event_response.rb +1 -1
- data/lib/cassandra/protocol/responses/function_failure_error_response.rb +13 -2
- data/lib/cassandra/protocol/responses/prepared_result_response.rb +11 -5
- data/lib/cassandra/protocol/responses/raw_rows_result_response.rb +14 -9
- data/lib/cassandra/protocol/responses/read_failure_error_response.rb +26 -4
- data/lib/cassandra/protocol/responses/read_timeout_error_response.rb +22 -3
- data/lib/cassandra/protocol/responses/ready_response.rb +3 -3
- data/lib/cassandra/protocol/responses/result_response.rb +4 -2
- data/lib/cassandra/protocol/responses/rows_result_response.rb +5 -3
- data/lib/cassandra/protocol/responses/schema_change_event_response.rb +5 -4
- data/lib/cassandra/protocol/responses/schema_change_result_response.rb +16 -9
- data/lib/cassandra/protocol/responses/set_keyspace_result_response.rb +2 -2
- data/lib/cassandra/protocol/responses/status_change_event_response.rb +2 -2
- data/lib/cassandra/protocol/responses/supported_response.rb +1 -1
- data/lib/cassandra/protocol/responses/topology_change_event_response.rb +1 -1
- data/lib/cassandra/protocol/responses/unavailable_error_response.rb +20 -3
- data/lib/cassandra/protocol/responses/unprepared_error_response.rb +11 -2
- data/lib/cassandra/protocol/responses/void_result_response.rb +1 -1
- data/lib/cassandra/protocol/responses/write_failure_error_response.rb +26 -4
- data/lib/cassandra/protocol/responses/write_timeout_error_response.rb +22 -3
- data/lib/cassandra/protocol/v1.rb +101 -36
- data/lib/cassandra/protocol/v3.rb +124 -51
- data/lib/cassandra/protocol/v4.rb +172 -68
- data/lib/cassandra/reconnection.rb +1 -1
- data/lib/cassandra/reconnection/policies.rb +1 -1
- data/lib/cassandra/reconnection/policies/constant.rb +2 -4
- data/lib/cassandra/reconnection/policies/exponential.rb +6 -6
- data/lib/cassandra/result.rb +53 -19
- data/lib/cassandra/retry.rb +8 -8
- data/lib/cassandra/retry/policies.rb +1 -1
- data/lib/cassandra/retry/policies/default.rb +1 -1
- data/lib/cassandra/retry/policies/downgrading_consistency.rb +7 -3
- data/lib/cassandra/retry/policies/fallthrough.rb +1 -1
- data/lib/cassandra/session.rb +22 -16
- data/lib/cassandra/statement.rb +1 -1
- data/lib/cassandra/statements.rb +1 -1
- data/lib/cassandra/statements/batch.rb +16 -10
- data/lib/cassandra/statements/bound.rb +10 -3
- data/lib/cassandra/statements/prepared.rb +59 -15
- data/lib/cassandra/statements/simple.rb +23 -10
- data/lib/cassandra/statements/void.rb +1 -1
- data/lib/cassandra/table.rb +79 -30
- data/lib/cassandra/time.rb +11 -6
- data/lib/cassandra/time_uuid.rb +7 -7
- data/lib/cassandra/tuple.rb +16 -8
- data/lib/cassandra/types.rb +20 -9
- data/lib/cassandra/udt.rb +32 -36
- data/lib/cassandra/util.rb +20 -13
- data/lib/cassandra/uuid.rb +22 -15
- data/lib/cassandra/uuid/generator.rb +7 -5
- data/lib/cassandra/version.rb +2 -2
- data/lib/datastax/cassandra.rb +1 -1
- metadata +5 -3
@@ -1,7 +1,7 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
|
3
3
|
#--
|
4
|
-
# Copyright 2013-
|
4
|
+
# Copyright 2013-2016 DataStax, Inc.
|
5
5
|
#
|
6
6
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
7
7
|
# you may not use this file except in compliance with the License.
|
@@ -72,7 +72,7 @@ module Cassandra
|
|
72
72
|
@connections.each(&callback)
|
73
73
|
end
|
74
74
|
end
|
75
|
-
|
75
|
+
alias each each_connection
|
76
76
|
end
|
77
77
|
end
|
78
78
|
end
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
|
3
3
|
#--
|
4
|
-
# Copyright 2013-
|
4
|
+
# Copyright 2013-2016 DataStax, Inc.
|
5
5
|
#
|
6
6
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
7
7
|
# you may not use this file except in compliance with the License.
|
@@ -22,7 +22,11 @@ module Cassandra
|
|
22
22
|
class Connector
|
23
23
|
include MonitorMixin
|
24
24
|
|
25
|
-
def initialize(logger,
|
25
|
+
def initialize(logger,
|
26
|
+
io_reactor,
|
27
|
+
cluster_registry,
|
28
|
+
connection_options,
|
29
|
+
execution_options)
|
26
30
|
@logger = logger
|
27
31
|
@reactor = io_reactor
|
28
32
|
@registry = cluster_registry
|
@@ -82,7 +86,7 @@ module Cassandra
|
|
82
86
|
|
83
87
|
synchronize do
|
84
88
|
@open_connections[host] ||= []
|
85
|
-
@open_connections[host]
|
89
|
+
@open_connections[host] << connection
|
86
90
|
end
|
87
91
|
|
88
92
|
timer = @reactor.schedule_timer(UNCLAIMED_TIMEOUT)
|
@@ -112,7 +116,10 @@ module Cassandra
|
|
112
116
|
UNCLAIMED_TIMEOUT = 5 # close unclaimed connections in five seconds
|
113
117
|
|
114
118
|
def do_connect(host)
|
115
|
-
@reactor.connect(host.ip.to_s,
|
119
|
+
@reactor.connect(host.ip.to_s,
|
120
|
+
@connection_options.port,
|
121
|
+
timeout: @connection_options.connect_timeout,
|
122
|
+
ssl: @connection_options.ssl) do |connection|
|
116
123
|
raise Errors::ClientError, 'Not connected, reactor stopped' unless connection
|
117
124
|
|
118
125
|
if @connection_options.nodelay?
|
@@ -121,7 +128,13 @@ module Cassandra
|
|
121
128
|
connection.to_io.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 0)
|
122
129
|
end
|
123
130
|
|
124
|
-
Protocol::CqlProtocolHandler.new(connection,
|
131
|
+
Protocol::CqlProtocolHandler.new(connection,
|
132
|
+
@reactor,
|
133
|
+
@connection_options.protocol_version,
|
134
|
+
@connection_options.compressor,
|
135
|
+
@connection_options.heartbeat_interval,
|
136
|
+
@connection_options.idle_timeout,
|
137
|
+
@connection_options.requests_per_connection)
|
125
138
|
end.flat_map do |connection|
|
126
139
|
f = request_options(connection)
|
127
140
|
f = f.flat_map do |options|
|
@@ -129,12 +142,16 @@ module Cassandra
|
|
129
142
|
supported_algorithms = options['COMPRESSION']
|
130
143
|
|
131
144
|
if compression && !supported_algorithms.include?(compression)
|
132
|
-
@logger.warn("Compression with #{compression.inspect} is not supported
|
145
|
+
@logger.warn("Compression with #{compression.inspect} is not supported " \
|
146
|
+
"by host at #{host.ip}, supported algorithms are " \
|
147
|
+
"#{supported_algorithms.inspect}")
|
133
148
|
compression = nil
|
134
149
|
end
|
135
150
|
|
136
151
|
supported_cql_versions = options['CQL_VERSION']
|
137
|
-
cql_version = (supported_cql_versions && !supported_cql_versions.empty?) ?
|
152
|
+
cql_version = (supported_cql_versions && !supported_cql_versions.empty?) ?
|
153
|
+
supported_cql_versions.first :
|
154
|
+
'3.1.0'
|
138
155
|
|
139
156
|
startup_connection(connection, cql_version, compression)
|
140
157
|
end
|
@@ -143,7 +160,8 @@ module Cassandra
|
|
143
160
|
when Errors::ProtocolError
|
144
161
|
synchronize do
|
145
162
|
if @connection_options.protocol_version > 1
|
146
|
-
@logger.info("Host #{host.ip} doesn't support protocol version
|
163
|
+
@logger.info("Host #{host.ip} doesn't support protocol version " \
|
164
|
+
"#{@connection_options.protocol_version}, downgrading")
|
147
165
|
@connection_options.protocol_version -= 1
|
148
166
|
do_connect(host)
|
149
167
|
else
|
@@ -152,7 +170,7 @@ module Cassandra
|
|
152
170
|
end
|
153
171
|
when Errors::TimeoutError
|
154
172
|
future = Ione::CompletableFuture.new
|
155
|
-
connection.close(error).on_complete do |
|
173
|
+
connection.close(error).on_complete do |_f|
|
156
174
|
future.fail(error)
|
157
175
|
end
|
158
176
|
future
|
@@ -173,7 +191,8 @@ module Cassandra
|
|
173
191
|
end
|
174
192
|
|
175
193
|
def startup_connection(connection, cql_version, compression)
|
176
|
-
connection.send_request(Protocol::StartupRequest.new(cql_version, compression),
|
194
|
+
connection.send_request(Protocol::StartupRequest.new(cql_version, compression),
|
195
|
+
@execution_options.timeout).flat_map do |r|
|
177
196
|
case r
|
178
197
|
when Protocol::AuthenticateResponse
|
179
198
|
if @connection_options.protocol_version == 1
|
@@ -181,28 +200,48 @@ module Cassandra
|
|
181
200
|
if credentials
|
182
201
|
send_credentials(connection, credentials)
|
183
202
|
else
|
184
|
-
Ione::Future.failed(
|
203
|
+
Ione::Future.failed(cannot_authenticate_error)
|
185
204
|
end
|
186
205
|
else
|
187
|
-
authenticator = @connection_options.create_authenticator(
|
206
|
+
authenticator = @connection_options.create_authenticator(
|
207
|
+
r.authentication_class)
|
188
208
|
if authenticator
|
189
|
-
challenge_response_cycle(connection,
|
209
|
+
challenge_response_cycle(connection,
|
210
|
+
authenticator,
|
211
|
+
authenticator.initial_response)
|
190
212
|
else
|
191
|
-
Ione::Future.failed(
|
213
|
+
Ione::Future.failed(cannot_authenticate_error)
|
192
214
|
end
|
193
215
|
end
|
194
216
|
when Protocol::ReadyResponse
|
195
217
|
::Ione::Future.resolved(connection)
|
196
218
|
when Protocol::ErrorResponse
|
197
|
-
::Ione::Future.failed(
|
219
|
+
::Ione::Future.failed(
|
220
|
+
r.to_error(nil, VOID_STATEMENT, VOID_OPTIONS, EMPTY_LIST, :one, 0))
|
198
221
|
else
|
199
|
-
::Ione::Future.failed(
|
222
|
+
::Ione::Future.failed(
|
223
|
+
Errors::InternalError.new("Unexpected response #{r.inspect}"))
|
200
224
|
end
|
201
225
|
end
|
202
226
|
end
|
203
227
|
|
228
|
+
def cannot_authenticate_error
|
229
|
+
Errors::AuthenticationError.new(
|
230
|
+
'Server requested authentication, but client was not configured to ' \
|
231
|
+
'authenticate',
|
232
|
+
nil,
|
233
|
+
nil,
|
234
|
+
nil,
|
235
|
+
VOID_STATEMENT,
|
236
|
+
VOID_OPTIONS,
|
237
|
+
EMPTY_LIST,
|
238
|
+
:one,
|
239
|
+
0)
|
240
|
+
end
|
241
|
+
|
204
242
|
def request_options(connection)
|
205
|
-
connection.send_request(Protocol::OptionsRequest.new,
|
243
|
+
connection.send_request(Protocol::OptionsRequest.new,
|
244
|
+
@execution_options.timeout).map do |r|
|
206
245
|
case r
|
207
246
|
when Protocol::SupportedResponse
|
208
247
|
r.options
|
@@ -215,7 +254,8 @@ module Cassandra
|
|
215
254
|
end
|
216
255
|
|
217
256
|
def send_credentials(connection, credentials)
|
218
|
-
connection.send_request(Protocol::CredentialsRequest.new(credentials),
|
257
|
+
connection.send_request(Protocol::CredentialsRequest.new(credentials),
|
258
|
+
@execution_options.timeout).map do |r|
|
219
259
|
case r
|
220
260
|
when Protocol::ReadyResponse
|
221
261
|
connection
|
@@ -228,18 +268,25 @@ module Cassandra
|
|
228
268
|
end
|
229
269
|
|
230
270
|
def challenge_response_cycle(connection, authenticator, token)
|
231
|
-
connection.send_request(Protocol::AuthResponseRequest.new(token),
|
271
|
+
connection.send_request(Protocol::AuthResponseRequest.new(token),
|
272
|
+
@execution_options.timeout).flat_map do |r|
|
232
273
|
case r
|
233
274
|
when Protocol::AuthChallengeResponse
|
234
275
|
token = authenticator.challenge_response(r.token)
|
235
276
|
challenge_response_cycle(pending_connection, authenticator, token)
|
236
277
|
when Protocol::AuthSuccessResponse
|
237
|
-
|
278
|
+
begin
|
279
|
+
authenticator.authentication_successful(r.token)
|
280
|
+
rescue
|
281
|
+
nil
|
282
|
+
end
|
238
283
|
::Ione::Future.resolved(connection)
|
239
284
|
when Protocol::ErrorResponse
|
240
|
-
::Ione::Future.failed(
|
285
|
+
::Ione::Future.failed(
|
286
|
+
r.to_error(nil, VOID_STATEMENT, VOID_OPTIONS, EMPTY_LIST, :one, 0))
|
241
287
|
else
|
242
|
-
::Ione::Future.failed(
|
288
|
+
::Ione::Future.failed(
|
289
|
+
Errors::InternalError.new("Unexpected response #{r.inspect}"))
|
243
290
|
end
|
244
291
|
end
|
245
292
|
end
|
@@ -282,7 +329,8 @@ module Cassandra
|
|
282
329
|
end
|
283
330
|
end
|
284
331
|
|
285
|
-
@logger.debug("Host #{host.ip} closed connection (#{error.class.name}:
|
332
|
+
@logger.debug("Host #{host.ip} closed connection (#{error.class.name}: " \
|
333
|
+
"#{error.message})") if error
|
286
334
|
|
287
335
|
if notify
|
288
336
|
@logger.warn("Host #{host.ip} closed all connections")
|
@@ -296,10 +344,11 @@ module Cassandra
|
|
296
344
|
notify = false
|
297
345
|
|
298
346
|
synchronize do
|
299
|
-
notify = !error.nil? && !@connections.
|
347
|
+
notify = !error.nil? && !@connections.key?(host)
|
300
348
|
end
|
301
349
|
|
302
|
-
@logger.debug("Host #{host.ip} refused connection (#{error.class.name}:
|
350
|
+
@logger.debug("Host #{host.ip} refused connection (#{error.class.name}: " \
|
351
|
+
"#{error.message})")
|
303
352
|
|
304
353
|
if notify
|
305
354
|
@logger.warn("Host #{host.ip} refused all connections")
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
|
3
3
|
#--
|
4
|
-
# Copyright 2013-
|
4
|
+
# Copyright 2013-2016 DataStax, Inc.
|
5
5
|
#
|
6
6
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
7
7
|
# you may not use this file except in compliance with the License.
|
@@ -84,8 +84,11 @@ module Cassandra
|
|
84
84
|
timer = @refreshing_statuses.delete(host)
|
85
85
|
@io_reactor.cancel_timer(timer) if timer
|
86
86
|
|
87
|
-
unless @connection ||
|
88
|
-
|
87
|
+
unless @connection ||
|
88
|
+
(@status == :closing || @status == :closed) ||
|
89
|
+
@load_balancing_policy.distance(host) == :ignore
|
90
|
+
return connect_to_first_available(
|
91
|
+
@load_balancing_policy.plan(nil, VOID_STATEMENT, VOID_OPTIONS))
|
89
92
|
end
|
90
93
|
end
|
91
94
|
|
@@ -97,18 +100,22 @@ module Cassandra
|
|
97
100
|
timer = nil
|
98
101
|
|
99
102
|
synchronize do
|
100
|
-
|
103
|
+
if @refreshing_statuses[host] ||
|
104
|
+
@load_balancing_policy.distance(host) == :ignore
|
105
|
+
return Ione::Future.resolved
|
106
|
+
end
|
101
107
|
|
102
108
|
schedule = @reconnection_policy.schedule
|
103
109
|
timeout = schedule.next
|
104
110
|
|
105
|
-
@logger.debug("Starting to continuously refresh status of #{host.ip} in
|
111
|
+
@logger.debug("Starting to continuously refresh status of #{host.ip} in " \
|
112
|
+
"#{timeout} seconds")
|
106
113
|
|
107
114
|
@refreshing_statuses[host] = timer = @io_reactor.schedule_timer(timeout)
|
108
115
|
end
|
109
116
|
|
110
117
|
timer.on_value do
|
111
|
-
refresh_host_status(host).fallback do |
|
118
|
+
refresh_host_status(host).fallback do |_e|
|
112
119
|
refresh_host_status_with_retry(timer, host, schedule)
|
113
120
|
end
|
114
121
|
end
|
@@ -134,13 +141,28 @@ module Cassandra
|
|
134
141
|
end
|
135
142
|
|
136
143
|
def inspect
|
137
|
-
"#<#{self.class.name}:0x#{
|
144
|
+
"#<#{self.class.name}:0x#{object_id.to_s(16)}>"
|
138
145
|
end
|
139
146
|
|
140
147
|
private
|
141
148
|
|
142
|
-
SELECT_LOCAL = Protocol::QueryRequest.new(
|
143
|
-
|
149
|
+
SELECT_LOCAL = Protocol::QueryRequest.new(
|
150
|
+
'SELECT rack, data_center, host_id, release_version, tokens, partitioner ' \
|
151
|
+
'FROM system.local',
|
152
|
+
EMPTY_LIST,
|
153
|
+
EMPTY_LIST,
|
154
|
+
:one)
|
155
|
+
SELECT_PEERS = Protocol::QueryRequest.new(
|
156
|
+
'SELECT peer, rack, data_center, host_id, rpc_address, release_version, tokens ' \
|
157
|
+
'FROM system.peers',
|
158
|
+
EMPTY_LIST,
|
159
|
+
EMPTY_LIST,
|
160
|
+
:one)
|
161
|
+
|
162
|
+
SELECT_PEER_QUERY =
|
163
|
+
'SELECT rack, data_center, host_id, rpc_address, release_version, tokens ' \
|
164
|
+
'FROM system.peers ' \
|
165
|
+
"WHERE peer = '%s'".freeze
|
144
166
|
|
145
167
|
def reconnect_async(schedule)
|
146
168
|
timeout = schedule.next
|
@@ -159,25 +181,28 @@ module Cassandra
|
|
159
181
|
f.fallback do |e|
|
160
182
|
@logger.error("Control connection failed (#{e.class.name}: #{e.message})")
|
161
183
|
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
end
|
184
|
+
return Ione::Future.resolved unless synchronize { @status == :reconnecting }
|
185
|
+
|
186
|
+
# We're reconnecting...
|
187
|
+
reconnect_async(schedule)
|
167
188
|
end
|
168
189
|
end
|
169
190
|
|
170
191
|
def register_async
|
171
192
|
connection = @connection
|
172
193
|
|
173
|
-
|
194
|
+
if connection.nil?
|
195
|
+
return Ione::Future.failed(Errors::ClientError.new('Not connected'))
|
196
|
+
end
|
174
197
|
|
175
198
|
request = Protocol::RegisterRequest.new(
|
176
199
|
Protocol::TopologyChangeEventResponse::TYPE,
|
177
200
|
Protocol::StatusChangeEventResponse::TYPE
|
178
201
|
)
|
179
202
|
|
180
|
-
|
203
|
+
if @connection_options.synchronize_schema?
|
204
|
+
request.events << Protocol::SchemaChangeEventResponse::TYPE
|
205
|
+
end
|
181
206
|
|
182
207
|
f = connection.send_request(request)
|
183
208
|
f = f.map do |r|
|
@@ -190,7 +215,7 @@ module Cassandra
|
|
190
215
|
raise Errors::InternalError, "Unexpected response #{r.inspect}"
|
191
216
|
end
|
192
217
|
end
|
193
|
-
f
|
218
|
+
f.map do
|
194
219
|
connection.on_event do |event|
|
195
220
|
@logger.debug("Event received #{event}")
|
196
221
|
|
@@ -205,7 +230,7 @@ module Cassandra
|
|
205
230
|
@registry.host_up(address)
|
206
231
|
else
|
207
232
|
refresh_host_async_maybe_retry(address)
|
208
|
-
|
233
|
+
refresh_schema_async_wrapper
|
209
234
|
end
|
210
235
|
when 'DOWN'
|
211
236
|
@registry.host_down(event.address)
|
@@ -214,11 +239,11 @@ module Cassandra
|
|
214
239
|
|
215
240
|
unless @registry.has_host?(address)
|
216
241
|
refresh_host_async_maybe_retry(address)
|
217
|
-
|
242
|
+
refresh_schema_async_wrapper
|
218
243
|
end
|
219
244
|
when 'REMOVED_NODE'
|
220
245
|
@registry.host_lost(event.address)
|
221
|
-
|
246
|
+
refresh_schema_async_wrapper
|
222
247
|
end
|
223
248
|
end
|
224
249
|
end
|
@@ -230,21 +255,25 @@ module Cassandra
|
|
230
255
|
def refresh_schema_async
|
231
256
|
connection = @connection
|
232
257
|
|
233
|
-
@logger.info(
|
258
|
+
@logger.info('Refreshing schema')
|
234
259
|
|
235
|
-
|
260
|
+
if connection.nil?
|
261
|
+
return Ione::Future.failed(Errors::ClientError.new('Not connected'))
|
262
|
+
end
|
236
263
|
|
237
264
|
@schema_fetcher.fetch(connection).map do |keyspaces|
|
238
265
|
@schema.replace(keyspaces)
|
239
266
|
@metadata.rebuild_token_map
|
240
|
-
@logger.info(
|
267
|
+
@logger.info('Schema refreshed')
|
241
268
|
end
|
242
269
|
end
|
243
270
|
|
244
271
|
def refresh_keyspace_async(keyspace_name)
|
245
272
|
connection = @connection
|
246
273
|
|
247
|
-
|
274
|
+
if connection.nil?
|
275
|
+
return Ione::Future.failed(Errors::ClientError.new('Not connected'))
|
276
|
+
end
|
248
277
|
|
249
278
|
@logger.info("Refreshing keyspace \"#{keyspace_name}\"")
|
250
279
|
|
@@ -262,7 +291,9 @@ module Cassandra
|
|
262
291
|
def refresh_table_async(keyspace_name, table_name)
|
263
292
|
connection = @connection
|
264
293
|
|
265
|
-
|
294
|
+
if connection.nil?
|
295
|
+
return Ione::Future.failed(Errors::ClientError.new('Not connected'))
|
296
|
+
end
|
266
297
|
|
267
298
|
@logger.info("Refreshing table \"#{keyspace_name}.#{table_name}\"")
|
268
299
|
|
@@ -280,7 +311,9 @@ module Cassandra
|
|
280
311
|
def refresh_type_async(keyspace_name, type_name)
|
281
312
|
connection = @connection
|
282
313
|
|
283
|
-
|
314
|
+
if connection.nil?
|
315
|
+
return Ione::Future.failed(Errors::ClientError.new('Not connected'))
|
316
|
+
end
|
284
317
|
|
285
318
|
@logger.info("Refreshing user-defined type \"#{keyspace_name}.#{type_name}\"")
|
286
319
|
|
@@ -298,40 +331,57 @@ module Cassandra
|
|
298
331
|
def refresh_function_async(keyspace_name, function_name, function_args)
|
299
332
|
connection = @connection
|
300
333
|
|
301
|
-
|
334
|
+
if connection.nil?
|
335
|
+
return Ione::Future.failed(Errors::ClientError.new('Not connected'))
|
336
|
+
end
|
302
337
|
|
303
|
-
@logger.info(
|
338
|
+
@logger.info('Refreshing user-defined function ' \
|
339
|
+
"\"#{keyspace_name}.#{function_name}\"")
|
304
340
|
|
305
341
|
# function_args is an array of string, and we need an array of parsed types.
|
306
|
-
parsed_function_args =
|
307
|
-
|
342
|
+
parsed_function_args =
|
343
|
+
@schema_fetcher.parse_argument_types(connection, keyspace_name, function_args)
|
344
|
+
@schema_fetcher.fetch_function(connection,
|
345
|
+
keyspace_name,
|
346
|
+
function_name,
|
347
|
+
parsed_function_args).map do |function|
|
308
348
|
if function
|
309
349
|
@schema.replace_function(function)
|
310
350
|
else
|
311
351
|
@schema.delete_function(keyspace_name, function_name, parsed_function_args)
|
312
352
|
end
|
313
353
|
|
314
|
-
@logger.info(
|
354
|
+
@logger.info('Refreshed user-defined function ' \
|
355
|
+
"\"#{keyspace_name}.#{function_name}(#{function_args.join(',')})\"")
|
315
356
|
end
|
316
357
|
end
|
317
358
|
|
318
359
|
def refresh_aggregate_async(keyspace_name, aggregate_name, aggregate_args)
|
319
360
|
connection = @connection
|
320
361
|
|
321
|
-
|
362
|
+
if connection.nil?
|
363
|
+
return Ione::Future.failed(Errors::ClientError.new('Not connected'))
|
364
|
+
end
|
322
365
|
|
323
|
-
@logger.info(
|
366
|
+
@logger.info('Refreshing user-defined aggregate ' \
|
367
|
+
"\"#{keyspace_name}.#{aggregate_name}\"")
|
324
368
|
|
325
369
|
# aggregate_args is an array of string, and we need an array of parsed types.
|
326
|
-
parsed_aggregate_args = @schema_fetcher.parse_argument_types(connection,
|
327
|
-
|
370
|
+
parsed_aggregate_args = @schema_fetcher.parse_argument_types(connection,
|
371
|
+
keyspace_name,
|
372
|
+
aggregate_args)
|
373
|
+
@schema_fetcher.fetch_aggregate(connection,
|
374
|
+
keyspace_name,
|
375
|
+
aggregate_name,
|
376
|
+
parsed_aggregate_args).map do |aggregate|
|
328
377
|
if aggregate
|
329
378
|
@schema.replace_aggregate(aggregate)
|
330
379
|
else
|
331
380
|
@schema.delete_aggregate(keyspace_name, aggregate_name, parsed_aggregate_args)
|
332
381
|
end
|
333
382
|
|
334
|
-
@logger.info(
|
383
|
+
@logger.info('Refreshed user-defined aggregate ' \
|
384
|
+
"\"#{keyspace_name}.#{aggregate_name}(#{aggregate_args.join(',')})\"")
|
335
385
|
end
|
336
386
|
end
|
337
387
|
|
@@ -360,7 +410,8 @@ module Cassandra
|
|
360
410
|
|
361
411
|
def refresh_peers_async_retry(error, schedule)
|
362
412
|
timeout = schedule.next
|
363
|
-
@logger.info("Failed to refresh hosts (#{error.class.name}:
|
413
|
+
@logger.info("Failed to refresh hosts (#{error.class.name}: " \
|
414
|
+
"#{error.message}), retrying in #{timeout}")
|
364
415
|
|
365
416
|
timer = @io_reactor.schedule_timer(timeout)
|
366
417
|
timer.flat_map do
|
@@ -381,9 +432,11 @@ module Cassandra
|
|
381
432
|
def refresh_peers_async
|
382
433
|
connection = @connection
|
383
434
|
|
384
|
-
|
435
|
+
if connection.nil?
|
436
|
+
return Ione::Future.failed(Errors::ClientError.new('Not connected'))
|
437
|
+
end
|
385
438
|
|
386
|
-
@logger.info(
|
439
|
+
@logger.info('Refreshing peers metadata')
|
387
440
|
|
388
441
|
send_select_request(connection, SELECT_PEERS).map do |peers|
|
389
442
|
@logger.debug("#{peers.size} peer(s) found")
|
@@ -403,7 +456,7 @@ module Cassandra
|
|
403
456
|
@registry.host_lost(host.ip) unless ips.include?(host.ip)
|
404
457
|
end
|
405
458
|
|
406
|
-
@logger.info(
|
459
|
+
@logger.info('Refreshed peers metadata')
|
407
460
|
|
408
461
|
nil
|
409
462
|
end
|
@@ -412,7 +465,9 @@ module Cassandra
|
|
412
465
|
def refresh_metadata_async
|
413
466
|
connection = @connection
|
414
467
|
|
415
|
-
|
468
|
+
if connection.nil?
|
469
|
+
return Ione::Future.failed(Errors::ClientError.new('Not connected'))
|
470
|
+
end
|
416
471
|
|
417
472
|
@logger.info("Refreshing connected host's metadata")
|
418
473
|
|
@@ -452,7 +507,7 @@ module Cassandra
|
|
452
507
|
end
|
453
508
|
|
454
509
|
timer.on_value do
|
455
|
-
refresh_host_status(host).fallback do |
|
510
|
+
refresh_host_status(host).fallback do |_e|
|
456
511
|
refresh_host_status_with_retry(timer, host, schedule)
|
457
512
|
end
|
458
513
|
end
|
@@ -483,7 +538,8 @@ module Cassandra
|
|
483
538
|
|
484
539
|
def refresh_host_async_retry(address, error, schedule)
|
485
540
|
timeout = schedule.next
|
486
|
-
@logger.info("Failed to refresh host #{address
|
541
|
+
@logger.info("Failed to refresh host #{address} (#{error.class.name}: " \
|
542
|
+
"#{error.message}), retrying in #{timeout}")
|
487
543
|
|
488
544
|
timer = @io_reactor.schedule_timer(timeout)
|
489
545
|
timer.flat_map do
|
@@ -503,17 +559,22 @@ module Cassandra
|
|
503
559
|
|
504
560
|
def refresh_host_async(address)
|
505
561
|
connection = @connection
|
506
|
-
|
562
|
+
if connection.nil?
|
563
|
+
return Ione::Future.failed(Errors::ClientError.new('Not connected'))
|
564
|
+
end
|
507
565
|
|
508
566
|
ip = address.to_s
|
509
567
|
|
510
568
|
@logger.info("Refreshing host metadata: #{ip}")
|
511
569
|
|
512
|
-
if ip == connection.host
|
513
|
-
|
514
|
-
|
515
|
-
|
516
|
-
|
570
|
+
request = if ip == connection.host
|
571
|
+
SELECT_LOCAL
|
572
|
+
else
|
573
|
+
Protocol::QueryRequest.new(SELECT_PEER_QUERY % ip,
|
574
|
+
EMPTY_LIST,
|
575
|
+
EMPTY_LIST,
|
576
|
+
:one)
|
577
|
+
end
|
517
578
|
|
518
579
|
send_select_request(connection, request).map do |rows|
|
519
580
|
if rows.empty?
|
@@ -568,9 +629,10 @@ Control connection failed and is unlikely to recover.
|
|
568
629
|
end
|
569
630
|
|
570
631
|
if cause
|
571
|
-
@logger.info(
|
632
|
+
@logger.info('Control connection closed ' \
|
633
|
+
"(#{cause.class.name}: #{cause.message})")
|
572
634
|
else
|
573
|
-
@logger.info(
|
635
|
+
@logger.info('Control connection closed')
|
574
636
|
end
|
575
637
|
|
576
638
|
@connection = nil
|
@@ -585,9 +647,12 @@ Control connection failed and is unlikely to recover.
|
|
585
647
|
end
|
586
648
|
f = f.flat_map { register_async }
|
587
649
|
f = f.flat_map { refresh_peers_async_maybe_retry }
|
588
|
-
|
650
|
+
if @connection_options.synchronize_schema?
|
651
|
+
f = f.flat_map { refresh_maybe_retry(:schema) }
|
652
|
+
end
|
589
653
|
f = f.fallback do |error|
|
590
|
-
@logger.debug("Connection to #{host.ip} failed
|
654
|
+
@logger.debug("Connection to #{host.ip} failed " \
|
655
|
+
"(#{error.class.name}: #{error.message})")
|
591
656
|
|
592
657
|
case error
|
593
658
|
when Errors::HostError, Errors::TimeoutError
|
@@ -599,8 +664,8 @@ Control connection failed and is unlikely to recover.
|
|
599
664
|
end
|
600
665
|
end
|
601
666
|
|
602
|
-
f.on_complete do |
|
603
|
-
@logger.info('Control connection established') if
|
667
|
+
f.on_complete do |connection_future|
|
668
|
+
@logger.info('Control connection established') if connection_future.resolved?
|
604
669
|
end
|
605
670
|
|
606
671
|
f
|
@@ -631,7 +696,7 @@ Control connection failed and is unlikely to recover.
|
|
631
696
|
schema_changes.each do |change|
|
632
697
|
keyspace = change.keyspace
|
633
698
|
|
634
|
-
next if refresh_keyspaces.
|
699
|
+
next if refresh_keyspaces.key?(keyspace)
|
635
700
|
|
636
701
|
case change.target
|
637
702
|
when Protocol::Constants::SCHEMA_CHANGE_TARGET_KEYSPACE
|
@@ -675,13 +740,15 @@ Control connection failed and is unlikely to recover.
|
|
675
740
|
|
676
741
|
refresh_functions.each do |(keyspace, function_changes)|
|
677
742
|
function_changes.each do |change|
|
678
|
-
futures <<
|
743
|
+
futures <<
|
744
|
+
refresh_maybe_retry(:function, keyspace, change.name, change.arguments)
|
679
745
|
end
|
680
746
|
end
|
681
747
|
|
682
748
|
refresh_aggregates.each do |(keyspace, aggregate_changes)|
|
683
749
|
aggregate_changes.each do |change|
|
684
|
-
futures <<
|
750
|
+
futures <<
|
751
|
+
refresh_maybe_retry(:aggregate, keyspace, change.name, change.arguments)
|
685
752
|
end
|
686
753
|
end
|
687
754
|
|
@@ -704,7 +771,8 @@ Control connection failed and is unlikely to recover.
|
|
704
771
|
|
705
772
|
def refresh_retry(what, error, schedule, *args)
|
706
773
|
timeout = schedule.next
|
707
|
-
@logger.info("Failed to refresh #{what} #{args.inspect}
|
774
|
+
@logger.info("Failed to refresh #{what} #{args.inspect} " \
|
775
|
+
"(#{error.class.name}: #{error.message}), retrying in #{timeout}")
|
708
776
|
|
709
777
|
timer = @io_reactor.schedule_timer(timeout)
|
710
778
|
timer.flat_map do
|
@@ -722,38 +790,105 @@ Control connection failed and is unlikely to recover.
|
|
722
790
|
end
|
723
791
|
end
|
724
792
|
|
725
|
-
def
|
726
|
-
|
727
|
-
|
728
|
-
|
793
|
+
def refresh_schema_async_wrapper
|
794
|
+
# This is kinda tricky. We want to start refreshing the schema asynchronously.
|
795
|
+
# However, if we're already in the process of doing so, return the future
|
796
|
+
# representing that result rather than starting another schema refresh.
|
797
|
+
#
|
798
|
+
# A few other nuances while a refresh is in progress:
|
799
|
+
# * if a new attempt is made to refresh, keep track of that and schedule another
|
800
|
+
# refresh after the current one completes.
|
801
|
+
# * we don't want schema_change events to be processed since the full refresh
|
802
|
+
# may overwrite the results of handling the schema_change events with older
|
803
|
+
# data. That said, we don't want to lose track of schema_change events; just
|
804
|
+
# delay processing them until after the full refresh is done.
|
805
|
+
#
|
806
|
+
# Finally, when a full refresh begins, clear out any pending changes in
|
807
|
+
# @schema_changes because the full refresh subsumes them. This has two benefits:
|
808
|
+
# 1. avoid round trips to Cassandra to get details related to those schema
|
809
|
+
# changes.
|
810
|
+
# 2. avoid race conditions where those updates may return older data than our
|
811
|
+
# full refresh and might win as last writer with that potentially older data.
|
729
812
|
synchronize do
|
730
|
-
@
|
813
|
+
if @refresh_schema_future
|
814
|
+
@pending_schema_refresh = true
|
815
|
+
return @refresh_schema_future
|
816
|
+
end
|
817
|
+
|
818
|
+
# Fresh refresh; prep this connection!
|
731
819
|
|
820
|
+
# Since we're starting a new refresh, there can be no pending refresh request.
|
821
|
+
@pending_schema_refresh = false
|
822
|
+
|
823
|
+
# Clear outstanding schema changes and timers.
|
824
|
+
@schema_changes = []
|
732
825
|
@io_reactor.cancel_timer(@schema_refresh_timer) if @schema_refresh_timer
|
733
|
-
|
826
|
+
@schema_refresh_timer = nil
|
827
|
+
@io_reactor.cancel_timer(@schema_refresh_window) if @schema_refresh_window
|
828
|
+
@schema_refresh_window = nil
|
829
|
+
|
830
|
+
# Start refreshing..
|
831
|
+
@refresh_schema_future = refresh_maybe_retry(:schema)
|
832
|
+
@refresh_schema_future.on_complete do
|
833
|
+
pending = false
|
834
|
+
synchronize do
|
835
|
+
# We're done refreshing. If we have a pending refresh, launch it now.
|
836
|
+
@refresh_schema_future = nil
|
837
|
+
pending = @pending_schema_refresh
|
838
|
+
@pending_schema_refresh = false
|
839
|
+
unless pending
|
840
|
+
# Restore timers if there are pending schema changes.
|
841
|
+
handle_schema_change(nil)
|
842
|
+
end
|
843
|
+
end
|
734
844
|
|
735
|
-
|
736
|
-
expiration_timer = @schema_refresh_window = @io_reactor.schedule_timer(@connection_options.schema_refresh_timeout)
|
845
|
+
refresh_schema_async_wrapper if pending
|
737
846
|
end
|
847
|
+
|
848
|
+
# Return the (now cached) future
|
849
|
+
@refresh_schema_future
|
738
850
|
end
|
851
|
+
end
|
739
852
|
|
740
|
-
|
741
|
-
|
742
|
-
|
853
|
+
def handle_schema_change(change)
|
854
|
+
timer = nil
|
855
|
+
expiration_timer = nil
|
743
856
|
|
744
|
-
|
745
|
-
|
857
|
+
synchronize do
|
858
|
+
# If change is nil, it means we want to set up timers (if there are pending
|
859
|
+
# changes). Otherwise, we definitely have a change and want to set up timers.
|
860
|
+
# Also, we only want to set up timers if we're not in the middle of a full
|
861
|
+
# refresh.
|
862
|
+
@schema_changes << change if change
|
863
|
+
|
864
|
+
unless @schema_changes.empty? || @refresh_schema_future
|
865
|
+
@io_reactor.cancel_timer(@schema_refresh_timer) if @schema_refresh_timer
|
866
|
+
timer = @schema_refresh_timer =
|
867
|
+
@io_reactor.schedule_timer(@connection_options.schema_refresh_delay)
|
868
|
+
|
869
|
+
unless @schema_refresh_window
|
870
|
+
@schema_refresh_window =
|
871
|
+
@io_reactor.schedule_timer(@connection_options.schema_refresh_timeout)
|
872
|
+
expiration_timer = @schema_refresh_window
|
873
|
+
end
|
874
|
+
end
|
875
|
+
end
|
746
876
|
|
747
|
-
|
748
|
-
|
877
|
+
expiration_timer.on_value do
|
878
|
+
schema_changes = nil
|
749
879
|
|
750
|
-
|
751
|
-
|
752
|
-
end
|
880
|
+
synchronize do
|
881
|
+
@io_reactor.cancel_timer(@schema_refresh_timer)
|
753
882
|
|
754
|
-
|
883
|
+
@schema_refresh_window = nil
|
884
|
+
@schema_refresh_timer = nil
|
885
|
+
|
886
|
+
schema_changes = @schema_changes
|
887
|
+
@schema_changes = ::Array.new
|
755
888
|
end
|
756
|
-
|
889
|
+
|
890
|
+
process_schema_changes(schema_changes)
|
891
|
+
end if expiration_timer
|
757
892
|
|
758
893
|
timer.on_value do
|
759
894
|
schema_changes = nil
|
@@ -762,14 +897,14 @@ Control connection failed and is unlikely to recover.
|
|
762
897
|
@io_reactor.cancel_timer(@schema_refresh_window)
|
763
898
|
|
764
899
|
@schema_refresh_window = nil
|
765
|
-
@schema_refresh_timer
|
900
|
+
@schema_refresh_timer = nil
|
766
901
|
|
767
|
-
schema_changes
|
902
|
+
schema_changes = @schema_changes
|
768
903
|
@schema_changes = ::Array.new
|
769
904
|
end
|
770
905
|
|
771
906
|
process_schema_changes(schema_changes)
|
772
|
-
end
|
907
|
+
end if timer
|
773
908
|
|
774
909
|
nil
|
775
910
|
end
|