cql-rb 1.1.0.pre3 → 1.1.0.pre6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. data/README.md +2 -2
  2. data/lib/cql/client.rb +9 -5
  3. data/lib/cql/client/asynchronous_client.rb +105 -192
  4. data/lib/cql/client/asynchronous_prepared_statement.rb +51 -9
  5. data/lib/cql/client/connection_helper.rb +155 -0
  6. data/lib/cql/client/connection_manager.rb +56 -0
  7. data/lib/cql/client/keyspace_changer.rb +27 -0
  8. data/lib/cql/client/null_logger.rb +21 -0
  9. data/lib/cql/client/request_runner.rb +5 -3
  10. data/lib/cql/client/synchronous_client.rb +5 -5
  11. data/lib/cql/client/synchronous_prepared_statement.rb +4 -8
  12. data/lib/cql/future.rb +320 -210
  13. data/lib/cql/io/connection.rb +5 -5
  14. data/lib/cql/io/io_reactor.rb +21 -23
  15. data/lib/cql/protocol/cql_protocol_handler.rb +69 -38
  16. data/lib/cql/protocol/encoding.rb +5 -1
  17. data/lib/cql/protocol/requests/register_request.rb +2 -0
  18. data/lib/cql/protocol/type_converter.rb +1 -0
  19. data/lib/cql/version.rb +1 -1
  20. data/spec/cql/client/asynchronous_client_spec.rb +368 -175
  21. data/spec/cql/client/asynchronous_prepared_statement_spec.rb +132 -22
  22. data/spec/cql/client/connection_helper_spec.rb +335 -0
  23. data/spec/cql/client/connection_manager_spec.rb +118 -0
  24. data/spec/cql/client/keyspace_changer_spec.rb +50 -0
  25. data/spec/cql/client/request_runner_spec.rb +12 -12
  26. data/spec/cql/client/synchronous_client_spec.rb +15 -15
  27. data/spec/cql/client/synchronous_prepared_statement_spec.rb +15 -11
  28. data/spec/cql/future_spec.rb +529 -301
  29. data/spec/cql/io/connection_spec.rb +12 -12
  30. data/spec/cql/io/io_reactor_spec.rb +61 -61
  31. data/spec/cql/protocol/cql_protocol_handler_spec.rb +26 -12
  32. data/spec/cql/protocol/encoding_spec.rb +5 -0
  33. data/spec/cql/protocol/type_converter_spec.rb +1 -1
  34. data/spec/cql/time_uuid_spec.rb +7 -7
  35. data/spec/integration/client_spec.rb +2 -2
  36. data/spec/integration/io_spec.rb +20 -20
  37. data/spec/integration/protocol_spec.rb +17 -17
  38. data/spec/integration/regression_spec.rb +6 -0
  39. data/spec/integration/uuid_spec.rb +4 -0
  40. data/spec/support/fake_io_reactor.rb +38 -8
  41. data/spec/support/fake_server.rb +3 -3
  42. metadata +12 -2
@@ -6,16 +6,12 @@ require 'spec_helper'
6
6
  module Cql
7
7
  module Client
8
8
  describe AsynchronousPreparedStatement do
9
- let :statement do
10
- described_class.new(connection, statement_id, raw_metadata)
9
+ let :connection_manager do
10
+ ConnectionManager.new
11
11
  end
12
12
 
13
- let :connection do
14
- stub(:connection)
15
- end
16
-
17
- let :statement_id do
18
- "\x2a"
13
+ let :logger do
14
+ NullLogger.new
19
15
  end
20
16
 
21
17
  let :raw_metadata do
@@ -33,7 +29,72 @@ module Cql
33
29
  ]
34
30
  end
35
31
 
32
+ let :cql do
33
+ 'SELECT * FROM my_table'
34
+ end
35
+
36
+ let :connections do
37
+ [
38
+ FakeConnection.new('h0.example.com', 1234, 42),
39
+ FakeConnection.new('h1.example.com', 1234, 42),
40
+ FakeConnection.new('h2.example.com', 1234, 42),
41
+ ]
42
+ end
43
+
44
+ def handle_request(connection, request)
45
+ case request
46
+ when Protocol::PrepareRequest
47
+ statement_id = [rand(2**31)].pack('c*')
48
+ connection[:last_prepared_statement_id] = statement_id
49
+ Protocol::PreparedResultResponse.new(statement_id, raw_metadata)
50
+ when Protocol::ExecuteRequest
51
+ Protocol::RowsResultResponse.new(rows, raw_metadata)
52
+ else
53
+ raise %(Unexpected request: #{request})
54
+ end
55
+ end
56
+
57
+ before do
58
+ connections.each do |c|
59
+ c.handle_request { |r| handle_request(c, r) }
60
+ end
61
+ connection_manager.add_connections(connections)
62
+ end
63
+
64
+ describe '.prepare' do
65
+ it 'prepares a statement on all connections' do
66
+ f = described_class.prepare(cql, :one, connection_manager, logger)
67
+ f.value
68
+ connections.each do |c|
69
+ c.requests.should include(Protocol::PrepareRequest.new(cql))
70
+ end
71
+ end
72
+
73
+ it 'returns a prepared statement object' do
74
+ f = described_class.prepare(cql, :two, connection_manager, logger)
75
+ f.value.should be_a(PreparedStatement)
76
+ end
77
+
78
+ it 'returns a failed future when something goes wrong in the preparation' do
79
+ connections.each(&:close)
80
+ f = described_class.prepare(cql, :three, connection_manager, logger)
81
+ expect { f.value }.to raise_error(NotConnectedError)
82
+ end
83
+
84
+ it 'returns a failed future if the preparation results in an error' do
85
+ connections.each do |connection|
86
+ connection.stub(:send_request).and_return(Future.resolved(Protocol::ErrorResponse.new(99, 'bork')))
87
+ end
88
+ f = described_class.prepare(cql, :quorum, connection_manager, logger)
89
+ expect { f.value }.to raise_error('bork')
90
+ end
91
+ end
92
+
36
93
  describe '#metadata' do
94
+ let :statement do
95
+ described_class.prepare(cql, :all, connection_manager, logger).value
96
+ end
97
+
37
98
  it 'returns the interpreted metadata' do
38
99
  statement.metadata.should be_a(ResultMetadata)
39
100
  statement.metadata['my_column'].should be_a(ColumnMetadata)
@@ -41,28 +102,77 @@ module Cql
41
102
  end
42
103
 
43
104
  describe '#execute' do
44
- it 'creates and sends an EXECUTE request' do
45
- expected_request = Cql::Protocol::ExecuteRequest.new(statement_id, raw_metadata, [11, 'hello'], :one)
46
- connection.should_receive(:send_request).with(expected_request)
47
- statement.execute(11, 'hello', :one)
105
+ let :statement do
106
+ described_class.prepare(cql, :local_quorum, connection_manager, logger).value
107
+ end
108
+
109
+ it 'executes itself on one of the connections' do
110
+ statement.execute(11, 'hello')
111
+ requests = connections.flat_map(&:requests).select { |r| r.is_a?(Protocol::ExecuteRequest) }
112
+ requests.should have(1).item
113
+ requests.first.metadata.should == raw_metadata
114
+ requests.first.values.should == [11, 'hello']
115
+ end
116
+
117
+ it 'uses the right statement ID for the connection' do
118
+ statement.execute(11, 'hello')
119
+ connection, request = connections.map { |c| [c, c.requests.find { |r| r.is_a?(Protocol::ExecuteRequest) }] }.find { |c, r| r }
120
+ request.id.should == connection[:last_prepared_statement_id]
121
+ end
122
+
123
+ it 'sends the default consistency level' do
124
+ statement.execute(11, 'hello')
125
+ request = connections.flat_map(&:requests).find { |r| r.is_a?(Protocol::ExecuteRequest) }
126
+ request.consistency.should == :local_quorum
127
+ end
128
+
129
+ it 'sends the consistency given as last argument' do
130
+ statement.execute(11, 'hello', :two)
131
+ request = connections.flat_map(&:requests).find { |r| r.is_a?(Protocol::ExecuteRequest) }
132
+ request.consistency.should == :two
133
+ end
134
+
135
+ context 'when it receives a new connection from the connection manager' do
136
+ let :new_connection do
137
+ FakeConnection.new('h3.example.com', 1234, 5)
138
+ end
139
+
140
+ before do
141
+ statement
142
+ new_connection.handle_request { |r| handle_request(new_connection, r) }
143
+ connections.each(&:close)
144
+ connection_manager.add_connections([new_connection])
145
+ end
146
+
147
+ it 'prepares itself on the connection' do
148
+ statement.execute(11, 'hello')
149
+ new_connection.requests.should include(Protocol::PrepareRequest.new(cql))
150
+ execute_request = new_connection.requests.find { |r| r.is_a?(Protocol::ExecuteRequest) }
151
+ execute_request.metadata.should == raw_metadata
152
+ execute_request.values.should == [11, 'hello']
153
+ end
154
+
155
+ it 'logs a message' do
156
+ logger.stub(:debug)
157
+ statement.execute(11, 'hello')
158
+ logger.should have_received(:debug).with(/Statement prepared/).once
159
+ end
48
160
  end
49
161
 
50
- it 'returns a future that resolves to a QueryResult' do
51
- request = Cql::Protocol::ExecuteRequest.new(statement_id, raw_metadata, [11, 'hello'], :two)
52
- response = Cql::Protocol::RowsResultResponse.new(rows, raw_metadata)
53
- connection.stub(:send_request).with(request).and_return(Future.completed(response))
54
- result = statement.execute(11, 'hello', :two).get
55
- result.metadata['my_other_column'].should == ColumnMetadata.new('my_keyspace', 'my_table', 'my_other_column', :text)
56
- result.first.should == {'my_column' => 11, 'my_other_column' => 'hello'}
162
+ it 'returns a future that resolves to the result' do
163
+ f = statement.execute(11, 'hello')
164
+ query_result = f.value
165
+ query_result.metadata['my_other_column'].should == ColumnMetadata.new('my_keyspace', 'my_table', 'my_other_column', :text)
166
+ query_result.first.should == rows.first
57
167
  end
58
168
 
59
169
  it 'returns a failed future when the number of arguments is wrong' do
60
170
  f1 = statement.execute(11, :one)
61
171
  f2 = statement.execute(11, 'foo', 22, :one)
62
- expect { f1.get }.to raise_error
63
- expect { f2.get }.to raise_error
172
+ expect { f1.value }.to raise_error
173
+ expect { f2.value }.to raise_error
64
174
  end
65
175
  end
66
176
  end
67
177
  end
68
- end
178
+ end
@@ -0,0 +1,335 @@
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, 7, 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
+ describe '#connect' do
22
+ let :hosts do
23
+ %w[host0 host1]
24
+ end
25
+
26
+ let :local_metadata do
27
+ [
28
+ ['system', 'local', 'data_center', :text],
29
+ ['system', 'local', 'host_id', :uuid],
30
+ ]
31
+ end
32
+
33
+ before do
34
+ io_reactor.stub(:start).and_return(Future.resolved)
35
+ io_reactor.stub(:connect).and_return(Future.resolved)
36
+ end
37
+
38
+ it 'starts the IO reactor' do
39
+ connection_helper.connect(hosts, nil)
40
+ io_reactor.should have_received(:start)
41
+ end
42
+
43
+ it 'fails when the IO reactor fails to start' do
44
+ io_reactor.stub(:start).and_return(Future.failed(StandardError.new('bork')))
45
+ f = connection_helper.connect(hosts, nil)
46
+ expect { f.value }.to raise_error('bork')
47
+ end
48
+
49
+ it 'connects to the specified hosts' do
50
+ connection_helper.connect(hosts, nil)
51
+ io_reactor.should have_received(:connect).with('host0', 9876, 7)
52
+ io_reactor.should have_received(:connect).with('host1', 9876, 7)
53
+ end
54
+
55
+ it 'logs a message when a node connects' do
56
+ logger.stub(:info)
57
+ io_reactor.stub(:connect).and_return(Future.resolved(FakeConnection.new('host', 9876, 7)))
58
+ connection_helper.connect(hosts, nil)
59
+ logger.should have_received(:info).with(/Connected to node/).exactly(hosts.size).times
60
+ end
61
+
62
+ it 'logs a message when connecting to a node' do
63
+ logger.stub(:debug)
64
+ io_reactor.stub(:connect).and_return(Future.resolved(FakeConnection.new('host', 9876, 7)))
65
+ connection_helper.connect(hosts, nil)
66
+ logger.should have_received(:debug).with(/Connecting to node/).exactly(hosts.size).times
67
+ end
68
+
69
+ it 'fails when all hosts fail to connect' do
70
+ io_reactor.stub(:connect).and_return(Future.failed(StandardError.new('bork')))
71
+ f = connection_helper.connect(hosts, nil)
72
+ expect { f.value }.to raise_error('bork')
73
+ end
74
+
75
+ it 'logs a message when a node fails to connect' do
76
+ logger.stub(:warn)
77
+ io_reactor.stub(:connect).and_return(Future.failed(StandardError.new('bork')))
78
+ connection_helper.connect(hosts, nil)
79
+ logger.should have_received(:warn).with(/Failed connecting to node/).exactly(hosts.size).times
80
+ end
81
+
82
+ it 'fails with an AuthenticationError when the connections fail to connect because of authentication issues' do
83
+ io_reactor.stub(:connect).and_return(Future.failed(QueryError.new(0x100, 'bork')))
84
+ f = connection_helper.connect(hosts, nil)
85
+ expect { f.value }.to raise_error(AuthenticationError)
86
+ end
87
+
88
+ it 'initializes the connections' do
89
+ connection0 = FakeConnection.new('host0', 9876, 7)
90
+ connection1 = FakeConnection.new('host1', 9876, 7)
91
+ io_reactor.stub(:connect).with('host0', 9876, 7).and_return(Future.resolved(connection0))
92
+ io_reactor.stub(:connect).with('host1', 9876, 7).and_return(Future.resolved(connection1))
93
+ connection_helper.connect(hosts, 'some_keyspace')
94
+ [connection0, connection1].each do |c|
95
+ c.requests[0].should be_a(Protocol::StartupRequest)
96
+ c.requests[1].cql.should match(/SELECT .* FROM system.local/)
97
+ c.requests[2].cql.should == 'USE some_keyspace'
98
+ end
99
+ end
100
+
101
+ it 'fails if authentication is required and no credentials were specified' do
102
+ connection = FakeConnection.new('host0', 9876, 7)
103
+ connection.handle_request do |request|
104
+ if request.is_a?(Protocol::StartupRequest)
105
+ Protocol::AuthenticateResponse.new('xyz')
106
+ else
107
+ connection.default_request_handler(request)
108
+ end
109
+ end
110
+ io_reactor.stub(:connect).with('host0', 9876, 7).and_return(Future.resolved(connection))
111
+ f = connection_helper.connect(hosts, nil)
112
+ expect { f.value }.to raise_error(AuthenticationError)
113
+ end
114
+
115
+ it 'authenticates when authentication is required and credentials were specified' do
116
+ credentials = {'username' => 'foo', 'password' => 'bar'}
117
+ connection_helper = described_class.new(io_reactor, 9876, credentials, 7, logger)
118
+ connection = FakeConnection.new('host0', 9876, 7)
119
+ authentication_sent = false
120
+ connection.handle_request do |request|
121
+ if request.is_a?(Protocol::StartupRequest)
122
+ Protocol::AuthenticateResponse.new('xyz')
123
+ elsif request == Protocol::CredentialsRequest.new(credentials)
124
+ authentication_sent = true
125
+ Protocol::ReadyResponse.new
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
+ f.value
133
+ authentication_sent.should be_true
134
+ end
135
+
136
+ it 'decorates the connections with :host_id and :data_center' do
137
+ connection = FakeConnection.new('host0', 9876, 7)
138
+ connection.handle_request do |request|
139
+ if request.is_a?(Protocol::QueryRequest) && request.cql =~ /SELECT .* FROM system\.local/
140
+ row = {'data_center' => 'dc1', 'host_id' => Uuid.new('eac69196-1e28-11e3-8e2b-191b6d153d0c')}
141
+ Protocol::RowsResultResponse.new([row], local_metadata)
142
+ else
143
+ connection.default_request_handler(request)
144
+ end
145
+ end
146
+ io_reactor.stub(:connect).with('host0', 9876, 7).and_return(Future.resolved(connection))
147
+ connection_helper.connect(hosts, nil)
148
+ connection[:host_id].should == Uuid.new('eac69196-1e28-11e3-8e2b-191b6d153d0c')
149
+ connection[:data_center].should == 'dc1'
150
+ end
151
+
152
+ it 'registers a close handler that logs when connections close' do
153
+ logger.stub(:warn)
154
+ connection = FakeConnection.new('host', 9876, 7)
155
+ io_reactor.stub(:connect).and_return(Future.resolved(connection))
156
+ connection_helper.connect(hosts.take(1), nil)
157
+ connection.close
158
+ logger.should have_received(:warn).with(/Connection to node .* closed/)
159
+ end
160
+
161
+ it 'initializes a peer discovery when connected to the specified hosts' do
162
+ connection_helper.stub(:discover_peers)
163
+ connection_helper.connect(hosts, nil)
164
+ connection0 = FakeConnection.new('host0', 9876, 7)
165
+ connection1 = FakeConnection.new('host1', 9876, 7)
166
+ io_reactor.stub(:connect).with('host0', 9876, 7).and_return(Future.resolved(connection0))
167
+ io_reactor.stub(:connect).with('host1', 9876, 7).and_return(Future.resolved(connection1))
168
+ connection_helper.connect(hosts, 'some_keyspace')
169
+ connection_helper.should have_received(:discover_peers).with([connection0, connection1], 'some_keyspace')
170
+ end
171
+
172
+ it 'initializes a peer discovery with the successfull connections as seeds' do
173
+ connection_helper.stub(:discover_peers)
174
+ connection_helper.connect(hosts, nil)
175
+ connection = FakeConnection.new('host0', 9876, 7)
176
+ io_reactor.stub(:connect).with('host0', 9876, 7).and_return(Future.resolved(connection))
177
+ io_reactor.stub(:connect).with('host1', 9876, 7).and_return(Future.failed(StandardError.new('bork')))
178
+ connection_helper.connect(hosts, 'some_keyspace')
179
+ connection_helper.should have_received(:discover_peers).with([connection], 'some_keyspace')
180
+ end
181
+ end
182
+
183
+ describe '#discover_peers' do
184
+ let :seed_connections do
185
+ [
186
+ FakeConnection.new('host0', 9042, 5),
187
+ FakeConnection.new('host1', 9042, 5),
188
+ FakeConnection.new('host2', 9042, 5),
189
+ ]
190
+ end
191
+
192
+ let :seed_connection_rows do
193
+ [
194
+ {'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')},
195
+ {'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')},
196
+ {'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')},
197
+ ]
198
+ end
199
+
200
+ let :extra_connection_rows do
201
+ [
202
+ {'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')},
203
+ {'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')},
204
+ {'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')},
205
+ ]
206
+ end
207
+
208
+ let :peer_metadata do
209
+ [
210
+ ['system', 'peers', 'peer', :inet],
211
+ ['system', 'peers', 'data_center', :varchar],
212
+ ['system', 'peers', 'host_id', :uuid],
213
+ ['system', 'peers', 'rpc_address', :inet],
214
+ ]
215
+ end
216
+
217
+ before do
218
+ seed_connections.each_with_index do |c, i|
219
+ c[:host_id] = seed_connection_rows[i]['host_id']
220
+ c[:data_center] = seed_connection_rows[i]['data_center']
221
+ end
222
+ end
223
+
224
+ def peer_request_response
225
+ seed_connections.each do |c|
226
+ c.handle_request do |request|
227
+ if request.cql =~ /SELECT .* FROM system\.peers/
228
+ Protocol::RowsResultResponse.new(yield, peer_metadata)
229
+ end
230
+ end
231
+ end
232
+ end
233
+
234
+ it 'returns immediately if there are no seed connections' do
235
+ f = connection_helper.discover_peers([], nil)
236
+ f.value
237
+ end
238
+
239
+ it 'logs a message when it begins' do
240
+ logger.stub(:debug)
241
+ connection_helper.discover_peers([], nil)
242
+ logger.should have_received(:debug).with(/Looking for additional nodes/)
243
+ end
244
+
245
+ it 'asks a random connection for its peers' do
246
+ connection_helper.discover_peers(seed_connections, nil)
247
+ connection = seed_connections.find { |c| c.requests.any? }
248
+ connection.requests.first.cql.should match(/SELECT .* FROM system\.peers/)
249
+ end
250
+
251
+ it 'returns an empty list when it only finds nodes it\'s already connected to' do
252
+ peer_request_response { seed_connection_rows }
253
+ f = connection_helper.discover_peers(seed_connections, nil)
254
+ f.value.should be_empty
255
+ end
256
+
257
+ it 'logs a message when it finds no new nodes' do
258
+ logger.stub(:debug)
259
+ peer_request_response { seed_connection_rows }
260
+ connection_helper.discover_peers(seed_connections, nil)
261
+ logger.should have_received(:debug).with(/No additional nodes found/)
262
+ end
263
+
264
+ it 'returns an empty list when it only finds nodes data centers other than those of the seed connections' do
265
+ seed_connections[1][:data_center] = 'dc2'
266
+ seed_connection_rows[1]['data_center'] = 'dc2'
267
+ extra_connection_rows[0]['data_center'] = 'dc3'
268
+ peer_request_response { seed_connection_rows + extra_connection_rows.take(1) }
269
+ f = connection_helper.discover_peers(seed_connections, nil)
270
+ f.value.should be_empty
271
+ end
272
+
273
+ it 'connects to the nodes it finds that it is not already connected to' do
274
+ connection = FakeConnection.new('host3', 9876, 7)
275
+ io_reactor.stub(:connect).with('1.0.0.3', 9876, 7).and_return(Future.resolved(connection))
276
+ peer_request_response { seed_connection_rows + extra_connection_rows.take(1) }
277
+ f = connection_helper.discover_peers(seed_connections, nil)
278
+ f.value
279
+ end
280
+
281
+ it 'logs the number of new nodes found' do
282
+ logger.stub(:debug)
283
+ connection = FakeConnection.new('host3', 9876, 7)
284
+ io_reactor.stub(:connect).with('1.0.0.3', 9876, 7).and_return(Future.resolved(connection))
285
+ peer_request_response { seed_connection_rows + extra_connection_rows.take(1) }
286
+ connection_helper.discover_peers(seed_connections, nil)
287
+ logger.should have_received(:debug).with(/1 additional nodes found/)
288
+ end
289
+
290
+ it 'returns the new connections' do
291
+ connection = FakeConnection.new('host3', 9876, 7)
292
+ io_reactor.stub(:connect).with('1.0.0.3', 9876, 7).and_return(Future.resolved(connection))
293
+ peer_request_response { seed_connection_rows + extra_connection_rows.take(1) }
294
+ f = connection_helper.discover_peers(seed_connections, nil)
295
+ f.value.should == [connection]
296
+ end
297
+
298
+ it 'initializes the new connections' do
299
+ connection = FakeConnection.new('host3', 9876, 7)
300
+ io_reactor.stub(:connect).with('1.0.0.3', 9876, 7).and_return(Future.resolved(connection))
301
+ peer_request_response { seed_connection_rows + extra_connection_rows.take(1) }
302
+ f = connection_helper.discover_peers(seed_connections, 'some_keyspace')
303
+ f.value
304
+ connection.requests[0].should be_a(Protocol::StartupRequest)
305
+ connection.requests[1].cql.should match(/SELECT .* FROM system.local/)
306
+ connection.requests[2].cql.should == 'USE some_keyspace'
307
+ end
308
+
309
+ it 'connects only to node in the same data centers as the seed nodes' do
310
+ seed_connections[1][:data_center] = 'dc2'
311
+ seed_connection_rows[1]['data_center'] = 'dc2'
312
+ extra_connection_rows[0]['data_center'] = 'dc3'
313
+ extra_connection_rows[1]['data_center'] = 'dc2'
314
+ extra_connection_rows[2]['data_center'] = 'dc1'
315
+ connection4 = FakeConnection.new('host4', 9876, 7)
316
+ connection5 = FakeConnection.new('host5', 9876, 7)
317
+ io_reactor.stub(:connect).with('1.0.0.4', 9876, 7).and_return(Future.resolved(connection4))
318
+ io_reactor.stub(:connect).with('1.0.0.5', 9876, 7).and_return(Future.resolved(connection5))
319
+ peer_request_response { seed_connection_rows + extra_connection_rows.take(3) }
320
+ f = connection_helper.discover_peers(seed_connections, nil)
321
+ f.value.should == [connection4, connection5]
322
+ end
323
+
324
+ it 'uses the peer address instead of the RPC address when latter is 0.0.0.0' do
325
+ extra_connection_rows[0]['rpc_address'] = IPAddr.new('0.0.0.0')
326
+ connection = FakeConnection.new('host3', 9876, 7)
327
+ io_reactor.stub(:connect).with(extra_connection_rows[0]['peer'].to_s, 9876, 7).and_return(Future.resolved(connection))
328
+ peer_request_response { seed_connection_rows + extra_connection_rows.take(1) }
329
+ f = connection_helper.discover_peers(seed_connections, nil)
330
+ f.value
331
+ end
332
+ end
333
+ end
334
+ end
335
+ end