cassandra-driver 1.0.0.beta.3 → 1.0.0.rc.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
@@ -43,25 +43,17 @@ module Cassandra
|
|
43
43
|
end
|
44
44
|
end
|
45
45
|
|
46
|
-
@logger.info("Connecting ip=#{host.ip}")
|
47
|
-
|
48
46
|
f = do_connect(host)
|
49
47
|
|
50
48
|
f.on_failure do |error|
|
51
|
-
@logger.warn("Connection failed ip=#{host.ip} error=\"#{error.class.name}: #{error.message}\"")
|
52
49
|
connection_error(host, error)
|
53
50
|
end
|
54
51
|
|
55
52
|
f.on_value do |connection|
|
56
53
|
connection.on_closed do |cause|
|
57
|
-
message = "Disconnected ip=#{host.ip}"
|
58
|
-
message << " error=#{cause.message}" if cause
|
59
|
-
|
60
|
-
@logger.info(message)
|
61
54
|
disconnected(host, cause)
|
62
55
|
end
|
63
56
|
|
64
|
-
@logger.info("Connected ip=#{host.ip}")
|
65
57
|
connected(host)
|
66
58
|
end
|
67
59
|
|
@@ -75,22 +67,15 @@ module Cassandra
|
|
75
67
|
return Future.resolved
|
76
68
|
end
|
77
69
|
|
78
|
-
@logger.
|
70
|
+
@logger.debug("Checking if host #{host.ip} is up")
|
79
71
|
f = do_connect(host)
|
80
72
|
|
81
73
|
f.on_failure do |error|
|
82
|
-
@logger.info("Refreshed host status ip=#{host.ip}")
|
83
|
-
@logger.warn("Connection failed ip=#{host.ip} error=\"#{error.class.name}: #{error.message}\"")
|
84
74
|
connection_error(host, error)
|
85
75
|
end
|
86
76
|
|
87
77
|
f.on_value do |connection|
|
88
|
-
@logger.info("Refreshed host status ip=#{host.ip}")
|
89
78
|
connection.on_closed do |cause|
|
90
|
-
message = "Disconnected ip=#{host.ip}"
|
91
|
-
message << " error=#{cause.message}" if cause
|
92
|
-
|
93
|
-
@logger.info(message)
|
94
79
|
disconnected(host, cause)
|
95
80
|
end
|
96
81
|
|
@@ -99,7 +84,21 @@ module Cassandra
|
|
99
84
|
@open_connections[host] << connection
|
100
85
|
end
|
101
86
|
|
102
|
-
@
|
87
|
+
timer = @reactor.schedule_timer(UNCLAIMED_TIMEOUT)
|
88
|
+
timer.on_value do
|
89
|
+
close = false
|
90
|
+
|
91
|
+
synchronize do
|
92
|
+
open_connections = @open_connections[host]
|
93
|
+
if open_connections
|
94
|
+
close = !open_connections.delete(connection).nil?
|
95
|
+
@open_connections.delete(host) if open_connections.empty?
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
connection.close if close
|
100
|
+
end
|
101
|
+
|
103
102
|
connected(host)
|
104
103
|
end
|
105
104
|
|
@@ -112,22 +111,28 @@ module Cassandra
|
|
112
111
|
|
113
112
|
private
|
114
113
|
|
115
|
-
NO_CONNECTIONS
|
114
|
+
NO_CONNECTIONS = Ione::Future.resolved([])
|
115
|
+
UNCLAIMED_TIMEOUT = 5 # close unclaimed connections in five seconds
|
116
116
|
|
117
117
|
def do_connect(host)
|
118
118
|
create_connector.connect(host.ip.to_s).fallback do |error|
|
119
|
-
|
119
|
+
case error
|
120
|
+
when Errors::ProtocolError
|
120
121
|
synchronize do
|
121
122
|
if @options.protocol_version > 1
|
122
|
-
@logger.
|
123
|
+
@logger.info("Host #{host.ip} doesn't support protocol version #{@options.protocol_version}, downgrading")
|
123
124
|
@options.protocol_version -= 1
|
124
125
|
do_connect(host)
|
125
126
|
else
|
126
127
|
Ione::Future.failed(error)
|
127
128
|
end
|
128
129
|
end
|
129
|
-
|
130
|
+
when Error
|
130
131
|
Ione::Future.failed(error)
|
132
|
+
else
|
133
|
+
e = Errors::IOError.new(error.message)
|
134
|
+
e.set_backtrace(error.backtrace)
|
135
|
+
Ione::Future.failed(e)
|
131
136
|
end
|
132
137
|
end
|
133
138
|
end
|
@@ -135,8 +140,8 @@ module Cassandra
|
|
135
140
|
def create_connector
|
136
141
|
authentication_step = @options.protocol_version == 1 ? Cassandra::Client::CredentialsAuthenticationStep.new(@options.credentials) : Cassandra::Client::SaslAuthenticationStep.new(@options.auth_provider)
|
137
142
|
protocol_handler_factory = lambda do |connection|
|
138
|
-
raise
|
139
|
-
Protocol::CqlProtocolHandler.new(connection, @reactor, @options.protocol_version, @options.compressor)
|
143
|
+
raise Errors::ClientError, 'Not connected, reactor stopped' unless connection
|
144
|
+
Protocol::CqlProtocolHandler.new(connection, @reactor, @options.protocol_version, @options.compressor, @options.heartbeat_interval, @options.idle_timeout)
|
140
145
|
end
|
141
146
|
|
142
147
|
Cassandra::Client::Connector.new([
|
@@ -216,14 +221,19 @@ module Cassandra
|
|
216
221
|
connections -= 1
|
217
222
|
|
218
223
|
if connections == 0
|
219
|
-
notify = !error.nil?
|
224
|
+
notify = !error.nil?
|
220
225
|
@connections.delete(host)
|
221
226
|
else
|
222
227
|
@connections[host] = connections
|
223
228
|
end
|
224
229
|
end
|
225
230
|
|
226
|
-
@
|
231
|
+
@logger.debug("Host #{host.ip} closed connection (#{error.class.name}: #{error.message})") if error
|
232
|
+
|
233
|
+
if notify
|
234
|
+
@logger.warn("Host #{host.ip} closed all connections")
|
235
|
+
@registry.host_down(host.ip)
|
236
|
+
end
|
227
237
|
|
228
238
|
self
|
229
239
|
end
|
@@ -232,10 +242,15 @@ module Cassandra
|
|
232
242
|
notify = false
|
233
243
|
|
234
244
|
synchronize do
|
235
|
-
notify = !error.
|
245
|
+
notify = !error.nil? && !@connections.has_key?(host)
|
236
246
|
end
|
237
247
|
|
238
|
-
@
|
248
|
+
@logger.debug("Host #{host.ip} refused connection (#{error.class.name}: #{error.message})")
|
249
|
+
|
250
|
+
if notify
|
251
|
+
@logger.warn("Host #{host.ip} refused all connections")
|
252
|
+
@registry.host_down(host.ip)
|
253
|
+
end
|
239
254
|
|
240
255
|
self
|
241
256
|
end
|
@@ -22,7 +22,7 @@ module Cassandra
|
|
22
22
|
class ControlConnection
|
23
23
|
include MonitorMixin
|
24
24
|
|
25
|
-
def initialize(logger, io_reactor, cluster_registry, cluster_schema, cluster_metadata, load_balancing_policy, reconnection_policy, connector)
|
25
|
+
def initialize(logger, io_reactor, cluster_registry, cluster_schema, cluster_metadata, load_balancing_policy, reconnection_policy, address_resolution_policy, connector)
|
26
26
|
@logger = logger
|
27
27
|
@io_reactor = io_reactor
|
28
28
|
@registry = cluster_registry
|
@@ -30,9 +30,15 @@ module Cassandra
|
|
30
30
|
@metadata = cluster_metadata
|
31
31
|
@load_balancing_policy = load_balancing_policy
|
32
32
|
@reconnection_policy = reconnection_policy
|
33
|
+
@address_resolver = address_resolution_policy
|
33
34
|
@connector = connector
|
34
35
|
@refreshing_statuses = Hash.new(false)
|
35
36
|
@status = :closed
|
37
|
+
@refreshing_schema = false
|
38
|
+
@refreshing_keyspaces = Hash.new(false)
|
39
|
+
@refreshing_tables = Hash.new
|
40
|
+
@refreshing_hosts = false
|
41
|
+
@refreshing_host = Hash.new(false)
|
36
42
|
|
37
43
|
mon_initialize
|
38
44
|
end
|
@@ -43,12 +49,14 @@ module Cassandra
|
|
43
49
|
@status = :connecting
|
44
50
|
end
|
45
51
|
|
46
|
-
@
|
47
|
-
|
48
|
-
@io_reactor.start.flat_map do
|
52
|
+
f = @io_reactor.start.flat_map do
|
49
53
|
plan = @load_balancing_policy.plan(nil, VOID_STATEMENT, VOID_OPTIONS)
|
50
54
|
connect_to_first_available(plan)
|
51
55
|
end
|
56
|
+
f.on_failure do |e|
|
57
|
+
close_async
|
58
|
+
end
|
59
|
+
f
|
52
60
|
end
|
53
61
|
|
54
62
|
def host_found(host)
|
@@ -56,31 +64,48 @@ module Cassandra
|
|
56
64
|
|
57
65
|
def host_lost(host)
|
58
66
|
synchronize do
|
59
|
-
@refreshing_statuses.delete(host)
|
67
|
+
timer = @refreshing_statuses.delete(host)
|
68
|
+
@io_reactor.cancel_timer(timer) if timer
|
60
69
|
end
|
70
|
+
|
71
|
+
nil
|
61
72
|
end
|
62
73
|
|
63
74
|
def host_up(host)
|
64
75
|
synchronize do
|
65
|
-
@refreshing_statuses.delete(host)
|
76
|
+
timer = @refreshing_statuses.delete(host)
|
77
|
+
@io_reactor.cancel_timer(timer) if timer
|
66
78
|
|
67
|
-
|
79
|
+
unless @connection || (@status == :closing || @status == :closed) || @load_balancing_policy.distance(host) == :ignore
|
80
|
+
return connect_to_first_available(@load_balancing_policy.plan(nil, VOID_STATEMENT, VOID_OPTIONS))
|
81
|
+
end
|
68
82
|
end
|
69
83
|
|
70
84
|
Ione::Future.resolved
|
71
85
|
end
|
72
86
|
|
73
87
|
def host_down(host)
|
88
|
+
schedule = nil
|
89
|
+
timer = nil
|
90
|
+
|
74
91
|
synchronize do
|
75
|
-
return Ione::Future.resolved if @refreshing_statuses[host]
|
92
|
+
return Ione::Future.resolved if @refreshing_statuses[host] || @load_balancing_policy.distance(host) == :ignore
|
93
|
+
|
94
|
+
schedule = @reconnection_policy.schedule
|
95
|
+
timeout = schedule.next
|
76
96
|
|
77
|
-
@logger.debug("Starting to continuously refresh status
|
78
|
-
|
97
|
+
@logger.debug("Starting to continuously refresh status of #{host.ip} in #{timeout} seconds")
|
98
|
+
|
99
|
+
@refreshing_statuses[host] = timer = @io_reactor.schedule_timer(timeout)
|
79
100
|
end
|
80
101
|
|
81
|
-
|
82
|
-
|
102
|
+
timer.on_value do
|
103
|
+
refresh_host_status(host).fallback do |e|
|
104
|
+
refresh_host_status_with_retry(timer, host, schedule)
|
105
|
+
end
|
83
106
|
end
|
107
|
+
|
108
|
+
nil
|
84
109
|
end
|
85
110
|
|
86
111
|
def close_async
|
@@ -116,19 +141,18 @@ module Cassandra
|
|
116
141
|
f = @io_reactor.schedule_timer(timeout)
|
117
142
|
f = f.flat_map do
|
118
143
|
if synchronize { @status == :reconnecting }
|
119
|
-
@logger.debug('Reestablishing control connection')
|
120
144
|
plan = @load_balancing_policy.plan(nil, VOID_STATEMENT, VOID_OPTIONS)
|
121
145
|
connect_to_first_available(plan)
|
122
146
|
else
|
123
|
-
@logger.debug('Stopping reconnection')
|
124
147
|
Ione::Future.resolved
|
125
148
|
end
|
126
149
|
end
|
127
|
-
f.fallback do
|
150
|
+
f.fallback do |e|
|
151
|
+
@logger.error("Control connection failed (#{e.class.name}: #{e.message})")
|
152
|
+
|
128
153
|
if synchronize { @status == :reconnecting }
|
129
154
|
reconnect_async(schedule)
|
130
155
|
else
|
131
|
-
@logger.debug('Stopping reconnection')
|
132
156
|
return Ione::Future.resolved
|
133
157
|
end
|
134
158
|
end
|
@@ -137,13 +161,20 @@ module Cassandra
|
|
137
161
|
def register_async
|
138
162
|
connection = @connection
|
139
163
|
|
140
|
-
return Ione::Future.failed(
|
141
|
-
|
142
|
-
@logger.debug('Registering for events')
|
143
|
-
|
144
|
-
connection.send_request(REGISTER).map do
|
145
|
-
@logger.debug('Registered for events')
|
164
|
+
return Ione::Future.failed(Errors::ClientError.new('Not connected')) if connection.nil?
|
146
165
|
|
166
|
+
f = connection.send_request(REGISTER)
|
167
|
+
f = f.map do |r|
|
168
|
+
case r
|
169
|
+
when Protocol::ReadyResponse
|
170
|
+
nil
|
171
|
+
when Protocol::ErrorResponse
|
172
|
+
raise r.to_error(VOID_STATEMENT)
|
173
|
+
else
|
174
|
+
raise Errors::InternalError, "Unexpected response #{r.inspect}"
|
175
|
+
end
|
176
|
+
end
|
177
|
+
f = f.map do
|
147
178
|
connection.on_event do |event|
|
148
179
|
@logger.debug("Event received #{event}")
|
149
180
|
|
@@ -151,21 +182,21 @@ module Cassandra
|
|
151
182
|
case event.change
|
152
183
|
when 'CREATED'
|
153
184
|
if event.table.empty?
|
154
|
-
|
185
|
+
refresh_schema_async_maybe_retry
|
155
186
|
else
|
156
|
-
|
187
|
+
refresh_keyspace_async_maybe_retry(event.keyspace)
|
157
188
|
end
|
158
189
|
when 'DROPPED'
|
159
190
|
if event.table.empty?
|
160
|
-
|
191
|
+
refresh_schema_async_maybe_retry
|
161
192
|
else
|
162
|
-
|
193
|
+
refresh_keyspace_async_maybe_retry(event.keyspace)
|
163
194
|
end
|
164
195
|
when 'UPDATED'
|
165
196
|
if event.table.empty?
|
166
|
-
|
197
|
+
refresh_keyspace_async_maybe_retry(event.keyspace)
|
167
198
|
else
|
168
|
-
|
199
|
+
refresh_table_async_maybe_retry(event.keyspace, event.table)
|
169
200
|
end
|
170
201
|
end
|
171
202
|
else
|
@@ -173,19 +204,19 @@ module Cassandra
|
|
173
204
|
when 'UP'
|
174
205
|
address = event.address
|
175
206
|
|
176
|
-
|
207
|
+
refresh_host_async_maybe_retry(address) if @registry.has_host?(address)
|
177
208
|
when 'DOWN'
|
178
209
|
@registry.host_down(event.address)
|
179
210
|
when 'NEW_NODE'
|
180
211
|
address = event.address
|
181
212
|
|
182
213
|
unless @registry.has_host?(address)
|
183
|
-
|
184
|
-
|
214
|
+
refresh_host_async_maybe_retry(address)
|
215
|
+
refresh_schema_async_maybe_retry
|
185
216
|
end
|
186
217
|
when 'REMOVED_NODE'
|
187
218
|
@registry.host_lost(event.address)
|
188
|
-
|
219
|
+
refresh_schema_async_maybe_retry
|
189
220
|
end
|
190
221
|
end
|
191
222
|
end
|
@@ -197,85 +228,247 @@ module Cassandra
|
|
197
228
|
def refresh_schema_async
|
198
229
|
connection = @connection
|
199
230
|
|
200
|
-
return Ione::Future.failed(
|
201
|
-
|
202
|
-
@logger.debug('Fetching schema metadata')
|
231
|
+
return Ione::Future.failed(Errors::ClientError.new('Not connected')) if connection.nil?
|
203
232
|
|
204
|
-
keyspaces = connection
|
205
|
-
tables = connection
|
206
|
-
columns = connection
|
233
|
+
keyspaces = send_select_request(connection, SELECT_KEYSPACES)
|
234
|
+
tables = send_select_request(connection, SELECT_TABLES)
|
235
|
+
columns = send_select_request(connection, SELECT_COLUMNS)
|
207
236
|
|
208
237
|
Ione::Future.all(keyspaces, tables, columns).map do |(keyspaces, tables, columns)|
|
209
|
-
@logger.debug('Fetched schema metadata')
|
210
|
-
|
211
238
|
host = @registry.host(connection.host)
|
212
239
|
|
213
|
-
@schema.update_keyspaces(host, keyspaces
|
240
|
+
@schema.update_keyspaces(host, keyspaces, tables, columns)
|
214
241
|
@metadata.rebuild_token_map
|
215
242
|
end
|
216
243
|
end
|
217
244
|
|
245
|
+
def refresh_schema_async_maybe_retry
|
246
|
+
synchronize do
|
247
|
+
return Ione::Future.resolved if @refreshing_schema
|
248
|
+
@refreshing_schema = true
|
249
|
+
end
|
250
|
+
|
251
|
+
refresh_schema_async.fallback do |e|
|
252
|
+
case e
|
253
|
+
when Errors::HostError
|
254
|
+
refresh_schema_async_retry(e, @reconnection_policy.schedule)
|
255
|
+
else
|
256
|
+
connection = @connection
|
257
|
+
connection && connection.close(e)
|
258
|
+
|
259
|
+
Ione::Future.resolved
|
260
|
+
end
|
261
|
+
end.map do
|
262
|
+
synchronize do
|
263
|
+
@refreshing_schema = false
|
264
|
+
end
|
265
|
+
end
|
266
|
+
end
|
267
|
+
|
268
|
+
def refresh_schema_async_retry(error, schedule)
|
269
|
+
timeout = schedule.next
|
270
|
+
@logger.info("Failed to refresh schema (#{error.class.name}: #{error.message}), retrying in #{timeout}")
|
271
|
+
|
272
|
+
timer = @io_reactor.schedule_timer(timeout)
|
273
|
+
timer.flat_map do
|
274
|
+
refresh_schema_async.fallback do |e|
|
275
|
+
case e
|
276
|
+
when Errors::HostError
|
277
|
+
refresh_schema_async_retry(e, schedule)
|
278
|
+
else
|
279
|
+
connection = @connection
|
280
|
+
connection && connection.close(e)
|
281
|
+
|
282
|
+
Ione::Future.resolved
|
283
|
+
end
|
284
|
+
end
|
285
|
+
end
|
286
|
+
end
|
287
|
+
|
288
|
+
def refresh_keyspace_async_maybe_retry(keyspace)
|
289
|
+
synchronize do
|
290
|
+
return Ione::Future.resolved if @refreshing_schema || @refreshing_keyspaces[keyspace]
|
291
|
+
@refreshing_keyspaces[keyspace] = true
|
292
|
+
end
|
293
|
+
|
294
|
+
refresh_keyspace_async(keyspace).fallback do |e|
|
295
|
+
case e
|
296
|
+
when Errors::HostError
|
297
|
+
refresh_keyspace_async_retry(keyspace, e, @reconnection_policy.schedule)
|
298
|
+
else
|
299
|
+
connection = @connection
|
300
|
+
connection && connection.close(e)
|
301
|
+
|
302
|
+
Ione::Future.resolved
|
303
|
+
end
|
304
|
+
end.map do
|
305
|
+
synchronize do
|
306
|
+
@refreshing_keyspaces.delete(host)
|
307
|
+
end
|
308
|
+
end
|
309
|
+
end
|
310
|
+
|
311
|
+
def refresh_keyspace_async_retry(keyspace, error, schedule)
|
312
|
+
timeout = schedule.next
|
313
|
+
@logger.info("Failed to refresh keyspace #{keyspace} (#{error.class.name}: #{error.message}), retrying in #{timeout}")
|
314
|
+
|
315
|
+
timer = @io_reactor.schedule_timer(timeout)
|
316
|
+
timer.flat_map do
|
317
|
+
refresh_keyspace_async(keyspace).fallback do |e|
|
318
|
+
case e
|
319
|
+
when Errors::HostError
|
320
|
+
refresh_keyspace_async_retry(keyspace, e, schedule)
|
321
|
+
else
|
322
|
+
connection = @connection
|
323
|
+
connection && connection.close(e)
|
324
|
+
|
325
|
+
Ione::Future.resolved
|
326
|
+
end
|
327
|
+
end
|
328
|
+
end
|
329
|
+
end
|
330
|
+
|
218
331
|
def refresh_keyspace_async(keyspace)
|
219
332
|
connection = @connection
|
220
333
|
|
221
|
-
return Ione::Future.failed(
|
222
|
-
|
223
|
-
@logger.debug("Fetching keyspace #{keyspace.inspect} metadata")
|
334
|
+
return Ione::Future.failed(Errors::ClientError.new('Not connected')) if connection.nil?
|
224
335
|
|
225
336
|
params = [keyspace]
|
226
|
-
keyspaces = connection
|
227
|
-
tables = connection
|
228
|
-
columns = connection
|
337
|
+
keyspaces = send_select_request(connection, Protocol::QueryRequest.new("SELECT * FROM system.schema_keyspaces WHERE keyspace_name = ?", params, nil, :one))
|
338
|
+
tables = send_select_request(connection, Protocol::QueryRequest.new("SELECT * FROM system.schema_columnfamilies WHERE keyspace_name = ?", params, nil, :one))
|
339
|
+
columns = send_select_request(connection, Protocol::QueryRequest.new("SELECT * FROM system.schema_columns WHERE keyspace_name = ?", params, nil, :one))
|
229
340
|
|
230
341
|
Ione::Future.all(keyspaces, tables, columns).map do |(keyspaces, tables, columns)|
|
231
|
-
@logger.debug("Fetched keyspace #{keyspace.inspect} metadata")
|
232
|
-
|
233
342
|
host = @registry.host(connection.host)
|
234
343
|
|
235
|
-
@schema.update_keyspace(host, keyspaces.
|
344
|
+
@schema.update_keyspace(host, keyspaces.first, tables, columns) unless keyspaces.empty?
|
345
|
+
end
|
346
|
+
end
|
347
|
+
|
348
|
+
def refresh_table_async_maybe_retry(keyspace, table)
|
349
|
+
synchronize do
|
350
|
+
return Ione::Future.resolved if @refreshing_schema || @refreshing_keyspaces[keyspace] || @refreshing_tables[keyspace][table]
|
351
|
+
@refreshing_tables[keyspace] ||= ::Hash.new(false)
|
352
|
+
@refreshing_tables[keyspace][table] = true
|
353
|
+
end
|
354
|
+
|
355
|
+
refresh_table_async(keyspace, table).fallback do |e|
|
356
|
+
case e
|
357
|
+
when Errors::HostError
|
358
|
+
refresh_keyspace_async_retry(keyspace, e, @reconnection_policy.schedule)
|
359
|
+
else
|
360
|
+
connection = @connection
|
361
|
+
connection && connection.close(e)
|
362
|
+
|
363
|
+
Ione::Future.resolved
|
364
|
+
end
|
365
|
+
end.map do
|
366
|
+
synchronize do
|
367
|
+
@refreshing_tables[keyspace].delete(table)
|
368
|
+
@refreshing_tables.delete(keyspace) if @refreshing_tables[keyspace].empty?
|
369
|
+
end
|
370
|
+
end
|
371
|
+
end
|
372
|
+
|
373
|
+
def refresh_table_async_retry(keyspace, table, error, schedule)
|
374
|
+
timeout = schedule.next
|
375
|
+
@logger.info("Failed to refresh keyspace #{keyspace} (#{error.class.name}: #{error.message}), retrying in #{timeout}")
|
376
|
+
|
377
|
+
timer = @io_reactor.schedule_timer(timeout)
|
378
|
+
timer.flat_map do
|
379
|
+
refresh_keyspace_async(keyspace).fallback do |e|
|
380
|
+
case e
|
381
|
+
when Errors::HostError
|
382
|
+
refresh_keyspace_async_retry(keyspace, e, schedule)
|
383
|
+
else
|
384
|
+
connection = @connection
|
385
|
+
connection && connection.close(e)
|
386
|
+
|
387
|
+
Ione::Future.resolved
|
388
|
+
end
|
389
|
+
end
|
236
390
|
end
|
237
391
|
end
|
238
392
|
|
239
393
|
def refresh_table_async(keyspace, table)
|
240
394
|
connection = @connection
|
241
395
|
|
242
|
-
return Ione::Future.failed(
|
243
|
-
|
244
|
-
@logger.debug("Fetching table \"#{keyspace}.#{table}\" metadata")
|
396
|
+
return Ione::Future.failed(Errors::ClientError.new('Not connected')) if connection.nil?
|
245
397
|
|
246
398
|
params = [keyspace, table]
|
247
|
-
table = connection
|
248
|
-
columns = connection
|
399
|
+
table = send_select_request(connection, Protocol::QueryRequest.new("SELECT * FROM system.schema_columnfamilies WHERE keyspace_name = ? AND columnfamily_name = ?", params, nil, :one))
|
400
|
+
columns = send_select_request(connection, Protocol::QueryRequest.new("SELECT * FROM system.schema_columns WHERE keyspace_name = ? AND columnfamily_name = ?", params, nil, :one))
|
249
401
|
|
250
402
|
Ione::Future.all(table, columns).map do |(table, columns)|
|
251
|
-
@logger.debug("Fetched table \"#{keyspace}.#{table}\" metadata")
|
252
|
-
|
253
403
|
host = @registry.host(connection.host)
|
254
404
|
|
255
|
-
@schema.udpate_table(host, keyspace, table
|
405
|
+
@schema.udpate_table(host, keyspace, table, columns)
|
256
406
|
end
|
257
407
|
end
|
258
408
|
|
259
|
-
def
|
260
|
-
|
409
|
+
def refresh_hosts_async_maybe_retry
|
410
|
+
synchronize do
|
411
|
+
return Ione::Future.resolved if @refreshing_hosts
|
412
|
+
@refreshing_hosts = true
|
413
|
+
end
|
261
414
|
|
262
|
-
|
415
|
+
refresh_hosts_async.fallback do |e|
|
416
|
+
case e
|
417
|
+
when Errors::HostError
|
418
|
+
refresh_hosts_async_retry(e, @reconnection_policy.schedule)
|
419
|
+
else
|
420
|
+
connection = @connection
|
421
|
+
connection && connection.close(e)
|
263
422
|
|
264
|
-
|
423
|
+
Ione::Future.resolved
|
424
|
+
end
|
425
|
+
end.map do
|
426
|
+
synchronize do
|
427
|
+
@refreshing_hosts = false
|
428
|
+
end
|
429
|
+
end
|
430
|
+
end
|
265
431
|
|
266
|
-
|
267
|
-
|
432
|
+
def refresh_hosts_async_retry(error, schedule)
|
433
|
+
timeout = schedule.next
|
434
|
+
@logger.info("Failed to refresh hosts (#{error.class.name}: #{error.message}), retrying in #{timeout}")
|
435
|
+
|
436
|
+
timer = @io_reactor.schedule_timer(timeout)
|
437
|
+
timer.flat_map do
|
438
|
+
refresh_hosts_async.fallback do |e|
|
439
|
+
case e
|
440
|
+
when Errors::HostError
|
441
|
+
refresh_hosts_async_retry(e, schedule)
|
442
|
+
else
|
443
|
+
connection = @connection
|
444
|
+
connection && connection.close(e)
|
445
|
+
|
446
|
+
Ione::Future.resolved
|
447
|
+
end
|
448
|
+
end
|
449
|
+
end
|
450
|
+
end
|
268
451
|
|
269
|
-
|
270
|
-
|
271
|
-
peers = peers.rows
|
452
|
+
def refresh_hosts_async
|
453
|
+
connection = @connection
|
272
454
|
|
273
|
-
|
455
|
+
return Ione::Future.failed(Errors::ClientError.new('Not connected')) if connection.nil?
|
274
456
|
|
275
|
-
|
457
|
+
local = send_select_request(connection, SELECT_LOCAL)
|
458
|
+
peers = send_select_request(connection, SELECT_PEERS)
|
459
|
+
|
460
|
+
Ione::Future.all(local, peers).map do |(local, peers)|
|
461
|
+
@logger.debug("#{peers.size} peer(s) found")
|
276
462
|
|
277
463
|
ips = ::Set.new
|
278
464
|
|
465
|
+
unless local.empty?
|
466
|
+
ips << ip = IPAddr.new(connection.host)
|
467
|
+
data = local.first
|
468
|
+
@registry.host_found(ip, data)
|
469
|
+
@metadata.update(data)
|
470
|
+
end
|
471
|
+
|
279
472
|
peers.each do |data|
|
280
473
|
ip = peer_ip(data)
|
281
474
|
next unless ip
|
@@ -283,27 +476,11 @@ module Cassandra
|
|
283
476
|
@registry.host_found(ip, data)
|
284
477
|
end
|
285
478
|
|
286
|
-
ips << ip = IPAddr.new(connection.host)
|
287
|
-
data = local.first
|
288
|
-
@registry.host_found(ip, data)
|
289
|
-
|
290
|
-
futures = []
|
291
|
-
|
292
479
|
@registry.each_host do |host|
|
293
|
-
|
294
|
-
futures << refresh_host_status(host) if host.down?
|
295
|
-
else
|
296
|
-
@registry.host_lost(host.ip)
|
297
|
-
end
|
480
|
+
@registry.host_lost(host.ip) unless ips.include?(host.ip)
|
298
481
|
end
|
299
482
|
|
300
|
-
|
301
|
-
|
302
|
-
if futures.empty?
|
303
|
-
Ione::Future.resolved
|
304
|
-
else
|
305
|
-
Ione::Future.all(*futures)
|
306
|
-
end
|
483
|
+
nil
|
307
484
|
end
|
308
485
|
end
|
309
486
|
|
@@ -311,46 +488,87 @@ module Cassandra
|
|
311
488
|
@connector.refresh_status(host)
|
312
489
|
end
|
313
490
|
|
314
|
-
def refresh_host_status_with_retry(host, schedule)
|
315
|
-
|
491
|
+
def refresh_host_status_with_retry(original_timer, host, schedule)
|
492
|
+
timer = nil
|
316
493
|
|
317
|
-
|
494
|
+
synchronize do
|
495
|
+
timer = @refreshing_statuses[host]
|
318
496
|
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
497
|
+
# host must have been lost/up or timer was rescheduled
|
498
|
+
return Ione::Future.resolved if timer.nil? || timer != original_timer
|
499
|
+
|
500
|
+
timeout = schedule.next
|
501
|
+
|
502
|
+
@logger.debug("Checking host #{host.ip} in #{timeout} seconds")
|
503
|
+
|
504
|
+
@refreshing_statuses[host] = timer = @io_reactor.schedule_timer(timeout)
|
505
|
+
end
|
506
|
+
|
507
|
+
timer.on_value do
|
508
|
+
refresh_host_status(host).fallback do |e|
|
509
|
+
refresh_host_status_with_retry(timer, host, schedule)
|
510
|
+
end
|
511
|
+
end
|
512
|
+
end
|
513
|
+
|
514
|
+
def refresh_host_async_maybe_retry(host)
|
515
|
+
synchronize do
|
516
|
+
return Ione::Future.resolved if @refreshing_hosts || @refreshing_host[host]
|
517
|
+
@refreshing_host[host] = true
|
518
|
+
end
|
519
|
+
|
520
|
+
refresh_host_async(host).fallback do |e|
|
521
|
+
case e
|
522
|
+
when Errors::HostError
|
523
|
+
refresh_host_async_retry(host, e, @reconnection_policy.schedule)
|
325
524
|
else
|
525
|
+
connection = @connection
|
526
|
+
connection && connection.close(e)
|
527
|
+
|
326
528
|
Ione::Future.resolved
|
327
529
|
end
|
530
|
+
end.map do
|
531
|
+
synchronize do
|
532
|
+
@refreshing_host.delete(host)
|
533
|
+
end
|
534
|
+
end
|
535
|
+
end
|
536
|
+
|
537
|
+
def refresh_host_async_retry(host, error, schedule)
|
538
|
+
timeout = schedule.next
|
539
|
+
@logger.info("Failed to refresh host #{host.ip.to_s} (#{error.class.name}: #{error.message}), retrying in #{timeout}")
|
540
|
+
|
541
|
+
timer = @io_reactor.schedule_timer(timeout)
|
542
|
+
timer.flat_map do
|
543
|
+
refresh_host_async(host).fallback do |e|
|
544
|
+
case e
|
545
|
+
when Errors::HostError
|
546
|
+
refresh_host_async_retry(host, e, schedule)
|
547
|
+
else
|
548
|
+
connection = @connection
|
549
|
+
connection && connection.close(e)
|
550
|
+
|
551
|
+
Ione::Future.resolved
|
552
|
+
end
|
553
|
+
end
|
328
554
|
end
|
329
555
|
end
|
330
556
|
|
331
557
|
def refresh_host_async(address)
|
332
558
|
connection = @connection
|
333
|
-
return Ione::Future.failed(
|
559
|
+
return Ione::Future.failed(Errors::ClientError.new('Not connected')) if connection.nil?
|
334
560
|
|
335
561
|
ip = address.to_s
|
336
562
|
|
337
|
-
@logger.debug('Fetching node information for %s' % ip)
|
338
|
-
|
339
563
|
if ip == connection.host
|
340
564
|
request = SELECT_LOCAL
|
341
565
|
else
|
342
566
|
request = Protocol::QueryRequest.new('SELECT rack, data_center, host_id, rpc_address, release_version, tokens FROM system.peers WHERE peer = ?', [address], nil, :one)
|
343
567
|
end
|
344
568
|
|
345
|
-
connection
|
346
|
-
@logger.debug('Fetched node information for %s' % ip)
|
347
|
-
|
348
|
-
rows = response.rows
|
349
|
-
|
569
|
+
send_select_request(connection, request).map do |rows|
|
350
570
|
unless rows.empty?
|
351
571
|
@registry.host_found(address, rows.first)
|
352
|
-
host = @registry.host(address)
|
353
|
-
refresh_host_status(host) if host.down?
|
354
572
|
end
|
355
573
|
|
356
574
|
self
|
@@ -359,33 +577,50 @@ module Cassandra
|
|
359
577
|
|
360
578
|
def connect_to_first_available(plan, errors = nil)
|
361
579
|
unless plan.has_next?
|
362
|
-
@
|
363
|
-
|
580
|
+
if errors.nil? && synchronize { @refreshing_statuses.empty? }
|
581
|
+
@logger.fatal(<<-MSG)
|
582
|
+
Control connection failed and is unlikely to recover.
|
583
|
+
|
584
|
+
This usually means that all hosts are ignored by current load
|
585
|
+
balancing policy, most likely because they changed datacenters.
|
586
|
+
Reconnections attempts will continue getting scheduled to
|
587
|
+
repeat this message in the logs.
|
588
|
+
MSG
|
589
|
+
end
|
590
|
+
|
591
|
+
return Ione::Future.failed(Errors::NoHostsAvailable.new(errors))
|
364
592
|
end
|
365
593
|
|
366
594
|
host = plan.next
|
367
|
-
@logger.debug("
|
595
|
+
@logger.debug("Connecting to #{host.ip}")
|
596
|
+
|
368
597
|
f = connect_to_host(host)
|
369
598
|
f = f.flat_map do |connection|
|
370
599
|
synchronize do
|
371
600
|
@status = :connected
|
372
601
|
|
373
|
-
@logger.debug("Control connection established ip=#{connection.host}")
|
374
602
|
@connection = connection
|
375
603
|
|
376
|
-
connection.on_closed do
|
604
|
+
connection.on_closed do |cause|
|
377
605
|
reconnect = false
|
378
606
|
|
379
607
|
synchronize do
|
380
|
-
if
|
381
|
-
@status
|
382
|
-
|
383
|
-
|
384
|
-
|
608
|
+
if connection == @connection
|
609
|
+
if @status == :closing
|
610
|
+
@status = :closed
|
611
|
+
else
|
612
|
+
@status = :reconnecting
|
613
|
+
reconnect = true
|
614
|
+
end
|
615
|
+
|
616
|
+
if cause
|
617
|
+
@logger.info("Control connection closed (#{cause.class.name}: #{cause.message})")
|
618
|
+
else
|
619
|
+
@logger.info("Control connection closed")
|
620
|
+
end
|
621
|
+
|
622
|
+
@connection = nil
|
385
623
|
end
|
386
|
-
|
387
|
-
@logger.debug("Control connection closed ip=#{connection.host}")
|
388
|
-
@connection = nil
|
389
624
|
end
|
390
625
|
|
391
626
|
reconnect_async(@reconnection_policy.schedule) if reconnect
|
@@ -394,23 +629,26 @@ module Cassandra
|
|
394
629
|
|
395
630
|
register_async
|
396
631
|
end
|
397
|
-
f = f.flat_map {
|
398
|
-
f = f.flat_map {
|
399
|
-
f.fallback do |error|
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
Ione::Future.failed(Errors::AuthenticationError.new(error.message))
|
405
|
-
else
|
406
|
-
Ione::Future.failed(error)
|
407
|
-
end
|
408
|
-
else
|
632
|
+
f = f.flat_map { refresh_hosts_async_maybe_retry }
|
633
|
+
f = f.flat_map { refresh_schema_async_maybe_retry }
|
634
|
+
f = f.fallback do |error|
|
635
|
+
@logger.debug("Connection to #{host.ip} failed (#{error.class.name}: #{error.message})")
|
636
|
+
|
637
|
+
case error
|
638
|
+
when Errors::HostError
|
409
639
|
errors ||= {}
|
410
640
|
errors[host] = error
|
411
641
|
connect_to_first_available(plan, errors)
|
642
|
+
else
|
643
|
+
Ione::Future.failed(error)
|
412
644
|
end
|
413
645
|
end
|
646
|
+
|
647
|
+
f.on_complete do |f|
|
648
|
+
@logger.info('Control connection established') if f.resolved?
|
649
|
+
end
|
650
|
+
|
651
|
+
f
|
414
652
|
end
|
415
653
|
|
416
654
|
def connect_to_host(host)
|
@@ -420,7 +658,21 @@ module Cassandra
|
|
420
658
|
def peer_ip(data)
|
421
659
|
ip = data['rpc_address']
|
422
660
|
ip = data['peer'] if ip == '0.0.0.0'
|
423
|
-
|
661
|
+
|
662
|
+
@address_resolver.resolve(ip)
|
663
|
+
end
|
664
|
+
|
665
|
+
def send_select_request(connection, request)
|
666
|
+
connection.send_request(request).map do |r|
|
667
|
+
case r
|
668
|
+
when Protocol::RowsResultResponse
|
669
|
+
r.rows
|
670
|
+
when Protocol::ErrorResponse
|
671
|
+
raise r.to_error(VOID_STATEMENT)
|
672
|
+
else
|
673
|
+
raise Errors::InternalError, "Unexpected response #{r.inspect}"
|
674
|
+
end
|
675
|
+
end
|
424
676
|
end
|
425
677
|
end
|
426
678
|
end
|