cql-rb 1.2.2 → 2.0.0.pre0

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 (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