cql-rb 1.0.6 → 1.1.0.pre0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. data/README.md +4 -9
  2. data/lib/cql.rb +1 -0
  3. data/lib/cql/byte_buffer.rb +23 -7
  4. data/lib/cql/client.rb +11 -6
  5. data/lib/cql/client/asynchronous_client.rb +37 -83
  6. data/lib/cql/client/asynchronous_prepared_statement.rb +10 -4
  7. data/lib/cql/client/column_metadata.rb +16 -0
  8. data/lib/cql/client/request_runner.rb +46 -0
  9. data/lib/cql/future.rb +4 -5
  10. data/lib/cql/io.rb +2 -5
  11. data/lib/cql/io/connection.rb +220 -0
  12. data/lib/cql/io/io_reactor.rb +213 -185
  13. data/lib/cql/protocol.rb +1 -0
  14. data/lib/cql/protocol/cql_protocol_handler.rb +201 -0
  15. data/lib/cql/protocol/decoding.rb +6 -31
  16. data/lib/cql/protocol/encoding.rb +1 -5
  17. data/lib/cql/protocol/request.rb +4 -0
  18. data/lib/cql/protocol/responses/schema_change_result_response.rb +15 -0
  19. data/lib/cql/protocol/type_converter.rb +56 -76
  20. data/lib/cql/time_uuid.rb +104 -0
  21. data/lib/cql/uuid.rb +4 -2
  22. data/lib/cql/version.rb +1 -1
  23. data/spec/cql/client/asynchronous_client_spec.rb +47 -71
  24. data/spec/cql/client/asynchronous_prepared_statement_spec.rb +68 -0
  25. data/spec/cql/client/client_shared.rb +3 -3
  26. data/spec/cql/client/column_metadata_spec.rb +80 -0
  27. data/spec/cql/client/request_runner_spec.rb +120 -0
  28. data/spec/cql/future_spec.rb +26 -11
  29. data/spec/cql/io/connection_spec.rb +460 -0
  30. data/spec/cql/io/io_reactor_spec.rb +212 -265
  31. data/spec/cql/protocol/cql_protocol_handler_spec.rb +216 -0
  32. data/spec/cql/protocol/decoding_spec.rb +9 -28
  33. data/spec/cql/protocol/encoding_spec.rb +0 -5
  34. data/spec/cql/protocol/request_spec.rb +16 -0
  35. data/spec/cql/protocol/response_frame_spec.rb +2 -2
  36. data/spec/cql/protocol/responses/schema_change_result_response_spec.rb +70 -0
  37. data/spec/cql/time_uuid_spec.rb +136 -0
  38. data/spec/cql/uuid_spec.rb +1 -5
  39. data/spec/integration/client_spec.rb +34 -38
  40. data/spec/integration/io_spec.rb +283 -0
  41. data/spec/integration/protocol_spec.rb +53 -113
  42. data/spec/integration/regression_spec.rb +124 -0
  43. data/spec/integration/uuid_spec.rb +76 -0
  44. data/spec/spec_helper.rb +12 -9
  45. data/spec/support/fake_io_reactor.rb +52 -21
  46. data/spec/support/fake_server.rb +2 -2
  47. metadata +33 -10
  48. checksums.yaml +0 -15
  49. data/lib/cql/io/node_connection.rb +0 -209
  50. data/spec/cql/protocol/type_converter_spec.rb +0 -52
@@ -36,10 +36,6 @@ module Cql
36
36
  Uuid.new(276263553384940695775376958868900023510).should eql(Uuid.new('cfd66ccc-d857-4e90-b1e5-df98a3d40cd6'))
37
37
  end
38
38
 
39
- it 'is not equal when anything other than a Uuid is passed' do
40
- [nil, 123, 'test'].each { |v| Uuid.new(276263553384940695775376958868900023510).should_not eql(v) }
41
- end
42
-
43
39
  it 'aliases #== to #eql?' do
44
40
  Uuid.new(276263553384940695775376958868900023510).should == Uuid.new('cfd66ccc-d857-4e90-b1e5-df98a3d40cd6')
45
41
  end
@@ -77,4 +73,4 @@ module Cql
77
73
  end
78
74
  end
79
75
  end
80
- end
76
+ end
@@ -12,38 +12,36 @@ describe 'A CQL client' do
12
12
  Cql::Client.connect(connection_options)
13
13
  end
14
14
 
15
- before do
16
- client.connect
17
- end
18
-
19
15
  after do
20
- client.close
16
+ client.close rescue nil
21
17
  end
22
18
 
23
- it 'executes a query and returns the result' do
24
- result = client.execute('SELECT * FROM system.schema_keyspaces')
25
- result.should_not be_empty
26
- end
19
+ context 'with common operations' do
20
+ it 'executes a query and returns the result' do
21
+ result = client.execute('SELECT * FROM system.schema_keyspaces')
22
+ result.should_not be_empty
23
+ end
27
24
 
28
- it 'knows which keyspace it\'s in' do
29
- client.use('system')
30
- client.keyspace.should == 'system'
31
- client.use('system_auth')
32
- client.keyspace.should == 'system_auth'
33
- end
25
+ it 'knows which keyspace it\'s in' do
26
+ client.use('system')
27
+ client.keyspace.should == 'system'
28
+ client.use('system_auth')
29
+ client.keyspace.should == 'system_auth'
30
+ end
34
31
 
35
- it 'is not in a keyspace initially' do
36
- client.keyspace.should be_nil
37
- end
32
+ it 'is not in a keyspace initially' do
33
+ client.keyspace.should be_nil
34
+ end
38
35
 
39
- it 'can be initialized with a keyspace' do
40
- c = Cql::Client.connect(connection_options.merge(:keyspace => 'system'))
41
- c.connect
42
- begin
43
- c.keyspace.should == 'system'
44
- expect { c.execute('SELECT * FROM schema_keyspaces') }.to_not raise_error
45
- ensure
46
- c.close
36
+ it 'can be initialized with a keyspace' do
37
+ c = Cql::Client.connect(connection_options.merge(:keyspace => 'system'))
38
+ c.connect
39
+ begin
40
+ c.keyspace.should == 'system'
41
+ expect { c.execute('SELECT * FROM schema_keyspaces') }.to_not raise_error
42
+ ensure
43
+ c.close
44
+ end
47
45
  end
48
46
  end
49
47
 
@@ -77,15 +75,14 @@ describe 'A CQL client' do
77
75
 
78
76
  before do
79
77
  client.close
80
- multi_client.connect
81
- multi_client.use('system')
82
78
  end
83
79
 
84
80
  after do
85
- multi_client.close
81
+ multi_client.close rescue nil
86
82
  end
87
83
 
88
84
  it 'handles keyspace changes with #use' do
85
+ multi_client.use('system')
89
86
  100.times do
90
87
  result = multi_client.execute(%<SELECT * FROM schema_keyspaces WHERE keyspace_name = 'system'>)
91
88
  result.should have(1).item
@@ -93,6 +90,7 @@ describe 'A CQL client' do
93
90
  end
94
91
 
95
92
  it 'handles keyspace changes with #execute' do
93
+ multi_client.execute('USE system')
96
94
  100.times do
97
95
  result = multi_client.execute(%<SELECT * FROM schema_keyspaces WHERE keyspace_name = 'system'>)
98
96
  result.should have(1).item
@@ -100,6 +98,7 @@ describe 'A CQL client' do
100
98
  end
101
99
 
102
100
  it 'executes a prepared statement' do
101
+ multi_client.use('system')
103
102
  statement = multi_client.prepare('SELECT * FROM system.schema_keyspaces WHERE keyspace_name = ?')
104
103
  100.times do
105
104
  result = statement.execute('system')
@@ -122,25 +121,22 @@ describe 'A CQL client' do
122
121
  end
123
122
  end
124
123
 
125
- it 'send credentials given in :credentials' do
124
+ it 'sends credentials given in :credentials' do
126
125
  client = Cql::Client.connect(connection_options.merge(credentials: {username: 'cassandra', password: 'cassandra'}))
127
126
  client.execute('SELECT * FROM system.schema_keyspaces')
128
127
  end
129
128
 
130
129
  it 'raises an error when no credentials have been given' do
131
- if authentication_enabled
130
+ pending('authentication not configured', unless: authentication_enabled) do
132
131
  expect { Cql::Client.connect(connection_options.merge(credentials: nil)) }.to raise_error(Cql::AuthenticationError)
133
- else
134
- pending 'authentication not configured'
135
132
  end
136
133
  end
137
134
 
138
135
  it 'raises an error when the credentials are bad' do
139
- if authentication_enabled
140
- client = Cql::Client.connect(connection_options.merge(credentials: {username: 'foo', password: 'bar'}))
141
- expect { client.connect }.to raise_error(Cql::AuthenticationError)
142
- else
143
- pending 'authentication not configured'
136
+ pending('authentication not configured', unless: authentication_enabled) do
137
+ expect {
138
+ Cql::Client.connect(connection_options.merge(credentials: {username: 'foo', password: 'bar'}))
139
+ }.to raise_error(Cql::AuthenticationError)
144
140
  end
145
141
  end
146
142
  end
@@ -0,0 +1,283 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+
6
+ describe 'An IO reactor' do
7
+ context 'with a generic server' do
8
+ let :io_reactor do
9
+ Cql::Io::IoReactor.new(IoSpec::TestConnection)
10
+ end
11
+
12
+ let :fake_server do
13
+ FakeServer.new
14
+ end
15
+
16
+ before do
17
+ fake_server.start!
18
+ io_reactor.start
19
+ end
20
+
21
+ after do
22
+ io_reactor.stop
23
+ fake_server.stop!
24
+ end
25
+
26
+ it 'connects to the server' do
27
+ io_reactor.connect(ENV['CASSANDRA_HOST'], fake_server.port, 1)
28
+ fake_server.await_connects!(1)
29
+ end
30
+
31
+ it 'receives data' do
32
+ protocol_handler = io_reactor.connect(ENV['CASSANDRA_HOST'], fake_server.port, 1).get
33
+ fake_server.await_connects!(1)
34
+ fake_server.broadcast!('hello world')
35
+ await { protocol_handler.data.bytesize > 0 }
36
+ protocol_handler.data.should == 'hello world'
37
+ end
38
+
39
+ it 'receives data on multiple connections' do
40
+ protocol_handlers = Array.new(10) { io_reactor.connect(ENV['CASSANDRA_HOST'], fake_server.port, 1).get }
41
+ fake_server.await_connects!(10)
42
+ fake_server.broadcast!('hello world')
43
+ await { protocol_handlers.all? { |c| c.data.bytesize > 0 } }
44
+ protocol_handlers.sample.data.should == 'hello world'
45
+ end
46
+ end
47
+
48
+ context 'when talking to Redis' do
49
+ let :io_reactor do
50
+ Cql::Io::IoReactor.new(IoSpec::RedisProtocolHandler)
51
+ end
52
+
53
+ let :protocol_handler do
54
+ begin
55
+ io_reactor.connect(ENV['CASSANDRA_HOST'], 6379, 1).get
56
+ rescue Cql::Io::ConnectionError
57
+ nil
58
+ end
59
+ end
60
+
61
+ before do
62
+ io_reactor.start.get
63
+ end
64
+
65
+ after do
66
+ io_reactor.stop.get
67
+ end
68
+
69
+ it 'can set a value' do
70
+ pending('Redis not running', unless: protocol_handler)
71
+ response = protocol_handler.send_request('SET', 'foo', 'bar').get
72
+ response.should == 'OK'
73
+ end
74
+
75
+ it 'can get a value' do
76
+ pending('Redis not running', unless: protocol_handler)
77
+ f = protocol_handler.send_request('SET', 'foo', 'bar').flat_map do
78
+ protocol_handler.send_request('GET', 'foo')
79
+ end
80
+ f.get.should == 'bar'
81
+ end
82
+
83
+ it 'can delete values' do
84
+ pending('Redis not running', unless: protocol_handler)
85
+ f = protocol_handler.send_request('SET', 'hello', 'world').flat_map do
86
+ protocol_handler.send_request('DEL', 'hello')
87
+ end
88
+ f.get.should == 1
89
+ end
90
+
91
+ it 'handles nil values' do
92
+ pending('Redis not running', unless: protocol_handler)
93
+ f = protocol_handler.send_request('DEL', 'hello').flat_map do
94
+ protocol_handler.send_request('GET', 'hello')
95
+ end
96
+ f.get.should be_nil
97
+ end
98
+
99
+ it 'handles errors' do
100
+ pending('Redis not running', unless: protocol_handler)
101
+ f = protocol_handler.send_request('SET', 'foo')
102
+ expect { f.get }.to raise_error("ERR wrong number of arguments for 'set' command")
103
+ end
104
+
105
+ it 'handles replies with multiple elements' do
106
+ pending('Redis not running', unless: protocol_handler)
107
+ f = protocol_handler.send_request('DEL', 'stuff')
108
+ f.get
109
+ f = protocol_handler.send_request('RPUSH', 'stuff', 'hello', 'world')
110
+ f.get.should == 2
111
+ f = protocol_handler.send_request('LRANGE', 'stuff', 0, 2)
112
+ f.get.should == ['hello', 'world']
113
+ end
114
+
115
+ it 'handles nil values when reading multiple elements' do
116
+ pending('Redis not running', unless: protocol_handler)
117
+ protocol_handler.send_request('DEL', 'things')
118
+ protocol_handler.send_request('HSET', 'things', 'hello', 'world')
119
+ f = protocol_handler.send_request('HMGET', 'things', 'hello', 'foo')
120
+ f.get.should == ['world', nil]
121
+ end
122
+ end
123
+ end
124
+
125
+ module IoSpec
126
+ class TestConnection
127
+ def initialize(connection)
128
+ @connection = connection
129
+ @connection.on_data(&method(:receive_data))
130
+ @lock = Mutex.new
131
+ @data = Cql::ByteBuffer.new
132
+ end
133
+
134
+ def data
135
+ @lock.synchronize { @data.to_s }
136
+ end
137
+
138
+ private
139
+
140
+ def receive_data(new_data)
141
+ @lock.synchronize { @data << new_data }
142
+ end
143
+ end
144
+
145
+ class LineProtocolHandler
146
+ def initialize(connection)
147
+ @connection = connection
148
+ @connection.on_data(&method(:process_data))
149
+ @lock = Mutex.new
150
+ @buffer = ''
151
+ @requests = []
152
+ end
153
+
154
+ def on_line(&listener)
155
+ @line_listener = listener
156
+ end
157
+
158
+ def write(command_string)
159
+ @connection.write(command_string)
160
+ end
161
+
162
+ def process_data(new_data)
163
+ lines = []
164
+ @lock.synchronize do
165
+ @buffer << new_data
166
+ while newline_index = @buffer.index("\r\n")
167
+ line = @buffer.slice!(0, newline_index + 2)
168
+ line.chomp!
169
+ lines << line
170
+ end
171
+ end
172
+ lines.each do |line|
173
+ @line_listener.call(line) if @line_listener
174
+ end
175
+ end
176
+ end
177
+
178
+ class RedisProtocolHandler
179
+ def initialize(connection)
180
+ @line_protocol = LineProtocolHandler.new(connection)
181
+ @line_protocol.on_line(&method(:handle_line))
182
+ @lock = Mutex.new
183
+ @responses = []
184
+ @state = BaseState.new(method(:handle_response))
185
+ end
186
+
187
+ def send_request(*args)
188
+ future = Cql::Future.new
189
+ @lock.synchronize do
190
+ @responses << future
191
+ end
192
+ request = "*#{args.size}\r\n"
193
+ args.each do |arg|
194
+ arg_str = arg.to_s
195
+ request << "$#{arg_str.bytesize}\r\n#{arg_str}\r\n"
196
+ end
197
+ @line_protocol.write(request)
198
+ future
199
+ end
200
+
201
+ def handle_response(result, error=false)
202
+ future = @lock.synchronize do
203
+ @responses.shift
204
+ end
205
+ if error
206
+ future.fail!(StandardError.new(result))
207
+ else
208
+ future.complete!(result)
209
+ end
210
+ end
211
+
212
+ def handle_line(line)
213
+ @state = @state.handle_line(line)
214
+ end
215
+
216
+ class State
217
+ def initialize(result_handler)
218
+ @result_handler = result_handler
219
+ end
220
+
221
+ def complete!(result)
222
+ @result_handler.call(result)
223
+ end
224
+
225
+ def fail!(message)
226
+ @result_handler.call(message, true)
227
+ end
228
+ end
229
+
230
+ class BulkState < State
231
+ def handle_line(line)
232
+ complete!(line)
233
+ BaseState.new(@result_handler)
234
+ end
235
+ end
236
+
237
+ class MultiBulkState < State
238
+ def initialize(result_handler, expected_elements)
239
+ super(result_handler)
240
+ @expected_elements = expected_elements
241
+ @elements = []
242
+ end
243
+
244
+ def handle_line(line)
245
+ if line.start_with?('$')
246
+ line.slice!(0, 1)
247
+ if line.to_i == -1
248
+ @elements << nil
249
+ end
250
+ else
251
+ @elements << line
252
+ end
253
+ if @elements.size == @expected_elements
254
+ complete!(@elements)
255
+ BaseState.new(@result_handler)
256
+ else
257
+ self
258
+ end
259
+ end
260
+ end
261
+
262
+ class BaseState < State
263
+ def handle_line(line)
264
+ next_state = self
265
+ first_char = line.slice!(0, 1)
266
+ case first_char
267
+ when '+' then complete!(line)
268
+ when ':' then complete!(line.to_i)
269
+ when '-' then fail!(line)
270
+ when '$'
271
+ if line.to_i == -1
272
+ complete!(nil)
273
+ else
274
+ next_state = BulkState.new(@result_handler)
275
+ end
276
+ when '*'
277
+ next_state = MultiBulkState.new(@result_handler, line.to_i)
278
+ end
279
+ next_state
280
+ end
281
+ end
282
+ end
283
+ end
@@ -4,23 +4,31 @@ require 'spec_helper'
4
4
 
5
5
 
6
6
  describe 'Protocol parsing and communication' do
7
- let :io_reactor do
8
- ir = Cql::Io::IoReactor.new
7
+ let! :io_reactor do
8
+ ir = Cql::Io::IoReactor.new(Cql::Protocol::CqlProtocolHandler)
9
9
  ir.start
10
- ir.add_connection(ENV['CASSANDRA_HOST'], 9042).get
10
+ connections << ir.connect(ENV['CASSANDRA_HOST'], 9042, 5).get
11
11
  ir
12
12
  end
13
13
 
14
+ let :connections do
15
+ []
16
+ end
17
+
14
18
  let :keyspace_name do
15
19
  "cql_rb_#{rand(1000)}"
16
20
  end
17
21
 
18
22
  after do
19
- io_reactor.stop.get if io_reactor.running?
23
+ if io_reactor.running?
24
+ drop_keyspace! rescue nil
25
+ io_reactor.stop.get rescue nil
26
+ end
20
27
  end
21
28
 
22
29
  def raw_execute_request(request)
23
- io_reactor.queue_request(request).get.first
30
+ connection = connections.first
31
+ connection.send_request(request).get
24
32
  end
25
33
 
26
34
  def execute_request(request)
@@ -41,7 +49,7 @@ describe 'Protocol parsing and communication' do
41
49
  end
42
50
 
43
51
  def create_keyspace!
44
- query("CREATE KEYSPACE #{keyspace_name} WITH REPLICATION = {'class': 'SimpleStrategy', 'replication_factor': 1}")
52
+ query("CREATE KEYSPACE #{keyspace_name} WITH replication = {'class': 'SimpleStrategy', 'replication_factor': 1}")
45
53
  end
46
54
 
47
55
  def use_keyspace!
@@ -68,8 +76,9 @@ describe 'Protocol parsing and communication' do
68
76
  ensure
69
77
  begin
70
78
  drop_keyspace!
71
- rescue Errno::EPIPE => e
72
- # ignore since we're shutting down
79
+ rescue Cql::NotConnectedError, Errno::EPIPE => e
80
+ # ignore since we're shutting down, and these errors are likely caused
81
+ # by the code under test, re-raising them would mask the real errors
73
82
  end
74
83
  end
75
84
  end
@@ -109,56 +118,46 @@ describe 'Protocol parsing and communication' do
109
118
 
110
119
  context 'when authentication is required' do
111
120
  let :authentication_enabled do
112
- ir = Cql::Io::IoReactor.new
121
+ ir = Cql::Io::IoReactor.new(Cql::Protocol::CqlProtocolHandler)
113
122
  ir.start
114
- connected = ir.add_connection(ENV['CASSANDRA_HOST'], 9042)
115
- started = connected.flat_map do
116
- ir.queue_request(Cql::Protocol::StartupRequest.new)
123
+ connected = ir.connect(ENV['CASSANDRA_HOST'], 9042, 5)
124
+ started = connected.flat_map do |connection|
125
+ connection.send_request(Cql::Protocol::StartupRequest.new)
117
126
  end
118
- response = started.get.first
127
+ response = started.get
119
128
  required = response.is_a?(Cql::Protocol::AuthenticateResponse)
120
129
  ir.stop.get
121
130
  required
122
131
  end
123
132
 
124
133
  it 'sends STARTUP and receives AUTHENTICATE' do
125
- if authentication_enabled
134
+ pending('authentication not configured', unless: authentication_enabled) do
126
135
  response = raw_execute_request(Cql::Protocol::StartupRequest.new)
127
136
  response.should be_a(Cql::Protocol::AuthenticateResponse)
128
- else
129
- pending 'authentication not configured'
130
137
  end
131
138
  end
132
139
 
133
140
  it 'ignores the AUTHENTICATE response and receives ERROR' do
134
- if authentication_enabled
141
+ pending('authentication not configured', unless: authentication_enabled) do
135
142
  raw_execute_request(Cql::Protocol::StartupRequest.new)
136
143
  response = raw_execute_request(Cql::Protocol::RegisterRequest.new('TOPOLOGY_CHANGE'))
137
144
  response.code.should == 10
138
145
  response.message.should include('needs authentication')
139
- else
140
- pending 'authentication not configured'
141
146
  end
142
147
  end
143
148
 
144
149
  it 'sends STARTUP followed by CREDENTIALS and receives READY' do
145
- if authentication_enabled
146
- raw_execute_request(Cql::Protocol::StartupRequest.new)
147
- response = raw_execute_request(Cql::Protocol::CredentialsRequest.new('username' => 'cassandra', 'password' => 'cassandra'))
148
- response.should be_a(Cql::Protocol::ReadyResponse)
149
- else
150
- pending 'authentication not configured'
151
- end
150
+ raw_execute_request(Cql::Protocol::StartupRequest.new)
151
+ response = raw_execute_request(Cql::Protocol::CredentialsRequest.new('username' => 'cassandra', 'password' => 'cassandra'))
152
+ response.should be_a(Cql::Protocol::ReadyResponse)
152
153
  end
153
154
 
154
155
  it 'sends bad username and password in CREDENTIALS and receives ERROR' do
155
- if authentication_enabled
156
+ pending('authentication not configured', unless: authentication_enabled) do
156
157
  raw_execute_request(Cql::Protocol::StartupRequest.new)
157
158
  response = raw_execute_request(Cql::Protocol::CredentialsRequest.new('username' => 'foo', 'password' => 'bar'))
158
159
  response.code.should == 0x100
159
160
  response.message.should include('Username and/or password are incorrect')
160
- else
161
- pending 'authentication not configured'
162
161
  end
163
162
  end
164
163
  end
@@ -180,7 +179,7 @@ describe 'Protocol parsing and communication' do
180
179
  semaphore = Queue.new
181
180
  event = nil
182
181
  execute_request(Cql::Protocol::RegisterRequest.new('SCHEMA_CHANGE'))
183
- io_reactor.add_event_listener do |event_response|
182
+ connections.first.on_event do |event_response|
184
183
  event = event_response
185
184
  semaphore << :ping
186
185
  end
@@ -285,76 +284,6 @@ describe 'Protocol parsing and communication' do
285
284
  end
286
285
  end
287
286
 
288
- it 'decodes positive counters' do
289
- in_keyspace_with_counters_table do
290
- max_long = (1 << 63) -1
291
- response = query(%<UPDATE counters SET c1 = c1 + 1, c2 = c2 +#{max_long} WHERE id = 'positive'>)
292
- response = query(%<SELECT * FROM counters>, :quorum)
293
- response.rows.should == [
294
- {'id' => 'positive', 'c1' => 1, 'c2' => max_long},
295
- ]
296
- end
297
- end
298
-
299
- it 'decodes negative counters' do
300
- in_keyspace_with_counters_table do
301
- min_long = 0 - (1 << 63)
302
- response = query(%<UPDATE counters SET c1 = c1 - 1 , c2 = c2 -#{min_long.abs} WHERE id = 'negative'>)
303
- response = query(%<SELECT * FROM counters>, :quorum)
304
- response.rows.should == [
305
- {'id' => 'negative', 'c1' => -1 , 'c2' => min_long}
306
- ]
307
- end
308
- end
309
-
310
- it 'decodes positive bigints' do
311
- in_keyspace do
312
- max_long = (1 << 63) -1
313
- response = query('CREATE TABLE stuff (id VARCHAR, c1 bigint, c2 bigint, PRIMARY KEY (id))')
314
- response = query(%<UPDATE stuff SET c1 = 1, c2 = #{max_long} WHERE id = 'stuff'>)
315
- response = query(%<SELECT * FROM stuff>, :quorum)
316
- response.rows.should == [
317
- {'id' => 'stuff', 'c1' => 1, 'c2' => max_long}
318
- ]
319
- end
320
- end
321
-
322
- it 'decodes negative bigints' do
323
- in_keyspace do
324
- min_long = 0 - (1 << 63)
325
- response = query('CREATE TABLE stuff (id VARCHAR, c1 bigint, c2 bigint, PRIMARY KEY (id))')
326
- response = query(%<UPDATE stuff SET c1 = -1, c2 = #{min_long} WHERE id = 'stuff'>)
327
- response = query(%<SELECT * FROM stuff>, :quorum)
328
- response.rows.should == [
329
- {'id' => 'stuff', 'c1' => -1, 'c2' => min_long}
330
- ]
331
- end
332
- end
333
-
334
- it 'decodes positive ints' do
335
- in_keyspace do
336
- max_int = (1 << 31) -1
337
- response = query('CREATE TABLE stuff (id VARCHAR, c1 int, c2 int, PRIMARY KEY (id))')
338
- response = query(%<UPDATE stuff SET c1 = 1, c2 = #{max_int} WHERE id = 'stuff'>)
339
- response = query(%<SELECT * FROM stuff>, :quorum)
340
- response.rows.should == [
341
- {'id' => 'stuff', 'c1' => 1, 'c2' => max_int}
342
- ]
343
- end
344
- end
345
-
346
- it 'decodes negative ints' do
347
- in_keyspace do
348
- min_int = 0 - (1 << 31)
349
- response = query('CREATE TABLE stuff (id VARCHAR, c1 int, c2 int, PRIMARY KEY (id))')
350
- response = query(%<UPDATE stuff SET c1 = -1, c2 = #{min_int} WHERE id = 'stuff'>)
351
- response = query(%<SELECT * FROM stuff>, :quorum)
352
- response.rows.should == [
353
- {'id' => 'stuff', 'c1' => -1, 'c2' => min_int}
354
- ]
355
- end
356
- end
357
-
358
287
  it 'sends a DELETE command' do
359
288
  in_keyspace_with_table do
360
289
  response = query(%<DELETE email FROM users WHERE user_name = 'sue'>)
@@ -418,13 +347,17 @@ describe 'Protocol parsing and communication' do
418
347
  end
419
348
 
420
349
  context 'with pipelining' do
350
+ let :connection do
351
+ connections.first
352
+ end
353
+
421
354
  it 'handles multiple concurrent requests' do
422
355
  in_keyspace_with_table do
423
356
  futures = 10.times.map do
424
- io_reactor.queue_request(Cql::Protocol::QueryRequest.new('SELECT * FROM users', :quorum))
357
+ connection.send_request(Cql::Protocol::QueryRequest.new('SELECT * FROM users', :quorum))
425
358
  end
426
359
 
427
- futures << io_reactor.queue_request(Cql::Protocol::QueryRequest.new(%<INSERT INTO users (user_name, email) VALUES ('sam', 'sam@ham.com')>, :one))
360
+ futures << connection.send_request(Cql::Protocol::QueryRequest.new(%<INSERT INTO users (user_name, email) VALUES ('sam', 'sam@ham.com')>, :one))
428
361
 
429
362
  Cql::Future.combine(*futures).get
430
363
  end
@@ -432,11 +365,15 @@ describe 'Protocol parsing and communication' do
432
365
 
433
366
  it 'handles lots of concurrent requests' do
434
367
  in_keyspace_with_table do
435
- futures = 2000.times.map do
436
- io_reactor.queue_request(Cql::Protocol::QueryRequest.new('SELECT * FROM users', :quorum))
368
+ threads = Array.new(10) do
369
+ Thread.new do
370
+ futures = 200.times.map do
371
+ connection.send_request(Cql::Protocol::QueryRequest.new('SELECT * FROM users', :quorum))
372
+ end
373
+ Cql::Future.combine(*futures).get
374
+ end
437
375
  end
438
-
439
- Cql::Future.combine(*futures).get
376
+ threads.each(&:join)
440
377
  end
441
378
  end
442
379
  end
@@ -445,20 +382,23 @@ describe 'Protocol parsing and communication' do
445
382
 
446
383
  context 'in special circumstances' do
447
384
  it 'raises an exception when it cannot connect to Cassandra' do
448
- io_reactor = Cql::Io::IoReactor.new(connection_timeout: 0.1)
385
+ io_reactor = Cql::Io::IoReactor.new(Cql::Protocol::CqlProtocolHandler)
449
386
  io_reactor.start.get
450
- expect { io_reactor.add_connection('example.com', 9042).get }.to raise_error(Cql::Io::ConnectionError)
451
- expect { io_reactor.add_connection('blackhole', 9042).get }.to raise_error(Cql::Io::ConnectionError)
387
+ expect { io_reactor.connect('example.com', 9042, 0.1).get }.to raise_error(Cql::Io::ConnectionError)
388
+ expect { io_reactor.connect('blackhole', 9042, 0.1).get }.to raise_error(Cql::Io::ConnectionError)
452
389
  io_reactor.stop.get
453
390
  end
454
391
 
455
392
  it 'does nothing the second time #start is called' do
456
- io_reactor = Cql::Io::IoReactor.new
393
+ io_reactor = Cql::Io::IoReactor.new(Cql::Protocol::CqlProtocolHandler)
457
394
  io_reactor.start.get
458
- io_reactor.add_connection(ENV['CASSANDRA_HOST'], 9042)
459
- io_reactor.queue_request(Cql::Protocol::StartupRequest.new).get
395
+ connection = io_reactor.connect(ENV['CASSANDRA_HOST'], 9042, 0.1).get
396
+ response = connection.send_request(Cql::Protocol::StartupRequest.new).get
397
+ if response.is_a?(Cql::Protocol::AuthenticateResponse)
398
+ connection.send_request(Cql::Protocol::CredentialsRequest.new('username' => 'cassandra', 'password' => 'cassandra')).get
399
+ end
460
400
  io_reactor.start.get
461
- response = io_reactor.queue_request(Cql::Protocol::QueryRequest.new('USE system', :any)).get
401
+ response = connection.send_request(Cql::Protocol::QueryRequest.new('USE system', :any)).get
462
402
  response.should_not be_a(Cql::Protocol::ErrorResponse)
463
403
  end
464
404
  end