cql-rb 1.2.2 → 2.0.0.pre0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (108) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +4 -0
  3. data/README.md +139 -17
  4. data/lib/cql/client.rb +237 -8
  5. data/lib/cql/client/asynchronous_client.rb +138 -54
  6. data/lib/cql/client/asynchronous_prepared_statement.rb +41 -6
  7. data/lib/cql/client/authenticators.rb +46 -0
  8. data/lib/cql/client/batch.rb +115 -0
  9. data/lib/cql/client/connector.rb +255 -0
  10. data/lib/cql/client/execute_options_decoder.rb +25 -9
  11. data/lib/cql/client/keyspace_changer.rb +5 -5
  12. data/lib/cql/client/peer_discovery.rb +33 -0
  13. data/lib/cql/client/query_result.rb +124 -1
  14. data/lib/cql/client/request_runner.rb +4 -2
  15. data/lib/cql/client/synchronous_client.rb +14 -2
  16. data/lib/cql/client/synchronous_prepared_statement.rb +19 -1
  17. data/lib/cql/future.rb +97 -50
  18. data/lib/cql/io/connection.rb +0 -1
  19. data/lib/cql/io/io_reactor.rb +1 -1
  20. data/lib/cql/protocol.rb +8 -1
  21. data/lib/cql/protocol/cql_protocol_handler.rb +2 -2
  22. data/lib/cql/protocol/decoding.rb +10 -15
  23. data/lib/cql/protocol/frame_decoder.rb +2 -1
  24. data/lib/cql/protocol/frame_encoder.rb +5 -4
  25. data/lib/cql/protocol/requests/auth_response_request.rb +31 -0
  26. data/lib/cql/protocol/requests/batch_request.rb +59 -0
  27. data/lib/cql/protocol/requests/credentials_request.rb +1 -1
  28. data/lib/cql/protocol/requests/execute_request.rb +45 -17
  29. data/lib/cql/protocol/requests/options_request.rb +1 -1
  30. data/lib/cql/protocol/requests/prepare_request.rb +1 -1
  31. data/lib/cql/protocol/requests/query_request.rb +97 -5
  32. data/lib/cql/protocol/requests/register_request.rb +1 -1
  33. data/lib/cql/protocol/requests/startup_request.rb +4 -4
  34. data/lib/cql/protocol/response.rb +2 -2
  35. data/lib/cql/protocol/responses/auth_challenge_response.rb +25 -0
  36. data/lib/cql/protocol/responses/auth_success_response.rb +25 -0
  37. data/lib/cql/protocol/responses/authenticate_response.rb +1 -1
  38. data/lib/cql/protocol/responses/detailed_error_response.rb +1 -1
  39. data/lib/cql/protocol/responses/error_response.rb +3 -2
  40. data/lib/cql/protocol/responses/event_response.rb +3 -2
  41. data/lib/cql/protocol/responses/prepared_result_response.rb +10 -6
  42. data/lib/cql/protocol/responses/raw_rows_result_response.rb +27 -0
  43. data/lib/cql/protocol/responses/ready_response.rb +1 -1
  44. data/lib/cql/protocol/responses/result_response.rb +2 -2
  45. data/lib/cql/protocol/responses/rows_result_response.rb +43 -23
  46. data/lib/cql/protocol/responses/schema_change_event_response.rb +1 -1
  47. data/lib/cql/protocol/responses/schema_change_result_response.rb +1 -1
  48. data/lib/cql/protocol/responses/set_keyspace_result_response.rb +1 -1
  49. data/lib/cql/protocol/responses/status_change_event_response.rb +1 -1
  50. data/lib/cql/protocol/responses/supported_response.rb +1 -1
  51. data/lib/cql/protocol/responses/void_result_response.rb +1 -1
  52. data/lib/cql/protocol/type_converter.rb +2 -2
  53. data/lib/cql/uuid.rb +2 -2
  54. data/lib/cql/version.rb +1 -1
  55. data/spec/cql/client/asynchronous_client_spec.rb +493 -50
  56. data/spec/cql/client/asynchronous_prepared_statement_spec.rb +193 -11
  57. data/spec/cql/client/authenticators_spec.rb +56 -0
  58. data/spec/cql/client/batch_spec.rb +277 -0
  59. data/spec/cql/client/connector_spec.rb +606 -0
  60. data/spec/cql/client/execute_options_decoder_spec.rb +95 -0
  61. data/spec/cql/client/keyspace_changer_spec.rb +8 -8
  62. data/spec/cql/client/peer_discovery_spec.rb +92 -0
  63. data/spec/cql/client/query_result_spec.rb +352 -0
  64. data/spec/cql/client/request_runner_spec.rb +31 -5
  65. data/spec/cql/client/synchronous_client_spec.rb +44 -1
  66. data/spec/cql/client/synchronous_prepared_statement_spec.rb +63 -1
  67. data/spec/cql/future_spec.rb +50 -2
  68. data/spec/cql/protocol/cql_protocol_handler_spec.rb +16 -5
  69. data/spec/cql/protocol/decoding_spec.rb +16 -6
  70. data/spec/cql/protocol/encoding_spec.rb +3 -1
  71. data/spec/cql/protocol/frame_encoder_spec.rb +99 -50
  72. data/spec/cql/protocol/requests/auth_response_request_spec.rb +62 -0
  73. data/spec/cql/protocol/requests/batch_request_spec.rb +155 -0
  74. data/spec/cql/protocol/requests/credentials_request_spec.rb +1 -1
  75. data/spec/cql/protocol/requests/execute_request_spec.rb +184 -71
  76. data/spec/cql/protocol/requests/options_request_spec.rb +1 -1
  77. data/spec/cql/protocol/requests/prepare_request_spec.rb +1 -1
  78. data/spec/cql/protocol/requests/query_request_spec.rb +255 -32
  79. data/spec/cql/protocol/requests/register_request_spec.rb +1 -1
  80. data/spec/cql/protocol/requests/startup_request_spec.rb +12 -6
  81. data/spec/cql/protocol/responses/auth_challenge_response_spec.rb +31 -0
  82. data/spec/cql/protocol/responses/auth_success_response_spec.rb +31 -0
  83. data/spec/cql/protocol/responses/authenticate_response_spec.rb +2 -1
  84. data/spec/cql/protocol/responses/detailed_error_response_spec.rb +14 -7
  85. data/spec/cql/protocol/responses/error_response_spec.rb +4 -2
  86. data/spec/cql/protocol/responses/event_response_spec.rb +7 -4
  87. data/spec/cql/protocol/responses/prepared_result_response_spec.rb +89 -34
  88. data/spec/cql/protocol/responses/raw_rows_result_response_spec.rb +66 -0
  89. data/spec/cql/protocol/responses/ready_response_spec.rb +1 -1
  90. data/spec/cql/protocol/responses/result_response_spec.rb +19 -7
  91. data/spec/cql/protocol/responses/rows_result_response_spec.rb +56 -11
  92. data/spec/cql/protocol/responses/schema_change_event_response_spec.rb +2 -1
  93. data/spec/cql/protocol/responses/schema_change_result_response_spec.rb +2 -1
  94. data/spec/cql/protocol/responses/set_keyspace_result_response_spec.rb +1 -1
  95. data/spec/cql/protocol/responses/status_change_event_response_spec.rb +2 -1
  96. data/spec/cql/protocol/responses/supported_response_spec.rb +2 -1
  97. data/spec/cql/protocol/responses/topology_change_event_response_spec.rb +2 -1
  98. data/spec/cql/protocol/responses/void_result_response_spec.rb +1 -1
  99. data/spec/cql/protocol/type_converter_spec.rb +21 -4
  100. data/spec/cql/uuid_spec.rb +10 -3
  101. data/spec/integration/client_spec.rb +251 -28
  102. data/spec/integration/protocol_spec.rb +213 -62
  103. data/spec/integration/regression_spec.rb +4 -1
  104. data/spec/integration/uuid_spec.rb +4 -1
  105. data/spec/support/fake_io_reactor.rb +5 -5
  106. metadata +36 -7
  107. data/lib/cql/client/connection_helper.rb +0 -181
  108. data/spec/cql/client/connection_helper_spec.rb +0 -429
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cql-rb
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.2
4
+ version: 2.0.0.pre0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Theo Hultberg
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-05-17 00:00:00.000000000 Z
11
+ date: 2014-02-13 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: A pure Ruby CQL3 driver for Cassandra
14
14
  email:
@@ -17,18 +17,22 @@ executables: []
17
17
  extensions: []
18
18
  extra_rdoc_files: []
19
19
  files:
20
+ - .yardopts
20
21
  - README.md
21
22
  - lib/cql.rb
22
23
  - lib/cql/byte_buffer.rb
23
24
  - lib/cql/client.rb
24
25
  - lib/cql/client/asynchronous_client.rb
25
26
  - lib/cql/client/asynchronous_prepared_statement.rb
27
+ - lib/cql/client/authenticators.rb
28
+ - lib/cql/client/batch.rb
26
29
  - lib/cql/client/column_metadata.rb
27
- - lib/cql/client/connection_helper.rb
28
30
  - lib/cql/client/connection_manager.rb
31
+ - lib/cql/client/connector.rb
29
32
  - lib/cql/client/execute_options_decoder.rb
30
33
  - lib/cql/client/keyspace_changer.rb
31
34
  - lib/cql/client/null_logger.rb
35
+ - lib/cql/client/peer_discovery.rb
32
36
  - lib/cql/client/query_result.rb
33
37
  - lib/cql/client/query_trace.rb
34
38
  - lib/cql/client/request_runner.rb
@@ -49,6 +53,8 @@ files:
49
53
  - lib/cql/protocol/frame_decoder.rb
50
54
  - lib/cql/protocol/frame_encoder.rb
51
55
  - lib/cql/protocol/request.rb
56
+ - lib/cql/protocol/requests/auth_response_request.rb
57
+ - lib/cql/protocol/requests/batch_request.rb
52
58
  - lib/cql/protocol/requests/credentials_request.rb
53
59
  - lib/cql/protocol/requests/execute_request.rb
54
60
  - lib/cql/protocol/requests/options_request.rb
@@ -57,11 +63,14 @@ files:
57
63
  - lib/cql/protocol/requests/register_request.rb
58
64
  - lib/cql/protocol/requests/startup_request.rb
59
65
  - lib/cql/protocol/response.rb
66
+ - lib/cql/protocol/responses/auth_challenge_response.rb
67
+ - lib/cql/protocol/responses/auth_success_response.rb
60
68
  - lib/cql/protocol/responses/authenticate_response.rb
61
69
  - lib/cql/protocol/responses/detailed_error_response.rb
62
70
  - lib/cql/protocol/responses/error_response.rb
63
71
  - lib/cql/protocol/responses/event_response.rb
64
72
  - lib/cql/protocol/responses/prepared_result_response.rb
73
+ - lib/cql/protocol/responses/raw_rows_result_response.rb
65
74
  - lib/cql/protocol/responses/ready_response.rb
66
75
  - lib/cql/protocol/responses/result_response.rb
67
76
  - lib/cql/protocol/responses/rows_result_response.rb
@@ -79,10 +88,15 @@ files:
79
88
  - spec/cql/byte_buffer_spec.rb
80
89
  - spec/cql/client/asynchronous_client_spec.rb
81
90
  - spec/cql/client/asynchronous_prepared_statement_spec.rb
91
+ - spec/cql/client/authenticators_spec.rb
92
+ - spec/cql/client/batch_spec.rb
82
93
  - spec/cql/client/column_metadata_spec.rb
83
- - spec/cql/client/connection_helper_spec.rb
84
94
  - spec/cql/client/connection_manager_spec.rb
95
+ - spec/cql/client/connector_spec.rb
96
+ - spec/cql/client/execute_options_decoder_spec.rb
85
97
  - spec/cql/client/keyspace_changer_spec.rb
98
+ - spec/cql/client/peer_discovery_spec.rb
99
+ - spec/cql/client/query_result_spec.rb
86
100
  - spec/cql/client/query_trace_spec.rb
87
101
  - spec/cql/client/request_runner_spec.rb
88
102
  - spec/cql/client/synchronous_client_spec.rb
@@ -98,6 +112,8 @@ files:
98
112
  - spec/cql/protocol/encoding_spec.rb
99
113
  - spec/cql/protocol/frame_decoder_spec.rb
100
114
  - spec/cql/protocol/frame_encoder_spec.rb
115
+ - spec/cql/protocol/requests/auth_response_request_spec.rb
116
+ - spec/cql/protocol/requests/batch_request_spec.rb
101
117
  - spec/cql/protocol/requests/credentials_request_spec.rb
102
118
  - spec/cql/protocol/requests/execute_request_spec.rb
103
119
  - spec/cql/protocol/requests/options_request_spec.rb
@@ -105,11 +121,14 @@ files:
105
121
  - spec/cql/protocol/requests/query_request_spec.rb
106
122
  - spec/cql/protocol/requests/register_request_spec.rb
107
123
  - spec/cql/protocol/requests/startup_request_spec.rb
124
+ - spec/cql/protocol/responses/auth_challenge_response_spec.rb
125
+ - spec/cql/protocol/responses/auth_success_response_spec.rb
108
126
  - spec/cql/protocol/responses/authenticate_response_spec.rb
109
127
  - spec/cql/protocol/responses/detailed_error_response_spec.rb
110
128
  - spec/cql/protocol/responses/error_response_spec.rb
111
129
  - spec/cql/protocol/responses/event_response_spec.rb
112
130
  - spec/cql/protocol/responses/prepared_result_response_spec.rb
131
+ - spec/cql/protocol/responses/raw_rows_result_response_spec.rb
113
132
  - spec/cql/protocol/responses/ready_response_spec.rb
114
133
  - spec/cql/protocol/responses/result_response_spec.rb
115
134
  - spec/cql/protocol/responses/rows_result_response_spec.rb
@@ -148,9 +167,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
148
167
  version: 1.9.2
149
168
  required_rubygems_version: !ruby/object:Gem::Requirement
150
169
  requirements:
151
- - - '>='
170
+ - - '>'
152
171
  - !ruby/object:Gem::Version
153
- version: '0'
172
+ version: 1.3.1
154
173
  requirements: []
155
174
  rubyforge_project:
156
175
  rubygems_version: 2.2.1
@@ -161,10 +180,15 @@ test_files:
161
180
  - spec/cql/byte_buffer_spec.rb
162
181
  - spec/cql/client/asynchronous_client_spec.rb
163
182
  - spec/cql/client/asynchronous_prepared_statement_spec.rb
183
+ - spec/cql/client/authenticators_spec.rb
184
+ - spec/cql/client/batch_spec.rb
164
185
  - spec/cql/client/column_metadata_spec.rb
165
- - spec/cql/client/connection_helper_spec.rb
166
186
  - spec/cql/client/connection_manager_spec.rb
187
+ - spec/cql/client/connector_spec.rb
188
+ - spec/cql/client/execute_options_decoder_spec.rb
167
189
  - spec/cql/client/keyspace_changer_spec.rb
190
+ - spec/cql/client/peer_discovery_spec.rb
191
+ - spec/cql/client/query_result_spec.rb
168
192
  - spec/cql/client/query_trace_spec.rb
169
193
  - spec/cql/client/request_runner_spec.rb
170
194
  - spec/cql/client/synchronous_client_spec.rb
@@ -180,6 +204,8 @@ test_files:
180
204
  - spec/cql/protocol/encoding_spec.rb
181
205
  - spec/cql/protocol/frame_decoder_spec.rb
182
206
  - spec/cql/protocol/frame_encoder_spec.rb
207
+ - spec/cql/protocol/requests/auth_response_request_spec.rb
208
+ - spec/cql/protocol/requests/batch_request_spec.rb
183
209
  - spec/cql/protocol/requests/credentials_request_spec.rb
184
210
  - spec/cql/protocol/requests/execute_request_spec.rb
185
211
  - spec/cql/protocol/requests/options_request_spec.rb
@@ -187,11 +213,14 @@ test_files:
187
213
  - spec/cql/protocol/requests/query_request_spec.rb
188
214
  - spec/cql/protocol/requests/register_request_spec.rb
189
215
  - spec/cql/protocol/requests/startup_request_spec.rb
216
+ - spec/cql/protocol/responses/auth_challenge_response_spec.rb
217
+ - spec/cql/protocol/responses/auth_success_response_spec.rb
190
218
  - spec/cql/protocol/responses/authenticate_response_spec.rb
191
219
  - spec/cql/protocol/responses/detailed_error_response_spec.rb
192
220
  - spec/cql/protocol/responses/error_response_spec.rb
193
221
  - spec/cql/protocol/responses/event_response_spec.rb
194
222
  - spec/cql/protocol/responses/prepared_result_response_spec.rb
223
+ - spec/cql/protocol/responses/raw_rows_result_response_spec.rb
195
224
  - spec/cql/protocol/responses/ready_response_spec.rb
196
225
  - spec/cql/protocol/responses/result_response_spec.rb
197
226
  - spec/cql/protocol/responses/rows_result_response_spec.rb
@@ -1,181 +0,0 @@
1
- # encoding: utf-8
2
-
3
- module Cql
4
- module Client
5
- # @private
6
- class ConnectionHelper
7
- def initialize(io_reactor, port, credentials, connections_per_node, connection_timeout, compressor, logger)
8
- @io_reactor = io_reactor
9
- @port = port
10
- @credentials = credentials
11
- @connections_per_node = connections_per_node
12
- @connection_timeout = connection_timeout
13
- @logger = logger
14
- @request_runner = RequestRunner.new
15
- @keyspace_changer = KeyspaceChanger.new
16
- @compressor = compressor
17
- end
18
-
19
- def connect(hosts, initial_keyspace)
20
- f = @io_reactor.start.flat_map do
21
- connect_to_hosts(hosts, initial_keyspace, true)
22
- end
23
- f = f.map do |connections|
24
- connected_connections = connections.select(&:connected?)
25
- if connected_connections.empty?
26
- e = connections.first.error
27
- if e.is_a?(Cql::QueryError) && e.code == 0x100
28
- e = AuthenticationError.new(e.message)
29
- end
30
- raise e
31
- end
32
- connected_connections
33
- end
34
- f
35
- end
36
-
37
- def discover_peers(seed_connections, initial_keyspace)
38
- @logger.debug('Looking for additional nodes')
39
- connection = seed_connections.sample
40
- return Future.resolved([]) unless connection
41
- request = Protocol::QueryRequest.new('SELECT peer, data_center, host_id, rpc_address FROM system.peers', :one)
42
- peer_info = @request_runner.execute(connection, request)
43
- peer_info.flat_map do |result|
44
- seed_dcs = seed_connections.map { |c| c[:data_center] }.uniq
45
- unconnected_peers = result.select do |row|
46
- seed_dcs.include?(row['data_center']) && seed_connections.none? { |c| c[:host_id] == row['host_id'] }
47
- end
48
- if unconnected_peers.empty?
49
- @logger.debug('No additional nodes found')
50
- else
51
- @logger.debug('%d additional nodes found' % unconnected_peers.size)
52
- end
53
- node_addresses = unconnected_peers.map do |row|
54
- rpc_address = row['rpc_address'].to_s
55
- if rpc_address == '0.0.0.0'
56
- row['peer'].to_s
57
- else
58
- rpc_address
59
- end
60
- end
61
- if node_addresses.any?
62
- connect_to_hosts(node_addresses, initial_keyspace, false)
63
- else
64
- Future.resolved([])
65
- end
66
- end
67
- end
68
-
69
- private
70
-
71
- def connect_to_hosts(hosts, initial_keyspace, peer_discovery)
72
- connection_futures = hosts.flat_map do |host|
73
- Array.new(@connections_per_node) do
74
- connect_to_host(host, initial_keyspace).recover do |error|
75
- FailedConnection.new(error, host, @port)
76
- end
77
- end
78
- end
79
- connection_futures.each do |cf|
80
- cf.on_value do |c|
81
- if c.is_a?(FailedConnection)
82
- @logger.warn('Failed connecting to node at %s:%d: %s' % [c.host, c.port, c.error.message])
83
- else
84
- @logger.info('Connected to node %s at %s:%d in data center %s' % [c[:host_id], c.host, c.port, c[:data_center]])
85
- end
86
- c.on_closed do
87
- @logger.warn('Connection to node %s at %s:%d in data center %s unexpectedly closed' % [c[:host_id], c.host, c.port, c[:data_center]])
88
- end
89
- end
90
- end
91
- hosts_connected_future = Future.all(*connection_futures)
92
- if peer_discovery
93
- hosts_connected_future.flat_map do |connections|
94
- discover_peers(connections.select(&:connected?), initial_keyspace).map do |peer_connections|
95
- connections + peer_connections
96
- end
97
- end
98
- else
99
- hosts_connected_future
100
- end
101
- end
102
-
103
- def connect_to_host(host, keyspace)
104
- if @compressor
105
- @logger.debug('Connecting to node at %s:%d using "%s" compression' % [host, @port, @compressor.algorithm])
106
- else
107
- @logger.debug('Connecting to node at %s:%d' % [host, @port])
108
- end
109
- connected = @io_reactor.connect(host, @port, @connection_timeout)
110
- connected.flat_map do |connection|
111
- f = cache_supported_options(connection)
112
- f = f.flat_map { send_startup_request(connection) }
113
- f = f.flat_map { |response| maybe_authenticate(response, connection) }
114
- f = f.flat_map { identify_node(connection) }
115
- f = f.flat_map { change_keyspace(keyspace, connection) }
116
- f
117
- end
118
- end
119
-
120
- def cache_supported_options(connection)
121
- f = @request_runner.execute(connection, Protocol::OptionsRequest.new)
122
- f.on_value do |supported_options|
123
- connection[:cql_version] = supported_options['CQL_VERSION']
124
- connection[:compression] = supported_options['COMPRESSION']
125
- end
126
- f
127
- end
128
-
129
- def send_startup_request(connection)
130
- compression = @compressor && @compressor.algorithm
131
- if @compressor && !connection[:compression].include?(@compressor.algorithm)
132
- @logger.warn(%[Compression algorithm "#{@compressor.algorithm}" not supported (server supports "#{connection[:compression].join('", "')}")])
133
- compression = nil
134
- end
135
- request = Protocol::StartupRequest.new(nil, compression)
136
- @request_runner.execute(connection, request)
137
- end
138
-
139
- def maybe_authenticate(response, connection)
140
- return Future.resolved(connection) unless response.is_a?(AuthenticationRequired)
141
- return Future.failed(AuthenticationError.new('Server requested authentication, but no credentials given')) unless @credentials
142
- send_credentials(@credentials, connection)
143
- end
144
-
145
- def send_credentials(credentials, connection)
146
- credentials_request = Protocol::CredentialsRequest.new(credentials)
147
- @request_runner.execute(connection, credentials_request)
148
- end
149
-
150
- def identify_node(connection)
151
- request = Protocol::QueryRequest.new('SELECT data_center, host_id FROM system.local', :one)
152
- f = @request_runner.execute(connection, request)
153
- f.on_value do |result|
154
- unless result.empty?
155
- connection[:host_id] = result.first['host_id']
156
- connection[:data_center] = result.first['data_center']
157
- end
158
- end
159
- f
160
- end
161
-
162
- def change_keyspace(keyspace, connection)
163
- @keyspace_changer.use_keyspace(keyspace, connection)
164
- end
165
-
166
- class FailedConnection
167
- attr_reader :error, :host, :port
168
-
169
- def initialize(error, host, port)
170
- @error = error
171
- @host = host
172
- @port = port
173
- end
174
-
175
- def connected?
176
- false
177
- end
178
- end
179
- end
180
- end
181
- end
@@ -1,429 +0,0 @@
1
- # encoding: utf-8
2
-
3
- require 'spec_helper'
4
-
5
-
6
- module Cql
7
- module Client
8
- describe ConnectionHelper do
9
- let :connection_helper do
10
- described_class.new(io_reactor, 9876, nil, 1, 7, compressor, logger)
11
- end
12
-
13
- let :io_reactor do
14
- double(:io_reactor)
15
- end
16
-
17
- let :logger do
18
- NullLogger.new
19
- end
20
-
21
- let :compressor do
22
- nil
23
- end
24
-
25
- describe '#connect' do
26
- let :hosts do
27
- %w[host0 host1]
28
- end
29
-
30
- let :local_metadata do
31
- [
32
- ['system', 'local', 'data_center', :text],
33
- ['system', 'local', 'host_id', :uuid],
34
- ]
35
- end
36
-
37
- before do
38
- io_reactor.stub(:start).and_return(Future.resolved)
39
- io_reactor.stub(:connect).and_return(Future.resolved)
40
- end
41
-
42
- it 'starts the IO reactor' do
43
- connection_helper.connect(hosts, nil)
44
- io_reactor.should have_received(:start)
45
- end
46
-
47
- it 'fails when the IO reactor fails to start' do
48
- io_reactor.stub(:start).and_return(Future.failed(StandardError.new('bork')))
49
- f = connection_helper.connect(hosts, nil)
50
- expect { f.value }.to raise_error('bork')
51
- end
52
-
53
- it 'connects to the specified hosts' do
54
- connection_helper.connect(hosts, nil)
55
- io_reactor.should have_received(:connect).with('host0', 9876, 7)
56
- io_reactor.should have_received(:connect).with('host1', 9876, 7)
57
- end
58
-
59
- it 'logs a message when a node connects' do
60
- logger.stub(:info)
61
- io_reactor.stub(:connect).and_return(Future.resolved(FakeConnection.new('host', 9876, 7)))
62
- connection_helper.connect(hosts, nil)
63
- logger.should have_received(:info).with(/Connected to node/).exactly(hosts.size).times
64
- end
65
-
66
- it 'logs a message when connecting to a node' do
67
- logger.stub(:debug)
68
- io_reactor.stub(:connect).and_return(Future.resolved(FakeConnection.new('host', 9876, 7)))
69
- connection_helper.connect(hosts, nil)
70
- logger.should have_received(:debug).with(/Connecting to node/).exactly(hosts.size).times
71
- end
72
-
73
- it 'fails when all hosts fail to connect' do
74
- io_reactor.stub(:connect).and_return(Future.failed(StandardError.new('bork')))
75
- f = connection_helper.connect(hosts, nil)
76
- expect { f.value }.to raise_error('bork')
77
- end
78
-
79
- it 'logs a message when a node fails to connect' do
80
- logger.stub(:warn)
81
- io_reactor.stub(:connect).and_return(Future.failed(StandardError.new('bork')))
82
- connection_helper.connect(hosts, nil)
83
- logger.should have_received(:warn).with(/Failed connecting to node/).exactly(hosts.size).times
84
- end
85
-
86
- it 'fails with an AuthenticationError when the connections fail to connect because of authentication issues' do
87
- io_reactor.stub(:connect).and_return(Future.failed(QueryError.new(0x100, 'bork')))
88
- f = connection_helper.connect(hosts, nil)
89
- expect { f.value }.to raise_error(AuthenticationError)
90
- end
91
-
92
- it 'initializes the connections' do
93
- connection0 = FakeConnection.new('host0', 9876, 7)
94
- connection1 = FakeConnection.new('host1', 9876, 7)
95
- io_reactor.stub(:connect).with('host0', 9876, 7).and_return(Future.resolved(connection0))
96
- io_reactor.stub(:connect).with('host1', 9876, 7).and_return(Future.resolved(connection1))
97
- connection_helper.connect(hosts, 'some_keyspace')
98
- [connection0, connection1].each do |c|
99
- c.requests[0].should be_a(Protocol::OptionsRequest)
100
- c.requests[1].should be_a(Protocol::StartupRequest)
101
- c.requests[2].cql.should match(/SELECT .* FROM system.local/)
102
- c.requests[3].cql.should == 'USE some_keyspace'
103
- end
104
- end
105
-
106
- it 'saves the supported CQL version and compression algorithms on the connection' do
107
- connection = FakeConnection.new('host0', 9876, 7)
108
- connection.handle_request do |request, timeout|
109
- if request.is_a?(Protocol::OptionsRequest)
110
- Protocol::SupportedResponse.new('CQL_VERSION' => %w[3.1.1], 'COMPRESSION' => %w[lz4 snappy])
111
- else
112
- connection.default_request_handler(request, timeout)
113
- end
114
- end
115
- io_reactor.stub(:connect).with('host0', 9876, 7).and_return(Future.resolved(connection))
116
- connection_helper.connect(hosts.take(1), 'some_keyspace')
117
- connection[:cql_version].should == %w[3.1.1]
118
- connection[:compression].should == %w[lz4 snappy]
119
- end
120
-
121
- it 'fails if authentication is required and no credentials were specified' do
122
- connection = FakeConnection.new('host0', 9876, 7)
123
- connection.handle_request do |request|
124
- if request.is_a?(Protocol::StartupRequest)
125
- Protocol::AuthenticateResponse.new('xyz')
126
- else
127
- connection.default_request_handler(request)
128
- end
129
- end
130
- io_reactor.stub(:connect).with('host0', 9876, 7).and_return(Future.resolved(connection))
131
- f = connection_helper.connect(hosts, nil)
132
- expect { f.value }.to raise_error(AuthenticationError)
133
- end
134
-
135
- it 'authenticates when authentication is required and credentials were specified' do
136
- credentials = {'username' => 'foo', 'password' => 'bar'}
137
- connection_helper = described_class.new(io_reactor, 9876, credentials, 1, 7, nil, logger)
138
- connection = FakeConnection.new('host0', 9876, 7)
139
- authentication_sent = false
140
- connection.handle_request do |request|
141
- if request.is_a?(Protocol::StartupRequest)
142
- Protocol::AuthenticateResponse.new('xyz')
143
- elsif request == Protocol::CredentialsRequest.new(credentials)
144
- authentication_sent = true
145
- Protocol::ReadyResponse.new
146
- else
147
- connection.default_request_handler(request)
148
- end
149
- end
150
- io_reactor.stub(:connect).with('host0', 9876, 7).and_return(Future.resolved(connection))
151
- f = connection_helper.connect(hosts, nil)
152
- f.value
153
- authentication_sent.should be_true
154
- end
155
-
156
- it 'decorates the connections with :host_id and :data_center' do
157
- connection = FakeConnection.new('host0', 9876, 7)
158
- connection.handle_request do |request|
159
- if request.is_a?(Protocol::QueryRequest) && request.cql =~ /SELECT .* FROM system\.local/
160
- row = {'data_center' => 'dc1', 'host_id' => Uuid.new('eac69196-1e28-11e3-8e2b-191b6d153d0c')}
161
- Protocol::RowsResultResponse.new([row], local_metadata, nil)
162
- else
163
- connection.default_request_handler(request)
164
- end
165
- end
166
- io_reactor.stub(:connect).with('host0', 9876, 7).and_return(Future.resolved(connection))
167
- connection_helper.connect(hosts, nil)
168
- connection[:host_id].should == Uuid.new('eac69196-1e28-11e3-8e2b-191b6d153d0c')
169
- connection[:data_center].should == 'dc1'
170
- end
171
-
172
- it 'registers a close handler that logs when connections close' do
173
- logger.stub(:warn)
174
- connection = FakeConnection.new('host', 9876, 7)
175
- io_reactor.stub(:connect).and_return(Future.resolved(connection))
176
- connection_helper.connect(hosts.take(1), nil)
177
- connection.close
178
- logger.should have_received(:warn).with(/Connection to node .* closed/)
179
- end
180
-
181
- it 'initializes a peer discovery when connected to the specified hosts' do
182
- connection_helper.stub(:discover_peers)
183
- connection_helper.connect(hosts, nil)
184
- connection0 = FakeConnection.new('host0', 9876, 7)
185
- connection1 = FakeConnection.new('host1', 9876, 7)
186
- io_reactor.stub(:connect).with('host0', 9876, 7).and_return(Future.resolved(connection0))
187
- io_reactor.stub(:connect).with('host1', 9876, 7).and_return(Future.resolved(connection1))
188
- connection_helper.connect(hosts, 'some_keyspace')
189
- connection_helper.should have_received(:discover_peers).with([connection0, connection1], 'some_keyspace')
190
- end
191
-
192
- it 'initializes a peer discovery with the successfull connections as seeds' do
193
- connection_helper.stub(:discover_peers)
194
- connection_helper.connect(hosts, nil)
195
- connection = FakeConnection.new('host0', 9876, 7)
196
- io_reactor.stub(:connect).with('host0', 9876, 7).and_return(Future.resolved(connection))
197
- io_reactor.stub(:connect).with('host1', 9876, 7).and_return(Future.failed(StandardError.new('bork')))
198
- connection_helper.connect(hosts, 'some_keyspace')
199
- connection_helper.should have_received(:discover_peers).with([connection], 'some_keyspace')
200
- end
201
-
202
- it 'connects to each node a configurable number of times' do
203
- connection_helper = described_class.new(io_reactor, 9876, nil, connections_per_node = 3, 7, nil, logger)
204
- connection_helper.connect(hosts, nil)
205
- io_reactor.should have_received(:connect).with('host0', 9876, 7).exactly(3).times
206
- io_reactor.should have_received(:connect).with('host1', 9876, 7).exactly(3).times
207
- end
208
-
209
- context 'when a compressor is specified' do
210
- let :compressor do
211
- double(:compressor, algorithm: 'snappy')
212
- end
213
-
214
- let :connection do
215
- FakeConnection.new('host0', 9876, 7)
216
- end
217
-
218
- it 'enables compression by sending the algorithm with the STARTUP request' do
219
- connection.handle_request do |request, timeout|
220
- if request.is_a?(Protocol::OptionsRequest)
221
- Protocol::SupportedResponse.new('CQL_VERSION' => %w[3.1.1], 'COMPRESSION' => %w[lz4 snappy])
222
- else
223
- connection.default_request_handler(request, timeout)
224
- end
225
- end
226
- io_reactor.stub(:connect).with('host0', 9876, 7).and_return(Future.resolved(connection))
227
- connection_helper.connect(hosts.take(1), 'some_keyspace')
228
- connection.requests[1].options['COMPRESSION'].should == 'snappy'
229
- end
230
-
231
- it 'does not enable compression when the algorithm is not supported' do
232
- connection.handle_request do |request, timeout|
233
- if request.is_a?(Protocol::OptionsRequest)
234
- Protocol::SupportedResponse.new('CQL_VERSION' => %w[3.1.1], 'COMPRESSION' => %w[lz4])
235
- else
236
- connection.default_request_handler(request, timeout)
237
- end
238
- end
239
- io_reactor.stub(:connect).with('host0', 9876, 7).and_return(Future.resolved(connection))
240
- connection_helper.connect(hosts.take(1), 'some_keyspace')
241
- connection.requests[1].options.should_not have_key('COMPRESSION')
242
- end
243
-
244
- it 'logs a warning when compression was disabled because the algorithm was not supported' do
245
- logger.stub(:warn)
246
- connection.handle_request do |request, timeout|
247
- if request.is_a?(Protocol::OptionsRequest)
248
- Protocol::SupportedResponse.new('CQL_VERSION' => %w[3.1.1], 'COMPRESSION' => %w[lz4])
249
- else
250
- connection.default_request_handler(request, timeout)
251
- end
252
- end
253
- io_reactor.stub(:connect).with('host0', 9876, 7).and_return(Future.resolved(connection))
254
- connection_helper.connect(hosts.take(1), 'some_keyspace')
255
- logger.should have_received(:warn).with(/not supported/)
256
- end
257
-
258
- it 'logs the name of the compression algorithm when connecting' do
259
- logger.stub(:debug)
260
- io_reactor.stub(:connect).with('host0', 9876, 7).and_return(Future.resolved(connection))
261
- connection_helper.connect(hosts.take(1), 'some_keyspace')
262
- logger.should have_received(:debug).with(/using "snappy" compression/)
263
- end
264
- end
265
- end
266
-
267
- describe '#discover_peers' do
268
- let :seed_connections do
269
- [
270
- FakeConnection.new('host0', 9042, 5),
271
- FakeConnection.new('host1', 9042, 5),
272
- FakeConnection.new('host2', 9042, 5),
273
- ]
274
- end
275
-
276
- let :seed_connection_rows do
277
- [
278
- {'peer' => IPAddr.new('2.0.0.0'), 'rpc_address' => IPAddr.new('1.0.0.0'), 'data_center' => 'dc1', 'host_id' => Uuid.new('eac69196-1e28-11e3-8e2b-191b6d153d0c')},
279
- {'peer' => IPAddr.new('2.0.0.1'), 'rpc_address' => IPAddr.new('1.0.0.1'), 'data_center' => 'dc1', 'host_id' => Uuid.new('fa5f9562-1e28-11e3-bf05-3d3a155d0608')},
280
- {'peer' => IPAddr.new('2.0.0.2'), 'rpc_address' => IPAddr.new('1.0.0.2'), 'data_center' => 'dc1', 'host_id' => Uuid.new('018b8f1c-1e29-11e3-b14f-532d016437ce')},
281
- ]
282
- end
283
-
284
- let :extra_connection_rows do
285
- [
286
- {'peer' => IPAddr.new('2.0.0.3'), 'rpc_address' => IPAddr.new('1.0.0.3'), 'data_center' => 'dc1', 'host_id' => Uuid.new('7a3ccace-1e2a-11e3-a447-43312b1c66e4')},
287
- {'peer' => IPAddr.new('2.0.0.4'), 'rpc_address' => IPAddr.new('1.0.0.4'), 'data_center' => 'dc1', 'host_id' => Uuid.new('7bbd4e32-1e2a-11e3-b21d-69d7c02cece8')},
288
- {'peer' => IPAddr.new('2.0.0.5'), 'rpc_address' => IPAddr.new('1.0.0.5'), 'data_center' => 'dc1', 'host_id' => Uuid.new('7d7e76f6-1e2a-11e3-bfa0-4fb416ef4064')},
289
- ]
290
- end
291
-
292
- let :peer_metadata do
293
- [
294
- ['system', 'peers', 'peer', :inet],
295
- ['system', 'peers', 'data_center', :varchar],
296
- ['system', 'peers', 'host_id', :uuid],
297
- ['system', 'peers', 'rpc_address', :inet],
298
- ]
299
- end
300
-
301
- before do
302
- seed_connections.each_with_index do |c, i|
303
- c[:host_id] = seed_connection_rows[i]['host_id']
304
- c[:data_center] = seed_connection_rows[i]['data_center']
305
- end
306
- end
307
-
308
- def peer_request_response
309
- seed_connections.each do |c|
310
- c.handle_request do |request|
311
- if request.cql =~ /SELECT .* FROM system\.peers/
312
- Protocol::RowsResultResponse.new(yield, peer_metadata, nil)
313
- end
314
- end
315
- end
316
- end
317
-
318
- it 'returns immediately if there are no seed connections' do
319
- f = connection_helper.discover_peers([], nil)
320
- f.value
321
- end
322
-
323
- it 'logs a message when it begins' do
324
- logger.stub(:debug)
325
- connection_helper.discover_peers([], nil)
326
- logger.should have_received(:debug).with(/Looking for additional nodes/)
327
- end
328
-
329
- it 'asks a random connection for its peers' do
330
- connection_helper.discover_peers(seed_connections, nil)
331
- connection = seed_connections.find { |c| c.requests.any? }
332
- connection.requests.first.cql.should match(/SELECT .* FROM system\.peers/)
333
- end
334
-
335
- it 'returns an empty list when it only finds nodes it\'s already connected to' do
336
- peer_request_response { seed_connection_rows }
337
- f = connection_helper.discover_peers(seed_connections, nil)
338
- f.value.should be_empty
339
- end
340
-
341
- it 'logs a message when it finds no new nodes' do
342
- logger.stub(:debug)
343
- peer_request_response { seed_connection_rows }
344
- connection_helper.discover_peers(seed_connections, nil)
345
- logger.should have_received(:debug).with(/No additional nodes found/)
346
- end
347
-
348
- it 'returns an empty list when it only finds nodes data centers other than those of the seed connections' do
349
- seed_connections[1][:data_center] = 'dc2'
350
- seed_connection_rows[1]['data_center'] = 'dc2'
351
- extra_connection_rows[0]['data_center'] = 'dc3'
352
- peer_request_response { seed_connection_rows + extra_connection_rows.take(1) }
353
- f = connection_helper.discover_peers(seed_connections, nil)
354
- f.value.should be_empty
355
- end
356
-
357
- it 'connects to the nodes it finds that it is not already connected to' do
358
- connection = FakeConnection.new('host3', 9876, 7)
359
- io_reactor.stub(:connect).with('1.0.0.3', 9876, 7).and_return(Future.resolved(connection))
360
- peer_request_response { seed_connection_rows + extra_connection_rows.take(1) }
361
- f = connection_helper.discover_peers(seed_connections, nil)
362
- f.value
363
- end
364
-
365
- it 'logs the number of new nodes found' do
366
- logger.stub(:debug)
367
- connection = FakeConnection.new('host3', 9876, 7)
368
- io_reactor.stub(:connect).with('1.0.0.3', 9876, 7).and_return(Future.resolved(connection))
369
- peer_request_response { seed_connection_rows + extra_connection_rows.take(1) }
370
- connection_helper.discover_peers(seed_connections, nil)
371
- logger.should have_received(:debug).with(/1 additional nodes found/)
372
- end
373
-
374
- it 'returns the new connections' do
375
- connection = FakeConnection.new('host3', 9876, 7)
376
- io_reactor.stub(:connect).with('1.0.0.3', 9876, 7).and_return(Future.resolved(connection))
377
- peer_request_response { seed_connection_rows + extra_connection_rows.take(1) }
378
- f = connection_helper.discover_peers(seed_connections, nil)
379
- f.value.should == [connection]
380
- end
381
-
382
- it 'initializes the new connections' do
383
- connection = FakeConnection.new('host3', 9876, 7)
384
- io_reactor.stub(:connect).with('1.0.0.3', 9876, 7).and_return(Future.resolved(connection))
385
- peer_request_response { seed_connection_rows + extra_connection_rows.take(1) }
386
- f = connection_helper.discover_peers(seed_connections, 'some_keyspace')
387
- f.value
388
- connection.requests[0].should be_a(Protocol::OptionsRequest)
389
- connection.requests[1].should be_a(Protocol::StartupRequest)
390
- connection.requests[2].cql.should match(/SELECT .* FROM system.local/)
391
- connection.requests[3].cql.should == 'USE some_keyspace'
392
- end
393
-
394
- it 'connects only to node in the same data centers as the seed nodes' do
395
- seed_connections[1][:data_center] = 'dc2'
396
- seed_connection_rows[1]['data_center'] = 'dc2'
397
- extra_connection_rows[0]['data_center'] = 'dc3'
398
- extra_connection_rows[1]['data_center'] = 'dc2'
399
- extra_connection_rows[2]['data_center'] = 'dc1'
400
- connection4 = FakeConnection.new('host4', 9876, 7)
401
- connection5 = FakeConnection.new('host5', 9876, 7)
402
- io_reactor.stub(:connect).with('1.0.0.4', 9876, 7).and_return(Future.resolved(connection4))
403
- io_reactor.stub(:connect).with('1.0.0.5', 9876, 7).and_return(Future.resolved(connection5))
404
- peer_request_response { seed_connection_rows + extra_connection_rows.take(3) }
405
- f = connection_helper.discover_peers(seed_connections, nil)
406
- f.value.should == [connection4, connection5]
407
- end
408
-
409
- it 'uses the peer address instead of the RPC address when latter is 0.0.0.0' do
410
- extra_connection_rows[0]['rpc_address'] = IPAddr.new('0.0.0.0')
411
- connection = FakeConnection.new('host3', 9876, 7)
412
- io_reactor.stub(:connect).with(extra_connection_rows[0]['peer'].to_s, 9876, 7).and_return(Future.resolved(connection))
413
- peer_request_response { seed_connection_rows + extra_connection_rows.take(1) }
414
- f = connection_helper.discover_peers(seed_connections, nil)
415
- f.value
416
- end
417
-
418
- it 'connects to each node a configurable number of times' do
419
- connection_helper = described_class.new(io_reactor, 9876, nil, connections_per_node = 3, 7, nil, logger)
420
- connection = FakeConnection.new('host3', 9876, 7)
421
- io_reactor.stub(:connect).with('1.0.0.3', 9876, 7).and_return(Future.resolved(connection))
422
- peer_request_response { seed_connection_rows + extra_connection_rows.take(1) }
423
- connection_helper.discover_peers(seed_connections, nil).value
424
- io_reactor.should have_received(:connect).with('1.0.0.3', 9876, 7).exactly(3).times
425
- end
426
- end
427
- end
428
- end
429
- end