cassandra-driver 1.0.0.beta.2-java
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.yardopts +4 -0
- data/README.md +125 -0
- data/lib/cassandra/auth/providers/password.rb +73 -0
- data/lib/cassandra/auth/providers.rb +16 -0
- data/lib/cassandra/auth.rb +97 -0
- data/lib/cassandra/client/batch.rb +212 -0
- data/lib/cassandra/client/client.rb +591 -0
- data/lib/cassandra/client/column_metadata.rb +54 -0
- data/lib/cassandra/client/connection_manager.rb +72 -0
- data/lib/cassandra/client/connector.rb +277 -0
- data/lib/cassandra/client/execute_options_decoder.rb +59 -0
- data/lib/cassandra/client/null_logger.rb +37 -0
- data/lib/cassandra/client/peer_discovery.rb +50 -0
- data/lib/cassandra/client/prepared_statement.rb +314 -0
- data/lib/cassandra/client/query_result.rb +230 -0
- data/lib/cassandra/client/request_runner.rb +71 -0
- data/lib/cassandra/client/result_metadata.rb +48 -0
- data/lib/cassandra/client/void_result.rb +78 -0
- data/lib/cassandra/client.rb +144 -0
- data/lib/cassandra/cluster/client.rb +768 -0
- data/lib/cassandra/cluster/connector.rb +244 -0
- data/lib/cassandra/cluster/control_connection.rb +425 -0
- data/lib/cassandra/cluster/metadata.rb +124 -0
- data/lib/cassandra/cluster/options.rb +42 -0
- data/lib/cassandra/cluster/registry.rb +198 -0
- data/lib/cassandra/cluster/schema/partitioners/murmur3.rb +47 -0
- data/lib/cassandra/cluster/schema/partitioners/ordered.rb +37 -0
- data/lib/cassandra/cluster/schema/partitioners/random.rb +37 -0
- data/lib/cassandra/cluster/schema/partitioners.rb +21 -0
- data/lib/cassandra/cluster/schema/replication_strategies/network_topology.rb +92 -0
- data/lib/cassandra/cluster/schema/replication_strategies/none.rb +39 -0
- data/lib/cassandra/cluster/schema/replication_strategies/simple.rb +44 -0
- data/lib/cassandra/cluster/schema/replication_strategies.rb +21 -0
- data/lib/cassandra/cluster/schema/type_parser.rb +138 -0
- data/lib/cassandra/cluster/schema.rb +340 -0
- data/lib/cassandra/cluster.rb +215 -0
- data/lib/cassandra/column.rb +92 -0
- data/lib/cassandra/compression/compressors/lz4.rb +72 -0
- data/lib/cassandra/compression/compressors/snappy.rb +66 -0
- data/lib/cassandra/compression.rb +66 -0
- data/lib/cassandra/driver.rb +111 -0
- data/lib/cassandra/errors.rb +79 -0
- data/lib/cassandra/execution/info.rb +51 -0
- data/lib/cassandra/execution/options.rb +80 -0
- data/lib/cassandra/execution/trace.rb +152 -0
- data/lib/cassandra/future.rb +675 -0
- data/lib/cassandra/host.rb +79 -0
- data/lib/cassandra/keyspace.rb +133 -0
- data/lib/cassandra/listener.rb +87 -0
- data/lib/cassandra/load_balancing/policies/dc_aware_round_robin.rb +149 -0
- data/lib/cassandra/load_balancing/policies/round_robin.rb +132 -0
- data/lib/cassandra/load_balancing/policies/token_aware.rb +119 -0
- data/lib/cassandra/load_balancing/policies/white_list.rb +90 -0
- data/lib/cassandra/load_balancing/policies.rb +19 -0
- data/lib/cassandra/load_balancing.rb +113 -0
- data/lib/cassandra/protocol/cql_byte_buffer.rb +307 -0
- data/lib/cassandra/protocol/cql_protocol_handler.rb +323 -0
- data/lib/cassandra/protocol/frame_decoder.rb +128 -0
- data/lib/cassandra/protocol/frame_encoder.rb +48 -0
- data/lib/cassandra/protocol/request.rb +38 -0
- data/lib/cassandra/protocol/requests/auth_response_request.rb +47 -0
- data/lib/cassandra/protocol/requests/batch_request.rb +76 -0
- data/lib/cassandra/protocol/requests/credentials_request.rb +47 -0
- data/lib/cassandra/protocol/requests/execute_request.rb +103 -0
- data/lib/cassandra/protocol/requests/options_request.rb +39 -0
- data/lib/cassandra/protocol/requests/prepare_request.rb +50 -0
- data/lib/cassandra/protocol/requests/query_request.rb +153 -0
- data/lib/cassandra/protocol/requests/register_request.rb +38 -0
- data/lib/cassandra/protocol/requests/startup_request.rb +49 -0
- data/lib/cassandra/protocol/requests/void_query_request.rb +24 -0
- data/lib/cassandra/protocol/response.rb +38 -0
- data/lib/cassandra/protocol/responses/auth_challenge_response.rb +41 -0
- data/lib/cassandra/protocol/responses/auth_success_response.rb +41 -0
- data/lib/cassandra/protocol/responses/authenticate_response.rb +41 -0
- data/lib/cassandra/protocol/responses/detailed_error_response.rb +60 -0
- data/lib/cassandra/protocol/responses/error_response.rb +50 -0
- data/lib/cassandra/protocol/responses/event_response.rb +39 -0
- data/lib/cassandra/protocol/responses/prepared_result_response.rb +64 -0
- data/lib/cassandra/protocol/responses/raw_rows_result_response.rb +43 -0
- data/lib/cassandra/protocol/responses/ready_response.rb +44 -0
- data/lib/cassandra/protocol/responses/result_response.rb +48 -0
- data/lib/cassandra/protocol/responses/rows_result_response.rb +139 -0
- data/lib/cassandra/protocol/responses/schema_change_event_response.rb +60 -0
- data/lib/cassandra/protocol/responses/schema_change_result_response.rb +57 -0
- data/lib/cassandra/protocol/responses/set_keyspace_result_response.rb +42 -0
- data/lib/cassandra/protocol/responses/status_change_event_response.rb +44 -0
- data/lib/cassandra/protocol/responses/supported_response.rb +41 -0
- data/lib/cassandra/protocol/responses/topology_change_event_response.rb +34 -0
- data/lib/cassandra/protocol/responses/void_result_response.rb +39 -0
- data/lib/cassandra/protocol/type_converter.rb +384 -0
- data/lib/cassandra/protocol.rb +93 -0
- data/lib/cassandra/reconnection/policies/constant.rb +48 -0
- data/lib/cassandra/reconnection/policies/exponential.rb +79 -0
- data/lib/cassandra/reconnection/policies.rb +20 -0
- data/lib/cassandra/reconnection.rb +49 -0
- data/lib/cassandra/result.rb +215 -0
- data/lib/cassandra/retry/policies/default.rb +47 -0
- data/lib/cassandra/retry/policies/downgrading_consistency.rb +71 -0
- data/lib/cassandra/retry/policies/fallthrough.rb +39 -0
- data/lib/cassandra/retry/policies.rb +21 -0
- data/lib/cassandra/retry.rb +142 -0
- data/lib/cassandra/session.rb +202 -0
- data/lib/cassandra/statement.rb +22 -0
- data/lib/cassandra/statements/batch.rb +95 -0
- data/lib/cassandra/statements/bound.rb +48 -0
- data/lib/cassandra/statements/prepared.rb +81 -0
- data/lib/cassandra/statements/simple.rb +58 -0
- data/lib/cassandra/statements/void.rb +33 -0
- data/lib/cassandra/statements.rb +23 -0
- data/lib/cassandra/table.rb +299 -0
- data/lib/cassandra/time_uuid.rb +142 -0
- data/lib/cassandra/util.rb +167 -0
- data/lib/cassandra/uuid.rb +104 -0
- data/lib/cassandra/version.rb +21 -0
- data/lib/cassandra.rb +428 -0
- data/lib/cassandra_murmur3.jar +0 -0
- metadata +211 -0
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
#--
|
|
4
|
+
# Copyright 2013-2014 DataStax, Inc.
|
|
5
|
+
#
|
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
7
|
+
# you may not use this file except in compliance with the License.
|
|
8
|
+
# You may obtain a copy of the License at
|
|
9
|
+
#
|
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
11
|
+
#
|
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
15
|
+
# See the License for the specific language governing permissions and
|
|
16
|
+
# limitations under the License.
|
|
17
|
+
#++
|
|
18
|
+
|
|
19
|
+
module Cassandra
|
|
20
|
+
class Cluster
|
|
21
|
+
# @private
|
|
22
|
+
class Connector
|
|
23
|
+
include MonitorMixin
|
|
24
|
+
|
|
25
|
+
def initialize(logger, io_reactor, cluster_registry, connection_options)
|
|
26
|
+
@logger = logger
|
|
27
|
+
@reactor = io_reactor
|
|
28
|
+
@registry = cluster_registry
|
|
29
|
+
@options = connection_options
|
|
30
|
+
@connections = ::Hash.new
|
|
31
|
+
@open_connections = ::Hash.new
|
|
32
|
+
|
|
33
|
+
mon_initialize
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def connect(host)
|
|
37
|
+
synchronize do
|
|
38
|
+
open_connections = @open_connections[host]
|
|
39
|
+
if open_connections
|
|
40
|
+
connection = open_connections.shift
|
|
41
|
+
@open_connections.delete(host) if open_connections.empty?
|
|
42
|
+
return Ione::Future.resolved(connection)
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
@logger.info("Connecting ip=#{host.ip}")
|
|
47
|
+
|
|
48
|
+
f = do_connect(host)
|
|
49
|
+
|
|
50
|
+
f.on_failure do |error|
|
|
51
|
+
@logger.warn("Connection failed ip=#{host.ip} error=\"#{error.class.name}: #{error.message}\"")
|
|
52
|
+
connection_error(host, error)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
f.on_value do |connection|
|
|
56
|
+
connection.on_closed do |cause|
|
|
57
|
+
message = "Disconnected ip=#{host.ip}"
|
|
58
|
+
message << " error=#{cause.message}" if cause
|
|
59
|
+
|
|
60
|
+
@logger.info(message)
|
|
61
|
+
disconnected(host, cause)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
@logger.info("Connected ip=#{host.ip}")
|
|
65
|
+
connected(host)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
f
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def refresh_status(host)
|
|
72
|
+
if synchronize { @connections[host] }
|
|
73
|
+
@registry.host_up(host.ip)
|
|
74
|
+
|
|
75
|
+
return Future.resolved
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
@logger.info("Refreshing host status ip=#{host.ip}")
|
|
79
|
+
f = do_connect(host)
|
|
80
|
+
|
|
81
|
+
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
|
+
connection_error(host, error)
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
f.on_value do |connection|
|
|
88
|
+
@logger.info("Refreshed host status ip=#{host.ip}")
|
|
89
|
+
connection.on_closed do |cause|
|
|
90
|
+
message = "Disconnected ip=#{host.ip}"
|
|
91
|
+
message << " error=#{cause.message}" if cause
|
|
92
|
+
|
|
93
|
+
@logger.info(message)
|
|
94
|
+
disconnected(host, cause)
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
synchronize do
|
|
98
|
+
@open_connections[host] ||= []
|
|
99
|
+
@open_connections[host] << connection
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
@logger.info("Connected ip=#{host.ip}")
|
|
103
|
+
connected(host)
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
f
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def connect_many(host, count)
|
|
110
|
+
create_additional_connections(host, count, [])
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
private
|
|
114
|
+
|
|
115
|
+
NO_CONNECTIONS = Ione::Future.resolved([])
|
|
116
|
+
|
|
117
|
+
def do_connect(host)
|
|
118
|
+
create_connector.connect(host.ip.to_s).fallback do |error|
|
|
119
|
+
if error.is_a?(Errors::QueryError) && error.code == 0x0a
|
|
120
|
+
synchronize do
|
|
121
|
+
if @options.protocol_version > 1
|
|
122
|
+
@logger.warn('Could not connect using protocol version %d (will try again with %d): %s' % [@options.protocol_version, @options.protocol_version - 1, error.message])
|
|
123
|
+
@options.protocol_version -= 1
|
|
124
|
+
do_connect(host)
|
|
125
|
+
else
|
|
126
|
+
Ione::Future.failed(error)
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
else
|
|
130
|
+
Ione::Future.failed(error)
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
def create_connector
|
|
136
|
+
authentication_step = @options.protocol_version == 1 ? Cassandra::Client::CredentialsAuthenticationStep.new(@options.credentials) : Cassandra::Client::SaslAuthenticationStep.new(@options.auth_provider)
|
|
137
|
+
protocol_handler_factory = lambda do |connection|
|
|
138
|
+
raise "no connection given" unless connection
|
|
139
|
+
Protocol::CqlProtocolHandler.new(connection, @reactor, @options.protocol_version, @options.compressor)
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
Cassandra::Client::Connector.new([
|
|
143
|
+
Cassandra::Client::ConnectStep.new(
|
|
144
|
+
@reactor,
|
|
145
|
+
protocol_handler_factory,
|
|
146
|
+
@options.port,
|
|
147
|
+
{:timeout => @options.connect_timeout, :ssl => @options.ssl},
|
|
148
|
+
@logger
|
|
149
|
+
),
|
|
150
|
+
Cassandra::Client::CacheOptionsStep.new(@options.connect_timeout),
|
|
151
|
+
Cassandra::Client::InitializeStep.new(@options.compressor, @logger),
|
|
152
|
+
authentication_step,
|
|
153
|
+
Cassandra::Client::CachePropertiesStep.new,
|
|
154
|
+
])
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
def create_additional_connections(host, count, established_connections, error = nil)
|
|
158
|
+
futures = count.times.map do
|
|
159
|
+
connect(host).recover do |e|
|
|
160
|
+
Cassandra::Client::FailedConnection.new(e, host)
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
Ione::Future.all(*futures).flat_map do |connections|
|
|
165
|
+
established_connections.select!(&:connected?)
|
|
166
|
+
|
|
167
|
+
connections.each do |connection|
|
|
168
|
+
if connection.connected?
|
|
169
|
+
established_connections << connection
|
|
170
|
+
else
|
|
171
|
+
error = connection.error
|
|
172
|
+
end
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
if !established_connections.empty?
|
|
176
|
+
connections_left = count - established_connections.size
|
|
177
|
+
if connections_left == 0
|
|
178
|
+
Ione::Future.resolved(established_connections)
|
|
179
|
+
else
|
|
180
|
+
create_additional_connections(host, connections_left, established_connections, error)
|
|
181
|
+
end
|
|
182
|
+
else
|
|
183
|
+
Ione::Future.failed(error)
|
|
184
|
+
end
|
|
185
|
+
end
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
def connected(host)
|
|
189
|
+
notify = false
|
|
190
|
+
|
|
191
|
+
synchronize do
|
|
192
|
+
connections = @connections[host]
|
|
193
|
+
|
|
194
|
+
if connections
|
|
195
|
+
@connections[host] = connections + 1
|
|
196
|
+
else
|
|
197
|
+
notify = true
|
|
198
|
+
|
|
199
|
+
@connections[host] = 1
|
|
200
|
+
end
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
@registry.host_up(host.ip) if notify
|
|
204
|
+
|
|
205
|
+
self
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
def disconnected(host, error)
|
|
209
|
+
notify = false
|
|
210
|
+
|
|
211
|
+
synchronize do
|
|
212
|
+
connections = @connections[host]
|
|
213
|
+
|
|
214
|
+
return self unless connections
|
|
215
|
+
|
|
216
|
+
connections -= 1
|
|
217
|
+
|
|
218
|
+
if connections == 0
|
|
219
|
+
notify = !error.nil? && !error.is_a?(Cassandra::Error)
|
|
220
|
+
@connections.delete(host)
|
|
221
|
+
else
|
|
222
|
+
@connections[host] = connections
|
|
223
|
+
end
|
|
224
|
+
end
|
|
225
|
+
|
|
226
|
+
@registry.host_down(host.ip) if notify
|
|
227
|
+
|
|
228
|
+
self
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
def connection_error(host, error)
|
|
232
|
+
notify = false
|
|
233
|
+
|
|
234
|
+
synchronize do
|
|
235
|
+
notify = !error.is_a?(Cassandra::Error) && !@connections.has_key?(host)
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
@registry.host_down(host.ip) if notify
|
|
239
|
+
|
|
240
|
+
self
|
|
241
|
+
end
|
|
242
|
+
end
|
|
243
|
+
end
|
|
244
|
+
end
|
|
@@ -0,0 +1,425 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
#--
|
|
4
|
+
# Copyright 2013-2014 DataStax, Inc.
|
|
5
|
+
#
|
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
7
|
+
# you may not use this file except in compliance with the License.
|
|
8
|
+
# You may obtain a copy of the License at
|
|
9
|
+
#
|
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
11
|
+
#
|
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
15
|
+
# See the License for the specific language governing permissions and
|
|
16
|
+
# limitations under the License.
|
|
17
|
+
#++
|
|
18
|
+
|
|
19
|
+
module Cassandra
|
|
20
|
+
class Cluster
|
|
21
|
+
# @private
|
|
22
|
+
class ControlConnection
|
|
23
|
+
include MonitorMixin
|
|
24
|
+
|
|
25
|
+
def initialize(logger, io_reactor, cluster_registry, cluster_schema, cluster_metadata, load_balancing_policy, reconnection_policy, connector)
|
|
26
|
+
@logger = logger
|
|
27
|
+
@io_reactor = io_reactor
|
|
28
|
+
@registry = cluster_registry
|
|
29
|
+
@schema = cluster_schema
|
|
30
|
+
@metadata = cluster_metadata
|
|
31
|
+
@load_balancing_policy = load_balancing_policy
|
|
32
|
+
@reconnection_policy = reconnection_policy
|
|
33
|
+
@connector = connector
|
|
34
|
+
@refreshing_statuses = Hash.new(false)
|
|
35
|
+
@status = :closed
|
|
36
|
+
|
|
37
|
+
mon_initialize
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def connect_async
|
|
41
|
+
synchronize do
|
|
42
|
+
return Ione::Future.resolved if @status == :connecting || @status == :connected
|
|
43
|
+
@status = :connecting
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
@logger.debug('Establishing control connection')
|
|
47
|
+
|
|
48
|
+
@io_reactor.start.flat_map do
|
|
49
|
+
plan = @load_balancing_policy.plan(nil, VOID_STATEMENT, VOID_OPTIONS)
|
|
50
|
+
connect_to_first_available(plan)
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def host_found(host)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def host_lost(host)
|
|
58
|
+
synchronize do
|
|
59
|
+
@refreshing_statuses.delete(host)
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def host_up(host)
|
|
64
|
+
synchronize do
|
|
65
|
+
@refreshing_statuses.delete(host)
|
|
66
|
+
|
|
67
|
+
return connect_async if !@connection && !(@status == :closed || @status == :closed)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
Ione::Future.resolved
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def host_down(host)
|
|
74
|
+
synchronize do
|
|
75
|
+
return Ione::Future.resolved if @refreshing_statuses[host]
|
|
76
|
+
|
|
77
|
+
@logger.debug("Starting to continuously refresh status for ip=#{host.ip}")
|
|
78
|
+
@refreshing_statuses[host] = true
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
refresh_host_status(host).fallback do |e|
|
|
82
|
+
refresh_host_status_with_retry(host, @reconnection_policy.schedule)
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def close_async
|
|
87
|
+
synchronize do
|
|
88
|
+
return Ione::Future.resolved if @status == :closing || @status == :closed
|
|
89
|
+
@status = :closing
|
|
90
|
+
end
|
|
91
|
+
@io_reactor.stop
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def inspect
|
|
95
|
+
"#<#{self.class.name}:0x#{self.object_id.to_s(16)}>"
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
private
|
|
99
|
+
|
|
100
|
+
SELECT_LOCAL = Protocol::QueryRequest.new('SELECT rack, data_center, host_id, release_version, tokens, partitioner FROM system.local', nil, nil, :one)
|
|
101
|
+
SELECT_PEERS = Protocol::QueryRequest.new('SELECT peer, rack, data_center, host_id, rpc_address, release_version, tokens FROM system.peers', nil, nil, :one)
|
|
102
|
+
SELECT_KEYSPACES = Protocol::QueryRequest.new('SELECT * FROM system.schema_keyspaces', nil, nil, :one)
|
|
103
|
+
SELECT_TABLES = Protocol::QueryRequest.new('SELECT * FROM system.schema_columnfamilies', nil, nil, :one)
|
|
104
|
+
SELECT_COLUMNS = Protocol::QueryRequest.new('SELECT * FROM system.schema_columns', nil, nil, :one)
|
|
105
|
+
REGISTER = Protocol::RegisterRequest.new(
|
|
106
|
+
Protocol::TopologyChangeEventResponse::TYPE,
|
|
107
|
+
Protocol::StatusChangeEventResponse::TYPE,
|
|
108
|
+
Protocol::SchemaChangeEventResponse::TYPE
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
def reconnect_async(schedule)
|
|
112
|
+
timeout = schedule.next
|
|
113
|
+
|
|
114
|
+
@logger.debug("Reestablishing control connection in #{timeout} seconds")
|
|
115
|
+
|
|
116
|
+
f = @io_reactor.schedule_timer(timeout)
|
|
117
|
+
f = f.flat_map do
|
|
118
|
+
if synchronize { @status == :reconnecting }
|
|
119
|
+
@logger.debug('Reestablishing control connection')
|
|
120
|
+
plan = @load_balancing_policy.plan(nil, VOID_STATEMENT, VOID_OPTIONS)
|
|
121
|
+
connect_to_first_available(plan)
|
|
122
|
+
else
|
|
123
|
+
@logger.debug('Stopping reconnection')
|
|
124
|
+
Ione::Future.resolved
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
f.fallback do
|
|
128
|
+
if synchronize { @status == :reconnecting }
|
|
129
|
+
reconnect_async(schedule)
|
|
130
|
+
else
|
|
131
|
+
@logger.debug('Stopping reconnection')
|
|
132
|
+
return Ione::Future.resolved
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
def register_async
|
|
138
|
+
connection = @connection
|
|
139
|
+
|
|
140
|
+
return Ione::Future.failed("not connected") if connection.nil?
|
|
141
|
+
|
|
142
|
+
@logger.debug('Registering for events')
|
|
143
|
+
|
|
144
|
+
connection.send_request(REGISTER).map do
|
|
145
|
+
@logger.debug('Registered for events')
|
|
146
|
+
|
|
147
|
+
connection.on_event do |event|
|
|
148
|
+
@logger.debug("Event received #{event}")
|
|
149
|
+
|
|
150
|
+
if event.type == 'SCHEMA_CHANGE'
|
|
151
|
+
case event.change
|
|
152
|
+
when 'CREATED'
|
|
153
|
+
if event.table.empty?
|
|
154
|
+
refresh_schema_async
|
|
155
|
+
else
|
|
156
|
+
refresh_keyspace_async(event.keyspace)
|
|
157
|
+
end
|
|
158
|
+
when 'DROPPED'
|
|
159
|
+
if event.table.empty?
|
|
160
|
+
refresh_schema_async
|
|
161
|
+
else
|
|
162
|
+
refresh_keyspace_async(event.keyspace)
|
|
163
|
+
end
|
|
164
|
+
when 'UPDATED'
|
|
165
|
+
if event.table.empty?
|
|
166
|
+
refresh_keyspace_async(event.keyspace)
|
|
167
|
+
else
|
|
168
|
+
refresh_table_async(event.keyspace, event.table)
|
|
169
|
+
end
|
|
170
|
+
end
|
|
171
|
+
else
|
|
172
|
+
case event.change
|
|
173
|
+
when 'UP'
|
|
174
|
+
address = event.address
|
|
175
|
+
|
|
176
|
+
refresh_host_async(address) if @registry.has_host?(address)
|
|
177
|
+
when 'DOWN'
|
|
178
|
+
@registry.host_down(event.address)
|
|
179
|
+
when 'NEW_NODE'
|
|
180
|
+
address = event.address
|
|
181
|
+
|
|
182
|
+
unless @registry.has_host?(address)
|
|
183
|
+
refresh_host_async(address)
|
|
184
|
+
refresh_schema_async
|
|
185
|
+
end
|
|
186
|
+
when 'REMOVED_NODE'
|
|
187
|
+
@registry.host_lost(event.address)
|
|
188
|
+
refresh_schema_async
|
|
189
|
+
end
|
|
190
|
+
end
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
self
|
|
194
|
+
end
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
def refresh_schema_async
|
|
198
|
+
connection = @connection
|
|
199
|
+
|
|
200
|
+
return Ione::Future.failed("not connected") if connection.nil?
|
|
201
|
+
|
|
202
|
+
@logger.debug('Fetching schema metadata')
|
|
203
|
+
|
|
204
|
+
keyspaces = connection.send_request(SELECT_KEYSPACES)
|
|
205
|
+
tables = connection.send_request(SELECT_TABLES)
|
|
206
|
+
columns = connection.send_request(SELECT_COLUMNS)
|
|
207
|
+
|
|
208
|
+
Ione::Future.all(keyspaces, tables, columns).map do |(keyspaces, tables, columns)|
|
|
209
|
+
@logger.debug('Fetched schema metadata')
|
|
210
|
+
|
|
211
|
+
host = @registry.host(connection.host)
|
|
212
|
+
|
|
213
|
+
@schema.update_keyspaces(host, keyspaces.rows, tables.rows, columns.rows)
|
|
214
|
+
@metadata.rebuild_token_map
|
|
215
|
+
end
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
def refresh_keyspace_async(keyspace)
|
|
219
|
+
connection = @connection
|
|
220
|
+
|
|
221
|
+
return Ione::Future.failed("not connected") if connection.nil?
|
|
222
|
+
|
|
223
|
+
@logger.debug("Fetching keyspace #{keyspace.inspect} metadata")
|
|
224
|
+
|
|
225
|
+
params = [keyspace]
|
|
226
|
+
keyspaces = connection.send_request(Protocol::QueryRequest.new("SELECT * FROM system.schema_keyspaces WHERE keyspace_name = ?", params, nil, :one))
|
|
227
|
+
tables = connection.send_request(Protocol::QueryRequest.new("SELECT * FROM system.schema_columnfamilies WHERE keyspace_name = ?", params, nil, :one))
|
|
228
|
+
columns = connection.send_request(Protocol::QueryRequest.new("SELECT * FROM system.schema_columns WHERE keyspace_name = ?", params, nil, :one))
|
|
229
|
+
|
|
230
|
+
Ione::Future.all(keyspaces, tables, columns).map do |(keyspaces, tables, columns)|
|
|
231
|
+
@logger.debug("Fetched keyspace #{keyspace.inspect} metadata")
|
|
232
|
+
|
|
233
|
+
host = @registry.host(connection.host)
|
|
234
|
+
|
|
235
|
+
@schema.update_keyspace(host, keyspaces.rows.first, tables.rows, columns.rows)
|
|
236
|
+
end
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
def refresh_table_async(keyspace, table)
|
|
240
|
+
connection = @connection
|
|
241
|
+
|
|
242
|
+
return Ione::Future.failed("not connected") if connection.nil?
|
|
243
|
+
|
|
244
|
+
@logger.debug("Fetching table \"#{keyspace}.#{table}\" metadata")
|
|
245
|
+
|
|
246
|
+
params = [keyspace, table]
|
|
247
|
+
table = connection.send_request(Protocol::QueryRequest.new("SELECT * FROM system.schema_columnfamilies WHERE keyspace_name = ? AND columnfamily_name = ?", params, nil, :one))
|
|
248
|
+
columns = connection.send_request(Protocol::QueryRequest.new("SELECT * FROM system.schema_columns WHERE keyspace_name = ? AND columnfamily_name = ?", params, nil, :one))
|
|
249
|
+
|
|
250
|
+
Ione::Future.all(table, columns).map do |(table, columns)|
|
|
251
|
+
@logger.debug("Fetched table \"#{keyspace}.#{table}\" metadata")
|
|
252
|
+
|
|
253
|
+
host = @registry.host(connection.host)
|
|
254
|
+
|
|
255
|
+
@schema.udpate_table(host, keyspace, table.rows, columns.rows)
|
|
256
|
+
end
|
|
257
|
+
end
|
|
258
|
+
|
|
259
|
+
def refresh_hosts_async
|
|
260
|
+
connection = @connection
|
|
261
|
+
|
|
262
|
+
return Ione::Future.failed("not connected") if connection.nil?
|
|
263
|
+
|
|
264
|
+
@logger.debug('Fetching cluster metadata and peers')
|
|
265
|
+
|
|
266
|
+
local = connection.send_request(SELECT_LOCAL)
|
|
267
|
+
peers = connection.send_request(SELECT_PEERS)
|
|
268
|
+
|
|
269
|
+
Ione::Future.all(local, peers).flat_map do |(local, peers)|
|
|
270
|
+
local = local.rows
|
|
271
|
+
peers = peers.rows
|
|
272
|
+
|
|
273
|
+
@logger.debug('%d peers found' % peers.size)
|
|
274
|
+
|
|
275
|
+
raise NO_HOSTS if local.empty? && peers.empty?
|
|
276
|
+
|
|
277
|
+
ips = ::Set.new
|
|
278
|
+
|
|
279
|
+
peers.each do |data|
|
|
280
|
+
ips << ip = peer_ip(data)
|
|
281
|
+
@registry.host_found(ip, data)
|
|
282
|
+
end
|
|
283
|
+
|
|
284
|
+
ips << ip = IPAddr.new(connection.host)
|
|
285
|
+
data = local.first
|
|
286
|
+
@registry.host_found(ip, data)
|
|
287
|
+
|
|
288
|
+
futures = []
|
|
289
|
+
|
|
290
|
+
@registry.each_host do |host|
|
|
291
|
+
if ips.include?(host.ip)
|
|
292
|
+
futures << refresh_host_status(host) if host.down?
|
|
293
|
+
else
|
|
294
|
+
@registry.host_lost(host.ip)
|
|
295
|
+
end
|
|
296
|
+
end
|
|
297
|
+
|
|
298
|
+
@metadata.update(data)
|
|
299
|
+
|
|
300
|
+
if futures.empty?
|
|
301
|
+
Ione::Future.resolved
|
|
302
|
+
else
|
|
303
|
+
Ione::Future.all(*futures)
|
|
304
|
+
end
|
|
305
|
+
end
|
|
306
|
+
end
|
|
307
|
+
|
|
308
|
+
def refresh_host_status(host)
|
|
309
|
+
@connector.refresh_status(host)
|
|
310
|
+
end
|
|
311
|
+
|
|
312
|
+
def refresh_host_status_with_retry(host, schedule)
|
|
313
|
+
timeout = schedule.next
|
|
314
|
+
|
|
315
|
+
@logger.info("Refreshing host status refresh ip=#{host.ip} in #{timeout}")
|
|
316
|
+
|
|
317
|
+
f = @io_reactor.schedule_timer(timeout)
|
|
318
|
+
f.flat_map do
|
|
319
|
+
if synchronize { @refreshing_statuses[host] }
|
|
320
|
+
refresh_host_status(host).fallback do |e|
|
|
321
|
+
refresh_host_status_with_retry(host, schedule)
|
|
322
|
+
end
|
|
323
|
+
else
|
|
324
|
+
Ione::Future.resolved
|
|
325
|
+
end
|
|
326
|
+
end
|
|
327
|
+
end
|
|
328
|
+
|
|
329
|
+
def refresh_host_async(address)
|
|
330
|
+
connection = @connection
|
|
331
|
+
return Ione::Future.failed("not connected") if connection.nil?
|
|
332
|
+
|
|
333
|
+
ip = address.to_s
|
|
334
|
+
|
|
335
|
+
@logger.debug('Fetching node information for %s' % ip)
|
|
336
|
+
|
|
337
|
+
if ip == connection.host
|
|
338
|
+
request = SELECT_LOCAL
|
|
339
|
+
else
|
|
340
|
+
request = Protocol::QueryRequest.new('SELECT rack, data_center, host_id, rpc_address, release_version, tokens FROM system.peers WHERE peer = ?', [address], nil, :one)
|
|
341
|
+
end
|
|
342
|
+
|
|
343
|
+
connection.send_request(request).map do |response|
|
|
344
|
+
@logger.debug('Fetched node information for %s' % ip)
|
|
345
|
+
|
|
346
|
+
rows = response.rows
|
|
347
|
+
|
|
348
|
+
unless rows.empty?
|
|
349
|
+
@registry.host_found(address, rows.first)
|
|
350
|
+
host = @registry.host(address)
|
|
351
|
+
refresh_host_status(host) if host.down?
|
|
352
|
+
end
|
|
353
|
+
|
|
354
|
+
self
|
|
355
|
+
end
|
|
356
|
+
end
|
|
357
|
+
|
|
358
|
+
def connect_to_first_available(plan, errors = nil)
|
|
359
|
+
unless plan.has_next?
|
|
360
|
+
@logger.warn("Control connection failed")
|
|
361
|
+
return Ione::Future.failed(Errors::NoHostsAvailable.new(errors || {}))
|
|
362
|
+
end
|
|
363
|
+
|
|
364
|
+
host = plan.next
|
|
365
|
+
@logger.debug("Attempting connection to ip=#{host.ip}")
|
|
366
|
+
f = connect_to_host(host)
|
|
367
|
+
f = f.flat_map do |connection|
|
|
368
|
+
synchronize do
|
|
369
|
+
@status = :connected
|
|
370
|
+
|
|
371
|
+
@logger.debug("Control connection established ip=#{connection.host}")
|
|
372
|
+
@connection = connection
|
|
373
|
+
|
|
374
|
+
connection.on_closed do
|
|
375
|
+
reconnect = false
|
|
376
|
+
|
|
377
|
+
synchronize do
|
|
378
|
+
if @status == :closing
|
|
379
|
+
@status = :closed
|
|
380
|
+
else
|
|
381
|
+
@status = :reconnecting
|
|
382
|
+
reconnect = true
|
|
383
|
+
end
|
|
384
|
+
|
|
385
|
+
@logger.debug("Control connection closed ip=#{connection.host}")
|
|
386
|
+
@connection = nil
|
|
387
|
+
end
|
|
388
|
+
|
|
389
|
+
reconnect_async(@reconnection_policy.schedule) if reconnect
|
|
390
|
+
end
|
|
391
|
+
end
|
|
392
|
+
|
|
393
|
+
register_async
|
|
394
|
+
end
|
|
395
|
+
f = f.flat_map { refresh_hosts_async }
|
|
396
|
+
f = f.flat_map { refresh_schema_async }
|
|
397
|
+
f.fallback do |error|
|
|
398
|
+
if error.is_a?(Errors::AuthenticationError)
|
|
399
|
+
Ione::Future.failed(error)
|
|
400
|
+
elsif error.is_a?(Errors::QueryError)
|
|
401
|
+
if error.code == 0x100
|
|
402
|
+
Ione::Future.failed(Errors::AuthenticationError.new(error.message))
|
|
403
|
+
else
|
|
404
|
+
Ione::Future.failed(error)
|
|
405
|
+
end
|
|
406
|
+
else
|
|
407
|
+
errors ||= {}
|
|
408
|
+
errors[host] = error
|
|
409
|
+
connect_to_first_available(plan, errors)
|
|
410
|
+
end
|
|
411
|
+
end
|
|
412
|
+
end
|
|
413
|
+
|
|
414
|
+
def connect_to_host(host)
|
|
415
|
+
@connector.connect(host)
|
|
416
|
+
end
|
|
417
|
+
|
|
418
|
+
def peer_ip(data)
|
|
419
|
+
ip = data['rpc_address']
|
|
420
|
+
ip = data['peer'] if ip == '0.0.0.0'
|
|
421
|
+
ip
|
|
422
|
+
end
|
|
423
|
+
end
|
|
424
|
+
end
|
|
425
|
+
end
|