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.
Files changed (118) hide show
  1. checksums.yaml +7 -0
  2. data/.yardopts +4 -0
  3. data/README.md +125 -0
  4. data/lib/cassandra/auth/providers/password.rb +73 -0
  5. data/lib/cassandra/auth/providers.rb +16 -0
  6. data/lib/cassandra/auth.rb +97 -0
  7. data/lib/cassandra/client/batch.rb +212 -0
  8. data/lib/cassandra/client/client.rb +591 -0
  9. data/lib/cassandra/client/column_metadata.rb +54 -0
  10. data/lib/cassandra/client/connection_manager.rb +72 -0
  11. data/lib/cassandra/client/connector.rb +277 -0
  12. data/lib/cassandra/client/execute_options_decoder.rb +59 -0
  13. data/lib/cassandra/client/null_logger.rb +37 -0
  14. data/lib/cassandra/client/peer_discovery.rb +50 -0
  15. data/lib/cassandra/client/prepared_statement.rb +314 -0
  16. data/lib/cassandra/client/query_result.rb +230 -0
  17. data/lib/cassandra/client/request_runner.rb +71 -0
  18. data/lib/cassandra/client/result_metadata.rb +48 -0
  19. data/lib/cassandra/client/void_result.rb +78 -0
  20. data/lib/cassandra/client.rb +144 -0
  21. data/lib/cassandra/cluster/client.rb +768 -0
  22. data/lib/cassandra/cluster/connector.rb +244 -0
  23. data/lib/cassandra/cluster/control_connection.rb +425 -0
  24. data/lib/cassandra/cluster/metadata.rb +124 -0
  25. data/lib/cassandra/cluster/options.rb +42 -0
  26. data/lib/cassandra/cluster/registry.rb +198 -0
  27. data/lib/cassandra/cluster/schema/partitioners/murmur3.rb +47 -0
  28. data/lib/cassandra/cluster/schema/partitioners/ordered.rb +37 -0
  29. data/lib/cassandra/cluster/schema/partitioners/random.rb +37 -0
  30. data/lib/cassandra/cluster/schema/partitioners.rb +21 -0
  31. data/lib/cassandra/cluster/schema/replication_strategies/network_topology.rb +92 -0
  32. data/lib/cassandra/cluster/schema/replication_strategies/none.rb +39 -0
  33. data/lib/cassandra/cluster/schema/replication_strategies/simple.rb +44 -0
  34. data/lib/cassandra/cluster/schema/replication_strategies.rb +21 -0
  35. data/lib/cassandra/cluster/schema/type_parser.rb +138 -0
  36. data/lib/cassandra/cluster/schema.rb +340 -0
  37. data/lib/cassandra/cluster.rb +215 -0
  38. data/lib/cassandra/column.rb +92 -0
  39. data/lib/cassandra/compression/compressors/lz4.rb +72 -0
  40. data/lib/cassandra/compression/compressors/snappy.rb +66 -0
  41. data/lib/cassandra/compression.rb +66 -0
  42. data/lib/cassandra/driver.rb +111 -0
  43. data/lib/cassandra/errors.rb +79 -0
  44. data/lib/cassandra/execution/info.rb +51 -0
  45. data/lib/cassandra/execution/options.rb +80 -0
  46. data/lib/cassandra/execution/trace.rb +152 -0
  47. data/lib/cassandra/future.rb +675 -0
  48. data/lib/cassandra/host.rb +79 -0
  49. data/lib/cassandra/keyspace.rb +133 -0
  50. data/lib/cassandra/listener.rb +87 -0
  51. data/lib/cassandra/load_balancing/policies/dc_aware_round_robin.rb +149 -0
  52. data/lib/cassandra/load_balancing/policies/round_robin.rb +132 -0
  53. data/lib/cassandra/load_balancing/policies/token_aware.rb +119 -0
  54. data/lib/cassandra/load_balancing/policies/white_list.rb +90 -0
  55. data/lib/cassandra/load_balancing/policies.rb +19 -0
  56. data/lib/cassandra/load_balancing.rb +113 -0
  57. data/lib/cassandra/protocol/cql_byte_buffer.rb +307 -0
  58. data/lib/cassandra/protocol/cql_protocol_handler.rb +323 -0
  59. data/lib/cassandra/protocol/frame_decoder.rb +128 -0
  60. data/lib/cassandra/protocol/frame_encoder.rb +48 -0
  61. data/lib/cassandra/protocol/request.rb +38 -0
  62. data/lib/cassandra/protocol/requests/auth_response_request.rb +47 -0
  63. data/lib/cassandra/protocol/requests/batch_request.rb +76 -0
  64. data/lib/cassandra/protocol/requests/credentials_request.rb +47 -0
  65. data/lib/cassandra/protocol/requests/execute_request.rb +103 -0
  66. data/lib/cassandra/protocol/requests/options_request.rb +39 -0
  67. data/lib/cassandra/protocol/requests/prepare_request.rb +50 -0
  68. data/lib/cassandra/protocol/requests/query_request.rb +153 -0
  69. data/lib/cassandra/protocol/requests/register_request.rb +38 -0
  70. data/lib/cassandra/protocol/requests/startup_request.rb +49 -0
  71. data/lib/cassandra/protocol/requests/void_query_request.rb +24 -0
  72. data/lib/cassandra/protocol/response.rb +38 -0
  73. data/lib/cassandra/protocol/responses/auth_challenge_response.rb +41 -0
  74. data/lib/cassandra/protocol/responses/auth_success_response.rb +41 -0
  75. data/lib/cassandra/protocol/responses/authenticate_response.rb +41 -0
  76. data/lib/cassandra/protocol/responses/detailed_error_response.rb +60 -0
  77. data/lib/cassandra/protocol/responses/error_response.rb +50 -0
  78. data/lib/cassandra/protocol/responses/event_response.rb +39 -0
  79. data/lib/cassandra/protocol/responses/prepared_result_response.rb +64 -0
  80. data/lib/cassandra/protocol/responses/raw_rows_result_response.rb +43 -0
  81. data/lib/cassandra/protocol/responses/ready_response.rb +44 -0
  82. data/lib/cassandra/protocol/responses/result_response.rb +48 -0
  83. data/lib/cassandra/protocol/responses/rows_result_response.rb +139 -0
  84. data/lib/cassandra/protocol/responses/schema_change_event_response.rb +60 -0
  85. data/lib/cassandra/protocol/responses/schema_change_result_response.rb +57 -0
  86. data/lib/cassandra/protocol/responses/set_keyspace_result_response.rb +42 -0
  87. data/lib/cassandra/protocol/responses/status_change_event_response.rb +44 -0
  88. data/lib/cassandra/protocol/responses/supported_response.rb +41 -0
  89. data/lib/cassandra/protocol/responses/topology_change_event_response.rb +34 -0
  90. data/lib/cassandra/protocol/responses/void_result_response.rb +39 -0
  91. data/lib/cassandra/protocol/type_converter.rb +384 -0
  92. data/lib/cassandra/protocol.rb +93 -0
  93. data/lib/cassandra/reconnection/policies/constant.rb +48 -0
  94. data/lib/cassandra/reconnection/policies/exponential.rb +79 -0
  95. data/lib/cassandra/reconnection/policies.rb +20 -0
  96. data/lib/cassandra/reconnection.rb +49 -0
  97. data/lib/cassandra/result.rb +215 -0
  98. data/lib/cassandra/retry/policies/default.rb +47 -0
  99. data/lib/cassandra/retry/policies/downgrading_consistency.rb +71 -0
  100. data/lib/cassandra/retry/policies/fallthrough.rb +39 -0
  101. data/lib/cassandra/retry/policies.rb +21 -0
  102. data/lib/cassandra/retry.rb +142 -0
  103. data/lib/cassandra/session.rb +202 -0
  104. data/lib/cassandra/statement.rb +22 -0
  105. data/lib/cassandra/statements/batch.rb +95 -0
  106. data/lib/cassandra/statements/bound.rb +48 -0
  107. data/lib/cassandra/statements/prepared.rb +81 -0
  108. data/lib/cassandra/statements/simple.rb +58 -0
  109. data/lib/cassandra/statements/void.rb +33 -0
  110. data/lib/cassandra/statements.rb +23 -0
  111. data/lib/cassandra/table.rb +299 -0
  112. data/lib/cassandra/time_uuid.rb +142 -0
  113. data/lib/cassandra/util.rb +167 -0
  114. data/lib/cassandra/uuid.rb +104 -0
  115. data/lib/cassandra/version.rb +21 -0
  116. data/lib/cassandra.rb +428 -0
  117. data/lib/cassandra_murmur3.jar +0 -0
  118. 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